lavf/ftp: properly read an empty path
[ffmpeg.git] / libavformat / ftp.c
1 /*
2  * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include "libavutil/avstring.h"
22 #include "libavutil/parseutils.h"
23 #include "avformat.h"
24 #include "internal.h"
25 #include "url.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/bprint.h"
28
29 #define CONTROL_BUFFER_SIZE 1024
30 #define DIR_BUFFER_SIZE 4096
31
32 typedef enum {
33     UNKNOWN,
34     READY,
35     DOWNLOADING,
36     UPLOADING,
37     LISTING_DIR,
38     DISCONNECTED
39 } FTPState;
40
41 typedef struct {
42     const AVClass *class;
43     URLContext *conn_control;                    /**< Control connection */
44     URLContext *conn_data;                       /**< Data connection, NULL when not connected */
45     uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
46     uint8_t *control_buf_ptr, *control_buf_end;
47     int server_data_port;                        /**< Data connection port opened by server, -1 on error. */
48     int server_control_port;                     /**< Control connection port, default is 21 */
49     char *hostname;                              /**< Server address. */
50     char *user;                                  /**< Server user */
51     char *password;                              /**< Server user's password */
52     char *path;                                  /**< Path to resource on server. */
53     int64_t filesize;                            /**< Size of file on server, -1 on error. */
54     int64_t position;                            /**< Current position, calculated. */
55     int rw_timeout;                              /**< Network timeout. */
56     const char *anonymous_password;              /**< Password to be used for anonymous user. An email should be used. */
57     int write_seekable;                          /**< Control seekability, 0 = disable, 1 = enable. */
58     FTPState state;                              /**< State of data connection */
59     char *dir_buffer;
60     size_t dir_buffer_size;
61     size_t dir_buffer_offset;
62     int utf8;
63 } FTPContext;
64
65 #define OFFSET(x) offsetof(FTPContext, x)
66 #define D AV_OPT_FLAG_DECODING_PARAM
67 #define E AV_OPT_FLAG_ENCODING_PARAM
68 static const AVOption options[] = {
69     {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
70     {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
71     {"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 },
72     {NULL}
73 };
74
75 static const AVClass ftp_context_class = {
76     .class_name     = "ftp",
77     .item_name      = av_default_item_name,
78     .option         = options,
79     .version        = LIBAVUTIL_VERSION_INT,
80 };
81
82 static int ftp_close(URLContext *h);
83
84 static int ftp_getc(FTPContext *s)
85 {
86     int len;
87     if (s->control_buf_ptr >= s->control_buf_end) {
88         len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
89         if (len < 0) {
90             return len;
91         } else if (!len) {
92             return -1;
93         } else {
94             s->control_buf_ptr = s->control_buffer;
95             s->control_buf_end = s->control_buffer + len;
96         }
97     }
98     return *s->control_buf_ptr++;
99 }
100
101 static int ftp_get_line(FTPContext *s, char *line, int line_size)
102 {
103     int ch;
104     char *q = line;
105
106     for (;;) {
107         ch = ftp_getc(s);
108         if (ch < 0) {
109             return ch;
110         }
111         if (ch == '\n') {
112             /* process line */
113             if (q > line && q[-1] == '\r')
114                 q--;
115             *q = '\0';
116             return 0;
117         } else {
118             if ((q - line) < line_size - 1)
119                 *q++ = ch;
120         }
121     }
122 }
123
124 /*
125  * This routine returns ftp server response code.
126  * Server may send more than one response for a certain command.
127  * First expected code is returned.
128  */
129 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
130 {
131     int err, i, dash = 0, result = 0, code_found = 0, linesize;
132     char buf[CONTROL_BUFFER_SIZE];
133     AVBPrint line_buffer;
134
135     if (line)
136         av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
137
138     while (!code_found || dash) {
139         if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
140             if (line)
141                 av_bprint_finalize(&line_buffer, NULL);
142             return err;
143         }
144
145         av_log(s, AV_LOG_DEBUG, "%s\n", buf);
146
147         linesize = strlen(buf);
148         err = 0;
149         if (linesize >= 3) {
150             for (i = 0; i < 3; ++i) {
151                 if (buf[i] < '0' || buf[i] > '9') {
152                     err = 0;
153                     break;
154                 }
155                 err *= 10;
156                 err += buf[i] - '0';
157             }
158         }
159
160         if (!code_found) {
161             if (err >= 500) {
162                 code_found = 1;
163                 result = err;
164             } else
165                 for (i = 0; response_codes[i]; ++i) {
166                     if (err == response_codes[i]) {
167                         code_found = 1;
168                         result = err;
169                         break;
170                     }
171                 }
172         }
173         if (code_found) {
174             if (line)
175                 av_bprintf(&line_buffer, "%s\r\n", buf);
176             if (linesize >= 4) {
177                 if (!dash && buf[3] == '-')
178                     dash = err;
179                 else if (err == dash && buf[3] == ' ')
180                     dash = 0;
181             }
182         }
183     }
184
185     if (line)
186         av_bprint_finalize(&line_buffer, line);
187     return result;
188 }
189
190 static int ftp_send_command(FTPContext *s, const char *command,
191                             const int response_codes[], char **response)
192 {
193     int err;
194
195     if (response)
196         *response = NULL;
197
198     if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
199         return err;
200     if (!err)
201         return -1;
202
203     /* return status */
204     if (response_codes) {
205         return ftp_status(s, response, response_codes);
206     }
207     return 0;
208 }
209
210 static void ftp_close_data_connection(FTPContext *s)
211 {
212     ffurl_closep(&s->conn_data);
213     s->position = 0;
214     s->state = DISCONNECTED;
215 }
216
217 static void ftp_close_both_connections(FTPContext *s)
218 {
219     ffurl_closep(&s->conn_control);
220     ftp_close_data_connection(s);
221 }
222
223 static int ftp_auth(FTPContext *s)
224 {
225     char buf[CONTROL_BUFFER_SIZE];
226     int err;
227     static const int user_codes[] = {331, 230, 0};
228     static const int pass_codes[] = {230, 0};
229
230     snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
231     err = ftp_send_command(s, buf, user_codes, NULL);
232     if (err == 331) {
233         if (s->password) {
234             snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
235             err = ftp_send_command(s, buf, pass_codes, NULL);
236         } else
237             return AVERROR(EACCES);
238     }
239     if (err != 230)
240         return AVERROR(EACCES);
241
242     return 0;
243 }
244
245 static int ftp_passive_mode_epsv(FTPContext *s)
246 {
247     char *res = NULL, *start = NULL, *end = NULL;
248     int i;
249     static const char d = '|';
250     static const char *command = "EPSV\r\n";
251     static const int epsv_codes[] = {229, 0};
252
253     if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
254         goto fail;
255
256     for (i = 0; res[i]; ++i) {
257         if (res[i] == '(') {
258             start = res + i + 1;
259         } else if (res[i] == ')') {
260             end = res + i;
261             break;
262         }
263     }
264     if (!start || !end)
265         goto fail;
266
267     *end = '\0';
268     if (strlen(start) < 5)
269         goto fail;
270     if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
271         goto fail;
272     start += 3;
273     end[-1] = '\0';
274
275     s->server_data_port = atoi(start);
276     av_dlog(s, "Server data port: %d\n", s->server_data_port);
277
278     av_free(res);
279     return 0;
280
281   fail:
282     av_free(res);
283     s->server_data_port = -1;
284     return AVERROR(ENOSYS);
285 }
286
287 static int ftp_passive_mode(FTPContext *s)
288 {
289     char *res = NULL, *start = NULL, *end = NULL;
290     int i;
291     static const char *command = "PASV\r\n";
292     static const int pasv_codes[] = {227, 0};
293
294     if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
295         goto fail;
296
297     for (i = 0; res[i]; ++i) {
298         if (res[i] == '(') {
299             start = res + i + 1;
300         } else if (res[i] == ')') {
301             end = res + i;
302             break;
303         }
304     }
305     if (!start || !end)
306         goto fail;
307
308     *end  = '\0';
309     /* skip ip */
310     if (!av_strtok(start, ",", &end)) goto fail;
311     if (!av_strtok(end, ",", &end)) goto fail;
312     if (!av_strtok(end, ",", &end)) goto fail;
313     if (!av_strtok(end, ",", &end)) goto fail;
314
315     /* parse port number */
316     start = av_strtok(end, ",", &end);
317     if (!start) goto fail;
318     s->server_data_port = atoi(start) * 256;
319     start = av_strtok(end, ",", &end);
320     if (!start) goto fail;
321     s->server_data_port += atoi(start);
322     av_dlog(s, "Server data port: %d\n", s->server_data_port);
323
324     av_free(res);
325     return 0;
326
327   fail:
328     av_free(res);
329     s->server_data_port = -1;
330     return AVERROR(EIO);
331 }
332
333 static int ftp_current_dir(FTPContext *s)
334 {
335     char *res = NULL, *start = NULL, *end = NULL;
336     int i;
337     static const char *command = "PWD\r\n";
338     static const int pwd_codes[] = {257, 0};
339
340     if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
341         goto fail;
342
343     for (i = 0; res[i]; ++i) {
344         if (res[i] == '"') {
345             if (!start) {
346                 start = res + i + 1;
347                 continue;
348             }
349             end = res + i;
350             break;
351         }
352     }
353
354     if (!end)
355         goto fail;
356
357     *end = '\0';
358     s->path = av_strdup(start);
359
360     av_free(res);
361
362     if (!s->path)
363         return AVERROR(ENOMEM);
364     return 0;
365
366   fail:
367     av_free(res);
368     return AVERROR(EIO);
369 }
370
371 static int ftp_file_size(FTPContext *s)
372 {
373     char command[CONTROL_BUFFER_SIZE];
374     char *res = NULL;
375     static const int size_codes[] = {213, 0};
376
377     snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
378     if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
379         s->filesize = strtoll(&res[4], NULL, 10);
380     } else {
381         s->filesize = -1;
382         av_free(res);
383         return AVERROR(EIO);
384     }
385
386     av_free(res);
387     return 0;
388 }
389
390 static int ftp_retrieve(FTPContext *s)
391 {
392     char command[CONTROL_BUFFER_SIZE];
393     static const int retr_codes[] = {150, 0};
394
395     snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
396     if (ftp_send_command(s, command, retr_codes, NULL) != 150)
397         return AVERROR(EIO);
398
399     s->state = DOWNLOADING;
400
401     return 0;
402 }
403
404 static int ftp_store(FTPContext *s)
405 {
406     char command[CONTROL_BUFFER_SIZE];
407     static const int stor_codes[] = {150, 0};
408
409     snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
410     if (ftp_send_command(s, command, stor_codes, NULL) != 150)
411         return AVERROR(EIO);
412
413     s->state = UPLOADING;
414
415     return 0;
416 }
417
418 static int ftp_type(FTPContext *s)
419 {
420     static const char *command = "TYPE I\r\n";
421     static const int type_codes[] = {200, 0};
422
423     if (ftp_send_command(s, command, type_codes, NULL) != 200)
424         return AVERROR(EIO);
425
426     return 0;
427 }
428
429 static int ftp_restart(FTPContext *s, int64_t pos)
430 {
431     char command[CONTROL_BUFFER_SIZE];
432     static const int rest_codes[] = {350, 0};
433
434     snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
435     if (ftp_send_command(s, command, rest_codes, NULL) != 350)
436         return AVERROR(EIO);
437
438     return 0;
439 }
440
441 static int ftp_set_dir(FTPContext *s)
442 {
443     static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
444     char command[MAX_URL_SIZE];
445
446     snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
447     if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
448         return AVERROR(EIO);
449     return 0;
450 }
451
452 static int ftp_list(FTPContext *s)
453 {
454     static const char *command = "MLSD\r\n";
455     static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
456
457     if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
458         return AVERROR(ENOSYS);
459     s->state = LISTING_DIR;
460     return 0;
461 }
462
463 static int ftp_features(FTPContext *s)
464 {
465     static const char *feat_command        = "FEAT\r\n";
466     static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
467     static const int feat_codes[] = {211, 0};
468     static const int opts_codes[] = {200, 451, 0};
469     char *feat = NULL;
470
471     if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
472         if (av_stristr(feat, "UTF8")) {
473             if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200)
474                 s->utf8 = 1;
475         }
476     }
477     av_freep(&feat);
478
479     return 0;
480 }
481
482 static int ftp_connect_control_connection(URLContext *h)
483 {
484     char buf[CONTROL_BUFFER_SIZE], *response = NULL;
485     int err;
486     AVDictionary *opts = NULL;
487     FTPContext *s = h->priv_data;
488     static const int connect_codes[] = {220, 0};
489
490     if (!s->conn_control) {
491         ff_url_join(buf, sizeof(buf), "tcp", NULL,
492                     s->hostname, s->server_control_port, NULL);
493         if (s->rw_timeout != -1) {
494             av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
495         } /* if option is not given, don't pass it and let tcp use its own default */
496         err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
497                          &h->interrupt_callback, &opts);
498         av_dict_free(&opts);
499         if (err < 0) {
500             av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
501             return err;
502         }
503
504         /* check if server is ready */
505         if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
506             av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
507             return AVERROR(EACCES);
508         }
509
510         if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
511             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.");
512         }
513         av_free(response);
514
515         if ((err = ftp_auth(s)) < 0) {
516             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
517             return err;
518         }
519
520         if ((err = ftp_type(s)) < 0) {
521             av_log(h, AV_LOG_ERROR, "Set content type failed\n");
522             return err;
523         }
524
525         ftp_features(s);
526     }
527     return 0;
528 }
529
530 static int ftp_connect_data_connection(URLContext *h)
531 {
532     int err;
533     char buf[CONTROL_BUFFER_SIZE];
534     AVDictionary *opts = NULL;
535     FTPContext *s = h->priv_data;
536
537     if (!s->conn_data) {
538         /* Enter passive mode */
539         if (ftp_passive_mode_epsv(s) < 0) {
540             /* Use PASV as fallback */
541             if ((err = ftp_passive_mode(s)) < 0)
542                 return err;
543         }
544         /* Open data connection */
545         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
546         if (s->rw_timeout != -1) {
547             av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
548         } /* if option is not given, don't pass it and let tcp use its own default */
549         err = ffurl_open(&s->conn_data, buf, h->flags,
550                          &h->interrupt_callback, &opts);
551         av_dict_free(&opts);
552         if (err < 0)
553             return err;
554
555         if (s->position)
556             if ((err = ftp_restart(s, s->position)) < 0)
557                 return err;
558     }
559     s->state = READY;
560     return 0;
561 }
562
563 static int ftp_abort(URLContext *h)
564 {
565     static const char *command = "ABOR\r\n";
566     int err;
567     static const int abor_codes[] = {225, 226, 0};
568     FTPContext *s = h->priv_data;
569
570     /* According to RCF 959:
571        "ABOR command tells the server to abort the previous FTP
572        service command and any associated transfer of data."
573
574        There are FTP server implementations that don't response
575        to any commands during data transfer in passive mode (including ABOR).
576
577        This implementation closes data connection by force.
578     */
579
580     if (ftp_send_command(s, command, NULL, NULL) < 0) {
581         ftp_close_both_connections(s);
582         if ((err = ftp_connect_control_connection(h)) < 0) {
583             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
584             return err;
585         }
586     } else {
587         ftp_close_data_connection(s);
588         if (ftp_status(s, NULL, abor_codes) < 225) {
589             /* wu-ftpd also closes control connection after data connection closing */
590             ffurl_closep(&s->conn_control);
591             if ((err = ftp_connect_control_connection(h)) < 0) {
592                 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
593                 return err;
594             }
595         }
596     }
597
598     return 0;
599 }
600
601 static int ftp_connect(URLContext *h, const char *url)
602 {
603     char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
604     const char *tok_user = NULL, *tok_pass = NULL;
605     char *end = NULL, *newpath = NULL;
606     int err;
607     FTPContext *s = h->priv_data;
608
609     s->state = DISCONNECTED;
610     s->filesize = -1;
611     s->position = 0;
612
613     av_url_split(proto, sizeof(proto),
614                  credencials, sizeof(credencials),
615                  hostname, sizeof(hostname),
616                  &s->server_control_port,
617                  path, sizeof(path),
618                  url);
619
620     tok_user = av_strtok(credencials, ":", &end);
621     tok_pass = av_strtok(end, ":", &end);
622     if (!tok_user) {
623         tok_user = "anonymous";
624         tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
625     }
626     s->user = av_strdup(tok_user);
627     s->password = av_strdup(tok_pass);
628     s->hostname = av_strdup(hostname);
629     if (!s->hostname || !s->user || (tok_pass && !s->password)) {
630         return AVERROR(ENOMEM);
631     }
632
633     if (s->server_control_port < 0 || s->server_control_port > 65535)
634         s->server_control_port = 21;
635
636     if ((err = ftp_connect_control_connection(h)) < 0)
637         return err;
638
639     if ((err = ftp_current_dir(s)) < 0)
640         return err;
641
642     newpath = av_append_path_component(s->path, path);
643     if (!newpath)
644         return AVERROR(ENOMEM);
645     av_free(s->path);
646     s->path = newpath;
647
648     return 0;
649 }
650
651 static int ftp_open(URLContext *h, const char *url, int flags)
652 {
653     FTPContext *s = h->priv_data;
654     int err;
655
656     av_dlog(h, "ftp protocol open\n");
657
658     if ((err = ftp_connect(h, url)) < 0)
659         goto fail;
660
661     if (ftp_restart(s, 0) < 0) {
662         h->is_streamed = 1;
663     } else {
664         if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
665             h->is_streamed = 1;
666         if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
667             h->is_streamed = 1;
668     }
669
670     return 0;
671
672   fail:
673     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
674     ftp_close(h);
675     return err;
676 }
677
678 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
679 {
680     FTPContext *s = h->priv_data;
681     int err;
682     int64_t new_pos, fake_pos;
683
684     av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
685
686     switch(whence) {
687     case AVSEEK_SIZE:
688         return s->filesize;
689     case SEEK_SET:
690         new_pos = pos;
691         break;
692     case SEEK_CUR:
693         new_pos = s->position + pos;
694         break;
695     case SEEK_END:
696         if (s->filesize < 0)
697             return AVERROR(EIO);
698         new_pos = s->filesize + pos;
699         break;
700     default:
701         return AVERROR(EINVAL);
702     }
703
704     if (h->is_streamed)
705         return AVERROR(EIO);
706
707     if (new_pos < 0) {
708         av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
709         return AVERROR(EINVAL);
710     }
711
712     fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
713     if (fake_pos != s->position) {
714         if ((err = ftp_abort(h)) < 0)
715             return err;
716         s->position = fake_pos;
717     }
718     return new_pos;
719 }
720
721 static int ftp_read(URLContext *h, unsigned char *buf, int size)
722 {
723     FTPContext *s = h->priv_data;
724     int read, err, retry_done = 0;
725
726     av_dlog(h, "ftp protocol read %d bytes\n", size);
727   retry:
728     if (s->state == DISCONNECTED) {
729         /* optimization */
730         if (s->position >= s->filesize)
731             return 0;
732         if ((err = ftp_connect_data_connection(h)) < 0)
733             return err;
734     }
735     if (s->state == READY) {
736         if (s->position >= s->filesize)
737             return 0;
738         if ((err = ftp_retrieve(s)) < 0)
739             return err;
740     }
741     if (s->conn_data && s->state == DOWNLOADING) {
742         read = ffurl_read(s->conn_data, buf, size);
743         if (read >= 0) {
744             s->position += read;
745             if (s->position >= s->filesize) {
746                 /* server will terminate, but keep current position to avoid madness */
747                 /* save position to restart from it */
748                 int64_t pos = s->position;
749                 if (ftp_abort(h) < 0) {
750                     s->position = pos;
751                     return AVERROR(EIO);
752                 }
753                 s->position = pos;
754             }
755         }
756         if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
757             /* Server closed connection. Probably due to inactivity */
758             int64_t pos = s->position;
759             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
760             if ((err = ftp_abort(h)) < 0)
761                 return err;
762             if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
763                 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
764                 return err;
765             }
766             if (!retry_done) {
767                 retry_done = 1;
768                 goto retry;
769             }
770         }
771         return read;
772     }
773
774     av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
775     return AVERROR(EIO);
776 }
777
778 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
779 {
780     int err;
781     FTPContext *s = h->priv_data;
782     int written;
783
784     av_dlog(h, "ftp protocol write %d bytes\n", size);
785
786     if (s->state == DISCONNECTED) {
787         if ((err = ftp_connect_data_connection(h)) < 0)
788             return err;
789     }
790     if (s->state == READY) {
791         if ((err = ftp_store(s)) < 0)
792             return err;
793     }
794     if (s->conn_data && s->state == UPLOADING) {
795         written = ffurl_write(s->conn_data, buf, size);
796         if (written > 0) {
797             s->position += written;
798             s->filesize = FFMAX(s->filesize, s->position);
799         }
800         return written;
801     }
802
803     av_log(h, AV_LOG_ERROR, "FTP write failed\n");
804     return AVERROR(EIO);
805 }
806
807 static int ftp_close(URLContext *h)
808 {
809     FTPContext *s = h->priv_data;
810
811     av_dlog(h, "ftp protocol close\n");
812
813     ftp_close_both_connections(s);
814     av_freep(&s->user);
815     av_freep(&s->password);
816     av_freep(&s->hostname);
817     av_freep(&s->path);
818
819     return 0;
820 }
821
822 static int ftp_get_file_handle(URLContext *h)
823 {
824     FTPContext *s = h->priv_data;
825
826     av_dlog(h, "ftp protocol get_file_handle\n");
827
828     if (s->conn_data)
829         return ffurl_get_file_handle(s->conn_data);
830
831     return AVERROR(EIO);
832 }
833
834 static int ftp_shutdown(URLContext *h, int flags)
835 {
836     FTPContext *s = h->priv_data;
837
838     av_dlog(h, "ftp protocol shutdown\n");
839
840     if (s->conn_data)
841         return ffurl_shutdown(s->conn_data, flags);
842
843     return AVERROR(EIO);
844 }
845
846 static int ftp_open_dir(URLContext *h)
847 {
848     FTPContext *s = h->priv_data;
849     int ret;
850
851     if ((ret = ftp_connect(h, h->filename)) < 0)
852         goto fail;
853     if ((ret = ftp_set_dir(s)) < 0)
854         goto fail;
855     if ((ret = ftp_connect_data_connection(h)) < 0)
856         goto fail;
857     if ((ret = ftp_list(s)) < 0)
858         goto fail;
859     s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
860     if (!s->dir_buffer) {
861         ret = AVERROR(ENOMEM);
862         goto fail;
863     }
864     s->dir_buffer[0] = 0;
865     if (s->conn_data && s->state == LISTING_DIR)
866         return 0;
867   fail:
868     ffurl_closep(&s->conn_control);
869     ffurl_closep(&s->conn_data);
870     return ret;
871 }
872
873 static int64_t ftp_parse_date(const char *date)
874 {
875     struct tm tv;
876     memset(&tv, 0, sizeof(struct tm));
877     av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
878     return INT64_C(1000000) * av_timegm(&tv);
879 }
880
881 /**
882  * @return 0 on success, negative on error, positive on entry to discard.
883  */
884 static int ftp_parse_entry(char *mlsd, AVIODirEntry *next)
885 {
886     char *fact, *value;
887     av_dlog(NULL, "%s\n", mlsd);
888     while(fact = av_strtok(mlsd, ";", &mlsd)) {
889         if (fact[0] == ' ') {
890             next->name = av_strdup(&fact[1]);
891             continue;
892         }
893         fact = av_strtok(fact, "=", &value);
894         if (!av_strcasecmp(fact, "type")) {
895             if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
896                 return 1;
897             if (!av_strcasecmp(value, "dir"))
898                 next->type = AVIO_ENTRY_DIRECTORY;
899             else if (!av_strcasecmp(value, "file"))
900                 next->type = AVIO_ENTRY_FILE;
901             else if (!av_strcasecmp(value, "OS.unix=slink:"))
902                 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
903         } else if (!av_strcasecmp(fact, "modify")) {
904             next->modification_timestamp = ftp_parse_date(value);
905         } else if (!av_strcasecmp(fact, "UNIX.mode")) {
906             next->filemode = strtoumax(value, NULL, 8);
907         } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
908             next->user_id = strtoumax(value, NULL, 10);
909         else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
910             next->group_id = strtoumax(value, NULL, 10);
911         else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
912             next->size = strtoll(value, NULL, 10);
913     }
914     return 0;
915 }
916
917 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
918 {
919     FTPContext *s = h->priv_data;
920     char *start, *found;
921     int ret, retried;
922
923     do {
924         retried = 0;
925         start = s->dir_buffer + s->dir_buffer_offset;
926         while (!(found = strstr(start, "\n"))) {
927             if (retried)
928                 return AVERROR(EIO);
929             s->dir_buffer_size -= s->dir_buffer_offset;
930             s->dir_buffer_offset = 0;
931             if (s->dir_buffer_size)
932                 memmove(s->dir_buffer, start, s->dir_buffer_size);
933             ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
934             if (ret < 0)
935                 return ret;
936             if (!ret) {
937                 *next = NULL;
938                 return 0;
939             }
940             s->dir_buffer_size += ret;
941             s->dir_buffer[s->dir_buffer_size] = 0;
942             start = s->dir_buffer;
943             retried = 1;
944         }
945         s->dir_buffer_offset += (found + 1 - start);
946         found[0] = 0;
947         if (found > start && found[-1] == '\r')
948             found[-1] = 0;
949
950         *next = ff_alloc_dir_entry();
951         if (!*next)
952             return AVERROR(ENOMEM);
953         (*next)->utf8 = s->utf8;
954         ret = ftp_parse_entry(start, *next);
955         if (ret) {
956             avio_free_directory_entry(next);
957             if (ret < 0)
958                 return ret;
959         }
960     } while (ret > 0);
961     return 0;
962 }
963
964 static int ftp_close_dir(URLContext *h)
965 {
966     FTPContext *s = h->priv_data;
967     av_free(s->dir_buffer);
968     ffurl_closep(&s->conn_control);
969     ffurl_closep(&s->conn_data);
970     return 0;
971 }
972
973 URLProtocol ff_ftp_protocol = {
974     .name                = "ftp",
975     .url_open            = ftp_open,
976     .url_read            = ftp_read,
977     .url_write           = ftp_write,
978     .url_seek            = ftp_seek,
979     .url_close           = ftp_close,
980     .url_get_file_handle = ftp_get_file_handle,
981     .url_shutdown        = ftp_shutdown,
982     .priv_data_size      = sizeof(FTPContext),
983     .priv_data_class     = &ftp_context_class,
984     .url_open_dir        = ftp_open_dir,
985     .url_read_dir        = ftp_read_dir,
986     .url_close_dir       = ftp_close_dir,
987     .flags               = URL_PROTOCOL_FLAG_NETWORK,
988 };