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;
72 const char *option_user; /**< User to be used if none given in the URL */
73 const char *option_password; /**< Password to be used if none given in the URL */
76 #define OFFSET(x) offsetof(FTPContext, x)
77 #define D AV_OPT_FLAG_DECODING_PARAM
78 #define E AV_OPT_FLAG_ENCODING_PARAM
79 static const AVOption options[] = {
80 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
81 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
82 {"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 },
83 {"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 },
84 {"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 },
88 static const AVClass ftp_context_class = {
90 .item_name = av_default_item_name,
92 .version = LIBAVUTIL_VERSION_INT,
95 static int ftp_close(URLContext *h);
97 static int ftp_getc(FTPContext *s)
100 if (s->control_buf_ptr >= s->control_buf_end) {
101 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
107 s->control_buf_ptr = s->control_buffer;
108 s->control_buf_end = s->control_buffer + len;
111 return *s->control_buf_ptr++;
114 static int ftp_get_line(FTPContext *s, char *line, int line_size)
126 if (q > line && q[-1] == '\r')
131 if ((q - line) < line_size - 1)
138 * This routine returns ftp server response code.
139 * Server may send more than one response for a certain command.
140 * First expected code is returned.
142 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
144 int err, i, dash = 0, result = 0, code_found = 0, linesize;
145 char buf[CONTROL_BUFFER_SIZE];
146 AVBPrint line_buffer;
149 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
151 while (!code_found || dash) {
152 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
154 av_bprint_finalize(&line_buffer, NULL);
158 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
160 linesize = strlen(buf);
163 for (i = 0; i < 3; ++i) {
164 if (buf[i] < '0' || buf[i] > '9') {
178 for (i = 0; response_codes[i]; ++i) {
179 if (err == response_codes[i]) {
188 av_bprintf(&line_buffer, "%s\r\n", buf);
190 if (!dash && buf[3] == '-')
192 else if (err == dash && buf[3] == ' ')
199 av_bprint_finalize(&line_buffer, line);
203 static int ftp_send_command(FTPContext *s, const char *command,
204 const int response_codes[], char **response)
208 ff_dlog(s, "%s", command);
213 if (!s->conn_control)
216 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
222 if (response_codes) {
223 return ftp_status(s, response, response_codes);
228 static void ftp_close_data_connection(FTPContext *s)
230 ffurl_closep(&s->conn_data);
232 s->state = DISCONNECTED;
235 static void ftp_close_both_connections(FTPContext *s)
237 ffurl_closep(&s->conn_control);
238 ftp_close_data_connection(s);
241 static int ftp_auth(FTPContext *s)
243 char buf[CONTROL_BUFFER_SIZE];
245 static const int user_codes[] = {331, 230, 0};
246 static const int pass_codes[] = {230, 0};
248 snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
249 err = ftp_send_command(s, buf, user_codes, NULL);
252 snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
253 err = ftp_send_command(s, buf, pass_codes, NULL);
255 return AVERROR(EACCES);
258 return AVERROR(EACCES);
263 static int ftp_passive_mode_epsv(FTPContext *s)
265 char *res = NULL, *start = NULL, *end = NULL;
267 static const char d = '|';
268 static const char *command = "EPSV\r\n";
269 static const int epsv_codes[] = {229, 0};
271 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
274 for (i = 0; res[i]; ++i) {
277 } else if (res[i] == ')') {
286 if (strlen(start) < 5)
288 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
293 s->server_data_port = atoi(start);
294 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
301 s->server_data_port = -1;
302 return AVERROR(ENOSYS);
305 static int ftp_passive_mode(FTPContext *s)
307 char *res = NULL, *start = NULL, *end = NULL;
309 static const char *command = "PASV\r\n";
310 static const int pasv_codes[] = {227, 0};
312 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
315 for (i = 0; res[i]; ++i) {
318 } else if (res[i] == ')') {
328 if (!av_strtok(start, ",", &end)) goto fail;
329 if (!av_strtok(end, ",", &end)) goto fail;
330 if (!av_strtok(end, ",", &end)) goto fail;
331 if (!av_strtok(end, ",", &end)) goto fail;
333 /* parse port number */
334 start = av_strtok(end, ",", &end);
335 if (!start) goto fail;
336 s->server_data_port = atoi(start) * 256;
337 start = av_strtok(end, ",", &end);
338 if (!start) goto fail;
339 s->server_data_port += atoi(start);
340 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
347 s->server_data_port = -1;
351 static int ftp_current_dir(FTPContext *s)
353 char *res = NULL, *start = NULL, *end = NULL;
355 static const char *command = "PWD\r\n";
356 static const int pwd_codes[] = {257, 0};
358 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
361 for (i = 0; res[i]; ++i) {
376 s->path = av_strdup(start);
381 return AVERROR(ENOMEM);
389 static int ftp_file_size(FTPContext *s)
391 char command[CONTROL_BUFFER_SIZE];
393 static const int size_codes[] = {213, 0};
395 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
396 if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) {
397 s->filesize = strtoll(&res[4], NULL, 10);
408 static int ftp_retrieve(FTPContext *s)
410 char command[CONTROL_BUFFER_SIZE];
411 static const int retr_codes[] = {150, 125, 0};
414 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
415 resp_code = ftp_send_command(s, command, retr_codes, NULL);
416 if (resp_code != 125 && resp_code != 150)
419 s->state = DOWNLOADING;
424 static int ftp_store(FTPContext *s)
426 char command[CONTROL_BUFFER_SIZE];
427 static const int stor_codes[] = {150, 125, 0};
430 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
431 resp_code = ftp_send_command(s, command, stor_codes, NULL);
432 if (resp_code != 125 && resp_code != 150)
435 s->state = UPLOADING;
440 static int ftp_type(FTPContext *s)
442 static const char *command = "TYPE I\r\n";
443 static const int type_codes[] = {200, 0};
445 if (ftp_send_command(s, command, type_codes, NULL) != 200)
451 static int ftp_restart(FTPContext *s, int64_t pos)
453 char command[CONTROL_BUFFER_SIZE];
454 static const int rest_codes[] = {350, 0};
456 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
457 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
463 static int ftp_set_dir(FTPContext *s)
465 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
466 char command[MAX_URL_SIZE];
468 snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
469 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
474 static int ftp_list_mlsd(FTPContext *s)
476 static const char *command = "MLSD\r\n";
477 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
479 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
480 return AVERROR(ENOSYS);
481 s->listing_method = MLSD;
485 static int ftp_list_nlst(FTPContext *s)
487 static const char *command = "NLST\r\n";
488 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
490 if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
491 return AVERROR(ENOSYS);
492 s->listing_method = NLST;
496 static int ftp_list(FTPContext *s)
499 s->state = LISTING_DIR;
501 if ((ret = ftp_list_mlsd(s)) < 0)
502 ret = ftp_list_nlst(s);
507 static int ftp_has_feature(FTPContext *s, const char *feature_name)
512 return av_stristr(s->features, feature_name) != NULL;
515 static int ftp_features(FTPContext *s)
517 static const char *feat_command = "FEAT\r\n";
518 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
519 static const int feat_codes[] = {211, 0};
520 static const int opts_codes[] = {200, 202, 451, 0};
522 av_freep(&s->features);
523 if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
524 av_freep(&s->features);
527 if (ftp_has_feature(s, "UTF8")) {
528 int ret = ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
529 if (ret == 200 || ret == 202)
536 static int ftp_connect_control_connection(URLContext *h)
538 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
540 AVDictionary *opts = NULL;
541 FTPContext *s = h->priv_data;
542 static const int connect_codes[] = {220, 0};
544 if (!s->conn_control) {
545 ff_url_join(buf, sizeof(buf), "tcp", NULL,
546 s->hostname, s->server_control_port, NULL);
547 if (s->rw_timeout != -1) {
548 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
549 } /* if option is not given, don't pass it and let tcp use its own default */
550 err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
551 &h->interrupt_callback, &opts,
552 h->protocol_whitelist, h->protocol_blacklist, h);
555 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
559 /* check if server is ready */
560 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
561 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
562 return AVERROR(EACCES);
565 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
566 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.");
570 if ((err = ftp_auth(s)) < 0) {
571 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
575 if ((err = ftp_type(s)) < 0) {
576 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
585 static int ftp_connect_data_connection(URLContext *h)
588 char buf[CONTROL_BUFFER_SIZE];
589 AVDictionary *opts = NULL;
590 FTPContext *s = h->priv_data;
593 /* Enter passive mode */
594 if (ftp_passive_mode_epsv(s) < 0) {
595 /* Use PASV as fallback */
596 if ((err = ftp_passive_mode(s)) < 0)
599 /* Open data connection */
600 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
601 if (s->rw_timeout != -1) {
602 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
603 } /* if option is not given, don't pass it and let tcp use its own default */
604 err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
605 &h->interrupt_callback, &opts,
606 h->protocol_whitelist, h->protocol_blacklist, h);
612 if ((err = ftp_restart(s, s->position)) < 0)
619 static int ftp_abort(URLContext *h)
621 static const char *command = "ABOR\r\n";
623 static const int abor_codes[] = {225, 226, 0};
624 FTPContext *s = h->priv_data;
626 /* According to RCF 959:
627 "ABOR command tells the server to abort the previous FTP
628 service command and any associated transfer of data."
630 There are FTP server implementations that don't response
631 to any commands during data transfer in passive mode (including ABOR).
633 This implementation closes data connection by force.
636 if (ftp_send_command(s, command, NULL, NULL) < 0) {
637 ftp_close_both_connections(s);
638 if ((err = ftp_connect_control_connection(h)) < 0) {
639 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
643 ftp_close_data_connection(s);
644 if (ftp_status(s, NULL, abor_codes) < 225) {
645 /* wu-ftpd also closes control connection after data connection closing */
646 ffurl_closep(&s->conn_control);
647 if ((err = ftp_connect_control_connection(h)) < 0) {
648 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
657 static int ftp_connect(URLContext *h, const char *url)
659 char proto[10], path[MAX_URL_SIZE], credentials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
660 const char *tok_user = NULL, *tok_pass = NULL;
661 char *end = NULL, *newpath = NULL;
663 FTPContext *s = h->priv_data;
665 s->state = DISCONNECTED;
666 s->listing_method = UNKNOWN_METHOD;
671 av_url_split(proto, sizeof(proto),
672 credentials, sizeof(credentials),
673 hostname, sizeof(hostname),
674 &s->server_control_port,
678 tok_user = av_strtok(credentials, ":", &end);
679 tok_pass = av_strtok(end, ":", &end);
681 if (!s->option_user) {
682 tok_user = "anonymous";
683 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
685 tok_user = s->option_user;
689 tok_pass = s->option_password;
691 s->user = av_strdup(tok_user);
692 s->password = av_strdup(tok_pass);
693 s->hostname = av_strdup(hostname);
694 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
695 return AVERROR(ENOMEM);
698 if (s->server_control_port < 0 || s->server_control_port > 65535)
699 s->server_control_port = 21;
701 if ((err = ftp_connect_control_connection(h)) < 0)
704 if ((err = ftp_current_dir(s)) < 0)
707 newpath = av_append_path_component(s->path, path);
709 return AVERROR(ENOMEM);
716 static int ftp_open(URLContext *h, const char *url, int flags)
718 FTPContext *s = h->priv_data;
721 ff_dlog(h, "ftp protocol open\n");
723 if ((err = ftp_connect(h, url)) < 0)
726 if (ftp_restart(s, 0) < 0) {
729 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
731 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
738 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
743 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
745 FTPContext *s = h->priv_data;
747 int64_t new_pos, fake_pos;
749 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
758 new_pos = s->position + pos;
763 new_pos = s->filesize + pos;
766 return AVERROR(EINVAL);
773 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
774 return AVERROR(EINVAL);
777 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
778 if (fake_pos != s->position) {
779 if ((err = ftp_abort(h)) < 0)
781 s->position = fake_pos;
786 static int ftp_read(URLContext *h, unsigned char *buf, int size)
788 FTPContext *s = h->priv_data;
789 int read, err, retry_done = 0;
791 ff_dlog(h, "ftp protocol read %d bytes\n", size);
793 if (s->state == DISCONNECTED) {
795 if (s->position >= s->filesize)
797 if ((err = ftp_connect_data_connection(h)) < 0)
800 if (s->state == READY) {
801 if (s->position >= s->filesize)
803 if ((err = ftp_retrieve(s)) < 0)
806 if (s->conn_data && s->state == DOWNLOADING) {
807 read = ffurl_read(s->conn_data, buf, size);
810 if (s->position >= s->filesize) {
811 /* server will terminate, but keep current position to avoid madness */
812 /* save position to restart from it */
813 int64_t pos = s->position;
814 if (ftp_abort(h) < 0) {
821 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
822 /* Server closed connection. Probably due to inactivity */
823 int64_t pos = s->position;
824 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
825 if ((err = ftp_abort(h)) < 0)
827 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
828 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
839 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
843 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
846 FTPContext *s = h->priv_data;
849 ff_dlog(h, "ftp protocol write %d bytes\n", size);
851 if (s->state == DISCONNECTED) {
852 if ((err = ftp_connect_data_connection(h)) < 0)
855 if (s->state == READY) {
856 if ((err = ftp_store(s)) < 0)
859 if (s->conn_data && s->state == UPLOADING) {
860 written = ffurl_write(s->conn_data, buf, size);
862 s->position += written;
863 s->filesize = FFMAX(s->filesize, s->position);
868 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
872 static int ftp_close(URLContext *h)
874 FTPContext *s = h->priv_data;
876 ff_dlog(h, "ftp protocol close\n");
878 ftp_close_both_connections(s);
880 av_freep(&s->password);
881 av_freep(&s->hostname);
883 av_freep(&s->features);
888 static int ftp_get_file_handle(URLContext *h)
890 FTPContext *s = h->priv_data;
892 ff_dlog(h, "ftp protocol get_file_handle\n");
895 return ffurl_get_file_handle(s->conn_data);
900 static int ftp_shutdown(URLContext *h, int flags)
902 FTPContext *s = h->priv_data;
904 ff_dlog(h, "ftp protocol shutdown\n");
907 return ffurl_shutdown(s->conn_data, flags);
912 static int ftp_open_dir(URLContext *h)
914 FTPContext *s = h->priv_data;
917 if ((ret = ftp_connect(h, h->filename)) < 0)
919 if ((ret = ftp_set_dir(s)) < 0)
921 if ((ret = ftp_connect_data_connection(h)) < 0)
923 if ((ret = ftp_list(s)) < 0)
925 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
926 if (!s->dir_buffer) {
927 ret = AVERROR(ENOMEM);
930 s->dir_buffer[0] = 0;
931 if (s->conn_data && s->state == LISTING_DIR)
934 ffurl_closep(&s->conn_control);
935 ffurl_closep(&s->conn_data);
939 static int64_t ftp_parse_date(const char *date)
942 memset(&tv, 0, sizeof(struct tm));
943 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
944 return INT64_C(1000000) * av_timegm(&tv);
947 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
949 next->name = av_strdup(line);
953 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
956 ff_dlog(NULL, "%s\n", mlsd);
957 while(fact = av_strtok(mlsd, ";", &mlsd)) {
958 if (fact[0] == ' ') {
959 next->name = av_strdup(&fact[1]);
962 fact = av_strtok(fact, "=", &value);
963 if (!av_strcasecmp(fact, "type")) {
964 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
966 if (!av_strcasecmp(value, "dir"))
967 next->type = AVIO_ENTRY_DIRECTORY;
968 else if (!av_strcasecmp(value, "file"))
969 next->type = AVIO_ENTRY_FILE;
970 else if (!av_strcasecmp(value, "OS.unix=slink:"))
971 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
972 } else if (!av_strcasecmp(fact, "modify")) {
973 next->modification_timestamp = ftp_parse_date(value);
974 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
975 next->filemode = strtoumax(value, NULL, 8);
976 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
977 next->user_id = strtoumax(value, NULL, 10);
978 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
979 next->group_id = strtoumax(value, NULL, 10);
980 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
981 next->size = strtoll(value, NULL, 10);
987 * @return 0 on success, negative on error, positive on entry to discard.
989 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
991 FTPContext *s = h->priv_data;
993 switch (s->listing_method) {
995 return ftp_parse_entry_mlsd(line, next);
997 return ftp_parse_entry_nlst(line, next);
1004 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
1006 FTPContext *s = h->priv_data;
1007 char *start, *found;
1012 start = s->dir_buffer + s->dir_buffer_offset;
1013 while (!(found = strstr(start, "\n"))) {
1015 return AVERROR(EIO);
1016 s->dir_buffer_size -= s->dir_buffer_offset;
1017 s->dir_buffer_offset = 0;
1018 if (s->dir_buffer_size)
1019 memmove(s->dir_buffer, start, s->dir_buffer_size);
1020 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1027 s->dir_buffer_size += ret;
1028 s->dir_buffer[s->dir_buffer_size] = 0;
1029 start = s->dir_buffer;
1032 s->dir_buffer_offset += (found + 1 - start);
1034 if (found > start && found[-1] == '\r')
1037 *next = ff_alloc_dir_entry();
1039 return AVERROR(ENOMEM);
1040 (*next)->utf8 = s->utf8;
1041 ret = ftp_parse_entry(h, start, *next);
1043 avio_free_directory_entry(next);
1051 static int ftp_close_dir(URLContext *h)
1053 FTPContext *s = h->priv_data;
1054 av_freep(&s->dir_buffer);
1055 ffurl_closep(&s->conn_control);
1056 ffurl_closep(&s->conn_data);
1060 static int ftp_delete(URLContext *h)
1062 FTPContext *s = h->priv_data;
1063 char command[MAX_URL_SIZE];
1064 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1065 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1068 if ((ret = ftp_connect(h, h->filename)) < 0)
1071 snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1072 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1077 snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1078 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1088 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1090 FTPContext *s = h_src->priv_data;
1091 char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1092 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1093 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1096 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1099 snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1100 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1105 av_url_split(0, 0, 0, 0, 0, 0, 0,
1108 snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1109 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1119 const URLProtocol ff_ftp_protocol = {
1121 .url_open = ftp_open,
1122 .url_read = ftp_read,
1123 .url_write = ftp_write,
1124 .url_seek = ftp_seek,
1125 .url_close = ftp_close,
1126 .url_get_file_handle = ftp_get_file_handle,
1127 .url_shutdown = ftp_shutdown,
1128 .priv_data_size = sizeof(FTPContext),
1129 .priv_data_class = &ftp_context_class,
1130 .url_open_dir = ftp_open_dir,
1131 .url_read_dir = ftp_read_dir,
1132 .url_close_dir = ftp_close_dir,
1133 .url_delete = ftp_delete,
1134 .url_move = ftp_move,
1135 .flags = URL_PROTOCOL_FLAG_NETWORK,
1136 .default_whitelist = "tcp",