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
22 #include "libavutil/avstring.h"
23 #include "libavutil/time.h"
27 #include "os_support.h"
29 #include "libavutil/opt.h"
31 #define CONTROL_BUFFER_SIZE 1024
32 #define CREDENTIALS_BUFFER_SIZE 128
44 URLContext *conn_control; /**< Control connection */
45 int conn_control_block_flag; /**< Controls block/unblock mode of data connection */
46 AVIOInterruptCB conn_control_interrupt_cb; /**< Controls block/unblock mode of data connection */
47 URLContext *conn_data; /**< Data connection, NULL when not connected */
48 uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
49 uint8_t *control_buf_ptr, *control_buf_end;
50 int server_data_port; /**< Data connection port opened by server, -1 on error. */
51 int server_control_port; /**< Control connection port, default is 21 */
52 char hostname[512]; /**< Server address. */
53 char credencials[CREDENTIALS_BUFFER_SIZE]; /**< Authentication data */
54 char path[MAX_URL_SIZE]; /**< Path to resource on server. */
55 int64_t filesize; /**< Size of file on server, -1 on error. */
56 int64_t position; /**< Current position, calculated. */
57 int rw_timeout; /**< Network timeout. */
58 const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
59 int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
60 FTPState state; /**< State of data connection */
63 #define OFFSET(x) offsetof(FTPContext, x)
64 #define D AV_OPT_FLAG_DECODING_PARAM
65 #define E AV_OPT_FLAG_ENCODING_PARAM
66 static const AVOption options[] = {
67 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
68 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
69 {"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 },
73 static const AVClass ftp_context_class = {
75 .item_name = av_default_item_name,
77 .version = LIBAVUTIL_VERSION_INT,
80 static int ftp_conn_control_block_control(void *data)
83 return s->conn_control_block_flag;
86 static int ftp_getc(FTPContext *s)
89 if (s->control_buf_ptr >= s->control_buf_end) {
90 if (s->conn_control_block_flag)
92 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
98 s->control_buf_ptr = s->control_buffer;
99 s->control_buf_end = s->control_buffer + len;
102 return *s->control_buf_ptr++;
105 static int ftp_get_line(FTPContext *s, char *line, int line_size)
109 int ori_block_flag = s->conn_control_block_flag;
114 s->conn_control_block_flag = ori_block_flag;
119 if (q > line && q[-1] == '\r')
123 s->conn_control_block_flag = ori_block_flag;
126 s->conn_control_block_flag = 0; /* line need to be finished */
127 if ((q - line) < line_size - 1)
133 static int ftp_flush_control_input(FTPContext *s)
135 char buf[CONTROL_BUFFER_SIZE];
136 int err, ori_block_flag = s->conn_control_block_flag;
138 s->conn_control_block_flag = 1;
140 err = ftp_get_line(s, buf, sizeof(buf));
143 s->conn_control_block_flag = ori_block_flag;
145 if (err < 0 && err != AVERROR_EXIT)
152 * This routine returns ftp server response code.
153 * Server may send more than one response for a certain command, following priorities are used:
154 * - When pref_codes are set then pref_code is return if occurred. (expected result)
155 * - 0 is returned when no pref_codes or not occurred
157 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
159 int err, i, result = 0, pref_code_found = 0, wait_count = 100;
160 char buf[CONTROL_BUFFER_SIZE];
162 /* Set blocking mode */
163 s->conn_control_block_flag = 0;
165 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
166 if (err == AVERROR_EXIT) {
167 if (!pref_code_found && wait_count--) {
175 /* first code received. Now get all lines in non blocking mode */
176 s->conn_control_block_flag = 1;
178 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
180 if (!pref_code_found) {
185 for (i = 0; i < 3; ++i) {
186 if (buf[i] < '0' || buf[i] > '9')
192 for (i = 0; response_codes[i]; ++i) {
193 if (err == response_codes[i]) {
197 *line = av_strdup(buf);
206 static int ftp_send_command(FTPContext *s, const char *command,
207 const int response_codes[], char **response)
211 /* Flush control connection input to get rid of non relevant responses if any */
212 if ((err = ftp_flush_control_input(s)) < 0)
215 /* send command in blocking mode */
216 s->conn_control_block_flag = 0;
217 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
221 return ftp_status(s, response, response_codes);
224 static void ftp_close_both_connections(FTPContext *s)
226 ffurl_closep(&s->conn_control);
227 ffurl_closep(&s->conn_data);
229 s->state = DISCONNECTED;
232 static int ftp_auth(FTPContext *s)
234 const char *user = NULL, *pass = NULL;
235 char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
237 const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
238 const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
240 /* Authentication may be repeated, original string has to be saved */
241 av_strlcpy(credencials, s->credencials, sizeof(credencials));
243 user = av_strtok(credencials, ":", &end);
244 pass = av_strtok(end, ":", &end);
248 pass = s->anonymous_password ? s->anonymous_password : "nopassword";
251 snprintf(buf, sizeof(buf), "USER %s\r\n", user);
252 err = ftp_send_command(s, buf, user_codes, NULL);
255 snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
256 err = ftp_send_command(s, buf, pass_codes, NULL);
258 return AVERROR(EACCES);
261 return AVERROR(EACCES);
266 static int ftp_passive_mode(FTPContext *s)
268 char *res = NULL, *start = NULL, *end = NULL;
270 const char *command = "PASV\r\n";
271 const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
273 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
276 for (i = 0; res[i]; ++i) {
279 } else if (res[i] == ')') {
289 if (!av_strtok(start, ",", &end)) goto fail;
290 if (!av_strtok(end, ",", &end)) goto fail;
291 if (!av_strtok(end, ",", &end)) goto fail;
292 if (!av_strtok(end, ",", &end)) goto fail;
294 /* parse port number */
295 start = av_strtok(end, ",", &end);
296 if (!start) goto fail;
297 s->server_data_port = atoi(start) * 256;
298 start = av_strtok(end, ",", &end);
299 if (!start) goto fail;
300 s->server_data_port += atoi(start);
301 av_dlog(s, "Server data port: %d\n", s->server_data_port);
308 s->server_data_port = -1;
312 static int ftp_current_dir(FTPContext *s)
314 char *res = NULL, *start = NULL, *end = NULL;
316 const char *command = "PWD\r\n";
317 const int pwd_codes[] = {257, 0};
319 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
322 for (i = 0; res[i]; ++i) {
336 if (end > res && end[-1] == '/') {
340 av_strlcpy(s->path, start, sizeof(s->path));
350 static int ftp_file_size(FTPContext *s)
352 char command[CONTROL_BUFFER_SIZE];
354 const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
356 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
357 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
358 s->filesize = strtoll(&res[4], NULL, 10);
369 static int ftp_retrieve(FTPContext *s)
371 char command[CONTROL_BUFFER_SIZE];
372 const int retr_codes[] = {150, 550, 0}; /* 550 is incorrect code */
374 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
375 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
378 s->state = DOWNLOADING;
383 static int ftp_store(FTPContext *s)
385 char command[CONTROL_BUFFER_SIZE];
386 const int stor_codes[] = {150, 0};
388 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
389 if (!ftp_send_command(s, command, stor_codes, NULL))
392 s->state = UPLOADING;
397 static int ftp_type(FTPContext *s)
399 const char *command = "TYPE I\r\n";
400 const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
402 if (ftp_send_command(s, command, type_codes, NULL) != 200)
408 static int ftp_restart(FTPContext *s, int64_t pos)
410 char command[CONTROL_BUFFER_SIZE];
411 const int rest_codes[] = {350, 501, 0}; /* 501 is incorrect code */
413 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
414 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
420 static int ftp_connect_control_connection(URLContext *h)
422 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
424 AVDictionary *opts = NULL;
425 FTPContext *s = h->priv_data;
426 const int connect_codes[] = {220, 0};
428 s->conn_control_block_flag = 0;
430 if (!s->conn_control) {
431 ff_url_join(buf, sizeof(buf), "tcp", NULL,
432 s->hostname, s->server_control_port, NULL);
433 if (s->rw_timeout != -1) {
434 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
435 av_dict_set(&opts, "timeout", opts_format, 0);
436 } /* if option is not given, don't pass it and let tcp use its own default */
437 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
438 &s->conn_control_interrupt_cb, &opts);
441 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
445 /* consume all messages from server */
446 if (!ftp_status(s, NULL, connect_codes)) {
447 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
448 err = AVERROR(EACCES);
452 if ((err = ftp_auth(s)) < 0) {
453 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
457 if ((err = ftp_type(s)) < 0) {
458 av_dlog(h, "Set content type failed\n");
465 static int ftp_connect_data_connection(URLContext *h)
468 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
469 AVDictionary *opts = NULL;
470 FTPContext *s = h->priv_data;
473 /* Enter passive mode */
474 if ((err = ftp_passive_mode(s)) < 0) {
475 av_dlog(h, "Set passive mode failed\n");
478 /* Open data connection */
479 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
480 if (s->rw_timeout != -1) {
481 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
482 av_dict_set(&opts, "timeout", opts_format, 0);
483 } /* if option is not given, don't pass it and let tcp use its own default */
484 err = ffurl_open(&s->conn_data, buf, AVIO_FLAG_READ_WRITE,
485 &h->interrupt_callback, &opts);
491 if ((err = ftp_restart(s, s->position)) < 0)
498 static int ftp_abort(URLContext *h)
501 ftp_close_both_connections(h->priv_data);
502 if ((err = ftp_connect_control_connection(h)) < 0)
507 static int ftp_open(URLContext *h, const char *url, int flags)
509 char proto[10], path[MAX_URL_SIZE];
511 FTPContext *s = h->priv_data;
513 av_dlog(h, "ftp protocol open\n");
515 s->state = DISCONNECTED;
518 s->conn_control_interrupt_cb.opaque = s;
519 s->conn_control_interrupt_cb.callback = ftp_conn_control_block_control;
521 av_url_split(proto, sizeof(proto),
522 s->credencials, sizeof(s->credencials),
523 s->hostname, sizeof(s->hostname),
524 &s->server_control_port,
528 if (s->server_control_port < 0 || s->server_control_port > 65535)
529 s->server_control_port = 21;
531 if ((err = ftp_connect_control_connection(h)) < 0)
534 if ((err = ftp_current_dir(s)) < 0)
536 av_strlcat(s->path, path, sizeof(s->path));
538 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
540 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
546 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
547 ffurl_closep(&s->conn_control);
548 ffurl_closep(&s->conn_data);
552 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
554 FTPContext *s = h->priv_data;
558 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
567 new_pos = s->position + pos;
572 new_pos = s->filesize + pos;
575 return AVERROR(EINVAL);
581 new_pos = FFMAX(0, new_pos);
582 if (s->filesize >= 0)
583 new_pos = FFMIN(s->filesize, new_pos);
585 if (new_pos != s->position) {
586 /* XXX: Full abort is a save solution here.
587 Some optimalizations are possible, but may lead to crazy states of FTP server.
588 The worst scenario would be when FTP server closed both connection due to no transfer. */
589 if ((err = ftp_abort(h)) < 0)
592 s->position = new_pos;
597 static int ftp_read(URLContext *h, unsigned char *buf, int size)
599 FTPContext *s = h->priv_data;
600 int read, err, retry_done = 0;
602 av_dlog(h, "ftp protocol read %d bytes\n", size);
604 if (s->state == DISCONNECTED) {
605 if ((err = ftp_connect_data_connection(h)) < 0)
608 if (s->state == READY) {
609 if ((err = ftp_retrieve(s)) < 0)
612 if (s->conn_data && s->state == DOWNLOADING) {
613 read = ffurl_read(s->conn_data, buf, size);
616 if (s->position >= s->filesize) {
617 if (ftp_abort(h) < 0)
621 if (!read && s->position < s->filesize && !h->is_streamed) {
622 /* Server closed connection. Probably due to inactivity */
623 /* TODO: Consider retry before reconnect */
624 int64_t pos = s->position;
625 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
626 if ((err = ftp_abort(h)) < 0) {
627 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
630 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
631 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
642 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
646 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
649 FTPContext *s = h->priv_data;
652 av_dlog(h, "ftp protocol write %d bytes\n", size);
654 if (s->state == DISCONNECTED) {
655 if ((err = ftp_connect_data_connection(h)) < 0)
658 if (s->state == READY) {
659 if ((err = ftp_store(s)) < 0)
662 if (s->conn_data && s->state == UPLOADING) {
663 written = ffurl_write(s->conn_data, buf, size);
665 s->position += written;
666 s->filesize = FFMAX(s->filesize, s->position);
671 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
675 static int ftp_close(URLContext *h)
677 av_dlog(h, "ftp protocol close\n");
679 ftp_close_both_connections(h->priv_data);
684 static int ftp_get_file_handle(URLContext *h)
686 FTPContext *s = h->priv_data;
688 av_dlog(h, "ftp protocol get_file_handle\n");
691 return ffurl_get_file_handle(s->conn_data);
696 static int ftp_shutdown(URLContext *h, int flags)
698 FTPContext *s = h->priv_data;
700 av_dlog(h, "ftp protocol shutdown\n");
703 return ffurl_shutdown(s->conn_data, flags);
708 URLProtocol ff_ftp_protocol = {
710 .url_open = ftp_open,
711 .url_read = ftp_read,
712 .url_write = ftp_write,
713 .url_seek = ftp_seek,
714 .url_close = ftp_close,
715 .url_get_file_handle = ftp_get_file_handle,
716 .url_shutdown = ftp_shutdown,
717 .priv_data_size = sizeof(FTPContext),
718 .priv_data_class = &ftp_context_class,
719 .flags = URL_PROTOCOL_FLAG_NETWORK,