*/
static int ftp_status(FTPContext *s, char **line, const int response_codes[])
{
- int err, i, dash = 0, result = 0, code_found = 0;
+ int err, i, dash = 0, result = 0, code_found = 0, linesize;
char buf[CONTROL_BUFFER_SIZE];
AVBPrint line_buffer;
av_log(s, AV_LOG_DEBUG, "%s\n", buf);
- if (strlen(buf) < 4)
- continue;
-
+ linesize = strlen(buf);
err = 0;
- for (i = 0; i < 3; ++i) {
- if (buf[i] < '0' || buf[i] > '9')
- continue;
- err *= 10;
- err += buf[i] - '0';
+ if (linesize >= 3) {
+ for (i = 0; i < 3; ++i) {
+ if (buf[i] < '0' || buf[i] > '9') {
+ err = 0;
+ break;
+ }
+ err *= 10;
+ err += buf[i] - '0';
+ }
}
- dash = !!(buf[3] == '-');
- for (i = 0; response_codes[i]; ++i) {
- if (err == response_codes[i]) {
- if (line)
- av_bprintf(&line_buffer, "%s", buf);
+ if (!code_found) {
+ if (err >= 500) {
code_found = 1;
result = err;
- break;
+ } else
+ for (i = 0; response_codes[i]; ++i) {
+ if (err == response_codes[i]) {
+ code_found = 1;
+ result = err;
+ break;
+ }
+ }
+ }
+ if (code_found) {
+ if (line)
+ av_bprintf(&line_buffer, "%s\r\n", buf);
+ if (linesize >= 4) {
+ if (!dash && buf[3] == '-')
+ dash = err;
+ else if (err == dash && buf[3] == ' ')
+ dash = 0;
}
}
}
const char *user = NULL, *pass = NULL;
char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
int err;
- static const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
- static const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
+ static const int user_codes[] = {331, 230, 0};
+ static const int pass_codes[] = {230, 0};
/* Authentication may be repeated, original string has to be saved */
av_strlcpy(credencials, s->credencials, sizeof(credencials));
return 0;
}
+static int ftp_passive_mode_epsv(FTPContext *s)
+{
+ char *res = NULL, *start = NULL, *end = NULL;
+ int i;
+ static const char d = '|';
+ static const char *command = "EPSV\r\n";
+ static const int epsv_codes[] = {229, 0};
+
+ if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
+ goto fail;
+
+ for (i = 0; res[i]; ++i) {
+ if (res[i] == '(') {
+ start = res + i + 1;
+ } else if (res[i] == ')') {
+ end = res + i;
+ break;
+ }
+ }
+ if (!start || !end)
+ goto fail;
+
+ *end = '\0';
+ if (strlen(start) < 5)
+ goto fail;
+ if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
+ goto fail;
+ start += 3;
+ end[-1] = '\0';
+
+ s->server_data_port = atoi(start);
+ av_dlog(s, "Server data port: %d\n", s->server_data_port);
+
+ av_free(res);
+ return 0;
+
+ fail:
+ av_free(res);
+ s->server_data_port = -1;
+ return AVERROR(ENOSYS);
+}
+
static int ftp_passive_mode(FTPContext *s)
{
char *res = NULL, *start = NULL, *end = NULL;
int i;
static const char *command = "PASV\r\n";
- static const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
+ static const int pasv_codes[] = {227, 0};
if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
goto fail;
fail:
av_free(res);
s->server_data_port = -1;
- av_log(s, AV_LOG_ERROR, "Set passive mode failed\n"
- "Your FTP server may use IPv6 which is not supported yet.\n");
return AVERROR(EIO);
}
{
char command[CONTROL_BUFFER_SIZE];
char *res = NULL;
- static const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
+ static const int size_codes[] = {213, 0};
snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
static int ftp_retrieve(FTPContext *s)
{
char command[CONTROL_BUFFER_SIZE];
- static const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
+ static const int retr_codes[] = {150, 0};
snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
if (ftp_send_command(s, command, retr_codes, NULL) != 150)
static int ftp_type(FTPContext *s)
{
static const char *command = "TYPE I\r\n";
- static const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
+ static const int type_codes[] = {200, 0};
if (ftp_send_command(s, command, type_codes, NULL) != 200)
return AVERROR(EIO);
static int ftp_restart(FTPContext *s, int64_t pos)
{
char command[CONTROL_BUFFER_SIZE];
- static const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
+ static const int rest_codes[] = {350, 0};
snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
if (ftp_send_command(s, command, rest_codes, NULL) != 350)
return 0;
}
+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};
+ char *feat;
+
+ if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
+ if (av_stristr(feat, "UTF8"))
+ ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
+ }
+ return 0;
+}
+
static int ftp_connect_control_connection(URLContext *h)
{
char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
av_log(h, AV_LOG_ERROR, "Set content type failed\n");
return err;
}
+
+ ftp_features(s);
}
return 0;
}
if (!s->conn_data) {
/* Enter passive mode */
- if ((err = ftp_passive_mode(s)) < 0)
- return err;
+ if (ftp_passive_mode_epsv(s) < 0) {
+ /* Use PASV as fallback */
+ if ((err = ftp_passive_mode(s)) < 0)
+ return err;
+ }
/* Open data connection */
ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
if (s->rw_timeout != -1) {