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;
273 av_log(s, AV_LOG_ERROR, "Set passive mode failed\n"
274 "Your FTP server may use IPv6 which is not supported yet.\n");
278 static int ftp_current_dir(FTPContext *s)
280 char *res = NULL, *start = NULL, *end = NULL;
282 static const char *command = "PWD\r\n";
283 static const int pwd_codes[] = {257, 0};
285 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
288 for (i = 0; res[i]; ++i) {
302 if (end > res && end[-1] == '/') {
306 av_strlcpy(s->path, start, sizeof(s->path));
316 static int ftp_file_size(FTPContext *s)
318 char command[CONTROL_BUFFER_SIZE];
320 static const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
322 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
323 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
324 s->filesize = strtoll(&res[4], NULL, 10);
335 static int ftp_retrieve(FTPContext *s)
337 char command[CONTROL_BUFFER_SIZE];
338 static const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
340 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
341 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
344 s->state = DOWNLOADING;
349 static int ftp_store(FTPContext *s)
351 char command[CONTROL_BUFFER_SIZE];
352 static const int stor_codes[] = {150, 0};
354 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
355 if (ftp_send_command(s, command, stor_codes, NULL) != 150)
358 s->state = UPLOADING;
363 static int ftp_type(FTPContext *s)
365 static const char *command = "TYPE I\r\n";
366 static const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
368 if (ftp_send_command(s, command, type_codes, NULL) != 200)
374 static int ftp_restart(FTPContext *s, int64_t pos)
376 char command[CONTROL_BUFFER_SIZE];
377 static const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
379 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
380 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
386 static int ftp_connect_control_connection(URLContext *h)
388 char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
390 AVDictionary *opts = NULL;
391 FTPContext *s = h->priv_data;
392 static const int connect_codes[] = {220, 0};
394 if (!s->conn_control) {
395 ff_url_join(buf, sizeof(buf), "tcp", NULL,
396 s->hostname, s->server_control_port, NULL);
397 if (s->rw_timeout != -1) {
398 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
399 av_dict_set(&opts, "timeout", opts_format, 0);
400 } /* if option is not given, don't pass it and let tcp use its own default */
401 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
402 &h->interrupt_callback, &opts);
405 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
409 /* check if server is ready */
410 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
411 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
412 return AVERROR(EACCES);
415 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
416 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.");
420 if ((err = ftp_auth(s)) < 0) {
421 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
425 if ((err = ftp_type(s)) < 0) {
426 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
433 static int ftp_connect_data_connection(URLContext *h)
436 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
437 AVDictionary *opts = NULL;
438 FTPContext *s = h->priv_data;
441 /* Enter passive mode */
442 if ((err = ftp_passive_mode(s)) < 0)
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);
489 if (ftp_status(s, NULL, abor_codes) < 225) {
490 /* wu-ftpd also closes control connection after data connection closing */
491 ffurl_closep(&s->conn_control);
492 if ((err = ftp_connect_control_connection(h)) < 0) {
493 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
502 static int ftp_open(URLContext *h, const char *url, int flags)
504 char proto[10], path[MAX_URL_SIZE];
506 FTPContext *s = h->priv_data;
508 av_dlog(h, "ftp protocol open\n");
510 s->state = DISCONNECTED;
514 av_url_split(proto, sizeof(proto),
515 s->credencials, sizeof(s->credencials),
516 s->hostname, sizeof(s->hostname),
517 &s->server_control_port,
521 if (s->server_control_port < 0 || s->server_control_port > 65535)
522 s->server_control_port = 21;
524 if ((err = ftp_connect_control_connection(h)) < 0)
527 if ((err = ftp_current_dir(s)) < 0)
529 av_strlcat(s->path, path, sizeof(s->path));
531 if (ftp_restart(s, 0) < 0) {
534 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
536 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
543 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
544 ffurl_closep(&s->conn_control);
545 ffurl_closep(&s->conn_data);
549 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
551 FTPContext *s = h->priv_data;
553 int64_t new_pos, fake_pos;
555 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
564 new_pos = s->position + pos;
569 new_pos = s->filesize + pos;
572 return AVERROR(EINVAL);
578 /* XXX: Simulate behaviour of lseek in file protocol, which could be treated as a reference */
579 new_pos = FFMAX(0, new_pos);
580 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
582 if (fake_pos != s->position) {
583 if ((err = ftp_abort(h)) < 0)
585 s->position = fake_pos;
590 static int ftp_read(URLContext *h, unsigned char *buf, int size)
592 FTPContext *s = h->priv_data;
593 int read, err, retry_done = 0;
595 av_dlog(h, "ftp protocol read %d bytes\n", size);
597 if (s->state == DISCONNECTED) {
599 if (s->position >= s->filesize)
601 if ((err = ftp_connect_data_connection(h)) < 0)
604 if (s->state == READY) {
605 if (s->position >= s->filesize)
607 if ((err = ftp_retrieve(s)) < 0)
610 if (s->conn_data && s->state == DOWNLOADING) {
611 read = ffurl_read(s->conn_data, buf, size);
614 if (s->position >= s->filesize) {
615 /* server will terminate, but keep current position to avoid madness */
616 /* save position to restart from it */
617 int64_t pos = s->position;
618 if (ftp_abort(h) < 0) {
625 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
626 /* Server closed connection. Probably due to inactivity */
627 int64_t pos = s->position;
628 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
629 if ((err = ftp_abort(h)) < 0)
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,