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/parseutils.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/bprint.h"
29 #define CONTROL_BUFFER_SIZE 1024
30 #define DIR_BUFFER_SIZE 4096
43 URLContext *conn_control; /**< Control connection */
44 URLContext *conn_data; /**< Data connection, NULL when not connected */
45 uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
46 uint8_t *control_buf_ptr, *control_buf_end;
47 int server_data_port; /**< Data connection port opened by server, -1 on error. */
48 int server_control_port; /**< Control connection port, default is 21 */
49 char *hostname; /**< Server address. */
50 char *user; /**< Server user */
51 char *password; /**< Server user's password */
52 char *path; /**< Path to resource on server. */
53 int64_t filesize; /**< Size of file on server, -1 on error. */
54 int64_t position; /**< Current position, calculated. */
55 int rw_timeout; /**< Network timeout. */
56 const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
57 int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
58 FTPState state; /**< State of data connection */
60 size_t dir_buffer_size;
61 size_t dir_buffer_offset;
65 #define OFFSET(x) offsetof(FTPContext, x)
66 #define D AV_OPT_FLAG_DECODING_PARAM
67 #define E AV_OPT_FLAG_ENCODING_PARAM
68 static const AVOption options[] = {
69 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
70 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
71 {"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 },
75 static const AVClass ftp_context_class = {
77 .item_name = av_default_item_name,
79 .version = LIBAVUTIL_VERSION_INT,
82 static int ftp_close(URLContext *h);
84 static int ftp_getc(FTPContext *s)
87 if (s->control_buf_ptr >= s->control_buf_end) {
88 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
94 s->control_buf_ptr = s->control_buffer;
95 s->control_buf_end = s->control_buffer + len;
98 return *s->control_buf_ptr++;
101 static int ftp_get_line(FTPContext *s, char *line, int line_size)
113 if (q > line && q[-1] == '\r')
118 if ((q - line) < line_size - 1)
125 * This routine returns ftp server response code.
126 * Server may send more than one response for a certain command.
127 * First expected code is returned.
129 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
131 int err, i, dash = 0, result = 0, code_found = 0, linesize;
132 char buf[CONTROL_BUFFER_SIZE];
133 AVBPrint line_buffer;
136 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
138 while (!code_found || dash) {
139 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
141 av_bprint_finalize(&line_buffer, NULL);
145 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
147 linesize = strlen(buf);
150 for (i = 0; i < 3; ++i) {
151 if (buf[i] < '0' || buf[i] > '9') {
165 for (i = 0; response_codes[i]; ++i) {
166 if (err == response_codes[i]) {
175 av_bprintf(&line_buffer, "%s\r\n", buf);
177 if (!dash && buf[3] == '-')
179 else if (err == dash && buf[3] == ' ')
186 av_bprint_finalize(&line_buffer, line);
190 static int ftp_send_command(FTPContext *s, const char *command,
191 const int response_codes[], char **response)
198 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
204 if (response_codes) {
205 return ftp_status(s, response, response_codes);
210 static void ftp_close_data_connection(FTPContext *s)
212 ffurl_closep(&s->conn_data);
214 s->state = DISCONNECTED;
217 static void ftp_close_both_connections(FTPContext *s)
219 ffurl_closep(&s->conn_control);
220 ftp_close_data_connection(s);
223 static int ftp_auth(FTPContext *s)
225 char buf[CONTROL_BUFFER_SIZE];
227 static const int user_codes[] = {331, 230, 0};
228 static const int pass_codes[] = {230, 0};
230 snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
231 err = ftp_send_command(s, buf, user_codes, NULL);
234 snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
235 err = ftp_send_command(s, buf, pass_codes, NULL);
237 return AVERROR(EACCES);
240 return AVERROR(EACCES);
245 static int ftp_passive_mode_epsv(FTPContext *s)
247 char *res = NULL, *start = NULL, *end = NULL;
249 static const char d = '|';
250 static const char *command = "EPSV\r\n";
251 static const int epsv_codes[] = {229, 0};
253 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
256 for (i = 0; res[i]; ++i) {
259 } else if (res[i] == ')') {
268 if (strlen(start) < 5)
270 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
275 s->server_data_port = atoi(start);
276 av_dlog(s, "Server data port: %d\n", s->server_data_port);
283 s->server_data_port = -1;
284 return AVERROR(ENOSYS);
287 static int ftp_passive_mode(FTPContext *s)
289 char *res = NULL, *start = NULL, *end = NULL;
291 static const char *command = "PASV\r\n";
292 static const int pasv_codes[] = {227, 0};
294 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
297 for (i = 0; res[i]; ++i) {
300 } else if (res[i] == ')') {
310 if (!av_strtok(start, ",", &end)) goto fail;
311 if (!av_strtok(end, ",", &end)) goto fail;
312 if (!av_strtok(end, ",", &end)) goto fail;
313 if (!av_strtok(end, ",", &end)) goto fail;
315 /* parse port number */
316 start = av_strtok(end, ",", &end);
317 if (!start) goto fail;
318 s->server_data_port = atoi(start) * 256;
319 start = av_strtok(end, ",", &end);
320 if (!start) goto fail;
321 s->server_data_port += atoi(start);
322 av_dlog(s, "Server data port: %d\n", s->server_data_port);
329 s->server_data_port = -1;
333 static int ftp_current_dir(FTPContext *s)
335 char *res = NULL, *start = NULL, *end = NULL;
337 static const char *command = "PWD\r\n";
338 static const int pwd_codes[] = {257, 0};
340 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
343 for (i = 0; res[i]; ++i) {
358 s->path = av_strdup(start);
363 return AVERROR(ENOMEM);
371 static int ftp_file_size(FTPContext *s)
373 char command[CONTROL_BUFFER_SIZE];
375 static const int size_codes[] = {213, 0};
377 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
378 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
379 s->filesize = strtoll(&res[4], NULL, 10);
390 static int ftp_retrieve(FTPContext *s)
392 char command[CONTROL_BUFFER_SIZE];
393 static const int retr_codes[] = {150, 0};
395 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
396 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
399 s->state = DOWNLOADING;
404 static int ftp_store(FTPContext *s)
406 char command[CONTROL_BUFFER_SIZE];
407 static const int stor_codes[] = {150, 0};
409 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
410 if (ftp_send_command(s, command, stor_codes, NULL) != 150)
413 s->state = UPLOADING;
418 static int ftp_type(FTPContext *s)
420 static const char *command = "TYPE I\r\n";
421 static const int type_codes[] = {200, 0};
423 if (ftp_send_command(s, command, type_codes, NULL) != 200)
429 static int ftp_restart(FTPContext *s, int64_t pos)
431 char command[CONTROL_BUFFER_SIZE];
432 static const int rest_codes[] = {350, 0};
434 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
435 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
441 static int ftp_set_dir(FTPContext *s)
443 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
444 char command[MAX_URL_SIZE];
446 snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
447 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
452 static int ftp_list(FTPContext *s)
454 static const char *command = "MLSD\r\n";
455 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
457 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
458 return AVERROR(ENOSYS);
459 s->state = LISTING_DIR;
463 static int ftp_features(FTPContext *s)
465 static const char *feat_command = "FEAT\r\n";
466 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
467 static const int feat_codes[] = {211, 0};
468 static const int opts_codes[] = {200, 451, 0};
471 if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
472 if (av_stristr(feat, "UTF8")) {
473 if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200)
482 static int ftp_connect_control_connection(URLContext *h)
484 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
486 AVDictionary *opts = NULL;
487 FTPContext *s = h->priv_data;
488 static const int connect_codes[] = {220, 0};
490 if (!s->conn_control) {
491 ff_url_join(buf, sizeof(buf), "tcp", NULL,
492 s->hostname, s->server_control_port, NULL);
493 if (s->rw_timeout != -1) {
494 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
495 } /* if option is not given, don't pass it and let tcp use its own default */
496 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
497 &h->interrupt_callback, &opts);
500 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
504 /* check if server is ready */
505 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
506 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
507 return AVERROR(EACCES);
510 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
511 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.");
515 if ((err = ftp_auth(s)) < 0) {
516 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
520 if ((err = ftp_type(s)) < 0) {
521 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
530 static int ftp_connect_data_connection(URLContext *h)
533 char buf[CONTROL_BUFFER_SIZE];
534 AVDictionary *opts = NULL;
535 FTPContext *s = h->priv_data;
538 /* Enter passive mode */
539 if (ftp_passive_mode_epsv(s) < 0) {
540 /* Use PASV as fallback */
541 if ((err = ftp_passive_mode(s)) < 0)
544 /* Open data connection */
545 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
546 if (s->rw_timeout != -1) {
547 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
548 } /* if option is not given, don't pass it and let tcp use its own default */
549 err = ffurl_open(&s->conn_data, buf, h->flags,
550 &h->interrupt_callback, &opts);
556 if ((err = ftp_restart(s, s->position)) < 0)
563 static int ftp_abort(URLContext *h)
565 static const char *command = "ABOR\r\n";
567 static const int abor_codes[] = {225, 226, 0};
568 FTPContext *s = h->priv_data;
570 /* According to RCF 959:
571 "ABOR command tells the server to abort the previous FTP
572 service command and any associated transfer of data."
574 There are FTP server implementations that don't response
575 to any commands during data transfer in passive mode (including ABOR).
577 This implementation closes data connection by force.
580 if (ftp_send_command(s, command, NULL, NULL) < 0) {
581 ftp_close_both_connections(s);
582 if ((err = ftp_connect_control_connection(h)) < 0) {
583 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
587 ftp_close_data_connection(s);
588 if (ftp_status(s, NULL, abor_codes) < 225) {
589 /* wu-ftpd also closes control connection after data connection closing */
590 ffurl_closep(&s->conn_control);
591 if ((err = ftp_connect_control_connection(h)) < 0) {
592 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
601 static int ftp_connect(URLContext *h, const char *url)
603 char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
604 const char *tok_user = NULL, *tok_pass = NULL;
605 char *end = NULL, *newpath = NULL;
607 FTPContext *s = h->priv_data;
609 s->state = DISCONNECTED;
613 av_url_split(proto, sizeof(proto),
614 credencials, sizeof(credencials),
615 hostname, sizeof(hostname),
616 &s->server_control_port,
620 tok_user = av_strtok(credencials, ":", &end);
621 tok_pass = av_strtok(end, ":", &end);
623 tok_user = "anonymous";
624 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
626 s->user = av_strdup(tok_user);
627 s->password = av_strdup(tok_pass);
628 s->hostname = av_strdup(hostname);
629 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
630 return AVERROR(ENOMEM);
633 if (s->server_control_port < 0 || s->server_control_port > 65535)
634 s->server_control_port = 21;
636 if ((err = ftp_connect_control_connection(h)) < 0)
639 if ((err = ftp_current_dir(s)) < 0)
642 newpath = av_append_path_component(s->path, path);
644 return AVERROR(ENOMEM);
651 static int ftp_open(URLContext *h, const char *url, int flags)
653 FTPContext *s = h->priv_data;
656 av_dlog(h, "ftp protocol open\n");
658 if ((err = ftp_connect(h, url)) < 0)
661 if (ftp_restart(s, 0) < 0) {
664 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
666 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
673 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
678 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
680 FTPContext *s = h->priv_data;
682 int64_t new_pos, fake_pos;
684 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
693 new_pos = s->position + pos;
698 new_pos = s->filesize + pos;
701 return AVERROR(EINVAL);
708 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
709 return AVERROR(EINVAL);
712 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
713 if (fake_pos != s->position) {
714 if ((err = ftp_abort(h)) < 0)
716 s->position = fake_pos;
721 static int ftp_read(URLContext *h, unsigned char *buf, int size)
723 FTPContext *s = h->priv_data;
724 int read, err, retry_done = 0;
726 av_dlog(h, "ftp protocol read %d bytes\n", size);
728 if (s->state == DISCONNECTED) {
730 if (s->position >= s->filesize)
732 if ((err = ftp_connect_data_connection(h)) < 0)
735 if (s->state == READY) {
736 if (s->position >= s->filesize)
738 if ((err = ftp_retrieve(s)) < 0)
741 if (s->conn_data && s->state == DOWNLOADING) {
742 read = ffurl_read(s->conn_data, buf, size);
745 if (s->position >= s->filesize) {
746 /* server will terminate, but keep current position to avoid madness */
747 /* save position to restart from it */
748 int64_t pos = s->position;
749 if (ftp_abort(h) < 0) {
756 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
757 /* Server closed connection. Probably due to inactivity */
758 int64_t pos = s->position;
759 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
760 if ((err = ftp_abort(h)) < 0)
762 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
763 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
774 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
778 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
781 FTPContext *s = h->priv_data;
784 av_dlog(h, "ftp protocol write %d bytes\n", size);
786 if (s->state == DISCONNECTED) {
787 if ((err = ftp_connect_data_connection(h)) < 0)
790 if (s->state == READY) {
791 if ((err = ftp_store(s)) < 0)
794 if (s->conn_data && s->state == UPLOADING) {
795 written = ffurl_write(s->conn_data, buf, size);
797 s->position += written;
798 s->filesize = FFMAX(s->filesize, s->position);
803 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
807 static int ftp_close(URLContext *h)
809 FTPContext *s = h->priv_data;
811 av_dlog(h, "ftp protocol close\n");
813 ftp_close_both_connections(s);
815 av_freep(&s->password);
816 av_freep(&s->hostname);
822 static int ftp_get_file_handle(URLContext *h)
824 FTPContext *s = h->priv_data;
826 av_dlog(h, "ftp protocol get_file_handle\n");
829 return ffurl_get_file_handle(s->conn_data);
834 static int ftp_shutdown(URLContext *h, int flags)
836 FTPContext *s = h->priv_data;
838 av_dlog(h, "ftp protocol shutdown\n");
841 return ffurl_shutdown(s->conn_data, flags);
846 static int ftp_open_dir(URLContext *h)
848 FTPContext *s = h->priv_data;
851 if ((ret = ftp_connect(h, h->filename)) < 0)
853 if ((ret = ftp_set_dir(s)) < 0)
855 if ((ret = ftp_connect_data_connection(h)) < 0)
857 if ((ret = ftp_list(s)) < 0)
859 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
860 if (!s->dir_buffer) {
861 ret = AVERROR(ENOMEM);
864 s->dir_buffer[0] = 0;
865 if (s->conn_data && s->state == LISTING_DIR)
868 ffurl_closep(&s->conn_control);
869 ffurl_closep(&s->conn_data);
873 static int64_t ftp_parse_date(const char *date)
876 memset(&tv, 0, sizeof(struct tm));
877 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
878 return INT64_C(1000000) * av_timegm(&tv);
882 * @return 0 on success, negative on error, positive on entry to discard.
884 static int ftp_parse_entry(char *mlsd, AVIODirEntry *next)
887 av_dlog(NULL, "%s\n", mlsd);
888 while(fact = av_strtok(mlsd, ";", &mlsd)) {
889 if (fact[0] == ' ') {
890 next->name = av_strdup(&fact[1]);
893 fact = av_strtok(fact, "=", &value);
894 if (!av_strcasecmp(fact, "type")) {
895 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
897 if (!av_strcasecmp(value, "dir"))
898 next->type = AVIO_ENTRY_DIRECTORY;
899 else if (!av_strcasecmp(value, "file"))
900 next->type = AVIO_ENTRY_FILE;
901 else if (!av_strcasecmp(value, "OS.unix=slink:"))
902 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
903 } else if (!av_strcasecmp(fact, "modify")) {
904 next->modification_timestamp = ftp_parse_date(value);
905 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
906 next->filemode = strtoumax(value, NULL, 8);
907 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
908 next->user_id = strtoumax(value, NULL, 10);
909 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
910 next->group_id = strtoumax(value, NULL, 10);
911 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
912 next->size = strtoll(value, NULL, 10);
917 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
919 FTPContext *s = h->priv_data;
925 start = s->dir_buffer + s->dir_buffer_offset;
926 while (!(found = strstr(start, "\n"))) {
929 s->dir_buffer_size -= s->dir_buffer_offset;
930 s->dir_buffer_offset = 0;
931 if (s->dir_buffer_size)
932 memmove(s->dir_buffer, start, s->dir_buffer_size);
933 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
940 s->dir_buffer_size += ret;
941 s->dir_buffer[s->dir_buffer_size] = 0;
942 start = s->dir_buffer;
945 s->dir_buffer_offset += (found + 1 - start);
947 if (found > start && found[-1] == '\r')
950 *next = ff_alloc_dir_entry();
952 return AVERROR(ENOMEM);
953 (*next)->utf8 = s->utf8;
954 ret = ftp_parse_entry(start, *next);
956 avio_free_directory_entry(next);
964 static int ftp_close_dir(URLContext *h)
966 FTPContext *s = h->priv_data;
967 av_freep(&s->dir_buffer);
968 ffurl_closep(&s->conn_control);
969 ffurl_closep(&s->conn_data);
973 URLProtocol ff_ftp_protocol = {
975 .url_open = ftp_open,
976 .url_read = ftp_read,
977 .url_write = ftp_write,
978 .url_seek = ftp_seek,
979 .url_close = ftp_close,
980 .url_get_file_handle = ftp_get_file_handle,
981 .url_shutdown = ftp_shutdown,
982 .priv_data_size = sizeof(FTPContext),
983 .priv_data_class = &ftp_context_class,
984 .url_open_dir = ftp_open_dir,
985 .url_read_dir = ftp_read_dir,
986 .url_close_dir = ftp_close_dir,
987 .flags = URL_PROTOCOL_FLAG_NETWORK,