diff --git a/libavformat/ftp.c b/libavformat/ftp.c index 5d007804fd..f603379e5b 100644 --- a/libavformat/ftp.c +++ b/libavformat/ftp.c @@ -38,6 +38,12 @@ typedef enum { DISCONNECTED } FTPState; +typedef enum { + UNKNOWN_METHOD, + NLST, + MLSD +} FTPListingMethod; + typedef struct { const AVClass *class; URLContext *conn_control; /**< Control connection */ @@ -56,6 +62,8 @@ typedef struct { const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */ int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */ FTPState state; /**< State of data connection */ + FTPListingMethod listing_method; /**< Called listing method */ + char *features; /**< List of server's features represented as raw response */ char *dir_buffer; size_t dir_buffer_size; size_t dir_buffer_offset; @@ -192,6 +200,8 @@ static int ftp_send_command(FTPContext *s, const char *command, { int err; + av_dlog(s, "%s", command); + if (response) *response = NULL; @@ -449,32 +459,65 @@ static int ftp_set_dir(FTPContext *s) return 0; } -static int ftp_list(FTPContext *s) +static int ftp_list_mlsd(FTPContext *s) { static const char *command = "MLSD\r\n"; static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */ if (ftp_send_command(s, command, mlsd_codes, NULL) != 150) return AVERROR(ENOSYS); - s->state = LISTING_DIR; + s->listing_method = MLSD; return 0; } +static int ftp_list_nlst(FTPContext *s) +{ + static const char *command = "NLST\r\n"; + static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0}; + + if (ftp_send_command(s, command, nlst_codes, NULL) != 226) + return AVERROR(ENOSYS); + s->listing_method = NLST; + return 0; +} + +static int ftp_has_feature(FTPContext *s, const char *feature_name); + +static int ftp_list(FTPContext *s) +{ + int ret; + s->state = LISTING_DIR; + + if ((ret = ftp_list_mlsd(s)) < 0) + ret = ftp_list_nlst(s); + + return ret; +} + +static int ftp_has_feature(FTPContext *s, const char *feature_name) +{ + if (!s->features) + return 0; + + return av_stristr(s->features, feature_name) != NULL; +} + static int ftp_features(FTPContext *s) { static const char *feat_command = "FEAT\r\n"; static const char *enable_utf8_command = "OPTS UTF8 ON\r\n"; static const int feat_codes[] = {211, 0}; static const int opts_codes[] = {200, 451, 0}; - char *feat = NULL; - if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) { - if (av_stristr(feat, "UTF8")) { - if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200) - s->utf8 = 1; - } + av_freep(&s->features); + if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) { + av_freep(&s->features); + } + + if (ftp_has_feature(s, "UTF8")) { + if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200) + s->utf8 = 1; } - av_freep(&feat); return 0; } @@ -607,8 +650,10 @@ static int ftp_connect(URLContext *h, const char *url) FTPContext *s = h->priv_data; s->state = DISCONNECTED; + s->listing_method = UNKNOWN; s->filesize = -1; s->position = 0; + s->features = NULL; av_url_split(proto, sizeof(proto), credencials, sizeof(credencials), @@ -815,6 +860,7 @@ static int ftp_close(URLContext *h) av_freep(&s->password); av_freep(&s->hostname); av_freep(&s->path); + av_freep(&s->features); return 0; } @@ -878,10 +924,13 @@ static int64_t ftp_parse_date(const char *date) return INT64_C(1000000) * av_timegm(&tv); } -/** - * @return 0 on success, negative on error, positive on entry to discard. - */ -static int ftp_parse_entry(char *mlsd, AVIODirEntry *next) +static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next) +{ + next->name = av_strdup(line); + return 0; +} + +static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next) { char *fact, *value; av_dlog(NULL, "%s\n", mlsd); @@ -914,6 +963,24 @@ static int ftp_parse_entry(char *mlsd, AVIODirEntry *next) return 0; } +/** + * @return 0 on success, negative on error, positive on entry to discard. + */ +static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next) +{ + FTPContext *s = h->priv_data; + + switch (s->listing_method) { + case MLSD: + return ftp_parse_entry_mlsd(line, next); + case NLST: + return ftp_parse_entry_nlst(line, next); + case UNKNOWN_METHOD: + default: + return -1; + } +} + static int ftp_read_dir(URLContext *h, AVIODirEntry **next) { FTPContext *s = h->priv_data; @@ -951,7 +1018,7 @@ static int ftp_read_dir(URLContext *h, AVIODirEntry **next) if (!*next) return AVERROR(ENOMEM); (*next)->utf8 = s->utf8; - ret = ftp_parse_entry(start, *next); + ret = ftp_parse_entry(h, start, *next); if (ret) { avio_free_directory_entry(next); if (ret < 0)