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 err =
snprintf(buf,
sizeof(buf),
"USER %s\r\n",
s->user);
254 if (err >= sizeof(buf))
256
258 if (err == 331) {
260 if (strpbrk(
s->password,
"\r\n"))
262 err =
snprintf(buf,
sizeof(buf),
"PASS %s\r\n",
s->password);
263 if (err >= sizeof(buf))
265
267 } else
269 }
270 if (err != 230)
272
273 return 0;
274 }
275
277 {
280 static const char d =
'|';
281 static const char *
command =
"EPSV\r\n";
282 static const int epsv_codes[] = {229, 0};
283
286
287 for (
i = 0; res[
i]; ++
i) {
290 }
else if (res[
i] ==
')') {
292 break;
293 }
294 }
295 if (!start || !end)
297
298 *end = '0円';
299 if (strlen(start) < 5)
301 if (start[0] !=
d || start[1] !=
d || start[2] !=
d || end[-1] !=
d)
303 start += 3;
304 end[-1] = '0円';
305
306 s->server_data_port = atoi(start);
307 ff_dlog(
s,
"Server data port: %d\n",
s->server_data_port);
308
310 return 0;
311
314 s->server_data_port = -1;
316 }
317
319 {
322 static const char *
command =
"PASV\r\n";
323 static const int pasv_codes[] = {227, 0};
324
327
328 for (
i = 0; res[
i]; ++
i) {
331 }
else if (res[
i] ==
')') {
333 break;
334 }
335 }
336 if (!start || !end)
338
339 *end = '0円';
340 /* skip ip */
345
346 /* parse port number */
348 if (!start)
goto fail;
349 s->server_data_port = atoi(start) * 256;
351 if (!start)
goto fail;
352 s->server_data_port += atoi(start);
353 ff_dlog(
s,
"Server data port: %d\n",
s->server_data_port);
354
356 return 0;
357
360 s->server_data_port = -1;
362 }
363
365 {
368 static const char *
command =
"PWD\r\n";
369 static const int pwd_codes[] = {257, 0};
370
373
374 for (
i = 0; res[
i]; ++
i) {
376 if (!start) {
378 continue;
379 }
381 break;
382 }
383 }
384
385 if (!end)
387
388 *end = '0円';
390
392
395 return 0;
396
400 }
401
403 {
407 static const int size_codes[] = {213, 0};
408
412
414 s->filesize = strtoll(&res[4],
NULL, 10);
415 } else {
419 }
420
422 return 0;
423 }
424
426 {
428 static const int retr_codes[] = {150, 125, 0};
430
434
436 if (resp_code != 125 && resp_code != 150)
438
440
441 return 0;
442 }
443
445 {
447 static const int stor_codes[] = {150, 125, 0};
449
453
455 if (resp_code != 125 && resp_code != 150)
457
459
460 return 0;
461 }
462
464 {
465 static const char *
command =
"TYPE I\r\n";
466 static const int type_codes[] = {200, 0};
467
470
471 return 0;
472 }
473
475 {
477 static const int rest_codes[] = {350, 0};
478
482
483 return 0;
484 }
485
487 {
488 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
491
495
498 return 0;
499 }
500
502 {
503 static const char *
command =
"MLSD\r\n";
504 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
505
508 s->listing_method =
MLSD;
509 return 0;
510 }
511
513 {
514 static const char *
command =
"NLST\r\n";
515 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
516
519 s->listing_method =
NLST;
520 return 0;
521 }
522
524 {
527
530
532 }
533
535 {
537 return 0;
538
540 }
541
543 {
544 static const char *feat_command = "FEAT\r\n";
545 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
546 static const int feat_codes[] = {211, 0};
547 static const int opts_codes[] = {200, 202, 451, 0};
548
552 }
553
556 if (
ret == 200 ||
ret == 202)
558 }
559
560 return 0;
561 }
562
564 {
566 int err;
569 static const int connect_codes[] = {220, 0};
570
571 if (!
s->conn_control) {
573 s->hostname,
s->server_control_port,
NULL);
574 if (
s->rw_timeout != -1) {
576 } /* if option is not given, don't pass it and let tcp use its own default */
578 &
h->interrupt_callback, &
opts,
579 h->protocol_whitelist,
h->protocol_blacklist,
h);
581 if (err < 0) {
583 return err;
584 }
585
586 /* check if server is ready */
590 }
591
593 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.");
594 }
596
599 return err;
600 }
601
604 return err;
605 }
606
608 }
609 return 0;
610 }
611
613 {
614 int err;
618
620 /* Enter passive mode */
622 /* Use PASV as fallback */
624 return err;
625 }
626 /* Open data connection */
628 if (
s->rw_timeout != -1) {
630 } /* if option is not given, don't pass it and let tcp use its own default */
632 &
h->interrupt_callback, &
opts,
633 h->protocol_whitelist,
h->protocol_blacklist,
h);
635 if (err < 0)
636 return err;
637
640 return err;
641 }
643 return 0;
644 }
645
647 {
648 static const char *
command =
"ABOR\r\n";
649 int err;
650 static const int abor_codes[] = {225, 226, 0};
652
653 /* According to RCF 959:
654 "ABOR command tells the server to abort the previous FTP
655 service command and any associated transfer of data."
656
657 There are FTP server implementations that don't response
658 to any commands during data transfer in passive mode (including ABOR).
659
660 This implementation closes data connection by force.
661 */
662
667 return err;
668 }
669 } else {
672 /* wu-ftpd also closes control connection after data connection closing */
676 return err;
677 }
678 }
679 }
680
681 return 0;
682 }
683
685 {
687 const char *tok_user =
NULL, *tok_pass =
NULL;
688 char *newpath =
NULL;
689 int err;
691
697
699 credentials, sizeof(credentials),
700 hostname, sizeof(hostname),
701 &
s->server_control_port,
702 path, sizeof(path),
703 url);
704
705 if (!*credentials) {
706 if (!
s->option_user) {
707 tok_user = "anonymous";
708 tok_pass =
av_x_if_null(
s->anonymous_password,
"nopassword");
709 } else {
710 tok_user =
s->option_user;
711 tok_pass =
s->option_password;
712 }
715 } else {
716 char *
pass = strchr(credentials,
':');
721 } else {
722 tok_pass =
s->option_password;
724 }
726 }
728 if (!
s->hostname || !
s->user || (tok_pass && !
s->password)) {
730 }
731
732 if (
s->server_control_port < 0 ||
s->server_control_port > 65535)
733 s->server_control_port = 21;
734
736 return err;
737
739 return err;
740
742 if (!newpath)
746
747 return 0;
748 }
749
751 {
753 int err;
754
756
759
762 } else {
766 }
767
768 return 0;
769
773 return err;
774 }
775
777 {
779 int err;
780 int64_t new_pos;
781
782 ff_dlog(
h,
"ftp protocol seek %"PRId64
" %d\n",
pos, whence);
783
784 switch(whence) {
787 case SEEK_SET:
789 break;
790 case SEEK_CUR:
791 new_pos =
s->position +
pos;
792 break;
793 case SEEK_END:
796 new_pos =
s->filesize +
pos;
797 break;
798 default:
800 }
801
804
805 if (new_pos < 0) {
808 }
809
810 if (new_pos !=
s->position) {
812 return err;
813 s->position = new_pos;
814 }
815 return new_pos;
816 }
817
819 {
821 int read, err, retry_done = 0;
822
824 retry:
829 return err;
830 }
833 return err;
834 }
839 s->filesize =
FFMAX(
s->filesize,
s->position);
840 }
842 static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
843 char *response =
NULL;
845 if (err == 226) {
850 }
851 /* 250 is not allowed, any other status means some kind of error */
855 }
856 if (read <= 0 && !h->is_streamed) {
857 /* Server closed connection. Probably due to inactivity */
860 return err;
861 if (!retry_done) {
862 retry_done = 1;
863 goto retry;
864 }
865 }
867 }
868
871 }
872
874 {
875 int err;
877 int written;
878
880
883 return err;
884 }
887 return err;
888 }
891 if (written > 0) {
892 s->position += written;
893 s->filesize =
FFMAX(
s->filesize,
s->position);
894 }
895 return written;
896 }
897
900 }
901
903 {
905
907
914
915 return 0;
916 }
917
919 {
921
922 ff_dlog(
h,
"ftp protocol get_file_handle\n");
923
926
928 }
929
931 {
933
934 ff_dlog(
h,
"ftp protocol shutdown\n");
935
938
940 }
941
943 {
946
956 if (!
s->dir_buffer) {
959 }
960 s->dir_buffer[0] = 0;
962 return 0;
967 }
968
970 {
971 struct tm tv;
972 memset(&tv, 0, sizeof(struct tm));
974 return INT64_C(1000000) *
av_timegm(&tv);
975 }
976
978 {
980 return 0;
981 }
982
984 {
986 char *saveptr =
NULL, *p = mlsd;
990 if (
fact[0] ==
' ') {
992 continue;
993 }
996 continue;
999 return 1;
1016 }
1017 return 0;
1018 }
1019
1020 /**
1021 * @return 0 on success, negative on error, positive on entry to discard.
1022 */
1024 {
1026
1027 switch (
s->listing_method) {
1033 default:
1034 return -1;
1035 }
1036 }
1037
1039 {
1041 char *start, *found;
1043
1044 do {
1045 retried = 0;
1046 start =
s->dir_buffer +
s->dir_buffer_offset;
1047 while (!(found = strstr(start, "\n"))) {
1048 if (retried)
1050 s->dir_buffer_size -=
s->dir_buffer_offset;
1051 s->dir_buffer_offset = 0;
1052 if (
s->dir_buffer_size)
1053 memmove(
s->dir_buffer, start,
s->dir_buffer_size);
1059 return 0;
1060 }
1061 s->dir_buffer_size +=
ret;
1062 s->dir_buffer[
s->dir_buffer_size] = 0;
1063 start =
s->dir_buffer;
1064 retried = 1;
1065 }
1066 s->dir_buffer_offset += (found + 1 - start);
1067 found[0] = 0;
1068 if (found > start && found[-1] == '\r')
1069 found[-1] = 0;
1070
1072 if (!*next)
1074 (*next)->utf8 =
s->utf8;
1080 }
1082 return 0;
1083 }
1084
1086 {
1091 return 0;
1092 }
1093
1095 {
1098 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1099 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1101
1104
1109 }
1110
1114 }
1115
1120 }
1121
1124 else
1126
1130 }
1131
1133 {
1136 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1137 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1139
1142
1147 }
1148
1152 }
1153
1155 path, sizeof(path),
1161 }
1162
1165 else
1167
1171 }
1172
1190 .default_whitelist = "tcp",
1191 };