1 /*
2 * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
3 *
4 * This file is part of FFmpeg.
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <string.h>
22
23 #include "libavutil/avstring.h"
24 #include "libavutil/internal.h"
25 #include "libavutil/parseutils.h"
26 #include "avformat.h"
27 #include "internal.h"
28 #include "url.h"
29 #include "urldecode.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/bprint.h"
32
33 #define CONTROL_BUFFER_SIZE 1024
34 #define DIR_BUFFER_SIZE 4096
35
36 typedef enum {
37 UNKNOWN,
38 READY,
39 DOWNLOADING,
40 UPLOADING,
41 LISTING_DIR,
42 DISCONNECTED,
43 ENDOFFILE,
44 } FTPState;
45
46 typedef enum {
47 UNKNOWN_METHOD,
48 NLST,
49 MLSD
50 } FTPListingMethod;
51
52 typedef struct {
53 const AVClass *class;
54 URLContext *conn_control; /**< Control connection */
55 URLContext *conn_data; /**< Data connection, NULL when not connected */
56 uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
57 uint8_t *control_buf_ptr, *control_buf_end;
58 int server_data_port; /**< Data connection port opened by server, -1 on error. */
59 int server_control_port; /**< Control connection port, default is 21 */
60 char *hostname; /**< Server address. */
61 char *user; /**< Server user */
62 char *password; /**< Server user's password */
63 char *path; /**< Path to resource on server. */
64 int64_t filesize; /**< Size of file on server, -1 on error. */
65 int64_t position; /**< Current position, calculated. */
66 int rw_timeout; /**< Network timeout. */
67 const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
68 int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
69 FTPState state; /**< State of data connection */
70 FTPListingMethod listing_method; /**< Called listing method */
71 char *features; /**< List of server's features represented as raw response */
72 char *dir_buffer;
73 size_t dir_buffer_size;
74 size_t dir_buffer_offset;
75 int utf8;
76 const char *option_user; /**< User to be used if none given in the URL */
77 const char *option_password; /**< Password to be used if none given in the URL */
78 } FTPContext;
79
80 #define OFFSET(x) offsetof(FTPContext, x)
81 #define D AV_OPT_FLAG_DECODING_PARAM
82 #define E AV_OPT_FLAG_ENCODING_PARAM
83 static const AVOption options[] = {
84 {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
85 {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
86 {"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 },
87 {"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 },
88 {"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 },
89 {NULL}
90 };
91
92 static const AVClass ftp_context_class = {
93 .class_name = "ftp",
94 .item_name = av_default_item_name,
95 .option = options,
96 .version = LIBAVUTIL_VERSION_INT,
97 };
98
99 static int ftp_close(URLContext *h);
100
ftp_getc(FTPContext * s)101 static int ftp_getc(FTPContext *s)
102 {
103 int len;
104 if (s->control_buf_ptr >= s->control_buf_end) {
105 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
106 if (len < 0) {
107 return len;
108 } else if (!len) {
109 return -1;
110 } else {
111 s->control_buf_ptr = s->control_buffer;
112 s->control_buf_end = s->control_buffer + len;
113 }
114 }
115 return *s->control_buf_ptr++;
116 }
117
ftp_get_line(FTPContext * s,char * line,int line_size)118 static int ftp_get_line(FTPContext *s, char *line, int line_size)
119 {
120 int ch;
121 char *q = line;
122
123 for (;;) {
124 ch = ftp_getc(s);
125 if (ch < 0) {
126 return ch;
127 }
128 if (ch == '\n') {
129 /* process line */
130 if (q > line && q[-1] == '\r')
131 q--;
132 *q = '\0';
133 return 0;
134 } else {
135 if ((q - line) < line_size - 1)
136 *q++ = ch;
137 }
138 }
139 }
140
141 /*
142 * This routine returns ftp server response code.
143 * Server may send more than one response for a certain command.
144 * First expected code is returned.
145 */
ftp_status(FTPContext * s,char ** line,const int response_codes[])146 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
147 {
148 int err, i, dash = 0, result = 0, code_found = 0, linesize;
149 char buf[CONTROL_BUFFER_SIZE];
150 AVBPrint line_buffer;
151
152 if (line)
153 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
154
155 while (!code_found || dash) {
156 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
157 if (line)
158 av_bprint_finalize(&line_buffer, NULL);
159 return err;
160 }
161
162 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
163
164 linesize = strlen(buf);
165 err = 0;
166 if (linesize >= 3) {
167 for (i = 0; i < 3; ++i) {
168 if (buf[i] < '0' || buf[i] > '9') {
169 err = 0;
170 break;
171 }
172 err *= 10;
173 err += buf[i] - '0';
174 }
175 }
176
177 if (!code_found) {
178 if (err >= 500) {
179 code_found = 1;
180 result = err;
181 } else
182 for (i = 0; response_codes[i]; ++i) {
183 if (err == response_codes[i]) {
184 code_found = 1;
185 result = err;
186 break;
187 }
188 }
189 }
190 if (code_found) {
191 if (line)
192 av_bprintf(&line_buffer, "%s\r\n", buf);
193 if (linesize >= 4) {
194 if (!dash && buf[3] == '-')
195 dash = err;
196 else if (err == dash && buf[3] == ' ')
197 dash = 0;
198 }
199 }
200 }
201
202 if (line)
203 av_bprint_finalize(&line_buffer, line);
204 return result;
205 }
206
ftp_send_command(FTPContext * s,const char * command,const int response_codes[],char ** response)207 static int ftp_send_command(FTPContext *s, const char *command,
208 const int response_codes[], char **response)
209 {
210 int err;
211
212 ff_dlog(s, "%s", command);
213
214 if (response)
215 *response = NULL;
216
217 if (!s->conn_control)
218 return AVERROR(EIO);
219
220 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
221 return err;
222 if (!err)
223 return -1;
224
225 /* return status */
226 if (response_codes) {
227 return ftp_status(s, response, response_codes);
228 }
229 return 0;
230 }
231
ftp_close_data_connection(FTPContext * s)232 static void ftp_close_data_connection(FTPContext *s)
233 {
234 ffurl_closep(&s->conn_data);
235 s->state = DISCONNECTED;
236 }
237
ftp_close_both_connections(FTPContext * s)238 static void ftp_close_both_connections(FTPContext *s)
239 {
240 ffurl_closep(&s->conn_control);
241 ftp_close_data_connection(s);
242 }
243
ftp_auth(FTPContext * s)244 static int ftp_auth(FTPContext *s)
245 {
246 char buf[CONTROL_BUFFER_SIZE];
247 int err;
248 static const int user_codes[] = {331, 230, 0};
249 static const int pass_codes[] = {230, 0};
250
251 if (strpbrk(s->user, "\r\n"))
252 return AVERROR(EINVAL);
253 snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
254 err = ftp_send_command(s, buf, user_codes, NULL);
255 if (err == 331) {
256 if (s->password) {
257 if (strpbrk(s->password, "\r\n"))
258 return AVERROR(EINVAL);
259 snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
260 err = ftp_send_command(s, buf, pass_codes, NULL);
261 } else
262 return AVERROR(EACCES);
263 }
264 if (err != 230)
265 return AVERROR(EACCES);
266
267 return 0;
268 }
269
ftp_passive_mode_epsv(FTPContext * s)270 static int ftp_passive_mode_epsv(FTPContext *s)
271 {
272 char *res = NULL, *start = NULL, *end = NULL;
273 int i;
274 static const char d = '|';
275 static const char *command = "EPSV\r\n";
276 static const int epsv_codes[] = {229, 0};
277
278 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
279 goto fail;
280
281 for (i = 0; res[i]; ++i) {
282 if (res[i] == '(') {
283 start = res + i + 1;
284 } else if (res[i] == ')') {
285 end = res + i;
286 break;
287 }
288 }
289 if (!start || !end)
290 goto fail;
291
292 *end = '\0';
293 if (strlen(start) < 5)
294 goto fail;
295 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
296 goto fail;
297 start += 3;
298 end[-1] = '\0';
299
300 s->server_data_port = atoi(start);
301 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
302
303 av_free(res);
304 return 0;
305
306 fail:
307 av_free(res);
308 s->server_data_port = -1;
309 return AVERROR(ENOSYS);
310 }
311
ftp_passive_mode(FTPContext * s)312 static int ftp_passive_mode(FTPContext *s)
313 {
314 char *res = NULL, *start = NULL, *end = NULL;
315 int i;
316 static const char *command = "PASV\r\n";
317 static const int pasv_codes[] = {227, 0};
318
319 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
320 goto fail;
321
322 for (i = 0; res[i]; ++i) {
323 if (res[i] == '(') {
324 start = res + i + 1;
325 } else if (res[i] == ')') {
326 end = res + i;
327 break;
328 }
329 }
330 if (!start || !end)
331 goto fail;
332
333 *end = '\0';
334 /* skip ip */
335 if (!av_strtok(start, ",", &end)) goto fail;
336 if (!av_strtok(NULL, ",", &end)) goto fail;
337 if (!av_strtok(NULL, ",", &end)) goto fail;
338 if (!av_strtok(NULL, ",", &end)) goto fail;
339
340 /* parse port number */
341 start = av_strtok(NULL, ",", &end);
342 if (!start) goto fail;
343 s->server_data_port = atoi(start) * 256;
344 start = av_strtok(NULL, ",", &end);
345 if (!start) goto fail;
346 s->server_data_port += atoi(start);
347 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
348
349 av_free(res);
350 return 0;
351
352 fail:
353 av_free(res);
354 s->server_data_port = -1;
355 return AVERROR(EIO);
356 }
357
ftp_current_dir(FTPContext * s)358 static int ftp_current_dir(FTPContext *s)
359 {
360 char *res = NULL, *start = NULL, *end = NULL;
361 int i;
362 static const char *command = "PWD\r\n";
363 static const int pwd_codes[] = {257, 0};
364
365 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
366 goto fail;
367
368 for (i = 0; res[i]; ++i) {
369 if (res[i] == '"') {
370 if (!start) {
371 start = res + i + 1;
372 continue;
373 }
374 end = res + i;
375 break;
376 }
377 }
378
379 if (!end)
380 goto fail;
381
382 *end = '\0';
383 s->path = av_strdup(start);
384
385 av_free(res);
386
387 if (!s->path)
388 return AVERROR(ENOMEM);
389 return 0;
390
391 fail:
392 av_free(res);
393 return AVERROR(EIO);
394 }
395
ftp_file_size(FTPContext * s)396 static int ftp_file_size(FTPContext *s)
397 {
398 char command[CONTROL_BUFFER_SIZE];
399 char *res = NULL;
400 static const int size_codes[] = {213, 0};
401
402 snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
403 if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) {
404 s->filesize = strtoll(&res[4], NULL, 10);
405 } else {
406 s->filesize = -1;
407 av_free(res);
408 return AVERROR(EIO);
409 }
410
411 av_free(res);
412 return 0;
413 }
414
ftp_retrieve(FTPContext * s)415 static int ftp_retrieve(FTPContext *s)
416 {
417 char command[CONTROL_BUFFER_SIZE];
418 static const int retr_codes[] = {150, 125, 0};
419 int resp_code;
420
421 snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
422 resp_code = ftp_send_command(s, command, retr_codes, NULL);
423 if (resp_code != 125 && resp_code != 150)
424 return AVERROR(EIO);
425
426 s->state = DOWNLOADING;
427
428 return 0;
429 }
430
ftp_store(FTPContext * s)431 static int ftp_store(FTPContext *s)
432 {
433 char command[CONTROL_BUFFER_SIZE];
434 static const int stor_codes[] = {150, 125, 0};
435 int resp_code;
436
437 snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
438 resp_code = ftp_send_command(s, command, stor_codes, NULL);
439 if (resp_code != 125 && resp_code != 150)
440 return AVERROR(EIO);
441
442 s->state = UPLOADING;
443
444 return 0;
445 }
446
ftp_type(FTPContext * s)447 static int ftp_type(FTPContext *s)
448 {
449 static const char *command = "TYPE I\r\n";
450 static const int type_codes[] = {200, 0};
451
452 if (ftp_send_command(s, command, type_codes, NULL) != 200)
453 return AVERROR(EIO);
454
455 return 0;
456 }
457
ftp_restart(FTPContext * s,int64_t pos)458 static int ftp_restart(FTPContext *s, int64_t pos)
459 {
460 char command[CONTROL_BUFFER_SIZE];
461 static const int rest_codes[] = {350, 0};
462
463 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
464 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
465 return AVERROR(EIO);
466
467 return 0;
468 }
469
ftp_set_dir(FTPContext * s)470 static int ftp_set_dir(FTPContext *s)
471 {
472 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
473 char command[MAX_URL_SIZE];
474
475 snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
476 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
477 return AVERROR(EIO);
478 return 0;
479 }
480
ftp_list_mlsd(FTPContext * s)481 static int ftp_list_mlsd(FTPContext *s)
482 {
483 static const char *command = "MLSD\r\n";
484 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
485
486 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
487 return AVERROR(ENOSYS);
488 s->listing_method = MLSD;
489 return 0;
490 }
491
ftp_list_nlst(FTPContext * s)492 static int ftp_list_nlst(FTPContext *s)
493 {
494 static const char *command = "NLST\r\n";
495 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
496
497 if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
498 return AVERROR(ENOSYS);
499 s->listing_method = NLST;
500 return 0;
501 }
502
ftp_list(FTPContext * s)503 static int ftp_list(FTPContext *s)
504 {
505 int ret;
506 s->state = LISTING_DIR;
507
508 if ((ret = ftp_list_mlsd(s)) < 0)
509 ret = ftp_list_nlst(s);
510
511 return ret;
512 }
513
ftp_has_feature(FTPContext * s,const char * feature_name)514 static int ftp_has_feature(FTPContext *s, const char *feature_name)
515 {
516 if (!s->features)
517 return 0;
518
519 return av_stristr(s->features, feature_name) != NULL;
520 }
521
ftp_features(FTPContext * s)522 static int ftp_features(FTPContext *s)
523 {
524 static const char *feat_command = "FEAT\r\n";
525 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
526 static const int feat_codes[] = {211, 0};
527 static const int opts_codes[] = {200, 202, 451, 0};
528
529 av_freep(&s->features);
530 if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
531 av_freep(&s->features);
532 }
533
534 if (ftp_has_feature(s, "UTF8")) {
535 int ret = ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
536 if (ret == 200 || ret == 202)
537 s->utf8 = 1;
538 }
539
540 return 0;
541 }
542
ftp_connect_control_connection(URLContext * h)543 static int ftp_connect_control_connection(URLContext *h)
544 {
545 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
546 int err;
547 AVDictionary *opts = NULL;
548 FTPContext *s = h->priv_data;
549 static const int connect_codes[] = {220, 0};
550
551 if (!s->conn_control) {
552 ff_url_join(buf, sizeof(buf), "tcp", NULL,
553 s->hostname, s->server_control_port, NULL);
554 if (s->rw_timeout != -1) {
555 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
556 } /* if option is not given, don't pass it and let tcp use its own default */
557 err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
558 &h->interrupt_callback, &opts,
559 h->protocol_whitelist, h->protocol_blacklist, h);
560 av_dict_free(&opts);
561 if (err < 0) {
562 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
563 return err;
564 }
565
566 /* check if server is ready */
567 if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
568 av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
569 return AVERROR(EACCES);
570 }
571
572 if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
573 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.");
574 }
575 av_free(response);
576
577 if ((err = ftp_auth(s)) < 0) {
578 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
579 return err;
580 }
581
582 if ((err = ftp_type(s)) < 0) {
583 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
584 return err;
585 }
586
587 ftp_features(s);
588 }
589 return 0;
590 }
591
ftp_connect_data_connection(URLContext * h)592 static int ftp_connect_data_connection(URLContext *h)
593 {
594 int err;
595 char buf[CONTROL_BUFFER_SIZE];
596 AVDictionary *opts = NULL;
597 FTPContext *s = h->priv_data;
598
599 if (!s->conn_data) {
600 /* Enter passive mode */
601 if (ftp_passive_mode_epsv(s) < 0) {
602 /* Use PASV as fallback */
603 if ((err = ftp_passive_mode(s)) < 0)
604 return err;
605 }
606 /* Open data connection */
607 ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
608 if (s->rw_timeout != -1) {
609 av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
610 } /* if option is not given, don't pass it and let tcp use its own default */
611 err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
612 &h->interrupt_callback, &opts,
613 h->protocol_whitelist, h->protocol_blacklist, h);
614 av_dict_free(&opts);
615 if (err < 0)
616 return err;
617
618 if (s->position)
619 if ((err = ftp_restart(s, s->position)) < 0)
620 return err;
621 }
622 s->state = READY;
623 return 0;
624 }
625
ftp_abort(URLContext * h)626 static int ftp_abort(URLContext *h)
627 {
628 static const char *command = "ABOR\r\n";
629 int err;
630 static const int abor_codes[] = {225, 226, 0};
631 FTPContext *s = h->priv_data;
632
633 /* According to RCF 959:
634 "ABOR command tells the server to abort the previous FTP
635 service command and any associated transfer of data."
636
637 There are FTP server implementations that don't response
638 to any commands during data transfer in passive mode (including ABOR).
639
640 This implementation closes data connection by force.
641 */
642
643 if (ftp_send_command(s, command, NULL, NULL) < 0) {
644 ftp_close_both_connections(s);
645 if ((err = ftp_connect_control_connection(h)) < 0) {
646 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
647 return err;
648 }
649 } else {
650 ftp_close_data_connection(s);
651 if (ftp_status(s, NULL, abor_codes) < 225) {
652 /* wu-ftpd also closes control connection after data connection closing */
653 ffurl_closep(&s->conn_control);
654 if ((err = ftp_connect_control_connection(h)) < 0) {
655 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
656 return err;
657 }
658 }
659 }
660
661 return 0;
662 }
663
ftp_connect(URLContext * h,const char * url)664 static int ftp_connect(URLContext *h, const char *url)
665 {
666 char proto[10], path[MAX_URL_SIZE], credentials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
667 const char *tok_user = NULL, *tok_pass = NULL;
668 char *newpath = NULL;
669 int err;
670 FTPContext *s = h->priv_data;
671
672 s->state = DISCONNECTED;
673 s->listing_method = UNKNOWN_METHOD;
674 s->filesize = -1;
675 s->position = 0;
676 s->features = NULL;
677
678 av_url_split(proto, sizeof(proto),
679 credentials, sizeof(credentials),
680 hostname, sizeof(hostname),
681 &s->server_control_port,
682 path, sizeof(path),
683 url);
684
685 if (!*credentials) {
686 if (!s->option_user) {
687 tok_user = "anonymous";
688 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
689 } else {
690 tok_user = s->option_user;
691 tok_pass = s->option_password;
692 }
693 s->user = av_strdup(tok_user);
694 s->password = av_strdup(tok_pass);
695 } else {
696 char *pass = strchr(credentials, ':');
697 if (pass) {
698 *pass++ = '\0';
699 tok_pass = pass;
700 s->password = ff_urldecode(pass, 0);
701 } else {
702 tok_pass = s->option_password;
703 s->password = av_strdup(tok_pass);
704 }
705 s->user = ff_urldecode(credentials, 0);
706 }
707 s->hostname = av_strdup(hostname);
708 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
709 return AVERROR(ENOMEM);
710 }
711
712 if (s->server_control_port < 0 || s->server_control_port > 65535)
713 s->server_control_port = 21;
714
715 if ((err = ftp_connect_control_connection(h)) < 0)
716 return err;
717
718 if ((err = ftp_current_dir(s)) < 0)
719 return err;
720
721 newpath = av_append_path_component(s->path, path);
722 if (!newpath)
723 return AVERROR(ENOMEM);
724 av_free(s->path);
725 s->path = newpath;
726
727 return 0;
728 }
729
ftp_open(URLContext * h,const char * url,int flags)730 static int ftp_open(URLContext *h, const char *url, int flags)
731 {
732 FTPContext *s = h->priv_data;
733 int err;
734
735 ff_dlog(h, "ftp protocol open\n");
736
737 if ((err = ftp_connect(h, url)) < 0)
738 goto fail;
739
740 if (ftp_restart(s, 0) < 0) {
741 h->is_streamed = 1;
742 } else {
743 ftp_file_size(s);
744 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
745 h->is_streamed = 1;
746 }
747
748 return 0;
749
750 fail:
751 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
752 ftp_close(h);
753 return err;
754 }
755
ftp_seek(URLContext * h,int64_t pos,int whence)756 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
757 {
758 FTPContext *s = h->priv_data;
759 int err;
760 int64_t new_pos;
761
762 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
763
764 switch(whence) {
765 case AVSEEK_SIZE:
766 return s->filesize;
767 case SEEK_SET:
768 new_pos = pos;
769 break;
770 case SEEK_CUR:
771 new_pos = s->position + pos;
772 break;
773 case SEEK_END:
774 if (s->filesize < 0)
775 return AVERROR(EIO);
776 new_pos = s->filesize + pos;
777 break;
778 default:
779 return AVERROR(EINVAL);
780 }
781
782 if (h->is_streamed)
783 return AVERROR(EIO);
784
785 if (new_pos < 0) {
786 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
787 return AVERROR(EINVAL);
788 }
789
790 if (new_pos != s->position) {
791 if ((err = ftp_abort(h)) < 0)
792 return err;
793 s->position = new_pos;
794 }
795 return new_pos;
796 }
797
ftp_read(URLContext * h,unsigned char * buf,int size)798 static int ftp_read(URLContext *h, unsigned char *buf, int size)
799 {
800 FTPContext *s = h->priv_data;
801 int read, err, retry_done = 0;
802
803 ff_dlog(h, "ftp protocol read %d bytes\n", size);
804 retry:
805 if (s->state == ENDOFFILE)
806 return AVERROR_EOF;
807 if (s->state == DISCONNECTED) {
808 if ((err = ftp_connect_data_connection(h)) < 0)
809 return err;
810 }
811 if (s->state == READY) {
812 if ((err = ftp_retrieve(s)) < 0)
813 return err;
814 }
815 if (s->conn_data && s->state == DOWNLOADING) {
816 read = ffurl_read(s->conn_data, buf, size);
817 if (read >= 0) {
818 s->position += read;
819 s->filesize = FFMAX(s->filesize, s->position);
820 }
821 if (read == AVERROR_EOF) {
822 static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
823 char *response = NULL;
824 err = ftp_status(s, &response, retr_codes);
825 if (err == 226) {
826 ftp_close_data_connection(s);
827 av_freep(&response);
828 s->state = ENDOFFILE;
829 return AVERROR_EOF;
830 }
831 /* 250 is not allowed, any other status means some kind of error */
832 av_log(h, AV_LOG_ERROR, "FTP transfer failed: %s\n", response ? response : (err < 0 ? av_err2str(err) : "?"));
833 av_freep(&response);
834 read = AVERROR(EIO);
835 }
836 if (read <= 0 && !h->is_streamed) {
837 /* Server closed connection. Probably due to inactivity */
838 av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
839 if ((err = ftp_abort(h)) < 0)
840 return err;
841 if (!retry_done) {
842 retry_done = 1;
843 goto retry;
844 }
845 }
846 return read;
847 }
848
849 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
850 return AVERROR(EIO);
851 }
852
ftp_write(URLContext * h,const unsigned char * buf,int size)853 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
854 {
855 int err;
856 FTPContext *s = h->priv_data;
857 int written;
858
859 ff_dlog(h, "ftp protocol write %d bytes\n", size);
860
861 if (s->state == DISCONNECTED) {
862 if ((err = ftp_connect_data_connection(h)) < 0)
863 return err;
864 }
865 if (s->state == READY) {
866 if ((err = ftp_store(s)) < 0)
867 return err;
868 }
869 if (s->conn_data && s->state == UPLOADING) {
870 written = ffurl_write(s->conn_data, buf, size);
871 if (written > 0) {
872 s->position += written;
873 s->filesize = FFMAX(s->filesize, s->position);
874 }
875 return written;
876 }
877
878 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
879 return AVERROR(EIO);
880 }
881
ftp_close(URLContext * h)882 static int ftp_close(URLContext *h)
883 {
884 FTPContext *s = h->priv_data;
885
886 ff_dlog(h, "ftp protocol close\n");
887
888 ftp_close_both_connections(s);
889 av_freep(&s->user);
890 av_freep(&s->password);
891 av_freep(&s->hostname);
892 av_freep(&s->path);
893 av_freep(&s->features);
894
895 return 0;
896 }
897
ftp_get_file_handle(URLContext * h)898 static int ftp_get_file_handle(URLContext *h)
899 {
900 FTPContext *s = h->priv_data;
901
902 ff_dlog(h, "ftp protocol get_file_handle\n");
903
904 if (s->conn_data)
905 return ffurl_get_file_handle(s->conn_data);
906
907 return AVERROR(EIO);
908 }
909
ftp_shutdown(URLContext * h,int flags)910 static int ftp_shutdown(URLContext *h, int flags)
911 {
912 FTPContext *s = h->priv_data;
913
914 ff_dlog(h, "ftp protocol shutdown\n");
915
916 if (s->conn_data)
917 return ffurl_shutdown(s->conn_data, flags);
918
919 return AVERROR(EIO);
920 }
921
ftp_open_dir(URLContext * h)922 static int ftp_open_dir(URLContext *h)
923 {
924 FTPContext *s = h->priv_data;
925 int ret;
926
927 if ((ret = ftp_connect(h, h->filename)) < 0)
928 goto fail;
929 if ((ret = ftp_set_dir(s)) < 0)
930 goto fail;
931 if ((ret = ftp_connect_data_connection(h)) < 0)
932 goto fail;
933 if ((ret = ftp_list(s)) < 0)
934 goto fail;
935 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
936 if (!s->dir_buffer) {
937 ret = AVERROR(ENOMEM);
938 goto fail;
939 }
940 s->dir_buffer[0] = 0;
941 if (s->conn_data && s->state == LISTING_DIR)
942 return 0;
943 fail:
944 ffurl_closep(&s->conn_control);
945 ffurl_closep(&s->conn_data);
946 return ret;
947 }
948
ftp_parse_date(const char * date)949 static int64_t ftp_parse_date(const char *date)
950 {
951 struct tm tv;
952 memset(&tv, 0, sizeof(struct tm));
953 av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
954 return INT64_C(1000000) * av_timegm(&tv);
955 }
956
ftp_parse_entry_nlst(char * line,AVIODirEntry * next)957 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
958 {
959 next->name = av_strdup(line);
960 return 0;
961 }
962
ftp_parse_entry_mlsd(char * mlsd,AVIODirEntry * next)963 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
964 {
965 char *fact, *value;
966 char *saveptr = NULL, *p = mlsd;
967 ff_dlog(NULL, "%s\n", mlsd);
968 while(fact = av_strtok(p, ";", &saveptr)) {
969 p = NULL;
970 if (fact[0] == ' ') {
971 next->name = av_strdup(&fact[1]);
972 continue;
973 }
974 fact = av_strtok(fact, "=", &value);
975 if (!fact)
976 continue;
977 if (!av_strcasecmp(fact, "type")) {
978 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
979 return 1;
980 if (!av_strcasecmp(value, "dir"))
981 next->type = AVIO_ENTRY_DIRECTORY;
982 else if (!av_strcasecmp(value, "file"))
983 next->type = AVIO_ENTRY_FILE;
984 else if (!av_strcasecmp(value, "OS.unix=slink:"))
985 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
986 } else if (!av_strcasecmp(fact, "modify")) {
987 next->modification_timestamp = ftp_parse_date(value);
988 } else if (!av_strcasecmp(fact, "UNIX.mode")) {
989 next->filemode = strtoumax(value, NULL, 8);
990 } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
991 next->user_id = strtoumax(value, NULL, 10);
992 else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
993 next->group_id = strtoumax(value, NULL, 10);
994 else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
995 next->size = strtoll(value, NULL, 10);
996 }
997 return 0;
998 }
999
1000 /**
1001 * @return 0 on success, negative on error, positive on entry to discard.
1002 */
ftp_parse_entry(URLContext * h,char * line,AVIODirEntry * next)1003 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
1004 {
1005 FTPContext *s = h->priv_data;
1006
1007 switch (s->listing_method) {
1008 case MLSD:
1009 return ftp_parse_entry_mlsd(line, next);
1010 case NLST:
1011 return ftp_parse_entry_nlst(line, next);
1012 case UNKNOWN_METHOD:
1013 default:
1014 return -1;
1015 }
1016 }
1017
ftp_read_dir(URLContext * h,AVIODirEntry ** next)1018 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
1019 {
1020 FTPContext *s = h->priv_data;
1021 char *start, *found;
1022 int ret, retried;
1023
1024 do {
1025 retried = 0;
1026 start = s->dir_buffer + s->dir_buffer_offset;
1027 while (!(found = strstr(start, "\n"))) {
1028 if (retried)
1029 return AVERROR(EIO);
1030 s->dir_buffer_size -= s->dir_buffer_offset;
1031 s->dir_buffer_offset = 0;
1032 if (s->dir_buffer_size)
1033 memmove(s->dir_buffer, start, s->dir_buffer_size);
1034 ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1035 if (ret < 0)
1036 return ret;
1037 if (!ret) {
1038 *next = NULL;
1039 return 0;
1040 }
1041 s->dir_buffer_size += ret;
1042 s->dir_buffer[s->dir_buffer_size] = 0;
1043 start = s->dir_buffer;
1044 retried = 1;
1045 }
1046 s->dir_buffer_offset += (found + 1 - start);
1047 found[0] = 0;
1048 if (found > start && found[-1] == '\r')
1049 found[-1] = 0;
1050
1051 *next = ff_alloc_dir_entry();
1052 if (!*next)
1053 return AVERROR(ENOMEM);
1054 (*next)->utf8 = s->utf8;
1055 ret = ftp_parse_entry(h, start, *next);
1056 if (ret) {
1057 avio_free_directory_entry(next);
1058 if (ret < 0)
1059 return ret;
1060 }
1061 } while (ret > 0);
1062 return 0;
1063 }
1064
ftp_close_dir(URLContext * h)1065 static int ftp_close_dir(URLContext *h)
1066 {
1067 FTPContext *s = h->priv_data;
1068 av_freep(&s->dir_buffer);
1069 ffurl_closep(&s->conn_control);
1070 ffurl_closep(&s->conn_data);
1071 return 0;
1072 }
1073
ftp_delete(URLContext * h)1074 static int ftp_delete(URLContext *h)
1075 {
1076 FTPContext *s = h->priv_data;
1077 char command[MAX_URL_SIZE];
1078 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1079 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1080 int ret;
1081
1082 if ((ret = ftp_connect(h, h->filename)) < 0)
1083 goto cleanup;
1084
1085 snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1086 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1087 ret = 0;
1088 goto cleanup;
1089 }
1090
1091 snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1092 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1093 ret = 0;
1094 else
1095 ret = AVERROR(EIO);
1096
1097 cleanup:
1098 ftp_close(h);
1099 return ret;
1100 }
1101
ftp_move(URLContext * h_src,URLContext * h_dst)1102 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1103 {
1104 FTPContext *s = h_src->priv_data;
1105 char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1106 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1107 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1108 int ret;
1109
1110 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1111 goto cleanup;
1112
1113 snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1114 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1115 ret = AVERROR(EIO);
1116 goto cleanup;
1117 }
1118
1119 av_url_split(0, 0, 0, 0, 0, 0, 0,
1120 path, sizeof(path),
1121 h_dst->filename);
1122 snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1123 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1124 ret = 0;
1125 else
1126 ret = AVERROR(EIO);
1127
1128 cleanup:
1129 ftp_close(h_src);
1130 return ret;
1131 }
1132
1133 const URLProtocol ff_ftp_protocol = {
1134 .name = "ftp",
1135 .url_open = ftp_open,
1136 .url_read = ftp_read,
1137 .url_write = ftp_write,
1138 .url_seek = ftp_seek,
1139 .url_close = ftp_close,
1140 .url_get_file_handle = ftp_get_file_handle,
1141 .url_shutdown = ftp_shutdown,
1142 .priv_data_size = sizeof(FTPContext),
1143 .priv_data_class = &ftp_context_class,
1144 .url_open_dir = ftp_open_dir,
1145 .url_read_dir = ftp_read_dir,
1146 .url_close_dir = ftp_close_dir,
1147 .url_delete = ftp_delete,
1148 .url_move = ftp_move,
1149 .flags = URL_PROTOCOL_FLAG_NETWORK,
1150 .default_whitelist = "tcp",
1151 };
1152