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) {
131 av_bprint_finalize(&line_buffer, NULL);
135 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
141 for (i = 0; i < 3; ++i) {
142 if (buf[i] < '0' || buf[i] > '9')
147 dash = !!(buf[3] == '-');
149 for (i = 0; response_codes[i]; ++i) {
150 if (err == response_codes[i]) {
152 av_bprintf(&line_buffer, "%s", buf);
161 av_bprint_finalize(&line_buffer, line);
165 static int ftp_send_command(FTPContext *s, const char *command,
166 const int response_codes[], char **response)
170 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
176 if (response_codes) {
177 return ftp_status(s, response, response_codes);
182 static void ftp_close_data_connection(FTPContext *s)
184 ffurl_closep(&s->conn_data);
186 s->state = DISCONNECTED;
189 static void ftp_close_both_connections(FTPContext *s)
191 ffurl_closep(&s->conn_control);
192 ftp_close_data_connection(s);
195 static int ftp_auth(FTPContext *s)
197 const char *user = NULL, *pass = NULL;
198 char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
200 const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
201 const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
203 /* Authentication may be repeated, original string has to be saved */
204 av_strlcpy(credencials, s->credencials, sizeof(credencials));
206 user = av_strtok(credencials, ":", &end);
207 pass = av_strtok(end, ":", &end);
211 pass = s->anonymous_password ? s->anonymous_password : "nopassword";
214 snprintf(buf, sizeof(buf), "USER %s\r\n", user);
215 err = ftp_send_command(s, buf, user_codes, NULL);
218 snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
219 err = ftp_send_command(s, buf, pass_codes, NULL);
221 return AVERROR(EACCES);
224 return AVERROR(EACCES);
229 static int ftp_passive_mode(FTPContext *s)
231 char *res = NULL, *start = NULL, *end = NULL;
233 const char *command = "PASV\r\n";
234 const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
236 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
239 for (i = 0; res[i]; ++i) {
242 } else if (res[i] == ')') {
252 if (!av_strtok(start, ",", &end)) goto fail;
253 if (!av_strtok(end, ",", &end)) goto fail;
254 if (!av_strtok(end, ",", &end)) goto fail;
255 if (!av_strtok(end, ",", &end)) goto fail;
257 /* parse port number */
258 start = av_strtok(end, ",", &end);
259 if (!start) goto fail;
260 s->server_data_port = atoi(start) * 256;
261 start = av_strtok(end, ",", &end);
262 if (!start) goto fail;
263 s->server_data_port += atoi(start);
264 av_dlog(s, "Server data port: %d\n", s->server_data_port);
271 s->server_data_port = -1;
275 static int ftp_current_dir(FTPContext *s)
277 char *res = NULL, *start = NULL, *end = NULL;
279 const char *command = "PWD\r\n";
280 const int pwd_codes[] = {257, 0};
282 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
285 for (i = 0; res[i]; ++i) {
299 if (end > res && end[-1] == '/') {
303 av_strlcpy(s->path, start, sizeof(s->path));
313 static int ftp_file_size(FTPContext *s)
315 char command[CONTROL_BUFFER_SIZE];
317 const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
319 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
320 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
321 s->filesize = strtoll(&res[4], NULL, 10);
332 static int ftp_retrieve(FTPContext *s)
334 char command[CONTROL_BUFFER_SIZE];
335 const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
337 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
338 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
341 s->state = DOWNLOADING;
346 static int ftp_store(FTPContext *s)
348 char command[CONTROL_BUFFER_SIZE];
349 const int stor_codes[] = {150, 0};
351 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
352 if (ftp_send_command(s, command, stor_codes, NULL) != 150)
355 s->state = UPLOADING;
360 static int ftp_type(FTPContext *s)
362 const char *command = "TYPE I\r\n";
363 const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
365 if (ftp_send_command(s, command, type_codes, NULL) != 200)
371 static int ftp_restart(FTPContext *s, int64_t pos)
373 char command[CONTROL_BUFFER_SIZE];
374 const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
376 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
377 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
383 static int ftp_connect_control_connection(URLContext *h)
385 char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
387 AVDictionary *opts = NULL;
388 FTPContext *s = h->priv_data;
389 const int connect_codes[] = {220, 0};
391 if (!s->conn_control) {
392 ff_url_join(buf, sizeof(buf), "tcp", NULL,
393 s->hostname, s->server_control_port, NULL);
394 if (s->rw_timeout != -1) {
395 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
396 av_dict_set(&opts, "timeout", opts_format, 0);
397 } /* if option is not given, don't pass it and let tcp use its own default */
398 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
399 &h->interrupt_callback, &opts);
402 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
406 /* check if server is ready */
407 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
408 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
409 return AVERROR(EACCES);
412 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
413 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.");
417 if ((err = ftp_auth(s)) < 0) {
418 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
422 if ((err = ftp_type(s)) < 0) {
423 av_dlog(h, "Set content type failed\n");
430 static int ftp_connect_data_connection(URLContext *h)
433 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
434 AVDictionary *opts = NULL;
435 FTPContext *s = h->priv_data;
438 /* Enter passive mode */
439 if ((err = ftp_passive_mode(s)) < 0) {
440 av_dlog(h, "Set passive mode failed\n");
443 /* Open data connection */
444 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
445 if (s->rw_timeout != -1) {
446 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
447 av_dict_set(&opts, "timeout", opts_format, 0);
448 } /* if option is not given, don't pass it and let tcp use its own default */
449 err = ffurl_open(&s->conn_data, buf, h->flags,
450 &h->interrupt_callback, &opts);
456 if ((err = ftp_restart(s, s->position)) < 0)
463 static int ftp_abort(URLContext *h)
465 const char *command = "ABOR\r\n";
467 const int abor_codes[] = {225, 226, 0};
468 FTPContext *s = h->priv_data;
470 /* According to RCF 959:
471 "ABOR command tells the server to abort the previous FTP
472 service command and any associated transfer of data."
474 There are FTP server implementations that don't response
475 to any commands during data transfer in passive mode (including ABOR).
477 This implementation closes data connection by force.
480 if (ftp_send_command(s, command, NULL, NULL) < 0) {
481 ftp_close_both_connections(s);
482 if ((err = ftp_connect_control_connection(h)) < 0) {
483 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
487 ftp_close_data_connection(s);
490 if (ftp_status(s, NULL, abor_codes) < 225) {
491 /* wu-ftpd also closes control connection after data connection closing */
492 ffurl_closep(&s->conn_control);
493 if ((err = ftp_connect_control_connection(h)) < 0) {
494 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,