dae8aa086d2f45ede2cea0b81694c04351cda14b
[platform/framework/web/crosswalk.git] / src / third_party / 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 "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             if (line)
132                 av_bprint_finalize(&line_buffer, NULL);
133             return err;
134         }
135
136         av_log(s, AV_LOG_DEBUG, "%s\n", buf);
137
138         if (strlen(buf) < 4)
139             continue;
140
141         err = 0;
142         for (i = 0; i < 3; ++i) {
143             if (buf[i] < '0' || buf[i] > '9')
144                 continue;
145             err *= 10;
146             err += buf[i] - '0';
147         }
148         dash = !!(buf[3] == '-');
149
150         for (i = 0; response_codes[i]; ++i) {
151             if (err == response_codes[i]) {
152                 if (line)
153                     av_bprintf(&line_buffer, "%s", buf);
154                 code_found = 1;
155                 result = err;
156                 break;
157             }
158         }
159     }
160
161     if (line)
162         av_bprint_finalize(&line_buffer, line);
163     return result;
164 }
165
166 static int ftp_send_command(FTPContext *s, const char *command,
167                             const int response_codes[], char **response)
168 {
169     int err;
170
171     if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
172         return err;
173     if (!err)
174         return -1;
175
176     /* return status */
177     if (response_codes) {
178         return ftp_status(s, response, response_codes);
179     }
180     return 0;
181 }
182
183 static void ftp_close_data_connection(FTPContext *s)
184 {
185     ffurl_closep(&s->conn_data);
186     s->position = 0;
187     s->state = DISCONNECTED;
188 }
189
190 static void ftp_close_both_connections(FTPContext *s)
191 {
192     ffurl_closep(&s->conn_control);
193     ftp_close_data_connection(s);
194 }
195
196 static int ftp_auth(FTPContext *s)
197 {
198     const char *user = NULL, *pass = NULL;
199     char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
200     int err;
201     static const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
202     static const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
203
204     /* Authentication may be repeated, original string has to be saved */
205     av_strlcpy(credencials, s->credencials, sizeof(credencials));
206
207     user = av_strtok(credencials, ":", &end);
208     pass = av_strtok(end, ":", &end);
209
210     if (!user) {
211         user = "anonymous";
212         pass = s->anonymous_password ? s->anonymous_password : "nopassword";
213     }
214
215     snprintf(buf, sizeof(buf), "USER %s\r\n", user);
216     err = ftp_send_command(s, buf, user_codes, NULL);
217     if (err == 331) {
218         if (pass) {
219             snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
220             err = ftp_send_command(s, buf, pass_codes, NULL);
221         } else
222             return AVERROR(EACCES);
223     }
224     if (err != 230)
225         return AVERROR(EACCES);
226
227     return 0;
228 }
229
230 static int ftp_passive_mode(FTPContext *s)
231 {
232     char *res = NULL, *start = NULL, *end = NULL;
233     int i;
234     static const char *command = "PASV\r\n";
235     static const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
236
237     if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
238         goto fail;
239
240     for (i = 0; res[i]; ++i) {
241         if (res[i] == '(') {
242             start = res + i + 1;
243         } else if (res[i] == ')') {
244             end = res + i;
245             break;
246         }
247     }
248     if (!start || !end)
249         goto fail;
250
251     *end  = '\0';
252     /* skip ip */
253     if (!av_strtok(start, ",", &end)) goto fail;
254     if (!av_strtok(end, ",", &end)) goto fail;
255     if (!av_strtok(end, ",", &end)) goto fail;
256     if (!av_strtok(end, ",", &end)) goto fail;
257
258     /* parse port number */
259     start = av_strtok(end, ",", &end);
260     if (!start) goto fail;
261     s->server_data_port = atoi(start) * 256;
262     start = av_strtok(end, ",", &end);
263     if (!start) goto fail;
264     s->server_data_port += atoi(start);
265     av_dlog(s, "Server data port: %d\n", s->server_data_port);
266
267     av_free(res);
268     return 0;
269
270   fail:
271     av_free(res);
272     s->server_data_port = -1;
273     av_log(s, AV_LOG_ERROR, "Set passive mode failed\n"
274                             "Your FTP server may use IPv6 which is not supported yet.\n");
275     return AVERROR(EIO);
276 }
277
278 static int ftp_current_dir(FTPContext *s)
279 {
280     char *res = NULL, *start = NULL, *end = NULL;
281     int i;
282     static const char *command = "PWD\r\n";
283     static const int pwd_codes[] = {257, 0};
284
285     if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
286         goto fail;
287
288     for (i = 0; res[i]; ++i) {
289         if (res[i] == '"') {
290             if (!start) {
291                 start = res + i + 1;
292                 continue;
293             }
294             end = res + i;
295             break;
296         }
297     }
298
299     if (!end)
300         goto fail;
301
302     if (end > res && end[-1] == '/') {
303         end[-1] = '\0';
304     } else
305         *end = '\0';
306     av_strlcpy(s->path, start, sizeof(s->path));
307
308     av_free(res);
309     return 0;
310
311   fail:
312     av_free(res);
313     return AVERROR(EIO);
314 }
315
316 static int ftp_file_size(FTPContext *s)
317 {
318     char command[CONTROL_BUFFER_SIZE];
319     char *res = NULL;
320     static const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
321
322     snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
323     if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
324         s->filesize = strtoll(&res[4], NULL, 10);
325     } else {
326         s->filesize = -1;
327         av_free(res);
328         return AVERROR(EIO);
329     }
330
331     av_free(res);
332     return 0;
333 }
334
335 static int ftp_retrieve(FTPContext *s)
336 {
337     char command[CONTROL_BUFFER_SIZE];
338     static const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
339
340     snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
341     if (ftp_send_command(s, command, retr_codes, NULL) != 150)
342         return AVERROR(EIO);
343
344     s->state = DOWNLOADING;
345
346     return 0;
347 }
348
349 static int ftp_store(FTPContext *s)
350 {
351     char command[CONTROL_BUFFER_SIZE];
352     static const int stor_codes[] = {150, 0};
353
354     snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
355     if (ftp_send_command(s, command, stor_codes, NULL) != 150)
356         return AVERROR(EIO);
357
358     s->state = UPLOADING;
359
360     return 0;
361 }
362
363 static int ftp_type(FTPContext *s)
364 {
365     static const char *command = "TYPE I\r\n";
366     static const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
367
368     if (ftp_send_command(s, command, type_codes, NULL) != 200)
369         return AVERROR(EIO);
370
371     return 0;
372 }
373
374 static int ftp_restart(FTPContext *s, int64_t pos)
375 {
376     char command[CONTROL_BUFFER_SIZE];
377     static const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
378
379     snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
380     if (ftp_send_command(s, command, rest_codes, NULL) != 350)
381         return AVERROR(EIO);
382
383     return 0;
384 }
385
386 static int ftp_connect_control_connection(URLContext *h)
387 {
388     char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
389     int err;
390     AVDictionary *opts = NULL;
391     FTPContext *s = h->priv_data;
392     static const int connect_codes[] = {220, 0};
393
394     if (!s->conn_control) {
395         ff_url_join(buf, sizeof(buf), "tcp", NULL,
396                     s->hostname, s->server_control_port, NULL);
397         if (s->rw_timeout != -1) {
398             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
399             av_dict_set(&opts, "timeout", opts_format, 0);
400         } /* if option is not given, don't pass it and let tcp use its own default */
401         err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
402                          &h->interrupt_callback, &opts);
403         av_dict_free(&opts);
404         if (err < 0) {
405             av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
406             return err;
407         }
408
409         /* check if server is ready */
410         if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
411             av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
412             return AVERROR(EACCES);
413         }
414
415         if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
416             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.");
417         }
418         av_free(response);
419
420         if ((err = ftp_auth(s)) < 0) {
421             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
422             return err;
423         }
424
425         if ((err = ftp_type(s)) < 0) {
426             av_log(h, AV_LOG_ERROR, "Set content type failed\n");
427             return err;
428         }
429     }
430     return 0;
431 }
432
433 static int ftp_connect_data_connection(URLContext *h)
434 {
435     int err;
436     char buf[CONTROL_BUFFER_SIZE], opts_format[20];
437     AVDictionary *opts = NULL;
438     FTPContext *s = h->priv_data;
439
440     if (!s->conn_data) {
441         /* Enter passive mode */
442         if ((err = ftp_passive_mode(s)) < 0)
443             return err;
444         /* Open data connection */
445         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
446         if (s->rw_timeout != -1) {
447             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
448             av_dict_set(&opts, "timeout", opts_format, 0);
449         } /* if option is not given, don't pass it and let tcp use its own default */
450         err = ffurl_open(&s->conn_data, buf, h->flags,
451                          &h->interrupt_callback, &opts);
452         av_dict_free(&opts);
453         if (err < 0)
454             return err;
455
456         if (s->position)
457             if ((err = ftp_restart(s, s->position)) < 0)
458                 return err;
459     }
460     s->state = READY;
461     return 0;
462 }
463
464 static int ftp_abort(URLContext *h)
465 {
466     static const char *command = "ABOR\r\n";
467     int err;
468     static const int abor_codes[] = {225, 226, 0};
469     FTPContext *s = h->priv_data;
470
471     /* According to RCF 959:
472        "ABOR command tells the server to abort the previous FTP
473        service command and any associated transfer of data."
474
475        There are FTP server implementations that don't response
476        to any commands during data transfer in passive mode (including ABOR).
477
478        This implementation closes data connection by force.
479     */
480
481     if (ftp_send_command(s, command, NULL, NULL) < 0) {
482         ftp_close_both_connections(s);
483         if ((err = ftp_connect_control_connection(h)) < 0) {
484             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
485             return err;
486         }
487     } else {
488         ftp_close_data_connection(s);
489         if (ftp_status(s, NULL, abor_codes) < 225) {
490             /* wu-ftpd also closes control connection after data connection closing */
491             ffurl_closep(&s->conn_control);
492             if ((err = ftp_connect_control_connection(h)) < 0) {
493                 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
494                 return err;
495             }
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     if (new_pos < 0) {
579         av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
580         return AVERROR(EINVAL);
581     }
582
583     fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
584     if (fake_pos != s->position) {
585         if ((err = ftp_abort(h)) < 0)
586             return err;
587         s->position = fake_pos;
588     }
589     return new_pos;
590 }
591
592 static int ftp_read(URLContext *h, unsigned char *buf, int size)
593 {
594     FTPContext *s = h->priv_data;
595     int read, err, retry_done = 0;
596
597     av_dlog(h, "ftp protocol read %d bytes\n", size);
598   retry:
599     if (s->state == DISCONNECTED) {
600         /* optimization */
601         if (s->position >= s->filesize)
602             return 0;
603         if ((err = ftp_connect_data_connection(h)) < 0)
604             return err;
605     }
606     if (s->state == READY) {
607         if (s->position >= s->filesize)
608             return 0;
609         if ((err = ftp_retrieve(s)) < 0)
610             return err;
611     }
612     if (s->conn_data && s->state == DOWNLOADING) {
613         read = ffurl_read(s->conn_data, buf, size);
614         if (read >= 0) {
615             s->position += read;
616             if (s->position >= s->filesize) {
617                 /* server will terminate, but keep current position to avoid madness */
618                 /* save position to restart from it */
619                 int64_t pos = s->position;
620                 if (ftp_abort(h) < 0) {
621                     s->position = pos;
622                     return AVERROR(EIO);
623                 }
624                 s->position = pos;
625             }
626         }
627         if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
628             /* Server closed connection. Probably due to inactivity */
629             int64_t pos = s->position;
630             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
631             if ((err = ftp_abort(h)) < 0)
632                 return err;
633             if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
634                 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
635                 return err;
636             }
637             if (!retry_done) {
638                 retry_done = 1;
639                 goto retry;
640             }
641         }
642         return read;
643     }
644
645     av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
646     return AVERROR(EIO);
647 }
648
649 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
650 {
651     int err;
652     FTPContext *s = h->priv_data;
653     int written;
654
655     av_dlog(h, "ftp protocol write %d bytes\n", size);
656
657     if (s->state == DISCONNECTED) {
658         if ((err = ftp_connect_data_connection(h)) < 0)
659             return err;
660     }
661     if (s->state == READY) {
662         if ((err = ftp_store(s)) < 0)
663             return err;
664     }
665     if (s->conn_data && s->state == UPLOADING) {
666         written = ffurl_write(s->conn_data, buf, size);
667         if (written > 0) {
668             s->position += written;
669             s->filesize = FFMAX(s->filesize, s->position);
670         }
671         return written;
672     }
673
674     av_log(h, AV_LOG_ERROR, "FTP write failed\n");
675     return AVERROR(EIO);
676 }
677
678 static int ftp_close(URLContext *h)
679 {
680     av_dlog(h, "ftp protocol close\n");
681
682     ftp_close_both_connections(h->priv_data);
683
684     return 0;
685 }
686
687 static int ftp_get_file_handle(URLContext *h)
688 {
689     FTPContext *s = h->priv_data;
690
691     av_dlog(h, "ftp protocol get_file_handle\n");
692
693     if (s->conn_data)
694         return ffurl_get_file_handle(s->conn_data);
695
696     return AVERROR(EIO);
697 }
698
699 static int ftp_shutdown(URLContext *h, int flags)
700 {
701     FTPContext *s = h->priv_data;
702
703     av_dlog(h, "ftp protocol shutdown\n");
704
705     if (s->conn_data)
706         return ffurl_shutdown(s->conn_data, flags);
707
708     return AVERROR(EIO);
709 }
710
711 URLProtocol ff_ftp_protocol = {
712     .name                = "ftp",
713     .url_open            = ftp_open,
714     .url_read            = ftp_read,
715     .url_write           = ftp_write,
716     .url_seek            = ftp_seek,
717     .url_close           = ftp_close,
718     .url_get_file_handle = ftp_get_file_handle,
719     .url_shutdown        = ftp_shutdown,
720     .priv_data_size      = sizeof(FTPContext),
721     .priv_data_class     = &ftp_context_class,
722     .flags               = URL_PROTOCOL_FLAG_NETWORK,
723 };