ftp: fix flush control connection input
[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 <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);
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 = NULL, *end = NULL;
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     for (i = 0; res[i]; ++i) {
277         if (res[i] == '(') {
278             start = res + i + 1;
279         } else if (res[i] == ')') {
280             end = res + i;
281             break;
282         }
283     }
284     if (!start || !end)
285         goto fail;
286
287     *end  = '\0';
288     /* skip ip */
289     if (!av_strtok(start, ",", &end)) goto fail;
290     if (!av_strtok(end, ",", &end)) goto fail;
291     if (!av_strtok(end, ",", &end)) goto fail;
292     if (!av_strtok(end, ",", &end)) goto fail;
293
294     /* parse port number */
295     start = av_strtok(end, ",", &end);
296     if (!start) goto fail;
297     s->server_data_port = atoi(start) * 256;
298     start = av_strtok(end, ",", &end);
299     if (!start) goto fail;
300     s->server_data_port += atoi(start);
301     av_dlog(s, "Server data port: %d\n", s->server_data_port);
302
303     av_free(res);
304     return 0;
305
306   fail:
307     av_free(res);
308     s->server_data_port = -1;
309     return AVERROR(EIO);
310 }
311
312 static int ftp_current_dir(FTPContext *s)
313 {
314     char *res = NULL, *start = NULL, *end = NULL;
315     int i;
316     const char *command = "PWD\r\n";
317     const int pwd_codes[] = {257, 0};
318
319     if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
320         goto fail;
321
322     for (i = 0; res[i]; ++i) {
323         if (res[i] == '"') {
324             if (!start) {
325                 start = res + i + 1;
326                 continue;
327             }
328             end = res + i;
329             break;
330         }
331     }
332
333     if (!end)
334         goto fail;
335
336     if (end > res && end[-1] == '/') {
337         end[-1] = '\0';
338     } else
339         *end = '\0';
340     av_strlcpy(s->path, start, sizeof(s->path));
341
342     av_free(res);
343     return 0;
344
345   fail:
346     av_free(res);
347     return AVERROR(EIO);
348 }
349
350 static int ftp_file_size(FTPContext *s)
351 {
352     char command[CONTROL_BUFFER_SIZE];
353     char *res = NULL;
354     const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
355
356     snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
357     if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
358         s->filesize = strtoll(&res[4], NULL, 10);
359     } else {
360         s->filesize = -1;
361         av_free(res);
362         return AVERROR(EIO);
363     }
364
365     av_free(res);
366     return 0;
367 }
368
369 static int ftp_retrieve(FTPContext *s)
370 {
371     char command[CONTROL_BUFFER_SIZE];
372     const int retr_codes[] = {150, 550, 0}; /* 550 is incorrect code */
373
374     snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
375     if (ftp_send_command(s, command, retr_codes, NULL) != 150)
376         return AVERROR(EIO);
377
378     s->state = DOWNLOADING;
379
380     return 0;
381 }
382
383 static int ftp_store(FTPContext *s)
384 {
385     char command[CONTROL_BUFFER_SIZE];
386     const int stor_codes[] = {150, 0};
387
388     snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
389     if (!ftp_send_command(s, command, stor_codes, NULL))
390         return AVERROR(EIO);
391
392     s->state = UPLOADING;
393
394     return 0;
395 }
396
397 static int ftp_type(FTPContext *s)
398 {
399     const char *command = "TYPE I\r\n";
400     const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
401
402     if (ftp_send_command(s, command, type_codes, NULL) != 200)
403         return AVERROR(EIO);
404
405     return 0;
406 }
407
408 static int ftp_restart(FTPContext *s, int64_t pos)
409 {
410     char command[CONTROL_BUFFER_SIZE];
411     const int rest_codes[] = {350, 501, 0}; /* 501 is incorrect code */
412
413     snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
414     if (ftp_send_command(s, command, rest_codes, NULL) != 350)
415         return AVERROR(EIO);
416
417     return 0;
418 }
419
420 static int ftp_connect_control_connection(URLContext *h)
421 {
422     char buf[CONTROL_BUFFER_SIZE], opts_format[20];
423     int err;
424     AVDictionary *opts = NULL;
425     FTPContext *s = h->priv_data;
426     const int connect_codes[] = {220, 0};
427
428     s->conn_control_block_flag = 0;
429
430     if (!s->conn_control) {
431         ff_url_join(buf, sizeof(buf), "tcp", NULL,
432                     s->hostname, s->server_control_port, NULL);
433         if (s->rw_timeout != -1) {
434             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
435             av_dict_set(&opts, "timeout", opts_format, 0);
436         } /* if option is not given, don't pass it and let tcp use its own default */
437         err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
438                          &s->conn_control_interrupt_cb, &opts);
439         av_dict_free(&opts);
440         if (err < 0) {
441             av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
442             return err;
443         }
444
445         /* consume all messages from server */
446         if (!ftp_status(s, NULL, connect_codes)) {
447             av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
448             err = AVERROR(EACCES);
449             return err;
450         }
451
452         if ((err = ftp_auth(s)) < 0) {
453             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
454             return err;
455         }
456
457         if ((err = ftp_type(s)) < 0) {
458             av_dlog(h, "Set content type failed\n");
459             return err;
460         }
461     }
462     return 0;
463 }
464
465 static int ftp_connect_data_connection(URLContext *h)
466 {
467     int err;
468     char buf[CONTROL_BUFFER_SIZE], opts_format[20];
469     AVDictionary *opts = NULL;
470     FTPContext *s = h->priv_data;
471
472     if (!s->conn_data) {
473         /* Enter passive mode */
474         if ((err = ftp_passive_mode(s)) < 0) {
475             av_dlog(h, "Set passive mode failed\n");
476             return err;
477         }
478         /* Open data connection */
479         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
480         if (s->rw_timeout != -1) {
481             snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
482             av_dict_set(&opts, "timeout", opts_format, 0);
483         } /* if option is not given, don't pass it and let tcp use its own default */
484         err = ffurl_open(&s->conn_data, buf, AVIO_FLAG_READ_WRITE,
485                          &h->interrupt_callback, &opts);
486         av_dict_free(&opts);
487         if (err < 0)
488             return err;
489
490         if (s->position)
491             if ((err = ftp_restart(s, s->position)) < 0)
492                 return err;
493     }
494     s->state = READY;
495     return 0;
496 }
497
498 static int ftp_abort(URLContext *h)
499 {
500     int err;
501     ftp_close_both_connections(h->priv_data);
502     if ((err = ftp_connect_control_connection(h)) < 0) {
503         av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
504         return err;
505     }
506     return 0;
507 }
508
509 static int ftp_open(URLContext *h, const char *url, int flags)
510 {
511     char proto[10], path[MAX_URL_SIZE];
512     int err;
513     FTPContext *s = h->priv_data;
514
515     av_dlog(h, "ftp protocol open\n");
516
517     s->state = DISCONNECTED;
518     s->filesize = -1;
519     s->position = 0;
520     s->conn_control_interrupt_cb.opaque = s;
521     s->conn_control_interrupt_cb.callback = ftp_conn_control_block_control;
522
523     av_url_split(proto, sizeof(proto),
524                  s->credencials, sizeof(s->credencials),
525                  s->hostname, sizeof(s->hostname),
526                  &s->server_control_port,
527                  path, sizeof(path),
528                  url);
529
530     if (s->server_control_port < 0 || s->server_control_port > 65535)
531         s->server_control_port = 21;
532
533     if ((err = ftp_connect_control_connection(h)) < 0)
534         goto fail;
535
536     if ((err = ftp_current_dir(s)) < 0)
537         goto fail;
538     av_strlcat(s->path, path, sizeof(s->path));
539
540     if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
541         h->is_streamed = 1;
542     if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
543         h->is_streamed = 1;
544
545     return 0;
546
547   fail:
548     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
549     ffurl_closep(&s->conn_control);
550     ffurl_closep(&s->conn_data);
551     return err;
552 }
553
554 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
555 {
556     FTPContext *s = h->priv_data;
557     int err;
558     int64_t new_pos;
559
560     av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
561
562     switch(whence) {
563     case AVSEEK_SIZE:
564         return s->filesize;
565     case SEEK_SET:
566         new_pos = pos;
567         break;
568     case SEEK_CUR:
569         new_pos = s->position + pos;
570         break;
571     case SEEK_END:
572         if (s->filesize < 0)
573             return AVERROR(EIO);
574         new_pos = s->filesize + pos;
575         break;
576     default:
577         return AVERROR(EINVAL);
578     }
579
580     if  (h->is_streamed)
581         return AVERROR(EIO);
582
583     new_pos = FFMAX(0, new_pos);
584     if (s->filesize >= 0)
585         new_pos = FFMIN(s->filesize, new_pos);
586
587     if (new_pos != s->position) {
588         /* XXX: Full abort is a save solution here.
589            Some optimalizations are possible, but may lead to crazy states of FTP server.
590            The worst scenario would be when FTP server closed both connection due to no transfer. */
591         if ((err = ftp_abort(h)) < 0)
592             return err;
593
594         s->position = new_pos;
595     }
596     return new_pos;
597 }
598
599 static int ftp_read(URLContext *h, unsigned char *buf, int size)
600 {
601     FTPContext *s = h->priv_data;
602     int read, err, retry_done = 0;
603
604     av_dlog(h, "ftp protocol read %d bytes\n", size);
605   retry:
606     if (s->state == DISCONNECTED) {
607         if (s->position >= s->filesize)
608             return 0;
609         if ((err = ftp_connect_data_connection(h)) < 0)
610             return err;
611     }
612     if (s->state == READY) {
613         if (s->position >= s->filesize)
614             return 0;
615         if ((err = ftp_retrieve(s)) < 0)
616             return err;
617     }
618     if (s->conn_data && s->state == DOWNLOADING) {
619         read = ffurl_read(s->conn_data, buf, size);
620         if (read >= 0) {
621             s->position += read;
622             if (s->position >= s->filesize) {
623                 if (ftp_abort(h) < 0)
624                     return AVERROR(EIO);
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 };