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