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 "urldecode.h"
28 #include "libavutil/opt.h"
29 #include "libavutil/bprint.h"
31 #define CONTROL_BUFFER_SIZE 1024
32 #define DIR_BUFFER_SIZE 4096
51 URLContext *conn_control; /**< Control connection */
52 URLContext *conn_data; /**< Data connection, NULL when not connected */
53 uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
54 uint8_t *control_buf_ptr, *control_buf_end;
55 int server_data_port; /**< Data connection port opened by server, -1 on error. */
56 int server_control_port; /**< Control connection port, default is 21 */
57 char *hostname; /**< Server address. */
58 char *user; /**< Server user */
59 char *password; /**< Server user's password */
60 char *path; /**< Path to resource on server. */
61 int64_t filesize; /**< Size of file on server, -1 on error. */
62 int64_t position; /**< Current position, calculated. */
63 int rw_timeout; /**< Network timeout. */
64 const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
65 int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
66 FTPState state; /**< State of data connection */
67 FTPListingMethod listing_method; /**< Called listing method */
68 char *features; /**< List of server's features represented as raw response */
70 size_t dir_buffer_size;
71 size_t dir_buffer_offset;
73 const char *option_user; /**< User to be used if none given in the URL */
74 const char *option_password; /**< Password to be used if none given in the URL */
77 #define OFFSET(x) offsetof(FTPContext, x)
78 #define D AV_OPT_FLAG_DECODING_PARAM
79 #define E AV_OPT_FLAG_ENCODING_PARAM
80 static const AVOption options[] = {
81 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
82 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
83 {"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 {"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 },
85 {"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 },
89 static const AVClass ftp_context_class = {
91 .item_name = av_default_item_name,
93 .version = LIBAVUTIL_VERSION_INT,
96 static int ftp_close(URLContext *h);
98 static int ftp_getc(FTPContext *s)
101 if (s->control_buf_ptr >= s->control_buf_end) {
102 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
108 s->control_buf_ptr = s->control_buffer;
109 s->control_buf_end = s->control_buffer + len;
112 return *s->control_buf_ptr++;
115 static int ftp_get_line(FTPContext *s, char *line, int line_size)
127 if (q > line && q[-1] == '\r')
132 if ((q - line) < line_size - 1)
139 * This routine returns ftp server response code.
140 * Server may send more than one response for a certain command.
141 * First expected code is returned.
143 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
145 int err, i, dash = 0, result = 0, code_found = 0, linesize;
146 char buf[CONTROL_BUFFER_SIZE];
147 AVBPrint line_buffer;
150 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
152 while (!code_found || dash) {
153 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
155 av_bprint_finalize(&line_buffer, NULL);
159 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
161 linesize = strlen(buf);
164 for (i = 0; i < 3; ++i) {
165 if (buf[i] < '0' || buf[i] > '9') {
179 for (i = 0; response_codes[i]; ++i) {
180 if (err == response_codes[i]) {
189 av_bprintf(&line_buffer, "%s\r\n", buf);
191 if (!dash && buf[3] == '-')
193 else if (err == dash && buf[3] == ' ')
200 av_bprint_finalize(&line_buffer, line);
204 static int ftp_send_command(FTPContext *s, const char *command,
205 const int response_codes[], char **response)
209 ff_dlog(s, "%s", command);
214 if (!s->conn_control)
217 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
223 if (response_codes) {
224 return ftp_status(s, response, response_codes);
229 static void ftp_close_data_connection(FTPContext *s)
231 ffurl_closep(&s->conn_data);
233 s->state = DISCONNECTED;
236 static void ftp_close_both_connections(FTPContext *s)
238 ffurl_closep(&s->conn_control);
239 ftp_close_data_connection(s);
242 static int ftp_auth(FTPContext *s)
244 char buf[CONTROL_BUFFER_SIZE];
246 static const int user_codes[] = {331, 230, 0};
247 static const int pass_codes[] = {230, 0};
249 snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
250 err = ftp_send_command(s, buf, user_codes, NULL);
253 snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
254 err = ftp_send_command(s, buf, pass_codes, NULL);
256 return AVERROR(EACCES);
259 return AVERROR(EACCES);
264 static int ftp_passive_mode_epsv(FTPContext *s)
266 char *res = NULL, *start = NULL, *end = NULL;
268 static const char d = '|';
269 static const char *command = "EPSV\r\n";
270 static const int epsv_codes[] = {229, 0};
272 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
275 for (i = 0; res[i]; ++i) {
278 } else if (res[i] == ')') {
287 if (strlen(start) < 5)
289 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
294 s->server_data_port = atoi(start);
295 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
302 s->server_data_port = -1;
303 return AVERROR(ENOSYS);
306 static int ftp_passive_mode(FTPContext *s)
308 char *res = NULL, *start = NULL, *end = NULL;
310 static const char *command = "PASV\r\n";
311 static const int pasv_codes[] = {227, 0};
313 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
316 for (i = 0; res[i]; ++i) {
319 } else if (res[i] == ')') {
329 if (!av_strtok(start, ",", &end)) goto fail;
330 if (!av_strtok(end, ",", &end)) goto fail;
331 if (!av_strtok(end, ",", &end)) goto fail;
332 if (!av_strtok(end, ",", &end)) goto fail;
334 /* parse port number */
335 start = av_strtok(end, ",", &end);
336 if (!start) goto fail;
337 s->server_data_port = atoi(start) * 256;
338 start = av_strtok(end, ",", &end);
339 if (!start) goto fail;
340 s->server_data_port += atoi(start);
341 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
348 s->server_data_port = -1;
352 static int ftp_current_dir(FTPContext *s)
354 char *res = NULL, *start = NULL, *end = NULL;
356 static const char *command = "PWD\r\n";
357 static const int pwd_codes[] = {257, 0};
359 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
362 for (i = 0; res[i]; ++i) {
377 s->path = av_strdup(start);
382 return AVERROR(ENOMEM);
390 static int ftp_file_size(FTPContext *s)
392 char command[CONTROL_BUFFER_SIZE];
394 static const int size_codes[] = {213, 0};
396 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
397 if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) {
398 s->filesize = strtoll(&res[4], NULL, 10);
409 static int ftp_retrieve(FTPContext *s)
411 char command[CONTROL_BUFFER_SIZE];
412 static const int retr_codes[] = {150, 125, 0};
415 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
416 resp_code = ftp_send_command(s, command, retr_codes, NULL);
417 if (resp_code != 125 && resp_code != 150)
420 s->state = DOWNLOADING;
425 static int ftp_store(FTPContext *s)
427 char command[CONTROL_BUFFER_SIZE];
428 static const int stor_codes[] = {150, 125, 0};
431 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
432 resp_code = ftp_send_command(s, command, stor_codes, NULL);
433 if (resp_code != 125 && resp_code != 150)
436 s->state = UPLOADING;
441 static int ftp_type(FTPContext *s)
443 static const char *command = "TYPE I\r\n";
444 static const int type_codes[] = {200, 0};
446 if (ftp_send_command(s, command, type_codes, NULL) != 200)
452 static int ftp_restart(FTPContext *s, int64_t pos)
454 char command[CONTROL_BUFFER_SIZE];
455 static const int rest_codes[] = {350, 0};
457 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
458 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
464 static int ftp_set_dir(FTPContext *s)
466 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
467 char command[MAX_URL_SIZE];
469 snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
470 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
475 static int ftp_list_mlsd(FTPContext *s)
477 static const char *command = "MLSD\r\n";
478 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
480 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
481 return AVERROR(ENOSYS);
482 s->listing_method = MLSD;
486 static int ftp_list_nlst(FTPContext *s)
488 static const char *command = "NLST\r\n";
489 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
491 if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
492 return AVERROR(ENOSYS);
493 s->listing_method = NLST;
497 static int ftp_list(FTPContext *s)
500 s->state = LISTING_DIR;
502 if ((ret = ftp_list_mlsd(s)) < 0)
503 ret = ftp_list_nlst(s);
508 static int ftp_has_feature(FTPContext *s, const char *feature_name)
513 return av_stristr(s->features, feature_name) != NULL;
516 static int ftp_features(FTPContext *s)
518 static const char *feat_command = "FEAT\r\n";
519 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
520 static const int feat_codes[] = {211, 0};
521 static const int opts_codes[] = {200, 202, 451, 0};
523 av_freep(&s->features);
524 if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
525 av_freep(&s->features);
528 if (ftp_has_feature(s, "UTF8")) {
529 int ret = ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
530 if (ret == 200 || ret == 202)
537 static int ftp_connect_control_connection(URLContext *h)
539 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
541 AVDictionary *opts = NULL;
542 FTPContext *s = h->priv_data;
543 static const int connect_codes[] = {220, 0};
545 if (!s->conn_control) {
546 ff_url_join(buf, sizeof(buf), "tcp", NULL,
547 s->hostname, s->server_control_port, NULL);
548 if (s->rw_timeout != -1) {
549 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
550 } /* if option is not given, don't pass it and let tcp use its own default */
551 err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
552 &h->interrupt_callback, &opts,
553 h->protocol_whitelist, h->protocol_blacklist, h);
556 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
560 /* check if server is ready */
561 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
562 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
563 return AVERROR(EACCES);
566 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
567 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.");
571 if ((err = ftp_auth(s)) < 0) {
572 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
576 if ((err = ftp_type(s)) < 0) {
577 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
586 static int ftp_connect_data_connection(URLContext *h)
589 char buf[CONTROL_BUFFER_SIZE];
590 AVDictionary *opts = NULL;
591 FTPContext *s = h->priv_data;
594 /* Enter passive mode */
595 if (ftp_passive_mode_epsv(s) < 0) {
596 /* Use PASV as fallback */
597 if ((err = ftp_passive_mode(s)) < 0)
600 /* Open data connection */
601 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
602 if (s->rw_timeout != -1) {
603 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
604 } /* if option is not given, don't pass it and let tcp use its own default */
605 err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
606 &h->interrupt_callback, &opts,
607 h->protocol_whitelist, h->protocol_blacklist, h);
613 if ((err = ftp_restart(s, s->position)) < 0)
620 static int ftp_abort(URLContext *h)
622 static const char *command = "ABOR\r\n";
624 static const int abor_codes[] = {225, 226, 0};
625 FTPContext *s = h->priv_data;
627 /* According to RCF 959:
628 "ABOR command tells the server to abort the previous FTP
629 service command and any associated transfer of data."
631 There are FTP server implementations that don't response
632 to any commands during data transfer in passive mode (including ABOR).
634 This implementation closes data connection by force.
637 if (ftp_send_command(s, command, NULL, NULL) < 0) {
638 ftp_close_both_connections(s);
639 if ((err = ftp_connect_control_connection(h)) < 0) {
640 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
644 ftp_close_data_connection(s);
645 if (ftp_status(s, NULL, abor_codes) < 225) {
646 /* wu-ftpd also closes control connection after data connection closing */
647 ffurl_closep(&s->conn_control);
648 if ((err = ftp_connect_control_connection(h)) < 0) {
649 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
658 static int ftp_connect(URLContext *h, const char *url)
660 char proto[10], path[MAX_URL_SIZE], credentials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
661 const char *tok_user = NULL, *tok_pass = NULL;
662 char *newpath = NULL;
664 FTPContext *s = h->priv_data;
666 s->state = DISCONNECTED;
667 s->listing_method = UNKNOWN_METHOD;
672 av_url_split(proto, sizeof(proto),
673 credentials, sizeof(credentials),
674 hostname, sizeof(hostname),
675 &s->server_control_port,
680 if (!s->option_user) {
681 tok_user = "anonymous";
682 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
684 tok_user = s->option_user;
685 tok_pass = s->option_password;
687 s->user = av_strdup(tok_user);
688 s->password = av_strdup(tok_pass);
690 char *pass = strchr(credentials, ':');
694 s->password = ff_urldecode(pass, 0);
696 tok_pass = s->option_password;
697 s->password = av_strdup(tok_pass);
699 s->user = ff_urldecode(credentials, 0);
701 s->hostname = av_strdup(hostname);
702 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
703 return AVERROR(ENOMEM);
706 if (s->server_control_port < 0 || s->server_control_port > 65535)
707 s->server_control_port = 21;
709 if ((err = ftp_connect_control_connection(h)) < 0)
712 if ((err = ftp_current_dir(s)) < 0)
715 newpath = av_append_path_component(s->path, path);
717 return AVERROR(ENOMEM);
724 static int ftp_open(URLContext *h, const char *url, int flags)
726 FTPContext *s = h->priv_data;
729 ff_dlog(h, "ftp protocol open\n");
731 if ((err = ftp_connect(h, url)) < 0)
734 if (ftp_restart(s, 0) < 0) {
737 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
739 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
746 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
751 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
753 FTPContext *s = h->priv_data;
755 int64_t new_pos, fake_pos;
757 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
766 new_pos = s->position + pos;
771 new_pos = s->filesize + pos;
774 return AVERROR(EINVAL);
781 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
782 return AVERROR(EINVAL);
785 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
786 if (fake_pos != s->position) {
787 if ((err = ftp_abort(h)) < 0)
789 s->position = fake_pos;
794 static int ftp_read(URLContext *h, unsigned char *buf, int size)
796 FTPContext *s = h->priv_data;
797 int read, err, retry_done = 0;
799 ff_dlog(h, "ftp protocol read %d bytes\n", size);
801 if (s->state == DISCONNECTED) {
803 if (s->position >= s->filesize)
805 if ((err = ftp_connect_data_connection(h)) < 0)
808 if (s->state == READY) {
809 if (s->position >= s->filesize)
811 if ((err = ftp_retrieve(s)) < 0)
814 if (s->conn_data && s->state == DOWNLOADING) {
815 read = ffurl_read(s->conn_data, buf, size);
818 if (s->position >= s->filesize) {
819 /* server will terminate, but keep current position to avoid madness */
820 /* save position to restart from it */
821 int64_t pos = s->position;
822 if (ftp_abort(h) < 0) {
829 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
830 /* Server closed connection. Probably due to inactivity */
831 int64_t pos = s->position;
832 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
833 if ((err = ftp_abort(h)) < 0)
835 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
836 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
847 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
851 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
854 FTPContext *s = h->priv_data;
857 ff_dlog(h, "ftp protocol write %d bytes\n", size);
859 if (s->state == DISCONNECTED) {
860 if ((err = ftp_connect_data_connection(h)) < 0)
863 if (s->state == READY) {
864 if ((err = ftp_store(s)) < 0)
867 if (s->conn_data && s->state == UPLOADING) {
868 written = ffurl_write(s->conn_data, buf, size);
870 s->position += written;
871 s->filesize = FFMAX(s->filesize, s->position);
876 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
880 static int ftp_close(URLContext *h)
882 FTPContext *s = h->priv_data;
884 ff_dlog(h, "ftp protocol close\n");
886 ftp_close_both_connections(s);
888 av_freep(&s->password);
889 av_freep(&s->hostname);
891 av_freep(&s->features);
896 static int ftp_get_file_handle(URLContext *h)
898 FTPContext *s = h->priv_data;
900 ff_dlog(h, "ftp protocol get_file_handle\n");
903 return ffurl_get_file_handle(s->conn_data);
908 static int ftp_shutdown(URLContext *h, int flags)
910 FTPContext *s = h->priv_data;
912 ff_dlog(h, "ftp protocol shutdown\n");
915 return ffurl_shutdown(s->conn_data, flags);
920 static int ftp_open_dir(URLContext *h)
922 FTPContext *s = h->priv_data;
925 if ((ret = ftp_connect(h, h->filename)) < 0)
927 if ((ret = ftp_set_dir(s)) < 0)
929 if ((ret = ftp_connect_data_connection(h)) < 0)
931 if ((ret = ftp_list(s)) < 0)
933 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
934 if (!s->dir_buffer) {
935 ret = AVERROR(ENOMEM);
938 s->dir_buffer[0] = 0;
939 if (s->conn_data && s->state == LISTING_DIR)
942 ffurl_closep(&s->conn_control);
943 ffurl_closep(&s->conn_data);
947 static int64_t ftp_parse_date(const char *date)
950 memset(&tv, 0, sizeof(struct tm));
951 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
952 return INT64_C(1000000) * av_timegm(&tv);
955 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
957 next->name = av_strdup(line);
961 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
964 ff_dlog(NULL, "%s\n", mlsd);
965 while(fact = av_strtok(mlsd, ";", &mlsd)) {
966 if (fact[0] == ' ') {
967 next->name = av_strdup(&fact[1]);
970 fact = av_strtok(fact, "=", &value);
971 if (!av_strcasecmp(fact, "type")) {
972 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
974 if (!av_strcasecmp(value, "dir"))
975 next->type = AVIO_ENTRY_DIRECTORY;
976 else if (!av_strcasecmp(value, "file"))
977 next->type = AVIO_ENTRY_FILE;
978 else if (!av_strcasecmp(value, "OS.unix=slink:"))
979 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
980 } else if (!av_strcasecmp(fact, "modify")) {
981 next->modification_timestamp = ftp_parse_date(value);
982 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
983 next->filemode = strtoumax(value, NULL, 8);
984 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
985 next->user_id = strtoumax(value, NULL, 10);
986 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
987 next->group_id = strtoumax(value, NULL, 10);
988 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
989 next->size = strtoll(value, NULL, 10);
995 * @return 0 on success, negative on error, positive on entry to discard.
997 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
999 FTPContext *s = h->priv_data;
1001 switch (s->listing_method) {
1003 return ftp_parse_entry_mlsd(line, next);
1005 return ftp_parse_entry_nlst(line, next);
1006 case UNKNOWN_METHOD:
1012 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
1014 FTPContext *s = h->priv_data;
1015 char *start, *found;
1020 start = s->dir_buffer + s->dir_buffer_offset;
1021 while (!(found = strstr(start, "\n"))) {
1023 return AVERROR(EIO);
1024 s->dir_buffer_size -= s->dir_buffer_offset;
1025 s->dir_buffer_offset = 0;
1026 if (s->dir_buffer_size)
1027 memmove(s->dir_buffer, start, s->dir_buffer_size);
1028 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1035 s->dir_buffer_size += ret;
1036 s->dir_buffer[s->dir_buffer_size] = 0;
1037 start = s->dir_buffer;
1040 s->dir_buffer_offset += (found + 1 - start);
1042 if (found > start && found[-1] == '\r')
1045 *next = ff_alloc_dir_entry();
1047 return AVERROR(ENOMEM);
1048 (*next)->utf8 = s->utf8;
1049 ret = ftp_parse_entry(h, start, *next);
1051 avio_free_directory_entry(next);
1059 static int ftp_close_dir(URLContext *h)
1061 FTPContext *s = h->priv_data;
1062 av_freep(&s->dir_buffer);
1063 ffurl_closep(&s->conn_control);
1064 ffurl_closep(&s->conn_data);
1068 static int ftp_delete(URLContext *h)
1070 FTPContext *s = h->priv_data;
1071 char command[MAX_URL_SIZE];
1072 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1073 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1076 if ((ret = ftp_connect(h, h->filename)) < 0)
1079 snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1080 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1085 snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1086 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1096 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1098 FTPContext *s = h_src->priv_data;
1099 char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1100 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1101 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1104 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1107 snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1108 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1113 av_url_split(0, 0, 0, 0, 0, 0, 0,
1116 snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1117 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1127 const URLProtocol ff_ftp_protocol = {
1129 .url_open = ftp_open,
1130 .url_read = ftp_read,
1131 .url_write = ftp_write,
1132 .url_seek = ftp_seek,
1133 .url_close = ftp_close,
1134 .url_get_file_handle = ftp_get_file_handle,
1135 .url_shutdown = ftp_shutdown,
1136 .priv_data_size = sizeof(FTPContext),
1137 .priv_data_class = &ftp_context_class,
1138 .url_open_dir = ftp_open_dir,
1139 .url_read_dir = ftp_read_dir,
1140 .url_close_dir = ftp_close_dir,
1141 .url_delete = ftp_delete,
1142 .url_move = ftp_move,
1143 .flags = URL_PROTOCOL_FLAG_NETWORK,
1144 .default_whitelist = "tcp",