/* * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com> * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h>#include <time.h> #include "libavutil/avstring.h"#include "libavutil/internal.h"#include "libavutil/mem.h"#include "libavutil/parseutils.h"#include "avformat.h"#include "internal.h"#include "url.h"#include "urldecode.h"#include "libavutil/opt.h"#include "libavutil/bprint.h" #define CONTROL_BUFFER_SIZE 1024#define DIR_BUFFER_SIZE 4096 typedef enum { UNKNOWN, READY, DOWNLOADING, UPLOADING, LISTING_DIR, DISCONNECTED, ENDOFFILE,} FTPState; typedef enum { UNKNOWN_METHOD, NLST, MLSD} FTPListingMethod; typedef struct { const AVClass *class; URLContext *conn_control; /**< Control connection */ URLContext *conn_data; /**< Data connection, NULL when not connected */ uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */ uint8_t *control_buf_ptr, *control_buf_end; int server_data_port; /**< Data connection port opened by server, -1 on error. */ int server_control_port; /**< Control connection port, default is 21 */ char *hostname; /**< Server address. */ char *user; /**< Server user */ char *password; /**< Server user's password */ char *path; /**< Path to resource on server. */ int64_t filesize; /**< Size of file on server, -1 on error. */ int64_t position; /**< Current position, calculated. */ int rw_timeout; /**< Network timeout. */ 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; int utf8; const char *option_user; /**< User to be used if none given in the URL */ const char *option_password; /**< Password to be used if none given in the URL */} FTPContext; #define OFFSET(x) offsetof(FTPContext, x)#define D AV_OPT_FLAG_DECODING_PARAM#define E AV_OPT_FLAG_ENCODING_PARAMstatic const AVOption options[] = { {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E }, {"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 }, {"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 }, {"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 }, {NULL}}; static const AVClass ftp_context_class = { .class_name = "ftp", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT,}; static int ftp_close(URLContext *h); static int ftp_getc(FTPContext *s){ int len; if (s->control_buf_ptr >= s->control_buf_end) { len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE); if (len < 0) { return len; } else if (!len) { return -1; } else { s->control_buf_ptr = s->control_buffer; s->control_buf_end = s->control_buffer + len; } } return *s->control_buf_ptr++;} static int ftp_get_line(FTPContext *s, char *line, int line_size){ int ch; char *q = line; for (;;) { ch = ftp_getc(s); if (ch < 0) { return ch; } if (ch == '\n') { /* process line */ if (q > line && q[-1] == '\r') q--; *q = '\0'; return 0; } else { if ((q - line) < line_size - 1) *q++ = ch; } }} /* * This routine returns ftp server response code. * Server may send more than one response for a certain command. * First expected code is returned. */static int ftp_status(FTPContext *s, char **line, const int response_codes[]){ int err, i, dash = 0, result = 0, code_found = 0, linesize; char buf[CONTROL_BUFFER_SIZE]; AVBPrint line_buffer; if (line) av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC); while (!code_found || dash) { if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) { if (line) av_bprint_finalize(&line_buffer, NULL); return err; } av_log(s, AV_LOG_DEBUG, "%s\n", buf); linesize = strlen(buf); err = 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'; } } if (!code_found) { if (err >= 500) { code_found = 1; result = err; } 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; } } } if (line) av_bprint_finalize(&line_buffer, line); return result;} static int ftp_send_command(FTPContext *s, const char *command, const int response_codes[], char **response){ int err; ff_dlog(s, "%s", command); if (response) *response = NULL; if (!s->conn_control) return AVERROR(EIO); if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0) return err; if (!err) return -1; /* return status */ if (response_codes) { return ftp_status(s, response, response_codes); } return 0;} static void ftp_close_data_connection(FTPContext *s){ ffurl_closep(&s->conn_data); s->state = DISCONNECTED;} static void ftp_close_both_connections(FTPContext *s){ ffurl_closep(&s->conn_control); ftp_close_data_connection(s);} static int ftp_auth(FTPContext *s){ char buf[CONTROL_BUFFER_SIZE]; int err; static const int user_codes[] = {331, 230, 0}; static const int pass_codes[] = {230, 0}; if (strpbrk(s->user, "\r\n")) return AVERROR(EINVAL); err = snprintf(buf, sizeof(buf), "USER %s\r\n", s->user); if (err >= sizeof(buf)) return AVERROR(ENOSYS); err = ftp_send_command(s, buf, user_codes, NULL); if (err == 331) { if (s->password) { if (strpbrk(s->password, "\r\n")) return AVERROR(EINVAL); err = snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password); if (err >= sizeof(buf)) return AVERROR(ENOSYS); err = ftp_send_command(s, buf, pass_codes, NULL); } else return AVERROR(EACCES); } if (err != 230) return AVERROR(EACCES); 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); ff_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, 0}; if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !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'; /* skip ip */ if (!av_strtok(start, ",", &end)) goto fail; if (!av_strtok(NULL, ",", &end)) goto fail; if (!av_strtok(NULL, ",", &end)) goto fail; if (!av_strtok(NULL, ",", &end)) goto fail; /* parse port number */ start = av_strtok(NULL, ",", &end); if (!start) goto fail; s->server_data_port = atoi(start) * 256; start = av_strtok(NULL, ",", &end); if (!start) goto fail; s->server_data_port += atoi(start); ff_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(EIO);} static int ftp_current_dir(FTPContext *s){ char *res = NULL, *start = NULL, *end = NULL; int i; static const char *command = "PWD\r\n"; static const int pwd_codes[] = {257, 0}; if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res) goto fail; for (i = 0; res[i]; ++i) { if (res[i] == '"') { if (!start) { start = res + i + 1; continue; } end = res + i; break; } } if (!end) goto fail; *end = '\0'; s->path = av_strdup(start); av_free(res); if (!s->path) return AVERROR(ENOMEM); return 0; fail: av_free(res); return AVERROR(EIO);} static int ftp_file_size(FTPContext *s){ char command[CONTROL_BUFFER_SIZE]; char *res = NULL; int ret; static const int size_codes[] = {213, 0}; ret = snprintf(command, sizeof(command), "SIZE %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) { s->filesize = strtoll(&res[4], NULL, 10); } else { s->filesize = -1; av_free(res); return AVERROR(EIO); } av_free(res); return 0;} static int ftp_retrieve(FTPContext *s){ char command[CONTROL_BUFFER_SIZE]; static const int retr_codes[] = {150, 125, 0}; int resp_code, ret; ret = snprintf(command, sizeof(command), "RETR %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); resp_code = ftp_send_command(s, command, retr_codes, NULL); if (resp_code != 125 && resp_code != 150) return AVERROR(EIO); s->state = DOWNLOADING; return 0;} static int ftp_store(FTPContext *s){ char command[CONTROL_BUFFER_SIZE]; static const int stor_codes[] = {150, 125, 0}; int resp_code, ret; ret = snprintf(command, sizeof(command), "STOR %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); resp_code = ftp_send_command(s, command, stor_codes, NULL); if (resp_code != 125 && resp_code != 150) return AVERROR(EIO); s->state = UPLOADING; return 0;} static int ftp_type(FTPContext *s){ static const char *command = "TYPE I\r\n"; static const int type_codes[] = {200, 0}; if (ftp_send_command(s, command, type_codes, NULL) != 200) return AVERROR(EIO); return 0;} static int ftp_restart(FTPContext *s, int64_t pos){ char command[CONTROL_BUFFER_SIZE]; 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 AVERROR(EIO); return 0;} static int ftp_set_dir(FTPContext *s){ static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */ char command[MAX_URL_SIZE]; int ret; ret = snprintf(command, sizeof(command), "CWD %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); if (ftp_send_command(s, command, cwd_codes, NULL) != 250) return AVERROR(EIO); return 0;Lines 1–500 of 1193. Load more
/* * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com> * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <time.h> #include "libavutil/avstring.h" #include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/parseutils.h" #include "avformat.h" #include "internal.h" #include "url.h" #include "urldecode.h" #include "libavutil/opt.h" #include "libavutil/bprint.h" #define CONTROL_BUFFER_SIZE 1024 #define DIR_BUFFER_SIZE 4096 typedef enum { UNKNOWN, READY, DOWNLOADING, UPLOADING, LISTING_DIR, DISCONNECTED, ENDOFFILE, } FTPState; typedef enum { UNKNOWN_METHOD, NLST, MLSD } FTPListingMethod; typedef struct { const AVClass *class; URLContext *conn_control; /**< Control connection */ URLContext *conn_data; /**< Data connection, NULL when not connected */ uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */ uint8_t *control_buf_ptr, *control_buf_end; int server_data_port; /**< Data connection port opened by server, -1 on error. */ int server_control_port; /**< Control connection port, default is 21 */ char *hostname; /**< Server address. */ char *user; /**< Server user */ char *password; /**< Server user's password */ char *path; /**< Path to resource on server. */ int64_t filesize; /**< Size of file on server, -1 on error. */ int64_t position; /**< Current position, calculated. */ int rw_timeout; /**< Network timeout. */ 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; int utf8; const char *option_user; /**< User to be used if none given in the URL */ const char *option_password; /**< Password to be used if none given in the URL */ } FTPContext; #define OFFSET(x) offsetof(FTPContext, x) #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E }, {"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 }, {"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 }, {"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 }, {NULL} }; static const AVClass ftp_context_class = { .class_name = "ftp", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, }; static int ftp_close(URLContext *h); static int ftp_getc(FTPContext *s) { int len; if (s->control_buf_ptr >= s->control_buf_end) { len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE); if (len < 0) { return len; } else if (!len) { return -1; } else { s->control_buf_ptr = s->control_buffer; s->control_buf_end = s->control_buffer + len; } } return *s->control_buf_ptr++; } static int ftp_get_line(FTPContext *s, char *line, int line_size) { int ch; char *q = line; for (;;) { ch = ftp_getc(s); if (ch < 0) { return ch; } if (ch == '\n') { /* process line */ if (q > line && q[-1] == '\r') q--; *q = '\0'; return 0; } else { if ((q - line) < line_size - 1) *q++ = ch; } } } /* * This routine returns ftp server response code. * Server may send more than one response for a certain command. * First expected code is returned. */ static int ftp_status(FTPContext *s, char **line, const int response_codes[]) { int err, i, dash = 0, result = 0, code_found = 0, linesize; char buf[CONTROL_BUFFER_SIZE]; AVBPrint line_buffer; if (line) av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC); while (!code_found || dash) { if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) { if (line) av_bprint_finalize(&line_buffer, NULL); return err; } av_log(s, AV_LOG_DEBUG, "%s\n", buf); linesize = strlen(buf); err = 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'; } } if (!code_found) { if (err >= 500) { code_found = 1; result = err; } 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; } } } if (line) av_bprint_finalize(&line_buffer, line); return result; } static int ftp_send_command(FTPContext *s, const char *command, const int response_codes[], char **response) { int err; ff_dlog(s, "%s", command); if (response) *response = NULL; if (!s->conn_control) return AVERROR(EIO); if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0) return err; if (!err) return -1; /* return status */ if (response_codes) { return ftp_status(s, response, response_codes); } return 0; } static void ftp_close_data_connection(FTPContext *s) { ffurl_closep(&s->conn_data); s->state = DISCONNECTED; } static void ftp_close_both_connections(FTPContext *s) { ffurl_closep(&s->conn_control); ftp_close_data_connection(s); } static int ftp_auth(FTPContext *s) { char buf[CONTROL_BUFFER_SIZE]; int err; static const int user_codes[] = {331, 230, 0}; static const int pass_codes[] = {230, 0}; if (strpbrk(s->user, "\r\n")) return AVERROR(EINVAL); err = snprintf(buf, sizeof(buf), "USER %s\r\n", s->user); if (err >= sizeof(buf)) return AVERROR(ENOSYS); err = ftp_send_command(s, buf, user_codes, NULL); if (err == 331) { if (s->password) { if (strpbrk(s->password, "\r\n")) return AVERROR(EINVAL); err = snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password); if (err >= sizeof(buf)) return AVERROR(ENOSYS); err = ftp_send_command(s, buf, pass_codes, NULL); } else return AVERROR(EACCES); } if (err != 230) return AVERROR(EACCES); 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); ff_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, 0}; if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !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'; /* skip ip */ if (!av_strtok(start, ",", &end)) goto fail; if (!av_strtok(NULL, ",", &end)) goto fail; if (!av_strtok(NULL, ",", &end)) goto fail; if (!av_strtok(NULL, ",", &end)) goto fail; /* parse port number */ start = av_strtok(NULL, ",", &end); if (!start) goto fail; s->server_data_port = atoi(start) * 256; start = av_strtok(NULL, ",", &end); if (!start) goto fail; s->server_data_port += atoi(start); ff_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(EIO); } static int ftp_current_dir(FTPContext *s) { char *res = NULL, *start = NULL, *end = NULL; int i; static const char *command = "PWD\r\n"; static const int pwd_codes[] = {257, 0}; if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res) goto fail; for (i = 0; res[i]; ++i) { if (res[i] == '"') { if (!start) { start = res + i + 1; continue; } end = res + i; break; } } if (!end) goto fail; *end = '\0'; s->path = av_strdup(start); av_free(res); if (!s->path) return AVERROR(ENOMEM); return 0; fail: av_free(res); return AVERROR(EIO); } static int ftp_file_size(FTPContext *s) { char command[CONTROL_BUFFER_SIZE]; char *res = NULL; int ret; static const int size_codes[] = {213, 0}; ret = snprintf(command, sizeof(command), "SIZE %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) { s->filesize = strtoll(&res[4], NULL, 10); } else { s->filesize = -1; av_free(res); return AVERROR(EIO); } av_free(res); return 0; } static int ftp_retrieve(FTPContext *s) { char command[CONTROL_BUFFER_SIZE]; static const int retr_codes[] = {150, 125, 0}; int resp_code, ret; ret = snprintf(command, sizeof(command), "RETR %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); resp_code = ftp_send_command(s, command, retr_codes, NULL); if (resp_code != 125 && resp_code != 150) return AVERROR(EIO); s->state = DOWNLOADING; return 0; } static int ftp_store(FTPContext *s) { char command[CONTROL_BUFFER_SIZE]; static const int stor_codes[] = {150, 125, 0}; int resp_code, ret; ret = snprintf(command, sizeof(command), "STOR %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); resp_code = ftp_send_command(s, command, stor_codes, NULL); if (resp_code != 125 && resp_code != 150) return AVERROR(EIO); s->state = UPLOADING; return 0; } static int ftp_type(FTPContext *s) { static const char *command = "TYPE I\r\n"; static const int type_codes[] = {200, 0}; if (ftp_send_command(s, command, type_codes, NULL) != 200) return AVERROR(EIO); return 0; } static int ftp_restart(FTPContext *s, int64_t pos) { char command[CONTROL_BUFFER_SIZE]; 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 AVERROR(EIO); return 0; } static int ftp_set_dir(FTPContext *s) { static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */ char command[MAX_URL_SIZE]; int ret; ret = snprintf(command, sizeof(command), "CWD %s\r\n", s->path); if (ret >= sizeof(command)) return AVERROR(ENOSYS); if (ftp_send_command(s, command, cwd_codes, NULL) != 250) return AVERROR(EIO); return 0;