Skip to content

ftp

stable

Encode RFC 959 FTP command strings and parse FTP server responses. Use with a raw TCP socket to build an FTP client without external dependencies.

use plugin ftp::{encode_user, encode_pass, encode_list, …}
22 functions Networking
/ filter jk navigate Esc clear
Functions (22)
  1. encode_user Encode a USER command string
  2. encode_pass Encode a PASS command string
  3. encode_list Encode a LIST command string
  4. encode_retr Encode a RETR (download) command string
  5. encode_stor Encode a STOR (upload) command string
  6. encode_cwd Encode a CWD (change directory) command
  7. encode_pwd Encode a PWD command string
  8. encode_quit Encode a QUIT command string
  9. encode_mkd Encode a MKD (make directory) command
  10. encode_rmd Encode a RMD (remove directory) command
  11. encode_dele Encode a DELE (delete file) command
  12. encode_type Encode a TYPE command (A or I)
  13. encode_pasv Encode a PASV command string
  14. encode_noop Encode a NOOP keepalive command
  15. encode_syst Encode a SYST command string
  16. encode_size Encode a SIZE command string
  17. encode_rnfr Encode a RNFR (rename from) command
  18. encode_rnto Encode a RNTO (rename to) command
  19. encode_cdup Encode a CDUP (parent directory) command
  20. parse_response Parse an FTP response line into code + message
  21. parse_list_entry Parse a Unix-style LIST directory entry
  22. parse_pasv_response Parse a PASV 227 response into host + port

Overview

ftp is a stateless protocol-helper toolkit: it builds the RFC 959 command strings an FTP client sends and parses the text lines an FTP server sends back. It does not open sockets or transfer data itself — you supply the networking (a raw TCP socket from the net/tcp layer) and use these functions to translate between Zolo values and the wire format. Every encode_* function returns a ready-to-send string that already includes the trailing \r\n, and every parse_* function turns one server line into a plain table you can index by field name.

The mental model is a request/response loop: encode a command, write it to the socket, read a reply line, and feed that line to parse_response (or the specialized parse_pasv_response / parse_list_entry) to interpret it. Reach for this plugin when you need a dependency-free FTP client and want full control over the connection.

Common patterns

Log in by encoding the USER/PASS handshake and reading each reply:

use plugin ftp::{encode_user, encode_pass, parse_response}

let user_cmd = encode_user("anonymous")
let pass_cmd = encode_pass("[email protected]")

// write user_cmd and pass_cmd to your socket, then parse the replies:
let reply = parse_response("230 Login successful")
if reply["code"] == 230 {
  print("logged in: {reply["message"]}")
}

Enter passive mode, then open the data connection at the advertised address:

use plugin ftp::{encode_pasv, parse_pasv_response, encode_list}

let pasv = encode_pasv()
// send pasv, read the 227 reply, then decode where to connect:
let data = parse_pasv_response("227 Entering Passive Mode (192,168,1,1,19,136)")
print("open data socket to {data["host"]}:{data["port"]}")
print(encode_list("/pub"))

Rename a remote file with the two-step RNFR / RNTO exchange:

use plugin ftp::{encode_rnfr, encode_rnto, parse_response}

print(encode_rnfr("/tmp/old.txt"))
let intermediate = parse_response("350 Ready for RNTO")
if intermediate["code"] == 350 {
  print(encode_rnto("/tmp/new.txt"))
}

Encode a USER command string

Returns the RFC 959 USER username\r\n command string ready to send over a TCP socket.

use plugin ftp::{encode_user, encode_pass}

let user_cmd = encode_user("anonymous")
let pass_cmd = encode_pass("[email protected]")
print(user_cmd)
print(pass_cmd)

Encode a PASS command string

Returns the PASS password\r\n command string.

use plugin ftp::{encode_pass}

let cmd = encode_pass("s3cr3t")
print(cmd)  // PASS s3cr3t\r\n

Encode a LIST command string

Returns LIST path\r\n, or just LIST\r\n when path is empty. Used to request a directory listing over the data connection.

use plugin ftp::{encode_list}

print(encode_list("/pub"))   // LIST /pub\r\n
print(encode_list(""))       // LIST\r\n

Encode a RETR (download) command string

Returns RETR path\r\n to request a file download.

use plugin ftp::{encode_retr}

let cmd = encode_retr("/pub/readme.txt")
print(cmd)

Encode a STOR (upload) command string

Returns STOR path\r\n to initiate a file upload.

use plugin ftp::{encode_stor}

let cmd = encode_stor("/uploads/data.csv")
print(cmd)

