gftp/actor

OTP actor wrapper for gftp that serializes all FTP operations.

Wrapping an FtpClient in an actor prevents protocol state corruption by ensuring that control commands are rejected while a data channel is open.

Usage

import gftp
import gftp/actor as ftp_actor

let assert Ok(client) = gftp.connect("ftp.example.com", 21)
let assert Ok(started) = ftp_actor.start(client)
let handle = started.data

let assert Ok(_) = ftp_actor.login(handle, "user", "password")
let assert Ok(cwd) = ftp_actor.pwd(handle)
let assert Ok(_) = ftp_actor.quit(handle)

Message-based streaming

The actor enforces chunk protection: once a data channel is opened via open_retr, open_stor, etc., all control commands return Error(DataTransferInProgress) until close_data_channel is called.

let assert Ok(data_stream) = ftp_actor.open_retr(handle, "file.txt")
// ... receive data via stream.receive_next_packet_as_message ...
let assert Ok(_) = ftp_actor.close_data_channel(handle, data_stream)

Types

Handle to an FTP actor. Use the public functions in this module to interact with it.

The handle wraps the actor subject and a configurable call timeout (default 30 seconds). Use with_call_timeout to adjust the timeout for all subsequent calls.

pub opaque type Handle

Internal message type for the FTP actor. Users interact via the public functions, not by sending messages directly.

pub opaque type Message

Values

pub fn abor(handle: Handle) -> Result(Nil, result.FtpError)

Abort an active file transfer.

pub fn appe(
  handle: Handle,
  path: String,
  writer: fn(stream.DataStream) -> Result(Nil, result.FtpError),
) -> Result(Nil, result.FtpError)

Append to a file using a callback.

pub fn cdup(handle: Handle) -> Result(Nil, result.FtpError)

Change to parent directory.

pub fn clear_command_channel(
  handle: Handle,
) -> Result(Nil, result.FtpError)

Clear the command channel encryption.

pub fn close_data_channel(
  handle: Handle,
  data_stream: stream.DataStream,
) -> Result(Nil, result.FtpError)

Close a data channel and finalize the transfer.

pub fn custom_command(
  handle: Handle,
  command_str: String,
  expected_statuses: List(status.Status),
) -> Result(response.Response, result.FtpError)

Execute a custom FTP command.

pub fn custom_data_command(
  handle: Handle,
  command_str: String,
  expected_statuses: List(status.Status),
  on_data_stream: fn(stream.DataStream, response.Response) -> Result(
    Nil,
    result.FtpError,
  ),
) -> Result(Nil, result.FtpError)

Execute a custom data command with a callback.

pub fn cwd(
  handle: Handle,
  path: String,
) -> Result(Nil, result.FtpError)

Change working directory.

pub fn dele(
  handle: Handle,
  path: String,
) -> Result(Nil, result.FtpError)

Delete a file.

pub fn eprt(
  handle: Handle,
  address: String,
  port: Int,
  ip_version: mode.IpVersion,
) -> Result(Nil, result.FtpError)

Execute an EPRT command.

pub fn feat(
  handle: Handle,
) -> Result(
  dict.Dict(String, option.Option(String)),
  result.FtpError,
)

Retrieve server features (FEAT command).

pub fn into_secure(
  handle: Handle,
  ssl_options: kafein.WrapOptions,
) -> Result(Nil, result.FtpError)

Switch to explicit secure mode (FTPS).

pub fn list(
  handle: Handle,
  pathname: option.Option(String),
) -> Result(List(String), result.FtpError)

List directory contents.

pub fn login(
  handle: Handle,
  username: String,
  password: String,
) -> Result(Nil, result.FtpError)

Log in to the FTP server.

pub fn mdtm(
  handle: Handle,
  pathname: String,
) -> Result(timestamp.Timestamp, result.FtpError)

Get the modification time of a file.

pub fn mkd(
  handle: Handle,
  path: String,
) -> Result(Nil, result.FtpError)

Create a directory.

pub fn mlsd(
  handle: Handle,
  pathname: option.Option(String),
) -> Result(List(String), result.FtpError)

Machine-readable directory listing.

pub fn mlst(
  handle: Handle,
  pathname: option.Option(String),
) -> Result(String, result.FtpError)

Execute an MLST command.

pub fn nlst(
  handle: Handle,
  pathname: option.Option(String),
) -> Result(List(String), result.FtpError)

List file names only.

pub fn noop(handle: Handle) -> Result(Nil, result.FtpError)

Send a NOOP command.

pub fn open_appe(
  handle: Handle,
  path: String,
) -> Result(stream.DataStream, result.FtpError)

Open a data channel for appending to a file.

The returned DataStream has its controlling process set to the caller, so message-based I/O (receive_next_packet_as_message) works correctly.

