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_BOOL, {.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, 125, 0};
407 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
408 resp_code = ftp_send_command(s, command, retr_codes, NULL);
409 if (resp_code != 125 && resp_code != 150)
412 s->state = DOWNLOADING;
417 static int ftp_store(FTPContext *s)
419 char command[CONTROL_BUFFER_SIZE];
420 static const int stor_codes[] = {150, 125, 0};
423 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
424 resp_code = ftp_send_command(s, command, stor_codes, NULL);
425 if (resp_code != 125 && resp_code != 150)
428 s->state = UPLOADING;
433 static int ftp_type(FTPContext *s)
435 static const char *command = "TYPE I\r\n";
436 static const int type_codes[] = {200, 0};
438 if (ftp_send_command(s, command, type_codes, NULL) != 200)
444 static int ftp_restart(FTPContext *s, int64_t pos)
446 char command[CONTROL_BUFFER_SIZE];
447 static const int rest_codes[] = {350, 0};
449 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
450 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
456 static int ftp_set_dir(FTPContext *s)
458 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
459 char command[MAX_URL_SIZE];
461 snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
462 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
467 static int ftp_list_mlsd(FTPContext *s)
469 static const char *command = "MLSD\r\n";
470 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
472 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
473 return AVERROR(ENOSYS);
474 s->listing_method = MLSD;
478 static int ftp_list_nlst(FTPContext *s)
480 static const char *command = "NLST\r\n";
481 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
483 if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
484 return AVERROR(ENOSYS);
485 s->listing_method = NLST;
489 static int ftp_has_feature(FTPContext *s, const char *feature_name);
491 static int ftp_list(FTPContext *s)
494 s->state = LISTING_DIR;
496 if ((ret = ftp_list_mlsd(s)) < 0)
497 ret = ftp_list_nlst(s);
502 static int ftp_has_feature(FTPContext *s, const char *feature_name)
507 return av_stristr(s->features, feature_name) != NULL;
510 static int ftp_features(FTPContext *s)
512 static const char *feat_command = "FEAT\r\n";
513 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
514 static const int feat_codes[] = {211, 0};
515 static const int opts_codes[] = {200, 451, 0};
517 av_freep(&s->features);
518 if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
519 av_freep(&s->features);
522 if (ftp_has_feature(s, "UTF8")) {
523 if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200)
530 static int ftp_connect_control_connection(URLContext *h)
532 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
534 AVDictionary *opts = NULL;
535 FTPContext *s = h->priv_data;
536 static const int connect_codes[] = {220, 0};
538 if (!s->conn_control) {
539 ff_url_join(buf, sizeof(buf), "tcp", NULL,
540 s->hostname, s->server_control_port, NULL);
541 if (s->rw_timeout != -1) {
542 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
543 } /* if option is not given, don't pass it and let tcp use its own default */
544 err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
545 &h->interrupt_callback, &opts,
546 h->protocol_whitelist, h->protocol_blacklist, h);
549 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
553 /* check if server is ready */
554 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
555 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
556 return AVERROR(EACCES);
559 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
560 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.");
564 if ((err = ftp_auth(s)) < 0) {
565 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
569 if ((err = ftp_type(s)) < 0) {
570 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
579 static int ftp_connect_data_connection(URLContext *h)
582 char buf[CONTROL_BUFFER_SIZE];
583 AVDictionary *opts = NULL;
584 FTPContext *s = h->priv_data;
587 /* Enter passive mode */
588 if (ftp_passive_mode_epsv(s) < 0) {
589 /* Use PASV as fallback */
590 if ((err = ftp_passive_mode(s)) < 0)
593 /* Open data connection */
594 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
595 if (s->rw_timeout != -1) {
596 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
597 } /* if option is not given, don't pass it and let tcp use its own default */
598 err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
599 &h->interrupt_callback, &opts,
600 h->protocol_whitelist, h->protocol_blacklist, h);
606 if ((err = ftp_restart(s, s->position)) < 0)
613 static int ftp_abort(URLContext *h)
615 static const char *command = "ABOR\r\n";
617 static const int abor_codes[] = {225, 226, 0};
618 FTPContext *s = h->priv_data;
620 /* According to RCF 959:
621 "ABOR command tells the server to abort the previous FTP
622 service command and any associated transfer of data."
624 There are FTP server implementations that don't response
625 to any commands during data transfer in passive mode (including ABOR).
627 This implementation closes data connection by force.
630 if (ftp_send_command(s, command, NULL, NULL) < 0) {
631 ftp_close_both_connections(s);
632 if ((err = ftp_connect_control_connection(h)) < 0) {
633 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
637 ftp_close_data_connection(s);
638 if (ftp_status(s, NULL, abor_codes) < 225) {
639 /* wu-ftpd also closes control connection after data connection closing */
640 ffurl_closep(&s->conn_control);
641 if ((err = ftp_connect_control_connection(h)) < 0) {
642 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
651 static int ftp_connect(URLContext *h, const char *url)
653 char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
654 const char *tok_user = NULL, *tok_pass = NULL;
655 char *end = NULL, *newpath = NULL;
657 FTPContext *s = h->priv_data;
659 s->state = DISCONNECTED;
660 s->listing_method = UNKNOWN_METHOD;
665 av_url_split(proto, sizeof(proto),
666 credencials, sizeof(credencials),
667 hostname, sizeof(hostname),
668 &s->server_control_port,
672 tok_user = av_strtok(credencials, ":", &end);
673 tok_pass = av_strtok(end, ":", &end);
675 tok_user = "anonymous";
676 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
678 s->user = av_strdup(tok_user);
679 s->password = av_strdup(tok_pass);
680 s->hostname = av_strdup(hostname);
681 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
682 return AVERROR(ENOMEM);
685 if (s->server_control_port < 0 || s->server_control_port > 65535)
686 s->server_control_port = 21;
688 if ((err = ftp_connect_control_connection(h)) < 0)
691 if ((err = ftp_current_dir(s)) < 0)
694 newpath = av_append_path_component(s->path, path);
696 return AVERROR(ENOMEM);
703 static int ftp_open(URLContext *h, const char *url, int flags)
705 FTPContext *s = h->priv_data;
708 ff_dlog(h, "ftp protocol open\n");
710 if ((err = ftp_connect(h, url)) < 0)
713 if (ftp_restart(s, 0) < 0) {
716 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
718 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
725 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
730 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
732 FTPContext *s = h->priv_data;
734 int64_t new_pos, fake_pos;
736 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
745 new_pos = s->position + pos;
750 new_pos = s->filesize + pos;
753 return AVERROR(EINVAL);
760 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
761 return AVERROR(EINVAL);
764 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
765 if (fake_pos != s->position) {
766 if ((err = ftp_abort(h)) < 0)
768 s->position = fake_pos;
773 static int ftp_read(URLContext *h, unsigned char *buf, int size)
775 FTPContext *s = h->priv_data;
776 int read, err, retry_done = 0;
778 ff_dlog(h, "ftp protocol read %d bytes\n", size);
780 if (s->state == DISCONNECTED) {
782 if (s->position >= s->filesize)
784 if ((err = ftp_connect_data_connection(h)) < 0)
787 if (s->state == READY) {
788 if (s->position >= s->filesize)
790 if ((err = ftp_retrieve(s)) < 0)
793 if (s->conn_data && s->state == DOWNLOADING) {
794 read = ffurl_read(s->conn_data, buf, size);
797 if (s->position >= s->filesize) {
798 /* server will terminate, but keep current position to avoid madness */
799 /* save position to restart from it */
800 int64_t pos = s->position;
801 if (ftp_abort(h) < 0) {
808 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
809 /* Server closed connection. Probably due to inactivity */
810 int64_t pos = s->position;
811 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
812 if ((err = ftp_abort(h)) < 0)
814 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
815 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
826 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
830 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
833 FTPContext *s = h->priv_data;
836 ff_dlog(h, "ftp protocol write %d bytes\n", size);
838 if (s->state == DISCONNECTED) {
839 if ((err = ftp_connect_data_connection(h)) < 0)
842 if (s->state == READY) {
843 if ((err = ftp_store(s)) < 0)
846 if (s->conn_data && s->state == UPLOADING) {
847 written = ffurl_write(s->conn_data, buf, size);
849 s->position += written;
850 s->filesize = FFMAX(s->filesize, s->position);
855 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
859 static int ftp_close(URLContext *h)
861 FTPContext *s = h->priv_data;
863 ff_dlog(h, "ftp protocol close\n");
865 ftp_close_both_connections(s);
867 av_freep(&s->password);
868 av_freep(&s->hostname);
870 av_freep(&s->features);
875 static int ftp_get_file_handle(URLContext *h)
877 FTPContext *s = h->priv_data;
879 ff_dlog(h, "ftp protocol get_file_handle\n");
882 return ffurl_get_file_handle(s->conn_data);
887 static int ftp_shutdown(URLContext *h, int flags)
889 FTPContext *s = h->priv_data;
891 ff_dlog(h, "ftp protocol shutdown\n");
894 return ffurl_shutdown(s->conn_data, flags);
899 static int ftp_open_dir(URLContext *h)
901 FTPContext *s = h->priv_data;
904 if ((ret = ftp_connect(h, h->filename)) < 0)
906 if ((ret = ftp_set_dir(s)) < 0)
908 if ((ret = ftp_connect_data_connection(h)) < 0)
910 if ((ret = ftp_list(s)) < 0)
912 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
913 if (!s->dir_buffer) {
914 ret = AVERROR(ENOMEM);
917 s->dir_buffer[0] = 0;
918 if (s->conn_data && s->state == LISTING_DIR)
921 ffurl_closep(&s->conn_control);
922 ffurl_closep(&s->conn_data);
926 static int64_t ftp_parse_date(const char *date)
929 memset(&tv, 0, sizeof(struct tm));
930 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
931 return INT64_C(1000000) * av_timegm(&tv);
934 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
936 next->name = av_strdup(line);
940 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
943 ff_dlog(NULL, "%s\n", mlsd);
944 while(fact = av_strtok(mlsd, ";", &mlsd)) {
945 if (fact[0] == ' ') {
946 next->name = av_strdup(&fact[1]);
949 fact = av_strtok(fact, "=", &value);
950 if (!av_strcasecmp(fact, "type")) {
951 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
953 if (!av_strcasecmp(value, "dir"))
954 next->type = AVIO_ENTRY_DIRECTORY;
955 else if (!av_strcasecmp(value, "file"))
956 next->type = AVIO_ENTRY_FILE;
957 else if (!av_strcasecmp(value, "OS.unix=slink:"))
958 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
959 } else if (!av_strcasecmp(fact, "modify")) {
960 next->modification_timestamp = ftp_parse_date(value);
961 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
962 next->filemode = strtoumax(value, NULL, 8);
963 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
964 next->user_id = strtoumax(value, NULL, 10);
965 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
966 next->group_id = strtoumax(value, NULL, 10);
967 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
968 next->size = strtoll(value, NULL, 10);
974 * @return 0 on success, negative on error, positive on entry to discard.
976 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
978 FTPContext *s = h->priv_data;
980 switch (s->listing_method) {
982 return ftp_parse_entry_mlsd(line, next);
984 return ftp_parse_entry_nlst(line, next);
991 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
993 FTPContext *s = h->priv_data;
999 start = s->dir_buffer + s->dir_buffer_offset;
1000 while (!(found = strstr(start, "\n"))) {
1002 return AVERROR(EIO);
1003 s->dir_buffer_size -= s->dir_buffer_offset;
1004 s->dir_buffer_offset = 0;
1005 if (s->dir_buffer_size)
1006 memmove(s->dir_buffer, start, s->dir_buffer_size);
1007 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1014 s->dir_buffer_size += ret;
1015 s->dir_buffer[s->dir_buffer_size] = 0;
1016 start = s->dir_buffer;
1019 s->dir_buffer_offset += (found + 1 - start);
1021 if (found > start && found[-1] == '\r')
1024 *next = ff_alloc_dir_entry();
1026 return AVERROR(ENOMEM);
1027 (*next)->utf8 = s->utf8;
1028 ret = ftp_parse_entry(h, start, *next);
1030 avio_free_directory_entry(next);
1038 static int ftp_close_dir(URLContext *h)
1040 FTPContext *s = h->priv_data;
1041 av_freep(&s->dir_buffer);
1042 ffurl_closep(&s->conn_control);
1043 ffurl_closep(&s->conn_data);
1047 static int ftp_delete(URLContext *h)
1049 FTPContext *s = h->priv_data;
1050 char command[MAX_URL_SIZE];
1051 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1052 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1055 if ((ret = ftp_connect(h, h->filename)) < 0)
1058 snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1059 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1064 snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1065 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1075 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1077 FTPContext *s = h_src->priv_data;
1078 char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1079 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1080 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1083 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1086 snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1087 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1092 av_url_split(0, 0, 0, 0, 0, 0, 0,
1095 snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1096 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1106 const URLProtocol ff_ftp_protocol = {
1108 .url_open = ftp_open,
1109 .url_read = ftp_read,
1110 .url_write = ftp_write,
1111 .url_seek = ftp_seek,
1112 .url_close = ftp_close,
1113 .url_get_file_handle = ftp_get_file_handle,
1114 .url_shutdown = ftp_shutdown,
1115 .priv_data_size = sizeof(FTPContext),
1116 .priv_data_class = &ftp_context_class,
1117 .url_open_dir = ftp_open_dir,
1118 .url_read_dir = ftp_read_dir,
1119 .url_close_dir = ftp_close_dir,
1120 .url_delete = ftp_delete,
1121 .url_move = ftp_move,
1122 .flags = URL_PROTOCOL_FLAG_NETWORK,
1123 .default_whitelist = "tcp",