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
32
33 #define CONTROL_BUFFER_SIZE 1024
34 #define DIR_BUFFER_SIZE 4096
35
45
51
61 char *
user;
/**< Server user */
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. */
67 const char *
anonymous_password;
/**< Password to be used for anonymous user. An email should be used. */
71 char *
features;
/**< List of server's features represented as raw response */
76 const char *
option_user;
/**< User to be used if none given in the URL */
79
80 #define OFFSET(x) offsetof(FTPContext, x)
81 #define D AV_OPT_FLAG_DECODING_PARAM
82 #define E AV_OPT_FLAG_ENCODING_PARAM
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 },
90 };
91
97 };
98
100
102 {
104 if (
s->control_buf_ptr >=
s->control_buf_end) {
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
119 {
120 int ch;
122
123 for (;;) {
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 */
147 {
148 int err,
i, dash = 0,
result = 0, code_found = 0, linesize;
150 AVBPrint line_buffer;
151
154
155 while (!code_found || dash) {
159 return err;
160 }
161
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;
174 }
175 }
176
177 if (!code_found) {
178 if (err >= 500) {
179 code_found = 1;
181 } else
182 for (
i = 0; response_codes[
i]; ++
i) {
183 if (err == response_codes[
i]) {
184 code_found = 1;
186 break;
187 }
188 }
189 }
190 if (code_found) {
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
205 }
206
208 const int response_codes[], char **response)
209 {
210 int err;
211
213
214 if (response)
216
217 if (!
s->conn_control)
219
221 return err;
222 if (!err)
223 return -1;
224
225 /* return status */
226 if (response_codes) {
228 }
229 return 0;
230 }
231
233 {
236 }
237
239 {
242 }
243
245 {
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"))
253 snprintf(buf,
sizeof(buf),
"USER %s\r\n",
s->user);
255 if (err == 331) {
257 if (strpbrk(
s->password,
"\r\n"))
259 snprintf(buf,
sizeof(buf),
"PASS %s\r\n",
s->password);
261 } else
263 }
264 if (err != 230)
266
267 return 0;
268 }
269
271 {
274 static const char d = '|';
275 static const char *
command =
"EPSV\r\n";
276 static const int epsv_codes[] = {229, 0};
277
280
281 for (
i = 0; res[
i]; ++
i) {
284 }
else if (res[
i] ==
')') {
286 break;
287 }
288 }
289 if (!start || !end)
291
292 *end = '0円';
293 if (strlen(start) < 5)
295 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
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
304 return 0;
305
308 s->server_data_port = -1;
310 }
311
313 {
316 static const char *
command =
"PASV\r\n";
317 static const int pasv_codes[] = {227, 0};
318
321
322 for (
i = 0; res[
i]; ++
i) {
325 }
else if (res[
i] ==
')') {
327 break;
328 }
329 }
330 if (!start || !end)
332
333 *end = '0円';
334 /* skip ip */
339
340 /* parse port number */
342 if (!start)
goto fail;
343 s->server_data_port = atoi(start) * 256;
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
350 return 0;
351
354 s->server_data_port = -1;
356 }
357
359 {
362 static const char *
command =
"PWD\r\n";
363 static const int pwd_codes[] = {257, 0};
364
367
368 for (
i = 0; res[
i]; ++
i) {
370 if (!start) {
372 continue;
373 }
375 break;
376 }
377 }
378
379 if (!end)
381
382 *end = '0円';
384
386
389 return 0;
390
394 }
395
397 {
400 static const int size_codes[] = {213, 0};
401
404 s->filesize = strtoll(&res[4],
NULL, 10);
405 } else {
409 }
410
412 return 0;
413 }
414
416 {
418 static const int retr_codes[] = {150, 125, 0};
419 int resp_code;
420
423 if (resp_code != 125 && resp_code != 150)
425
427
428 return 0;
429 }
430
432 {
434 static const int stor_codes[] = {150, 125, 0};
435 int resp_code;
436
439 if (resp_code != 125 && resp_code != 150)
441
443
444 return 0;
445 }
446
448 {
449 static const char *
command =
"TYPE I\r\n";
450 static const int type_codes[] = {200, 0};
451
454
455 return 0;
456 }
457
459 {
461 static const int rest_codes[] = {350, 0};
462
466
467 return 0;
468 }
469
471 {
472 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
474
478 return 0;
479 }
480
482 {
483 static const char *
command =
"MLSD\r\n";
484 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
485
488 s->listing_method =
MLSD;
489 return 0;
490 }
491
493 {
494 static const char *
command =
"NLST\r\n";
495 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
496
499 s->listing_method =
NLST;
500 return 0;
501 }
502
504 {
507
510
512 }
513
515 {
517 return 0;
518
520 }
521
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
532 }
533
536 if (
ret == 200 ||
ret == 202)
538 }
539
540 return 0;
541 }
542
544 {
546 int err;
549 static const int connect_codes[] = {220, 0};
550
551 if (!
s->conn_control) {
553 s->hostname,
s->server_control_port,
NULL);
554 if (
s->rw_timeout != -1) {
556 } /* if option is not given, don't pass it and let tcp use its own default */
558 &
h->interrupt_callback, &
opts,
559 h->protocol_whitelist,
h->protocol_blacklist,
h);
561 if (err < 0) {
563 return err;
564 }
565
566 /* check if server is ready */
570 }
571
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 }
576
579 return err;
580 }
581
584 return err;
585 }
586
588 }
589 return 0;
590 }
591
593 {
594 int err;
598
600 /* Enter passive mode */
602 /* Use PASV as fallback */
604 return err;
605 }
606 /* Open data connection */
608 if (
s->rw_timeout != -1) {
610 } /* if option is not given, don't pass it and let tcp use its own default */
612 &
h->interrupt_callback, &
opts,
613 h->protocol_whitelist,
h->protocol_blacklist,
h);
615 if (err < 0)
616 return err;
617
620 return err;
621 }
623 return 0;
624 }
625
627 {
628 static const char *
command =
"ABOR\r\n";
629 int err;
630 static const int abor_codes[] = {225, 226, 0};
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
647 return err;
648 }
649 } else {
652 /* wu-ftpd also closes control connection after data connection closing */
656 return err;
657 }
658 }
659 }
660
661 return 0;
662 }
663
665 {
667 const char *tok_user =
NULL, *tok_pass =
NULL;
668 char *newpath =
NULL;
669 int err;
671
677
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 }
695 } else {
696 char *
pass = strchr(credentials,
':');
701 } else {
702 tok_pass =
s->option_password;
704 }
706 }
708 if (!
s->hostname || !
s->user || (tok_pass && !
s->password)) {
710 }
711
712 if (
s->server_control_port < 0 ||
s->server_control_port > 65535)
713 s->server_control_port = 21;
714
716 return err;
717
719 return err;
720
722 if (!newpath)
726
727 return 0;
728 }
729
731 {
733 int err;
734
736
739
742 } else {
746 }
747
748 return 0;
749
753 return err;
754 }
755
757 {
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) {
767 case SEEK_SET:
769 break;
770 case SEEK_CUR:
771 new_pos =
s->position +
pos;
772 break;
773 case SEEK_END:
776 new_pos =
s->filesize +
pos;
777 break;
778 default:
780 }
781
784
785 if (new_pos < 0) {
788 }
789
790 if (new_pos !=
s->position) {
792 return err;
793 s->position = new_pos;
794 }
795 return new_pos;
796 }
797
799 {
801 int read, err, retry_done = 0;
802
804 retry:
809 return err;
810 }
813 return err;
814 }
817 if (read >= 0) {
819 s->filesize =
FFMAX(
s->filesize,
s->position);
820 }
822 static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
823 char *response =
NULL;
825 if (err == 226) {
830 }
831 /* 250 is not allowed, any other status means some kind of error */
835 }
836 if (read <= 0 && !h->is_streamed) {
837 /* Server closed connection. Probably due to inactivity */
840 return err;
841 if (!retry_done) {
842 retry_done = 1;
843 goto retry;
844 }
845 }
846 return read;
847 }
848
851 }
852
854 {
855 int err;
857 int written;
858
860
863 return err;
864 }
867 return err;
868 }
871 if (written > 0) {
872 s->position += written;
873 s->filesize =
FFMAX(
s->filesize,
s->position);
874 }
875 return written;
876 }
877
880 }
881
883 {
885
887
894
895 return 0;
896 }
897
899 {
901
902 ff_dlog(
h,
"ftp protocol get_file_handle\n");
903
906
908 }
909
911 {
913
914 ff_dlog(
h,
"ftp protocol shutdown\n");
915
918
920 }
921
923 {
926
936 if (!
s->dir_buffer) {
939 }
940 s->dir_buffer[0] = 0;
942 return 0;
947 }
948
950 {
951 struct tm tv;
952 memset(&tv, 0, sizeof(struct tm));
954 return INT64_C(1000000) *
av_timegm(&tv);
955 }
956
958 {
960 return 0;
961 }
962
964 {
966 char *saveptr =
NULL, *p = mlsd;
970 if (
fact[0] ==
' ') {
972 continue;
973 }
976 continue;
979 return 1;
996 }
997 return 0;
998 }
999
1000 /**
1001 * @return 0 on success, negative on error, positive on entry to discard.
1002 */
1004 {
1006
1007 switch (
s->listing_method) {
1013 default:
1014 return -1;
1015 }
1016 }
1017
1019 {
1021 char *start, *found;
1023
1024 do {
1025 retried = 0;
1026 start =
s->dir_buffer +
s->dir_buffer_offset;
1027 while (!(found = strstr(start, "\n"))) {
1028 if (retried)
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);
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
1052 if (!*next)
1054 (*next)->utf8 =
s->utf8;
1060 }
1062 return 0;
1063 }
1064
1066 {
1071 return 0;
1072 }
1073
1075 {
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};
1081
1084
1089 }
1090
1094 else
1096
1100 }
1101
1103 {
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};
1109
1112
1117 }
1118
1120 path, sizeof(path),
1125 else
1127
1131 }
1132
1150 .default_whitelist = "tcp",
1151 };