lavf/ftp: implement directory listing callbacks
[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     if (end > res && end[-1] == '/') {
358         end[-1] = '\0';
359     } else
360         *end = '\0';
361     s->path = av_strdup(start);
362
363     av_free(res);
364
365     if (!s->path)
366         return AVERROR(ENOMEM);
367     return 0;
368
369   fail:
370     av_free(res);
371     return AVERROR(EIO);
372 }
373
374 static int ftp_file_size(FTPContext *s)
375 {
376     char command[CONTROL_BUFFER_SIZE];
377     char *res = NULL;
378     static const int size_codes[] = {213, 0};
379
380     snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
381     if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
382         s->filesize = strtoll(&res[4], NULL, 10);
383     } else {
384         s->filesize = -1;
385         av_free(res);
386         return AVERROR(EIO);
387     }
388
389     av_free(res);
390     return 0;
391 }
392
393 static int ftp_retrieve(FTPContext *s)
394 {
395     char command[CONTROL_BUFFER_SIZE];
396     static const int retr_codes[] = {150, 0};
397
398     snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
399     if (ftp_send_command(s, command, retr_codes, NULL) != 150)
400         return AVERROR(EIO);
401
402     s->state = DOWNLOADING;
403
404     return 0;
405 }
406
407 static int ftp_store(FTPContext *s)
408 {
409     char command[CONTROL_BUFFER_SIZE];
410     static const int stor_codes[] = {150, 0};
411
412     snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
413     if (ftp_send_command(s, command, stor_codes, NULL) != 150)
414         return AVERROR(EIO);
415
416     s->state = UPLOADING;
417
418     return 0;
419 }
420
421 static int ftp_type(FTPContext *s)
422 {
423     static const char *command = "TYPE I\r\n";
424     static const int type_codes[] = {200, 0};
425
426     if (ftp_send_command(s, command, type_codes, NULL) != 200)
427         return AVERROR(EIO);
428
429     return 0;
430 }
431
432 static int ftp_restart(FTPContext *s, int64_t pos)
433 {
434     char command[CONTROL_BUFFER_SIZE];
435     static const int rest_codes[] = {350, 0};
436
437     snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
438     if (ftp_send_command(s, command, rest_codes, NULL) != 350)
439         return AVERROR(EIO);
440
441     return 0;
442 }
443
444 static int ftp_set_dir(FTPContext *s)
445 {
446     static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
447     char command[MAX_URL_SIZE];
448
449     snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
450     if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
451         return AVERROR(EIO);
452     return 0;
453 }
454
455 static int ftp_list(FTPContext *s)
456 {
457     static const char *command = "MLSD\r\n";
458     static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
459
460     if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
461         return AVERROR(ENOSYS);
462     s->state = LISTING_DIR;
463     return 0;
464 }
465
466 static int ftp_features(FTPContext *s)
467 {
468     static const char *feat_command        = "FEAT\r\n";
469     static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
470     static const int feat_codes[] = {211, 0};
471     static const int opts_codes[] = {200, 451, 0};
472     char *feat = NULL;
473
474     if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
475         if (av_stristr(feat, "UTF8")) {
476             if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200)
477                 s->utf8 = 1;
478         }
479     }
480     av_freep(&feat);
481
482     return 0;
483 }
484
485 static int ftp_connect_control_connection(URLContext *h)
486 {
487     char buf[CONTROL_BUFFER_SIZE], *response = NULL;
488     int err;
489     AVDictionary *opts = NULL;
490     FTPContext *s = h->priv_data;
491     static const int connect_codes[] = {220, 0};
492
493     if (!s->conn_control) {
494         ff_url_join(buf, sizeof(buf), "tcp", NULL,
495                     s->hostname, s->server_control_port, NULL);
496         if (s->rw_timeout != -1) {
497             av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
498         } /* if option is not given, don't pass it and let tcp use its own default */
499         err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
500                          &h->interrupt_callback, &opts);
501         av_dict_free(&opts);
502         if (err < 0) {
503             av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
504             return err;
505         }
506
507         /* check if server is ready */
508         if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
509             av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
510             return AVERROR(EACCES);
511         }
512
513         if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
514             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.");
515         }
516         av_free(response);
517
518         if ((err = ftp_auth(s)) < 0) {
519             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
520             return err;
521         }
522
523         if ((err = ftp_type(s)) < 0) {
524             av_log(h, AV_LOG_ERROR, "Set content type failed\n");
525             return err;
526         }
527
528         ftp_features(s);
529     }
530     return 0;
531 }
532
533 static int ftp_connect_data_connection(URLContext *h)
534 {
535     int err;
536     char buf[CONTROL_BUFFER_SIZE];
537     AVDictionary *opts = NULL;
538     FTPContext *s = h->priv_data;
539
540     if (!s->conn_data) {
541         /* Enter passive mode */
542         if (ftp_passive_mode_epsv(s) < 0) {
543             /* Use PASV as fallback */
544             if ((err = ftp_passive_mode(s)) < 0)
545                 return err;
546         }
547         /* Open data connection */
548         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
549         if (s->rw_timeout != -1) {
550             av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
551         } /* if option is not given, don't pass it and let tcp use its own default */
552         err = ffurl_open(&s->conn_data, buf, h->flags,
553                          &h->interrupt_callback, &opts);
554         av_dict_free(&opts);
555         if (err < 0)
556             return err;
557
558         if (s->position)
559             if ((err = ftp_restart(s, s->position)) < 0)
560                 return err;
561     }
562     s->state = READY;
563     return 0;
564 }
565
566 static int ftp_abort(URLContext *h)
567 {
568     static const char *command = "ABOR\r\n";
569     int err;
570     static const int abor_codes[] = {225, 226, 0};
571     FTPContext *s = h->priv_data;
572
573     /* According to RCF 959:
574        "ABOR command tells the server to abort the previous FTP
575        service command and any associated transfer of data."
576
577        There are FTP server implementations that don't response
578        to any commands during data transfer in passive mode (including ABOR).
579
580        This implementation closes data connection by force.
581     */
582
583     if (ftp_send_command(s, command, NULL, NULL) < 0) {
584         ftp_close_both_connections(s);
585         if ((err = ftp_connect_control_connection(h)) < 0) {
586             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
587             return err;
588         }
589     } else {
590         ftp_close_data_connection(s);
591         if (ftp_status(s, NULL, abor_codes) < 225) {
592             /* wu-ftpd also closes control connection after data connection closing */
593             ffurl_closep(&s->conn_control);
594             if ((err = ftp_connect_control_connection(h)) < 0) {
595                 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
596                 return err;
597             }
598         }
599     }
600
601     return 0;
602 }
603
604 static int ftp_connect(URLContext *h, const char *url)
605 {
606     char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
607     const char *tok_user = NULL, *tok_pass = NULL;
608     char *end = NULL;
609     int err;
610     size_t pathlen;
611     FTPContext *s = h->priv_data;
612
613     s->state = DISCONNECTED;
614     s->filesize = -1;
615     s->position = 0;
616
617     av_url_split(proto, sizeof(proto),
618                  credencials, sizeof(credencials),
619                  hostname, sizeof(hostname),
620                  &s->server_control_port,
621                  path, sizeof(path),
622                  url);
623
624     tok_user = av_strtok(credencials, ":", &end);
625     tok_pass = av_strtok(end, ":", &end);
626     if (!tok_user) {
627         tok_user = "anonymous";
628         tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
629     }
630     s->user = av_strdup(tok_user);
631     s->password = av_strdup(tok_pass);
632     s->hostname = av_strdup(hostname);
633     if (!s->hostname || !s->user || (tok_pass && !s->password)) {
634         return AVERROR(ENOMEM);
635     }
636
637     if (s->server_control_port < 0 || s->server_control_port > 65535)
638         s->server_control_port = 21;
639
640     if ((err = ftp_connect_control_connection(h)) < 0)
641         return err;
642
643     if ((err = ftp_current_dir(s)) < 0)
644         return err;
645     pathlen = strlen(s->path) + strlen(path) + 1;
646     if ((err = av_reallocp(&s->path, pathlen)) < 0)
647         return err;
648     av_strlcat(s->path + strlen(s->path), path, pathlen);
649
650     return 0;
651 }
652
653 static int ftp_open(URLContext *h, const char *url, int flags)
654 {
655     FTPContext *s = h->priv_data;
656     int err;
657
658     av_dlog(h, "ftp protocol open\n");
659
660     if ((err = ftp_connect(h, url)) < 0)
661         goto fail;
662
663     if (ftp_restart(s, 0) < 0) {
664         h->is_streamed = 1;
665     } else {
666         if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
667             h->is_streamed = 1;
668         if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
669             h->is_streamed = 1;
670     }
671
672     return 0;
673
674   fail:
675     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
676     ftp_close(h);
677     return err;
678 }
679
680 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
681 {
682     FTPContext *s = h->priv_data;
683     int err;
684     int64_t new_pos, fake_pos;
685
686     av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
687
688     switch(whence) {
689     case AVSEEK_SIZE:
690         return s->filesize;
691     case SEEK_SET:
692         new_pos = pos;
693         break;
694     case SEEK_CUR:
695         new_pos = s->position + pos;
696         break;
697     case SEEK_END:
698         if (s->filesize < 0)
699             return AVERROR(EIO);
700         new_pos = s->filesize + pos;
701         break;
702     default:
703         return AVERROR(EINVAL);
704     }
705
706     if (h->is_streamed)
707         return AVERROR(EIO);
708
709     if (new_pos < 0) {
710         av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
711         return AVERROR(EINVAL);
712     }
713
714     fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
715     if (fake_pos != s->position) {
716         if ((err = ftp_abort(h)) < 0)
717             return err;
718         s->position = fake_pos;
719     }
720     return new_pos;
721 }
722
723 static int ftp_read(URLContext *h, unsigned char *buf, int size)
724 {
725     FTPContext *s = h->priv_data;
726     int read, err, retry_done = 0;
727
728     av_dlog(h, "ftp protocol read %d bytes\n", size);
729   retry:
730     if (s->state == DISCONNECTED) {
731         /* optimization */
732         if (s->position >= s->filesize)
733             return 0;
734         if ((err = ftp_connect_data_connection(h)) < 0)
735             return err;
736     }
737     if (s->state == READY) {
738         if (s->position >= s->filesize)
739             return 0;
740         if ((err = ftp_retrieve(s)) < 0)
741             return err;
742     }
743     if (s->conn_data && s->state == DOWNLOADING) {
744         read = ffurl_read(s->conn_data, buf, size);
745         if (read >= 0) {
746             s->position += read;
747             if (s->position >= s->filesize) {
748                 /* server will terminate, but keep current position to avoid madness */
749                 /* save position to restart from it */
750                 int64_t pos = s->position;
751                 if (ftp_abort(h) < 0) {
752                     s->position = pos;
753                     return AVERROR(EIO);
754                 }
755                 s->position = pos;
756             }
757         }
758         if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
759             /* Server closed connection. Probably due to inactivity */
760             int64_t pos = s->position;
761             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
762             if ((err = ftp_abort(h)) < 0)
763                 return err;
764             if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
765                 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
766                 return err;
767             }
768             if (!retry_done) {
769                 retry_done = 1;
770                 goto retry;
771             }
772         }
773         return read;
774     }
775
776     av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
777     return AVERROR(EIO);
778 }
779
780 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
781 {
782     int err;
783     FTPContext *s = h->priv_data;
784     int written;
785
786     av_dlog(h, "ftp protocol write %d bytes\n", size);
787
788     if (s->state == DISCONNECTED) {
789         if ((err = ftp_connect_data_connection(h)) < 0)
790             return err;
791     }
792     if (s->state == READY) {
793         if ((err = ftp_store(s)) < 0)
794             return err;
795     }
796     if (s->conn_data && s->state == UPLOADING) {
797         written = ffurl_write(s->conn_data, buf, size);
798         if (written > 0) {
799             s->position += written;
800             s->filesize = FFMAX(s->filesize, s->position);
801         }
802         return written;
803     }
804
805     av_log(h, AV_LOG_ERROR, "FTP write failed\n");
806     return AVERROR(EIO);
807 }
808
809 static int ftp_close(URLContext *h)
810 {
811     FTPContext *s = h->priv_data;
812
813     av_dlog(h, "ftp protocol close\n");
814
815     ftp_close_both_connections(s);
816     av_freep(&s->user);
817     av_freep(&s->password);
818     av_freep(&s->hostname);
819     av_freep(&s->path);
820
821     return 0;
822 }
823
824 static int ftp_get_file_handle(URLContext *h)
825 {
826     FTPContext *s = h->priv_data;
827
828     av_dlog(h, "ftp protocol get_file_handle\n");
829
830     if (s->conn_data)
831         return ffurl_get_file_handle(s->conn_data);
832
833     return AVERROR(EIO);
834 }
835
836 static int ftp_shutdown(URLContext *h, int flags)
837 {
838     FTPContext *s = h->priv_data;
839
840     av_dlog(h, "ftp protocol shutdown\n");
841
842     if (s->conn_data)
843         return ffurl_shutdown(s->conn_data, flags);
844
845     return AVERROR(EIO);
846 }
847
848 static int ftp_open_dir(URLContext *h)
849 {
850     FTPContext *s = h->priv_data;
851     int ret;
852
853     if ((ret = ftp_connect(h, h->filename)) < 0)
854         goto fail;
855     if ((ret = ftp_set_dir(s)) < 0)
856         goto fail;
857     if ((ret = ftp_connect_data_connection(h)) < 0)
858         goto fail;
859     if ((ret = ftp_list(s)) < 0)
860         goto fail;
861     s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
862     if (!s->dir_buffer) {
863         ret = AVERROR(ENOMEM);
864         goto fail;
865     }
866     s->dir_buffer[0] = 0;
867     if (s->conn_data && s->state == LISTING_DIR)
868         return 0;
869   fail:
870     ffurl_closep(&s->conn_control);
871     ffurl_closep(&s->conn_data);
872     return ret;
873 }
874
875 static int64_t ftp_parse_date(const char *date)
876 {
877     struct tm tv;
878     memset(&tv, 0, sizeof(struct tm));
879     av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
880     return INT64_C(1000000) * av_timegm(&tv);
881 }
882
883 /**
884  * @return 0 on success, negative on error, positive on entry to discard.
885  */
886 static int ftp_parse_entry(char *mlsd, AVIODirEntry *next)
887 {
888     char *fact, *value;
889     av_dlog(NULL, "%s\n", mlsd);
890     while(fact = av_strtok(mlsd, ";", &mlsd)) {
891         if (fact[0] == ' ') {
892             next->name = av_strdup(&fact[1]);
893             continue;
894         }
895         fact = av_strtok(fact, "=", &value);
896         if (!av_strcasecmp(fact, "type")) {
897             if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
898                 return 1;
899             if (!av_strcasecmp(value, "dir"))
900                 next->type = AVIO_ENTRY_DIRECTORY;
901             else if (!av_strcasecmp(value, "file"))
902                 next->type = AVIO_ENTRY_FILE;
903             else if (!av_strcasecmp(value, "OS.unix=slink:"))
904                 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
905         } else if (!av_strcasecmp(fact, "modify")) {
906             next->modification_timestamp = ftp_parse_date(value);
907         } else if (!av_strcasecmp(fact, "UNIX.mode")) {
908             next->filemode = strtoumax(value, NULL, 8);
909         } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
910             next->user_id = strtoumax(value, NULL, 10);
911         else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
912             next->group_id = strtoumax(value, NULL, 10);
913         else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
914             next->size = strtoll(value, NULL, 10);
915     }
916     return 0;
917 }
918
919 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
920 {
921     FTPContext *s = h->priv_data;
922     char *start, *found;
923     int ret, retried;
924
925     do {
926         retried = 0;
927         start = s->dir_buffer + s->dir_buffer_offset;
928         while (!(found = strstr(start, "\n"))) {
929             if (retried)
930                 return AVERROR(EIO);
931             s->dir_buffer_size -= s->dir_buffer_offset;
932             s->dir_buffer_offset = 0;
933             if (s->dir_buffer_size)
934                 memmove(s->dir_buffer, start, s->dir_buffer_size);
935             ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
936             if (ret < 0)
937                 return ret;
938             if (!ret) {
939                 *next = NULL;
940                 return 0;
941             }
942             s->dir_buffer_size += ret;
943             s->dir_buffer[s->dir_buffer_size] = 0;
944             start = s->dir_buffer;
945             retried = 1;
946         }
947         s->dir_buffer_offset += (found + 1 - start);
948         found[0] = 0;
949         if (found > start && found[-1] == '\r')
950             found[-1] = 0;
951
952         *next = ff_alloc_dir_entry();
953         if (!*next)
954             return AVERROR(ENOMEM);
955         (*next)->utf8 = s->utf8;
956         ret = ftp_parse_entry(start, *next);
957         if (ret) {
958             avio_free_directory_entry(next);
959             if (ret < 0)
960                 return ret;
961         }
962     } while (ret > 0);
963     return 0;
964 }
965
966 static int ftp_close_dir(URLContext *h)
967 {
968     FTPContext *s = h->priv_data;
969     av_free(s->dir_buffer);
970     ffurl_closep(&s->conn_control);
971     ffurl_closep(&s->conn_data);
972     return 0;
973 }
974
975 URLProtocol ff_ftp_protocol = {
976     .name                = "ftp",
977     .url_open            = ftp_open,
978     .url_read            = ftp_read,
979     .url_write           = ftp_write,
980     .url_seek            = ftp_seek,
981     .url_close           = ftp_close,
982     .url_get_file_handle = ftp_get_file_handle,
983     .url_shutdown        = ftp_shutdown,
984     .priv_data_size      = sizeof(FTPContext),
985     .priv_data_class     = &ftp_context_class,
986     .url_open_dir        = ftp_open_dir,
987     .url_read_dir        = ftp_read_dir,
988     .url_close_dir       = ftp_close_dir,
989     .flags               = URL_PROTOCOL_FLAG_NETWORK,
990 };