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, 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_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
541 &h->interrupt_callback, &opts,
542 h->protocol_whitelist);
545 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
549 /* check if server is ready */
550 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
551 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
552 return AVERROR(EACCES);
555 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
556 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.");
560 if ((err = ftp_auth(s)) < 0) {
561 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
565 if ((err = ftp_type(s)) < 0) {
566 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
575 static int ftp_connect_data_connection(URLContext *h)
578 char buf[CONTROL_BUFFER_SIZE];
579 AVDictionary *opts = NULL;
580 FTPContext *s = h->priv_data;
583 /* Enter passive mode */
584 if (ftp_passive_mode_epsv(s) < 0) {
585 /* Use PASV as fallback */
586 if ((err = ftp_passive_mode(s)) < 0)
589 /* Open data connection */
590 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
591 if (s->rw_timeout != -1) {
592 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
593 } /* if option is not given, don't pass it and let tcp use its own default */
594 err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
595 &h->interrupt_callback, &opts,
596 h->protocol_whitelist);
602 if ((err = ftp_restart(s, s->position)) < 0)
609 static int ftp_abort(URLContext *h)
611 static const char *command = "ABOR\r\n";
613 static const int abor_codes[] = {225, 226, 0};
614 FTPContext *s = h->priv_data;
616 /* According to RCF 959:
617 "ABOR command tells the server to abort the previous FTP
618 service command and any associated transfer of data."
620 There are FTP server implementations that don't response
621 to any commands during data transfer in passive mode (including ABOR).
623 This implementation closes data connection by force.
626 if (ftp_send_command(s, command, NULL, NULL) < 0) {
627 ftp_close_both_connections(s);
628 if ((err = ftp_connect_control_connection(h)) < 0) {
629 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
633 ftp_close_data_connection(s);
634 if (ftp_status(s, NULL, abor_codes) < 225) {
635 /* wu-ftpd also closes control connection after data connection closing */
636 ffurl_closep(&s->conn_control);
637 if ((err = ftp_connect_control_connection(h)) < 0) {
638 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
647 static int ftp_connect(URLContext *h, const char *url)
649 char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
650 const char *tok_user = NULL, *tok_pass = NULL;
651 char *end = NULL, *newpath = NULL;
653 FTPContext *s = h->priv_data;
655 s->state = DISCONNECTED;
656 s->listing_method = UNKNOWN_METHOD;
661 av_url_split(proto, sizeof(proto),
662 credencials, sizeof(credencials),
663 hostname, sizeof(hostname),
664 &s->server_control_port,
668 tok_user = av_strtok(credencials, ":", &end);
669 tok_pass = av_strtok(end, ":", &end);
671 tok_user = "anonymous";
672 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
674 s->user = av_strdup(tok_user);
675 s->password = av_strdup(tok_pass);
676 s->hostname = av_strdup(hostname);
677 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
678 return AVERROR(ENOMEM);
681 if (s->server_control_port < 0 || s->server_control_port > 65535)
682 s->server_control_port = 21;
684 if ((err = ftp_connect_control_connection(h)) < 0)
687 if ((err = ftp_current_dir(s)) < 0)
690 newpath = av_append_path_component(s->path, path);
692 return AVERROR(ENOMEM);
699 static int ftp_open(URLContext *h, const char *url, int flags)
701 FTPContext *s = h->priv_data;
704 ff_dlog(h, "ftp protocol open\n");
706 if ((err = ftp_connect(h, url)) < 0)
709 if (ftp_restart(s, 0) < 0) {
712 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
714 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
721 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
726 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
728 FTPContext *s = h->priv_data;
730 int64_t new_pos, fake_pos;
732 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
741 new_pos = s->position + pos;
746 new_pos = s->filesize + pos;
749 return AVERROR(EINVAL);
756 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
757 return AVERROR(EINVAL);
760 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
761 if (fake_pos != s->position) {
762 if ((err = ftp_abort(h)) < 0)
764 s->position = fake_pos;
769 static int ftp_read(URLContext *h, unsigned char *buf, int size)
771 FTPContext *s = h->priv_data;
772 int read, err, retry_done = 0;
774 ff_dlog(h, "ftp protocol read %d bytes\n", size);
776 if (s->state == DISCONNECTED) {
778 if (s->position >= s->filesize)
780 if ((err = ftp_connect_data_connection(h)) < 0)
783 if (s->state == READY) {
784 if (s->position >= s->filesize)
786 if ((err = ftp_retrieve(s)) < 0)
789 if (s->conn_data && s->state == DOWNLOADING) {
790 read = ffurl_read(s->conn_data, buf, size);
793 if (s->position >= s->filesize) {
794 /* server will terminate, but keep current position to avoid madness */
795 /* save position to restart from it */
796 int64_t pos = s->position;
797 if (ftp_abort(h) < 0) {
804 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
805 /* Server closed connection. Probably due to inactivity */
806 int64_t pos = s->position;
807 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
808 if ((err = ftp_abort(h)) < 0)
810 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
811 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
822 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
826 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
829 FTPContext *s = h->priv_data;
832 ff_dlog(h, "ftp protocol write %d bytes\n", size);
834 if (s->state == DISCONNECTED) {
835 if ((err = ftp_connect_data_connection(h)) < 0)
838 if (s->state == READY) {
839 if ((err = ftp_store(s)) < 0)
842 if (s->conn_data && s->state == UPLOADING) {
843 written = ffurl_write(s->conn_data, buf, size);
845 s->position += written;
846 s->filesize = FFMAX(s->filesize, s->position);
851 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
855 static int ftp_close(URLContext *h)
857 FTPContext *s = h->priv_data;
859 ff_dlog(h, "ftp protocol close\n");
861 ftp_close_both_connections(s);
863 av_freep(&s->password);
864 av_freep(&s->hostname);
866 av_freep(&s->features);
871 static int ftp_get_file_handle(URLContext *h)
873 FTPContext *s = h->priv_data;
875 ff_dlog(h, "ftp protocol get_file_handle\n");
878 return ffurl_get_file_handle(s->conn_data);
883 static int ftp_shutdown(URLContext *h, int flags)
885 FTPContext *s = h->priv_data;
887 ff_dlog(h, "ftp protocol shutdown\n");
890 return ffurl_shutdown(s->conn_data, flags);
895 static int ftp_open_dir(URLContext *h)
897 FTPContext *s = h->priv_data;
900 if ((ret = ftp_connect(h, h->filename)) < 0)
902 if ((ret = ftp_set_dir(s)) < 0)
904 if ((ret = ftp_connect_data_connection(h)) < 0)
906 if ((ret = ftp_list(s)) < 0)
908 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
909 if (!s->dir_buffer) {
910 ret = AVERROR(ENOMEM);
913 s->dir_buffer[0] = 0;
914 if (s->conn_data && s->state == LISTING_DIR)
917 ffurl_closep(&s->conn_control);
918 ffurl_closep(&s->conn_data);
922 static int64_t ftp_parse_date(const char *date)
925 memset(&tv, 0, sizeof(struct tm));
926 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
927 return INT64_C(1000000) * av_timegm(&tv);
930 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
932 next->name = av_strdup(line);
936 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
939 ff_dlog(NULL, "%s\n", mlsd);
940 while(fact = av_strtok(mlsd, ";", &mlsd)) {
941 if (fact[0] == ' ') {
942 next->name = av_strdup(&fact[1]);
945 fact = av_strtok(fact, "=", &value);
946 if (!av_strcasecmp(fact, "type")) {
947 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
949 if (!av_strcasecmp(value, "dir"))
950 next->type = AVIO_ENTRY_DIRECTORY;
951 else if (!av_strcasecmp(value, "file"))
952 next->type = AVIO_ENTRY_FILE;
953 else if (!av_strcasecmp(value, "OS.unix=slink:"))
954 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
955 } else if (!av_strcasecmp(fact, "modify")) {
956 next->modification_timestamp = ftp_parse_date(value);
957 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
958 next->filemode = strtoumax(value, NULL, 8);
959 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
960 next->user_id = strtoumax(value, NULL, 10);
961 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
962 next->group_id = strtoumax(value, NULL, 10);
963 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
964 next->size = strtoll(value, NULL, 10);
970 * @return 0 on success, negative on error, positive on entry to discard.
972 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
974 FTPContext *s = h->priv_data;
976 switch (s->listing_method) {
978 return ftp_parse_entry_mlsd(line, next);
980 return ftp_parse_entry_nlst(line, next);
987 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
989 FTPContext *s = h->priv_data;
995 start = s->dir_buffer + s->dir_buffer_offset;
996 while (!(found = strstr(start, "\n"))) {
999 s->dir_buffer_size -= s->dir_buffer_offset;
1000 s->dir_buffer_offset = 0;
1001 if (s->dir_buffer_size)
1002 memmove(s->dir_buffer, start, s->dir_buffer_size);
1003 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1010 s->dir_buffer_size += ret;
1011 s->dir_buffer[s->dir_buffer_size] = 0;
1012 start = s->dir_buffer;
1015 s->dir_buffer_offset += (found + 1 - start);
1017 if (found > start && found[-1] == '\r')
1020 *next = ff_alloc_dir_entry();
1022 return AVERROR(ENOMEM);
1023 (*next)->utf8 = s->utf8;
1024 ret = ftp_parse_entry(h, start, *next);
1026 avio_free_directory_entry(next);
1034 static int ftp_close_dir(URLContext *h)
1036 FTPContext *s = h->priv_data;
1037 av_freep(&s->dir_buffer);
1038 ffurl_closep(&s->conn_control);
1039 ffurl_closep(&s->conn_data);
1043 static int ftp_delete(URLContext *h)
1045 FTPContext *s = h->priv_data;
1046 char command[MAX_URL_SIZE];
1047 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1048 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1051 if ((ret = ftp_connect(h, h->filename)) < 0)
1054 snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1055 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1060 snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1061 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1071 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1073 FTPContext *s = h_src->priv_data;
1074 char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1075 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1076 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1079 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1082 snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1083 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1088 av_url_split(0, 0, 0, 0, 0, 0, 0,
1091 snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1092 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1102 URLProtocol ff_ftp_protocol = {
1104 .url_open = ftp_open,
1105 .url_read = ftp_read,
1106 .url_write = ftp_write,
1107 .url_seek = ftp_seek,
1108 .url_close = ftp_close,
1109 .url_get_file_handle = ftp_get_file_handle,
1110 .url_shutdown = ftp_shutdown,
1111 .priv_data_size = sizeof(FTPContext),
1112 .priv_data_class = &ftp_context_class,
1113 .url_open_dir = ftp_open_dir,
1114 .url_read_dir = ftp_read_dir,
1115 .url_close_dir = ftp_close_dir,
1116 .url_delete = ftp_delete,
1117 .url_move = ftp_move,
1118 .flags = URL_PROTOCOL_FLAG_NETWORK,
1119 .default_whitelist = "tcp",