15static const size_t FTP_SENT = (size_t)-1;
18static size_t pc_ftp_emit(
char *buf,
size_t cap,
size_t n,
const char *s,
size_t slen)
23 if (n == FTP_SENT || n > cap || slen > cap - n)
30 memcpy(buf + n, s, slen);
35static size_t pc_ftp_emit_uint(
char *buf,
size_t cap,
size_t n,
unsigned v)
51 rev[ri++] = (char)(
'0' + (v % 10));
55 if ((
size_t)ri > cap - n)
59 for (
int k = 0; k < ri; k++)
61 buf[n + k] = rev[ri - 1 - k];
63 return n + (size_t)ri;
67static size_t pc_ftp_finish(
char *buf,
size_t cap,
size_t n)
69 if (n == FTP_SENT || n >= cap)
77size_t pc_ftp_build_command(
char *buf,
size_t cap,
const char *verb,
const char *arg)
79 if (!buf || !verb || !verb[0])
84 n = pc_ftp_emit(buf, cap, n, verb, strnlen(verb, cap));
87 n = pc_ftp_emit(buf, cap, n,
" ", 1);
88 n = pc_ftp_emit(buf, cap, n, arg, strnlen(arg, cap));
90 n = pc_ftp_emit(buf, cap, n,
"\r\n", 2);
91 return pc_ftp_finish(buf, cap, n);
94size_t pc_ftp_build_port(
char *buf,
size_t cap,
const uint8_t ip[4], uint16_t port)
101 n = pc_ftp_emit(buf, cap, n,
"PORT ", 5);
102 for (
int i = 0; i < 4; i++)
104 n = pc_ftp_emit_uint(buf, cap, n, ip[i]);
105 n = pc_ftp_emit(buf, cap, n,
",", 1);
107 n = pc_ftp_emit_uint(buf, cap, n, (
unsigned)(port >> 8));
108 n = pc_ftp_emit(buf, cap, n,
",", 1);
109 n = pc_ftp_emit_uint(buf, cap, n, (
unsigned)(port & 0xFF));
110 n = pc_ftp_emit(buf, cap, n,
"\r\n", 2);
111 return pc_ftp_finish(buf, cap, n);
114size_t pc_ftp_build_eprt(
char *buf,
size_t cap,
const char *ip_str,
bool ipv6, uint16_t port)
116 if (!buf || !ip_str || !ip_str[0])
121 n = pc_ftp_emit(buf, cap, n,
"EPRT |", 6);
122 n = pc_ftp_emit(buf, cap, n, ipv6 ?
"2" :
"1", 1);
123 n = pc_ftp_emit(buf, cap, n,
"|", 1);
124 n = pc_ftp_emit(buf, cap, n, ip_str, strnlen(ip_str, cap));
125 n = pc_ftp_emit(buf, cap, n,
"|", 1);
126 n = pc_ftp_emit_uint(buf, cap, n, port);
127 n = pc_ftp_emit(buf, cap, n,
"|\r\n", 3);
128 return pc_ftp_finish(buf, cap, n);
131static bool pc_ftp_is_3digit(
const char *p)
133 return p[0] >=
'0' && p[0] <=
'9' && p[1] >=
'0' && p[1] <=
'9' && p[2] >=
'0' && p[2] <=
'9';
136static int pc_ftp_code3(
const char *p)
138 return (p[0] -
'0') * 100 + (p[1] -
'0') * 10 + (p[2] -
'0');
142static size_t pc_ftp_line_end(
const char *buf,
size_t len,
size_t start)
144 for (
size_t i = start; i < len; i++)
154bool pc_ftp_parse_reply(
const char *buf,
size_t len,
int *code,
size_t *consumed)
156 if (!buf || len < 4 || !pc_ftp_is_3digit(buf))
160 int first = pc_ftp_code3(buf);
165 size_t eol = pc_ftp_line_end(buf, len, 0);
180 size_t pos = pc_ftp_line_end(buf, len, 0);
187 if (len - pos >= 4 && pc_ftp_is_3digit(buf + pos) && pc_ftp_code3(buf + pos) == first && buf[pos + 3] ==
' ')
189 size_t eol = pc_ftp_line_end(buf, len, pos);
198 size_t eol = pc_ftp_line_end(buf, len, pos);
208bool pc_ftp_parse_pasv(
const char *buf,
size_t len, uint8_t ip[4], uint16_t *port)
210 if (!buf || !ip || !port)
215 while (i < len && buf[i] !=
'(')
226 for (
int ni = 0; ni < 6; ni++)
228 if (i >= len || buf[i] <
'0' || buf[i] >
'9')
233 while (i < len && buf[i] >=
'0' && buf[i] <=
'9')
235 v = v * 10 + (unsigned)(buf[i] -
'0');
245 if (i >= len || buf[i] !=
',')
252 ip[0] = (uint8_t)nums[0];
253 ip[1] = (uint8_t)nums[1];
254 ip[2] = (uint8_t)nums[2];
255 ip[3] = (uint8_t)nums[3];
256 *port = (uint16_t)(nums[4] * 256 + nums[5]);
260bool pc_ftp_parse_epsv(
const char *buf,
size_t len, uint16_t *port)
267 while (i < len && buf[i] !=
'(')
284 while (i < len && seen < 3)
297 if (i >= len || buf[i] <
'0' || buf[i] >
'9')
302 while (i < len && buf[i] >=
'0' && buf[i] <=
'9')
304 v = v * 10 + (unsigned)(buf[i] -
'0');
FTP client wire codec (RFC 959 + RFC 2428 + RFC 3659), PC_ENABLE_FTP.