FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/ftp.c
Date: 2024-04-25 15:36:26
Exec Total Coverage
Lines: 0 623 0.0%
Functions: 0 41 0.0%
Branches: 0 420 0.0%

Line Branch Exec Source
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 #include <time.h>
23
24 #include "libavutil/avstring.h"
25 #include "libavutil/internal.h"
26 #include "libavutil/mem.h"
27 #include "libavutil/parseutils.h"
28 #include "avformat.h"
29 #include "internal.h"
30 #include "url.h"
31 #include "urldecode.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/bprint.h"
34
35 #define CONTROL_BUFFER_SIZE 1024
36 #define DIR_BUFFER_SIZE 4096
37
38 typedef enum {
39 UNKNOWN,
40 READY,
41 DOWNLOADING,
42 UPLOADING,
43 LISTING_DIR,
44 DISCONNECTED,
45 ENDOFFILE,
46 } FTPState;
47
48 typedef enum {
49 UNKNOWN_METHOD,
50 NLST,
51 MLSD
52 } FTPListingMethod;
53
54 typedef struct {
55 const AVClass *class;
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 */
74 char *dir_buffer;
75 size_t dir_buffer_size;
76 size_t dir_buffer_offset;
77 int utf8;
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 */
80 } FTPContext;
81
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 },
91 {NULL}
92 };
93
94 static const AVClass ftp_context_class = {
95 .class_name = "ftp",
96 .item_name = av_default_item_name,
97 .option = options,
98 .version = LIBAVUTIL_VERSION_INT,
99 };
100
101 static int ftp_close(URLContext *h);
102
103 static int ftp_getc(FTPContext *s)
104 {
105 int len;
106 if (s->control_buf_ptr >= s->control_buf_end) {
107 len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
108 if (len < 0) {
109 return len;
110 } else if (!len) {
111 return -1;
112 } else {
113 s->control_buf_ptr = s->control_buffer;
114 s->control_buf_end = s->control_buffer + len;
115 }
116 }
117 return *s->control_buf_ptr++;
118 }
119
120 static int ftp_get_line(FTPContext *s, char *line, int line_size)
121 {
122 int ch;
123 char *q = line;
124
125 for (;;) {
126 ch = ftp_getc(s);
127 if (ch < 0) {
128 return ch;
129 }
130 if (ch == '\n') {
131 /* process line */
132 if (q > line && q[-1] == '\r')
133 q--;
134 *q = '\0';
135 return 0;
136 } else {
137 if ((q - line) < line_size - 1)
138 *q++ = ch;
139 }
140 }
141 }
142
143 /*
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.
147 */
148 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
149 {
150 int err, i, dash = 0, result = 0, code_found = 0, linesize;
151 char buf[CONTROL_BUFFER_SIZE];
152 AVBPrint line_buffer;
153
154 if (line)
155 av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
156
157 while (!code_found || dash) {
158 if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
159 if (line)
160 av_bprint_finalize(&line_buffer, NULL);
161 return err;
162 }
163
164 av_log(s, AV_LOG_DEBUG, "%s\n", buf);
165
166 linesize = strlen(buf);
167 err = 0;
168 if (linesize >= 3) {
169 for (i = 0; i < 3; ++i) {
170 if (buf[i] < '0' || buf[i] > '9') {
171 err = 0;
172 break;
173 }
174 err *= 10;
175 err += buf[i] - '0';
176 }
177 }
178
179 if (!code_found) {
180 if (err >= 500) {
181 code_found = 1;
182 result = err;
183 } else
184 for (i = 0; response_codes[i]; ++i) {
185 if (err == response_codes[i]) {
186 code_found = 1;
187 result = err;
188 break;
189 }
190 }
191 }
192 if (code_found) {
193 if (line)
194 av_bprintf(&line_buffer, "%s\r\n", buf);
195 if (linesize >= 4) {
196 if (!dash && buf[3] == '-')
197 dash = err;
198 else if (err == dash && buf[3] == ' ')
199 dash = 0;
200 }
201 }
202 }
203
204 if (line)
205 av_bprint_finalize(&line_buffer, line);
206 return result;
207 }
208
209 static int ftp_send_command(FTPContext *s, const char *command,
210 const int response_codes[], char **response)
211 {
212 int err;
213
214 ff_dlog(s, "%s", command);
215
216 if (response)
217 *response = NULL;
218
219 if (!s->conn_control)
220 return AVERROR(EIO);
221
222 if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
223 return err;
224 if (!err)
225 return -1;
226
227 /* return status */
228 if (response_codes) {
229 return ftp_status(s, response, response_codes);
230 }
231 return 0;
232 }
233
234 static void ftp_close_data_connection(FTPContext *s)
235 {
236 ffurl_closep(&s->conn_data);
237 s->state = DISCONNECTED;
238 }
239
240 static void ftp_close_both_connections(FTPContext *s)
241 {
242 ffurl_closep(&s->conn_control);
243 ftp_close_data_connection(s);
244 }
245
246 static int ftp_auth(FTPContext *s)
247 {
248 char buf[CONTROL_BUFFER_SIZE];
249 int err;
250 static const int user_codes[] = {331, 230, 0};
251 static const int pass_codes[] = {230, 0};
252
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);
258
259 err = ftp_send_command(s, buf, user_codes, NULL);
260 if (err == 331) {
261 if (s->password) {
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);
267
268 err = ftp_send_command(s, buf, pass_codes, NULL);
269 } else
270 return AVERROR(EACCES);
271 }
272 if (err != 230)
273 return AVERROR(EACCES);
274
275 return 0;
276 }
277
278 static int ftp_passive_mode_epsv(FTPContext *s)
279 {
280 char *res = NULL, *start = NULL, *end = NULL;
281 int i;
282 static const char d = '|';
283 static const char *command = "EPSV\r\n";
284 static const int epsv_codes[] = {229, 0};
285
286 if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
287 goto fail;
288
289 for (i = 0; res[i]; ++i) {
290 if (res[i] == '(') {
291 start = res + i + 1;
292 } else if (res[i] == ')') {
293 end = res + i;
294 break;
295 }
296 }
297 if (!start || !end)
298 goto fail;
299
300 *end = '\0';
301 if (strlen(start) < 5)
302 goto fail;
303 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
304 goto fail;
305 start += 3;
306 end[-1] = '\0';
307
308 s->server_data_port = atoi(start);
309 ff_dlog(s, "Server data port: %d\n", s->server_data_port);
310
311 av_free(res);
312 return 0;
313
314 fail:
315 av_free(res);
316 s->server_data_port = -1;
317 return AVERROR(ENOSYS);
318 }
319
320 static int ftp_passive_mode(FTPContext *s)
321 {
322 char *res = NULL, *start = NULL, *end = NULL;
323 int i;
324 static const char *command = "PASV\r\n";
325 static const int pasv_codes[] = {227, 0};
326
327 if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
328 goto fail;
329
330 for (i = 0; res[i]; ++i) {
331 if (res[i] == '(') {
332 start = res + i + 1;
333 } else if (res[i] == ')') {
334 end = res + i;
335 break;
336 }
337 }
338 if (!start || !end)
339 goto fail;
340
341 *end = '\0';
342 /* skip ip */
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;
347
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);
356
357 av_free(res);
358 return 0;
359
360 fail:
361 av_free(res);
362 s->server_data_port = -1;
363 return AVERROR(EIO);
364 }
365
366 static int ftp_current_dir(FTPContext *s)
367 {
368 char *res = NULL, *start = NULL, *end = NULL;
369 int i;
370 static const char *command = "PWD\r\n";
371 static const int pwd_codes[] = {257, 0};
372
373 if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
374 goto fail;
375
376 for (i = 0; res[i]; ++i) {
377 if (res[i] == '"') {
378 if (!start) {
379 start = res + i + 1;
380 continue;
381 }
382 end = res + i;
383 break;
384 }
385 }
386
387 if (!end)
388 goto fail;
389
390 *end = '\0';
391 s->path = av_strdup(start);
392
393 av_free(res);
394
395 if (!s->path)
396 return AVERROR(ENOMEM);
397 return 0;
398
399 fail:
400 av_free(res);
401 return AVERROR(EIO);
402 }
403
404 static int ftp_file_size(FTPContext *s)
405 {
406 char command[CONTROL_BUFFER_SIZE];
407 char *res = NULL;
408 int ret;
409 static const int size_codes[] = {213, 0};
410
411 ret = snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
412 if (ret >= sizeof(command))
413 return AVERROR(ENOSYS);
414
415 if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) {
416 s->filesize = strtoll(&res[4], NULL, 10);
417 } else {
418 s->filesize = -1;
419 av_free(res);
420 return AVERROR(EIO);
421 }
422
423 av_free(res);
424 return 0;
425 }
426
427 static int ftp_retrieve(FTPContext *s)
428 {
429 char command[CONTROL_BUFFER_SIZE];
430 static const int retr_codes[] = {150, 125, 0};
431 int resp_code, ret;
432
433 ret = snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
434 if (ret >= sizeof(command))
435 return AVERROR(ENOSYS);
436
437 resp_code = ftp_send_command(s, command, retr_codes, NULL);
438 if (resp_code != 125 && resp_code != 150)
439 return AVERROR(EIO);
440
441 s->state = DOWNLOADING;
442
443 return 0;
444 }
445
446 static int ftp_store(FTPContext *s)
447 {
448 char command[CONTROL_BUFFER_SIZE];
449 static const int stor_codes[] = {150, 125, 0};
450 int resp_code, ret;
451
452 ret = snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
453 if (ret >= sizeof(command))
454 return AVERROR(ENOSYS);
455
456 resp_code = ftp_send_command(s, command, stor_codes, NULL);
457 if (resp_code != 125 && resp_code != 150)
458 return AVERROR(EIO);
459
460 s->state = UPLOADING;
461
462 return 0;
463 }
464
465 static int ftp_type(FTPContext *s)
466 {
467 static const char *command = "TYPE I\r\n";
468 static const int type_codes[] = {200, 0};
469
470 if (ftp_send_command(s, command, type_codes, NULL) != 200)
471 return AVERROR(EIO);
472
473 return 0;
474 }
475
476 static int ftp_restart(FTPContext *s, int64_t pos)
477 {
478 char command[CONTROL_BUFFER_SIZE];
479 static const int rest_codes[] = {350, 0};
480
481 snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
482 if (ftp_send_command(s, command, rest_codes, NULL) != 350)
483 return AVERROR(EIO);
484
485 return 0;
486 }
487
488 static int ftp_set_dir(FTPContext *s)
489 {
490 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
491 char command[MAX_URL_SIZE];
492 int ret;
493
494 ret = snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
495 if (ret >= sizeof(command))
496 return AVERROR(ENOSYS);
497
498 if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
499 return AVERROR(EIO);
500 return 0;
501 }
502
503 static int ftp_list_mlsd(FTPContext *s)
504 {
505 static const char *command = "MLSD\r\n";
506 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
507
508 if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
509 return AVERROR(ENOSYS);
510 s->listing_method = MLSD;
511 return 0;
512 }
513
514 static int ftp_list_nlst(FTPContext *s)
515 {
516 static const char *command = "NLST\r\n";
517 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
518
519 if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
520 return AVERROR(ENOSYS);
521 s->listing_method = NLST;
522 return 0;
523 }
524
525 static int ftp_list(FTPContext *s)
526 {
527 int ret;
528 s->state = LISTING_DIR;
529
530 if ((ret = ftp_list_mlsd(s)) < 0)
531 ret = ftp_list_nlst(s);
532
533 return ret;
534 }
535
536 static int ftp_has_feature(FTPContext *s, const char *feature_name)
537 {
538 if (!s->features)
539 return 0;
540
541 return av_stristr(s->features, feature_name) != NULL;
542 }
543
544 static int ftp_features(FTPContext *s)
545 {
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};
550
551 av_freep(&s->features);
552 if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
553 av_freep(&s->features);
554 }
555
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)
559 s->utf8 = 1;
560 }
561
562 return 0;
563 }
564
565 static int ftp_connect_control_connection(URLContext *h)
566 {
567 char buf[CONTROL_BUFFER_SIZE], *response = NULL;
568 int err;
569 AVDictionary *opts = NULL;
570 FTPContext *s = h->priv_data;
571 static const int connect_codes[] = {220, 0};
572
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);
582 av_dict_free(&opts);
583 if (err < 0) {
584 av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
585 return err;
586 }
587
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);
592 }
593
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.");
596 }
597 av_free(response);
598
599 if ((err = ftp_auth(s)) < 0) {
600 av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
601 return err;
602 }
603
604 if ((err = ftp_type(s)) < 0) {
605 av_log(h, AV_LOG_ERROR, "Set content type failed\n");
606 return err;
607 }
608
609 ftp_features(s);
610 }
611 return 0;
612 }
613
614 static int ftp_connect_data_connection(URLContext *h)
615 {
616 int err;
617 char buf[CONTROL_BUFFER_SIZE];
618 AVDictionary *opts = NULL;
619 FTPContext *s = h->priv_data;
620
621 if (!s->conn_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)
626 return err;
627 }
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);
636 av_dict_free(&opts);
637 if (err < 0)
638 return err;
639
640 if (s->position)
641 if ((err = ftp_restart(s, s->position)) < 0)
642 return err;
643 }
644 s->state = READY;
645 return 0;
646 }
647
648 static int ftp_abort(URLContext *h)
649 {
650 static const char *command = "ABOR\r\n";
651 int err;
652 static const int abor_codes[] = {225, 226, 0};
653 FTPContext *s = h->priv_data;
654
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."
658
659 There are FTP server implementations that don't response
660 to any commands during data transfer in passive mode (including ABOR).
661
662 This implementation closes data connection by force.
663 */
664
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");
669 return err;
670 }
671 } else {
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");
678 return err;
679 }
680 }
681 }
682
683 return 0;
684 }
685
686 static int ftp_connect(URLContext *h, const char *url)
687 {
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;
691 int err;
692 FTPContext *s = h->priv_data;
693
694 s->state = DISCONNECTED;
695 s->listing_method = UNKNOWN_METHOD;
696 s->filesize = -1;
697 s->position = 0;
698 s->features = NULL;
699
700 av_url_split(proto, sizeof(proto),
701 credentials, sizeof(credentials),
702 hostname, sizeof(hostname),
703 &s->server_control_port,
704 path, sizeof(path),
705 url);
706
707 if (!*credentials) {
708 if (!s->option_user) {
709 tok_user = "anonymous";
710 tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
711 } else {
712 tok_user = s->option_user;
713 tok_pass = s->option_password;
714 }
715 s->user = av_strdup(tok_user);
716 s->password = av_strdup(tok_pass);
717 } else {
718 char *pass = strchr(credentials, ':');
719 if (pass) {
720 *pass++ = '\0';
721 tok_pass = pass;
722 s->password = ff_urldecode(pass, 0);
723 } else {
724 tok_pass = s->option_password;
725 s->password = av_strdup(tok_pass);
726 }
727 s->user = ff_urldecode(credentials, 0);
728 }
729 s->hostname = av_strdup(hostname);
730 if (!s->hostname || !s->user || (tok_pass && !s->password)) {
731 return AVERROR(ENOMEM);
732 }
733
734 if (s->server_control_port < 0 || s->server_control_port > 65535)
735 s->server_control_port = 21;
736
737 if ((err = ftp_connect_control_connection(h)) < 0)
738 return err;
739
740 if ((err = ftp_current_dir(s)) < 0)
741 return err;
742
743 newpath = av_append_path_component(s->path, path);
744 if (!newpath)
745 return AVERROR(ENOMEM);
746 av_free(s->path);
747 s->path = newpath;
748
749 return 0;
750 }
751
752 static int ftp_open(URLContext *h, const char *url, int flags)
753 {
754 FTPContext *s = h->priv_data;
755 int err;
756
757 ff_dlog(h, "ftp protocol open\n");
758
759 if ((err = ftp_connect(h, url)) < 0)
760 goto fail;
761
762 if (ftp_restart(s, 0) < 0) {
763 h->is_streamed = 1;
764 } else {
765 ftp_file_size(s);
766 if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
767 h->is_streamed = 1;
768 }
769
770 return 0;
771
772 fail:
773 av_log(h, AV_LOG_ERROR, "FTP open failed\n");
774 ftp_close(h);
775 return err;
776 }
777
778 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
779 {
780 FTPContext *s = h->priv_data;
781 int err;
782 int64_t new_pos;
783
784 ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
785
786 switch(whence) {
787 case AVSEEK_SIZE:
788 return s->filesize;
789 case SEEK_SET:
790 new_pos = pos;
791 break;
792 case SEEK_CUR:
793 new_pos = s->position + pos;
794 break;
795 case SEEK_END:
796 if (s->filesize < 0)
797 return AVERROR(EIO);
798 new_pos = s->filesize + pos;
799 break;
800 default:
801 return AVERROR(EINVAL);
802 }
803
804 if (h->is_streamed)
805 return AVERROR(EIO);
806
807 if (new_pos < 0) {
808 av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
809 return AVERROR(EINVAL);
810 }
811
812 if (new_pos != s->position) {
813 if ((err = ftp_abort(h)) < 0)
814 return err;
815 s->position = new_pos;
816 }
817 return new_pos;
818 }
819
820 static int ftp_read(URLContext *h, unsigned char *buf, int size)
821 {
822 FTPContext *s = h->priv_data;
823 int read, err, retry_done = 0;
824
825 ff_dlog(h, "ftp protocol read %d bytes\n", size);
826 retry:
827 if (s->state == ENDOFFILE)
828 return AVERROR_EOF;
829 if (s->state == DISCONNECTED) {
830 if ((err = ftp_connect_data_connection(h)) < 0)
831 return err;
832 }
833 if (s->state == READY) {
834 if ((err = ftp_retrieve(s)) < 0)
835 return err;
836 }
837 if (s->conn_data && s->state == DOWNLOADING) {
838 read = ffurl_read(s->conn_data, buf, size);
839 if (read >= 0) {
840 s->position += read;
841 s->filesize = FFMAX(s->filesize, s->position);
842 }
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);
847 if (err == 226) {
848 ftp_close_data_connection(s);
849 av_freep(&response);
850 s->state = ENDOFFILE;
851 return AVERROR_EOF;
852 }
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) : "?"));
855 av_freep(&response);
856 read = AVERROR(EIO);
857 }
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)
862 return err;
863 if (!retry_done) {
864 retry_done = 1;
865 goto retry;
866 }
867 }
868 return read;
869 }
870
871 av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
872 return AVERROR(EIO);
873 }
874
875 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
876 {
877 int err;
878 FTPContext *s = h->priv_data;
879 int written;
880
881 ff_dlog(h, "ftp protocol write %d bytes\n", size);
882
883 if (s->state == DISCONNECTED) {
884 if ((err = ftp_connect_data_connection(h)) < 0)
885 return err;
886 }
887 if (s->state == READY) {
888 if ((err = ftp_store(s)) < 0)
889 return err;
890 }
891 if (s->conn_data && s->state == UPLOADING) {
892 written = ffurl_write(s->conn_data, buf, size);
893 if (written > 0) {
894 s->position += written;
895 s->filesize = FFMAX(s->filesize, s->position);
896 }
897 return written;
898 }
899
900 av_log(h, AV_LOG_ERROR, "FTP write failed\n");
901 return AVERROR(EIO);
902 }
903
904 static int ftp_close(URLContext *h)
905 {
906 FTPContext *s = h->priv_data;
907
908 ff_dlog(h, "ftp protocol close\n");
909
910 ftp_close_both_connections(s);
911 av_freep(&s->user);
912 av_freep(&s->password);
913 av_freep(&s->hostname);
914 av_freep(&s->path);
915 av_freep(&s->features);
916
917 return 0;
918 }
919
920 static int ftp_get_file_handle(URLContext *h)
921 {
922 FTPContext *s = h->priv_data;
923
924 ff_dlog(h, "ftp protocol get_file_handle\n");
925
926 if (s->conn_data)
927 return ffurl_get_file_handle(s->conn_data);
928
929 return AVERROR(EIO);
930 }
931
932 static int ftp_shutdown(URLContext *h, int flags)
933 {
934 FTPContext *s = h->priv_data;
935
936 ff_dlog(h, "ftp protocol shutdown\n");
937
938 if (s->conn_data)
939 return ffurl_shutdown(s->conn_data, flags);
940
941 return AVERROR(EIO);
942 }
943
944 static int ftp_open_dir(URLContext *h)
945 {
946 FTPContext *s = h->priv_data;
947 int ret;
948
949 if ((ret = ftp_connect(h, h->filename)) < 0)
950 goto fail;
951 if ((ret = ftp_set_dir(s)) < 0)
952 goto fail;
953 if ((ret = ftp_connect_data_connection(h)) < 0)
954 goto fail;
955 if ((ret = ftp_list(s)) < 0)
956 goto fail;
957 s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
958 if (!s->dir_buffer) {
959 ret = AVERROR(ENOMEM);
960 goto fail;
961 }
962 s->dir_buffer[0] = 0;
963 if (s->conn_data && s->state == LISTING_DIR)
964 return 0;
965 fail:
966 ffurl_closep(&s->conn_control);
967 ffurl_closep(&s->conn_data);
968 return ret;
969 }
970
971 static int64_t ftp_parse_date(const char *date)
972 {
973 struct tm tv;
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);
977 }
978
979 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
980 {
981 next->name = av_strdup(line);
982 return 0;
983 }
984
985 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
986 {
987 char *fact, *value;
988 char *saveptr = NULL, *p = mlsd;
989 ff_dlog(NULL, "%s\n", mlsd);
990 while(fact = av_strtok(p, ";", &saveptr)) {
991 p = NULL;
992 if (fact[0] == ' ') {
993 next->name = av_strdup(&fact[1]);
994 continue;
995 }
996 fact = av_strtok(fact, "=", &value);
997 if (!fact)
998 continue;
999 if (!av_strcasecmp(fact, "type")) {
1000 if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
1001 return 1;
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);
1018 }
1019 return 0;
1020 }
1021
1022 /**
1023 * @return 0 on success, negative on error, positive on entry to discard.
1024 */
1025 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
1026 {
1027 FTPContext *s = h->priv_data;
1028
1029 switch (s->listing_method) {
1030 case MLSD:
1031 return ftp_parse_entry_mlsd(line, next);
1032 case NLST:
1033 return ftp_parse_entry_nlst(line, next);
1034 case UNKNOWN_METHOD:
1035 default:
1036 return -1;
1037 }
1038 }
1039
1040 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
1041 {
1042 FTPContext *s = h->priv_data;
1043 char *start, *found;
1044 int ret, retried;
1045
1046 do {
1047 retried = 0;
1048 start = s->dir_buffer + s->dir_buffer_offset;
1049 while (!(found = strstr(start, "\n"))) {
1050 if (retried)
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));
1057 if (ret < 0)
1058 return ret;
1059 if (!ret) {
1060 *next = NULL;
1061 return 0;
1062 }
1063 s->dir_buffer_size += ret;
1064 s->dir_buffer[s->dir_buffer_size] = 0;
1065 start = s->dir_buffer;
1066 retried = 1;
1067 }
1068 s->dir_buffer_offset += (found + 1 - start);
1069 found[0] = 0;
1070 if (found > start && found[-1] == '\r')
1071 found[-1] = 0;
1072
1073 *next = ff_alloc_dir_entry();
1074 if (!*next)
1075 return AVERROR(ENOMEM);
1076 (*next)->utf8 = s->utf8;
1077 ret = ftp_parse_entry(h, start, *next);
1078 if (ret) {
1079 avio_free_directory_entry(next);
1080 if (ret < 0)
1081 return ret;
1082 }
1083 } while (ret > 0);
1084 return 0;
1085 }
1086
1087 static int ftp_close_dir(URLContext *h)
1088 {
1089 FTPContext *s = h->priv_data;
1090 av_freep(&s->dir_buffer);
1091 ffurl_closep(&s->conn_control);
1092 ffurl_closep(&s->conn_data);
1093 return 0;
1094 }
1095
1096 static int ftp_delete(URLContext *h)
1097 {
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};
1102 int ret;
1103
1104 if ((ret = ftp_connect(h, h->filename)) < 0)
1105 goto cleanup;
1106
1107 ret = snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1108 if (ret >= sizeof(command)) {
1109 ret = AVERROR(ENOSYS);
1110 goto cleanup;
1111 }
1112
1113 if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1114 ret = 0;
1115 goto cleanup;
1116 }
1117
1118 ret = snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1119 if (ret >= sizeof(command)) {
1120 ret = AVERROR(ENOSYS);
1121 goto cleanup;
1122 }
1123
1124 if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1125 ret = 0;
1126 else
1127 ret = AVERROR(EIO);
1128
1129 cleanup:
1130 ftp_close(h);
1131 return ret;
1132 }
1133
1134 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1135 {
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};
1140 int ret;
1141
1142 if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1143 goto cleanup;
1144
1145 ret = snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1146 if (ret >= sizeof(command)) {
1147 ret = AVERROR(ENOSYS);
1148 goto cleanup;
1149 }
1150
1151 if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1152 ret = AVERROR(EIO);
1153 goto cleanup;
1154 }
1155
1156 av_url_split(0, 0, 0, 0, 0, 0, 0,
1157 path, sizeof(path),
1158 h_dst->filename);
1159 ret = snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1160 if (ret >= sizeof(command)) {
1161 ret = AVERROR(ENOSYS);
1162 goto cleanup;
1163 }
1164
1165 if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1166 ret = 0;
1167 else
1168 ret = AVERROR(EIO);
1169
1170 cleanup:
1171 ftp_close(h_src);
1172 return ret;
1173 }
1174
1175 const URLProtocol ff_ftp_protocol = {
1176 .name = "ftp",
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",
1193 };
1194