DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ftp.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ftp.h
6 * @brief FTP client wire codec (RFC 959 + RFC 2428 + RFC 3659), DETWS_ENABLE_FTP.
7 *
8 * The pure protocol layer of an FTP client: build control-channel commands, parse the
9 * (possibly multiline) 3-digit reply, and decode the PASV / EPSV data-channel address the
10 * server hands back. A device can then push/pull files - e.g. drip a `.nc` program to a CNC
11 * controller's FTP program store (Fanuc / Haas / Mazak / Heidenhain all expose one), fetch a
12 * config, or archive a log. No heap, no stdlib; the two sockets (control + data) are the
13 * application's - this is only the bytes on the wire, so it is fully host-testable.
14 *
15 * FTP replies (RFC 959 sec 4.2): a single line is `NNN<SP>text<CRLF>`; a multiline reply is
16 * `NNN-text<CRLF>` continuation lines `... <CRLF>` and a final `NNN<SP>text<CRLF>` (the same
17 * code followed by a space marks the end). Passive mode: `227 ...(h1,h2,h3,h4,p1,p2)` gives the
18 * data address (ip = h1.h2.h3.h4, port = p1*256+p2); extended passive `229 ...(|||port|)`
19 * (RFC 2428) gives just the port on the control host.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef DETERMINISTICESPASYNCWEBSERVER_FTP_H
26#define DETERMINISTICESPASYNCWEBSERVER_FTP_H
27
28#include "ServerConfig.h"
29
30#if DETWS_ENABLE_FTP
31
32#include <stddef.h>
33#include <stdint.h>
34
35/**
36 * @brief Build a control command line: `VERB<CRLF>` or `VERB<SP>ARG<CRLF>`.
37 *
38 * Covers every simple verb (USER, PASS, TYPE, CWD, CDUP, PASV, EPSV, RETR, STOR, APPE, LIST,
39 * NLST, DELE, MKD, RMD, PWD, SIZE, REST, RNFR, RNTO, SYST, FEAT, NOOP, QUIT, ...). The verb and
40 * arg are copied verbatim; the caller supplies well-formed values (no embedded CR/LF).
41 *
42 * @param arg the argument, or nullptr / "" for a bare verb (no trailing space).
43 * @return bytes written (excluding the NUL terminator), or 0 on overflow / bad input.
44 */
45size_t ftp_build_command(char *buf, size_t cap, const char *verb, const char *arg);
46
47/**
48 * @brief Build an active-mode `PORT h1,h2,h3,h4,p1,p2<CRLF>` from an IPv4 address + port.
49 * @return bytes written (excluding NUL), or 0 on overflow.
50 */
51size_t ftp_build_port(char *buf, size_t cap, const uint8_t ip[4], uint16_t port);
52
53/**
54 * @brief Build an extended active-mode `EPRT<SP>|net-prt|net-addr|port|<CRLF>` (RFC 2428).
55 * @param ip_str dotted-decimal IPv4 or RFC 4291 IPv6 text (copied verbatim).
56 * @param ipv6 false => net-prt 1 (IPv4), true => net-prt 2 (IPv6).
57 * @return bytes written (excluding NUL), or 0 on overflow.
58 */
59size_t ftp_build_eprt(char *buf, size_t cap, const char *ip_str, bool ipv6, uint16_t port);
60
61/**
62 * @brief Detect and measure a complete control-channel reply at the head of @p buf.
63 *
64 * Handles single-line and multiline replies. On a complete reply, @p code receives the 3-digit
65 * reply code and @p consumed the byte count the reply occupied (so the caller can advance past it
66 * and keep any pipelined bytes).
67 *
68 * @return true if a complete reply is present; false if the buffer holds only a partial reply
69 * (need more bytes) or a malformed head (then @p code / @p consumed are unspecified).
70 */
71bool ftp_parse_reply(const char *buf, size_t len, int *code, size_t *consumed);
72
73/**
74 * @brief Decode the data address from a `227` passive-mode reply.
75 *
76 * Reads the `(h1,h2,h3,h4,p1,p2)` tuple anywhere in the reply text; ip = h1.h2.h3.h4,
77 * port = p1*256 + p2. Each field must be 0-255.
78 *
79 * @return true on a well-formed tuple, false otherwise (then @p ip / @p port are unspecified).
80 */
81bool ftp_parse_pasv(const char *buf, size_t len, uint8_t ip[4], uint16_t *port);
82
83/**
84 * @brief Decode the port from a `229` extended-passive reply `(<d><d><d>port<d>)` (RFC 2428).
85 *
86 * The data connection uses the control connection's host; only the port is carried.
87 *
88 * @return true on a well-formed reply, false otherwise (then @p port is unspecified).
89 */
90bool ftp_parse_epsv(const char *buf, size_t len, uint16_t *port);
91
92/** @brief First digit of a reply code (1 preliminary, 2 complete, 3 intermediate, 4/5 error), or 0. */
93static inline int ftp_reply_class(int code)
94{
95 return (code >= 100 && code <= 599) ? code / 100 : 0;
96}
97
98/** @brief A 2xx positive-completion reply. */
99static inline bool ftp_reply_ok(int code)
100{
101 return ftp_reply_class(code) == 2;
102}
103
104#endif // DETWS_ENABLE_FTP
105
106#endif // DETERMINISTICESPASYNCWEBSERVER_FTP_H
User-facing configuration for DeterministicESPAsyncWebServer.