xref: /third_party/ffmpeg/libavformat/ftp.c (revision cabdff1a)
1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * This file is part of FFmpeg.
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
8cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14cabdff1aSopenharmony_ci * Lesser General Public License for more details.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
17cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
18cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19cabdff1aSopenharmony_ci */
20cabdff1aSopenharmony_ci
21cabdff1aSopenharmony_ci#include <string.h>
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
24cabdff1aSopenharmony_ci#include "libavutil/internal.h"
25cabdff1aSopenharmony_ci#include "libavutil/parseutils.h"
26cabdff1aSopenharmony_ci#include "avformat.h"
27cabdff1aSopenharmony_ci#include "internal.h"
28cabdff1aSopenharmony_ci#include "url.h"
29cabdff1aSopenharmony_ci#include "urldecode.h"
30cabdff1aSopenharmony_ci#include "libavutil/opt.h"
31cabdff1aSopenharmony_ci#include "libavutil/bprint.h"
32cabdff1aSopenharmony_ci
33cabdff1aSopenharmony_ci#define CONTROL_BUFFER_SIZE 1024
34cabdff1aSopenharmony_ci#define DIR_BUFFER_SIZE 4096
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_citypedef enum {
37cabdff1aSopenharmony_ci    UNKNOWN,
38cabdff1aSopenharmony_ci    READY,
39cabdff1aSopenharmony_ci    DOWNLOADING,
40cabdff1aSopenharmony_ci    UPLOADING,
41cabdff1aSopenharmony_ci    LISTING_DIR,
42cabdff1aSopenharmony_ci    DISCONNECTED,
43cabdff1aSopenharmony_ci    ENDOFFILE,
44cabdff1aSopenharmony_ci} FTPState;
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_citypedef enum {
47cabdff1aSopenharmony_ci    UNKNOWN_METHOD,
48cabdff1aSopenharmony_ci    NLST,
49cabdff1aSopenharmony_ci    MLSD
50cabdff1aSopenharmony_ci} FTPListingMethod;
51cabdff1aSopenharmony_ci
52cabdff1aSopenharmony_citypedef struct {
53cabdff1aSopenharmony_ci    const AVClass *class;
54cabdff1aSopenharmony_ci    URLContext *conn_control;                    /**< Control connection */
55cabdff1aSopenharmony_ci    URLContext *conn_data;                       /**< Data connection, NULL when not connected */
56cabdff1aSopenharmony_ci    uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
57cabdff1aSopenharmony_ci    uint8_t *control_buf_ptr, *control_buf_end;
58cabdff1aSopenharmony_ci    int server_data_port;                        /**< Data connection port opened by server, -1 on error. */
59cabdff1aSopenharmony_ci    int server_control_port;                     /**< Control connection port, default is 21 */
60cabdff1aSopenharmony_ci    char *hostname;                              /**< Server address. */
61cabdff1aSopenharmony_ci    char *user;                                  /**< Server user */
62cabdff1aSopenharmony_ci    char *password;                              /**< Server user's password */
63cabdff1aSopenharmony_ci    char *path;                                  /**< Path to resource on server. */
64cabdff1aSopenharmony_ci    int64_t filesize;                            /**< Size of file on server, -1 on error. */
65cabdff1aSopenharmony_ci    int64_t position;                            /**< Current position, calculated. */
66cabdff1aSopenharmony_ci    int rw_timeout;                              /**< Network timeout. */
67cabdff1aSopenharmony_ci    const char *anonymous_password;              /**< Password to be used for anonymous user. An email should be used. */
68cabdff1aSopenharmony_ci    int write_seekable;                          /**< Control seekability, 0 = disable, 1 = enable. */
69cabdff1aSopenharmony_ci    FTPState state;                              /**< State of data connection */
70cabdff1aSopenharmony_ci    FTPListingMethod listing_method;             /**< Called listing method */
71cabdff1aSopenharmony_ci    char *features;                              /**< List of server's features represented as raw response */
72cabdff1aSopenharmony_ci    char *dir_buffer;
73cabdff1aSopenharmony_ci    size_t dir_buffer_size;
74cabdff1aSopenharmony_ci    size_t dir_buffer_offset;
75cabdff1aSopenharmony_ci    int utf8;
76cabdff1aSopenharmony_ci    const char *option_user;                     /**< User to be used if none given in the URL */
77cabdff1aSopenharmony_ci    const char *option_password;                 /**< Password to be used if none given in the URL */
78cabdff1aSopenharmony_ci} FTPContext;
79cabdff1aSopenharmony_ci
80cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(FTPContext, x)
81cabdff1aSopenharmony_ci#define D AV_OPT_FLAG_DECODING_PARAM
82cabdff1aSopenharmony_ci#define E AV_OPT_FLAG_ENCODING_PARAM
83cabdff1aSopenharmony_cistatic const AVOption options[] = {
84cabdff1aSopenharmony_ci    {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
85cabdff1aSopenharmony_ci    {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
86cabdff1aSopenharmony_ci    {"ftp-anonymous-password", "password for anonymous login. E-mail address should be used.", OFFSET(anonymous_password), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
87cabdff1aSopenharmony_ci    {"ftp-user", "user for FTP login. Overridden by whatever is in the URL.", OFFSET(option_user), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
88cabdff1aSopenharmony_ci    {"ftp-password", "password for FTP login. Overridden by whatever is in the URL.", OFFSET(option_password), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
89cabdff1aSopenharmony_ci    {NULL}
90cabdff1aSopenharmony_ci};
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_cistatic const AVClass ftp_context_class = {
93cabdff1aSopenharmony_ci    .class_name     = "ftp",
94cabdff1aSopenharmony_ci    .item_name      = av_default_item_name,
95cabdff1aSopenharmony_ci    .option         = options,
96cabdff1aSopenharmony_ci    .version        = LIBAVUTIL_VERSION_INT,
97cabdff1aSopenharmony_ci};
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_cistatic int ftp_close(URLContext *h);
100cabdff1aSopenharmony_ci
101cabdff1aSopenharmony_cistatic int ftp_getc(FTPContext *s)
102cabdff1aSopenharmony_ci{
103cabdff1aSopenharmony_ci    int len;
104cabdff1aSopenharmony_ci    if (s->control_buf_ptr >= s->control_buf_end) {
105cabdff1aSopenharmony_ci        len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
106cabdff1aSopenharmony_ci        if (len < 0) {
107cabdff1aSopenharmony_ci            return len;
108cabdff1aSopenharmony_ci        } else if (!len) {
109cabdff1aSopenharmony_ci            return -1;
110cabdff1aSopenharmony_ci        } else {
111cabdff1aSopenharmony_ci            s->control_buf_ptr = s->control_buffer;
112cabdff1aSopenharmony_ci            s->control_buf_end = s->control_buffer + len;
113cabdff1aSopenharmony_ci        }
114cabdff1aSopenharmony_ci    }
115cabdff1aSopenharmony_ci    return *s->control_buf_ptr++;
116cabdff1aSopenharmony_ci}
117cabdff1aSopenharmony_ci
118cabdff1aSopenharmony_cistatic int ftp_get_line(FTPContext *s, char *line, int line_size)
119cabdff1aSopenharmony_ci{
120cabdff1aSopenharmony_ci    int ch;
121cabdff1aSopenharmony_ci    char *q = line;
122cabdff1aSopenharmony_ci
123cabdff1aSopenharmony_ci    for (;;) {
124cabdff1aSopenharmony_ci        ch = ftp_getc(s);
125cabdff1aSopenharmony_ci        if (ch < 0) {
126cabdff1aSopenharmony_ci            return ch;
127cabdff1aSopenharmony_ci        }
128cabdff1aSopenharmony_ci        if (ch == '\n') {
129cabdff1aSopenharmony_ci            /* process line */
130cabdff1aSopenharmony_ci            if (q > line && q[-1] == '\r')
131cabdff1aSopenharmony_ci                q--;
132cabdff1aSopenharmony_ci            *q = '\0';
133cabdff1aSopenharmony_ci            return 0;
134cabdff1aSopenharmony_ci        } else {
135cabdff1aSopenharmony_ci            if ((q - line) < line_size - 1)
136cabdff1aSopenharmony_ci                *q++ = ch;
137cabdff1aSopenharmony_ci        }
138cabdff1aSopenharmony_ci    }
139cabdff1aSopenharmony_ci}
140cabdff1aSopenharmony_ci
141cabdff1aSopenharmony_ci/*
142cabdff1aSopenharmony_ci * This routine returns ftp server response code.
143cabdff1aSopenharmony_ci * Server may send more than one response for a certain command.
144cabdff1aSopenharmony_ci * First expected code is returned.
145cabdff1aSopenharmony_ci */
146cabdff1aSopenharmony_cistatic int ftp_status(FTPContext *s, char **line, const int response_codes[])
147cabdff1aSopenharmony_ci{
148cabdff1aSopenharmony_ci    int err, i, dash = 0, result = 0, code_found = 0, linesize;
149cabdff1aSopenharmony_ci    char buf[CONTROL_BUFFER_SIZE];
150cabdff1aSopenharmony_ci    AVBPrint line_buffer;
151cabdff1aSopenharmony_ci
152cabdff1aSopenharmony_ci    if (line)
153cabdff1aSopenharmony_ci        av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
154cabdff1aSopenharmony_ci
155cabdff1aSopenharmony_ci    while (!code_found || dash) {
156cabdff1aSopenharmony_ci        if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
157cabdff1aSopenharmony_ci            if (line)
158cabdff1aSopenharmony_ci                av_bprint_finalize(&line_buffer, NULL);
159cabdff1aSopenharmony_ci            return err;
160cabdff1aSopenharmony_ci        }
161cabdff1aSopenharmony_ci
162cabdff1aSopenharmony_ci        av_log(s, AV_LOG_DEBUG, "%s\n", buf);
163cabdff1aSopenharmony_ci
164cabdff1aSopenharmony_ci        linesize = strlen(buf);
165cabdff1aSopenharmony_ci        err = 0;
166cabdff1aSopenharmony_ci        if (linesize >= 3) {
167cabdff1aSopenharmony_ci            for (i = 0; i < 3; ++i) {
168cabdff1aSopenharmony_ci                if (buf[i] < '0' || buf[i] > '9') {
169cabdff1aSopenharmony_ci                    err = 0;
170cabdff1aSopenharmony_ci                    break;
171cabdff1aSopenharmony_ci                }
172cabdff1aSopenharmony_ci                err *= 10;
173cabdff1aSopenharmony_ci                err += buf[i] - '0';
174cabdff1aSopenharmony_ci            }
175cabdff1aSopenharmony_ci        }
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_ci        if (!code_found) {
178cabdff1aSopenharmony_ci            if (err >= 500) {
179cabdff1aSopenharmony_ci                code_found = 1;
180cabdff1aSopenharmony_ci                result = err;
181cabdff1aSopenharmony_ci            } else
182cabdff1aSopenharmony_ci                for (i = 0; response_codes[i]; ++i) {
183cabdff1aSopenharmony_ci                    if (err == response_codes[i]) {
184cabdff1aSopenharmony_ci                        code_found = 1;
185cabdff1aSopenharmony_ci                        result = err;
186cabdff1aSopenharmony_ci                        break;
187cabdff1aSopenharmony_ci                    }
188cabdff1aSopenharmony_ci                }
189cabdff1aSopenharmony_ci        }
190cabdff1aSopenharmony_ci        if (code_found) {
191cabdff1aSopenharmony_ci            if (line)
192cabdff1aSopenharmony_ci                av_bprintf(&line_buffer, "%s\r\n", buf);
193cabdff1aSopenharmony_ci            if (linesize >= 4) {
194cabdff1aSopenharmony_ci                if (!dash && buf[3] == '-')
195cabdff1aSopenharmony_ci                    dash = err;
196cabdff1aSopenharmony_ci                else if (err == dash && buf[3] == ' ')
197cabdff1aSopenharmony_ci                    dash = 0;
198cabdff1aSopenharmony_ci            }
199cabdff1aSopenharmony_ci        }
200cabdff1aSopenharmony_ci    }
201cabdff1aSopenharmony_ci
202cabdff1aSopenharmony_ci    if (line)
203cabdff1aSopenharmony_ci        av_bprint_finalize(&line_buffer, line);
204cabdff1aSopenharmony_ci    return result;
205cabdff1aSopenharmony_ci}
206cabdff1aSopenharmony_ci
207cabdff1aSopenharmony_cistatic int ftp_send_command(FTPContext *s, const char *command,
208cabdff1aSopenharmony_ci                            const int response_codes[], char **response)
209cabdff1aSopenharmony_ci{
210cabdff1aSopenharmony_ci    int err;
211cabdff1aSopenharmony_ci
212cabdff1aSopenharmony_ci    ff_dlog(s, "%s", command);
213cabdff1aSopenharmony_ci
214cabdff1aSopenharmony_ci    if (response)
215cabdff1aSopenharmony_ci        *response = NULL;
216cabdff1aSopenharmony_ci
217cabdff1aSopenharmony_ci    if (!s->conn_control)
218cabdff1aSopenharmony_ci        return AVERROR(EIO);
219cabdff1aSopenharmony_ci
220cabdff1aSopenharmony_ci    if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
221cabdff1aSopenharmony_ci        return err;
222cabdff1aSopenharmony_ci    if (!err)
223cabdff1aSopenharmony_ci        return -1;
224cabdff1aSopenharmony_ci
225cabdff1aSopenharmony_ci    /* return status */
226cabdff1aSopenharmony_ci    if (response_codes) {
227cabdff1aSopenharmony_ci        return ftp_status(s, response, response_codes);
228cabdff1aSopenharmony_ci    }
229cabdff1aSopenharmony_ci    return 0;
230cabdff1aSopenharmony_ci}
231cabdff1aSopenharmony_ci
232cabdff1aSopenharmony_cistatic void ftp_close_data_connection(FTPContext *s)
233cabdff1aSopenharmony_ci{
234cabdff1aSopenharmony_ci    ffurl_closep(&s->conn_data);
235cabdff1aSopenharmony_ci    s->state = DISCONNECTED;
236cabdff1aSopenharmony_ci}
237cabdff1aSopenharmony_ci
238cabdff1aSopenharmony_cistatic void ftp_close_both_connections(FTPContext *s)
239cabdff1aSopenharmony_ci{
240cabdff1aSopenharmony_ci    ffurl_closep(&s->conn_control);
241cabdff1aSopenharmony_ci    ftp_close_data_connection(s);
242cabdff1aSopenharmony_ci}
243cabdff1aSopenharmony_ci
244cabdff1aSopenharmony_cistatic int ftp_auth(FTPContext *s)
245cabdff1aSopenharmony_ci{
246cabdff1aSopenharmony_ci    char buf[CONTROL_BUFFER_SIZE];
247cabdff1aSopenharmony_ci    int err;
248cabdff1aSopenharmony_ci    static const int user_codes[] = {331, 230, 0};
249cabdff1aSopenharmony_ci    static const int pass_codes[] = {230, 0};
250cabdff1aSopenharmony_ci
251cabdff1aSopenharmony_ci    if (strpbrk(s->user, "\r\n"))
252cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
253cabdff1aSopenharmony_ci    err = snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
254cabdff1aSopenharmony_ci    if (err >= sizeof(buf))
255cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
256cabdff1aSopenharmony_ci
257cabdff1aSopenharmony_ci    err = ftp_send_command(s, buf, user_codes, NULL);
258cabdff1aSopenharmony_ci    if (err == 331) {
259cabdff1aSopenharmony_ci        if (s->password) {
260cabdff1aSopenharmony_ci            if (strpbrk(s->password, "\r\n"))
261cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
262cabdff1aSopenharmony_ci            err = snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
263cabdff1aSopenharmony_ci            if (err >= sizeof(buf))
264cabdff1aSopenharmony_ci                return AVERROR(ENOSYS);
265cabdff1aSopenharmony_ci
266cabdff1aSopenharmony_ci            err = ftp_send_command(s, buf, pass_codes, NULL);
267cabdff1aSopenharmony_ci        } else
268cabdff1aSopenharmony_ci            return AVERROR(EACCES);
269cabdff1aSopenharmony_ci    }
270cabdff1aSopenharmony_ci    if (err != 230)
271cabdff1aSopenharmony_ci        return AVERROR(EACCES);
272cabdff1aSopenharmony_ci
273cabdff1aSopenharmony_ci    return 0;
274cabdff1aSopenharmony_ci}
275cabdff1aSopenharmony_ci
276cabdff1aSopenharmony_cistatic int ftp_passive_mode_epsv(FTPContext *s)
277cabdff1aSopenharmony_ci{
278cabdff1aSopenharmony_ci    char *res = NULL, *start = NULL, *end = NULL;
279cabdff1aSopenharmony_ci    int i;
280cabdff1aSopenharmony_ci    static const char d = '|';
281cabdff1aSopenharmony_ci    static const char *command = "EPSV\r\n";
282cabdff1aSopenharmony_ci    static const int epsv_codes[] = {229, 0};
283cabdff1aSopenharmony_ci
284cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
285cabdff1aSopenharmony_ci        goto fail;
286cabdff1aSopenharmony_ci
287cabdff1aSopenharmony_ci    for (i = 0; res[i]; ++i) {
288cabdff1aSopenharmony_ci        if (res[i] == '(') {
289cabdff1aSopenharmony_ci            start = res + i + 1;
290cabdff1aSopenharmony_ci        } else if (res[i] == ')') {
291cabdff1aSopenharmony_ci            end = res + i;
292cabdff1aSopenharmony_ci            break;
293cabdff1aSopenharmony_ci        }
294cabdff1aSopenharmony_ci    }
295cabdff1aSopenharmony_ci    if (!start || !end)
296cabdff1aSopenharmony_ci        goto fail;
297cabdff1aSopenharmony_ci
298cabdff1aSopenharmony_ci    *end = '\0';
299cabdff1aSopenharmony_ci    if (strlen(start) < 5)
300cabdff1aSopenharmony_ci        goto fail;
301cabdff1aSopenharmony_ci    if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
302cabdff1aSopenharmony_ci        goto fail;
303cabdff1aSopenharmony_ci    start += 3;
304cabdff1aSopenharmony_ci    end[-1] = '\0';
305cabdff1aSopenharmony_ci
306cabdff1aSopenharmony_ci    s->server_data_port = atoi(start);
307cabdff1aSopenharmony_ci    ff_dlog(s, "Server data port: %d\n", s->server_data_port);
308cabdff1aSopenharmony_ci
309cabdff1aSopenharmony_ci    av_free(res);
310cabdff1aSopenharmony_ci    return 0;
311cabdff1aSopenharmony_ci
312cabdff1aSopenharmony_ci  fail:
313cabdff1aSopenharmony_ci    av_free(res);
314cabdff1aSopenharmony_ci    s->server_data_port = -1;
315cabdff1aSopenharmony_ci    return AVERROR(ENOSYS);
316cabdff1aSopenharmony_ci}
317cabdff1aSopenharmony_ci
318cabdff1aSopenharmony_cistatic int ftp_passive_mode(FTPContext *s)
319cabdff1aSopenharmony_ci{
320cabdff1aSopenharmony_ci    char *res = NULL, *start = NULL, *end = NULL;
321cabdff1aSopenharmony_ci    int i;
322cabdff1aSopenharmony_ci    static const char *command = "PASV\r\n";
323cabdff1aSopenharmony_ci    static const int pasv_codes[] = {227, 0};
324cabdff1aSopenharmony_ci
325cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
326cabdff1aSopenharmony_ci        goto fail;
327cabdff1aSopenharmony_ci
328cabdff1aSopenharmony_ci    for (i = 0; res[i]; ++i) {
329cabdff1aSopenharmony_ci        if (res[i] == '(') {
330cabdff1aSopenharmony_ci            start = res + i + 1;
331cabdff1aSopenharmony_ci        } else if (res[i] == ')') {
332cabdff1aSopenharmony_ci            end = res + i;
333cabdff1aSopenharmony_ci            break;
334cabdff1aSopenharmony_ci        }
335cabdff1aSopenharmony_ci    }
336cabdff1aSopenharmony_ci    if (!start || !end)
337cabdff1aSopenharmony_ci        goto fail;
338cabdff1aSopenharmony_ci
339cabdff1aSopenharmony_ci    *end  = '\0';
340cabdff1aSopenharmony_ci    /* skip ip */
341cabdff1aSopenharmony_ci    if (!av_strtok(start, ",", &end)) goto fail;
342cabdff1aSopenharmony_ci    if (!av_strtok(NULL, ",", &end)) goto fail;
343cabdff1aSopenharmony_ci    if (!av_strtok(NULL, ",", &end)) goto fail;
344cabdff1aSopenharmony_ci    if (!av_strtok(NULL, ",", &end)) goto fail;
345cabdff1aSopenharmony_ci
346cabdff1aSopenharmony_ci    /* parse port number */
347cabdff1aSopenharmony_ci    start = av_strtok(NULL, ",", &end);
348cabdff1aSopenharmony_ci    if (!start) goto fail;
349cabdff1aSopenharmony_ci    s->server_data_port = atoi(start) * 256;
350cabdff1aSopenharmony_ci    start = av_strtok(NULL, ",", &end);
351cabdff1aSopenharmony_ci    if (!start) goto fail;
352cabdff1aSopenharmony_ci    s->server_data_port += atoi(start);
353cabdff1aSopenharmony_ci    ff_dlog(s, "Server data port: %d\n", s->server_data_port);
354cabdff1aSopenharmony_ci
355cabdff1aSopenharmony_ci    av_free(res);
356cabdff1aSopenharmony_ci    return 0;
357cabdff1aSopenharmony_ci
358cabdff1aSopenharmony_ci  fail:
359cabdff1aSopenharmony_ci    av_free(res);
360cabdff1aSopenharmony_ci    s->server_data_port = -1;
361cabdff1aSopenharmony_ci    return AVERROR(EIO);
362cabdff1aSopenharmony_ci}
363cabdff1aSopenharmony_ci
364cabdff1aSopenharmony_cistatic int ftp_current_dir(FTPContext *s)
365cabdff1aSopenharmony_ci{
366cabdff1aSopenharmony_ci    char *res = NULL, *start = NULL, *end = NULL;
367cabdff1aSopenharmony_ci    int i;
368cabdff1aSopenharmony_ci    static const char *command = "PWD\r\n";
369cabdff1aSopenharmony_ci    static const int pwd_codes[] = {257, 0};
370cabdff1aSopenharmony_ci
371cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
372cabdff1aSopenharmony_ci        goto fail;
373cabdff1aSopenharmony_ci
374cabdff1aSopenharmony_ci    for (i = 0; res[i]; ++i) {
375cabdff1aSopenharmony_ci        if (res[i] == '"') {
376cabdff1aSopenharmony_ci            if (!start) {
377cabdff1aSopenharmony_ci                start = res + i + 1;
378cabdff1aSopenharmony_ci                continue;
379cabdff1aSopenharmony_ci            }
380cabdff1aSopenharmony_ci            end = res + i;
381cabdff1aSopenharmony_ci            break;
382cabdff1aSopenharmony_ci        }
383cabdff1aSopenharmony_ci    }
384cabdff1aSopenharmony_ci
385cabdff1aSopenharmony_ci    if (!end)
386cabdff1aSopenharmony_ci        goto fail;
387cabdff1aSopenharmony_ci
388cabdff1aSopenharmony_ci    *end = '\0';
389cabdff1aSopenharmony_ci    s->path = av_strdup(start);
390cabdff1aSopenharmony_ci
391cabdff1aSopenharmony_ci    av_free(res);
392cabdff1aSopenharmony_ci
393cabdff1aSopenharmony_ci    if (!s->path)
394cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
395cabdff1aSopenharmony_ci    return 0;
396cabdff1aSopenharmony_ci
397cabdff1aSopenharmony_ci  fail:
398cabdff1aSopenharmony_ci    av_free(res);
399cabdff1aSopenharmony_ci    return AVERROR(EIO);
400cabdff1aSopenharmony_ci}
401cabdff1aSopenharmony_ci
402cabdff1aSopenharmony_cistatic int ftp_file_size(FTPContext *s)
403cabdff1aSopenharmony_ci{
404cabdff1aSopenharmony_ci    char command[CONTROL_BUFFER_SIZE];
405cabdff1aSopenharmony_ci    char *res = NULL;
406cabdff1aSopenharmony_ci    int ret;
407cabdff1aSopenharmony_ci    static const int size_codes[] = {213, 0};
408cabdff1aSopenharmony_ci
409cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
410cabdff1aSopenharmony_ci    if (ret >= sizeof(command))
411cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
412cabdff1aSopenharmony_ci
413cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) {
414cabdff1aSopenharmony_ci        s->filesize = strtoll(&res[4], NULL, 10);
415cabdff1aSopenharmony_ci    } else {
416cabdff1aSopenharmony_ci        s->filesize = -1;
417cabdff1aSopenharmony_ci        av_free(res);
418cabdff1aSopenharmony_ci        return AVERROR(EIO);
419cabdff1aSopenharmony_ci    }
420cabdff1aSopenharmony_ci
421cabdff1aSopenharmony_ci    av_free(res);
422cabdff1aSopenharmony_ci    return 0;
423cabdff1aSopenharmony_ci}
424cabdff1aSopenharmony_ci
425cabdff1aSopenharmony_cistatic int ftp_retrieve(FTPContext *s)
426cabdff1aSopenharmony_ci{
427cabdff1aSopenharmony_ci    char command[CONTROL_BUFFER_SIZE];
428cabdff1aSopenharmony_ci    static const int retr_codes[] = {150, 125, 0};
429cabdff1aSopenharmony_ci    int resp_code, ret;
430cabdff1aSopenharmony_ci
431cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
432cabdff1aSopenharmony_ci    if (ret >= sizeof(command))
433cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
434cabdff1aSopenharmony_ci
435cabdff1aSopenharmony_ci    resp_code = ftp_send_command(s, command, retr_codes, NULL);
436cabdff1aSopenharmony_ci    if (resp_code != 125 && resp_code != 150)
437cabdff1aSopenharmony_ci        return AVERROR(EIO);
438cabdff1aSopenharmony_ci
439cabdff1aSopenharmony_ci    s->state = DOWNLOADING;
440cabdff1aSopenharmony_ci
441cabdff1aSopenharmony_ci    return 0;
442cabdff1aSopenharmony_ci}
443cabdff1aSopenharmony_ci
444cabdff1aSopenharmony_cistatic int ftp_store(FTPContext *s)
445cabdff1aSopenharmony_ci{
446cabdff1aSopenharmony_ci    char command[CONTROL_BUFFER_SIZE];
447cabdff1aSopenharmony_ci    static const int stor_codes[] = {150, 125, 0};
448cabdff1aSopenharmony_ci    int resp_code, ret;
449cabdff1aSopenharmony_ci
450cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
451cabdff1aSopenharmony_ci    if (ret >= sizeof(command))
452cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
453cabdff1aSopenharmony_ci
454cabdff1aSopenharmony_ci    resp_code = ftp_send_command(s, command, stor_codes, NULL);
455cabdff1aSopenharmony_ci    if (resp_code != 125 && resp_code != 150)
456cabdff1aSopenharmony_ci        return AVERROR(EIO);
457cabdff1aSopenharmony_ci
458cabdff1aSopenharmony_ci    s->state = UPLOADING;
459cabdff1aSopenharmony_ci
460cabdff1aSopenharmony_ci    return 0;
461cabdff1aSopenharmony_ci}
462cabdff1aSopenharmony_ci
463cabdff1aSopenharmony_cistatic int ftp_type(FTPContext *s)
464cabdff1aSopenharmony_ci{
465cabdff1aSopenharmony_ci    static const char *command = "TYPE I\r\n";
466cabdff1aSopenharmony_ci    static const int type_codes[] = {200, 0};
467cabdff1aSopenharmony_ci
468cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, type_codes, NULL) != 200)
469cabdff1aSopenharmony_ci        return AVERROR(EIO);
470cabdff1aSopenharmony_ci
471cabdff1aSopenharmony_ci    return 0;
472cabdff1aSopenharmony_ci}
473cabdff1aSopenharmony_ci
474cabdff1aSopenharmony_cistatic int ftp_restart(FTPContext *s, int64_t pos)
475cabdff1aSopenharmony_ci{
476cabdff1aSopenharmony_ci    char command[CONTROL_BUFFER_SIZE];
477cabdff1aSopenharmony_ci    static const int rest_codes[] = {350, 0};
478cabdff1aSopenharmony_ci
479cabdff1aSopenharmony_ci    snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
480cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, rest_codes, NULL) != 350)
481cabdff1aSopenharmony_ci        return AVERROR(EIO);
482cabdff1aSopenharmony_ci
483cabdff1aSopenharmony_ci    return 0;
484cabdff1aSopenharmony_ci}
485cabdff1aSopenharmony_ci
486cabdff1aSopenharmony_cistatic int ftp_set_dir(FTPContext *s)
487cabdff1aSopenharmony_ci{
488cabdff1aSopenharmony_ci    static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
489cabdff1aSopenharmony_ci    char command[MAX_URL_SIZE];
490cabdff1aSopenharmony_ci    int ret;
491cabdff1aSopenharmony_ci
492cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
493cabdff1aSopenharmony_ci    if (ret >= sizeof(command))
494cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
495cabdff1aSopenharmony_ci
496cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
497cabdff1aSopenharmony_ci        return AVERROR(EIO);
498cabdff1aSopenharmony_ci    return 0;
499cabdff1aSopenharmony_ci}
500cabdff1aSopenharmony_ci
501cabdff1aSopenharmony_cistatic int ftp_list_mlsd(FTPContext *s)
502cabdff1aSopenharmony_ci{
503cabdff1aSopenharmony_ci    static const char *command = "MLSD\r\n";
504cabdff1aSopenharmony_ci    static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
505cabdff1aSopenharmony_ci
506cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
507cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
508cabdff1aSopenharmony_ci    s->listing_method = MLSD;
509cabdff1aSopenharmony_ci    return 0;
510cabdff1aSopenharmony_ci}
511cabdff1aSopenharmony_ci
512cabdff1aSopenharmony_cistatic int ftp_list_nlst(FTPContext *s)
513cabdff1aSopenharmony_ci{
514cabdff1aSopenharmony_ci    static const char *command = "NLST\r\n";
515cabdff1aSopenharmony_ci    static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
516cabdff1aSopenharmony_ci
517cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
518cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
519cabdff1aSopenharmony_ci    s->listing_method = NLST;
520cabdff1aSopenharmony_ci    return 0;
521cabdff1aSopenharmony_ci}
522cabdff1aSopenharmony_ci
523cabdff1aSopenharmony_cistatic int ftp_list(FTPContext *s)
524cabdff1aSopenharmony_ci{
525cabdff1aSopenharmony_ci    int ret;
526cabdff1aSopenharmony_ci    s->state = LISTING_DIR;
527cabdff1aSopenharmony_ci
528cabdff1aSopenharmony_ci    if ((ret = ftp_list_mlsd(s)) < 0)
529cabdff1aSopenharmony_ci        ret = ftp_list_nlst(s);
530cabdff1aSopenharmony_ci
531cabdff1aSopenharmony_ci    return ret;
532cabdff1aSopenharmony_ci}
533cabdff1aSopenharmony_ci
534cabdff1aSopenharmony_cistatic int ftp_has_feature(FTPContext *s, const char *feature_name)
535cabdff1aSopenharmony_ci{
536cabdff1aSopenharmony_ci    if (!s->features)
537cabdff1aSopenharmony_ci        return 0;
538cabdff1aSopenharmony_ci
539cabdff1aSopenharmony_ci    return av_stristr(s->features, feature_name) != NULL;
540cabdff1aSopenharmony_ci}
541cabdff1aSopenharmony_ci
542cabdff1aSopenharmony_cistatic int ftp_features(FTPContext *s)
543cabdff1aSopenharmony_ci{
544cabdff1aSopenharmony_ci    static const char *feat_command        = "FEAT\r\n";
545cabdff1aSopenharmony_ci    static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
546cabdff1aSopenharmony_ci    static const int feat_codes[] = {211, 0};
547cabdff1aSopenharmony_ci    static const int opts_codes[] = {200, 202, 451, 0};
548cabdff1aSopenharmony_ci
549cabdff1aSopenharmony_ci    av_freep(&s->features);
550cabdff1aSopenharmony_ci    if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
551cabdff1aSopenharmony_ci        av_freep(&s->features);
552cabdff1aSopenharmony_ci    }
553cabdff1aSopenharmony_ci
554cabdff1aSopenharmony_ci    if (ftp_has_feature(s, "UTF8")) {
555cabdff1aSopenharmony_ci        int ret = ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
556cabdff1aSopenharmony_ci        if (ret == 200 || ret == 202)
557cabdff1aSopenharmony_ci            s->utf8 = 1;
558cabdff1aSopenharmony_ci    }
559cabdff1aSopenharmony_ci
560cabdff1aSopenharmony_ci    return 0;
561cabdff1aSopenharmony_ci}
562cabdff1aSopenharmony_ci
563cabdff1aSopenharmony_cistatic int ftp_connect_control_connection(URLContext *h)
564cabdff1aSopenharmony_ci{
565cabdff1aSopenharmony_ci    char buf[CONTROL_BUFFER_SIZE], *response = NULL;
566cabdff1aSopenharmony_ci    int err;
567cabdff1aSopenharmony_ci    AVDictionary *opts = NULL;
568cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
569cabdff1aSopenharmony_ci    static const int connect_codes[] = {220, 0};
570cabdff1aSopenharmony_ci
571cabdff1aSopenharmony_ci    if (!s->conn_control) {
572cabdff1aSopenharmony_ci        ff_url_join(buf, sizeof(buf), "tcp", NULL,
573cabdff1aSopenharmony_ci                    s->hostname, s->server_control_port, NULL);
574cabdff1aSopenharmony_ci        if (s->rw_timeout != -1) {
575cabdff1aSopenharmony_ci            av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
576cabdff1aSopenharmony_ci        } /* if option is not given, don't pass it and let tcp use its own default */
577cabdff1aSopenharmony_ci        err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
578cabdff1aSopenharmony_ci                                   &h->interrupt_callback, &opts,
579cabdff1aSopenharmony_ci                                   h->protocol_whitelist, h->protocol_blacklist, h);
580cabdff1aSopenharmony_ci        av_dict_free(&opts);
581cabdff1aSopenharmony_ci        if (err < 0) {
582cabdff1aSopenharmony_ci            av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
583cabdff1aSopenharmony_ci            return err;
584cabdff1aSopenharmony_ci        }
585cabdff1aSopenharmony_ci
586cabdff1aSopenharmony_ci        /* check if server is ready */
587cabdff1aSopenharmony_ci        if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
588cabdff1aSopenharmony_ci            av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
589cabdff1aSopenharmony_ci            return AVERROR(EACCES);
590cabdff1aSopenharmony_ci        }
591cabdff1aSopenharmony_ci
592cabdff1aSopenharmony_ci        if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
593cabdff1aSopenharmony_ci            av_log(h, AV_LOG_WARNING, "Pure-FTPd server is used as an output protocol. It is known issue this implementation may produce incorrect content and it cannot be fixed at this moment.");
594cabdff1aSopenharmony_ci        }
595cabdff1aSopenharmony_ci        av_free(response);
596cabdff1aSopenharmony_ci
597cabdff1aSopenharmony_ci        if ((err = ftp_auth(s)) < 0) {
598cabdff1aSopenharmony_ci            av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
599cabdff1aSopenharmony_ci            return err;
600cabdff1aSopenharmony_ci        }
601cabdff1aSopenharmony_ci
602cabdff1aSopenharmony_ci        if ((err = ftp_type(s)) < 0) {
603cabdff1aSopenharmony_ci            av_log(h, AV_LOG_ERROR, "Set content type failed\n");
604cabdff1aSopenharmony_ci            return err;
605cabdff1aSopenharmony_ci        }
606cabdff1aSopenharmony_ci
607cabdff1aSopenharmony_ci        ftp_features(s);
608cabdff1aSopenharmony_ci    }
609cabdff1aSopenharmony_ci    return 0;
610cabdff1aSopenharmony_ci}
611cabdff1aSopenharmony_ci
612cabdff1aSopenharmony_cistatic int ftp_connect_data_connection(URLContext *h)
613cabdff1aSopenharmony_ci{
614cabdff1aSopenharmony_ci    int err;
615cabdff1aSopenharmony_ci    char buf[CONTROL_BUFFER_SIZE];
616cabdff1aSopenharmony_ci    AVDictionary *opts = NULL;
617cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
618cabdff1aSopenharmony_ci
619cabdff1aSopenharmony_ci    if (!s->conn_data) {
620cabdff1aSopenharmony_ci        /* Enter passive mode */
621cabdff1aSopenharmony_ci        if (ftp_passive_mode_epsv(s) < 0) {
622cabdff1aSopenharmony_ci            /* Use PASV as fallback */
623cabdff1aSopenharmony_ci            if ((err = ftp_passive_mode(s)) < 0)
624cabdff1aSopenharmony_ci                return err;
625cabdff1aSopenharmony_ci        }
626cabdff1aSopenharmony_ci        /* Open data connection */
627cabdff1aSopenharmony_ci        ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
628cabdff1aSopenharmony_ci        if (s->rw_timeout != -1) {
629cabdff1aSopenharmony_ci            av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
630cabdff1aSopenharmony_ci        } /* if option is not given, don't pass it and let tcp use its own default */
631cabdff1aSopenharmony_ci        err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
632cabdff1aSopenharmony_ci                                   &h->interrupt_callback, &opts,
633cabdff1aSopenharmony_ci                                   h->protocol_whitelist, h->protocol_blacklist, h);
634cabdff1aSopenharmony_ci        av_dict_free(&opts);
635cabdff1aSopenharmony_ci        if (err < 0)
636cabdff1aSopenharmony_ci            return err;
637cabdff1aSopenharmony_ci
638cabdff1aSopenharmony_ci        if (s->position)
639cabdff1aSopenharmony_ci            if ((err = ftp_restart(s, s->position)) < 0)
640cabdff1aSopenharmony_ci                return err;
641cabdff1aSopenharmony_ci    }
642cabdff1aSopenharmony_ci    s->state = READY;
643cabdff1aSopenharmony_ci    return 0;
644cabdff1aSopenharmony_ci}
645cabdff1aSopenharmony_ci
646cabdff1aSopenharmony_cistatic int ftp_abort(URLContext *h)
647cabdff1aSopenharmony_ci{
648cabdff1aSopenharmony_ci    static const char *command = "ABOR\r\n";
649cabdff1aSopenharmony_ci    int err;
650cabdff1aSopenharmony_ci    static const int abor_codes[] = {225, 226, 0};
651cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
652cabdff1aSopenharmony_ci
653cabdff1aSopenharmony_ci    /* According to RCF 959:
654cabdff1aSopenharmony_ci       "ABOR command tells the server to abort the previous FTP
655cabdff1aSopenharmony_ci       service command and any associated transfer of data."
656cabdff1aSopenharmony_ci
657cabdff1aSopenharmony_ci       There are FTP server implementations that don't response
658cabdff1aSopenharmony_ci       to any commands during data transfer in passive mode (including ABOR).
659cabdff1aSopenharmony_ci
660cabdff1aSopenharmony_ci       This implementation closes data connection by force.
661cabdff1aSopenharmony_ci    */
662cabdff1aSopenharmony_ci
663cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, NULL, NULL) < 0) {
664cabdff1aSopenharmony_ci        ftp_close_both_connections(s);
665cabdff1aSopenharmony_ci        if ((err = ftp_connect_control_connection(h)) < 0) {
666cabdff1aSopenharmony_ci            av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
667cabdff1aSopenharmony_ci            return err;
668cabdff1aSopenharmony_ci        }
669cabdff1aSopenharmony_ci    } else {
670cabdff1aSopenharmony_ci        ftp_close_data_connection(s);
671cabdff1aSopenharmony_ci        if (ftp_status(s, NULL, abor_codes) < 225) {
672cabdff1aSopenharmony_ci            /* wu-ftpd also closes control connection after data connection closing */
673cabdff1aSopenharmony_ci            ffurl_closep(&s->conn_control);
674cabdff1aSopenharmony_ci            if ((err = ftp_connect_control_connection(h)) < 0) {
675cabdff1aSopenharmony_ci                av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
676cabdff1aSopenharmony_ci                return err;
677cabdff1aSopenharmony_ci            }
678cabdff1aSopenharmony_ci        }
679cabdff1aSopenharmony_ci    }
680cabdff1aSopenharmony_ci
681cabdff1aSopenharmony_ci    return 0;
682cabdff1aSopenharmony_ci}
683cabdff1aSopenharmony_ci
684cabdff1aSopenharmony_cistatic int ftp_connect(URLContext *h, const char *url)
685cabdff1aSopenharmony_ci{
686cabdff1aSopenharmony_ci    char proto[10], path[MAX_URL_SIZE], credentials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
687cabdff1aSopenharmony_ci    const char *tok_user = NULL, *tok_pass = NULL;
688cabdff1aSopenharmony_ci    char *newpath = NULL;
689cabdff1aSopenharmony_ci    int err;
690cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
691cabdff1aSopenharmony_ci
692cabdff1aSopenharmony_ci    s->state = DISCONNECTED;
693cabdff1aSopenharmony_ci    s->listing_method = UNKNOWN_METHOD;
694cabdff1aSopenharmony_ci    s->filesize = -1;
695cabdff1aSopenharmony_ci    s->position = 0;
696cabdff1aSopenharmony_ci    s->features = NULL;
697cabdff1aSopenharmony_ci
698cabdff1aSopenharmony_ci    av_url_split(proto, sizeof(proto),
699cabdff1aSopenharmony_ci                 credentials, sizeof(credentials),
700cabdff1aSopenharmony_ci                 hostname, sizeof(hostname),
701cabdff1aSopenharmony_ci                 &s->server_control_port,
702cabdff1aSopenharmony_ci                 path, sizeof(path),
703cabdff1aSopenharmony_ci                 url);
704cabdff1aSopenharmony_ci
705cabdff1aSopenharmony_ci    if (!*credentials) {
706cabdff1aSopenharmony_ci        if (!s->option_user) {
707cabdff1aSopenharmony_ci            tok_user = "anonymous";
708cabdff1aSopenharmony_ci            tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
709cabdff1aSopenharmony_ci        } else {
710cabdff1aSopenharmony_ci            tok_user = s->option_user;
711cabdff1aSopenharmony_ci            tok_pass = s->option_password;
712cabdff1aSopenharmony_ci        }
713cabdff1aSopenharmony_ci        s->user = av_strdup(tok_user);
714cabdff1aSopenharmony_ci        s->password = av_strdup(tok_pass);
715cabdff1aSopenharmony_ci    } else {
716cabdff1aSopenharmony_ci        char *pass = strchr(credentials, ':');
717cabdff1aSopenharmony_ci        if (pass) {
718cabdff1aSopenharmony_ci            *pass++ = '\0';
719cabdff1aSopenharmony_ci            tok_pass = pass;
720cabdff1aSopenharmony_ci            s->password = ff_urldecode(pass, 0);
721cabdff1aSopenharmony_ci        } else {
722cabdff1aSopenharmony_ci            tok_pass = s->option_password;
723cabdff1aSopenharmony_ci            s->password = av_strdup(tok_pass);
724cabdff1aSopenharmony_ci        }
725cabdff1aSopenharmony_ci        s->user = ff_urldecode(credentials, 0);
726cabdff1aSopenharmony_ci    }
727cabdff1aSopenharmony_ci    s->hostname = av_strdup(hostname);
728cabdff1aSopenharmony_ci    if (!s->hostname || !s->user || (tok_pass && !s->password)) {
729cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
730cabdff1aSopenharmony_ci    }
731cabdff1aSopenharmony_ci
732cabdff1aSopenharmony_ci    if (s->server_control_port < 0 || s->server_control_port > 65535)
733cabdff1aSopenharmony_ci        s->server_control_port = 21;
734cabdff1aSopenharmony_ci
735cabdff1aSopenharmony_ci    if ((err = ftp_connect_control_connection(h)) < 0)
736cabdff1aSopenharmony_ci        return err;
737cabdff1aSopenharmony_ci
738cabdff1aSopenharmony_ci    if ((err = ftp_current_dir(s)) < 0)
739cabdff1aSopenharmony_ci        return err;
740cabdff1aSopenharmony_ci
741cabdff1aSopenharmony_ci    newpath = av_append_path_component(s->path, path);
742cabdff1aSopenharmony_ci    if (!newpath)
743cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
744cabdff1aSopenharmony_ci    av_free(s->path);
745cabdff1aSopenharmony_ci    s->path = newpath;
746cabdff1aSopenharmony_ci
747cabdff1aSopenharmony_ci    return 0;
748cabdff1aSopenharmony_ci}
749cabdff1aSopenharmony_ci
750cabdff1aSopenharmony_cistatic int ftp_open(URLContext *h, const char *url, int flags)
751cabdff1aSopenharmony_ci{
752cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
753cabdff1aSopenharmony_ci    int err;
754cabdff1aSopenharmony_ci
755cabdff1aSopenharmony_ci    ff_dlog(h, "ftp protocol open\n");
756cabdff1aSopenharmony_ci
757cabdff1aSopenharmony_ci    if ((err = ftp_connect(h, url)) < 0)
758cabdff1aSopenharmony_ci        goto fail;
759cabdff1aSopenharmony_ci
760cabdff1aSopenharmony_ci    if (ftp_restart(s, 0) < 0) {
761cabdff1aSopenharmony_ci        h->is_streamed = 1;
762cabdff1aSopenharmony_ci    } else {
763cabdff1aSopenharmony_ci        ftp_file_size(s);
764cabdff1aSopenharmony_ci        if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
765cabdff1aSopenharmony_ci            h->is_streamed = 1;
766cabdff1aSopenharmony_ci    }
767cabdff1aSopenharmony_ci
768cabdff1aSopenharmony_ci    return 0;
769cabdff1aSopenharmony_ci
770cabdff1aSopenharmony_ci  fail:
771cabdff1aSopenharmony_ci    av_log(h, AV_LOG_ERROR, "FTP open failed\n");
772cabdff1aSopenharmony_ci    ftp_close(h);
773cabdff1aSopenharmony_ci    return err;
774cabdff1aSopenharmony_ci}
775cabdff1aSopenharmony_ci
776cabdff1aSopenharmony_cistatic int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
777cabdff1aSopenharmony_ci{
778cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
779cabdff1aSopenharmony_ci    int err;
780cabdff1aSopenharmony_ci    int64_t new_pos;
781cabdff1aSopenharmony_ci
782cabdff1aSopenharmony_ci    ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
783cabdff1aSopenharmony_ci
784cabdff1aSopenharmony_ci    switch(whence) {
785cabdff1aSopenharmony_ci    case AVSEEK_SIZE:
786cabdff1aSopenharmony_ci        return s->filesize;
787cabdff1aSopenharmony_ci    case SEEK_SET:
788cabdff1aSopenharmony_ci        new_pos = pos;
789cabdff1aSopenharmony_ci        break;
790cabdff1aSopenharmony_ci    case SEEK_CUR:
791cabdff1aSopenharmony_ci        new_pos = s->position + pos;
792cabdff1aSopenharmony_ci        break;
793cabdff1aSopenharmony_ci    case SEEK_END:
794cabdff1aSopenharmony_ci        if (s->filesize < 0)
795cabdff1aSopenharmony_ci            return AVERROR(EIO);
796cabdff1aSopenharmony_ci        new_pos = s->filesize + pos;
797cabdff1aSopenharmony_ci        break;
798cabdff1aSopenharmony_ci    default:
799cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
800cabdff1aSopenharmony_ci    }
801cabdff1aSopenharmony_ci
802cabdff1aSopenharmony_ci    if (h->is_streamed)
803cabdff1aSopenharmony_ci        return AVERROR(EIO);
804cabdff1aSopenharmony_ci
805cabdff1aSopenharmony_ci    if (new_pos < 0) {
806cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
807cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
808cabdff1aSopenharmony_ci    }
809cabdff1aSopenharmony_ci
810cabdff1aSopenharmony_ci    if (new_pos != s->position) {
811cabdff1aSopenharmony_ci        if ((err = ftp_abort(h)) < 0)
812cabdff1aSopenharmony_ci            return err;
813cabdff1aSopenharmony_ci        s->position = new_pos;
814cabdff1aSopenharmony_ci    }
815cabdff1aSopenharmony_ci    return new_pos;
816cabdff1aSopenharmony_ci}
817cabdff1aSopenharmony_ci
818cabdff1aSopenharmony_cistatic int ftp_read(URLContext *h, unsigned char *buf, int size)
819cabdff1aSopenharmony_ci{
820cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
821cabdff1aSopenharmony_ci    int read, err, retry_done = 0;
822cabdff1aSopenharmony_ci
823cabdff1aSopenharmony_ci    ff_dlog(h, "ftp protocol read %d bytes\n", size);
824cabdff1aSopenharmony_ci  retry:
825cabdff1aSopenharmony_ci    if (s->state == ENDOFFILE)
826cabdff1aSopenharmony_ci        return AVERROR_EOF;
827cabdff1aSopenharmony_ci    if (s->state == DISCONNECTED) {
828cabdff1aSopenharmony_ci        if ((err = ftp_connect_data_connection(h)) < 0)
829cabdff1aSopenharmony_ci            return err;
830cabdff1aSopenharmony_ci    }
831cabdff1aSopenharmony_ci    if (s->state == READY) {
832cabdff1aSopenharmony_ci        if ((err = ftp_retrieve(s)) < 0)
833cabdff1aSopenharmony_ci            return err;
834cabdff1aSopenharmony_ci    }
835cabdff1aSopenharmony_ci    if (s->conn_data && s->state == DOWNLOADING) {
836cabdff1aSopenharmony_ci        read = ffurl_read(s->conn_data, buf, size);
837cabdff1aSopenharmony_ci        if (read >= 0) {
838cabdff1aSopenharmony_ci            s->position += read;
839cabdff1aSopenharmony_ci            s->filesize = FFMAX(s->filesize, s->position);
840cabdff1aSopenharmony_ci        }
841cabdff1aSopenharmony_ci        if (read == AVERROR_EOF) {
842cabdff1aSopenharmony_ci           static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
843cabdff1aSopenharmony_ci           char *response = NULL;
844cabdff1aSopenharmony_ci           err = ftp_status(s, &response, retr_codes);
845cabdff1aSopenharmony_ci           if (err == 226) {
846cabdff1aSopenharmony_ci               ftp_close_data_connection(s);
847cabdff1aSopenharmony_ci               av_freep(&response);
848cabdff1aSopenharmony_ci               s->state = ENDOFFILE;
849cabdff1aSopenharmony_ci               return AVERROR_EOF;
850cabdff1aSopenharmony_ci           }
851cabdff1aSopenharmony_ci           /* 250 is not allowed, any other status means some kind of error */
852cabdff1aSopenharmony_ci           av_log(h, AV_LOG_ERROR, "FTP transfer failed: %s\n", response ? response : (err < 0 ? av_err2str(err) : "?"));
853cabdff1aSopenharmony_ci           av_freep(&response);
854cabdff1aSopenharmony_ci           read = AVERROR(EIO);
855cabdff1aSopenharmony_ci        }
856cabdff1aSopenharmony_ci        if (read <= 0 && !h->is_streamed) {
857cabdff1aSopenharmony_ci            /* Server closed connection. Probably due to inactivity */
858cabdff1aSopenharmony_ci            av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
859cabdff1aSopenharmony_ci            if ((err = ftp_abort(h)) < 0)
860cabdff1aSopenharmony_ci                return err;
861cabdff1aSopenharmony_ci            if (!retry_done) {
862cabdff1aSopenharmony_ci                retry_done = 1;
863cabdff1aSopenharmony_ci                goto retry;
864cabdff1aSopenharmony_ci            }
865cabdff1aSopenharmony_ci        }
866cabdff1aSopenharmony_ci        return read;
867cabdff1aSopenharmony_ci    }
868cabdff1aSopenharmony_ci
869cabdff1aSopenharmony_ci    av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
870cabdff1aSopenharmony_ci    return AVERROR(EIO);
871cabdff1aSopenharmony_ci}
872cabdff1aSopenharmony_ci
873cabdff1aSopenharmony_cistatic int ftp_write(URLContext *h, const unsigned char *buf, int size)
874cabdff1aSopenharmony_ci{
875cabdff1aSopenharmony_ci    int err;
876cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
877cabdff1aSopenharmony_ci    int written;
878cabdff1aSopenharmony_ci
879cabdff1aSopenharmony_ci    ff_dlog(h, "ftp protocol write %d bytes\n", size);
880cabdff1aSopenharmony_ci
881cabdff1aSopenharmony_ci    if (s->state == DISCONNECTED) {
882cabdff1aSopenharmony_ci        if ((err = ftp_connect_data_connection(h)) < 0)
883cabdff1aSopenharmony_ci            return err;
884cabdff1aSopenharmony_ci    }
885cabdff1aSopenharmony_ci    if (s->state == READY) {
886cabdff1aSopenharmony_ci        if ((err = ftp_store(s)) < 0)
887cabdff1aSopenharmony_ci            return err;
888cabdff1aSopenharmony_ci    }
889cabdff1aSopenharmony_ci    if (s->conn_data && s->state == UPLOADING) {
890cabdff1aSopenharmony_ci        written = ffurl_write(s->conn_data, buf, size);
891cabdff1aSopenharmony_ci        if (written > 0) {
892cabdff1aSopenharmony_ci            s->position += written;
893cabdff1aSopenharmony_ci            s->filesize = FFMAX(s->filesize, s->position);
894cabdff1aSopenharmony_ci        }
895cabdff1aSopenharmony_ci        return written;
896cabdff1aSopenharmony_ci    }
897cabdff1aSopenharmony_ci
898cabdff1aSopenharmony_ci    av_log(h, AV_LOG_ERROR, "FTP write failed\n");
899cabdff1aSopenharmony_ci    return AVERROR(EIO);
900cabdff1aSopenharmony_ci}
901cabdff1aSopenharmony_ci
902cabdff1aSopenharmony_cistatic int ftp_close(URLContext *h)
903cabdff1aSopenharmony_ci{
904cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
905cabdff1aSopenharmony_ci
906cabdff1aSopenharmony_ci    ff_dlog(h, "ftp protocol close\n");
907cabdff1aSopenharmony_ci
908cabdff1aSopenharmony_ci    ftp_close_both_connections(s);
909cabdff1aSopenharmony_ci    av_freep(&s->user);
910cabdff1aSopenharmony_ci    av_freep(&s->password);
911cabdff1aSopenharmony_ci    av_freep(&s->hostname);
912cabdff1aSopenharmony_ci    av_freep(&s->path);
913cabdff1aSopenharmony_ci    av_freep(&s->features);
914cabdff1aSopenharmony_ci
915cabdff1aSopenharmony_ci    return 0;
916cabdff1aSopenharmony_ci}
917cabdff1aSopenharmony_ci
918cabdff1aSopenharmony_cistatic int ftp_get_file_handle(URLContext *h)
919cabdff1aSopenharmony_ci{
920cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
921cabdff1aSopenharmony_ci
922cabdff1aSopenharmony_ci    ff_dlog(h, "ftp protocol get_file_handle\n");
923cabdff1aSopenharmony_ci
924cabdff1aSopenharmony_ci    if (s->conn_data)
925cabdff1aSopenharmony_ci        return ffurl_get_file_handle(s->conn_data);
926cabdff1aSopenharmony_ci
927cabdff1aSopenharmony_ci    return AVERROR(EIO);
928cabdff1aSopenharmony_ci}
929cabdff1aSopenharmony_ci
930cabdff1aSopenharmony_cistatic int ftp_shutdown(URLContext *h, int flags)
931cabdff1aSopenharmony_ci{
932cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
933cabdff1aSopenharmony_ci
934cabdff1aSopenharmony_ci    ff_dlog(h, "ftp protocol shutdown\n");
935cabdff1aSopenharmony_ci
936cabdff1aSopenharmony_ci    if (s->conn_data)
937cabdff1aSopenharmony_ci        return ffurl_shutdown(s->conn_data, flags);
938cabdff1aSopenharmony_ci
939cabdff1aSopenharmony_ci    return AVERROR(EIO);
940cabdff1aSopenharmony_ci}
941cabdff1aSopenharmony_ci
942cabdff1aSopenharmony_cistatic int ftp_open_dir(URLContext *h)
943cabdff1aSopenharmony_ci{
944cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
945cabdff1aSopenharmony_ci    int ret;
946cabdff1aSopenharmony_ci
947cabdff1aSopenharmony_ci    if ((ret = ftp_connect(h, h->filename)) < 0)
948cabdff1aSopenharmony_ci        goto fail;
949cabdff1aSopenharmony_ci    if ((ret = ftp_set_dir(s)) < 0)
950cabdff1aSopenharmony_ci        goto fail;
951cabdff1aSopenharmony_ci    if ((ret = ftp_connect_data_connection(h)) < 0)
952cabdff1aSopenharmony_ci        goto fail;
953cabdff1aSopenharmony_ci    if ((ret = ftp_list(s)) < 0)
954cabdff1aSopenharmony_ci        goto fail;
955cabdff1aSopenharmony_ci    s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
956cabdff1aSopenharmony_ci    if (!s->dir_buffer) {
957cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
958cabdff1aSopenharmony_ci        goto fail;
959cabdff1aSopenharmony_ci    }
960cabdff1aSopenharmony_ci    s->dir_buffer[0] = 0;
961cabdff1aSopenharmony_ci    if (s->conn_data && s->state == LISTING_DIR)
962cabdff1aSopenharmony_ci        return 0;
963cabdff1aSopenharmony_ci  fail:
964cabdff1aSopenharmony_ci    ffurl_closep(&s->conn_control);
965cabdff1aSopenharmony_ci    ffurl_closep(&s->conn_data);
966cabdff1aSopenharmony_ci    return ret;
967cabdff1aSopenharmony_ci}
968cabdff1aSopenharmony_ci
969cabdff1aSopenharmony_cistatic int64_t ftp_parse_date(const char *date)
970cabdff1aSopenharmony_ci{
971cabdff1aSopenharmony_ci    struct tm tv;
972cabdff1aSopenharmony_ci    memset(&tv, 0, sizeof(struct tm));
973cabdff1aSopenharmony_ci    av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
974cabdff1aSopenharmony_ci    return INT64_C(1000000) * av_timegm(&tv);
975cabdff1aSopenharmony_ci}
976cabdff1aSopenharmony_ci
977cabdff1aSopenharmony_cistatic int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
978cabdff1aSopenharmony_ci{
979cabdff1aSopenharmony_ci    next->name = av_strdup(line);
980cabdff1aSopenharmony_ci    return 0;
981cabdff1aSopenharmony_ci}
982cabdff1aSopenharmony_ci
983cabdff1aSopenharmony_cistatic int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
984cabdff1aSopenharmony_ci{
985cabdff1aSopenharmony_ci    char *fact, *value;
986cabdff1aSopenharmony_ci    char *saveptr = NULL, *p = mlsd;
987cabdff1aSopenharmony_ci    ff_dlog(NULL, "%s\n", mlsd);
988cabdff1aSopenharmony_ci    while(fact = av_strtok(p, ";", &saveptr)) {
989cabdff1aSopenharmony_ci        p = NULL;
990cabdff1aSopenharmony_ci        if (fact[0] == ' ') {
991cabdff1aSopenharmony_ci            next->name = av_strdup(&fact[1]);
992cabdff1aSopenharmony_ci            continue;
993cabdff1aSopenharmony_ci        }
994cabdff1aSopenharmony_ci        fact = av_strtok(fact, "=", &value);
995cabdff1aSopenharmony_ci        if (!fact)
996cabdff1aSopenharmony_ci            continue;
997cabdff1aSopenharmony_ci        if (!av_strcasecmp(fact, "type")) {
998cabdff1aSopenharmony_ci            if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
999cabdff1aSopenharmony_ci                return 1;
1000cabdff1aSopenharmony_ci            if (!av_strcasecmp(value, "dir"))
1001cabdff1aSopenharmony_ci                next->type = AVIO_ENTRY_DIRECTORY;
1002cabdff1aSopenharmony_ci            else if (!av_strcasecmp(value, "file"))
1003cabdff1aSopenharmony_ci                next->type = AVIO_ENTRY_FILE;
1004cabdff1aSopenharmony_ci            else if (!av_strcasecmp(value, "OS.unix=slink:"))
1005cabdff1aSopenharmony_ci                next->type = AVIO_ENTRY_SYMBOLIC_LINK;
1006cabdff1aSopenharmony_ci        } else if (!av_strcasecmp(fact, "modify")) {
1007cabdff1aSopenharmony_ci            next->modification_timestamp = ftp_parse_date(value);
1008cabdff1aSopenharmony_ci        } else if (!av_strcasecmp(fact, "UNIX.mode")) {
1009cabdff1aSopenharmony_ci            next->filemode = strtoumax(value, NULL, 8);
1010cabdff1aSopenharmony_ci        } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
1011cabdff1aSopenharmony_ci            next->user_id = strtoumax(value, NULL, 10);
1012cabdff1aSopenharmony_ci        else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
1013cabdff1aSopenharmony_ci            next->group_id = strtoumax(value, NULL, 10);
1014cabdff1aSopenharmony_ci        else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
1015cabdff1aSopenharmony_ci            next->size = strtoll(value, NULL, 10);
1016cabdff1aSopenharmony_ci    }
1017cabdff1aSopenharmony_ci    return 0;
1018cabdff1aSopenharmony_ci}
1019cabdff1aSopenharmony_ci
1020cabdff1aSopenharmony_ci/**
1021cabdff1aSopenharmony_ci * @return 0 on success, negative on error, positive on entry to discard.
1022cabdff1aSopenharmony_ci */
1023cabdff1aSopenharmony_cistatic int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
1024cabdff1aSopenharmony_ci{
1025cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
1026cabdff1aSopenharmony_ci
1027cabdff1aSopenharmony_ci    switch (s->listing_method) {
1028cabdff1aSopenharmony_ci    case MLSD:
1029cabdff1aSopenharmony_ci        return ftp_parse_entry_mlsd(line, next);
1030cabdff1aSopenharmony_ci    case NLST:
1031cabdff1aSopenharmony_ci        return ftp_parse_entry_nlst(line, next);
1032cabdff1aSopenharmony_ci    case UNKNOWN_METHOD:
1033cabdff1aSopenharmony_ci    default:
1034cabdff1aSopenharmony_ci        return -1;
1035cabdff1aSopenharmony_ci    }
1036cabdff1aSopenharmony_ci}
1037cabdff1aSopenharmony_ci
1038cabdff1aSopenharmony_cistatic int ftp_read_dir(URLContext *h, AVIODirEntry **next)
1039cabdff1aSopenharmony_ci{
1040cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
1041cabdff1aSopenharmony_ci    char *start, *found;
1042cabdff1aSopenharmony_ci    int ret, retried;
1043cabdff1aSopenharmony_ci
1044cabdff1aSopenharmony_ci    do {
1045cabdff1aSopenharmony_ci        retried = 0;
1046cabdff1aSopenharmony_ci        start = s->dir_buffer + s->dir_buffer_offset;
1047cabdff1aSopenharmony_ci        while (!(found = strstr(start, "\n"))) {
1048cabdff1aSopenharmony_ci            if (retried)
1049cabdff1aSopenharmony_ci                return AVERROR(EIO);
1050cabdff1aSopenharmony_ci            s->dir_buffer_size -= s->dir_buffer_offset;
1051cabdff1aSopenharmony_ci            s->dir_buffer_offset = 0;
1052cabdff1aSopenharmony_ci            if (s->dir_buffer_size)
1053cabdff1aSopenharmony_ci                memmove(s->dir_buffer, start, s->dir_buffer_size);
1054cabdff1aSopenharmony_ci            ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1055cabdff1aSopenharmony_ci            if (ret < 0)
1056cabdff1aSopenharmony_ci                return ret;
1057cabdff1aSopenharmony_ci            if (!ret) {
1058cabdff1aSopenharmony_ci                *next = NULL;
1059cabdff1aSopenharmony_ci                return 0;
1060cabdff1aSopenharmony_ci            }
1061cabdff1aSopenharmony_ci            s->dir_buffer_size += ret;
1062cabdff1aSopenharmony_ci            s->dir_buffer[s->dir_buffer_size] = 0;
1063cabdff1aSopenharmony_ci            start = s->dir_buffer;
1064cabdff1aSopenharmony_ci            retried = 1;
1065cabdff1aSopenharmony_ci        }
1066cabdff1aSopenharmony_ci        s->dir_buffer_offset += (found + 1 - start);
1067cabdff1aSopenharmony_ci        found[0] = 0;
1068cabdff1aSopenharmony_ci        if (found > start && found[-1] == '\r')
1069cabdff1aSopenharmony_ci            found[-1] = 0;
1070cabdff1aSopenharmony_ci
1071cabdff1aSopenharmony_ci        *next = ff_alloc_dir_entry();
1072cabdff1aSopenharmony_ci        if (!*next)
1073cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
1074cabdff1aSopenharmony_ci        (*next)->utf8 = s->utf8;
1075cabdff1aSopenharmony_ci        ret = ftp_parse_entry(h, start, *next);
1076cabdff1aSopenharmony_ci        if (ret) {
1077cabdff1aSopenharmony_ci            avio_free_directory_entry(next);
1078cabdff1aSopenharmony_ci            if (ret < 0)
1079cabdff1aSopenharmony_ci                return ret;
1080cabdff1aSopenharmony_ci        }
1081cabdff1aSopenharmony_ci    } while (ret > 0);
1082cabdff1aSopenharmony_ci    return 0;
1083cabdff1aSopenharmony_ci}
1084cabdff1aSopenharmony_ci
1085cabdff1aSopenharmony_cistatic int ftp_close_dir(URLContext *h)
1086cabdff1aSopenharmony_ci{
1087cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
1088cabdff1aSopenharmony_ci    av_freep(&s->dir_buffer);
1089cabdff1aSopenharmony_ci    ffurl_closep(&s->conn_control);
1090cabdff1aSopenharmony_ci    ffurl_closep(&s->conn_data);
1091cabdff1aSopenharmony_ci    return 0;
1092cabdff1aSopenharmony_ci}
1093cabdff1aSopenharmony_ci
1094cabdff1aSopenharmony_cistatic int ftp_delete(URLContext *h)
1095cabdff1aSopenharmony_ci{
1096cabdff1aSopenharmony_ci    FTPContext *s = h->priv_data;
1097cabdff1aSopenharmony_ci    char command[MAX_URL_SIZE];
1098cabdff1aSopenharmony_ci    static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1099cabdff1aSopenharmony_ci    static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1100cabdff1aSopenharmony_ci    int ret;
1101cabdff1aSopenharmony_ci
1102cabdff1aSopenharmony_ci    if ((ret = ftp_connect(h, h->filename)) < 0)
1103cabdff1aSopenharmony_ci        goto cleanup;
1104cabdff1aSopenharmony_ci
1105cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1106cabdff1aSopenharmony_ci    if (ret >= sizeof(command)) {
1107cabdff1aSopenharmony_ci        ret = AVERROR(ENOSYS);
1108cabdff1aSopenharmony_ci        goto cleanup;
1109cabdff1aSopenharmony_ci    }
1110cabdff1aSopenharmony_ci
1111cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1112cabdff1aSopenharmony_ci        ret = 0;
1113cabdff1aSopenharmony_ci        goto cleanup;
1114cabdff1aSopenharmony_ci    }
1115cabdff1aSopenharmony_ci
1116cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1117cabdff1aSopenharmony_ci    if (ret >= sizeof(command)) {
1118cabdff1aSopenharmony_ci        ret = AVERROR(ENOSYS);
1119cabdff1aSopenharmony_ci        goto cleanup;
1120cabdff1aSopenharmony_ci    }
1121cabdff1aSopenharmony_ci
1122cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1123cabdff1aSopenharmony_ci        ret = 0;
1124cabdff1aSopenharmony_ci    else
1125cabdff1aSopenharmony_ci        ret = AVERROR(EIO);
1126cabdff1aSopenharmony_ci
1127cabdff1aSopenharmony_cicleanup:
1128cabdff1aSopenharmony_ci    ftp_close(h);
1129cabdff1aSopenharmony_ci    return ret;
1130cabdff1aSopenharmony_ci}
1131cabdff1aSopenharmony_ci
1132cabdff1aSopenharmony_cistatic int ftp_move(URLContext *h_src, URLContext *h_dst)
1133cabdff1aSopenharmony_ci{
1134cabdff1aSopenharmony_ci    FTPContext *s = h_src->priv_data;
1135cabdff1aSopenharmony_ci    char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1136cabdff1aSopenharmony_ci    static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1137cabdff1aSopenharmony_ci    static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1138cabdff1aSopenharmony_ci    int ret;
1139cabdff1aSopenharmony_ci
1140cabdff1aSopenharmony_ci    if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1141cabdff1aSopenharmony_ci        goto cleanup;
1142cabdff1aSopenharmony_ci
1143cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1144cabdff1aSopenharmony_ci    if (ret >= sizeof(command)) {
1145cabdff1aSopenharmony_ci        ret = AVERROR(ENOSYS);
1146cabdff1aSopenharmony_ci        goto cleanup;
1147cabdff1aSopenharmony_ci    }
1148cabdff1aSopenharmony_ci
1149cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1150cabdff1aSopenharmony_ci        ret = AVERROR(EIO);
1151cabdff1aSopenharmony_ci        goto cleanup;
1152cabdff1aSopenharmony_ci    }
1153cabdff1aSopenharmony_ci
1154cabdff1aSopenharmony_ci    av_url_split(0, 0, 0, 0, 0, 0, 0,
1155cabdff1aSopenharmony_ci                 path, sizeof(path),
1156cabdff1aSopenharmony_ci                 h_dst->filename);
1157cabdff1aSopenharmony_ci    ret = snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1158cabdff1aSopenharmony_ci    if (ret >= sizeof(command)) {
1159cabdff1aSopenharmony_ci        ret = AVERROR(ENOSYS);
1160cabdff1aSopenharmony_ci        goto cleanup;
1161cabdff1aSopenharmony_ci    }
1162cabdff1aSopenharmony_ci
1163cabdff1aSopenharmony_ci    if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1164cabdff1aSopenharmony_ci        ret = 0;
1165cabdff1aSopenharmony_ci    else
1166cabdff1aSopenharmony_ci        ret = AVERROR(EIO);
1167cabdff1aSopenharmony_ci
1168cabdff1aSopenharmony_cicleanup:
1169cabdff1aSopenharmony_ci    ftp_close(h_src);
1170cabdff1aSopenharmony_ci    return ret;
1171cabdff1aSopenharmony_ci}
1172cabdff1aSopenharmony_ci
1173cabdff1aSopenharmony_ciconst URLProtocol ff_ftp_protocol = {
1174cabdff1aSopenharmony_ci    .name                = "ftp",
1175cabdff1aSopenharmony_ci    .url_open            = ftp_open,
1176cabdff1aSopenharmony_ci    .url_read            = ftp_read,
1177cabdff1aSopenharmony_ci    .url_write           = ftp_write,
1178cabdff1aSopenharmony_ci    .url_seek            = ftp_seek,
1179cabdff1aSopenharmony_ci    .url_close           = ftp_close,
1180cabdff1aSopenharmony_ci    .url_get_file_handle = ftp_get_file_handle,
1181cabdff1aSopenharmony_ci    .url_shutdown        = ftp_shutdown,
1182cabdff1aSopenharmony_ci    .priv_data_size      = sizeof(FTPContext),
1183cabdff1aSopenharmony_ci    .priv_data_class     = &ftp_context_class,
1184cabdff1aSopenharmony_ci    .url_open_dir        = ftp_open_dir,
1185cabdff1aSopenharmony_ci    .url_read_dir        = ftp_read_dir,
1186cabdff1aSopenharmony_ci    .url_close_dir       = ftp_close_dir,
1187cabdff1aSopenharmony_ci    .url_delete          = ftp_delete,
1188cabdff1aSopenharmony_ci    .url_move            = ftp_move,
1189cabdff1aSopenharmony_ci    .flags               = URL_PROTOCOL_FLAG_NETWORK,
1190cabdff1aSopenharmony_ci    .default_whitelist   = "tcp",
1191cabdff1aSopenharmony_ci};
1192