ftp: fix using uninitialized value
[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 > 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 = 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         return err;
504     return 0;
505 }
506
507 static int ftp_open(URLContext *h, const char *url, int flags)
508 {
509     char proto[10], path[MAX_URL_SIZE];
510     int err;
511     FTPContext *s = h->priv_data;
512
513     av_dlog(h, "ftp protocol open\n");
514
515     s->state = DISCONNECTED;
516     s->filesize = -1;
517     s->position = 0;
518     s->conn_control_interrupt_cb.opaque = s;
519     s->conn_control_interrupt_cb.callback = ftp_conn_control_block_control;
520
521     av_url_split(proto, sizeof(proto),
522                  s->credencials, sizeof(s->credencials),
523                  s->hostname, sizeof(s->hostname),
524                  &s->server_control_port,
525                  path, sizeof(path),
526                  url);
527
528     if (s->server_control_port < 0 || s->server_control_port > 65535)
529         s->server_control_port = 21;
530
531     if ((err = ftp_connect_control_connection(h)) < 0)
532         goto fail;
533
534     if ((err = ftp_current_dir(s)) < 0)
535         goto fail;
536     av_strlcat(s->path, path, sizeof(s->path));
537
538     if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
539         h->is_streamed = 1;
540     if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
541         h->is_streamed = 1;
542
543     return 0;
544
545   fail:
546     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
547     ffurl_closep(&s->conn_control);
548     ffurl_closep(&s->conn_data);
549     return err;
550 }
551
552 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
553 {
554     FTPContext *s = h->priv_data;
555     int err;
556     int64_t new_pos;
557
558     av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
559
560     switch(whence) {
561     case AVSEEK_SIZE:
562         return s->filesize;
563     case SEEK_SET:
564         new_pos = pos;
565         break;
566     case SEEK_CUR:
567         new_pos = s->position + pos;
568         break;
569     case SEEK_END:
570         if (s->filesize < 0)
571             return AVERROR(EIO);
572         new_pos = s->filesize + pos;
573         break;
574     default:
575         return AVERROR(EINVAL);
576     }
577
578     if  (h->is_streamed)
579         return AVERROR(EIO);
580
581     new_pos = FFMAX(0, new_pos);
582     if (s->filesize >= 0)
583         new_pos = FFMIN(s->filesize, new_pos);
584
585     if (new_pos != s->position) {
586         /* XXX: Full abort is a save solution here.
587            Some optimalizations are possible, but may lead to crazy states of FTP server.
588            The worst scenario would be when FTP server closed both connection due to no transfer. */
589         if ((err = ftp_abort(h)) < 0)
590             return err;
591
592         s->position = new_pos;
593     }
594     return new_pos;
595 }
596
597 static int ftp_read(URLContext *h, unsigned char *buf, int size)
598 {
599     FTPContext *s = h->priv_data;
600     int read, err, retry_done = 0;
601
602     av_dlog(h, "ftp protocol read %d bytes\n", size);
603   retry:
604     if (s->state == DISCONNECTED) {
605         if ((err = ftp_connect_data_connection(h)) < 0)
606             return err;
607     }
608     if (s->state == READY) {
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                 if (ftp_abort(h) < 0)
618                     return AVERROR(EIO);
619             }
620         }
621         if (!read && s->position < s->filesize && !h->is_streamed) {
622             /* Server closed connection. Probably due to inactivity */
623             /* TODO: Consider retry before reconnect */
624             int64_t pos = s->position;
625             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
626             if ((err = ftp_abort(h)) < 0) {
627                 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
628                 return err;
629             }
630             if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
631                 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
632                 return err;
633             }
634             if (!retry_done) {
635                 retry_done = 1;
636                 goto retry;
637             }
638         }
639         return read;
640     }
641
642     av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
643     return AVERROR(EIO);
644 }
645
646 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
647 {
648     int err;
649     FTPContext *s = h->priv_data;
650     int written;
651
652     av_dlog(h, "ftp protocol write %d bytes\n", size);
653
654     if (s->state == DISCONNECTED) {
655         if ((err = ftp_connect_data_connection(h)) < 0)
656             return err;
657     }
658     if (s->state == READY) {
659         if ((err = ftp_store(s)) < 0)
660             return err;
661     }
662     if (s->conn_data && s->state == UPLOADING) {
663         written = ffurl_write(s->conn_data, buf, size);
664         if (written > 0) {
665             s->position += written;
666             s->filesize = FFMAX(s->filesize, s->position);
667         }
668         return written;
669     }
670
671     av_log(h, AV_LOG_ERROR, "FTP write failed\n");
672     return AVERROR(EIO);
673 }
674
675 static int ftp_close(URLContext *h)
676 {
677     av_dlog(h, "ftp protocol close\n");
678
679     ftp_close_both_connections(h->priv_data);
680
681     return 0;
682 }
683
684 static int ftp_get_file_handle(URLContext *h)
685 {
686     FTPContext *s = h->priv_data;
687
688     av_dlog(h, "ftp protocol get_file_handle\n");
689
690     if (s->conn_data)
691         return ffurl_get_file_handle(s->conn_data);
692
693     return AVERROR(EIO);
694 }
695
696 static int ftp_shutdown(URLContext *h, int flags)
697 {
698     FTPContext *s = h->priv_data;
699
700     av_dlog(h, "ftp protocol shutdown\n");
701
702     if (s->conn_data)
703         return ffurl_shutdown(s->conn_data, flags);
704
705     return AVERROR(EIO);
706 }
707
708 URLProtocol ff_ftp_protocol = {
709     .name                = "ftp",
710     .url_open            = ftp_open,
711     .url_read            = ftp_read,
712     .url_write           = ftp_write,
713     .url_seek            = ftp_seek,
714     .url_close           = ftp_close,
715     .url_get_file_handle = ftp_get_file_handle,
716     .url_shutdown        = ftp_shutdown,
717     .priv_data_size      = sizeof(FTPContext),
718     .priv_data_class     = &ftp_context_class,
719     .flags               = URL_PROTOCOL_FLAG_NETWORK,
720 };