# src/asyncftpclient

Theme:

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs
- devel, stable

Search:

Note: In order to use this module, run `nimble install asyncftpclient`.

This module implements an asynchronous FTP client. It allows you to connect to an FTP server and perform operations on it such as for example:

- The upload of new files.
- The removal of existing files.
- Download of files.
- Changing of files' permissions.
- Navigation through the FTP server's directories.

# Connecting to an FTP server

In order to begin any sort of transfer of files you must first connect to an FTP server. You can do so with the `connect` procedure.

    import std/asyncdispatch
    import pkg/asyncftpclient
    proc main() {.async.} =
      var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
      await ftp.connect()
      echo("Connected")
    waitFor(main())

A new `main` async procedure must be declared to allow the use of the `await` keyword. The connection will complete asynchronously and the client will be connected after the `await ftp.connect()` call.

# Uploading a new file

After a connection is made you can use the `store` procedure to upload a new file to the FTP server. Make sure to check you are in the correct working directory before you do so with the `pwd` procedure, you can also instead specify an absolute path.

    import std/asyncdispatch
    import pkg/asyncftpclient
    proc main() {.async.} =
      var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
      await ftp.connect()
      let currentDir = await ftp.pwd()
      assert currentDir == "/home/user/"
      await ftp.store("file.txt", "file.txt")
      echo("File finished uploading")
    waitFor(main())

# Checking the progress of a file transfer

The progress of either a file upload or a file download can be checked by specifying a `onProgressChanged` procedure to the `store` or `retrFile` procedures.

Procs that take an `onProgressChanged` callback will call this every `progressInterval` milliseconds.

    import std/asyncdispatch
    import pkg/asyncftpclient

    proc onProgressChanged(total, progress: BiggestInt,
                            speed: float) {.async.} =
      echo("Uploaded ", progress, " of ", total, " bytes")
      echo("Current speed: ", speed, " kb/s")

    proc main() {.async.} =
      var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test", progressInterval = 500)
      await ftp.connect()
      await ftp.store("file.txt", "/home/user/file.txt", onProgressChanged)
      echo("File finished uploading")
    waitFor(main())

# Types

    AsyncFtpClient = ref object
      csock*: AsyncSocket
      dsock*: AsyncSocket
      user*, pass*: string
      address*: string
      port*: Port
      jobInProgress*: bool
      job*: FtpJob
      dsockConnected*: bool
      when defined(ssl):

    FtpEvent = object
      filename*: string
      case typ*: FtpEventType
      of EvLines:
        lines*: string           ## Lines that have been transferred.
      of EvRetr, EvStore:       ## Retr/Store operation finished.
        nil
      of EvTransferProgress:
        bytesTotal*: BiggestInt  ## Bytes total.
        bytesFinished*: BiggestInt ## Bytes transferred.
        speed*: BiggestInt       ## Speed in bytes/s
        currentJob*: FtpJobType  ## The current job being performed.

  Event

    FtpEventType = enum
      EvTransferProgress, EvLines, EvRetr, EvStore

    FtpJobType = enum
      JRetrText, JRetr, JStore

    ProgressChangedProc = proc (total, progress: BiggestInt; speed: float): Future[
        void] {.closure, ...gcsafe.}

    ReplyError = object of IOError

