UNPKG

promise-sftp

Version:

a promise-based sftp client for node.js

464 lines (301 loc) 22.5 kB
Description =========== promise-sftp is an SFTP client module for [node.js](http://nodejs.org/) that provides an asynchronous interface for communicating with an SFTP server. This module is a wrapper the SFTP client functionality from [ssh2](https://github.com/mscdex/ssh2), and provides some additional features as well as a convenient promise-based API. The API is semi-interchangeable with the [promise-ftp](https://github.com/realtymaps/promise-ftp) module, an idea that was inspired by [sftpjs](https://github.com/wankdanker/node-sftpjs). This library is written primarily in CoffeeScript, but may be used just as easily in a Node app using Javascript or CoffeeScript. Promises in this module are provided by [Bluebird](https://github.com/petkaantonov/bluebird). Change Log ============ * v0.9.0: Added `PromiseSftp.ERROR_CODES` * v0.8.0: Most features are in place. Requirements ============ * [node.js](http://nodejs.org/) -- v0.8.7 or newer Install ======= npm install promise-sftp Examples ======== **TODO**: examples API === For the most part, this module's API mirrors [ssh2's SFTPStream API](https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md#sftpstream-methods), except that it returns promises which resolve or reject, rather than emitting events or calling callbacks. However, there are some minor differences and some additional features, including attempts to make this API as interchangeable as possible with [promise-ftp's](https://github.com/realtymaps/promise-ftp#api). Errors ------ Most errors generated by this module will be instances of one of the following: * **FtpConnectionError**: Indicates the connection status was not appropriate for the method called; e.g. **connect**() or **reconnect()** was called when the connection was not in a disconnected state, **end()** was called when the connection has already closed, etc. * **FtpReconnectError**: Only possible when the `autoReconnect` option is set true, this indicates a reconnect was attempted and failed. It includes information about both the original disconnect error (if any) as well as the error while attempting to reconnect. In addition, errors from the underlying node-ftp library may be rejected unchanged through method calls. These errors may contain a `code` property that references the SFTP error code. This code may be translated into a (generic) human-readable text explanation by referencing the map `PromiseSftp.ERROR_CODES`. **TODO**: investigate and document ssh-level errors and the `level` property of errors. Methods ------- * **\[constructor\]**(): Creates and returns a new PromiseSftp client instance. ### Methods interoperable with [promise-ftp](https://github.com/realtymaps/promise-ftp#api) * **connect**(config <_object_>): Connects to an SFTP server; returned promise resolves to the server's greeting message. If multiple authentication methods are available based on the passed config, they are attempted in the following order: Password **>** Private Key **>** Agent **>** keyboard-interactive (if `tryKeyboard` is set) **>** Host-based **>** None. _NOTE_: many of the config properties below are NOT relevant for [promise-ftp](https://github.com/realtymaps/promise-ftp#api). However, all [promise-ftp](https://github.com/realtymaps/promise-ftp#api) connect options will work here, except for _secure_, _secureOptions_, and _preserveCwd_. Valid config properties are: * host <_string_>: The hostname or IP address of the SFTP server. **Default:** 'localhost' * port <_integer_>: The port of the SFTP server. **Default:** 22 * username <_string_>: Username for authentication. * password <_string_>: Password for password-based user authentication. **Default:** (none) * changePassword <_function_>: When using password-based user authentication, set this option to handle password change requests. If this option isn't set, and the server requests a password change, I haven't tested what will happen. The function should behave as described below. **Default:** (none) * changePassword(message <_string_>, language <_string_>): This function should return a promise which resolves to the new password. * autoReconnect <_boolean_>: Whether to attempt to automatically reconnect using the existing config if the connection is unexpectedly closed. Auto-reconnection is lazy, and so will wait until a command needs to be issued before attempting to reconnect. * forceIPv4 <_boolean_>: Only connect via resolved IPv4 address for `host`. **Default:** `false` * forceIPv6 <_boolean_>: Only connect via resolved IPv6 address for `host`. **Default:** `false` * hostHash <_string_>: 'md5' or 'sha1'. The host's key is hashed using this method and passed to the `hostVerifier` function. **Default:** (none) * hostVerifier <_function_>: Function that is passed a string hex hash of the host's key for verification purposes. Return `true` to continue with the handshake or `false` to reject and disconnect. **Default:** (auto-accept) * agent <_string_>: Path to ssh-agent's UNIX socket for ssh-agent-based user authentication. **Windows users: set to 'pageant' for authenticating with Pageant or (actual) path to a cygwin "UNIX socket."** **Default:** (none) * privateKey <_mixed_>: _Buffer_ or _string_ that contains a private key for either key-based or host-based user authentication (OpenSSH format). **Default:** (none) * privateKeyFile <_string_>: Path and name of a file containing a private key as would be passed to the `privateKey` option. If `privateKey` is also set, priority is given to the `privateKey` option. **Default:** (none) * passphrase <_string_>: For an encrypted private key, this is the passphrase used to decrypt it. **Default:** (none) * localHostname <_string_>: Along with `localUsername` and `privateKey`, set this to a non-empty string for host-based user authentication. **Default:** (none) * localUsername <_string_>: Along with `localHostname` and `privateKey`, set this to a non-empty string for host-based user authentication. **Default:** (none) * tryKeyboard <_function_>: Set this option to a function to try keyboard-interactive user authentication if primary user authentication method fails. The function should behave as described below. **Default:** (none) * tryKeyboard(name <_string_>, instructions <_string_>, instructionsLang <_string_>, prompts <_array_>): The goal of this function is to acquire responses to the given `prompts`. * `name` is generally what you'd use as a header or GUI window title to describe the purpose of the `prompts`. * `prompts` is an array of objects containing the fields `prompt` (the query to <_string_> pose to the user) and `echo` (a indicating <_boolean_> whether the user's input should be displayed on-screen). * The function's return value should be one of the following: an array of response strings, a promise to an array of response strings, or an array of promises to response strings. * _NOTE_: This function may be called more than once, with the same or different prompts. * keepaliveInterval <_integer_>: How often (in milliseconds) to send SSH-level keepalive packets to the server (in a similar way as OpenSSH's ServerAliveInterval config option). Set to 0 to disable. **Default:** `0` * keepaliveCountMax <_integer_>: How many consecutive, unanswered SSH-level keepalive packets that can be sent to the server before disconnection (similar to OpenSSH's ServerAliveCountMax config option). **Default:** `3` * readyTimeout <_integer_>: How long (in milliseconds) to wait for the SSH handshake to complete. **Default:** `20000` * strictVendor <_boolean_>: Performs a strict server vendor check before sending vendor-specific requests, etc. (e.g. check for OpenSSH server when using `openssh_noMoreSessions()`) **Default:** `true` * sock <_ReadableStream_>: A _ReadableStream_ to use for communicating with the server instead of creating and using a new TCP connection (useful for connection hopping). * agentForward <_boolean_>: Set to `true` to use OpenSSH agent forwarding (`auth-agent@openssh.com`) for the life of the connection. `agent` must also be set to use this feature. **Default:** `false` * debug <_function_>: Set this to a function that receives a single string argument to get detailed (local) debug information. **Default:** (none) * _the additional [promise-ftp](https://github.com/realtymaps/promise-ftp#api)-style option aliases below are used for compatibility purposes. The aliases are only used if the actual option is not set_: * user <_string_>: alias for `username` * connTimeout <_integer_>: alias for `readyTimeout` * pasvTimeout <_integer_>: alias for `readyTimeout`; if both this and `connTimeout` are given truthy values, `connTimeout` will be given priority. * keepalive <_integer_>: alias for `keepaliveInterval` * **reconnect**(): Connects to an SFTP server using the config from the most recent call to **connect()**. Returned promise resolves to the server's greeting message. * **end**(): Closes the connection to the server after any/all enqueued commands have been executed; returned promise resolves to a boolean indicating whether there was an error associated with closing the connection. * **destroy**(): Closes the connection to the server immediately. Returns a boolean indicating whether the connection was connected prior to the call to **destroy()**. * **getConnectionStatus**(): Returns a string describing the current connection state. Possible strings are enumerated in `PromiseSftp.STATUSES`, as well as below: * not yet connected * connecting * connected * logging out * disconnecting * disconnected * reconnecting * **list**(\[path <_string_>\]): Retrieves the directory listing of `path`. `path` defaults to '.'. Returned promise resolves to an array of objects with these properties: * type <_string_>: A single character denoting the entry type: 'd' for directory, '-' for file (or 'l' for symlink on **\*NIX only**). * name <_string_>: The name of the entry. * size <_string_>: The size of the entry in bytes. * date <_Date_>: The last modified date of the entry. * rights <_object_>: The various permissions for this entry **(*NIX only)**. * user <_string_>: An empty string or any combination of 'r', 'w', 'x'. * group <_string_>: An empty string or any combination of 'r', 'w', 'x'. * other <_string_>: An empty string or any combination of 'r', 'w', 'x'. * owner <_string_>: The user name or ID that this entry belongs to **(*NIX only)**. * group <_string_>: The group name or ID that this entry belongs to **(*NIX only)**. * target <_string_>: _null_ **TODO**: For symlink entries, this is the symlink's target **(*NIX only)**. * sticky <_boolean_>: _null_ **TODO**: True if the sticky bit is set for this entry **(*NIX only)**. * **get**(path <_string_>): Retrieves a file at `path` from the server. Returned promise resolves to a `ReadableStream`. * **put**(input <_mixed_>, destPath <_string_>): Sends data to the server to be stored as `destPath`. `input` can be a ReadableStream, a Buffer, or a path to a local file. Returned promise resolves to _undefined_. * **append**(input <_mixed_>, destPath <_string_>): Same as **put()**, except if `destPath` already exists, it will be appended to instead of overwritten. * **logout**(): Alias to **end()**. * **delete**(path <_string_>): Alias for **unlink()** * **rename**(oldPath <_string_>, newPath <_string_>): Renames/moves `oldPath` to `newPath` on the server. Returned promise resolves to _undefined_. * **mkdir**(path <_string_>\[, recursive <_boolean_>\]\[, attributes <_[ATTRS](#attrs)_>\]): Creates a new directory on the server at `path`. `recursive` is for enabling a 'mkdir -p' algorithm and defaults to false. `attributes` sets the [ATTRS](#attrs) used when creating directories. Returned promise resolves to _undefined_. **TODO**: The `recursive` option here may not work as desired currently -- fix it. * **rmdir**(path <_string_>): Removes the directory on the server at `path`. Returned promise resolves to _undefined_. **TODO**: Test whether this deletes directory contents recursively, and document and/or add `recursive` option to behave like [promise-ftp](https://github.com/realtymaps/promise-ftp#api) * **listSafe**(\[path <_string_>\]): Alias to **list()**. * **size**(path <_string_>): Retrieves the size of `path`. Returned promise resolves to number of bytes. * **lastMod**(path <_string_>): Retrieves the last modified date and time for `path`. Returned promise resolves an instance of `Date`. * **restart**(byteOffset <_integer_>): Sets the file byte offset for the next file transfer action initiated via **get()** or **put()** to `byteOffset`. Returned promise resolves to _undefined_. ### Methods specific to promise-sftp: * **fastGet**(remotePath <_string_>, localPath <_string_>\[, options <_object_>\]): Downloads a file at `remotePath` to `localPath` using parallel reads for faster throughput. Returned promise resolves to _undefined_. `options` can have the following properties: * concurrency <_integer_>: Number of concurrent reads **Default:** `25` * chunkSize <_integer_>: Size of each read in bytes **Default:** `32768` * step - <_function_(total_transferred <_integer_>, chunk <_integer_>, total <_integer_>)>: Called every time a part of a file was transferred * **fastPut**(localPath <_string_>, remotePath <_string_>\[, options <_object_>\]): Uploads a file from `localPath` to `remotePath` using parallel reads for faster throughput. Returned promise resolves to _undefined_. `options` can have the following properties: * concurrency <_integer_>: Number of concurrent reads **Default:** `25` * chunkSize <_integer_>: Size of each read in bytes **Default:** `32768` * step - <_function_(total_transferred <_integer_>, chunk <_integer_>, total <_integer_>)>: Called every time a part of a file was transferred * **createReadStream**(path <_string_>\[, options <_object_>\]): Returned promise resolves to a new readable stream for `path`. `options` has the following defaults: ```javascript { flags: 'r', encoding: null, handle: null, mode: 0666, autoClose: true } ``` `options` can include `start` and `end` values to read a range of bytes from the file instead of the entire file. Both `start` and `end` are inclusive and 0-indexed. The `encoding` can be `'utf8'`, `'ascii'`, or `'base64'`. If `autoClose` is false, then the file handle won't be closed, even if there's an error. It is your responsibility to close it and make sure there's no file handle leak. If `autoClose` is set to true (default behavior), on `error` or `end` the file handle will be closed automatically. An example to read the last 10 bytes of a file which is 100 bytes long: ```javascript sftp.createReadStream('sample.txt', {start: 90, end: 99}); ``` * **createWriteStream**(path <_string_>\[, options <_object_>\]): Returned promise resolves to a new readable stream for `path`. `options` has the following defaults: ```javascript { flags: 'w', encoding: null, mode: 0666 } ``` `options` may also include a `start` option to allow writing data at some position past the beginning of the file. Modifying a file rather than replacing it may require a flags mode of `'r+'` rather than the default mode `'w'`. If 'autoClose' is set to false and you pipe to this stream, this stream will not automatically close after there is no more data upstream -- allowing future pipes and/or manual writes. * **open**(filename <_string_>, mode <_string_>\[, attributes <_[ATTRS](#attrs)_>\]): Opens a file `filename` for `mode` with optional `attributes` (in case **open()** creates a file). `mode` is any of the modes supported by `fs.open` (except sync mode). Returned promise resolves to a _Buffer_ containing a handle to the file. * **close**(handle <_Buffer_>): Closes the resource associated with `handle` given by **open()** or **opendir()**. Returned promise resolves to _undefined_. * **read**(handle <_Buffer_>, buffer <_Buffer_>, offset <_integer_>, length <_integer_>, position <_integer_>): Reads `length` bytes from the resource associated with `handle` starting at `position` and stores the bytes in `buffer` starting at `offset`. Returned promise resolves to an object with the following properties: * bytesRead <_integer_>: bytes successfully read * buffer <_Buffer>: the buffer passed to **read()**, but `offset`-adjusted * position <_integer_>: the `position` passed to **read()** * **write**(handle <_Buffer_>, buffer <_Buffer_>, offset <_integer_>, length <_integer_>, position <_integer_>): Writes `length` bytes from `buffer` starting at `offset` to the resource associated with `handle` starting at `position`. Returned promise resolves to _undefined_. * **fstat**(handle <_Buffer_>): Retrieves attributes for the resource associated with `handle`. Returned promise resolves to a [Stats](#stats) object. * **fsetstat**(handle <_Buffer_>, attributes <_[ATTRS](#attrs)_>): Sets the `attributes` for the resource associated with `handle`. Returned promise resolves to _undefined_. * **futimes**(handle <_Buffer_>, atime <_mixed_>, mtime <_mixed_>): Sets the access time and modified time for the resource associated with `handle`. `atime` and `mtime` can be Date instances or UNIX timestamp integers. Returned promise resolves to _undefined_. * **fchown**(handle <_Buffer_>, uid <_integer_>, gid <_integer_>): Sets the owner for the resource associated with `handle`. Returned promise resolves to _undefined_. * **fchmod**(handle <_Buffer_>, mode <_mixed_>): Sets the mode for the resource associated with `handle`. `mode` can be an integer or a string containing an octal number. Returned promise resolves to _undefined_. * **opendir**(path <_string_>): Opens a directory `path`. Returned promise resolves to a _Buffer_ containing a directory handle. * **readdir**(location <_mixed_>): Retrieves a directory listing. `location` can either be a _Buffer_ containing a valid directory handle from **opendir()** or a _string_ containing the path to a directory. Returned promise resolves to an _Array_ of `{ filename: <_string_>, longname: <_string_>, attrs: [ATTRS](#attrs) }` style objects. _NOTE_: If `location` is a directory handle, this function may need to be called multiple times until it resolves to boolean false, which indicates that no more directory entries are available for that directory handle. * **unlink**(path <_string_>): Removes the file/symlink at `path`. Returned promise resolves to _undefined_. * **stat**(path <_string_>): Retrieves attributes for `path`. Returned promise resolves to a [Stats](#stats) object. * **lstat**(path <_string_>): Retrieves attributes for `path`. If `path` is a symlink, the link itself is stat'ed instead of the resource it refers to. Returned promise resolves to a [Stats](#stats) object. * **setstat**(path <_string_>, attributes <_[ATTRS](#attrs)_>): Sets the attributes defined in `attributes` for `path`. Returned promise resolves to _undefined_. * **utimes**(path <_string_>, atime <_mixed_>, mtime <_mixed_>): Sets the access time and modified time for `path`. `atime` and `mtime` can be Date instances or UNIX timestamp integers. Returned promise resolves to _undefined_. * **chown**(path <_string_>, uid <_integer_>, gid <_integer_>): Sets the owner and group for `path`. Returned promise resolves to _undefined_. * **chmod**(path <_string_>, mode <_mixed_>): Sets the mode for `path`. `mode` can be an integer or a string containing an octal number. Returned promise resolves to _undefined_. * **readlink**(path <_string_>): Returned promise resolves to the target for a symlink at `path`. * **symlink**(targetPath <_string_>, linkPath <_string_>): Creates a symlink at `linkPath` to `targetPath`. Returned promise resolves to _undefined_. * **realpath**(path <_string_>): Returned promise resolves to the absolute path corresponding to `path`. * **ext_openssh_rename**(srcPath <_string_>, destPath <_string_>): **_OpenSSH extension_** Performs POSIX rename(3) from `srcPath` to `destPath`. Returned promise resolves to _undefined_. * **ext_openssh_statvfs**(path <_string_>): **_OpenSSH extension_** Performs POSIX statvfs(2) on `path`. Returned promise resolves to an object containing the same fields as found in the [statvfs struct](http://linux.die.net/man/2/statvfs). * **ext_openssh_fstatvfs**(handle <_Buffer_>): **_OpenSSH extension_** Performs POSIX fstatvfs(2) on open handle `handle`. Returned promise resolves to an object containing the same fields as found in the [statvfs struct](http://linux.die.net/man/2/statvfs). * **ext_openssh_hardlink**(targetPath <_string_>, linkPath <_string_>): **_OpenSSH extension_** Performs POSIX link(2) to create a hard link to `targetPath` at `linkPath`. Returned promise resolves to _undefined_. * **ext_openssh_fsync**(handle <_Buffer_>): **_OpenSSH extension_** Performs POSIX fsync(3) on the open handle `handle`. Returned promise resolves to _undefined_. * **wait**(): Waits for any previously-issued commands to complete. ### ATTRS and Stats Objects ##### ATTRS **TODO**: document ##### Stats **TODO**: document Future Development ================== * implement additional FTP commands: * cwd * cdup * pwd * abort * site * status * ascii * binary * system * investigate and document (and maybe enhance) the way the `level` property of underlying errors works * investigate and document (and maybe enhance) the way the `code` property of underlying errors works, especially re: ssh errors vs sftp errors vs socket errors (?) * properly handle the `target` and `sticky` attributes on the **list()** command * fix **mkdir()** `recursive` option * add `recursive` option to **rmdir** * improve 'continue' handling to account for parallel promises