Encode a CWD (change directory) command

Returns CWD path\r\n to change the working directory on the server.

use plugin ftp::{encode_cwd, encode_pwd}

let cwd = encode_cwd("/home/user")
let pwd = encode_pwd()
print(cwd)
print(pwd)

Encode a PWD command string

Returns the bare PWD\r\n command to request the current working directory.

use plugin ftp::{encode_pwd}

print(encode_pwd())  // PWD\r\n

Encode a QUIT command string

Returns QUIT\r\n to close the FTP session cleanly.

use plugin ftp::{encode_quit}

print(encode_quit())  // QUIT\r\n

Encode a MKD (make directory) command

Returns MKD path\r\n to create a directory on the server.

use plugin ftp::{encode_mkd}

let cmd = encode_mkd("/backups/2026")
print(cmd)

Encode a RMD (remove directory) command

Returns RMD path\r\n to remove a directory.

use plugin ftp::{encode_rmd}

print(encode_rmd("/tmp/old"))

Encode a DELE (delete file) command

Returns DELE path\r\n to delete a file from the server.

use plugin ftp::{encode_dele}

print(encode_dele("/tmp/stale.log"))

Encode a TYPE command (A or I)

Returns TYPE mode\r\n. Pass "I" for binary (image) mode or "A" for ASCII mode.

use plugin ftp::{encode_type}

print(encode_type("I"))  // TYPE I\r\n  (binary)
print(encode_type("A"))  // TYPE A\r\n  (ascii)

Encode a PASV command string

Returns PASV\r\n to request passive mode. Parse the server reply with parse_pasv_response.

use plugin ftp::{encode_pasv}

print(encode_pasv())  // PASV\r\n

Encode a NOOP keepalive command

Returns NOOP\r\n. Send periodically to keep an idle connection alive.

use plugin ftp::{encode_noop}

print(encode_noop())  // NOOP\r\n

Encode a SYST command string

Returns SYST\r\n to query the server operating system type.

use plugin ftp::{encode_syst}

print(encode_syst())  // SYST\r\n

Encode a SIZE command string

Returns SIZE path\r\n to query the byte size of a remote file.

use plugin ftp::{encode_size}

print(encode_size("/data/archive.tar.gz"))

Encode a RNFR (rename from) command

Returns RNFR path\r\n, the first half of a rename. It names the file to be renamed; the server replies 350 and then expects RNTO.

use plugin ftp::{encode_rnfr}

print(encode_rnfr("/tmp/old.txt"))  // RNFR /tmp/old.txt\r\n

Encode a RNTO (rename to) command

Returns RNTO path\r\n, the second half of a rename. Send it immediately after encode_rnfr to give the file its new name.

use plugin ftp::{encode_rnfr, encode_rnto}

let from = encode_rnfr("/tmp/old.txt")
let to   = encode_rnto("/tmp/new.txt")
print(from)
print(to)

Encode a CDUP (parent directory) command

Returns CDUP\r\n to navigate to the parent directory.

use plugin ftp::{encode_cdup}

print(encode_cdup())  // CDUP\r\n

Parse an FTP response line into code + message

Parses a single FTP response line into {code, message, is_multiline}. code is the 3-digit integer. is_multiline is true when the code is followed by - (RFC 959 multi-line response).

use plugin ftp::{parse_response}

let r = parse_response("220 FTP server ready")
print(r["code"])     // 220
print(r["message"])  // FTP server ready

let m = parse_response("230-Login successful")
print(m["is_multiline"])  // true

Branch on the response code to decide what to do next:

use plugin ftp::{parse_response}

let r = parse_response("550 Requested action not taken")
if r["code"] >= 500 {
  print("error: {r["message"]}")
} else {
  print("ok: {r["message"]}")
}

Parse a Unix-style LIST directory entry

Parses a single Unix-style LIST directory line into {permissions, size, date, name}.

use plugin ftp::{parse_list_entry}

let entry = parse_list_entry("-rw-r--r--  1 user group  4096 Jan  1 12:00 readme.txt")
print(entry["name"])   // readme.txt
print(entry["size"])   // 4096

Parse a PASV 227 response into host + port

Parses the 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2) response into {host, port}. The port is computed as p1 * 256 + p2.

use plugin ftp::{encode_pasv, parse_pasv_response}

// After sending encode_pasv() and receiving the server reply:
let info = parse_pasv_response("227 Entering Passive Mode (192,168,1,1,19,136)")
print(info["host"])  // 192.168.1.1
print(info["port"])  // 5000
enespt-br