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)
186 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
192 if (response_codes) {
193 return ftp_status(s, response, response_codes);
198 static void ftp_close_data_connection(FTPContext *s)
200 ffurl_closep(&s->conn_data);
202 s->state = DISCONNECTED;
205 static void ftp_close_both_connections(FTPContext *s)
207 ffurl_closep(&s->conn_control);
208 ftp_close_data_connection(s);
211 static int ftp_auth(FTPContext *s)
213 const char *user = NULL, *pass = NULL;
214 char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
216 static const int user_codes[] = {331, 230, 0};
217 static const int pass_codes[] = {230, 0};
219 /* Authentication may be repeated, original string has to be saved */
220 av_strlcpy(credencials, s->credencials, sizeof(credencials));
222 user = av_strtok(credencials, ":", &end);
223 pass = av_strtok(end, ":", &end);
227 pass = s->anonymous_password ? s->anonymous_password : "nopassword";
230 snprintf(buf, sizeof(buf), "USER %s\r\n", user);
231 err = ftp_send_command(s, buf, user_codes, NULL);
234 snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
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) {
357 if (end > res && end[-1] == '/') {
361 av_strlcpy(s->path, start, sizeof(s->path));
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_features(FTPContext *s)
443 static const char *feat_command = "FEAT\r\n";
444 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
445 static const int feat_codes[] = {211, 0};
446 static const int opts_codes[] = {200, 451};
449 if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
450 if (av_stristr(feat, "UTF8"))
451 ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
456 static int ftp_connect_control_connection(URLContext *h)
458 char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
460 AVDictionary *opts = NULL;
461 FTPContext *s = h->priv_data;
462 static const int connect_codes[] = {220, 0};
464 if (!s->conn_control) {
465 ff_url_join(buf, sizeof(buf), "tcp", NULL,
466 s->hostname, s->server_control_port, NULL);
467 if (s->rw_timeout != -1) {
468 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
469 av_dict_set(&opts, "timeout", opts_format, 0);
470 } /* if option is not given, don't pass it and let tcp use its own default */
471 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
472 &h->interrupt_callback, &opts);
475 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
479 /* check if server is ready */
480 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
481 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
482 return AVERROR(EACCES);
485 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
486 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.");
490 if ((err = ftp_auth(s)) < 0) {
491 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
495 if ((err = ftp_type(s)) < 0) {
496 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
505 static int ftp_connect_data_connection(URLContext *h)
508 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
509 AVDictionary *opts = NULL;
510 FTPContext *s = h->priv_data;
513 /* Enter passive mode */
514 if (ftp_passive_mode_epsv(s) < 0) {
515 /* Use PASV as fallback */
516 if ((err = ftp_passive_mode(s)) < 0)
519 /* Open data connection */
520 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
521 if (s->rw_timeout != -1) {
522 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
523 av_dict_set(&opts, "timeout", opts_format, 0);
524 } /* if option is not given, don't pass it and let tcp use its own default */
525 err = ffurl_open(&s->conn_data, buf, h->flags,
526 &h->interrupt_callback, &opts);
532 if ((err = ftp_restart(s, s->position)) < 0)
539 static int ftp_abort(URLContext *h)
541 static const char *command = "ABOR\r\n";
543 static const int abor_codes[] = {225, 226, 0};
544 FTPContext *s = h->priv_data;
546 /* According to RCF 959:
547 "ABOR command tells the server to abort the previous FTP
548 service command and any associated transfer of data."
550 There are FTP server implementations that don't response
551 to any commands during data transfer in passive mode (including ABOR).
553 This implementation closes data connection by force.
556 if (ftp_send_command(s, command, NULL, NULL) < 0) {
557 ftp_close_both_connections(s);
558 if ((err = ftp_connect_control_connection(h)) < 0) {
559 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
563 ftp_close_data_connection(s);
564 if (ftp_status(s, NULL, abor_codes) < 225) {
565 /* wu-ftpd also closes control connection after data connection closing */
566 ffurl_closep(&s->conn_control);
567 if ((err = ftp_connect_control_connection(h)) < 0) {
568 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
577 static int ftp_open(URLContext *h, const char *url, int flags)
579 char proto[10], path[MAX_URL_SIZE];
581 FTPContext *s = h->priv_data;
583 av_dlog(h, "ftp protocol open\n");
585 s->state = DISCONNECTED;
589 av_url_split(proto, sizeof(proto),
590 s->credencials, sizeof(s->credencials),
591 s->hostname, sizeof(s->hostname),
592 &s->server_control_port,
596 if (s->server_control_port < 0 || s->server_control_port > 65535)
597 s->server_control_port = 21;
599 if ((err = ftp_connect_control_connection(h)) < 0)
602 if ((err = ftp_current_dir(s)) < 0)
604 av_strlcat(s->path, path, sizeof(s->path));
606 if (ftp_restart(s, 0) < 0) {
609 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
611 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
618 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
619 ffurl_closep(&s->conn_control);
620 ffurl_closep(&s->conn_data);
624 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
626 FTPContext *s = h->priv_data;
628 int64_t new_pos, fake_pos;
630 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
639 new_pos = s->position + pos;
644 new_pos = s->filesize + pos;
647 return AVERROR(EINVAL);
654 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
655 return AVERROR(EINVAL);
658 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
659 if (fake_pos != s->position) {
660 if ((err = ftp_abort(h)) < 0)
662 s->position = fake_pos;
667 static int ftp_read(URLContext *h, unsigned char *buf, int size)
669 FTPContext *s = h->priv_data;
670 int read, err, retry_done = 0;
672 av_dlog(h, "ftp protocol read %d bytes\n", size);
674 if (s->state == DISCONNECTED) {
676 if (s->position >= s->filesize)
678 if ((err = ftp_connect_data_connection(h)) < 0)
681 if (s->state == READY) {
682 if (s->position >= s->filesize)
684 if ((err = ftp_retrieve(s)) < 0)
687 if (s->conn_data && s->state == DOWNLOADING) {
688 read = ffurl_read(s->conn_data, buf, size);
691 if (s->position >= s->filesize) {
692 /* server will terminate, but keep current position to avoid madness */
693 /* save position to restart from it */
694 int64_t pos = s->position;
695 if (ftp_abort(h) < 0) {
702 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
703 /* Server closed connection. Probably due to inactivity */
704 int64_t pos = s->position;
705 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
706 if ((err = ftp_abort(h)) < 0)
708 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
709 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
720 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
724 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
727 FTPContext *s = h->priv_data;
730 av_dlog(h, "ftp protocol write %d bytes\n", size);
732 if (s->state == DISCONNECTED) {
733 if ((err = ftp_connect_data_connection(h)) < 0)
736 if (s->state == READY) {
737 if ((err = ftp_store(s)) < 0)
740 if (s->conn_data && s->state == UPLOADING) {
741 written = ffurl_write(s->conn_data, buf, size);
743 s->position += written;
744 s->filesize = FFMAX(s->filesize, s->position);
749 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
753 static int ftp_close(URLContext *h)
755 av_dlog(h, "ftp protocol close\n");
757 ftp_close_both_connections(h->priv_data);
762 static int ftp_get_file_handle(URLContext *h)
764 FTPContext *s = h->priv_data;
766 av_dlog(h, "ftp protocol get_file_handle\n");
769 return ffurl_get_file_handle(s->conn_data);
774 static int ftp_shutdown(URLContext *h, int flags)
776 FTPContext *s = h->priv_data;
778 av_dlog(h, "ftp protocol shutdown\n");
781 return ffurl_shutdown(s->conn_data, flags);
786 URLProtocol ff_ftp_protocol = {
788 .url_open = ftp_open,
789 .url_read = ftp_read,
790 .url_write = ftp_write,
791 .url_seek = ftp_seek,
792 .url_close = ftp_close,
793 .url_get_file_handle = ftp_get_file_handle,
794 .url_shutdown = ftp_shutdown,
795 .priv_data_size = sizeof(FTPContext),
796 .priv_data_class = &ftp_context_class,
797 .flags = URL_PROTOCOL_FLAG_NETWORK,