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, linesize;
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);
138 linesize = strlen(buf);
141 for (i = 0; i < 3; ++i) {
142 if (buf[i] < '0' || buf[i] > '9') {
152 for (i = 0; response_codes[i]; ++i) {
153 if (err == response_codes[i]) {
162 av_bprintf(&line_buffer, "%s\r\n", buf);
164 if (!dash && buf[3] == '-')
166 else if (err == dash && buf[3] == ' ')
173 av_bprint_finalize(&line_buffer, line);
177 static int ftp_send_command(FTPContext *s, const char *command,
178 const int response_codes[], char **response)
182 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
188 if (response_codes) {
189 return ftp_status(s, response, response_codes);
194 static void ftp_close_data_connection(FTPContext *s)
196 ffurl_closep(&s->conn_data);
198 s->state = DISCONNECTED;
201 static void ftp_close_both_connections(FTPContext *s)
203 ffurl_closep(&s->conn_control);
204 ftp_close_data_connection(s);
207 static int ftp_auth(FTPContext *s)
209 const char *user = NULL, *pass = NULL;
210 char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
212 static const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
213 static const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
215 /* Authentication may be repeated, original string has to be saved */
216 av_strlcpy(credencials, s->credencials, sizeof(credencials));
218 user = av_strtok(credencials, ":", &end);
219 pass = av_strtok(end, ":", &end);
223 pass = s->anonymous_password ? s->anonymous_password : "nopassword";
226 snprintf(buf, sizeof(buf), "USER %s\r\n", user);
227 err = ftp_send_command(s, buf, user_codes, NULL);
230 snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
231 err = ftp_send_command(s, buf, pass_codes, NULL);
233 return AVERROR(EACCES);
236 return AVERROR(EACCES);
241 static int ftp_passive_mode_epsv(FTPContext *s)
243 char *res = NULL, *start = NULL, *end = NULL;
245 static const char d = '|';
246 static const char *command = "EPSV\r\n";
247 static const int epsv_codes[] = {229, 500, 501, 0}; /* 500, 501 are incorrect codes */
249 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
252 for (i = 0; res[i]; ++i) {
255 } else if (res[i] == ')') {
264 if (strlen(start) < 5)
266 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
271 s->server_data_port = atoi(start);
272 av_dlog(s, "Server data port: %d\n", s->server_data_port);
279 s->server_data_port = -1;
280 return AVERROR(ENOSYS);
283 static int ftp_passive_mode(FTPContext *s)
285 char *res = NULL, *start = NULL, *end = NULL;
287 static const char *command = "PASV\r\n";
288 static const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
290 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
293 for (i = 0; res[i]; ++i) {
296 } else if (res[i] == ')') {
306 if (!av_strtok(start, ",", &end)) goto fail;
307 if (!av_strtok(end, ",", &end)) goto fail;
308 if (!av_strtok(end, ",", &end)) goto fail;
309 if (!av_strtok(end, ",", &end)) goto fail;
311 /* parse port number */
312 start = av_strtok(end, ",", &end);
313 if (!start) goto fail;
314 s->server_data_port = atoi(start) * 256;
315 start = av_strtok(end, ",", &end);
316 if (!start) goto fail;
317 s->server_data_port += atoi(start);
318 av_dlog(s, "Server data port: %d\n", s->server_data_port);
325 s->server_data_port = -1;
329 static int ftp_current_dir(FTPContext *s)
331 char *res = NULL, *start = NULL, *end = NULL;
333 static const char *command = "PWD\r\n";
334 static const int pwd_codes[] = {257, 0};
336 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
339 for (i = 0; res[i]; ++i) {
353 if (end > res && end[-1] == '/') {
357 av_strlcpy(s->path, start, sizeof(s->path));
367 static int ftp_file_size(FTPContext *s)
369 char command[CONTROL_BUFFER_SIZE];
371 static const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
373 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
374 if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
375 s->filesize = strtoll(&res[4], NULL, 10);
386 static int ftp_retrieve(FTPContext *s)
388 char command[CONTROL_BUFFER_SIZE];
389 static const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
391 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
392 if (ftp_send_command(s, command, retr_codes, NULL) != 150)
395 s->state = DOWNLOADING;
400 static int ftp_store(FTPContext *s)
402 char command[CONTROL_BUFFER_SIZE];
403 static const int stor_codes[] = {150, 0};
405 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
406 if (ftp_send_command(s, command, stor_codes, NULL) != 150)
409 s->state = UPLOADING;
414 static int ftp_type(FTPContext *s)
416 static const char *command = "TYPE I\r\n";
417 static const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
419 if (ftp_send_command(s, command, type_codes, NULL) != 200)
425 static int ftp_restart(FTPContext *s, int64_t pos)
427 char command[CONTROL_BUFFER_SIZE];
428 static const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
430 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
431 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
437 static int ftp_connect_control_connection(URLContext *h)
439 char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
441 AVDictionary *opts = NULL;
442 FTPContext *s = h->priv_data;
443 static const int connect_codes[] = {220, 0};
445 if (!s->conn_control) {
446 ff_url_join(buf, sizeof(buf), "tcp", NULL,
447 s->hostname, s->server_control_port, NULL);
448 if (s->rw_timeout != -1) {
449 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
450 av_dict_set(&opts, "timeout", opts_format, 0);
451 } /* if option is not given, don't pass it and let tcp use its own default */
452 err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
453 &h->interrupt_callback, &opts);
456 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
460 /* check if server is ready */
461 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
462 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
463 return AVERROR(EACCES);
466 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
467 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.");
471 if ((err = ftp_auth(s)) < 0) {
472 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
476 if ((err = ftp_type(s)) < 0) {
477 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
484 static int ftp_connect_data_connection(URLContext *h)
487 char buf[CONTROL_BUFFER_SIZE], opts_format[20];
488 AVDictionary *opts = NULL;
489 FTPContext *s = h->priv_data;
492 /* Enter passive mode */
493 if (ftp_passive_mode_epsv(s) < 0) {
494 /* Use PASV as fallback */
495 if ((err = ftp_passive_mode(s)) < 0)
498 /* Open data connection */
499 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
500 if (s->rw_timeout != -1) {
501 snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
502 av_dict_set(&opts, "timeout", opts_format, 0);
503 } /* if option is not given, don't pass it and let tcp use its own default */
504 err = ffurl_open(&s->conn_data, buf, h->flags,
505 &h->interrupt_callback, &opts);
511 if ((err = ftp_restart(s, s->position)) < 0)
518 static int ftp_abort(URLContext *h)
520 static const char *command = "ABOR\r\n";
522 static const int abor_codes[] = {225, 226, 0};
523 FTPContext *s = h->priv_data;
525 /* According to RCF 959:
526 "ABOR command tells the server to abort the previous FTP
527 service command and any associated transfer of data."
529 There are FTP server implementations that don't response
530 to any commands during data transfer in passive mode (including ABOR).
532 This implementation closes data connection by force.
535 if (ftp_send_command(s, command, NULL, NULL) < 0) {
536 ftp_close_both_connections(s);
537 if ((err = ftp_connect_control_connection(h)) < 0) {
538 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
542 ftp_close_data_connection(s);
543 if (ftp_status(s, NULL, abor_codes) < 225) {
544 /* wu-ftpd also closes control connection after data connection closing */
545 ffurl_closep(&s->conn_control);
546 if ((err = ftp_connect_control_connection(h)) < 0) {
547 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
556 static int ftp_open(URLContext *h, const char *url, int flags)
558 char proto[10], path[MAX_URL_SIZE];
560 FTPContext *s = h->priv_data;
562 av_dlog(h, "ftp protocol open\n");
564 s->state = DISCONNECTED;
568 av_url_split(proto, sizeof(proto),
569 s->credencials, sizeof(s->credencials),
570 s->hostname, sizeof(s->hostname),
571 &s->server_control_port,
575 if (s->server_control_port < 0 || s->server_control_port > 65535)
576 s->server_control_port = 21;
578 if ((err = ftp_connect_control_connection(h)) < 0)
581 if ((err = ftp_current_dir(s)) < 0)
583 av_strlcat(s->path, path, sizeof(s->path));
585 if (ftp_restart(s, 0) < 0) {
588 if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
590 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
597 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
598 ffurl_closep(&s->conn_control);
599 ffurl_closep(&s->conn_data);
603 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
605 FTPContext *s = h->priv_data;
607 int64_t new_pos, fake_pos;
609 av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
618 new_pos = s->position + pos;
623 new_pos = s->filesize + pos;
626 return AVERROR(EINVAL);
633 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
634 return AVERROR(EINVAL);
637 fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
638 if (fake_pos != s->position) {
639 if ((err = ftp_abort(h)) < 0)
641 s->position = fake_pos;
646 static int ftp_read(URLContext *h, unsigned char *buf, int size)
648 FTPContext *s = h->priv_data;
649 int read, err, retry_done = 0;
651 av_dlog(h, "ftp protocol read %d bytes\n", size);
653 if (s->state == DISCONNECTED) {
655 if (s->position >= s->filesize)
657 if ((err = ftp_connect_data_connection(h)) < 0)
660 if (s->state == READY) {
661 if (s->position >= s->filesize)
663 if ((err = ftp_retrieve(s)) < 0)
666 if (s->conn_data && s->state == DOWNLOADING) {
667 read = ffurl_read(s->conn_data, buf, size);
670 if (s->position >= s->filesize) {
671 /* server will terminate, but keep current position to avoid madness */
672 /* save position to restart from it */
673 int64_t pos = s->position;
674 if (ftp_abort(h) < 0) {
681 if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
682 /* Server closed connection. Probably due to inactivity */
683 int64_t pos = s->position;
684 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
685 if ((err = ftp_abort(h)) < 0)
687 if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
688 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
699 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
703 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
706 FTPContext *s = h->priv_data;
709 av_dlog(h, "ftp protocol write %d bytes\n", size);
711 if (s->state == DISCONNECTED) {
712 if ((err = ftp_connect_data_connection(h)) < 0)
715 if (s->state == READY) {
716 if ((err = ftp_store(s)) < 0)
719 if (s->conn_data && s->state == UPLOADING) {
720 written = ffurl_write(s->conn_data, buf, size);
722 s->position += written;
723 s->filesize = FFMAX(s->filesize, s->position);
728 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
732 static int ftp_close(URLContext *h)
734 av_dlog(h, "ftp protocol close\n");
736 ftp_close_both_connections(h->priv_data);
741 static int ftp_get_file_handle(URLContext *h)
743 FTPContext *s = h->priv_data;
745 av_dlog(h, "ftp protocol get_file_handle\n");
748 return ffurl_get_file_handle(s->conn_data);
753 static int ftp_shutdown(URLContext *h, int flags)
755 FTPContext *s = h->priv_data;
757 av_dlog(h, "ftp protocol shutdown\n");
760 return ffurl_shutdown(s->conn_data, flags);
765 URLProtocol ff_ftp_protocol = {
767 .url_open = ftp_open,
768 .url_read = ftp_read,
769 .url_write = ftp_write,
770 .url_seek = ftp_seek,
771 .url_close = ftp_close,
772 .url_get_file_handle = ftp_get_file_handle,
773 .url_shutdown = ftp_shutdown,
774 .priv_data_size = sizeof(FTPContext),
775 .priv_data_class = &ftp_context_class,
776 .flags = URL_PROTOCOL_FLAG_NETWORK,