pub fn open_data_command(
  handle: Handle,
  command_str: String,
  expected_statuses: List(status.Status),
) -> Result(
  #(stream.DataStream, response.Response),
  result.FtpError,
)

Open a data channel for a custom command.

The returned DataStream has its controlling process set to the caller, so message-based I/O (receive_next_packet_as_message) works correctly.

pub fn open_list(
  handle: Handle,
  pathname: option.Option(String),
) -> Result(stream.DataStream, result.FtpError)

Open a data channel for a LIST directory listing.

The returned DataStream has its controlling process set to the caller, so message-based I/O (receive_next_packet_as_message) works correctly.

pub fn open_mlsd(
  handle: Handle,
  pathname: option.Option(String),
) -> Result(stream.DataStream, result.FtpError)

Open a data channel for an MLSD machine-readable listing.

The returned DataStream has its controlling process set to the caller, so message-based I/O (receive_next_packet_as_message) works correctly.

pub fn open_nlst(
  handle: Handle,
  pathname: option.Option(String),
) -> Result(stream.DataStream, result.FtpError)

Open a data channel for an NLST file name listing.

The returned DataStream has its controlling process set to the caller, so message-based I/O (receive_next_packet_as_message) works correctly.

pub fn open_retr(
  handle: Handle,
  path: String,
) -> Result(stream.DataStream, result.FtpError)

Open a data channel for downloading a file.

The returned DataStream has its controlling process set to the caller, so message-based I/O (receive_next_packet_as_message) works correctly.

pub fn open_stor(
  handle: Handle,
  path: String,
) -> Result(stream.DataStream, result.FtpError)

Open a data channel for uploading a file.

The returned DataStream has its controlling process set to the caller, so message-based I/O (receive_next_packet_as_message) works correctly.

pub fn opts(
  handle: Handle,
  option: String,
  value: option.Option(String),
) -> Result(Nil, result.FtpError)

Set a server option (OPTS command).

pub fn pwd(handle: Handle) -> Result(String, result.FtpError)

Get the current working directory.

pub fn quit(handle: Handle) -> Result(Nil, result.FtpError)

Quit the FTP session and stop the actor.

pub fn rename(
  handle: Handle,
  from: String,
  to: String,
) -> Result(Nil, result.FtpError)

Rename a file.

pub fn rest(
  handle: Handle,
  offset: Int,
) -> Result(Nil, result.FtpError)

Set the restart offset for the next transfer.

pub fn retr(
  handle: Handle,
  path: String,
  reader: fn(stream.DataStream) -> Result(Nil, result.FtpError),
) -> Result(Nil, result.FtpError)

Download a file using a callback.

pub fn rmd(
  handle: Handle,
  path: String,
) -> Result(Nil, result.FtpError)

Remove a directory.

pub fn site(
  handle: Handle,
  sub_command: String,
) -> Result(response.Response, result.FtpError)

Execute a SITE command.

pub fn size(
  handle: Handle,
  pathname: String,
) -> Result(Int, result.FtpError)

Get the size of a file in bytes.

pub fn start(
  client: gftp.FtpClient,
) -> Result(actor.Started(Handle), actor.StartError)

Start an FTP actor wrapping the given client.

The client should already be connected (via gftp.connect). All subsequent operations should go through the returned Handle.

You should never keep an active FtpClient around after starting the actor, as it would allow bypassing the chunk protection and corrupting the protocol state. Only interact with the FTP session via the returned Handle and the functions in this module.

pub fn stor(
  handle: Handle,
  path: String,
  writer: fn(stream.DataStream) -> Result(Nil, result.FtpError),
) -> Result(Nil, result.FtpError)

Upload a file using a callback.

pub fn transfer_type(
  handle: Handle,
  file_type: file_type.FileType,
) -> Result(Nil, result.FtpError)

Set the file transfer type.

pub fn welcome_message(handle: Handle) -> option.Option(String)

Get the welcome message from the FTP server.

pub fn with_active_mode(handle: Handle, timeout: Int) -> Nil

Enable active mode with the specified data connection timeout.

pub fn with_call_timeout(handle: Handle, timeout: Int) -> Handle

Set the timeout in milliseconds for actor calls. Defaults to 30000 (30 seconds).

This timeout applies to all subsequent calls made through this handle. For large file transfers via callback-based methods (retr, stor, appe), consider increasing this value since the actor call blocks until the callback completes.

let handle = ftp_actor.with_call_timeout(handle, 120_000)
pub fn with_mode(handle: Handle, mode: mode.Mode) -> Nil

Set the data transfer mode.

pub fn with_nat_workaround(handle: Handle, enabled: Bool) -> Nil

Enable or disable the NAT workaround for passive mode.

pub fn with_passive_stream_builder(
  handle: Handle,
  builder: fn(String, Int) -> Result(
    stream.DataStream,
    result.FtpError,
  ),
) -> Nil

Set a custom passive stream builder.

Search Document