| 
				
				
					
				
				
				 | 
			
			 | 
			@@ -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) | 
		
		
	
	
		
			
				| 
				
					
				
				
				
				 | 
			
			 | 
			
  |