# Procs

    proc cd(ftp: AsyncFtpClient; dir: string): owned(Future[void]) {.
        ...stackTrace: false, raises: [Exception, ReplyError, ValueError, SslError],
        tags: [RootEffect], forbids: [].}

  Changes the current directory on the remote FTP server to `dir`.

    proc cdup(ftp: AsyncFtpClient): owned(Future[void]) {....stackTrace: false,
        raises: [Exception, ReplyError, ValueError, SslError], tags: [RootEffect],
        forbids: [].}

  Changes the current directory to the parent of the current directory.

    proc chmod(ftp: AsyncFtpClient; path: string; permissions: set[FilePermission]): owned(
        Future[void]) {....stackTrace: false,
                        raises: [Exception, ReplyError, ValueError, SslError],
                        tags: [RootEffect], forbids: [].}

  Changes permission of `path` to `permissions`.

    proc connect(ftp: AsyncFtpClient): owned(Future[void]) {....stackTrace: false,
        raises: [Exception, OSError, IOError, ValueError, SslError, ReplyError],
        tags: [RootEffect], forbids: [].}

  Connect to the FTP server specified by `ftp`.

    proc createDir(ftp: AsyncFtpClient; dir: string; recursive = false): owned(
        Future[void]) {....stackTrace: false,
                        raises: [Exception, ReplyError, ValueError, SslError],
                        tags: [RootEffect], forbids: [].}

  Creates a directory `dir`. If `recursive` is true, the topmost subdirectory of `dir` will be created first, following the secondmost... etc. this allows you to give a full path as the `dir` without worrying about subdirectories not existing.

    proc defaultOnProgressChanged(total, progress: BiggestInt; speed: float): Future[
        void] {.nimcall, ...gcsafe, raises: [Exception], tags: [RootEffect],
                forbids: [].}

  Default FTP `onProgressChanged` handler. Does nothing.

    proc fileExists(ftp: AsyncFtpClient; file: string): Future[bool] {.
        ...stackTrace: false, raises: [Exception, ValueError, OSError, SslError,
                                    ReplyError, IOError, LibraryError],
        tags: [RootEffect], forbids: [].}

  Determines whether `file` exists.

    proc list(ftp: AsyncFtpClient; dir = ""): Future[string] {....stackTrace: false, raises: [
        Exception, ValueError, OSError, SslError, ReplyError, IOError, LibraryError],
        tags: [RootEffect], forbids: [].}

  Lists all files in `dir`. If `dir` is `""`, uses the current working directory.

    proc listDirs(ftp: AsyncFtpClient; dir = ""): Future[seq[string]] {.
        ...stackTrace: false, raises: [Exception, ValueError, OSError, SslError,
                                    ReplyError, IOError, LibraryError],
        tags: [RootEffect], forbids: [].}

  Returns a list of filenames in the given directory. If `dir` is "", the current directory is used. If `async` is true, this function will return immediately and it will be your job to use asyncdispatch's `poll` to progress this operation.

    proc newAsyncFtpClient(address: string; port = Port(21); user, pass = "";
                           progressInterval: int = 1000; useTls = false;
                           sslContext: SslContext = nil): AsyncFtpClient {.
        ...raises: [OSError], tags: [], forbids: [].}

  Creates a new `AsyncFtpClient` object.

    proc pwd(ftp: AsyncFtpClient): Future[string] {....stackTrace: false,
        raises: [Exception, ValueError, SslError, ReplyError], tags: [RootEffect],
        forbids: [].}

  Returns the current working directory.

    proc removeDir(ftp: AsyncFtpClient; dir: string): owned(Future[void]) {.
        ...stackTrace: false, raises: [Exception, ReplyError, ValueError, SslError],
        tags: [RootEffect], forbids: [].}

  Delete a directory `dir` on the remote FTP server

    proc removeFile(ftp: AsyncFtpClient; filename: string): owned(Future[void]) {.
        ...stackTrace: false, raises: [Exception, ReplyError, ValueError, SslError],
        tags: [RootEffect], forbids: [].}

  Delete a file `filename` on the remote FTP server

    proc rename(ftp: AsyncFtpClient; nameFrom: string; nameTo: string): owned(
        Future[void]) {....stackTrace: false,
                        raises: [Exception, ReplyError, ValueError, SslError],
                        tags: [RootEffect], forbids: [].}

  Rename a file or directory on the remote FTP Server from current name `name_from` to new name `name_to`

    proc retrFile(ftp: AsyncFtpClient; file, dest: string;
                  onProgressChanged: ProgressChangedProc = defaultOnProgressChanged): owned(
        Future[void]) {....stackTrace: false, raises: [Exception, IOError, OSError,
        ValueError, SslError, ReplyError, LibraryError],
                        tags: [RootEffect, TimeEffect, WriteIOEffect], forbids: [].}

  Downloads `file` and saves it to `dest`. The `EvRetr` event is passed to the specified `handleEvent` function when the download is finished. The event's `filename` field will be equal to `file`.

    proc retrText(ftp: AsyncFtpClient; file: string): Future[string] {.
        ...stackTrace: false, raises: [Exception, ValueError, OSError, SslError,
                                    ReplyError, IOError, LibraryError],
        tags: [RootEffect], forbids: [].}

  Retrieves `file`. File must be ASCII text.

    proc send(ftp: AsyncFtpClient; m: string): Future[string] {....stackTrace: false,
        raises: [Exception, ValueError, SslError, ReplyError], tags: [RootEffect],
        forbids: [].}

  Send a message to the server, and wait for a primary reply. `\c\L` is added for you.

  You need to make sure that the message `m` doesn't contain any newline characters. Failing to do so will raise `AssertionDefect`.

  Note: The server may return multiple lines of coded replies.

    proc store(ftp: AsyncFtpClient; file, dest: string;
               onProgressChanged: ProgressChangedProc = defaultOnProgressChanged): owned(
        Future[void]) {....stackTrace: false, raises: [Exception, IOError, OSError,
        ValueError, SslError, ReplyError, LibraryError],
                        tags: [RootEffect, ReadIOEffect, TimeEffect], forbids: [].}

  Uploads `file` to `dest` on the remote FTP server. Usage of this function asynchronously is recommended to view the progress of the download. The `EvStore` event is passed to the specified `handleEvent` function when the upload is finished, and the `filename` field will be equal to `file`.