LCOV - code coverage report
Current view: top level - libavformat - ftp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 578 0.0 %
Date: 2017-12-16 13:57:32 Functions: 0 41 0.0 %

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

Generated by: LCOV version 1.13