ftp: comments
[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];
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, 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 ((err = ftp_auth(s)) < 0) {
413             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
414             return err;
415         }
416
417         if ((err = ftp_type(s)) < 0) {
418             av_dlog(h, "Set content type failed\n");
419             return err;
420         }
421     }
422     return 0;
423 }
424
425 static int ftp_connect_data_connection(URLContext *h)
426 {
427     int err;
428     char buf[CONTROL_BUFFER_SIZE], opts_format[20];
429     AVDictionary *opts = NULL;
430     FTPContext *s = h->priv_data;
431
432     if (!s->conn_data) {
433         /* Enter passive mode */
434         if ((err = ftp_passive_mode(s)) < 0) {
435             av_dlog(h, "Set passive mode failed\n");
436             return err;
437         }
438         /* Open data connection */
439         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
440         if (s->rw_timeout != -1) {
441             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
442             av_dict_set(&opts, "timeout", opts_format, 0);
443         } /* if option is not given, don't pass it and let tcp use its own default */
444         err = ffurl_open(&s->conn_data, buf, h->flags,
445                          &h->interrupt_callback, &opts);
446         av_dict_free(&opts);
447         if (err < 0)
448             return err;
449
450         if (s->position)
451             if ((err = ftp_restart(s, s->position)) < 0)
452                 return err;
453     }
454     s->state = READY;
455     return 0;
456 }
457
458 static int ftp_abort(URLContext *h)
459 {
460     const char *command = "ABOR\r\n";
461     int err;
462     const int abor_codes[] = {225, 226, 0};
463     FTPContext *s = h->priv_data;
464
465     /* According to RCF 959:
466        "ABOR command tells the server to abort the previous FTP
467        service command and any associated transfer of data."
468
469        There are FTP server implementations that don't response
470        to any commands during data transfer in passive mode (including ABOR).
471
472        This implementation closes data connection by force.
473     */
474
475     if (ftp_send_command(s, command, NULL, NULL) < 0) {
476         ftp_close_both_connections(s);
477         if ((err = ftp_connect_control_connection(h)) < 0) {
478             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
479             return err;
480         }
481     } else {
482         ftp_close_data_connection(s);
483     }
484
485     if (ftp_status(s, NULL, abor_codes) < 225) {
486         /* wu-ftpd also closes control connection after data connection closing */
487         ffurl_closep(&s->conn_control);
488         if ((err = ftp_connect_control_connection(h)) < 0) {
489             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
490             return err;
491         }
492     }
493
494     return 0;
495 }
496
497 static int ftp_open(URLContext *h, const char *url, int flags)
498 {
499     char proto[10], path[MAX_URL_SIZE];
500     int err;
501     FTPContext *s = h->priv_data;
502
503     av_dlog(h, "ftp protocol open\n");
504
505     s->state = DISCONNECTED;
506     s->filesize = -1;
507     s->position = 0;
508
509     av_url_split(proto, sizeof(proto),
510                  s->credencials, sizeof(s->credencials),
511                  s->hostname, sizeof(s->hostname),
512                  &s->server_control_port,
513                  path, sizeof(path),
514                  url);
515
516     if (s->server_control_port < 0 || s->server_control_port > 65535)
517         s->server_control_port = 21;
518
519     if ((err = ftp_connect_control_connection(h)) < 0)
520         goto fail;
521
522     if ((err = ftp_current_dir(s)) < 0)
523         goto fail;
524     av_strlcat(s->path, path, sizeof(s->path));
525
526     if (ftp_restart(s, 0) < 0) {
527         h->is_streamed = 1;
528     } else {
529         if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
530             h->is_streamed = 1;
531         if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
532             h->is_streamed = 1;
533     }
534
535     return 0;
536
537   fail:
538     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
539     ffurl_closep(&s->conn_control);
540     ffurl_closep(&s->conn_data);
541     return err;
542 }
543
544 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
545 {
546     FTPContext *s = h->priv_data;
547     int err;
548     int64_t new_pos, fake_pos;
549
550     av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
551
552     switch(whence) {
553     case AVSEEK_SIZE:
554         return s->filesize;
555     case SEEK_SET:
556         new_pos = pos;
557         break;
558     case SEEK_CUR:
559         new_pos = s->position + pos;
560         break;
561     case SEEK_END:
562         if (s->filesize < 0)
563             return AVERROR(EIO);
564         new_pos = s->filesize + pos;
565         break;
566     default:
567         return AVERROR(EINVAL);
568     }
569
570     if  (h->is_streamed)
571         return AVERROR(EIO);
572
573     /* XXX: Simulate behaviour of lseek in file protocol, which could be treated as a reference */
574     new_pos = FFMAX(0, new_pos);
575     fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
576
577     if (fake_pos != s->position) {
578         if ((err = ftp_abort(h)) < 0)
579             return err;
580         s->position = fake_pos;
581     }
582     return new_pos;
583 }
584
585 static int ftp_read(URLContext *h, unsigned char *buf, int size)
586 {
587     FTPContext *s = h->priv_data;
588     int read, err, retry_done = 0;
589
590     av_dlog(h, "ftp protocol read %d bytes\n", size);
591   retry:
592     if (s->state == DISCONNECTED) {
593         /* optimization */
594         if (s->position >= s->filesize)
595             return 0;
596         if ((err = ftp_connect_data_connection(h)) < 0)
597             return err;
598     }
599     if (s->state == READY) {
600         if (s->position >= s->filesize)
601             return 0;
602         if ((err = ftp_retrieve(s)) < 0)
603             return err;
604     }
605     if (s->conn_data && s->state == DOWNLOADING) {
606         read = ffurl_read(s->conn_data, buf, size);
607         if (read >= 0) {
608             s->position += read;
609             if (s->position >= s->filesize) {
610                 /* server will terminate, but keep current position to avoid madness */
611                 /* save position to restart from it */
612                 int64_t pos = s->position;
613                 if (ftp_abort(h) < 0) {
614                     s->position = pos;
615                     return AVERROR(EIO);
616                 }
617                 s->position = pos;
618             }
619         }
620         if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
621             /* Server closed connection. Probably due to inactivity */
622             int64_t pos = s->position;
623             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
624             if ((err = ftp_abort(h)) < 0)
625                 return err;
626             if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
627                 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
628                 return err;
629             }
630             if (!retry_done) {
631                 retry_done = 1;
632                 goto retry;
633             }
634         }
635         return read;
636     }
637
638     av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
639     return AVERROR(EIO);
640 }
641
642 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
643 {
644     int err;
645     FTPContext *s = h->priv_data;
646     int written;
647
648     av_dlog(h, "ftp protocol write %d bytes\n", size);
649
650     if (s->state == DISCONNECTED) {
651         if ((err = ftp_connect_data_connection(h)) < 0)
652             return err;
653     }
654     if (s->state == READY) {
655         if ((err = ftp_store(s)) < 0)
656             return err;
657     }
658     if (s->conn_data && s->state == UPLOADING) {
659         written = ffurl_write(s->conn_data, buf, size);
660         if (written > 0) {
661             s->position += written;
662             s->filesize = FFMAX(s->filesize, s->position);
663         }
664         return written;
665     }
666
667     av_log(h, AV_LOG_ERROR, "FTP write failed\n");
668     return AVERROR(EIO);
669 }
670
671 static int ftp_close(URLContext *h)
672 {
673     av_dlog(h, "ftp protocol close\n");
674
675     ftp_close_both_connections(h->priv_data);
676
677     return 0;
678 }
679
680 static int ftp_get_file_handle(URLContext *h)
681 {
682     FTPContext *s = h->priv_data;
683
684     av_dlog(h, "ftp protocol get_file_handle\n");
685
686     if (s->conn_data)
687         return ffurl_get_file_handle(s->conn_data);
688
689     return AVERROR(EIO);
690 }
691
692 static int ftp_shutdown(URLContext *h, int flags)
693 {
694     FTPContext *s = h->priv_data;
695
696     av_dlog(h, "ftp protocol shutdown\n");
697
698     if (s->conn_data)
699         return ffurl_shutdown(s->conn_data, flags);
700
701     return AVERROR(EIO);
702 }
703
704 URLProtocol ff_ftp_protocol = {
705     .name                = "ftp",
706     .url_open            = ftp_open,
707     .url_read            = ftp_read,
708     .url_write           = ftp_write,
709     .url_seek            = ftp_seek,
710     .url_close           = ftp_close,
711     .url_get_file_handle = ftp_get_file_handle,
712     .url_shutdown        = ftp_shutdown,
713     .priv_data_size      = sizeof(FTPContext),
714     .priv_data_class     = &ftp_context_class,
715     .flags               = URL_PROTOCOL_FLAG_NETWORK,
716 };