]> git.sesse.net Git - ffmpeg/blob - libavformat/ftp.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / 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 <stdlib.h>
22 #include "libavutil/avstring.h"
23 #include "libavutil/time.h"
24 #include "avformat.h"
25 #include "internal.h"
26 #include "network.h"
27 #include "os_support.h"
28 #include "url.h"
29 #include "libavutil/opt.h"
30
31 #define CONTROL_BUFFER_SIZE 1024
32 #define CREDENTIALS_BUFFER_SIZE 128
33
34 typedef enum {
35     UNKNOWN,
36     READY,
37     DOWNLOADING,
38     UPLOADING,
39     DISCONNECTED
40 } FTPState;
41
42 typedef struct {
43     const AVClass *class;
44     URLContext *conn_control;                    /**< Control connection */
45     int conn_control_block_flag;                 /**< Controls block/unblock mode of data connection */
46     AVIOInterruptCB conn_control_interrupt_cb;   /**< Controls block/unblock mode of data connection */
47     URLContext *conn_data;                       /**< Data connection, NULL when not connected */
48     uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
49     uint8_t *control_buf_ptr, *control_buf_end;
50     int server_data_port;                        /**< Data connection port opened by server, -1 on error. */
51     int server_control_port;                     /**< Control connection port, default is 21 */
52     char hostname[512];                          /**< Server address. */
53     char credencials[CREDENTIALS_BUFFER_SIZE];   /**< Authentication data */
54     char path[MAX_URL_SIZE];                     /**< Path to resource on server. */
55     int64_t filesize;                            /**< Size of file on server, -1 on error. */
56     int64_t position;                            /**< Current position, calculated. */
57     int rw_timeout;                              /**< Network timeout. */
58     const char *anonymous_password;              /**< Password to be used for anonymous user. An email should be used. */
59     int write_seekable;                          /**< Control seekability, 0 = disable, 1 = enable. */
60     FTPState state;                              /**< State of data connection */
61 } FTPContext;
62
63 #define OFFSET(x) offsetof(FTPContext, x)
64 #define D AV_OPT_FLAG_DECODING_PARAM
65 #define E AV_OPT_FLAG_ENCODING_PARAM
66 static const AVOption options[] = {
67     {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
68     {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
69     {"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 },
70     {NULL}
71 };
72
73 static const AVClass ftp_context_class = {
74     .class_name     = "ftp",
75     .item_name      = av_default_item_name,
76     .option         = options,
77     .version        = LIBAVUTIL_VERSION_INT,
78 };
79
80 static int ftp_conn_control_block_control(void *data)
81 {
82     FTPContext *s = data;
83     return s->conn_control_block_flag;
84 }
85
86 static int ftp_getc(FTPContext *s)
87 {
88     int len;
89     if (s->control_buf_ptr >= s->control_buf_end) {
90         if (s->conn_control_block_flag)
91             return AVERROR_EXIT;
92         len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
93         if (len < 0) {
94             return len;
95         } else if (!len) {
96             return -1;
97         } else {
98             s->control_buf_ptr = s->control_buffer;
99             s->control_buf_end = s->control_buffer + len;
100         }
101     }
102     return *s->control_buf_ptr++;
103 }
104
105 static int ftp_get_line(FTPContext *s, char *line, int line_size)
106 {
107     int ch;
108     char *q = line;
109     int ori_block_flag = s->conn_control_block_flag;
110
111     for (;;) {
112         ch = ftp_getc(s);
113         if (ch < 0) {
114             s->conn_control_block_flag = ori_block_flag;
115             return ch;
116         }
117         if (ch == '\n') {
118             /* process line */
119             if (q > line && q[-1] == '\r')
120                 q--;
121             *q = '\0';
122
123             s->conn_control_block_flag = ori_block_flag;
124             return 0;
125         } else {
126             s->conn_control_block_flag = 0; /* line need to be finished */
127             if ((q - line) < line_size - 1)
128                 *q++ = ch;
129         }
130     }
131 }
132
133 static int ftp_flush_control_input(FTPContext *s)
134 {
135     char buf[CONTROL_BUFFER_SIZE];
136     int err, ori_block_flag = s->conn_control_block_flag;
137
138     s->conn_control_block_flag = 1;
139     do {
140         err = ftp_get_line(s, buf, sizeof(buf));
141     } while (err > 0);
142
143     s->conn_control_block_flag = ori_block_flag;
144
145     if (err < 0 && err != AVERROR_EXIT)
146         return err;
147
148     return 0;
149 }
150
151 /*
152  * This routine returns ftp server response code.
153  * Server may send more than one response for a certain command, following priorities are used:
154  *   - When pref_codes are set then pref_code is return if occurred. (expected result)
155  *   - 0 is returned when no pref_codes or not occurred
156  */
157 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
158 {
159     int err, i, result = 0, pref_code_found = 0, wait_count = 100;
160     char buf[CONTROL_BUFFER_SIZE];
161
162     /* Set blocking mode */
163     s->conn_control_block_flag = 0;
164     for (;;) {
165         if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
166             if (err == AVERROR_EXIT) {
167                 if (!pref_code_found && wait_count--) {
168                     av_usleep(10000);
169                     continue;
170                 }
171             }
172             return result;
173         }
174
175         /* first code received. Now get all lines in non blocking mode */
176         s->conn_control_block_flag = 1;
177
178         av_log(s, AV_LOG_DEBUG, "%s\n", buf);
179
180         if (!pref_code_found) {
181             if (strlen(buf) < 3)
182                 continue;
183
184             err = 0;
185             for (i = 0; i < 3; ++i) {
186                 if (buf[i] < '0' || buf[i] > '9')
187                     continue;
188                 err *= 10;
189                 err += buf[i] - '0';
190             }
191
192             for (i = 0; response_codes[i]; ++i) {
193                 if (err == response_codes[i]) {
194                     pref_code_found = 1;
195                     result = err;
196                     if (line)
197                         *line = av_strdup(buf);
198                     break;
199                 }
200             }
201         }
202     }
203     return result;
204 }
205
206 static int ftp_send_command(FTPContext *s, const char *command,
207                             const int response_codes[], char **response)
208 {
209     int err;
210
211     /* Flush control connection input to get rid of non relevant responses if any */
212     if ((err = ftp_flush_control_input(s)) < 0)
213         return err;
214
215     /* send command in blocking mode */
216     s->conn_control_block_flag = 0;
217     if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
218         return err;
219
220     /* return status */
221     return ftp_status(s, response, response_codes);
222 }
223
224 static void ftp_close_both_connections(FTPContext *s)
225 {
226     ffurl_closep(&s->conn_control);
227     ffurl_closep(&s->conn_data);
228     s->position = 0;
229     s->state = DISCONNECTED;
230 }
231
232 static int ftp_auth(FTPContext *s)
233 {
234     const char *user = NULL, *pass = NULL;
235     char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
236     int err;
237     const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
238     const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
239
240     /* Authentication may be repeated, original string has to be saved */
241     av_strlcpy(credencials, s->credencials, sizeof(credencials));
242
243     user = av_strtok(credencials, ":", &end);
244     pass = av_strtok(end, ":", &end);
245
246     if (!user) {
247         user = "anonymous";
248         pass = s->anonymous_password ? s->anonymous_password : "nopassword";
249     }
250
251     snprintf(buf, sizeof(buf), "USER %s\r\n", user);
252     err = ftp_send_command(s, buf, user_codes, NULL);
253     if (err == 331) {
254         if (pass) {
255             snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
256             err = ftp_send_command(s, buf, pass_codes, NULL);
257         } else
258             return AVERROR(EACCES);
259     }
260     if (err != 230)
261         return AVERROR(EACCES);
262
263     return 0;
264 }
265
266 static int ftp_passive_mode(FTPContext *s)
267 {
268     char *res = NULL, *start, *end;
269     int i;
270     const char *command = "PASV\r\n";
271     const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
272
273     if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
274         goto fail;
275
276     start = NULL;
277     for (i = 0; i < strlen(res); ++i) {
278         if (res[i] == '(') {
279             start = res + i + 1;
280         } else if (res[i] == ')') {
281             end = res + i;
282             break;
283         }
284     }
285     if (!start || !end)
286         goto fail;
287
288     *end  = '\0';
289     /* skip ip */
290     if (!av_strtok(start, ",", &end)) goto fail;
291     if (!av_strtok(end, ",", &end)) goto fail;
292     if (!av_strtok(end, ",", &end)) goto fail;
293     if (!av_strtok(end, ",", &end)) goto fail;
294
295     /* parse port number */
296     start = av_strtok(end, ",", &end);
297     if (!start) goto fail;
298     s->server_data_port = atoi(start) * 256;
299     start = av_strtok(end, ",", &end);
300     if (!start) goto fail;
301     s->server_data_port += atoi(start);
302     av_dlog(s, "Server data port: %d\n", s->server_data_port);
303
304     av_free(res);
305     return 0;
306
307   fail:
308     av_free(res);
309     s->server_data_port = -1;
310     return AVERROR(EIO);
311 }
312
313 static int ftp_current_dir(FTPContext *s)
314 {
315     char *res = NULL, *start = NULL, *end = NULL;
316     int i;
317     const char *command = "PWD\r\n";
318     const int pwd_codes[] = {257, 0};
319
320     if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
321         goto fail;
322
323     for (i = 0; res[i]; ++i) {
324         if (res[i] == '"') {
325             if (!start) {
326                 start = res + i + 1;
327                 continue;
328             }
329             end = res + i;
330             break;
331         }
332     }
333
334     if (!end)
335         goto fail;
336
337     if (end > res && end[-1] == '/') {
338         end[-1] = '\0';
339     } else
340         *end = '\0';
341     av_strlcpy(s->path, start, sizeof(s->path));
342
343     av_free(res);
344     return 0;
345
346   fail:
347     av_free(res);
348     return AVERROR(EIO);
349 }
350
351 static int ftp_file_size(FTPContext *s)
352 {
353     char command[CONTROL_BUFFER_SIZE];
354     char *res = NULL;
355     const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
356
357     snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
358     if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
359         s->filesize = strtoll(&res[4], NULL, 10);
360     } else {
361         s->filesize = -1;
362         av_free(res);
363         return AVERROR(EIO);
364     }
365
366     av_free(res);
367     return 0;
368 }
369
370 static int ftp_retrieve(FTPContext *s)
371 {
372     char command[CONTROL_BUFFER_SIZE];
373     const int retr_codes[] = {150, 550, 0}; /* 550 is incorrect code */
374
375     snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
376     if (ftp_send_command(s, command, retr_codes, NULL) != 150)
377         return AVERROR(EIO);
378
379     s->state = DOWNLOADING;
380
381     return 0;
382 }
383
384 static int ftp_store(FTPContext *s)
385 {
386     char command[CONTROL_BUFFER_SIZE];
387     const int stor_codes[] = {150, 0};
388
389     snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
390     if (!ftp_send_command(s, command, stor_codes, NULL))
391         return AVERROR(EIO);
392
393     s->state = UPLOADING;
394
395     return 0;
396 }
397
398 static int ftp_type(FTPContext *s)
399 {
400     const char *command = "TYPE I\r\n";
401     const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
402
403     if (ftp_send_command(s, command, type_codes, NULL) != 200)
404         return AVERROR(EIO);
405
406     return 0;
407 }
408
409 static int ftp_restart(FTPContext *s, int64_t pos)
410 {
411     char command[CONTROL_BUFFER_SIZE];
412     const int rest_codes[] = {350, 501, 0}; /* 501 is incorrect code */
413
414     snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
415     if (ftp_send_command(s, command, rest_codes, NULL) != 350)
416         return AVERROR(EIO);
417
418     return 0;
419 }
420
421 static int ftp_connect_control_connection(URLContext *h)
422 {
423     char buf[CONTROL_BUFFER_SIZE], opts_format[20];
424     int err;
425     AVDictionary *opts = NULL;
426     FTPContext *s = h->priv_data;
427     const int connect_codes[] = {220, 0};
428
429     s->conn_control_block_flag = 0;
430
431     if (!s->conn_control) {
432         ff_url_join(buf, sizeof(buf), "tcp", NULL,
433                     s->hostname, s->server_control_port, NULL);
434         if (s->rw_timeout != -1) {
435             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
436             av_dict_set(&opts, "timeout", opts_format, 0);
437         } /* if option is not given, don't pass it and let tcp use its own default */
438         err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
439                          &s->conn_control_interrupt_cb, &opts);
440         av_dict_free(&opts);
441         if (err < 0) {
442             av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
443             return err;
444         }
445
446         /* consume all messages from server */
447         if (!ftp_status(s, NULL, connect_codes)) {
448             av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
449             err = AVERROR(EACCES);
450             return err;
451         }
452
453         if ((err = ftp_auth(s)) < 0) {
454             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
455             return err;
456         }
457
458         if ((err = ftp_type(s)) < 0) {
459             av_dlog(h, "Set content type failed\n");
460             return err;
461         }
462     }
463     return 0;
464 }
465
466 static int ftp_connect_data_connection(URLContext *h)
467 {
468     int err;
469     char buf[CONTROL_BUFFER_SIZE], opts_format[20];
470     AVDictionary *opts = NULL;
471     FTPContext *s = h->priv_data;
472
473     if (!s->conn_data) {
474         /* Enter passive mode */
475         if ((err = ftp_passive_mode(s)) < 0) {
476             av_dlog(h, "Set passive mode failed\n");
477             return err;
478         }
479         /* Open data connection */
480         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
481         if (s->rw_timeout != -1) {
482             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
483             av_dict_set(&opts, "timeout", opts_format, 0);
484         } /* if option is not given, don't pass it and let tcp use its own default */
485         err = ffurl_open(&s->conn_data, buf, AVIO_FLAG_READ_WRITE,
486                          &h->interrupt_callback, &opts);
487         av_dict_free(&opts);
488         if (err < 0)
489             return err;
490
491         if (s->position)
492             if ((err = ftp_restart(s, s->position)) < 0)
493                 return err;
494     }
495     s->state = READY;
496     return 0;
497 }
498
499 static int ftp_abort(URLContext *h)
500 {
501     int err;
502     ftp_close_both_connections(h->priv_data);
503     if ((err = ftp_connect_control_connection(h)) < 0)
504         return err;
505     return 0;
506 }
507
508 static int ftp_open(URLContext *h, const char *url, int flags)
509 {
510     char proto[10], path[MAX_URL_SIZE];
511     int err;
512     FTPContext *s = h->priv_data;
513
514     av_dlog(h, "ftp protocol open\n");
515
516     s->state = DISCONNECTED;
517     s->filesize = -1;
518     s->position = 0;
519     s->conn_control_interrupt_cb.opaque = s;
520     s->conn_control_interrupt_cb.callback = ftp_conn_control_block_control;
521
522     av_url_split(proto, sizeof(proto),
523                  s->credencials, sizeof(s->credencials),
524                  s->hostname, sizeof(s->hostname),
525                  &s->server_control_port,
526                  path, sizeof(path),
527                  url);
528
529     if (s->server_control_port < 0 || s->server_control_port > 65535)
530         s->server_control_port = 21;
531
532     if ((err = ftp_connect_control_connection(h)) < 0)
533         goto fail;
534
535     if ((err = ftp_current_dir(s)) < 0)
536         goto fail;
537     av_strlcat(s->path, path, sizeof(s->path));
538
539     if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
540         h->is_streamed = 1;
541     if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
542         h->is_streamed = 1;
543
544     return 0;
545
546   fail:
547     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
548     ffurl_closep(&s->conn_control);
549     ffurl_closep(&s->conn_data);
550     return err;
551 }
552
553 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
554 {
555     FTPContext *s = h->priv_data;
556     int err;
557     int64_t new_pos;
558
559     av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
560
561     switch(whence) {
562     case AVSEEK_SIZE:
563         return s->filesize;
564     case SEEK_SET:
565         new_pos = pos;
566         break;
567     case SEEK_CUR:
568         new_pos = s->position + pos;
569         break;
570     case SEEK_END:
571         if (s->filesize < 0)
572             return AVERROR(EIO);
573         new_pos = s->filesize + pos;
574         break;
575     default:
576         return AVERROR(EINVAL);
577     }
578
579     if  (h->is_streamed)
580         return AVERROR(EIO);
581
582     new_pos = FFMAX(0, new_pos);
583     if (s->filesize >= 0)
584         new_pos = FFMIN(s->filesize, new_pos);
585
586     if (new_pos != s->position) {
587         /* XXX: Full abort is a save solution here.
588            Some optimalizations are possible, but may lead to crazy states of FTP server.
589            The worst scenario would be when FTP server closed both connection due to no transfer. */
590         if ((err = ftp_abort(h)) < 0)
591             return err;
592
593         s->position = new_pos;
594     }
595     return new_pos;
596 }
597
598 static int ftp_read(URLContext *h, unsigned char *buf, int size)
599 {
600     FTPContext *s = h->priv_data;
601     int read, err, retry_done = 0;
602
603     av_dlog(h, "ftp protocol read %d bytes\n", size);
604   retry:
605     if (s->state == DISCONNECTED) {
606         if ((err = ftp_connect_data_connection(h)) < 0)
607             return err;
608     }
609     if (s->state == READY) {
610         if ((err = ftp_retrieve(s)) < 0)
611             return err;
612     }
613     if (s->conn_data && s->state == DOWNLOADING) {
614         read = ffurl_read(s->conn_data, buf, size);
615         if (read >= 0) {
616             s->position += read;
617             if (s->position >= s->filesize) {
618                 if (ftp_abort(h) < 0)
619                     return AVERROR(EIO);
620             }
621         }
622         if (!read && s->position < s->filesize && !h->is_streamed) {
623             /* Server closed connection. Probably due to inactivity */
624             /* TODO: Consider retry before reconnect */
625             int64_t pos = s->position;
626             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
627             if ((err = ftp_abort(h)) < 0) {
628                 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
629                 return err;
630             }
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 };