avformat: use AV_OPT_TYPE_BOOL in a bunch of places
[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(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
541                          &h->interrupt_callback, &opts);
542         av_dict_free(&opts);
543         if (err < 0) {
544             av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
545             return err;
546         }
547
548         /* check if server is ready */
549         if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
550             av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
551             return AVERROR(EACCES);
552         }
553
554         if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
555             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.");
556         }
557         av_free(response);
558
559         if ((err = ftp_auth(s)) < 0) {
560             av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
561             return err;
562         }
563
564         if ((err = ftp_type(s)) < 0) {
565             av_log(h, AV_LOG_ERROR, "Set content type failed\n");
566             return err;
567         }
568
569         ftp_features(s);
570     }
571     return 0;
572 }
573
574 static int ftp_connect_data_connection(URLContext *h)
575 {
576     int err;
577     char buf[CONTROL_BUFFER_SIZE];
578     AVDictionary *opts = NULL;
579     FTPContext *s = h->priv_data;
580
581     if (!s->conn_data) {
582         /* Enter passive mode */
583         if (ftp_passive_mode_epsv(s) < 0) {
584             /* Use PASV as fallback */
585             if ((err = ftp_passive_mode(s)) < 0)
586                 return err;
587         }
588         /* Open data connection */
589         ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
590         if (s->rw_timeout != -1) {
591             av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
592         } /* if option is not given, don't pass it and let tcp use its own default */
593         err = ffurl_open(&s->conn_data, buf, h->flags,
594                          &h->interrupt_callback, &opts);
595         av_dict_free(&opts);
596         if (err < 0)
597             return err;
598
599         if (s->position)
600             if ((err = ftp_restart(s, s->position)) < 0)
601                 return err;
602     }
603     s->state = READY;
604     return 0;
605 }
606
607 static int ftp_abort(URLContext *h)
608 {
609     static const char *command = "ABOR\r\n";
610     int err;
611     static const int abor_codes[] = {225, 226, 0};
612     FTPContext *s = h->priv_data;
613
614     /* According to RCF 959:
615        "ABOR command tells the server to abort the previous FTP
616        service command and any associated transfer of data."
617
618        There are FTP server implementations that don't response
619        to any commands during data transfer in passive mode (including ABOR).
620
621        This implementation closes data connection by force.
622     */
623
624     if (ftp_send_command(s, command, NULL, NULL) < 0) {
625         ftp_close_both_connections(s);
626         if ((err = ftp_connect_control_connection(h)) < 0) {
627             av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
628             return err;
629         }
630     } else {
631         ftp_close_data_connection(s);
632         if (ftp_status(s, NULL, abor_codes) < 225) {
633             /* wu-ftpd also closes control connection after data connection closing */
634             ffurl_closep(&s->conn_control);
635             if ((err = ftp_connect_control_connection(h)) < 0) {
636                 av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
637                 return err;
638             }
639         }
640     }
641
642     return 0;
643 }
644
645 static int ftp_connect(URLContext *h, const char *url)
646 {
647     char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
648     const char *tok_user = NULL, *tok_pass = NULL;
649     char *end = NULL, *newpath = NULL;
650     int err;
651     FTPContext *s = h->priv_data;
652
653     s->state = DISCONNECTED;
654     s->listing_method = UNKNOWN_METHOD;
655     s->filesize = -1;
656     s->position = 0;
657     s->features = NULL;
658
659     av_url_split(proto, sizeof(proto),
660                  credencials, sizeof(credencials),
661                  hostname, sizeof(hostname),
662                  &s->server_control_port,
663                  path, sizeof(path),
664                  url);
665
666     tok_user = av_strtok(credencials, ":", &end);
667     tok_pass = av_strtok(end, ":", &end);
668     if (!tok_user) {
669         tok_user = "anonymous";
670         tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
671     }
672     s->user = av_strdup(tok_user);
673     s->password = av_strdup(tok_pass);
674     s->hostname = av_strdup(hostname);
675     if (!s->hostname || !s->user || (tok_pass && !s->password)) {
676         return AVERROR(ENOMEM);
677     }
678
679     if (s->server_control_port < 0 || s->server_control_port > 65535)
680         s->server_control_port = 21;
681
682     if ((err = ftp_connect_control_connection(h)) < 0)
683         return err;
684
685     if ((err = ftp_current_dir(s)) < 0)
686         return err;
687
688     newpath = av_append_path_component(s->path, path);
689     if (!newpath)
690         return AVERROR(ENOMEM);
691     av_free(s->path);
692     s->path = newpath;
693
694     return 0;
695 }
696
697 static int ftp_open(URLContext *h, const char *url, int flags)
698 {
699     FTPContext *s = h->priv_data;
700     int err;
701
702     ff_dlog(h, "ftp protocol open\n");
703
704     if ((err = ftp_connect(h, url)) < 0)
705         goto fail;
706
707     if (ftp_restart(s, 0) < 0) {
708         h->is_streamed = 1;
709     } else {
710         if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
711             h->is_streamed = 1;
712         if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
713             h->is_streamed = 1;
714     }
715
716     return 0;
717
718   fail:
719     av_log(h, AV_LOG_ERROR, "FTP open failed\n");
720     ftp_close(h);
721     return err;
722 }
723
724 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
725 {
726     FTPContext *s = h->priv_data;
727     int err;
728     int64_t new_pos, fake_pos;
729
730     ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
731
732     switch(whence) {
733     case AVSEEK_SIZE:
734         return s->filesize;
735     case SEEK_SET:
736         new_pos = pos;
737         break;
738     case SEEK_CUR:
739         new_pos = s->position + pos;
740         break;
741     case SEEK_END:
742         if (s->filesize < 0)
743             return AVERROR(EIO);
744         new_pos = s->filesize + pos;
745         break;
746     default:
747         return AVERROR(EINVAL);
748     }
749
750     if (h->is_streamed)
751         return AVERROR(EIO);
752
753     if (new_pos < 0) {
754         av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
755         return AVERROR(EINVAL);
756     }
757
758     fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
759     if (fake_pos != s->position) {
760         if ((err = ftp_abort(h)) < 0)
761             return err;
762         s->position = fake_pos;
763     }
764     return new_pos;
765 }
766
767 static int ftp_read(URLContext *h, unsigned char *buf, int size)
768 {
769     FTPContext *s = h->priv_data;
770     int read, err, retry_done = 0;
771
772     ff_dlog(h, "ftp protocol read %d bytes\n", size);
773   retry:
774     if (s->state == DISCONNECTED) {
775         /* optimization */
776         if (s->position >= s->filesize)
777             return 0;
778         if ((err = ftp_connect_data_connection(h)) < 0)
779             return err;
780     }
781     if (s->state == READY) {
782         if (s->position >= s->filesize)
783             return 0;
784         if ((err = ftp_retrieve(s)) < 0)
785             return err;
786     }
787     if (s->conn_data && s->state == DOWNLOADING) {
788         read = ffurl_read(s->conn_data, buf, size);
789         if (read >= 0) {
790             s->position += read;
791             if (s->position >= s->filesize) {
792                 /* server will terminate, but keep current position to avoid madness */
793                 /* save position to restart from it */
794                 int64_t pos = s->position;
795                 if (ftp_abort(h) < 0) {
796                     s->position = pos;
797                     return AVERROR(EIO);
798                 }
799                 s->position = pos;
800             }
801         }
802         if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
803             /* Server closed connection. Probably due to inactivity */
804             int64_t pos = s->position;
805             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
806             if ((err = ftp_abort(h)) < 0)
807                 return err;
808             if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
809                 av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
810                 return err;
811             }
812             if (!retry_done) {
813                 retry_done = 1;
814                 goto retry;
815             }
816         }
817         return read;
818     }
819
820     av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
821     return AVERROR(EIO);
822 }
823
824 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
825 {
826     int err;
827     FTPContext *s = h->priv_data;
828     int written;
829
830     ff_dlog(h, "ftp protocol write %d bytes\n", size);
831
832     if (s->state == DISCONNECTED) {
833         if ((err = ftp_connect_data_connection(h)) < 0)
834             return err;
835     }
836     if (s->state == READY) {
837         if ((err = ftp_store(s)) < 0)
838             return err;
839     }
840     if (s->conn_data && s->state == UPLOADING) {
841         written = ffurl_write(s->conn_data, buf, size);
842         if (written > 0) {
843             s->position += written;
844             s->filesize = FFMAX(s->filesize, s->position);
845         }
846         return written;
847     }
848
849     av_log(h, AV_LOG_ERROR, "FTP write failed\n");
850     return AVERROR(EIO);
851 }
852
853 static int ftp_close(URLContext *h)
854 {
855     FTPContext *s = h->priv_data;
856
857     ff_dlog(h, "ftp protocol close\n");
858
859     ftp_close_both_connections(s);
860     av_freep(&s->user);
861     av_freep(&s->password);
862     av_freep(&s->hostname);
863     av_freep(&s->path);
864     av_freep(&s->features);
865
866     return 0;
867 }
868
869 static int ftp_get_file_handle(URLContext *h)
870 {
871     FTPContext *s = h->priv_data;
872
873     ff_dlog(h, "ftp protocol get_file_handle\n");
874
875     if (s->conn_data)
876         return ffurl_get_file_handle(s->conn_data);
877
878     return AVERROR(EIO);
879 }
880
881 static int ftp_shutdown(URLContext *h, int flags)
882 {
883     FTPContext *s = h->priv_data;
884
885     ff_dlog(h, "ftp protocol shutdown\n");
886
887     if (s->conn_data)
888         return ffurl_shutdown(s->conn_data, flags);
889
890     return AVERROR(EIO);
891 }
892
893 static int ftp_open_dir(URLContext *h)
894 {
895     FTPContext *s = h->priv_data;
896     int ret;
897
898     if ((ret = ftp_connect(h, h->filename)) < 0)
899         goto fail;
900     if ((ret = ftp_set_dir(s)) < 0)
901         goto fail;
902     if ((ret = ftp_connect_data_connection(h)) < 0)
903         goto fail;
904     if ((ret = ftp_list(s)) < 0)
905         goto fail;
906     s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
907     if (!s->dir_buffer) {
908         ret = AVERROR(ENOMEM);
909         goto fail;
910     }
911     s->dir_buffer[0] = 0;
912     if (s->conn_data && s->state == LISTING_DIR)
913         return 0;
914   fail:
915     ffurl_closep(&s->conn_control);
916     ffurl_closep(&s->conn_data);
917     return ret;
918 }
919
920 static int64_t ftp_parse_date(const char *date)
921 {
922     struct tm tv;
923     memset(&tv, 0, sizeof(struct tm));
924     av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
925     return INT64_C(1000000) * av_timegm(&tv);
926 }
927
928 static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
929 {
930     next->name = av_strdup(line);
931     return 0;
932 }
933
934 static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
935 {
936     char *fact, *value;
937     ff_dlog(NULL, "%s\n", mlsd);
938     while(fact = av_strtok(mlsd, ";", &mlsd)) {
939         if (fact[0] == ' ') {
940             next->name = av_strdup(&fact[1]);
941             continue;
942         }
943         fact = av_strtok(fact, "=", &value);
944         if (!av_strcasecmp(fact, "type")) {
945             if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
946                 return 1;
947             if (!av_strcasecmp(value, "dir"))
948                 next->type = AVIO_ENTRY_DIRECTORY;
949             else if (!av_strcasecmp(value, "file"))
950                 next->type = AVIO_ENTRY_FILE;
951             else if (!av_strcasecmp(value, "OS.unix=slink:"))
952                 next->type = AVIO_ENTRY_SYMBOLIC_LINK;
953         } else if (!av_strcasecmp(fact, "modify")) {
954             next->modification_timestamp = ftp_parse_date(value);
955         } else if (!av_strcasecmp(fact, "UNIX.mode")) {
956             next->filemode = strtoumax(value, NULL, 8);
957         } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
958             next->user_id = strtoumax(value, NULL, 10);
959         else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
960             next->group_id = strtoumax(value, NULL, 10);
961         else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
962             next->size = strtoll(value, NULL, 10);
963     }
964     return 0;
965 }
966
967 /**
968  * @return 0 on success, negative on error, positive on entry to discard.
969  */
970 static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
971 {
972     FTPContext *s = h->priv_data;
973
974     switch (s->listing_method) {
975     case MLSD:
976         return ftp_parse_entry_mlsd(line, next);
977     case NLST:
978         return ftp_parse_entry_nlst(line, next);
979     case UNKNOWN_METHOD:
980     default:
981         return -1;
982     }
983 }
984
985 static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
986 {
987     FTPContext *s = h->priv_data;
988     char *start, *found;
989     int ret, retried;
990
991     do {
992         retried = 0;
993         start = s->dir_buffer + s->dir_buffer_offset;
994         while (!(found = strstr(start, "\n"))) {
995             if (retried)
996                 return AVERROR(EIO);
997             s->dir_buffer_size -= s->dir_buffer_offset;
998             s->dir_buffer_offset = 0;
999             if (s->dir_buffer_size)
1000                 memmove(s->dir_buffer, start, s->dir_buffer_size);
1001             ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
1002             if (ret < 0)
1003                 return ret;
1004             if (!ret) {
1005                 *next = NULL;
1006                 return 0;
1007             }
1008             s->dir_buffer_size += ret;
1009             s->dir_buffer[s->dir_buffer_size] = 0;
1010             start = s->dir_buffer;
1011             retried = 1;
1012         }
1013         s->dir_buffer_offset += (found + 1 - start);
1014         found[0] = 0;
1015         if (found > start && found[-1] == '\r')
1016             found[-1] = 0;
1017
1018         *next = ff_alloc_dir_entry();
1019         if (!*next)
1020             return AVERROR(ENOMEM);
1021         (*next)->utf8 = s->utf8;
1022         ret = ftp_parse_entry(h, start, *next);
1023         if (ret) {
1024             avio_free_directory_entry(next);
1025             if (ret < 0)
1026                 return ret;
1027         }
1028     } while (ret > 0);
1029     return 0;
1030 }
1031
1032 static int ftp_close_dir(URLContext *h)
1033 {
1034     FTPContext *s = h->priv_data;
1035     av_freep(&s->dir_buffer);
1036     ffurl_closep(&s->conn_control);
1037     ffurl_closep(&s->conn_data);
1038     return 0;
1039 }
1040
1041 static int ftp_delete(URLContext *h)
1042 {
1043     FTPContext *s = h->priv_data;
1044     char command[MAX_URL_SIZE];
1045     static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
1046     static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
1047     int ret;
1048
1049     if ((ret = ftp_connect(h, h->filename)) < 0)
1050         goto cleanup;
1051
1052     snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
1053     if (ftp_send_command(s, command, del_codes, NULL) == 250) {
1054         ret = 0;
1055         goto cleanup;
1056     }
1057
1058     snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
1059     if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
1060         ret = 0;
1061     else
1062         ret = AVERROR(EIO);
1063
1064 cleanup:
1065     ftp_close(h);
1066     return ret;
1067 }
1068
1069 static int ftp_move(URLContext *h_src, URLContext *h_dst)
1070 {
1071     FTPContext *s = h_src->priv_data;
1072     char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
1073     static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
1074     static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
1075     int ret;
1076
1077     if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
1078         goto cleanup;
1079
1080     snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
1081     if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
1082         ret = AVERROR(EIO);
1083         goto cleanup;
1084     }
1085
1086     av_url_split(0, 0, 0, 0, 0, 0, 0,
1087                  path, sizeof(path),
1088                  h_dst->filename);
1089     snprintf(command, sizeof(command), "RNTO %s\r\n", path);
1090     if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
1091         ret = 0;
1092     else
1093         ret = AVERROR(EIO);
1094
1095 cleanup:
1096     ftp_close(h_src);
1097     return ret;
1098 }
1099
1100 URLProtocol ff_ftp_protocol = {
1101     .name                = "ftp",
1102     .url_open            = ftp_open,
1103     .url_read            = ftp_read,
1104     .url_write           = ftp_write,
1105     .url_seek            = ftp_seek,
1106     .url_close           = ftp_close,
1107     .url_get_file_handle = ftp_get_file_handle,
1108     .url_shutdown        = ftp_shutdown,
1109     .priv_data_size      = sizeof(FTPContext),
1110     .priv_data_class     = &ftp_context_class,
1111     .url_open_dir        = ftp_open_dir,
1112     .url_read_dir        = ftp_read_dir,
1113     .url_close_dir       = ftp_close_dir,
1114     .url_delete          = ftp_delete,
1115     .url_move            = ftp_move,
1116     .flags               = URL_PROTOCOL_FLAG_NETWORK,
1117 };