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