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