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, *end;
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)
277 for (i = 0; i < strlen(res); ++i) {
280 } else if (res[i] == ')') {
290 if (!av_strtok(start, ",", &end)) goto fail;
291 if (!av_strtok(end, ",", &end)) goto fail;
292 if (!av_strtok(end, ",", &end)) goto fail;
293 if (!av_strtok(end, ",", &end)) goto fail;
295 /* parse port number */
296 start = av_strtok(end, ",", &end);
297 if (!start) goto fail;
298 s->server_data_port = atoi(start) * 256;
299 start = av_strtok(end, ",", &end);
300 if (!start) goto fail;
301 s->server_data_port += atoi(start);
302 av_dlog(s, "Server data port: %d\n", s->server_data_port);
309 s->server_data_port = -1;
313 static int ftp_current_dir(FTPContext *s)
315 char *res = NULL, *start = NULL, *end = NULL;
317 const char *command = "PWD\r\n";
318 const int pwd_codes[] = {257, 0};
320 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
323 for (i = 0; res[i]; ++i) {
337 if (end > res && end[-1] == '/') {
341 av_strlcpy(s->path, start, sizeof(s->path));
351 static int ftp_file_size(FTPContext *s)
353 char command[CONTROL_BUFFER_SIZE];
355 const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
357 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
358 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
359 s->filesize = strtoll(&res[4], NULL, 10);
370 static int ftp_retrieve(FTPContext *s)
372 char command[CONTROL_BUFFER_SIZE];
373 const int retr_codes[] = {150, 550, 0}; /* 550 is incorrect code */
375 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
376 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
379 s->state = DOWNLOADING;
384 static int ftp_store(FTPContext *s)
386 char command[CONTROL_BUFFER_SIZE];
387 const int stor_codes[] = {150, 0};
389 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
390 if (!ftp_send_command(s, command, stor_codes, NULL))
393 s->state = UPLOADING;
398 static int ftp_type(FTPContext *s)
400 const char *command = "TYPE I\r\n";
401 const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
403 if (ftp_send_command(s, command, type_codes, NULL) != 200)
409 static int ftp_restart(FTPContext *s, int64_t pos)
411 char command[CONTROL_BUFFER_SIZE];
412 const int rest_codes[] = {350, 501, 0}; /* 501 is incorrect code */
414 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
415 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
421 static int ftp_connect_control_connection(URLContext *h)
423 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
425 AVDictionary *opts = NULL;
426 FTPContext *s = h->priv_data;
427 const int connect_codes[] = {220, 0};
429 s->conn_control_block_flag = 0;
431 if (!s->conn_control) {
432 ff_url_join(buf, sizeof(buf), "tcp", NULL,
433 s->hostname, s->server_control_port, NULL);
434 if (s->rw_timeout != -1) {
435 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
436 av_dict_set(&opts, "timeout", opts_format, 0);
437 } /* if option is not given, don't pass it and let tcp use its own default */
438 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
439 &s->conn_control_interrupt_cb, &opts);
442 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
446 /* consume all messages from server */
447 if (!ftp_status(s, NULL, connect_codes)) {
448 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
449 err = AVERROR(EACCES);
453 if ((err = ftp_auth(s)) < 0) {
454 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
458 if ((err = ftp_type(s)) < 0) {
459 av_dlog(h, "Set content type failed\n");
466 static int ftp_connect_data_connection(URLContext *h)
469 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
470 AVDictionary *opts = NULL;
471 FTPContext *s = h->priv_data;
474 /* Enter passive mode */
475 if ((err = ftp_passive_mode(s)) < 0) {
476 av_dlog(h, "Set passive mode failed\n");
479 /* Open data connection */
480 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
481 if (s->rw_timeout != -1) {
482 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
483 av_dict_set(&opts, "timeout", opts_format, 0);
484 } /* if option is not given, don't pass it and let tcp use its own default */
485 err = ffurl_open(&s->conn_data, buf, AVIO_FLAG_READ_WRITE,
486 &h->interrupt_callback, &opts);
492 if ((err = ftp_restart(s, s->position)) < 0)
499 static int ftp_abort(URLContext *h)
502 ftp_close_both_connections(h->priv_data);
503 if ((err = ftp_connect_control_connection(h)) < 0)
508 static int ftp_open(URLContext *h, const char *url, int flags)
510 char proto[10], path[MAX_URL_SIZE];
512 FTPContext *s = h->priv_data;
514 av_dlog(h, "ftp protocol open\n");
516 s->state = DISCONNECTED;
519 s->conn_control_interrupt_cb.opaque = s;
520 s->conn_control_interrupt_cb.callback = ftp_conn_control_block_control;
522 av_url_split(proto, sizeof(proto),
523 s->credencials, sizeof(s->credencials),
524 s->hostname, sizeof(s->hostname),
525 &s->server_control_port,
529 if (s->server_control_port < 0 || s->server_control_port > 65535)
530 s->server_control_port = 21;
532 if ((err = ftp_connect_control_connection(h)) < 0)
535 if ((err = ftp_current_dir(s)) < 0)
537 av_strlcat(s->path, path, sizeof(s->path));
539 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
541 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
547 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
548 ffurl_closep(&s->conn_control);
549 ffurl_closep(&s->conn_data);
553 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
555 FTPContext *s = h->priv_data;
559 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
568 new_pos = s->position + pos;
573 new_pos = s->filesize + pos;
576 return AVERROR(EINVAL);
582 new_pos = FFMAX(0, new_pos);
583 if (s->filesize >= 0)
584 new_pos = FFMIN(s->filesize, new_pos);
586 if (new_pos != s->position) {
587 /* XXX: Full abort is a save solution here.
588 Some optimalizations are possible, but may lead to crazy states of FTP server.
589 The worst scenario would be when FTP server closed both connection due to no transfer. */
590 if ((err = ftp_abort(h)) < 0)
593 s->position = new_pos;
598 static int ftp_read(URLContext *h, unsigned char *buf, int size)
600 FTPContext *s = h->priv_data;
601 int read, err, retry_done = 0;
603 av_dlog(h, "ftp protocol read %d bytes\n", size);
605 if (s->state == DISCONNECTED) {
606 if ((err = ftp_connect_data_connection(h)) < 0)
609 if (s->state == READY) {
610 if ((err = ftp_retrieve(s)) < 0)
613 if (s->conn_data && s->state == DOWNLOADING) {
614 read = ffurl_read(s->conn_data, buf, size);
617 if (s->position >= s->filesize) {
618 if (ftp_abort(h) < 0)
622 if (!read && s->position < s->filesize && !h->is_streamed) {
623 /* Server closed connection. Probably due to inactivity */
624 /* TODO: Consider retry before reconnect */
625 int64_t pos = s->position;
626 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
627 if ((err = ftp_abort(h)) < 0) {
628 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
631 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
632 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
643 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
647 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
650 FTPContext *s = h->priv_data;
653 av_dlog(h, "ftp protocol write %d bytes\n", size);
655 if (s->state == DISCONNECTED) {
656 if ((err = ftp_connect_data_connection(h)) < 0)
659 if (s->state == READY) {
660 if ((err = ftp_store(s)) < 0)
663 if (s->conn_data && s->state == UPLOADING) {
664 written = ffurl_write(s->conn_data, buf, size);
666 s->position += written;
667 s->filesize = FFMAX(s->filesize, s->position);
672 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
676 static int ftp_close(URLContext *h)
678 av_dlog(h, "ftp protocol close\n");
680 ftp_close_both_connections(h->priv_data);
685 static int ftp_get_file_handle(URLContext *h)
687 FTPContext *s = h->priv_data;
689 av_dlog(h, "ftp protocol get_file_handle\n");
692 return ffurl_get_file_handle(s->conn_data);
697 static int ftp_shutdown(URLContext *h, int flags)
699 FTPContext *s = h->priv_data;
701 av_dlog(h, "ftp protocol shutdown\n");
704 return ffurl_shutdown(s->conn_data, flags);
709 URLProtocol ff_ftp_protocol = {
711 .url_open = ftp_open,
712 .url_read = ftp_read,
713 .url_write = ftp_write,
714 .url_seek = ftp_seek,
715 .url_close = ftp_close,
716 .url_get_file_handle = ftp_get_file_handle,
717 .url_shutdown = ftp_shutdown,
718 .priv_data_size = sizeof(FTPContext),
719 .priv_data_class = &ftp_context_class,
720 .flags = URL_PROTOCOL_FLAG_NETWORK,