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