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
23 #include "libavutil/avstring.h"
24 #include "libavutil/internal.h"
25 #include "libavutil/parseutils.h"
29 #include "urldecode.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/bprint.h"
33 #define CONTROL_BUFFER_SIZE 1024
34 #define DIR_BUFFER_SIZE 4096
54 URLContext *conn_control; /**< Control connection */
55 URLContext *conn_data; /**< Data connection, NULL when not connected */
56 uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
57 uint8_t *control_buf_ptr, *control_buf_end;
58 int server_data_port; /**< Data connection port opened by server, -1 on error. */
59 int server_control_port; /**< Control connection port, default is 21 */
60 char *hostname; /**< Server address. */
61 char *user; /**< Server user */
62 char *password; /**< Server user's password */
63 char *path; /**< Path to resource on server. */
64 int64_t filesize; /**< Size of file on server, -1 on error. */
65 int64_t position; /**< Current position, calculated. */
66 int rw_timeout; /**< Network timeout. */
67 const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
68 int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
69 FTPState state; /**< State of data connection */
70 FTPListingMethod listing_method; /**< Called listing method */
71 char *features; /**< List of server's features represented as raw response */
73 size_t dir_buffer_size;
74 size_t dir_buffer_offset;
76 const char *option_user; /**< User to be used if none given in the URL */
77 const char *option_password; /**< Password to be used if none given in the URL */
80 #define OFFSET(x) offsetof(FTPContext, x)
81 #define D AV_OPT_FLAG_DECODING_PARAM
82 #define E AV_OPT_FLAG_ENCODING_PARAM
83 static const AVOption options[] = {
84 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
85 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
86 {"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 },
87 {"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 },
88 {"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 },
92 static const AVClass ftp_context_class = {
94 .item_name = av_default_item_name,
96 .version = LIBAVUTIL_VERSION_INT,
99 static int ftp_close(URLContext *h);
101 static int ftp_getc(FTPContext *s)
104 if (s->control_buf_ptr >= s->control_buf_end) {
105 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
111 s->control_buf_ptr = s->control_buffer;
112 s->control_buf_end = s->control_buffer + len;
115 return *s->control_buf_ptr++;
118 static int ftp_get_line(FTPContext *s, char *line, int line_size)
130 if (q > line && q[-1] == '\r')
135 if ((q - line) < line_size - 1)
142 * This routine returns ftp server response code.
143 * Server may send more than one response for a certain command.
144 * First expected code is returned.
146 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
148 int err, i, dash = 0, result = 0, code_found = 0, linesize;
149 char buf[CONTROL_BUFFER_SIZE];
150 AVBPrint line_buffer;
153 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
155 while (!code_found || dash) {
156 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
158 av_bprint_finalize(&line_buffer, NULL);
162 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
164 linesize = strlen(buf);
167 for (i = 0; i < 3; ++i) {
168 if (buf[i] < '0' || buf[i] > '9') {
182 for (i = 0; response_codes[i]; ++i) {
183 if (err == response_codes[i]) {
192 av_bprintf(&line_buffer, "%s\r\n", buf);
194 if (!dash && buf[3] == '-')
196 else if (err == dash && buf[3] == ' ')
203 av_bprint_finalize(&line_buffer, line);
207 static int ftp_send_command(FTPContext *s, const char *command,
208 const int response_codes[], char **response)
212 ff_dlog(s, "%s", command);
217 if (!s->conn_control)
220 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
226 if (response_codes) {
227 return ftp_status(s, response, response_codes);
232 static void ftp_close_data_connection(FTPContext *s)
234 ffurl_closep(&s->conn_data);
235 s->state = DISCONNECTED;
238 static void ftp_close_both_connections(FTPContext *s)
240 ffurl_closep(&s->conn_control);
241 ftp_close_data_connection(s);
244 static int ftp_auth(FTPContext *s)
246 char buf[CONTROL_BUFFER_SIZE];
248 static const int user_codes[] = {331, 230, 0};
249 static const int pass_codes[] = {230, 0};
251 if (strpbrk(s->user, "\r\n"))
252 return AVERROR(EINVAL);
253 snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
254 err = ftp_send_command(s, buf, user_codes, NULL);
257 if (strpbrk(s->password, "\r\n"))
258 return AVERROR(EINVAL);
259 snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
260 err = ftp_send_command(s, buf, pass_codes, NULL);
262 return AVERROR(EACCES);
265 return AVERROR(EACCES);
270 static int ftp_passive_mode_epsv(FTPContext *s)
272 char *res = NULL, *start = NULL, *end = NULL;
274 static const char d = '|';
275 static const char *command = "EPSV\r\n";
276 static const int epsv_codes[] = {229, 0};
278 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
281 for (i = 0; res[i]; ++i) {
284 } else if (res[i] == ')') {
293 if (strlen(start) < 5)
295 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
300 s->server_data_port = atoi(start);
301 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
308 s->server_data_port = -1;
309 return AVERROR(ENOSYS);
312 static int ftp_passive_mode(FTPContext *s)
314 char *res = NULL, *start = NULL, *end = NULL;
316 static const char *command = "PASV\r\n";
317 static const int pasv_codes[] = {227, 0};
319 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
322 for (i = 0; res[i]; ++i) {
325 } else if (res[i] == ')') {
335 if (!av_strtok(start, ",", &end)) goto fail;
336 if (!av_strtok(NULL, ",", &end)) goto fail;
337 if (!av_strtok(NULL, ",", &end)) goto fail;
338 if (!av_strtok(NULL, ",", &end)) goto fail;
340 /* parse port number */
341 start = av_strtok(NULL, ",", &end);
342 if (!start) goto fail;
343 s->server_data_port = atoi(start) * 256;
344 start = av_strtok(NULL, ",", &end);
345 if (!start) goto fail;
346 s->server_data_port += atoi(start);
347 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
354 s->server_data_port = -1;
358 static int ftp_current_dir(FTPContext *s)
360 char *res = NULL, *start = NULL, *end = NULL;
362 static const char *command = "PWD\r\n";
363 static const int pwd_codes[] = {257, 0};
365 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
368 for (i = 0; res[i]; ++i) {
383 s->path = av_strdup(start);
388 return AVERROR(ENOMEM);
396 static int ftp_file_size(FTPContext *s)
398 char command[CONTROL_BUFFER_SIZE];
400 static const int size_codes[] = {213, 0};
402 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
403 if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) {
404 s->filesize = strtoll(&res[4], NULL, 10);
415 static int ftp_retrieve(FTPContext *s)
417 char command[CONTROL_BUFFER_SIZE];
418 static const int retr_codes[] = {150, 125, 0};
421 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
422 resp_code = ftp_send_command(s, command, retr_codes, NULL);
423 if (resp_code != 125 && resp_code != 150)
426 s->state = DOWNLOADING;
431 static int ftp_store(FTPContext *s)
433 char command[CONTROL_BUFFER_SIZE];
434 static const int stor_codes[] = {150, 125, 0};
437 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
438 resp_code = ftp_send_command(s, command, stor_codes, NULL);
439 if (resp_code != 125 && resp_code != 150)
442 s->state = UPLOADING;
447 static int ftp_type(FTPContext *s)
449 static const char *command = "TYPE I\r\n";
450 static const int type_codes[] = {200, 0};
452 if (ftp_send_command(s, command, type_codes, NULL) != 200)
458 static int ftp_restart(FTPContext *s, int64_t pos)
460 char command[CONTROL_BUFFER_SIZE];
461 static const int rest_codes[] = {350, 0};
463 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
464 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
470 static int ftp_set_dir(FTPContext *s)
472 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
473 char command[MAX_URL_SIZE];
475 snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
476 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
481 static int ftp_list_mlsd(FTPContext *s)
483 static const char *command = "MLSD\r\n";
484 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
486 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
487 return AVERROR(ENOSYS);
488 s->listing_method = MLSD;
492 static int ftp_list_nlst(FTPContext *s)
494 static const char *command = "NLST\r\n";
495 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
497 if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
498 return AVERROR(ENOSYS);
499 s->listing_method = NLST;
503 static int ftp_list(FTPContext *s)
506 s->state = LISTING_DIR;
508 if ((ret = ftp_list_mlsd(s)) < 0)
509 ret = ftp_list_nlst(s);
514 static int ftp_has_feature(FTPContext *s, const char *feature_name)
519 return av_stristr(s->features, feature_name) != NULL;
522 static int ftp_features(FTPContext *s)
524 static const char *feat_command = "FEAT\r\n";
525 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
526 static const int feat_codes[] = {211, 0};
527 static const int opts_codes[] = {200, 202, 451, 0};
529 av_freep(&s->features);
530 if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
531 av_freep(&s->features);
534 if (ftp_has_feature(s, "UTF8")) {
535 int ret = ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
536 if (ret == 200 || ret == 202)
543 static int ftp_connect_control_connection(URLContext *h)
545 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
547 AVDictionary *opts = NULL;
548 FTPContext *s = h->priv_data;
549 static const int connect_codes[] = {220, 0};
551 if (!s->conn_control) {
552 ff_url_join(buf, sizeof(buf), "tcp", NULL,
553 s->hostname, s->server_control_port, NULL);
554 if (s->rw_timeout != -1) {
555 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
556 } /* if option is not given, don't pass it and let tcp use its own default */
557 err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
558 &h->interrupt_callback, &opts,
559 h->protocol_whitelist, h->protocol_blacklist, h);
562 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
566 /* check if server is ready */
567 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
568 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
569 return AVERROR(EACCES);
572 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
573 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.");
577 if ((err = ftp_auth(s)) < 0) {
578 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
582 if ((err = ftp_type(s)) < 0) {
583 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
592 static int ftp_connect_data_connection(URLContext *h)
595 char buf[CONTROL_BUFFER_SIZE];
596 AVDictionary *opts = NULL;
597 FTPContext *s = h->priv_data;
600 /* Enter passive mode */
601 if (ftp_passive_mode_epsv(s) < 0) {
602 /* Use PASV as fallback */
603 if ((err = ftp_passive_mode(s)) < 0)
606 /* Open data connection */
607 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
608 if (s->rw_timeout != -1) {
609 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
610 } /* if option is not given, don't pass it and let tcp use its own default */
611 err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
612 &h->interrupt_callback, &opts,
613 h->protocol_whitelist, h->protocol_blacklist, h);
619 if ((err = ftp_restart(s, s->position)) < 0)
626 static int ftp_abort(URLContext *h)
628 static const char *command = "ABOR\r\n";
630 static const int abor_codes[] = {225, 226, 0};
631 FTPContext *s = h->priv_data;
633 /* According to RCF 959:
634 "ABOR command tells the server to abort the previous FTP
635 service command and any associated transfer of data."
637 There are FTP server implementations that don't response
638 to any commands during data transfer in passive mode (including ABOR).
640 This implementation closes data connection by force.
643 if (ftp_send_command(s, command, NULL, NULL) < 0) {
644 ftp_close_both_connections(s);
645 if ((err = ftp_connect_control_connection(h)) < 0) {
646 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
650 ftp_close_data_connection(s);
651 if (ftp_status(s, NULL, abor_codes) < 225) {
652 /* wu-ftpd also closes control connection after data connection closing */
653 ffurl_closep(&s->conn_control);
654 if ((err = ftp_connect_control_connection(h)) < 0) {
655 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
664 static int ftp_connect(URLContext *h, const char *url)
666 char proto[10], path[MAX_URL_SIZE], credentials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
667 const char *tok_user = NULL, *tok_pass = NULL;
668 char *newpath = NULL;
670 FTPContext *s = h->priv_data;
672 s->state = DISCONNECTED;
673 s->listing_method = UNKNOWN_METHOD;
678 av_url_split(proto, sizeof(proto),
679 credentials, sizeof(credentials),
680 hostname, sizeof(hostname),
681 &s->server_control_port,
686 if (!s->option_user) {
687 tok_user = "anonymous";
688 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
690 tok_user = s->option_user;
691 tok_pass = s->option_password;
693 s->user = av_strdup(tok_user);
694 s->password = av_strdup(tok_pass);
696 char *pass = strchr(credentials, ':');
700 s->password = ff_urldecode(pass, 0);
702 tok_pass = s->option_password;
703 s->password = av_strdup(tok_pass);
705 s->user = ff_urldecode(credentials, 0);
707 s->hostname = av_strdup(hostname);
708 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
709 return AVERROR(ENOMEM);
712 if (s->server_control_port < 0 || s->server_control_port > 65535)
713 s->server_control_port = 21;
715 if ((err = ftp_connect_control_connection(h)) < 0)
718 if ((err = ftp_current_dir(s)) < 0)
721 newpath = av_append_path_component(s->path, path);
723 return AVERROR(ENOMEM);
730 static int ftp_open(URLContext *h, const char *url, int flags)
732 FTPContext *s = h->priv_data;
735 ff_dlog(h, "ftp protocol open\n");
737 if ((err = ftp_connect(h, url)) < 0)
740 if (ftp_restart(s, 0) < 0) {
744 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
751 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
756 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
758 FTPContext *s = h->priv_data;
762 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
771 new_pos = s->position + pos;
776 new_pos = s->filesize + pos;
779 return AVERROR(EINVAL);
786 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
787 return AVERROR(EINVAL);
790 if (new_pos != s->position) {
791 if ((err = ftp_abort(h)) < 0)
793 s->position = new_pos;
798 static int ftp_read(URLContext *h, unsigned char *buf, int size)
800 FTPContext *s = h->priv_data;
801 int read, err, retry_done = 0;
803 ff_dlog(h, "ftp protocol read %d bytes\n", size);
805 if (s->state == ENDOFFILE)
807 if (s->state == DISCONNECTED) {
808 if ((err = ftp_connect_data_connection(h)) < 0)
811 if (s->state == READY) {
812 if ((err = ftp_retrieve(s)) < 0)
815 if (s->conn_data && s->state == DOWNLOADING) {
816 read = ffurl_read(s->conn_data, buf, size);
819 s->filesize = FFMAX(s->filesize, s->position);
821 if (read == AVERROR_EOF) {
822 static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
823 char *response = NULL;
824 err = ftp_status(s, &response, retr_codes);
826 ftp_close_data_connection(s);
828 s->state = ENDOFFILE;
831 /* 250 is not allowed, any other status means some kind of error */
832 av_log(h, AV_LOG_ERROR, "FTP transfer failed: %s\n", response ? response : (err < 0 ? av_err2str(err) : "?"));
836 if (read <= 0 && !h->is_streamed) {
837 /* Server closed connection. Probably due to inactivity */
838 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
839 if ((err = ftp_abort(h)) < 0)
849 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
853 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
856 FTPContext *s = h->priv_data;
859 ff_dlog(h, "ftp protocol write %d bytes\n", size);
861 if (s->state == DISCONNECTED) {
862 if ((err = ftp_connect_data_connection(h)) < 0)
865 if (s->state == READY) {
866 if ((err = ftp_store(s)) < 0)
869 if (s->conn_data && s->state == UPLOADING) {
870 written = ffurl_write(s->conn_data, buf, size);
872 s->position += written;
873 s->filesize = FFMAX(s->filesize, s->position);
878 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
882 static int ftp_close(URLContext *h)
884 FTPContext *s = h->priv_data;
886 ff_dlog(h, "ftp protocol close\n");
888 ftp_close_both_connections(s);
890 av_freep(&s->password);
891 av_freep(&s->hostname);
893 av_freep(&s->features);
898 static int ftp_get_file_handle(URLContext *h)
900 FTPContext *s = h->priv_data;
902 ff_dlog(h, "ftp protocol get_file_handle\n");
905 return ffurl_get_file_handle(s->conn_data);
910 static int ftp_shutdown(URLContext *h, int flags)
912 FTPContext *s = h->priv_data;
914 ff_dlog(h, "ftp protocol shutdown\n");
917 return ffurl_shutdown(s->conn_data, flags);
922 static int ftp_open_dir(URLContext *h)
924 FTPContext *s = h->priv_data;
927 if ((ret = ftp_connect(h, h->filename)) < 0)
929 if ((ret = ftp_set_dir(s)) < 0)
931 if ((ret = ftp_connect_data_connection(h)) < 0)
933 if ((ret = ftp_list(s)) < 0)
935 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
936 if (!s->dir_buffer) {
937 ret = AVERROR(ENOMEM);
940 s->dir_buffer[0] = 0;
941 if (s->conn_data && s->state == LISTING_DIR)
944 ffurl_closep(&s->conn_control);
945 ffurl_closep(&s->conn_data);
949 static int64_t ftp_parse_date(const char *date)
952 memset(&tv, 0, sizeof(struct tm));
953 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
954 return INT64_C(1000000) * av_timegm(&tv);
957 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
959 next->name = av_strdup(line);
963 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
966 char *saveptr = NULL, *p = mlsd;
967 ff_dlog(NULL, "%s\n", mlsd);
968 while(fact = av_strtok(p, ";", &saveptr)) {
970 if (fact[0] == ' ') {
971 next->name = av_strdup(&fact[1]);
974 fact = av_strtok(fact, "=", &value);
975 if (!av_strcasecmp(fact, "type")) {
976 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
978 if (!av_strcasecmp(value, "dir"))
979 next->type = AVIO_ENTRY_DIRECTORY;
980 else if (!av_strcasecmp(value, "file"))
981 next->type = AVIO_ENTRY_FILE;
982 else if (!av_strcasecmp(value, "OS.unix=slink:"))
983 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
984 } else if (!av_strcasecmp(fact, "modify")) {
985 next->modification_timestamp = ftp_parse_date(value);
986 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
987 next->filemode = strtoumax(value, NULL, 8);
988 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
989 next->user_id = strtoumax(value, NULL, 10);
990 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
991 next->group_id = strtoumax(value, NULL, 10);
992 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
993 next->size = strtoll(value, NULL, 10);
999 * @return 0 on success, negative on error, positive on entry to discard.
1001 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
1003 FTPContext *s = h->priv_data;
1005 switch (s->listing_method) {
1007 return ftp_parse_entry_mlsd(line, next);
1009 return ftp_parse_entry_nlst(line, next);
1010 case UNKNOWN_METHOD:
1016 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
1018 FTPContext *s = h->priv_data;
1019 char *start, *found;
1024 start = s->dir_buffer + s->dir_buffer_offset;
1025 while (!(found = strstr(start, "\n"))) {
1027 return AVERROR(EIO);
1028 s->dir_buffer_size -= s->dir_buffer_offset;
1029 s->dir_buffer_offset = 0;
1030 if (s->dir_buffer_size)
1031 memmove(s->dir_buffer, start, s->dir_buffer_size);
1032 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1039 s->dir_buffer_size += ret;
1040 s->dir_buffer[s->dir_buffer_size] = 0;
1041 start = s->dir_buffer;
1044 s->dir_buffer_offset += (found + 1 - start);
1046 if (found > start && found[-1] == '\r')
1049 *next = ff_alloc_dir_entry();
1051 return AVERROR(ENOMEM);
1052 (*next)->utf8 = s->utf8;
1053 ret = ftp_parse_entry(h, start, *next);
1055 avio_free_directory_entry(next);
1063 static int ftp_close_dir(URLContext *h)
1065 FTPContext *s = h->priv_data;
1066 av_freep(&s->dir_buffer);
1067 ffurl_closep(&s->conn_control);
1068 ffurl_closep(&s->conn_data);
1072 static int ftp_delete(URLContext *h)
1074 FTPContext *s = h->priv_data;
1075 char command[MAX_URL_SIZE];
1076 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1077 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1080 if ((ret = ftp_connect(h, h->filename)) < 0)
1083 snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1084 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1089 snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1090 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1100 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1102 FTPContext *s = h_src->priv_data;
1103 char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1104 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1105 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1108 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1111 snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1112 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1117 av_url_split(0, 0, 0, 0, 0, 0, 0,
1120 snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1121 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1131 const URLProtocol ff_ftp_protocol = {
1133 .url_open = ftp_open,
1134 .url_read = ftp_read,
1135 .url_write = ftp_write,
1136 .url_seek = ftp_seek,
1137 .url_close = ftp_close,
1138 .url_get_file_handle = ftp_get_file_handle,
1139 .url_shutdown = ftp_shutdown,
1140 .priv_data_size = sizeof(FTPContext),
1141 .priv_data_class = &ftp_context_class,
1142 .url_open_dir = ftp_open_dir,
1143 .url_read_dir = ftp_read_dir,
1144 .url_close_dir = ftp_close_dir,
1145 .url_delete = ftp_delete,
1146 .url_move = ftp_move,
1147 .flags = URL_PROTOCOL_FLAG_NETWORK,
1148 .default_whitelist = "tcp",