ftp: warning about pure-ftp server used as and output
[ffmpeg.git] / libavformat / ftp.c
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 "libavutil/avstring.h"
22 #include "avformat.h"
23 #include "internal.h"
24 #include "url.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/bprint.h"
27
28 #define CONTROL_BUFFER_SIZE 1024
29 #define CREDENTIALS_BUFFER_SIZE 128
30
31 typedef enum {
32     UNKNOWN,
33     READY,
34     DOWNLOADING,
35     UPLOADING,
36     DISCONNECTED
37 } FTPState;
38
39 typedef struct {
40     const AVClass *class;
41     URLContext *conn_control;                    /**< Control connection */
42     URLContext *conn_data;                       /**< Data connection, NULL when not connected */
43     uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
44     uint8_t *control_buf_ptr, *control_buf_end;
45     int server_data_port;                        /**< Data connection port opened by server, -1 on error. */
46     int server_control_port;                     /**< Control connection port, default is 21 */
47     char hostname[512];                          /**< Server address. */
48     char credencials[CREDENTIALS_BUFFER_SIZE];   /**< Authentication data */
49     char path[MAX_URL_SIZE];                     /**< Path to resource on server. */
50     int64_t filesize;                            /**< Size of file on server, -1 on error. */
51     int64_t position;                            /**< Current position, calculated. */
52     int rw_timeout;                              /**< Network timeout. */
53     const char *anonymous_password;              /**< Password to be used for anonymous user. An email should be used. */
54     int write_seekable;                          /**< Control seekability, 0 = disable, 1 = enable. */
55     FTPState state;                              /**< State of data connection */
56 } FTPContext;
57
58 #define OFFSET(x) offsetof(FTPContext, x)
59 #define D AV_OPT_FLAG_DECODING_PARAM
60 #define E AV_OPT_FLAG_ENCODING_PARAM
61 static const AVOption options[] = {
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 },
65     {NULL}
66 };
67
68 static const AVClass ftp_context_class = {
69     .class_name     = "ftp",
70     .item_name      = av_default_item_name,
71     .option         = options,
72     .version        = LIBAVUTIL_VERSION_INT,
73 };
74
75 static int ftp_getc(FTPContext *s)
76 {
77     int len;
78     if (s->control_buf_ptr >= s->control_buf_end) {
79         len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
80         if (len < 0) {
81             return len;
82         } else if (!len) {
83             return -1;
84         } else {
85             s->control_buf_ptr = s->control_buffer;
86             s->control_buf_end = s->control_buffer + len;
87         }
88     }
89     return *s->control_buf_ptr++;
90 }
91
92 static int ftp_get_line(FTPContext *s, char *line, int line_size)
93 {
94     int ch;
95     char *q = line;
96
97     for (;;) {
98         ch = ftp_getc(s);
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  */
120 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
121 {
122     int err, i, dash = 0, result = 0, code_found = 0;
123     char buf[CONTROL_BUFFER_SIZE];
124     AVBPrint line_buffer;
125
126     if (line)
127         av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
128
129     while (!code_found || dash) {
130         if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
131             av_bprint_finalize(&line_buffer, NULL);
132             return err;
133         }
134
135         av_log(s, AV_LOG_DEBUG, "%s\n", buf);
136
137         if (strlen(buf) < 4)
138             continue;
139
140         err = 0;
141         for (i = 0; i < 3; ++i) {
142             if (buf[i] < '0' || buf[i] > '9')
143                 continue;
144             err *= 10;
145             err += buf[i] - '0';
146         }
147         dash = !!(buf[3] == '-');
148
149         for (i = 0; response_codes[i]; ++i) {
150             if (err == response_codes[i]) {
151                 if (line)
152                     av_bprintf(&line_buffer, "%s", buf);
153                 code_found = 1;
154                 result = err;
155                 break;
156             }
157         }
158     }
159
160     if (line)
161         av_bprint_finalize(&line_buffer, line);
162     return result;
163 }
164
165 static int ftp_send_command(FTPContext *s, const char *command,
166                             const int response_codes[], char **response)
167 {
168     int err;
169
170     if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
171         return err;
172     if (!err)
173         return -1;
174
175     /* return status */
176     if (response_codes) {
177         return ftp_status(s, response, response_codes);
178     }
179     return 0;
180 }
181
182 static void ftp_close_data_connection(FTPContext *s)
183 {
184     ffurl_closep(&s->conn_data);
185     s->position = 0;
186     s->state = DISCONNECTED;
187 }
188
189 static void ftp_close_both_connections(FTPContext *s)
190 {
191     ffurl_closep(&s->conn_control);
192     ftp_close_data_connection(s);
193 }
194
195 static int ftp_auth(FTPContext *s)
196 {
197     const char *user = NULL, *pass = NULL;
198     char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
199     int err;
200     const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
201     const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
202
203     /* Authentication may be repeated, original string has to be saved */
204     av_strlcpy(credencials, s->credencials, sizeof(credencials));
205
206     user = av_strtok(credencials, ":", &end);
207     pass = av_strtok(end, ":", &end);
208
209     if (!user) {
210         user = "anonymous";
211         pass = s->anonymous_password ? s->anonymous_password : "nopassword";
212     }
213
214     snprintf(buf, sizeof(buf), "USER %s\r\n", user);
215     err = ftp_send_command(s, buf, user_codes, NULL);
216     if (err == 331) {
217         if (pass) {
218             snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
219             err = ftp_send_command(s, buf, pass_codes, NULL);
220         } else
221             return AVERROR(EACCES);
222     }
223     if (err != 230)
224         return AVERROR(EACCES);
225
226     return 0;
227 }
228
229 static int ftp_passive_mode(FTPContext *s)
230 {
231     char *res = NULL, *start = NULL, *end = NULL;
232     int i;
233     const char *command = "PASV\r\n";
234     const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
235
236     if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
237         goto fail;
238
239     for (i = 0; res[i]; ++i) {
240         if (res[i] == '(') {
241             start = res + i + 1;
242         } else if (res[i] == ')') {
243             end = res + i;
244             break;
245         }
246     }
247     if (!start || !end)
248         goto fail;
249
250     *end  = '\0';
251     /* skip ip */
252     if (!av_strtok(start, ",", &end)) goto fail;
253     if (!av_strtok(end, ",", &end)) goto fail;
254     if (!av_strtok(end, ",", &end)) goto fail;
255     if (!av_strtok(end, ",", &end)) goto fail;
256
257     /* parse port number */
258     start = av_strtok(end, ",", &end);
259     if (!start) goto fail;
260     s->server_data_port = atoi(start) * 256;
261     start = av_strtok(end, ",", &end);
262     if (!start) goto fail;
263     s->server_data_port += atoi(start);
264     av_dlog(s, "Server data port: %d\n", s->server_data_port);
265
266     av_free(res);
267     return 0;
268
269   fail:
270     av_free(res);
271     s->server_data_port = -1;
272     return AVERROR(EIO);
273 }
274
275 static int ftp_current_dir(FTPContext *s)
276 {
277     char *res = NULL, *start = NULL, *end = NULL;
278     int i;
279     const char *command = "PWD\r\n";
280     const int pwd_codes[] = {257, 0};
281
282     if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
283         goto fail;
284
285     for (i = 0; res[i]; ++i) {
286         if (res[i] == '"') {
287             if (!start) {
288                 start = res + i + 1;
289                 continue;
290             }
291             end = res + i;
292             break;
293         }
294     }
295
296     if (!end)
297         goto fail;
298
299     if (end > res && end[-1] == '/') {
300         end[-1] = '\0';
301     } else
302         *end = '\0';
303     av_strlcpy(s->path, start, sizeof(s->path));
304
305     av_free(res);
306     return 0;
307
308   fail:
309     av_free(res);
310     return AVERROR(EIO);
311 }
312
313 static int ftp_file_size(FTPContext *s)
314 {
315     char command[CONTROL_BUFFER_SIZE];
316     char *res = NULL;
317     const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
318
319     snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
320     if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
321         s->filesize = strtoll(&res[4], NULL, 10);
322     } else {
323         s->filesize = -1;
324         av_free(res);
325         return AVERROR(EIO);
326     }
327
328     av_free(res);
329     return 0;
330 }
331
332 static int ftp_retrieve(FTPContext *s)
333 {
334     char command[CONTROL_BUFFER_SIZE];
335     const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
336
337     snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
338     if (ftp_send_command(s, command, retr_codes, NULL) != 150)
339         return AVERROR(EIO);
340
341     s->state = DOWNLOADING;
342
343     return 0;
344 }
345
346 static int ftp_store(FTPContext *s)
347 {
348     char command[CONTROL_BUFFER_SIZE];
349     const int stor_codes[] = {150, 0};
350
351     snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
352     if (ftp_send_command(s, command, stor_codes, NULL) != 150)
353         return AVERROR(EIO);
354
355     s->state = UPLOADING;
356
357     return 0;
358 }
359
360 static int ftp_type(FTPContext *s)
361 {
362     const char *command = "TYPE I\r\n";
363     const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
364
365     if (ftp_send_command(s, command, type_codes, NULL) != 200)
366         return AVERROR(EIO);
367
368     return 0;
369 }
370
371 static int ftp_restart(FTPContext *s, int64_t pos)
372 {
373     char command[CONTROL_BUFFER_SIZE];
374     const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
375
376     snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
377     if (ftp_send_command(s, command, rest_codes, NULL) != 350)
378         return AVERROR(EIO);
379
380     return 0;
381 }
382
383 static int ftp_connect_control_connection(URLContext *h)
384 {
385     char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
386     int err;
387     AVDictionary *opts = NULL;
388     FTPContext *s = h->priv_data;
389     const int connect_codes[] = {220, 0};
390
391     if (!s->conn_control) {
392         ff_url_join(buf, sizeof(buf), "tcp", NULL,
393                     s->hostname, s->server_control_port, NULL);
394         if (s->rw_timeout != -1) {
395             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
396             av_dict_set(&opts, "timeout", opts_format, 0);
397         } /* if option is not given, don't pass it and let tcp use its own default */
398         err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
399                          &h->interrupt_callback, &opts);
400         av_dict_free(&opts);
401         if (err < 0) {
402             av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
403             return err;
404         }
405
406         /* check if server is ready */
407         if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
408             av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
409             return AVERROR(EACCES);
410         }
411
412         if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
413             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.");
414         }
415         av_free(response);
416
417         if ((err = ftp_auth(s)) < 0) {
418             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
419             return err;
420         }
421
422         if ((err = ftp_type(s)) < 0) {
423             av_dlog(h, "Set content type failed\n");
424             return err;
425         }
426     }
427     return 0;
428 }
429
430 static int ftp_connect_data_connection(URLContext *h)
431 {
432     int err;
433     char buf[CONTROL_BUFFER_SIZE], opts_format[20];
434     AVDictionary *opts = NULL;
435     FTPContext *s = h->priv_data;
436
437     if (!s->conn_data) {
438         /* Enter passive mode */
439         if ((err = ftp_passive_mode(s)) < 0) {
440             av_dlog(h, "Set passive mode failed\n");
441             return err;
442         }
443         /* Open data connection */
444         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
445         if (s->rw_timeout != -1) {
446             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
447             av_dict_set(&opts, "timeout", opts_format, 0);
448         } /* if option is not given, don't pass it and let tcp use its own default */
449         err = ffurl_open(&s->conn_data, buf, h->flags,
450                          &h->interrupt_callback, &opts);
451         av_dict_free(&opts);
452         if (err < 0)
453             return err;
454
455         if (s->position)
456             if ((err = ftp_restart(s, s->position)) < 0)
457                 return err;
458     }
459     s->state = READY;
460     return 0;
461 }
462
463 static int ftp_abort(URLContext *h)
464 {
465     const char *command = "ABOR\r\n";
466     int err;
467     const int abor_codes[] = {225, 226, 0};
468     FTPContext *s = h->priv_data;
469
470     /* According to RCF 959:
471        "ABOR command tells the server to abort the previous FTP
472        service command and any associated transfer of data."
473
474        There are FTP server implementations that don't response
475        to any commands during data transfer in passive mode (including ABOR).
476
477        This implementation closes data connection by force.
478     */
479
480     if (ftp_send_command(s, command, NULL, NULL) < 0) {
481         ftp_close_both_connections(s);
482         if ((err = ftp_connect_control_connection(h)) < 0) {
483             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
484             return err;
485         }
486     } else {
487         ftp_close_data_connection(s);
488     }
489
490     if (ftp_status(s, NULL, abor_codes) < 225) {
491         /* wu-ftpd also closes control connection after data connection closing */
492         ffurl_closep(&s->conn_control);
493         if ((err = ftp_connect_control_connection(h)) < 0) {
494             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
495             return err;
496         }
497     }
498
499     return 0;
500 }
501
502 static int ftp_open(URLContext *h, const char *url, int flags)
503 {
504     char proto[10], path[MAX_URL_SIZE];
505     int err;
506     FTPContext *s = h->priv_data;
507
508     av_dlog(h, "ftp protocol open\n");
509
510     s->state = DISCONNECTED;
511     s->filesize = -1;
512     s->position = 0;
513
514     av_url_split(proto, sizeof(proto),
515                  s->credencials, sizeof(s->credencials),
516                  s->hostname, sizeof(s->hostname),
517                  &s->server_control_port,
518                  path, sizeof(path),
519                  url);
520
521     if (s->server_control_port < 0 || s->server_control_port > 65535)
522         s->server_control_port = 21;
523
524     if ((err = ftp_connect_control_connection(h)) < 0)
525         goto fail;
526
527     if ((err = ftp_current_dir(s)) < 0)
528         goto fail;
529     av_strlcat(s->path, path, sizeof(s->path));
530
531     if (ftp_restart(s, 0) < 0) {
532         h->is_streamed = 1;
533     } else {
534         if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
535             h->is_streamed = 1;
536         if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
537             h->is_streamed = 1;
538     }
539
540     return 0;
541
542   fail:
543     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
544     ffurl_closep(&s->conn_control);
545     ffurl_closep(&s->conn_data);
546     return err;
547 }
548
549 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
550 {
551     FTPContext *s = h->priv_data;
552     int err;
553     int64_t new_pos, fake_pos;
554
555     av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
556
557     switch(whence) {
558     case AVSEEK_SIZE:
559         return s->filesize;
560     case SEEK_SET:
561         new_pos = pos;
562         break;
563     case SEEK_CUR:
564         new_pos = s->position + pos;
565         break;
566     case SEEK_END:
567         if (s->filesize < 0)
568             return AVERROR(EIO);
569         new_pos = s->filesize + pos;
570         break;
571     default:
572         return AVERROR(EINVAL);
573     }
574
575     if  (h->is_streamed)
576         return AVERROR(EIO);
577
578     /* XXX: Simulate behaviour of lseek in file protocol, which could be treated as a reference */
579     new_pos = FFMAX(0, new_pos);
580     fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
581
582     if (fake_pos != s->position) {
583         if ((err = ftp_abort(h)) < 0)
584             return err;
585         s->position = fake_pos;
586     }
587     return new_pos;
588 }
589
590 static int ftp_read(URLContext *h, unsigned char *buf, int size)
591 {
592     FTPContext *s = h->priv_data;
593     int read, err, retry_done = 0;
594
595     av_dlog(h, "ftp protocol read %d bytes\n", size);
596   retry:
597     if (s->state == DISCONNECTED) {
598         /* optimization */
599         if (s->position >= s->filesize)
600             return 0;
601         if ((err = ftp_connect_data_connection(h)) < 0)
602             return err;
603     }
604     if (s->state == READY) {
605         if (s->position >= s->filesize)
606             return 0;
607         if ((err = ftp_retrieve(s)) < 0)
608             return err;
609     }
610     if (s->conn_data && s->state == DOWNLOADING) {
611         read = ffurl_read(s->conn_data, buf, size);
612         if (read >= 0) {
613             s->position += read;
614             if (s->position >= s->filesize) {
615                 /* server will terminate, but keep current position to avoid madness */
616                 /* save position to restart from it */
617                 int64_t pos = s->position;
618                 if (ftp_abort(h) < 0) {
619                     s->position = pos;
620                     return AVERROR(EIO);
621                 }
622                 s->position = pos;
623             }
624         }
625         if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
626             /* Server closed connection. Probably due to inactivity */
627             int64_t pos = s->position;
628             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
629             if ((err = ftp_abort(h)) < 0)
630                 return err;
631             if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
632                 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
633                 return err;
634             }
635             if (!retry_done) {
636                 retry_done = 1;
637                 goto retry;
638             }
639         }
640         return read;
641     }
642
643     av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
644     return AVERROR(EIO);
645 }
646
647 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
648 {
649     int err;
650     FTPContext *s = h->priv_data;
651     int written;
652
653     av_dlog(h, "ftp protocol write %d bytes\n", size);
654
655     if (s->state == DISCONNECTED) {
656         if ((err = ftp_connect_data_connection(h)) < 0)
657             return err;
658     }
659     if (s->state == READY) {
660         if ((err = ftp_store(s)) < 0)
661             return err;
662     }
663     if (s->conn_data && s->state == UPLOADING) {
664         written = ffurl_write(s->conn_data, buf, size);
665         if (written > 0) {
666             s->position += written;
667             s->filesize = FFMAX(s->filesize, s->position);
668         }
669         return written;
670     }
671
672     av_log(h, AV_LOG_ERROR, "FTP write failed\n");
673     return AVERROR(EIO);
674 }
675
676 static int ftp_close(URLContext *h)
677 {
678     av_dlog(h, "ftp protocol close\n");
679
680     ftp_close_both_connections(h->priv_data);
681
682     return 0;
683 }
684
685 static int ftp_get_file_handle(URLContext *h)
686 {
687     FTPContext *s = h->priv_data;
688
689     av_dlog(h, "ftp protocol get_file_handle\n");
690
691     if (s->conn_data)
692         return ffurl_get_file_handle(s->conn_data);
693
694     return AVERROR(EIO);
695 }
696
697 static int ftp_shutdown(URLContext *h, int flags)
698 {
699     FTPContext *s = h->priv_data;
700
701     av_dlog(h, "ftp protocol shutdown\n");
702
703     if (s->conn_data)
704         return ffurl_shutdown(s->conn_data, flags);
705
706     return AVERROR(EIO);
707 }
708
709 URLProtocol ff_ftp_protocol = {
710     .name                = "ftp",
711     .url_open            = ftp_open,
712     .url_read            = ftp_read,
713     .url_write           = ftp_write,
714     .url_seek            = ftp_seek,
715     .url_close           = ftp_close,
716     .url_get_file_handle = ftp_get_file_handle,
717     .url_shutdown        = ftp_shutdown,
718     .priv_data_size      = sizeof(FTPContext),
719     .priv_data_class     = &ftp_context_class,
720     .flags               = URL_PROTOCOL_FLAG_NETWORK,
721 };