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
24 #include "libavutil/avstring.h"
25 #include "libavutil/internal.h"
26 #include "libavutil/mem.h"
27 #include "libavutil/parseutils.h"
31 #include "urldecode.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/bprint.h"
35 #define CONTROL_BUFFER_SIZE 1024
36 #define DIR_BUFFER_SIZE 4096
56 URLContext
*conn_control
; /**< Control connection */
57 URLContext
*conn_data
; /**< Data connection, NULL when not connected */
58 uint8_t control_buffer
[CONTROL_BUFFER_SIZE
]; /**< Control connection buffer */
59 uint8_t *control_buf_ptr
, *control_buf_end
;
60 int server_data_port
; /**< Data connection port opened by server, -1 on error. */
61 int server_control_port
; /**< Control connection port, default is 21 */
62 char *hostname
; /**< Server address. */
63 char *user
; /**< Server user */
64 char *password
; /**< Server user's password */
65 char *path
; /**< Path to resource on server. */
66 int64_t filesize
; /**< Size of file on server, -1 on error. */
67 int64_t position
; /**< Current position, calculated. */
68 int rw_timeout
; /**< Network timeout. */
69 const char *anonymous_password
; /**< Password to be used for anonymous user. An email should be used. */
70 int write_seekable
; /**< Control seekability, 0 = disable, 1 = enable. */
71 FTPState state
; /**< State of data connection */
72 FTPListingMethod listing_method
; /**< Called listing method */
73 char *features
; /**< List of server's features represented as raw response */
75 size_t dir_buffer_size
;
76 size_t dir_buffer_offset
;
78 const char *option_user
; /**< User to be used if none given in the URL */
79 const char *option_password
; /**< Password to be used if none given in the URL */
82 #define OFFSET(x) offsetof(FTPContext, x)
83 #define D AV_OPT_FLAG_DECODING_PARAM
84 #define E AV_OPT_FLAG_ENCODING_PARAM
85 static const AVOption options
[] = {
86 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout
), AV_OPT_TYPE_INT
, {.i64
= -1}, -1, INT_MAX
, D
|E
},
87 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
88 {"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
},
89 {"ftp-user", "user for FTP login. Overridden by whatever is in the URL.", OFFSET(option_user
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, D
|E
},
90 {"ftp-password", "password for FTP login. Overridden by whatever is in the URL.", OFFSET(option_password
), AV_OPT_TYPE_STRING
, { 0 }, 0, 0, D
|E
},
94 static const AVClass ftp_context_class
= {
96 .item_name
= av_default_item_name
,
98 .version
= LIBAVUTIL_VERSION_INT
,
101 static int ftp_close(URLContext
*h
);
103 static int ftp_getc(FTPContext
*s
)
106 if (s
->control_buf_ptr
>= s
->control_buf_end
) {
107 len
= ffurl_read(s
->conn_control
, s
->control_buffer
, CONTROL_BUFFER_SIZE
);
113 s
->control_buf_ptr
= s
->control_buffer
;
114 s
->control_buf_end
= s
->control_buffer
+ len
;
117 return *s
->control_buf_ptr
++;
120 static int ftp_get_line(FTPContext
*s
, char *line
, int line_size
)
132 if (q
> line
&& q
[-1] == '\r')
137 if ((q
- line
) < line_size
- 1)
144 * This routine returns ftp server response code.
145 * Server may send more than one response for a certain command.
146 * First expected code is returned.
148 static int ftp_status(FTPContext
*s
, char **line
, const int response_codes
[])
150 int err
, i
, dash
= 0, result
= 0, code_found
= 0, linesize
;
151 char buf
[CONTROL_BUFFER_SIZE
];
152 AVBPrint line_buffer
;
155 av_bprint_init(&line_buffer
, 0, AV_BPRINT_SIZE_AUTOMATIC
);
157 while (!code_found
|| dash
) {
158 if ((err
= ftp_get_line(s
, buf
, sizeof(buf
))) < 0) {
160 av_bprint_finalize(&line_buffer
, NULL
);
164 av_log(s
, AV_LOG_DEBUG
, "%s\n", buf
);
166 linesize
= strlen(buf
);
169 for (i
= 0; i
< 3; ++i
) {
170 if (buf
[i
] < '0' || buf
[i
] > '9') {
184 for (i
= 0; response_codes
[i
]; ++i
) {
185 if (err
== response_codes
[i
]) {
194 av_bprintf(&line_buffer
, "%s\r\n", buf
);
196 if (!dash
&& buf
[3] == '-')
198 else if (err
== dash
&& buf
[3] == ' ')
205 av_bprint_finalize(&line_buffer
, line
);
209 static int ftp_send_command(FTPContext
*s
, const char *command
,
210 const int response_codes
[], char **response
)
214 ff_dlog(s
, "%s", command
);
219 if (!s
->conn_control
)
222 if ((err
= ffurl_write(s
->conn_control
, command
, strlen(command
))) < 0)
228 if (response_codes
) {
229 return ftp_status(s
, response
, response_codes
);
234 static void ftp_close_data_connection(FTPContext
*s
)
236 ffurl_closep(&s
->conn_data
);
237 s
->state
= DISCONNECTED
;
240 static void ftp_close_both_connections(FTPContext
*s
)
242 ffurl_closep(&s
->conn_control
);
243 ftp_close_data_connection(s
);
246 static int ftp_auth(FTPContext
*s
)
248 char buf
[CONTROL_BUFFER_SIZE
];
250 static const int user_codes
[] = {331, 230, 0};
251 static const int pass_codes
[] = {230, 0};
253 if (strpbrk(s
->user
, "\r\n"))
254 return AVERROR(EINVAL
);
255 err
= snprintf(buf
, sizeof(buf
), "USER %s\r\n", s
->user
);
256 if (err
>= sizeof(buf
))
257 return AVERROR(ENOSYS
);
259 err
= ftp_send_command(s
, buf
, user_codes
, NULL
);
262 if (strpbrk(s
->password
, "\r\n"))
263 return AVERROR(EINVAL
);
264 err
= snprintf(buf
, sizeof(buf
), "PASS %s\r\n", s
->password
);
265 if (err
>= sizeof(buf
))
266 return AVERROR(ENOSYS
);
268 err
= ftp_send_command(s
, buf
, pass_codes
, NULL
);
270 return AVERROR(EACCES
);
273 return AVERROR(EACCES
);
278 static int ftp_passive_mode_epsv(FTPContext
*s
)
280 char *res
= NULL
, *start
= NULL
, *end
= NULL
;
282 static const char d
= '|';
283 static const char *command
= "EPSV\r\n";
284 static const int epsv_codes
[] = {229, 0};
286 if (ftp_send_command(s
, command
, epsv_codes
, &res
) != 229 || !res
)
289 for (i
= 0; res
[i
]; ++i
) {
292 } else if (res
[i
] == ')') {
301 if (strlen(start
) < 5)
303 if (start
[0] != d
|| start
[1] != d
|| start
[2] != d
|| end
[-1] != d
)
308 s
->server_data_port
= atoi(start
);
309 ff_dlog(s
, "Server data port: %d\n", s
->server_data_port
);
316 s
->server_data_port
= -1;
317 return AVERROR(ENOSYS
);
320 static int ftp_passive_mode(FTPContext
*s
)
322 char *res
= NULL
, *start
= NULL
, *end
= NULL
;
324 static const char *command
= "PASV\r\n";
325 static const int pasv_codes
[] = {227, 0};
327 if (ftp_send_command(s
, command
, pasv_codes
, &res
) != 227 || !res
)
330 for (i
= 0; res
[i
]; ++i
) {
333 } else if (res
[i
] == ')') {
343 if (!av_strtok(start
, ",", &end
)) goto fail
;
344 if (!av_strtok(NULL
, ",", &end
)) goto fail
;
345 if (!av_strtok(NULL
, ",", &end
)) goto fail
;
346 if (!av_strtok(NULL
, ",", &end
)) goto fail
;
348 /* parse port number */
349 start
= av_strtok(NULL
, ",", &end
);
350 if (!start
) goto fail
;
351 s
->server_data_port
= atoi(start
) * 256;
352 start
= av_strtok(NULL
, ",", &end
);
353 if (!start
) goto fail
;
354 s
->server_data_port
+= atoi(start
);
355 ff_dlog(s
, "Server data port: %d\n", s
->server_data_port
);
362 s
->server_data_port
= -1;
366 static int ftp_current_dir(FTPContext
*s
)
368 char *res
= NULL
, *start
= NULL
, *end
= NULL
;
370 static const char *command
= "PWD\r\n";
371 static const int pwd_codes
[] = {257, 0};
373 if (ftp_send_command(s
, command
, pwd_codes
, &res
) != 257 || !res
)
376 for (i
= 0; res
[i
]; ++i
) {
391 s
->path
= av_strdup(start
);
396 return AVERROR(ENOMEM
);
404 static int ftp_file_size(FTPContext
*s
)
406 char command
[CONTROL_BUFFER_SIZE
];
409 static const int size_codes
[] = {213, 0};
411 ret
= snprintf(command
, sizeof(command
), "SIZE %s\r\n", s
->path
);
412 if (ret
>= sizeof(command
))
413 return AVERROR(ENOSYS
);
415 if (ftp_send_command(s
, command
, size_codes
, &res
) == 213 && res
&& strlen(res
) > 4) {
416 s
->filesize
= strtoll(&res
[4], NULL
, 10);
427 static int ftp_retrieve(FTPContext
*s
)
429 char command
[CONTROL_BUFFER_SIZE
];
430 static const int retr_codes
[] = {150, 125, 0};
433 ret
= snprintf(command
, sizeof(command
), "RETR %s\r\n", s
->path
);
434 if (ret
>= sizeof(command
))
435 return AVERROR(ENOSYS
);
437 resp_code
= ftp_send_command(s
, command
, retr_codes
, NULL
);
438 if (resp_code
!= 125 && resp_code
!= 150)
441 s
->state
= DOWNLOADING
;
446 static int ftp_store(FTPContext
*s
)
448 char command
[CONTROL_BUFFER_SIZE
];
449 static const int stor_codes
[] = {150, 125, 0};
452 ret
= snprintf(command
, sizeof(command
), "STOR %s\r\n", s
->path
);
453 if (ret
>= sizeof(command
))
454 return AVERROR(ENOSYS
);
456 resp_code
= ftp_send_command(s
, command
, stor_codes
, NULL
);
457 if (resp_code
!= 125 && resp_code
!= 150)
460 s
->state
= UPLOADING
;
465 static int ftp_type(FTPContext
*s
)
467 static const char *command
= "TYPE I\r\n";
468 static const int type_codes
[] = {200, 0};
470 if (ftp_send_command(s
, command
, type_codes
, NULL
) != 200)
476 static int ftp_restart(FTPContext
*s
, int64_t pos
)
478 char command
[CONTROL_BUFFER_SIZE
];
479 static const int rest_codes
[] = {350, 0};
481 snprintf(command
, sizeof(command
), "REST %"PRId64
"\r\n", pos
);
482 if (ftp_send_command(s
, command
, rest_codes
, NULL
) != 350)
488 static int ftp_set_dir(FTPContext
*s
)
490 static const int cwd_codes
[] = {250, 550, 0}; /* 550 is incorrect code */
491 char command
[MAX_URL_SIZE
];
494 ret
= snprintf(command
, sizeof(command
), "CWD %s\r\n", s
->path
);
495 if (ret
>= sizeof(command
))
496 return AVERROR(ENOSYS
);
498 if (ftp_send_command(s
, command
, cwd_codes
, NULL
) != 250)
503 static int ftp_list_mlsd(FTPContext
*s
)
505 static const char *command
= "MLSD\r\n";
506 static const int mlsd_codes
[] = {150, 500, 0}; /* 500 is incorrect code */
508 if (ftp_send_command(s
, command
, mlsd_codes
, NULL
) != 150)
509 return AVERROR(ENOSYS
);
510 s
->listing_method
= MLSD
;
514 static int ftp_list_nlst(FTPContext
*s
)
516 static const char *command
= "NLST\r\n";
517 static const int nlst_codes
[] = {226, 425, 426, 451, 450, 550, 0};
519 if (ftp_send_command(s
, command
, nlst_codes
, NULL
) != 226)
520 return AVERROR(ENOSYS
);
521 s
->listing_method
= NLST
;
525 static int ftp_list(FTPContext
*s
)
528 s
->state
= LISTING_DIR
;
530 if ((ret
= ftp_list_mlsd(s
)) < 0)
531 ret
= ftp_list_nlst(s
);
536 static int ftp_has_feature(FTPContext
*s
, const char *feature_name
)
541 return av_stristr(s
->features
, feature_name
) != NULL
;
544 static int ftp_features(FTPContext
*s
)
546 static const char *feat_command
= "FEAT\r\n";
547 static const char *enable_utf8_command
= "OPTS UTF8 ON\r\n";
548 static const int feat_codes
[] = {211, 0};
549 static const int opts_codes
[] = {200, 202, 451, 0};
551 av_freep(&s
->features
);
552 if (ftp_send_command(s
, feat_command
, feat_codes
, &s
->features
) != 211) {
553 av_freep(&s
->features
);
556 if (ftp_has_feature(s
, "UTF8")) {
557 int ret
= ftp_send_command(s
, enable_utf8_command
, opts_codes
, NULL
);
558 if (ret
== 200 || ret
== 202)
565 static int ftp_connect_control_connection(URLContext
*h
)
567 char buf
[CONTROL_BUFFER_SIZE
], *response
= NULL
;
569 AVDictionary
*opts
= NULL
;
570 FTPContext
*s
= h
->priv_data
;
571 static const int connect_codes
[] = {220, 0};
573 if (!s
->conn_control
) {
574 ff_url_join(buf
, sizeof(buf
), "tcp", NULL
,
575 s
->hostname
, s
->server_control_port
, NULL
);
576 if (s
->rw_timeout
!= -1) {
577 av_dict_set_int(&opts
, "timeout", s
->rw_timeout
, 0);
578 } /* if option is not given, don't pass it and let tcp use its own default */
579 err
= ffurl_open_whitelist(&s
->conn_control
, buf
, AVIO_FLAG_READ_WRITE
,
580 &h
->interrupt_callback
, &opts
,
581 h
->protocol_whitelist
, h
->protocol_blacklist
, h
);
584 av_log(h
, AV_LOG_ERROR
, "Cannot open control connection\n");
588 /* check if server is ready */
589 if (ftp_status(s
, ((h
->flags
& AVIO_FLAG_WRITE
) ? &response
: NULL
), connect_codes
) != 220) {
590 av_log(h
, AV_LOG_ERROR
, "FTP server not ready for new users\n");
591 return AVERROR(EACCES
);
594 if ((h
->flags
& AVIO_FLAG_WRITE
) && av_stristr(response
, "pure-ftpd")) {
595 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.");
599 if ((err
= ftp_auth(s
)) < 0) {
600 av_log(h
, AV_LOG_ERROR
, "FTP authentication failed\n");
604 if ((err
= ftp_type(s
)) < 0) {
605 av_log(h
, AV_LOG_ERROR
, "Set content type failed\n");
614 static int ftp_connect_data_connection(URLContext
*h
)
617 char buf
[CONTROL_BUFFER_SIZE
];
618 AVDictionary
*opts
= NULL
;
619 FTPContext
*s
= h
->priv_data
;
622 /* Enter passive mode */
623 if (ftp_passive_mode_epsv(s
) < 0) {
624 /* Use PASV as fallback */
625 if ((err
= ftp_passive_mode(s
)) < 0)
628 /* Open data connection */
629 ff_url_join(buf
, sizeof(buf
), "tcp", NULL
, s
->hostname
, s
->server_data_port
, NULL
);
630 if (s
->rw_timeout
!= -1) {
631 av_dict_set_int(&opts
, "timeout", s
->rw_timeout
, 0);
632 } /* if option is not given, don't pass it and let tcp use its own default */
633 err
= ffurl_open_whitelist(&s
->conn_data
, buf
, h
->flags
,
634 &h
->interrupt_callback
, &opts
,
635 h
->protocol_whitelist
, h
->protocol_blacklist
, h
);
641 if ((err
= ftp_restart(s
, s
->position
)) < 0)
648 static int ftp_abort(URLContext
*h
)
650 static const char *command
= "ABOR\r\n";
652 static const int abor_codes
[] = {225, 226, 0};
653 FTPContext
*s
= h
->priv_data
;
655 /* According to RCF 959:
656 "ABOR command tells the server to abort the previous FTP
657 service command and any associated transfer of data."
659 There are FTP server implementations that don't response
660 to any commands during data transfer in passive mode (including ABOR).
662 This implementation closes data connection by force.
665 if (ftp_send_command(s
, command
, NULL
, NULL
) < 0) {
666 ftp_close_both_connections(s
);
667 if ((err
= ftp_connect_control_connection(h
)) < 0) {
668 av_log(h
, AV_LOG_ERROR
, "Reconnect failed.\n");
672 ftp_close_data_connection(s
);
673 if (ftp_status(s
, NULL
, abor_codes
) < 225) {
674 /* wu-ftpd also closes control connection after data connection closing */
675 ffurl_closep(&s
->conn_control
);
676 if ((err
= ftp_connect_control_connection(h
)) < 0) {
677 av_log(h
, AV_LOG_ERROR
, "Reconnect failed.\n");
686 static int ftp_connect(URLContext
*h
, const char *url
)
688 char proto
[10], path
[MAX_URL_SIZE
], credentials
[MAX_URL_SIZE
], hostname
[MAX_URL_SIZE
];
689 const char *tok_user
= NULL
, *tok_pass
= NULL
;
690 char *newpath
= NULL
;
692 FTPContext
*s
= h
->priv_data
;
694 s
->state
= DISCONNECTED
;
695 s
->listing_method
= UNKNOWN_METHOD
;
700 av_url_split(proto
, sizeof(proto
),
701 credentials
, sizeof(credentials
),
702 hostname
, sizeof(hostname
),
703 &s
->server_control_port
,
708 if (!s
->option_user
) {
709 tok_user
= "anonymous";
710 tok_pass
= av_x_if_null(s
->anonymous_password
, "nopassword");
712 tok_user
= s
->option_user
;
713 tok_pass
= s
->option_password
;
715 s
->user
= av_strdup(tok_user
);
716 s
->password
= av_strdup(tok_pass
);
718 char *pass
= strchr(credentials
, ':');
722 s
->password
= ff_urldecode(pass
, 0);
724 tok_pass
= s
->option_password
;
725 s
->password
= av_strdup(tok_pass
);
727 s
->user
= ff_urldecode(credentials
, 0);
729 s
->hostname
= av_strdup(hostname
);
730 if (!s
->hostname
|| !s
->user
|| (tok_pass
&& !s
->password
)) {
731 return AVERROR(ENOMEM
);
734 if (s
->server_control_port
< 0 || s
->server_control_port
> 65535)
735 s
->server_control_port
= 21;
737 if ((err
= ftp_connect_control_connection(h
)) < 0)
740 if ((err
= ftp_current_dir(s
)) < 0)
743 newpath
= av_append_path_component(s
->path
, path
);
745 return AVERROR(ENOMEM
);
752 static int ftp_open(URLContext
*h
, const char *url
, int flags
)
754 FTPContext
*s
= h
->priv_data
;
757 ff_dlog(h
, "ftp protocol open\n");
759 if ((err
= ftp_connect(h
, url
)) < 0)
762 if (ftp_restart(s
, 0) < 0) {
766 if (s
->write_seekable
!= 1 && flags
& AVIO_FLAG_WRITE
)
773 av_log(h
, AV_LOG_ERROR
, "FTP open failed\n");
778 static int64_t ftp_seek(URLContext
*h
, int64_t pos
, int whence
)
780 FTPContext
*s
= h
->priv_data
;
784 ff_dlog(h
, "ftp protocol seek %"PRId64
" %d\n", pos
, whence
);
793 new_pos
= s
->position
+ pos
;
798 new_pos
= s
->filesize
+ pos
;
801 return AVERROR(EINVAL
);
808 av_log(h
, AV_LOG_ERROR
, "Seeking to negative position.\n");
809 return AVERROR(EINVAL
);
812 if (new_pos
!= s
->position
) {
813 if ((err
= ftp_abort(h
)) < 0)
815 s
->position
= new_pos
;
820 static int ftp_read(URLContext
*h
, unsigned char *buf
, int size
)
822 FTPContext
*s
= h
->priv_data
;
823 int read
, err
, retry_done
= 0;
825 ff_dlog(h
, "ftp protocol read %d bytes\n", size
);
827 if (s
->state
== ENDOFFILE
)
829 if (s
->state
== DISCONNECTED
) {
830 if ((err
= ftp_connect_data_connection(h
)) < 0)
833 if (s
->state
== READY
) {
834 if ((err
= ftp_retrieve(s
)) < 0)
837 if (s
->conn_data
&& s
->state
== DOWNLOADING
) {
838 read
= ffurl_read(s
->conn_data
, buf
, size
);
841 s
->filesize
= FFMAX(s
->filesize
, s
->position
);
843 if (read
== AVERROR_EOF
) {
844 static const int retr_codes
[] = {226, 250, 425, 426, 451, 0};
845 char *response
= NULL
;
846 err
= ftp_status(s
, &response
, retr_codes
);
848 ftp_close_data_connection(s
);
850 s
->state
= ENDOFFILE
;
853 /* 250 is not allowed, any other status means some kind of error */
854 av_log(h
, AV_LOG_ERROR
, "FTP transfer failed: %s\n", response
? response
: (err
< 0 ? av_err2str(err
) : "?"));
858 if (read
<= 0 && !h
->is_streamed
) {
859 /* Server closed connection. Probably due to inactivity */
860 av_log(h
, AV_LOG_INFO
, "Reconnect to FTP server.\n");
861 if ((err
= ftp_abort(h
)) < 0)
871 av_log(h
, AV_LOG_DEBUG
, "FTP read failed\n");
875 static int ftp_write(URLContext
*h
, const unsigned char *buf
, int size
)
878 FTPContext
*s
= h
->priv_data
;
881 ff_dlog(h
, "ftp protocol write %d bytes\n", size
);
883 if (s
->state
== DISCONNECTED
) {
884 if ((err
= ftp_connect_data_connection(h
)) < 0)
887 if (s
->state
== READY
) {
888 if ((err
= ftp_store(s
)) < 0)
891 if (s
->conn_data
&& s
->state
== UPLOADING
) {
892 written
= ffurl_write(s
->conn_data
, buf
, size
);
894 s
->position
+= written
;
895 s
->filesize
= FFMAX(s
->filesize
, s
->position
);
900 av_log(h
, AV_LOG_ERROR
, "FTP write failed\n");
904 static int ftp_close(URLContext
*h
)
906 FTPContext
*s
= h
->priv_data
;
908 ff_dlog(h
, "ftp protocol close\n");
910 ftp_close_both_connections(s
);
912 av_freep(&s
->password
);
913 av_freep(&s
->hostname
);
915 av_freep(&s
->features
);
920 static int ftp_get_file_handle(URLContext
*h
)
922 FTPContext
*s
= h
->priv_data
;
924 ff_dlog(h
, "ftp protocol get_file_handle\n");
927 return ffurl_get_file_handle(s
->conn_data
);
932 static int ftp_shutdown(URLContext
*h
, int flags
)
934 FTPContext
*s
= h
->priv_data
;
936 ff_dlog(h
, "ftp protocol shutdown\n");
939 return ffurl_shutdown(s
->conn_data
, flags
);
944 static int ftp_open_dir(URLContext
*h
)
946 FTPContext
*s
= h
->priv_data
;
949 if ((ret
= ftp_connect(h
, h
->filename
)) < 0)
951 if ((ret
= ftp_set_dir(s
)) < 0)
953 if ((ret
= ftp_connect_data_connection(h
)) < 0)
955 if ((ret
= ftp_list(s
)) < 0)
957 s
->dir_buffer
= av_malloc(DIR_BUFFER_SIZE
);
958 if (!s
->dir_buffer
) {
959 ret
= AVERROR(ENOMEM
);
962 s
->dir_buffer
[0] = 0;
963 if (s
->conn_data
&& s
->state
== LISTING_DIR
)
966 ffurl_closep(&s
->conn_control
);
967 ffurl_closep(&s
->conn_data
);
971 static int64_t ftp_parse_date(const char *date
)
974 memset(&tv
, 0, sizeof(struct tm
));
975 av_small_strptime(date
, "%Y%m%d%H%M%S", &tv
);
976 return INT64_C(1000000) * av_timegm(&tv
);
979 static int ftp_parse_entry_nlst(char *line
, AVIODirEntry
*next
)
981 next
->name
= av_strdup(line
);
985 static int ftp_parse_entry_mlsd(char *mlsd
, AVIODirEntry
*next
)
988 char *saveptr
= NULL
, *p
= mlsd
;
989 ff_dlog(NULL
, "%s\n", mlsd
);
990 while(fact
= av_strtok(p
, ";", &saveptr
)) {
992 if (fact
[0] == ' ') {
993 next
->name
= av_strdup(&fact
[1]);
996 fact
= av_strtok(fact
, "=", &value
);
999 if (!av_strcasecmp(fact
, "type")) {
1000 if (!av_strcasecmp(value
, "cdir") || !av_strcasecmp(value
, "pdir"))
1002 if (!av_strcasecmp(value
, "dir"))
1003 next
->type
= AVIO_ENTRY_DIRECTORY
;
1004 else if (!av_strcasecmp(value
, "file"))
1005 next
->type
= AVIO_ENTRY_FILE
;
1006 else if (!av_strcasecmp(value
, "OS.unix=slink:"))
1007 next
->type
= AVIO_ENTRY_SYMBOLIC_LINK
;
1008 } else if (!av_strcasecmp(fact
, "modify")) {
1009 next
->modification_timestamp
= ftp_parse_date(value
);
1010 } else if (!av_strcasecmp(fact
, "UNIX.mode")) {
1011 next
->filemode
= strtoumax(value
, NULL
, 8);
1012 } else if (!av_strcasecmp(fact
, "UNIX.uid") || !av_strcasecmp(fact
, "UNIX.owner"))
1013 next
->user_id
= strtoumax(value
, NULL
, 10);
1014 else if (!av_strcasecmp(fact
, "UNIX.gid") || !av_strcasecmp(fact
, "UNIX.group"))
1015 next
->group_id
= strtoumax(value
, NULL
, 10);
1016 else if (!av_strcasecmp(fact
, "size") || !av_strcasecmp(fact
, "sizd"))
1017 next
->size
= strtoll(value
, NULL
, 10);
1023 * @return 0 on success, negative on error, positive on entry to discard.
1025 static int ftp_parse_entry(URLContext
*h
, char *line
, AVIODirEntry
*next
)
1027 FTPContext
*s
= h
->priv_data
;
1029 switch (s
->listing_method
) {
1031 return ftp_parse_entry_mlsd(line
, next
);
1033 return ftp_parse_entry_nlst(line
, next
);
1034 case UNKNOWN_METHOD
:
1040 static int ftp_read_dir(URLContext
*h
, AVIODirEntry
**next
)
1042 FTPContext
*s
= h
->priv_data
;
1043 char *start
, *found
;
1048 start
= s
->dir_buffer
+ s
->dir_buffer_offset
;
1049 while (!(found
= strstr(start
, "\n"))) {
1051 return AVERROR(EIO
);
1052 s
->dir_buffer_size
-= s
->dir_buffer_offset
;
1053 s
->dir_buffer_offset
= 0;
1054 if (s
->dir_buffer_size
)
1055 memmove(s
->dir_buffer
, start
, s
->dir_buffer_size
);
1056 ret
= ffurl_read(s
->conn_data
, s
->dir_buffer
+ s
->dir_buffer_size
, DIR_BUFFER_SIZE
- (s
->dir_buffer_size
+ 1));
1063 s
->dir_buffer_size
+= ret
;
1064 s
->dir_buffer
[s
->dir_buffer_size
] = 0;
1065 start
= s
->dir_buffer
;
1068 s
->dir_buffer_offset
+= (found
+ 1 - start
);
1070 if (found
> start
&& found
[-1] == '\r')
1073 *next
= ff_alloc_dir_entry();
1075 return AVERROR(ENOMEM
);
1076 (*next
)->utf8
= s
->utf8
;
1077 ret
= ftp_parse_entry(h
, start
, *next
);
1079 avio_free_directory_entry(next
);
1087 static int ftp_close_dir(URLContext
*h
)
1089 FTPContext
*s
= h
->priv_data
;
1090 av_freep(&s
->dir_buffer
);
1091 ffurl_closep(&s
->conn_control
);
1092 ffurl_closep(&s
->conn_data
);
1096 static int ftp_delete(URLContext
*h
)
1098 FTPContext
*s
= h
->priv_data
;
1099 char command
[MAX_URL_SIZE
];
1100 static const int del_codes
[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1101 static const int rmd_codes
[] = {250, 421, 500, 501, 502, 530, 550, 0};
1104 if ((ret
= ftp_connect(h
, h
->filename
)) < 0)
1107 ret
= snprintf(command
, sizeof(command
), "DELE %s\r\n", s
->path
);
1108 if (ret
>= sizeof(command
)) {
1109 ret
= AVERROR(ENOSYS
);
1113 if (ftp_send_command(s
, command
, del_codes
, NULL
) == 250) {
1118 ret
= snprintf(command
, sizeof(command
), "RMD %s\r\n", s
->path
);
1119 if (ret
>= sizeof(command
)) {
1120 ret
= AVERROR(ENOSYS
);
1124 if (ftp_send_command(s
, command
, rmd_codes
, NULL
) == 250)
1134 static int ftp_move(URLContext
*h_src
, URLContext
*h_dst
)
1136 FTPContext
*s
= h_src
->priv_data
;
1137 char command
[MAX_URL_SIZE
], path
[MAX_URL_SIZE
];
1138 static const int rnfr_codes
[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1139 static const int rnto_codes
[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1142 if ((ret
= ftp_connect(h_src
, h_src
->filename
)) < 0)
1145 ret
= snprintf(command
, sizeof(command
), "RNFR %s\r\n", s
->path
);
1146 if (ret
>= sizeof(command
)) {
1147 ret
= AVERROR(ENOSYS
);
1151 if (ftp_send_command(s
, command
, rnfr_codes
, NULL
) != 350) {
1156 av_url_split(0, 0, 0, 0, 0, 0, 0,
1159 ret
= snprintf(command
, sizeof(command
), "RNTO %s\r\n", path
);
1160 if (ret
>= sizeof(command
)) {
1161 ret
= AVERROR(ENOSYS
);
1165 if (ftp_send_command(s
, command
, rnto_codes
, NULL
) == 250)
1175 const URLProtocol ff_ftp_protocol
= {
1177 .url_open
= ftp_open
,
1178 .url_read
= ftp_read
,
1179 .url_write
= ftp_write
,
1180 .url_seek
= ftp_seek
,
1181 .url_close
= ftp_close
,
1182 .url_get_file_handle
= ftp_get_file_handle
,
1183 .url_shutdown
= ftp_shutdown
,
1184 .priv_data_size
= sizeof(FTPContext
),
1185 .priv_data_class
= &ftp_context_class
,
1186 .url_open_dir
= ftp_open_dir
,
1187 .url_read_dir
= ftp_read_dir
,
1188 .url_close_dir
= ftp_close_dir
,
1189 .url_delete
= ftp_delete
,
1190 .url_move
= ftp_move
,
1191 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
1192 .default_whitelist
= "tcp",