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