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
27
28 #define CONTROL_BUFFER_SIZE 1024
29 #define CREDENTIALS_BUFFER_SIZE 128
30
38
47 char hostname[512];
/**< Server address. */
50 int64_t
filesize;
/**< Size of file on server, -1 on error. */
51 int64_t
position;
/**< Current position, calculated. */
53 const char *
anonymous_password;
/**< Password to be used for anonymous user. An email should be used. */
57
58 #define OFFSET(x) offsetof(FTPContext, x)
59 #define D AV_OPT_FLAG_DECODING_PARAM
60 #define E AV_OPT_FLAG_ENCODING_PARAM
62 {
"timeout",
"set timeout of socket I/O operations",
OFFSET(rw_timeout),
AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX,
D|
E },
63 {
"ftp-write-seekable",
"control seekability of connection during encoding",
OFFSET(write_seekable),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1,
E },
64 {
"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 },
66 };
67
73 };
74
76 {
80 if (len < 0) {
82 } else if (!len) {
83 return -1;
84 } else {
87 }
88 }
90 }
91
93 {
94 int ch;
95 char *q = line;
96
97 for (;;) {
99 if (ch < 0) {
100 return ch;
101 }
102 if (ch == '\n') {
103 /* process line */
104 if (q > line && q[-1] == '\r')
105 q--;
106 *q = '0円';
107 return 0;
108 } else {
109 if ((q - line) < line_size - 1)
110 *q++ = ch;
111 }
112 }
113 }
114
115 /*
116 * This routine returns ftp server response code.
117 * Server may send more than one response for a certain command.
118 * First expected code is returned.
119 */
121 {
122 int err, i, dash = 0, result = 0, code_found = 0, linesize;
124 AVBPrint line_buffer;
125
126 if (line)
128
129 while (!code_found || dash) {
131 if (line)
133 return err;
134 }
135
137
138 linesize = strlen(buf);
139 err = 0;
140 if (linesize >= 3) {
141 for (i = 0; i < 3; ++i) {
142 if (buf[i] < '0' || buf[i] > '9') {
143 err = 0;
144 break;
145 }
146 err *= 10;
147 err += buf[i] - '0';
148 }
149 }
150
151 if (!code_found) {
152 if (err >= 500) {
153 code_found = 1;
154 result = err;
155 } else
156 for (i = 0; response_codes[i]; ++i) {
157 if (err == response_codes[i]) {
158 code_found = 1;
159 result = err;
160 break;
161 }
162 }
163 }
164 if (code_found) {
165 if (line)
167 if (linesize >= 4) {
168 if (!dash && buf[3] == '-')
169 dash = err;
170 else if (err == dash && buf[3] == ' ')
171 dash = 0;
172 }
173 }
174 }
175
176 if (line)
178 return result;
179 }
180
182 const int response_codes[], char **response)
183 {
184 int err;
185
186 if (response)
188
190 return err;
191 if (!err)
192 return -1;
193
194 /* return status */
195 if (response_codes) {
196 return ftp_status(s, response, response_codes);
197 }
198 return 0;
199 }
200
202 {
206 }
207
209 {
212 }
213
215 {
218 int err;
219 static const int user_codes[] = {331, 230, 0};
220 static const int pass_codes[] = {230, 0};
221
222 /* Authentication may be repeated, original string has to be saved */
224
225 user =
av_strtok(credencials,
":", &end);
227
228 if (!user) {
229 user = "anonymous";
231 }
232
233 snprintf(buf,
sizeof(buf),
"USER %s\r\n", user);
235 if (err == 331) {
236 if (pass) {
237 snprintf(buf,
sizeof(buf),
"PASS %s\r\n", pass);
239 } else
241 }
242 if (err != 230)
244
245 return 0;
246 }
247
249 {
251 int i;
252 static const char d = '|';
253 static const char *
command =
"EPSV\r\n";
254 static const int epsv_codes[] = {229, 0};
255
257 goto fail;
258
259 for (i = 0; res[i]; ++i) {
260 if (res[i] == '(') {
261 start = res + i + 1;
262 } else if (res[i] == ')') {
263 end = res + i;
264 break;
265 }
266 }
267 if (!start || !end)
268 goto fail;
269
270 *end = '0円';
271 if (strlen(start) < 5)
272 goto fail;
273 if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
274 goto fail;
275 start += 3;
276 end[-1] = '0円';
277
280
282 return 0;
283
284 fail:
288 }
289
291 {
293 int i;
294 static const char *
command =
"PASV\r\n";
295 static const int pasv_codes[] = {227, 0};
296
298 goto fail;
299
300 for (i = 0; res[i]; ++i) {
301 if (res[i] == '(') {
302 start = res + i + 1;
303 } else if (res[i] == ')') {
304 end = res + i;
305 break;
306 }
307 }
308 if (!start || !end)
309 goto fail;
310
311 *end = '0円';
312 /* skip ip */
313 if (!
av_strtok(start,
",", &end))
goto fail;
314 if (!
av_strtok(end,
",", &end))
goto fail;
315 if (!
av_strtok(end,
",", &end))
goto fail;
316 if (!
av_strtok(end,
",", &end))
goto fail;
317
318 /* parse port number */
320 if (!start) goto fail;
323 if (!start) goto fail;
326
328 return 0;
329
330 fail:
334 }
335
337 {
339 int i;
340 static const char *
command =
"PWD\r\n";
341 static const int pwd_codes[] = {257, 0};
342
344 goto fail;
345
346 for (i = 0; res[i]; ++i) {
347 if (res[i] == '"') {
348 if (!start) {
349 start = res + i + 1;
350 continue;
351 }
352 end = res + i;
353 break;
354 }
355 }
356
357 if (!end)
358 goto fail;
359
360 if (end > res && end[-1] == '/') {
361 end[-1] = '0円';
362 } else
363 *end = '0円';
365
367 return 0;
368
369 fail:
372 }
373
375 {
378 static const int size_codes[] = {213, 0};
379
380 snprintf(command,
sizeof(command),
"SIZE %s\r\n", s->
path);
383 } else {
387 }
388
390 return 0;
391 }
392
394 {
396 static const int retr_codes[] = {150, 0};
397
398 snprintf(command,
sizeof(command),
"RETR %s\r\n", s->
path);
401
403
404 return 0;
405 }
406
408 {
410 static const int stor_codes[] = {150, 0};
411
412 snprintf(command,
sizeof(command),
"STOR %s\r\n", s->
path);
415
417
418 return 0;
419 }
420
422 {
423 static const char *
command =
"TYPE I\r\n";
424 static const int type_codes[] = {200, 0};
425
428
429 return 0;
430 }
431
433 {
435 static const int rest_codes[] = {350, 0};
436
437 snprintf(command,
sizeof(command),
"REST %"PRId64
"\r\n", pos);
440
441 return 0;
442 }
443
445 {
446 static const char *feat_command = "FEAT\r\n";
447 static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
448 static const int feat_codes[] = {211, 0};
449 static const int opts_codes[] = {200, 451};
451
455 }
457
458 return 0;
459 }
460
462 {
464 int err;
467 static const int connect_codes[] = {220, 0};
468
474 } /* if option is not given, don't pass it and let tcp use its own default */
478 if (err < 0) {
480 return err;
481 }
482
483 /* check if server is ready */
487 }
488
490 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.");
491 }
493
496 return err;
497 }
498
501 return err;
502 }
503
505 }
506 return 0;
507 }
508
510 {
511 int err;
515
517 /* Enter passive mode */
519 /* Use PASV as fallback */
521 return err;
522 }
523 /* Open data connection */
527 } /* if option is not given, don't pass it and let tcp use its own default */
531 if (err < 0)
532 return err;
533
536 return err;
537 }
539 return 0;
540 }
541
543 {
544 static const char *
command =
"ABOR\r\n";
545 int err;
546 static const int abor_codes[] = {225, 226, 0};
548
549 /* According to RCF 959:
550 "ABOR command tells the server to abort the previous FTP
551 service command and any associated transfer of data."
552
553 There are FTP server implementations that don't response
554 to any commands during data transfer in passive mode (including ABOR).
555
556 This implementation closes data connection by force.
557 */
558
563 return err;
564 }
565 } else {
568 /* wu-ftpd also closes control connection after data connection closing */
572 return err;
573 }
574 }
575 }
576
577 return 0;
578 }
579
581 {
583 int err;
585
586 av_dlog(h,
"ftp protocol open\n");
587
591
596 path, sizeof(path),
597 url);
598
601
603 goto fail;
604
606 goto fail;
608
611 } else {
616 }
617
618 return 0;
619
620 fail:
624 return err;
625 }
626
628 {
630 int err;
631 int64_t new_pos, fake_pos;
632
633 av_dlog(h,
"ftp protocol seek %"PRId64
" %d\n", pos, whence);
634
635 switch(whence) {
638 case SEEK_SET:
639 new_pos = pos;
640 break;
641 case SEEK_CUR:
643 break;
644 case SEEK_END:
648 break;
649 default:
651 }
652
655
656 if (new_pos < 0) {
659 }
660
664 return err;
666 }
667 return new_pos;
668 }
669
671 {
673 int read, err, retry_done = 0;
674
675 av_dlog(h,
"ftp protocol read %d bytes\n", size);
676 retry:
678 /* optimization */
680 return 0;
682 return err;
683 }
686 return 0;
688 return err;
689 }
692 if (read >= 0) {
695 /* server will terminate, but keep current position to avoid madness */
696 /* save position to restart from it */
701 }
703 }
704 }
705 if (read <= 0 && s->position < s->filesize && !h->
is_streamed) {
706 /* Server closed connection. Probably due to inactivity */
710 return err;
711 if ((err =
ftp_seek(h, pos, SEEK_SET)) < 0) {
713 return err;
714 }
715 if (!retry_done) {
716 retry_done = 1;
717 goto retry;
718 }
719 }
720 return read;
721 }
722
725 }
726
728 {
729 int err;
731 int written;
732
733 av_dlog(h,
"ftp protocol write %d bytes\n", size);
734
737 return err;
738 }
741 return err;
742 }
745 if (written > 0) {
748 }
749 return written;
750 }
751
754 }
755
757 {
758 av_dlog(h,
"ftp protocol close\n");
759
761
762 return 0;
763 }
764
766 {
768
769 av_dlog(h,
"ftp protocol get_file_handle\n");
770
773
775 }
776
778 {
780
781 av_dlog(h,
"ftp protocol shutdown\n");
782
785
787 }
788
799 .priv_data_class = &ftp_context_class,
801 };