Description
===========

promise-ftp is an FTP client module for [node.js](http://nodejs.org/) that provides an asynchronous interface for
communicating with an FTP server.

This module is a wrapper around the [node-ftp](https://github.com/mscdex/node-ftp) module, and provides some additional
features as well as a convenient promise-based API.

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
============

* Version 1.3.0 changes:
  * addition of the **rawClient** property which exposes the underlying ftp client.

* Version 1.2.0 changes:
  * the `FtpConnectionError` and `FtpReconectError` classes have been moved to
  [their own module](https://github.com/realtymaps/promise-ftp-errors), in anticipation of creating a
  semi-interchangeable API for [promise-sftp](https://github.com/realtymaps/promise-sftp).  They are still exposed
  on the main module as well.

* Version 1.1.0 adds:
  * the `autoReconnect` and `preserveCwd` options
  * the `FtpReconectError` error class
  * the `reconnect()` and `getConnectionStatus()` methods
  * the `STATUSES` and `ERROR_CODES` maps
  * completed API documentation

* Version 1.0.0 provides a promise-based API that mirrors the
[node-ftp API](https://github.com/mscdex/node-ftp/blob/master/README.md#api) almost identically, except with a promise
interface, plus:
  * the `FtpConnectionError` error class


Requirements
============

* [node.js](http://nodejs.org/) -- v0.8.0 or newer


Install
=======

    npm install promise-ftp


Examples
========

Get a directory listing of the current (remote) working directory:

```javascript
  var PromiseFtp = require('promise-ftp');
  
  var ftp = new PromiseFtp();
  ftp.connect({host: host, user: user, password: password})
  .then(function (serverMessage) {
    console.log('Server message: '+serverMessage);
    return ftp.list('/');
  }).then(function (list) {
    console.log('Directory listing:');
    console.dir(list);
    return ftp.end();
  });
```

Download remote file 'foo.txt' and save it to the local file system:

```javascript
  var PromiseFtp = require('promise-ftp');
  var fs = require('fs');
  
  var ftp = new PromiseFtp();
  ftp.connect({host: host, user: user, password: password})
  .then(function (serverMessage) {
    return ftp.get('foo.txt');
  }).then(function (stream) {
    return new Promise(function (resolve, reject) {
      stream.once('close', resolve);
      stream.once('error', reject);
      stream.pipe(fs.createWriteStream('foo.local-copy.txt'));
    });
  }).then(function () {
    return ftp.end();
  });
```

Upload local file 'foo.txt' to the server:

```javascript
  var PromiseFtp = require('promise-ftp');
  var fs = require('fs');
  
  var ftp = new PromiseFtp();
  ftp.connect({host: host, user: user, password: password})
  .then(function (serverMessage) {
    return ftp.put('foo.txt', 'foo.remote-copy.txt');
  }).then(function () {
    return ftp.end();
  });
```


API
===

For the most part, this module's API mirrors [node-ftp's API](https://github.com/mscdex/node-ftp#api), 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. If you need access to the underlying events or callback-based methods,
you can access the raw node-ftp client via **rawClient** property.

Errors
------

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.  In the case
of protocol-level errors, the rejected error will contain a `code` property that references the related 3-digit FTP
response code.  This code may be translated into a (generic) human-readable text explanation by referencing the map
`PromiseFtp.ERROR_CODES`.

Methods
-------

* **\[constructor\]**(): Creates and returns a new PromiseFtp client instance.

* **connect**(config <_object_>): Connects to an FTP server; returned promise resolves to the server's greeting
message. Valid config properties:

    * host <_string_>: The hostname or IP address of the FTP server. **Default:** 'localhost'

    * port <_integer_>: The port of the FTP server. **Default:** 21

    * secure <_mixed_>: Set to true for both control and data connection encryption, 'control' for control connection
    encryption only, or 'implicit' for implicitly encrypted control connection (this mode is deprecated in modern times,
    but usually uses port 990) **Default:** false

    * secureOptions <_object_>: Additional options to be passed to `tls.connect()`. **Default:** (none)

    * user <_string_>: Username for authentication. **Default:** 'anonymous'

    * password <_string_>: Password for authentication. **Default:** 'anonymous@'

    * connTimeout <_integer_>: How long (in milliseconds) to wait for the control connection to be established.
    **Default:** 10000

    * pasvTimeout <_integer_>: How long (in milliseconds) to wait for a PASV data connection to be established.
    **Default:** 10000

    * keepalive <_integer_>: How often (in milliseconds) to send a 'dummy' (NOOP) command to keep the connection alive.
    **Default:** 10000
    
    * 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.  **Default:** false
    
    * preserveCwd <_boolean_>: Whether to attempt to return to the prior current working directory after a successful
    automatic reconnection.  Only used if `autoReconnect` is true.  **Default:** false
    
* **reconnect**(): Connects to an FTP 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 any error associated with closing the connection, or _true_ if there was an error but it wasn't captured.

* **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 `PromiseFtp.`STATUSES, as well as below:

    * not yet connected
    
    * connecting
    
    * connected
    
    * logging out
    
    * disconnecting
    
    * disconnected
    
    * reconnecting

### Required "standard" commands (RFC 959)

* **list**(\[path <_string_>\]\[, useCompression <_boolean_>\]): Retrieves the directory listing of `path`. `path`
defaults to the current working directory. `useCompression` defaults to false.  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_>: For symlink entries, this is the symlink's target **(*NIX only)**.

      * sticky <_boolean_>: True if the sticky bit is set for this entry **(*NIX only)**.

* **get**(path <_string_>\[, useCompression <_boolean_>\]): Retrieves a file at `path` from the server.
`useCompression` defaults to false. Returned promise resolves to a `ReadableStream`.

* **put**(input <_mixed_>, destPath <_string_>\[, useCompression <_boolean_>\]): Sends data to the server to be stored
as `destPath`. `input` can be a ReadableStream, a Buffer, or a path to a local file. `useCompression` defaults to
false. Returned promise resolves to _undefined_.

* **append**(input <_mixed_>, destPath <_string_>\[, useCompression <_boolean_>\]): Same as **put()**, except if
`destPath` already exists, it will be appended to instead of overwritten.

* **rename**(oldPath <_string_>, newPath <_string_>): Renames/moves `oldPath` to `newPath` on the server. Returned
promise resolves to _undefined_.

* **logout**(): Logs the user out from the server. Returned promise resolves to _undefined_.

* **delete**(path <_string_>): Deletes the file at `path`. Returned promise resolves to _undefined_.

* **cwd**(path <_string_>): Changes the current working directory to `path`. Returned promise resolves to the new
current directory, if the server replies with it in the response text; otherwise resolves to _undefined_.

* **abort**(): Aborts the current data transfer (e.g. from **get()**, **put()**, or **list()**). Returned promise
resolves to _undefined_.

* **site**(command <_string_>): Sends `command` (e.g. 'CHMOD 755 foo', 'QUOTA') using SITE; returned promise resolves
to an object with the following attributes:

  * text <_string_>: responseText
  
  * code <_integer_>: responseCode.

* **status**(): Retrieves human-readable information about the server's status. Returned promise resolves to the status
string sent by the server.

* **ascii**(): Sets the transfer data type to ASCII. Returned promise resolves to _undefined_.

* **binary**(): Sets the transfer data type to binary (default at time of connection). Returned promise resolves to
_undefined_.

### Optional "standard" commands (RFC 959)

* **mkdir**(path <_string_>\[, recursive <_boolean_>\]): Creates a new directory, `path`, on the server. `recursive` is
for enabling a 'mkdir -p' algorithm and defaults to false. Returned promise resolves to _undefined_.

* **rmdir**(path <_string_>\[, includeContents <_boolean_>\]): Removes a directory, `path`, on the server. If
`includeContents` is true, this call will delete the contents of the directory if it is not empty; note that this
currently only deletes files within the directory, not subdirectories (the command will fail if there are subdirectories
present). Returned promise resolves to _undefined_.

* **cdup**(): Changes the working directory to the parent of the current directory. Returned promise resolves to
_undefined_.

* **pwd**(): Retrieves the current working directory. Returned promise resolves to the current working directory.

* **system**(): Retrieves the server's operating system. Returned promise resolves to the OS string sent by the server.

* **listSafe**(\[path <_string_>\]\[, useCompression <_boolean_>\]): Similar to **list()**, except the directory is
temporarily changed to `path` to retrieve the directory listing. This is useful for servers that do not handle
characters like spaces and quotes in directory names well for the LIST command. This function is "optional" because it
relies on **pwd()** being available.

### Extended commands (RFC 3659)

* **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_.
