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>
23
33
34 #define CONTROL_BUFFER_SIZE 1024
35 #define DIR_BUFFER_SIZE 4096
36
46
52
62 char *
user;
/**< Server user */
64 char *
path;
/**< Path to resource on server. */
68 const char *
anonymous_password;
/**< Password to be used for anonymous user. An email should be used. */
72 char *
features;
/**< List of server's features represented as raw response */
77 const char *
option_user;
/**< User to be used if none given in the URL */
80
81 #define OFFSET(x) offsetof(FTPContext, x)
82 #define D AV_OPT_FLAG_DECODING_PARAM
83 #define E AV_OPT_FLAG_ENCODING_PARAM
85 {
"timeout",
"set timeout of socket I/O operations",
OFFSET(rw_timeout),
AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX,
D|
E },
86 {
"ftp-write-seekable",
"control seekability of connection during encoding",
OFFSET(write_seekable),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
E },
87 {
"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 },
88 {
"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 },
89 {
"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 };
92
98 };
99
101
103 {
105 if (
s->control_buf_ptr >=
s->control_buf_end) {
110 return -1;
111 } else {
112 s->control_buf_ptr =
s->control_buffer;
113 s->control_buf_end =
s->control_buffer +
len;
114 }
115 }
116 return *
s->control_buf_ptr++;
117 }
118
120 {
121 int ch;
123
124 for (;;) {
126 if (ch < 0) {
127 return ch;
128 }
129 if (ch == '\n') {
130 /* process line */
131 if (q >
line && q[-1] ==
'\r')
132 q--;
133 *q = '0円';
134 return 0;
135 } else {
136 if ((q -
line) < line_size - 1)
137 *q++ = ch;
138 }
139 }
140 }
141
142 /*
143 * This routine returns ftp server response code.
144 * Server may send more than one response for a certain command.
145 * First expected code is returned.
146 */
148 {
149 int err,
i, dash = 0,
result = 0, code_found = 0, linesize;
151 AVBPrint line_buffer;
152
155
156 while (!code_found || dash) {
160 return err;
161 }
162
164
165 linesize = strlen(buf);
166 err = 0;
167 if (linesize >= 3) {
168 for (
i = 0;
i < 3; ++
i) {
169 if (buf[
i] <
'0' || buf[
i] >
'9') {
170 err = 0;
171 break;
172 }
173 err *= 10;
175 }
176 }
177
178 if (!code_found) {
179 if (err >= 500) {
180 code_found = 1;
182 } else
183 for (
i = 0; response_codes[
i]; ++
i) {
184 if (err == response_codes[
i]) {
185 code_found = 1;
187 break;
188 }
189 }
190 }
191 if (code_found) {
194 if (linesize >= 4) {
195 if (!dash && buf[3] == '-')
196 dash = err;
197 else if (err == dash && buf[3] == ' ')
198 dash = 0;
199 }
200 }
201 }
202
206 }
207
209 const int response_codes[], char **response)
210 {
211 int err;
212
214
215 if (response)
217
218 if (!
s->conn_control)
220
222 return err;
223 if (!err)
224 return -1;
225
226 /* return status */
227 if (response_codes) {
229 }
230 return 0;
231 }
232
234 {
237 }
238
240 {
243 }
244
246 {
248 int err;
249 static const int user_codes[] = {331, 230, 0};
250 static const int pass_codes[] = {230, 0};
251
252 if (strpbrk(
s->user,
"\r\n"))
254 err =
snprintf(buf,
sizeof(buf),
"USER %s\r\n",
s->user);
255 if (err >= sizeof(buf))
257
259 if (err == 331) {
261 if (strpbrk(
s->password,
"\r\n"))
263 err =
snprintf(buf,
sizeof(buf),
"PASS %s\r\n",
s->password);
264 if (err >= sizeof(buf))
266
268 } else
270 }
271 if (err != 230)
273
274 return 0;
275 }
276
278 {
281 static const char d =
'|';
282 static const char *
command =
"EPSV\r\n";
283 static const int epsv_codes[] = {229, 0};
284
287
288 for (
i = 0; res[
i]; ++
i) {
291 }
else if (res[
i] ==
')') {
293 break;
294 }
295 }
296 if (!start || !end)
298
299 *end = '0円';
300 if (strlen(start) < 5)
302 if (start[0] !=
d || start[1] !=
d || start[2] !=
d || end[-1] !=
d)
304 start += 3;
305 end[-1] = '0円';
306
307 s->server_data_port = atoi(start);
308 ff_dlog(
s,
"Server data port: %d\n",
s->server_data_port);
309
311 return 0;
312
315 s->server_data_port = -1;
317 }
318
320 {
323 static const char *
command =
"PASV\r\n";
324 static const int pasv_codes[] = {227, 0};
325
328
329 for (
i = 0; res[
i]; ++
i) {
332 }
else if (res[
i] ==
')') {
334 break;
335 }
336 }
337 if (!start || !end)
339
340 *end = '0円';
341 /* skip ip */
346
347 /* parse port number */
349 if (!start)
goto fail;
350 s->server_data_port = atoi(start) * 256;
352 if (!start)
goto fail;
353 s->server_data_port += atoi(start);
354 ff_dlog(
s,
"Server data port: %d\n",
s->server_data_port);
355
357 return 0;
358
361 s->server_data_port = -1;
363 }
364
366 {
369 static const char *
command =
"PWD\r\n";
370 static const int pwd_codes[] = {257, 0};
371
374
375 for (
i = 0; res[
i]; ++
i) {
377 if (!start) {
379 continue;
380 }
382 break;
383 }
384 }
385
386 if (!end)
388
389 *end = '0円';
391
393
396 return 0;
397
401 }
402
404 {
408 static const int size_codes[] = {213, 0};
409
413
415 s->filesize = strtoll(&res[4],
NULL, 10);
416 } else {
420 }
421
423 return 0;
424 }
425
427 {
429 static const int retr_codes[] = {150, 125, 0};
431
435
437 if (resp_code != 125 && resp_code != 150)
439
441
442 return 0;
443 }
444
446 {
448 static const int stor_codes[] = {150, 125, 0};
450
454
456 if (resp_code != 125 && resp_code != 150)
458
460
461 return 0;
462 }
463
465 {
466 static const char *
command =
"TYPE I\r\n";
467 static const int type_codes[] = {200, 0};
468
471
472 return 0;
473 }
474
476 {
478 static const int rest_codes[] = {350, 0};
479
483
484 return 0;
485 }
486
488 {
489 static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
492
496
499 return 0;
500 }
501
503 {
504 static const char *
command =
"MLSD\r\n";
505 static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
506
509 s->listing_method =
MLSD;
510 return 0;
511 }
512
514 {
515 static const char *
command =
"NLST\r\n";
516 static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
517
520 s->listing_method =
NLST;
521 return 0;
522 }
523
525 {
528
531
533 }
534
536 {
538 return 0;
539
541 }
542
544 {
545 static const char *feat_command = "FEAT\r\n";
546 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
547 static const int feat_codes[] = {211, 0};
548 static const int opts_codes[] = {200, 202, 451, 0};
549
553 }
554
557 if (
ret == 200 ||
ret == 202)
559 }
560
561 return 0;
562 }
563
565 {
567 int err;
570 static const int connect_codes[] = {220, 0};
571
572 if (!
s->conn_control) {
574 s->hostname,
s->server_control_port,
NULL);
575 if (
s->rw_timeout != -1) {
577 } /* if option is not given, don't pass it and let tcp use its own default */
579 &
h->interrupt_callback, &
opts,
580 h->protocol_whitelist,
h->protocol_blacklist,
h);
582 if (err < 0) {
584 return err;
585 }
586
587 /* check if server is ready */
591 }
592
594 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.");
595 }
597
600 return err;
601 }
602
605 return err;
606 }
607
609 }
610 return 0;
611 }
612
614 {
615 int err;
619
621 /* Enter passive mode */
623 /* Use PASV as fallback */
625 return err;
626 }
627 /* Open data connection */
629 if (
s->rw_timeout != -1) {
631 } /* if option is not given, don't pass it and let tcp use its own default */
633 &
h->interrupt_callback, &
opts,
634 h->protocol_whitelist,
h->protocol_blacklist,
h);
636 if (err < 0)
637 return err;
638
641 return err;
642 }
644 return 0;
645 }
646
648 {
649 static const char *
command =
"ABOR\r\n";
650 int err;
651 static const int abor_codes[] = {225, 226, 0};
653
654 /* According to RCF 959:
655 "ABOR command tells the server to abort the previous FTP
656 service command and any associated transfer of data."
657
658 There are FTP server implementations that don't response
659 to any commands during data transfer in passive mode (including ABOR).
660
661 This implementation closes data connection by force.
662 */
663
668 return err;
669 }
670 } else {
673 /* wu-ftpd also closes control connection after data connection closing */
677 return err;
678 }
679 }
680 }
681
682 return 0;
683 }
684
686 {
688 const char *tok_user =
NULL, *tok_pass =
NULL;
689 char *newpath =
NULL;
690 int err;
692
698
700 credentials, sizeof(credentials),
701 hostname, sizeof(hostname),
702 &
s->server_control_port,
703 path, sizeof(path),
704 url);
705
706 if (!*credentials) {
707 if (!
s->option_user) {
708 tok_user = "anonymous";
709 tok_pass =
av_x_if_null(
s->anonymous_password,
"nopassword");
710 } else {
711 tok_user =
s->option_user;
712 tok_pass =
s->option_password;
713 }
716 } else {
717 char *pass = strchr(credentials, ':');
718 if (pass) {
719 *pass++ = '0円';
720 tok_pass = pass;
722 } else {
723 tok_pass =
s->option_password;
725 }
727 }
729 if (!
s->hostname || !
s->user || (tok_pass && !
s->password)) {
731 }
732
733 if (
s->server_control_port < 0 ||
s->server_control_port > 65535)
734 s->server_control_port = 21;
735
737 return err;
738
740 return err;
741
743 if (!newpath)
747
748 return 0;
749 }
750
752 {
754 int err;
755
757
760
763 } else {
767 }
768
769 return 0;
770
774 return err;
775 }
776
778 {
780 int err;
782
783 ff_dlog(
h,
"ftp protocol seek %"PRId64
" %d\n",
pos, whence);
784
785 switch(whence) {
788 case SEEK_SET:
790 break;
791 case SEEK_CUR:
792 new_pos =
s->position +
pos;
793 break;
794 case SEEK_END:
797 new_pos =
s->filesize +
pos;
798 break;
799 default:
801 }
802
805
806 if (new_pos < 0) {
809 }
810
811 if (new_pos !=
s->position) {
813 return err;
814 s->position = new_pos;
815 }
816 return new_pos;
817 }
818
820 {
822 int read, err, retry_done = 0;
823
825 retry:
830 return err;
831 }
834 return err;
835 }
840 s->filesize =
FFMAX(
s->filesize,
s->position);
841 }
843 static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
844 char *response =
NULL;
846 if (err == 226) {
851 }
852 /* 250 is not allowed, any other status means some kind of error */
856 }
857 if (read <= 0 && !h->is_streamed) {
858 /* Server closed connection. Probably due to inactivity */
861 return err;
862 if (!retry_done) {
863 retry_done = 1;
864 goto retry;
865 }
866 }
868 }
869
872 }
873
875 {
876 int err;
878 int written;
879
881
884 return err;
885 }
888 return err;
889 }
892 if (written > 0) {
893 s->position += written;
894 s->filesize =
FFMAX(
s->filesize,
s->position);
895 }
896 return written;
897 }
898
901 }
902
904 {
906
908
915
916 return 0;
917 }
918
920 {
922
923 ff_dlog(
h,
"ftp protocol get_file_handle\n");
924
927
929 }
930
932 {
934
935 ff_dlog(
h,
"ftp protocol shutdown\n");
936
939
941 }
942
944 {
947
957 if (!
s->dir_buffer) {
960 }
961 s->dir_buffer[0] = 0;
963 return 0;
968 }
969
971 {
972 struct tm tv;
973 memset(&tv, 0, sizeof(struct tm));
975 return INT64_C(1000000) *
av_timegm(&tv);
976 }
977
979 {
981 return 0;
982 }
983
985 {
987 char *saveptr =
NULL, *p = mlsd;
991 if (
fact[0] ==
' ') {
993 continue;
994 }
997 continue;
1000 return 1;
1017 }
1018 return 0;
1019 }
1020
1021 /**
1022 * @return 0 on success, negative on error, positive on entry to discard.
1023 */
1025 {
1027
1028 switch (
s->listing_method) {
1034 default:
1035 return -1;
1036 }
1037 }
1038
1040 {
1042 char *start, *found;
1044
1045 do {
1046 retried = 0;
1047 start =
s->dir_buffer +
s->dir_buffer_offset;
1048 while (!(found = strstr(start, "\n"))) {
1049 if (retried)
1051 s->dir_buffer_size -=
s->dir_buffer_offset;
1052 s->dir_buffer_offset = 0;
1053 if (
s->dir_buffer_size)
1054 memmove(
s->dir_buffer, start,
s->dir_buffer_size);
1060 return 0;
1061 }
1062 s->dir_buffer_size +=
ret;
1063 s->dir_buffer[
s->dir_buffer_size] = 0;
1064 start =
s->dir_buffer;
1065 retried = 1;
1066 }
1067 s->dir_buffer_offset += (found + 1 - start);
1068 found[0] = 0;
1069 if (found > start && found[-1] == '\r')
1070 found[-1] = 0;
1071
1073 if (!*next)
1075 (*next)->utf8 =
s->utf8;
1081 }
1083 return 0;
1084 }
1085
1087 {
1092 return 0;
1093 }
1094
1096 {
1099 static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1100 static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1102
1105
1110 }
1111
1115 }
1116
1121 }
1122
1125 else
1127
1131 }
1132
1134 {
1137 static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1138 static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1140
1143
1148 }
1149
1153 }
1154
1156 path, sizeof(path),
1162 }
1163
1166 else
1168
1172 }
1173
1191 .default_whitelist = "tcp",
1192 };