OSDN Git Service

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