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"
25 #include "libavutil/opt.h"
26 #include "libavutil/bprint.h"
28 #define CONTROL_BUFFER_SIZE 1024
29 #define CREDENTIALS_BUFFER_SIZE 128
41 URLContext *conn_control; /**< Control connection */
42 URLContext *conn_data; /**< Data connection, NULL when not connected */
43 uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
44 uint8_t *control_buf_ptr, *control_buf_end;
45 int server_data_port; /**< Data connection port opened by server, -1 on error. */
46 int server_control_port; /**< Control connection port, default is 21 */
47 char hostname[512]; /**< Server address. */
48 char credencials[CREDENTIALS_BUFFER_SIZE]; /**< Authentication data */
49 char path[MAX_URL_SIZE]; /**< Path to resource on server. */
50 int64_t filesize; /**< Size of file on server, -1 on error. */
51 int64_t position; /**< Current position, calculated. */
52 int rw_timeout; /**< Network timeout. */
53 const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
54 int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
55 FTPState state; /**< State of data connection */
58 #define OFFSET(x) offsetof(FTPContext, x)
59 #define D AV_OPT_FLAG_DECODING_PARAM
60 #define E AV_OPT_FLAG_ENCODING_PARAM
61 static const AVOption options[] = {
62 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
63 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
64 {"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 },
68 static const AVClass ftp_context_class = {
70 .item_name = av_default_item_name,
72 .version = LIBAVUTIL_VERSION_INT,
75 static int ftp_getc(FTPContext *s)
78 if (s->control_buf_ptr >= s->control_buf_end) {
79 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
85 s->control_buf_ptr = s->control_buffer;
86 s->control_buf_end = s->control_buffer + len;
89 return *s->control_buf_ptr++;
92 static int ftp_get_line(FTPContext *s, char *line, int line_size)
104 if (q > line && q[-1] == '\r')
109 if ((q - line) < line_size - 1)
116 * This routine returns ftp server response code.
117 * Server may send more than one response for a certain command.
118 * First expected code is returned.
120 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
122 int err, i, dash = 0, result = 0, code_found = 0, linesize;
123 char buf[CONTROL_BUFFER_SIZE];
124 AVBPrint line_buffer;
127 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
129 while (!code_found || dash) {
130 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
132 av_bprint_finalize(&line_buffer, NULL);
136 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
138 linesize = strlen(buf);
141 for (i = 0; i < 3; ++i) {
142 if (buf[i] < '0' || buf[i] > '9') {
156 for (i = 0; response_codes[i]; ++i) {
157 if (err == response_codes[i]) {
166 av_bprintf(&line_buffer, "%s\r\n", buf);
168 if (!dash && buf[3] == '-')
170 else if (err == dash && buf[3] == ' ')
177 av_bprint_finalize(&line_buffer, line);
181 static int ftp_send_command(FTPContext *s, const char *command,
182 const int response_codes[], char **response)
189 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
195 if (response_codes) {
196 return ftp_status(s, response, response_codes);
201 static void ftp_close_data_connection(FTPContext *s)
203 ffurl_closep(&s->conn_data);
205 s->state = DISCONNECTED;
208 static void ftp_close_both_connections(FTPContext *s)
210 ffurl_closep(&s->conn_control);
211 ftp_close_data_connection(s);
214 static int ftp_auth(FTPContext *s)
216 const char *user = NULL, *pass = NULL;
217 char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
219 static const int user_codes[] = {331, 230, 0};
220 static const int pass_codes[] = {230, 0};
222 /* Authentication may be repeated, original string has to be saved */
223 av_strlcpy(credencials, s->credencials, sizeof(credencials));
225 user = av_strtok(credencials, ":", &end);
226 pass = av_strtok(end, ":", &end);
230 pass = s->anonymous_password ? s->anonymous_password : "nopassword";
233 snprintf(buf, sizeof(buf), "USER %s\r\n", user);
234 err = ftp_send_command(s, buf, user_codes, NULL);
237 snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
238 err = ftp_send_command(s, buf, pass_codes, NULL);
240 return AVERROR(EACCES);
243 return AVERROR(EACCES);
248 static int ftp_passive_mode_epsv(FTPContext *s)
250 char *res = NULL, *start = NULL, *end = NULL;
252 static const char d = '|';
253 static const char *command = "EPSV\r\n";
254 static const int epsv_codes[] = {229, 0};
256 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
259 for (i = 0; res[i]; ++i) {
262 } else if (res[i] == ')') {
271 if (strlen(start) < 5)
273 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
278 s->server_data_port = atoi(start);
279 av_dlog(s, "Server data port: %d\n", s->server_data_port);
286 s->server_data_port = -1;
287 return AVERROR(ENOSYS);
290 static int ftp_passive_mode(FTPContext *s)
292 char *res = NULL, *start = NULL, *end = NULL;
294 static const char *command = "PASV\r\n";
295 static const int pasv_codes[] = {227, 0};
297 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
300 for (i = 0; res[i]; ++i) {
303 } else if (res[i] == ')') {
313 if (!av_strtok(start, ",", &end)) goto fail;
314 if (!av_strtok(end, ",", &end)) goto fail;
315 if (!av_strtok(end, ",", &end)) goto fail;
316 if (!av_strtok(end, ",", &end)) goto fail;
318 /* parse port number */
319 start = av_strtok(end, ",", &end);
320 if (!start) goto fail;
321 s->server_data_port = atoi(start) * 256;
322 start = av_strtok(end, ",", &end);
323 if (!start) goto fail;
324 s->server_data_port += atoi(start);
325 av_dlog(s, "Server data port: %d\n", s->server_data_port);
332 s->server_data_port = -1;
336 static int ftp_current_dir(FTPContext *s)
338 char *res = NULL, *start = NULL, *end = NULL;
340 static const char *command = "PWD\r\n";
341 static const int pwd_codes[] = {257, 0};
343 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
346 for (i = 0; res[i]; ++i) {
360 if (end > res && end[-1] == '/') {
364 av_strlcpy(s->path, start, sizeof(s->path));
374 static int ftp_file_size(FTPContext *s)
376 char command[CONTROL_BUFFER_SIZE];
378 static const int size_codes[] = {213, 0};
380 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
381 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
382 s->filesize = strtoll(&res[4], NULL, 10);
393 static int ftp_retrieve(FTPContext *s)
395 char command[CONTROL_BUFFER_SIZE];
396 static const int retr_codes[] = {150, 0};
398 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
399 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
402 s->state = DOWNLOADING;
407 static int ftp_store(FTPContext *s)
409 char command[CONTROL_BUFFER_SIZE];
410 static const int stor_codes[] = {150, 0};
412 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
413 if (ftp_send_command(s, command, stor_codes, NULL) != 150)
416 s->state = UPLOADING;
421 static int ftp_type(FTPContext *s)
423 static const char *command = "TYPE I\r\n";
424 static const int type_codes[] = {200, 0};
426 if (ftp_send_command(s, command, type_codes, NULL) != 200)
432 static int ftp_restart(FTPContext *s, int64_t pos)
434 char command[CONTROL_BUFFER_SIZE];
435 static const int rest_codes[] = {350, 0};
437 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
438 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
444 static int ftp_features(FTPContext *s)
446 static const char *feat_command = "FEAT\r\n";
447 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
448 static const int feat_codes[] = {211, 0};
449 static const int opts_codes[] = {200, 451};
452 if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
453 if (av_stristr(feat, "UTF8"))
454 ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
461 static int ftp_connect_control_connection(URLContext *h)
463 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
465 AVDictionary *opts = NULL;
466 FTPContext *s = h->priv_data;
467 static const int connect_codes[] = {220, 0};
469 if (!s->conn_control) {
470 ff_url_join(buf, sizeof(buf), "tcp", NULL,
471 s->hostname, s->server_control_port, NULL);
472 if (s->rw_timeout != -1) {
473 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
474 } /* if option is not given, don't pass it and let tcp use its own default */
475 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
476 &h->interrupt_callback, &opts);
479 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
483 /* check if server is ready */
484 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
485 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
486 return AVERROR(EACCES);
489 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
490 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.");
494 if ((err = ftp_auth(s)) < 0) {
495 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
499 if ((err = ftp_type(s)) < 0) {
500 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
509 static int ftp_connect_data_connection(URLContext *h)
512 char buf[CONTROL_BUFFER_SIZE];
513 AVDictionary *opts = NULL;
514 FTPContext *s = h->priv_data;
517 /* Enter passive mode */
518 if (ftp_passive_mode_epsv(s) < 0) {
519 /* Use PASV as fallback */
520 if ((err = ftp_passive_mode(s)) < 0)
523 /* Open data connection */
524 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
525 if (s->rw_timeout != -1) {
526 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
527 } /* if option is not given, don't pass it and let tcp use its own default */
528 err = ffurl_open(&s->conn_data, buf, h->flags,
529 &h->interrupt_callback, &opts);
535 if ((err = ftp_restart(s, s->position)) < 0)
542 static int ftp_abort(URLContext *h)
544 static const char *command = "ABOR\r\n";
546 static const int abor_codes[] = {225, 226, 0};
547 FTPContext *s = h->priv_data;
549 /* According to RCF 959:
550 "ABOR command tells the server to abort the previous FTP
551 service command and any associated transfer of data."
553 There are FTP server implementations that don't response
554 to any commands during data transfer in passive mode (including ABOR).
556 This implementation closes data connection by force.
559 if (ftp_send_command(s, command, NULL, NULL) < 0) {
560 ftp_close_both_connections(s);
561 if ((err = ftp_connect_control_connection(h)) < 0) {
562 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
566 ftp_close_data_connection(s);
567 if (ftp_status(s, NULL, abor_codes) < 225) {
568 /* wu-ftpd also closes control connection after data connection closing */
569 ffurl_closep(&s->conn_control);
570 if ((err = ftp_connect_control_connection(h)) < 0) {
571 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
580 static int ftp_open(URLContext *h, const char *url, int flags)
582 char proto[10], path[MAX_URL_SIZE];
584 FTPContext *s = h->priv_data;
586 av_dlog(h, "ftp protocol open\n");
588 s->state = DISCONNECTED;
592 av_url_split(proto, sizeof(proto),
593 s->credencials, sizeof(s->credencials),
594 s->hostname, sizeof(s->hostname),
595 &s->server_control_port,
599 if (s->server_control_port < 0 || s->server_control_port > 65535)
600 s->server_control_port = 21;
602 if ((err = ftp_connect_control_connection(h)) < 0)
605 if ((err = ftp_current_dir(s)) < 0)
607 av_strlcat(s->path, path, sizeof(s->path));
609 if (ftp_restart(s, 0) < 0) {
612 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
614 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
621 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
622 ffurl_closep(&s->conn_control);
623 ffurl_closep(&s->conn_data);
627 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
629 FTPContext *s = h->priv_data;
631 int64_t new_pos, fake_pos;
633 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
642 new_pos = s->position + pos;
647 new_pos = s->filesize + pos;
650 return AVERROR(EINVAL);
657 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
658 return AVERROR(EINVAL);
661 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
662 if (fake_pos != s->position) {
663 if ((err = ftp_abort(h)) < 0)
665 s->position = fake_pos;
670 static int ftp_read(URLContext *h, unsigned char *buf, int size)
672 FTPContext *s = h->priv_data;
673 int read, err, retry_done = 0;
675 av_dlog(h, "ftp protocol read %d bytes\n", size);
677 if (s->state == DISCONNECTED) {
679 if (s->position >= s->filesize)
681 if ((err = ftp_connect_data_connection(h)) < 0)
684 if (s->state == READY) {
685 if (s->position >= s->filesize)
687 if ((err = ftp_retrieve(s)) < 0)
690 if (s->conn_data && s->state == DOWNLOADING) {
691 read = ffurl_read(s->conn_data, buf, size);
694 if (s->position >= s->filesize) {
695 /* server will terminate, but keep current position to avoid madness */
696 /* save position to restart from it */
697 int64_t pos = s->position;
698 if (ftp_abort(h) < 0) {
705 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
706 /* Server closed connection. Probably due to inactivity */
707 int64_t pos = s->position;
708 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
709 if ((err = ftp_abort(h)) < 0)
711 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
712 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
723 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
727 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
730 FTPContext *s = h->priv_data;
733 av_dlog(h, "ftp protocol write %d bytes\n", size);
735 if (s->state == DISCONNECTED) {
736 if ((err = ftp_connect_data_connection(h)) < 0)
739 if (s->state == READY) {
740 if ((err = ftp_store(s)) < 0)
743 if (s->conn_data && s->state == UPLOADING) {
744 written = ffurl_write(s->conn_data, buf, size);
746 s->position += written;
747 s->filesize = FFMAX(s->filesize, s->position);
752 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
756 static int ftp_close(URLContext *h)
758 av_dlog(h, "ftp protocol close\n");
760 ftp_close_both_connections(h->priv_data);
765 static int ftp_get_file_handle(URLContext *h)
767 FTPContext *s = h->priv_data;
769 av_dlog(h, "ftp protocol get_file_handle\n");
772 return ffurl_get_file_handle(s->conn_data);
777 static int ftp_shutdown(URLContext *h, int flags)
779 FTPContext *s = h->priv_data;
781 av_dlog(h, "ftp protocol shutdown\n");
784 return ffurl_shutdown(s->conn_data, flags);
789 URLProtocol ff_ftp_protocol = {
791 .url_open = ftp_open,
792 .url_read = ftp_read,
793 .url_write = ftp_write,
794 .url_seek = ftp_seek,
795 .url_close = ftp_close,
796 .url_get_file_handle = ftp_get_file_handle,
797 .url_shutdown = ftp_shutdown,
798 .priv_data_size = sizeof(FTPContext),
799 .priv_data_class = &ftp_context_class,
800 .flags = URL_PROTOCOL_FLAG_NETWORK,