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;
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);
142 for (i = 0; i < 3; ++i) {
143 if (buf[i] < '0' || buf[i] > '9')
148 dash = !!(buf[3] == '-');
150 for (i = 0; response_codes[i]; ++i) {
151 if (err == response_codes[i]) {
153 av_bprintf(&line_buffer, "%s", buf);
162 av_bprint_finalize(&line_buffer, line);
166 static int ftp_send_command(FTPContext *s, const char *command,
167 const int response_codes[], char **response)
171 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
177 if (response_codes) {
178 return ftp_status(s, response, response_codes);
183 static void ftp_close_data_connection(FTPContext *s)
185 ffurl_closep(&s->conn_data);
187 s->state = DISCONNECTED;
190 static void ftp_close_both_connections(FTPContext *s)
192 ffurl_closep(&s->conn_control);
193 ftp_close_data_connection(s);
196 static int ftp_auth(FTPContext *s)
198 const char *user = NULL, *pass = NULL;
199 char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
201 static const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
202 static const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
204 /* Authentication may be repeated, original string has to be saved */
205 av_strlcpy(credencials, s->credencials, sizeof(credencials));
207 user = av_strtok(credencials, ":", &end);
208 pass = av_strtok(end, ":", &end);
212 pass = s->anonymous_password ? s->anonymous_password : "nopassword";
215 snprintf(buf, sizeof(buf), "USER %s\r\n", user);
216 err = ftp_send_command(s, buf, user_codes, NULL);
219 snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
220 err = ftp_send_command(s, buf, pass_codes, NULL);
222 return AVERROR(EACCES);
225 return AVERROR(EACCES);
230 static int ftp_passive_mode(FTPContext *s)
232 char *res = NULL, *start = NULL, *end = NULL;
234 static const char *command = "PASV\r\n";
235 static const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
237 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
240 for (i = 0; res[i]; ++i) {
243 } else if (res[i] == ')') {
253 if (!av_strtok(start, ",", &end)) goto fail;
254 if (!av_strtok(end, ",", &end)) goto fail;
255 if (!av_strtok(end, ",", &end)) goto fail;
256 if (!av_strtok(end, ",", &end)) goto fail;
258 /* parse port number */
259 start = av_strtok(end, ",", &end);
260 if (!start) goto fail;
261 s->server_data_port = atoi(start) * 256;
262 start = av_strtok(end, ",", &end);
263 if (!start) goto fail;
264 s->server_data_port += atoi(start);
265 av_dlog(s, "Server data port: %d\n", s->server_data_port);
272 s->server_data_port = -1;
276 static int ftp_current_dir(FTPContext *s)
278 char *res = NULL, *start = NULL, *end = NULL;
280 static const char *command = "PWD\r\n";
281 static const int pwd_codes[] = {257, 0};
283 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
286 for (i = 0; res[i]; ++i) {
300 if (end > res && end[-1] == '/') {
304 av_strlcpy(s->path, start, sizeof(s->path));
314 static int ftp_file_size(FTPContext *s)
316 char command[CONTROL_BUFFER_SIZE];
318 static const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
320 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
321 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
322 s->filesize = strtoll(&res[4], NULL, 10);
333 static int ftp_retrieve(FTPContext *s)
335 char command[CONTROL_BUFFER_SIZE];
336 static const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
338 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
339 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
342 s->state = DOWNLOADING;
347 static int ftp_store(FTPContext *s)
349 char command[CONTROL_BUFFER_SIZE];
350 static const int stor_codes[] = {150, 0};
352 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
353 if (ftp_send_command(s, command, stor_codes, NULL) != 150)
356 s->state = UPLOADING;
361 static int ftp_type(FTPContext *s)
363 static const char *command = "TYPE I\r\n";
364 static const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
366 if (ftp_send_command(s, command, type_codes, NULL) != 200)
372 static int ftp_restart(FTPContext *s, int64_t pos)
374 char command[CONTROL_BUFFER_SIZE];
375 static const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
377 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
378 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
384 static int ftp_connect_control_connection(URLContext *h)
386 char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
388 AVDictionary *opts = NULL;
389 FTPContext *s = h->priv_data;
390 static const int connect_codes[] = {220, 0};
392 if (!s->conn_control) {
393 ff_url_join(buf, sizeof(buf), "tcp", NULL,
394 s->hostname, s->server_control_port, NULL);
395 if (s->rw_timeout != -1) {
396 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
397 av_dict_set(&opts, "timeout", opts_format, 0);
398 } /* if option is not given, don't pass it and let tcp use its own default */
399 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
400 &h->interrupt_callback, &opts);
403 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
407 /* check if server is ready */
408 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
409 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
410 return AVERROR(EACCES);
413 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
414 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.");
418 if ((err = ftp_auth(s)) < 0) {
419 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
423 if ((err = ftp_type(s)) < 0) {
424 av_dlog(h, "Set content type failed\n");
431 static int ftp_connect_data_connection(URLContext *h)
434 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
435 AVDictionary *opts = NULL;
436 FTPContext *s = h->priv_data;
439 /* Enter passive mode */
440 if ((err = ftp_passive_mode(s)) < 0) {
441 av_dlog(h, "Set passive mode failed\n");
444 /* Open data connection */
445 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
446 if (s->rw_timeout != -1) {
447 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
448 av_dict_set(&opts, "timeout", opts_format, 0);
449 } /* if option is not given, don't pass it and let tcp use its own default */
450 err = ffurl_open(&s->conn_data, buf, h->flags,
451 &h->interrupt_callback, &opts);
457 if ((err = ftp_restart(s, s->position)) < 0)
464 static int ftp_abort(URLContext *h)
466 static const char *command = "ABOR\r\n";
468 static const int abor_codes[] = {225, 226, 0};
469 FTPContext *s = h->priv_data;
471 /* According to RCF 959:
472 "ABOR command tells the server to abort the previous FTP
473 service command and any associated transfer of data."
475 There are FTP server implementations that don't response
476 to any commands during data transfer in passive mode (including ABOR).
478 This implementation closes data connection by force.
481 if (ftp_send_command(s, command, NULL, NULL) < 0) {
482 ftp_close_both_connections(s);
483 if ((err = ftp_connect_control_connection(h)) < 0) {
484 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
488 ftp_close_data_connection(s);
491 if (ftp_status(s, NULL, abor_codes) < 225) {
492 /* wu-ftpd also closes control connection after data connection closing */
493 ffurl_closep(&s->conn_control);
494 if ((err = ftp_connect_control_connection(h)) < 0) {
495 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
503 static int ftp_open(URLContext *h, const char *url, int flags)
505 char proto[10], path[MAX_URL_SIZE];
507 FTPContext *s = h->priv_data;
509 av_dlog(h, "ftp protocol open\n");
511 s->state = DISCONNECTED;
515 av_url_split(proto, sizeof(proto),
516 s->credencials, sizeof(s->credencials),
517 s->hostname, sizeof(s->hostname),
518 &s->server_control_port,
522 if (s->server_control_port < 0 || s->server_control_port > 65535)
523 s->server_control_port = 21;
525 if ((err = ftp_connect_control_connection(h)) < 0)
528 if ((err = ftp_current_dir(s)) < 0)
530 av_strlcat(s->path, path, sizeof(s->path));
532 if (ftp_restart(s, 0) < 0) {
535 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
537 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
544 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
545 ffurl_closep(&s->conn_control);
546 ffurl_closep(&s->conn_data);
550 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
552 FTPContext *s = h->priv_data;
554 int64_t new_pos, fake_pos;
556 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
565 new_pos = s->position + pos;
570 new_pos = s->filesize + pos;
573 return AVERROR(EINVAL);
579 /* XXX: Simulate behaviour of lseek in file protocol, which could be treated as a reference */
580 new_pos = FFMAX(0, new_pos);
581 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
583 if (fake_pos != s->position) {
584 if ((err = ftp_abort(h)) < 0)
586 s->position = fake_pos;
591 static int ftp_read(URLContext *h, unsigned char *buf, int size)
593 FTPContext *s = h->priv_data;
594 int read, err, retry_done = 0;
596 av_dlog(h, "ftp protocol read %d bytes\n", size);
598 if (s->state == DISCONNECTED) {
600 if (s->position >= s->filesize)
602 if ((err = ftp_connect_data_connection(h)) < 0)
605 if (s->state == READY) {
606 if (s->position >= s->filesize)
608 if ((err = ftp_retrieve(s)) < 0)
611 if (s->conn_data && s->state == DOWNLOADING) {
612 read = ffurl_read(s->conn_data, buf, size);
615 if (s->position >= s->filesize) {
616 /* server will terminate, but keep current position to avoid madness */
617 /* save position to restart from it */
618 int64_t pos = s->position;
619 if (ftp_abort(h) < 0) {
626 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
627 /* Server closed connection. Probably due to inactivity */
628 int64_t pos = s->position;
629 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
630 if ((err = ftp_abort(h)) < 0)
632 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
633 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
644 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
648 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
651 FTPContext *s = h->priv_data;
654 av_dlog(h, "ftp protocol write %d bytes\n", size);
656 if (s->state == DISCONNECTED) {
657 if ((err = ftp_connect_data_connection(h)) < 0)
660 if (s->state == READY) {
661 if ((err = ftp_store(s)) < 0)
664 if (s->conn_data && s->state == UPLOADING) {
665 written = ffurl_write(s->conn_data, buf, size);
667 s->position += written;
668 s->filesize = FFMAX(s->filesize, s->position);
673 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
677 static int ftp_close(URLContext *h)
679 av_dlog(h, "ftp protocol close\n");
681 ftp_close_both_connections(h->priv_data);
686 static int ftp_get_file_handle(URLContext *h)
688 FTPContext *s = h->priv_data;
690 av_dlog(h, "ftp protocol get_file_handle\n");
693 return ffurl_get_file_handle(s->conn_data);
698 static int ftp_shutdown(URLContext *h, int flags)
700 FTPContext *s = h->priv_data;
702 av_dlog(h, "ftp protocol shutdown\n");
705 return ffurl_shutdown(s->conn_data, flags);
710 URLProtocol ff_ftp_protocol = {
712 .url_open = ftp_open,
713 .url_read = ftp_read,
714 .url_write = ftp_write,
715 .url_seek = ftp_seek,
716 .url_close = ftp_close,
717 .url_get_file_handle = ftp_get_file_handle,
718 .url_shutdown = ftp_shutdown,
719 .priv_data_size = sizeof(FTPContext),
720 .priv_data_class = &ftp_context_class,
721 .flags = URL_PROTOCOL_FLAG_NETWORK,