2 * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "libavutil/avstring.h"
22 #include "libavutil/internal.h"
23 #include "libavutil/parseutils.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/bprint.h"
30 #define CONTROL_BUFFER_SIZE 1024
31 #define DIR_BUFFER_SIZE 4096
50 URLContext *conn_control; /**< Control connection */
51 URLContext *conn_data; /**< Data connection, NULL when not connected */
52 uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
53 uint8_t *control_buf_ptr, *control_buf_end;
54 int server_data_port; /**< Data connection port opened by server, -1 on error. */
55 int server_control_port; /**< Control connection port, default is 21 */
56 char *hostname; /**< Server address. */
57 char *user; /**< Server user */
58 char *password; /**< Server user's password */
59 char *path; /**< Path to resource on server. */
60 int64_t filesize; /**< Size of file on server, -1 on error. */
61 int64_t position; /**< Current position, calculated. */
62 int rw_timeout; /**< Network timeout. */
63 const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
64 int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
65 FTPState state; /**< State of data connection */
66 FTPListingMethod listing_method; /**< Called listing method */
67 char *features; /**< List of server's features represented as raw response */
69 size_t dir_buffer_size;
70 size_t dir_buffer_offset;
74 #define OFFSET(x) offsetof(FTPContext, x)
75 #define D AV_OPT_FLAG_DECODING_PARAM
76 #define E AV_OPT_FLAG_ENCODING_PARAM
77 static const AVOption options[] = {
78 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
79 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
80 {"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 },
84 static const AVClass ftp_context_class = {
86 .item_name = av_default_item_name,
88 .version = LIBAVUTIL_VERSION_INT,
91 static int ftp_close(URLContext *h);
93 static int ftp_getc(FTPContext *s)
96 if (s->control_buf_ptr >= s->control_buf_end) {
97 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
103 s->control_buf_ptr = s->control_buffer;
104 s->control_buf_end = s->control_buffer + len;
107 return *s->control_buf_ptr++;
110 static int ftp_get_line(FTPContext *s, char *line, int line_size)
122 if (q > line && q[-1] == '\r')
127 if ((q - line) < line_size - 1)
134 * This routine returns ftp server response code.
135 * Server may send more than one response for a certain command.
136 * First expected code is returned.
138 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
140 int err, i, dash = 0, result = 0, code_found = 0, linesize;
141 char buf[CONTROL_BUFFER_SIZE];
142 AVBPrint line_buffer;
145 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
147 while (!code_found || dash) {
148 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
150 av_bprint_finalize(&line_buffer, NULL);
154 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
156 linesize = strlen(buf);
159 for (i = 0; i < 3; ++i) {
160 if (buf[i] < '0' || buf[i] > '9') {
174 for (i = 0; response_codes[i]; ++i) {
175 if (err == response_codes[i]) {
184 av_bprintf(&line_buffer, "%s\r\n", buf);
186 if (!dash && buf[3] == '-')
188 else if (err == dash && buf[3] == ' ')
195 av_bprint_finalize(&line_buffer, line);
199 static int ftp_send_command(FTPContext *s, const char *command,
200 const int response_codes[], char **response)
204 ff_dlog(s, "%s", command);
209 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
215 if (response_codes) {
216 return ftp_status(s, response, response_codes);
221 static void ftp_close_data_connection(FTPContext *s)
223 ffurl_closep(&s->conn_data);
225 s->state = DISCONNECTED;
228 static void ftp_close_both_connections(FTPContext *s)
230 ffurl_closep(&s->conn_control);
231 ftp_close_data_connection(s);
234 static int ftp_auth(FTPContext *s)
236 char buf[CONTROL_BUFFER_SIZE];
238 static const int user_codes[] = {331, 230, 0};
239 static const int pass_codes[] = {230, 0};
241 snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
242 err = ftp_send_command(s, buf, user_codes, NULL);
245 snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
246 err = ftp_send_command(s, buf, pass_codes, NULL);
248 return AVERROR(EACCES);
251 return AVERROR(EACCES);
256 static int ftp_passive_mode_epsv(FTPContext *s)
258 char *res = NULL, *start = NULL, *end = NULL;
260 static const char d = '|';
261 static const char *command = "EPSV\r\n";
262 static const int epsv_codes[] = {229, 0};
264 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
267 for (i = 0; res[i]; ++i) {
270 } else if (res[i] == ')') {
279 if (strlen(start) < 5)
281 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
286 s->server_data_port = atoi(start);
287 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
294 s->server_data_port = -1;
295 return AVERROR(ENOSYS);
298 static int ftp_passive_mode(FTPContext *s)
300 char *res = NULL, *start = NULL, *end = NULL;
302 static const char *command = "PASV\r\n";
303 static const int pasv_codes[] = {227, 0};
305 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
308 for (i = 0; res[i]; ++i) {
311 } else if (res[i] == ')') {
321 if (!av_strtok(start, ",", &end)) goto fail;
322 if (!av_strtok(end, ",", &end)) goto fail;
323 if (!av_strtok(end, ",", &end)) goto fail;
324 if (!av_strtok(end, ",", &end)) goto fail;
326 /* parse port number */
327 start = av_strtok(end, ",", &end);
328 if (!start) goto fail;
329 s->server_data_port = atoi(start) * 256;
330 start = av_strtok(end, ",", &end);
331 if (!start) goto fail;
332 s->server_data_port += atoi(start);
333 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
340 s->server_data_port = -1;
344 static int ftp_current_dir(FTPContext *s)
346 char *res = NULL, *start = NULL, *end = NULL;
348 static const char *command = "PWD\r\n";
349 static const int pwd_codes[] = {257, 0};
351 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
354 for (i = 0; res[i]; ++i) {
369 s->path = av_strdup(start);
374 return AVERROR(ENOMEM);
382 static int ftp_file_size(FTPContext *s)
384 char command[CONTROL_BUFFER_SIZE];
386 static const int size_codes[] = {213, 0};
388 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
389 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
390 s->filesize = strtoll(&res[4], NULL, 10);
401 static int ftp_retrieve(FTPContext *s)
403 char command[CONTROL_BUFFER_SIZE];
404 static const int retr_codes[] = {150, 0};
406 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
407 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
410 s->state = DOWNLOADING;
415 static int ftp_store(FTPContext *s)
417 char command[CONTROL_BUFFER_SIZE];
418 static const int stor_codes[] = {150, 0};
420 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
421 if (ftp_send_command(s, command, stor_codes, NULL) != 150)
424 s->state = UPLOADING;
429 static int ftp_type(FTPContext *s)
431 static const char *command = "TYPE I\r\n";
432 static const int type_codes[] = {200, 0};
434 if (ftp_send_command(s, command, type_codes, NULL) != 200)
440 static int ftp_restart(FTPContext *s, int64_t pos)
442 char command[CONTROL_BUFFER_SIZE];
443 static const int rest_codes[] = {350, 0};
445 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
446 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
452 static int ftp_set_dir(FTPContext *s)
454 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
455 char command[MAX_URL_SIZE];
457 snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
458 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
463 static int ftp_list_mlsd(FTPContext *s)
465 static const char *command = "MLSD\r\n";
466 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
468 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
469 return AVERROR(ENOSYS);
470 s->listing_method = MLSD;
474 static int ftp_list_nlst(FTPContext *s)
476 static const char *command = "NLST\r\n";
477 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
479 if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
480 return AVERROR(ENOSYS);
481 s->listing_method = NLST;
485 static int ftp_has_feature(FTPContext *s, const char *feature_name);
487 static int ftp_list(FTPContext *s)
490 s->state = LISTING_DIR;
492 if ((ret = ftp_list_mlsd(s)) < 0)
493 ret = ftp_list_nlst(s);
498 static int ftp_has_feature(FTPContext *s, const char *feature_name)
503 return av_stristr(s->features, feature_name) != NULL;
506 static int ftp_features(FTPContext *s)
508 static const char *feat_command = "FEAT\r\n";
509 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
510 static const int feat_codes[] = {211, 0};
511 static const int opts_codes[] = {200, 451, 0};
513 av_freep(&s->features);
514 if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
515 av_freep(&s->features);
518 if (ftp_has_feature(s, "UTF8")) {
519 if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200)
526 static int ftp_connect_control_connection(URLContext *h)
528 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
530 AVDictionary *opts = NULL;
531 FTPContext *s = h->priv_data;
532 static const int connect_codes[] = {220, 0};
534 if (!s->conn_control) {
535 ff_url_join(buf, sizeof(buf), "tcp", NULL,
536 s->hostname, s->server_control_port, NULL);
537 if (s->rw_timeout != -1) {
538 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
539 } /* if option is not given, don't pass it and let tcp use its own default */
540 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
541 &h->interrupt_callback, &opts);
544 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
548 /* check if server is ready */
549 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
550 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
551 return AVERROR(EACCES);
554 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
555 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.");
559 if ((err = ftp_auth(s)) < 0) {
560 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
564 if ((err = ftp_type(s)) < 0) {
565 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
574 static int ftp_connect_data_connection(URLContext *h)
577 char buf[CONTROL_BUFFER_SIZE];
578 AVDictionary *opts = NULL;
579 FTPContext *s = h->priv_data;
582 /* Enter passive mode */
583 if (ftp_passive_mode_epsv(s) < 0) {
584 /* Use PASV as fallback */
585 if ((err = ftp_passive_mode(s)) < 0)
588 /* Open data connection */
589 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
590 if (s->rw_timeout != -1) {
591 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
592 } /* if option is not given, don't pass it and let tcp use its own default */
593 err = ffurl_open(&s->conn_data, buf, h->flags,
594 &h->interrupt_callback, &opts);
600 if ((err = ftp_restart(s, s->position)) < 0)
607 static int ftp_abort(URLContext *h)
609 static const char *command = "ABOR\r\n";
611 static const int abor_codes[] = {225, 226, 0};
612 FTPContext *s = h->priv_data;
614 /* According to RCF 959:
615 "ABOR command tells the server to abort the previous FTP
616 service command and any associated transfer of data."
618 There are FTP server implementations that don't response
619 to any commands during data transfer in passive mode (including ABOR).
621 This implementation closes data connection by force.
624 if (ftp_send_command(s, command, NULL, NULL) < 0) {
625 ftp_close_both_connections(s);
626 if ((err = ftp_connect_control_connection(h)) < 0) {
627 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
631 ftp_close_data_connection(s);
632 if (ftp_status(s, NULL, abor_codes) < 225) {
633 /* wu-ftpd also closes control connection after data connection closing */
634 ffurl_closep(&s->conn_control);
635 if ((err = ftp_connect_control_connection(h)) < 0) {
636 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
645 static int ftp_connect(URLContext *h, const char *url)
647 char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
648 const char *tok_user = NULL, *tok_pass = NULL;
649 char *end = NULL, *newpath = NULL;
651 FTPContext *s = h->priv_data;
653 s->state = DISCONNECTED;
654 s->listing_method = UNKNOWN_METHOD;
659 av_url_split(proto, sizeof(proto),
660 credencials, sizeof(credencials),
661 hostname, sizeof(hostname),
662 &s->server_control_port,
666 tok_user = av_strtok(credencials, ":", &end);
667 tok_pass = av_strtok(end, ":", &end);
669 tok_user = "anonymous";
670 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
672 s->user = av_strdup(tok_user);
673 s->password = av_strdup(tok_pass);
674 s->hostname = av_strdup(hostname);
675 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
676 return AVERROR(ENOMEM);
679 if (s->server_control_port < 0 || s->server_control_port > 65535)
680 s->server_control_port = 21;
682 if ((err = ftp_connect_control_connection(h)) < 0)
685 if ((err = ftp_current_dir(s)) < 0)
688 newpath = av_append_path_component(s->path, path);
690 return AVERROR(ENOMEM);
697 static int ftp_open(URLContext *h, const char *url, int flags)
699 FTPContext *s = h->priv_data;
702 ff_dlog(h, "ftp protocol open\n");
704 if ((err = ftp_connect(h, url)) < 0)
707 if (ftp_restart(s, 0) < 0) {
710 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
712 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
719 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
724 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
726 FTPContext *s = h->priv_data;
728 int64_t new_pos, fake_pos;
730 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
739 new_pos = s->position + pos;
744 new_pos = s->filesize + pos;
747 return AVERROR(EINVAL);
754 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
755 return AVERROR(EINVAL);
758 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
759 if (fake_pos != s->position) {
760 if ((err = ftp_abort(h)) < 0)
762 s->position = fake_pos;
767 static int ftp_read(URLContext *h, unsigned char *buf, int size)
769 FTPContext *s = h->priv_data;
770 int read, err, retry_done = 0;
772 ff_dlog(h, "ftp protocol read %d bytes\n", size);
774 if (s->state == DISCONNECTED) {
776 if (s->position >= s->filesize)
778 if ((err = ftp_connect_data_connection(h)) < 0)
781 if (s->state == READY) {
782 if (s->position >= s->filesize)
784 if ((err = ftp_retrieve(s)) < 0)
787 if (s->conn_data && s->state == DOWNLOADING) {
788 read = ffurl_read(s->conn_data, buf, size);
791 if (s->position >= s->filesize) {
792 /* server will terminate, but keep current position to avoid madness */
793 /* save position to restart from it */
794 int64_t pos = s->position;
795 if (ftp_abort(h) < 0) {
802 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
803 /* Server closed connection. Probably due to inactivity */
804 int64_t pos = s->position;
805 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
806 if ((err = ftp_abort(h)) < 0)
808 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
809 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
820 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
824 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
827 FTPContext *s = h->priv_data;
830 ff_dlog(h, "ftp protocol write %d bytes\n", size);
832 if (s->state == DISCONNECTED) {
833 if ((err = ftp_connect_data_connection(h)) < 0)
836 if (s->state == READY) {
837 if ((err = ftp_store(s)) < 0)
840 if (s->conn_data && s->state == UPLOADING) {
841 written = ffurl_write(s->conn_data, buf, size);
843 s->position += written;
844 s->filesize = FFMAX(s->filesize, s->position);
849 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
853 static int ftp_close(URLContext *h)
855 FTPContext *s = h->priv_data;
857 ff_dlog(h, "ftp protocol close\n");
859 ftp_close_both_connections(s);
861 av_freep(&s->password);
862 av_freep(&s->hostname);
864 av_freep(&s->features);
869 static int ftp_get_file_handle(URLContext *h)
871 FTPContext *s = h->priv_data;
873 ff_dlog(h, "ftp protocol get_file_handle\n");
876 return ffurl_get_file_handle(s->conn_data);
881 static int ftp_shutdown(URLContext *h, int flags)
883 FTPContext *s = h->priv_data;
885 ff_dlog(h, "ftp protocol shutdown\n");
888 return ffurl_shutdown(s->conn_data, flags);
893 static int ftp_open_dir(URLContext *h)
895 FTPContext *s = h->priv_data;
898 if ((ret = ftp_connect(h, h->filename)) < 0)
900 if ((ret = ftp_set_dir(s)) < 0)
902 if ((ret = ftp_connect_data_connection(h)) < 0)
904 if ((ret = ftp_list(s)) < 0)
906 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
907 if (!s->dir_buffer) {
908 ret = AVERROR(ENOMEM);
911 s->dir_buffer[0] = 0;
912 if (s->conn_data && s->state == LISTING_DIR)
915 ffurl_closep(&s->conn_control);
916 ffurl_closep(&s->conn_data);
920 static int64_t ftp_parse_date(const char *date)
923 memset(&tv, 0, sizeof(struct tm));
924 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
925 return INT64_C(1000000) * av_timegm(&tv);
928 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
930 next->name = av_strdup(line);
934 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
937 ff_dlog(NULL, "%s\n", mlsd);
938 while(fact = av_strtok(mlsd, ";", &mlsd)) {
939 if (fact[0] == ' ') {
940 next->name = av_strdup(&fact[1]);
943 fact = av_strtok(fact, "=", &value);
944 if (!av_strcasecmp(fact, "type")) {
945 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
947 if (!av_strcasecmp(value, "dir"))
948 next->type = AVIO_ENTRY_DIRECTORY;
949 else if (!av_strcasecmp(value, "file"))
950 next->type = AVIO_ENTRY_FILE;
951 else if (!av_strcasecmp(value, "OS.unix=slink:"))
952 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
953 } else if (!av_strcasecmp(fact, "modify")) {
954 next->modification_timestamp = ftp_parse_date(value);
955 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
956 next->filemode = strtoumax(value, NULL, 8);
957 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
958 next->user_id = strtoumax(value, NULL, 10);
959 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
960 next->group_id = strtoumax(value, NULL, 10);
961 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
962 next->size = strtoll(value, NULL, 10);
968 * @return 0 on success, negative on error, positive on entry to discard.
970 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
972 FTPContext *s = h->priv_data;
974 switch (s->listing_method) {
976 return ftp_parse_entry_mlsd(line, next);
978 return ftp_parse_entry_nlst(line, next);
985 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
987 FTPContext *s = h->priv_data;
993 start = s->dir_buffer + s->dir_buffer_offset;
994 while (!(found = strstr(start, "\n"))) {
997 s->dir_buffer_size -= s->dir_buffer_offset;
998 s->dir_buffer_offset = 0;
999 if (s->dir_buffer_size)
1000 memmove(s->dir_buffer, start, s->dir_buffer_size);
1001 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1008 s->dir_buffer_size += ret;
1009 s->dir_buffer[s->dir_buffer_size] = 0;
1010 start = s->dir_buffer;
1013 s->dir_buffer_offset += (found + 1 - start);
1015 if (found > start && found[-1] == '\r')
1018 *next = ff_alloc_dir_entry();
1020 return AVERROR(ENOMEM);
1021 (*next)->utf8 = s->utf8;
1022 ret = ftp_parse_entry(h, start, *next);
1024 avio_free_directory_entry(next);
1032 static int ftp_close_dir(URLContext *h)
1034 FTPContext *s = h->priv_data;
1035 av_freep(&s->dir_buffer);
1036 ffurl_closep(&s->conn_control);
1037 ffurl_closep(&s->conn_data);
1041 static int ftp_delete(URLContext *h)
1043 FTPContext *s = h->priv_data;
1044 char command[MAX_URL_SIZE];
1045 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1046 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1049 if ((ret = ftp_connect(h, h->filename)) < 0)
1052 snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1053 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1058 snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1059 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1069 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1071 FTPContext *s = h_src->priv_data;
1072 char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1073 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1074 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1077 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1080 snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1081 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1086 av_url_split(0, 0, 0, 0, 0, 0, 0,
1089 snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1090 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1100 URLProtocol ff_ftp_protocol = {
1102 .url_open = ftp_open,
1103 .url_read = ftp_read,
1104 .url_write = ftp_write,
1105 .url_seek = ftp_seek,
1106 .url_close = ftp_close,
1107 .url_get_file_handle = ftp_get_file_handle,
1108 .url_shutdown = ftp_shutdown,
1109 .priv_data_size = sizeof(FTPContext),
1110 .priv_data_class = &ftp_context_class,
1111 .url_open_dir = ftp_open_dir,
1112 .url_read_dir = ftp_read_dir,
1113 .url_close_dir = ftp_close_dir,
1114 .url_delete = ftp_delete,
1115 .url_move = ftp_move,
1116 .flags = URL_PROTOCOL_FLAG_NETWORK,