1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com> 3cabdff1aSopenharmony_ci * 4cabdff1aSopenharmony_ci * This file is part of FFmpeg. 5cabdff1aSopenharmony_ci * 6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 8cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 10cabdff1aSopenharmony_ci * 11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14cabdff1aSopenharmony_ci * Lesser General Public License for more details. 15cabdff1aSopenharmony_ci * 16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 17cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 18cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19cabdff1aSopenharmony_ci */ 20cabdff1aSopenharmony_ci 21cabdff1aSopenharmony_ci#include <string.h> 22cabdff1aSopenharmony_ci 23cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 24cabdff1aSopenharmony_ci#include "libavutil/internal.h" 25cabdff1aSopenharmony_ci#include "libavutil/parseutils.h" 26cabdff1aSopenharmony_ci#include "avformat.h" 27cabdff1aSopenharmony_ci#include "internal.h" 28cabdff1aSopenharmony_ci#include "url.h" 29cabdff1aSopenharmony_ci#include "urldecode.h" 30cabdff1aSopenharmony_ci#include "libavutil/opt.h" 31cabdff1aSopenharmony_ci#include "libavutil/bprint.h" 32cabdff1aSopenharmony_ci 33cabdff1aSopenharmony_ci#define CONTROL_BUFFER_SIZE 1024 34cabdff1aSopenharmony_ci#define DIR_BUFFER_SIZE 4096 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_citypedef enum { 37cabdff1aSopenharmony_ci UNKNOWN, 38cabdff1aSopenharmony_ci READY, 39cabdff1aSopenharmony_ci DOWNLOADING, 40cabdff1aSopenharmony_ci UPLOADING, 41cabdff1aSopenharmony_ci LISTING_DIR, 42cabdff1aSopenharmony_ci DISCONNECTED, 43cabdff1aSopenharmony_ci ENDOFFILE, 44cabdff1aSopenharmony_ci} FTPState; 45cabdff1aSopenharmony_ci 46cabdff1aSopenharmony_citypedef enum { 47cabdff1aSopenharmony_ci UNKNOWN_METHOD, 48cabdff1aSopenharmony_ci NLST, 49cabdff1aSopenharmony_ci MLSD 50cabdff1aSopenharmony_ci} FTPListingMethod; 51cabdff1aSopenharmony_ci 52cabdff1aSopenharmony_citypedef struct { 53cabdff1aSopenharmony_ci const AVClass *class; 54cabdff1aSopenharmony_ci URLContext *conn_control; /**< Control connection */ 55cabdff1aSopenharmony_ci URLContext *conn_data; /**< Data connection, NULL when not connected */ 56cabdff1aSopenharmony_ci uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */ 57cabdff1aSopenharmony_ci uint8_t *control_buf_ptr, *control_buf_end; 58cabdff1aSopenharmony_ci int server_data_port; /**< Data connection port opened by server, -1 on error. */ 59cabdff1aSopenharmony_ci int server_control_port; /**< Control connection port, default is 21 */ 60cabdff1aSopenharmony_ci char *hostname; /**< Server address. */ 61cabdff1aSopenharmony_ci char *user; /**< Server user */ 62cabdff1aSopenharmony_ci char *password; /**< Server user's password */ 63cabdff1aSopenharmony_ci char *path; /**< Path to resource on server. */ 64cabdff1aSopenharmony_ci int64_t filesize; /**< Size of file on server, -1 on error. */ 65cabdff1aSopenharmony_ci int64_t position; /**< Current position, calculated. */ 66cabdff1aSopenharmony_ci int rw_timeout; /**< Network timeout. */ 67cabdff1aSopenharmony_ci const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */ 68cabdff1aSopenharmony_ci int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */ 69cabdff1aSopenharmony_ci FTPState state; /**< State of data connection */ 70cabdff1aSopenharmony_ci FTPListingMethod listing_method; /**< Called listing method */ 71cabdff1aSopenharmony_ci char *features; /**< List of server's features represented as raw response */ 72cabdff1aSopenharmony_ci char *dir_buffer; 73cabdff1aSopenharmony_ci size_t dir_buffer_size; 74cabdff1aSopenharmony_ci size_t dir_buffer_offset; 75cabdff1aSopenharmony_ci int utf8; 76cabdff1aSopenharmony_ci const char *option_user; /**< User to be used if none given in the URL */ 77cabdff1aSopenharmony_ci const char *option_password; /**< Password to be used if none given in the URL */ 78cabdff1aSopenharmony_ci} FTPContext; 79cabdff1aSopenharmony_ci 80cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(FTPContext, x) 81cabdff1aSopenharmony_ci#define D AV_OPT_FLAG_DECODING_PARAM 82cabdff1aSopenharmony_ci#define E AV_OPT_FLAG_ENCODING_PARAM 83cabdff1aSopenharmony_cistatic const AVOption options[] = { 84cabdff1aSopenharmony_ci {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, 85cabdff1aSopenharmony_ci {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E }, 86cabdff1aSopenharmony_ci {"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 }, 87cabdff1aSopenharmony_ci {"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 }, 88cabdff1aSopenharmony_ci {"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 }, 89cabdff1aSopenharmony_ci {NULL} 90cabdff1aSopenharmony_ci}; 91cabdff1aSopenharmony_ci 92cabdff1aSopenharmony_cistatic const AVClass ftp_context_class = { 93cabdff1aSopenharmony_ci .class_name = "ftp", 94cabdff1aSopenharmony_ci .item_name = av_default_item_name, 95cabdff1aSopenharmony_ci .option = options, 96cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 97cabdff1aSopenharmony_ci}; 98cabdff1aSopenharmony_ci 99cabdff1aSopenharmony_cistatic int ftp_close(URLContext *h); 100cabdff1aSopenharmony_ci 101cabdff1aSopenharmony_cistatic int ftp_getc(FTPContext *s) 102cabdff1aSopenharmony_ci{ 103cabdff1aSopenharmony_ci int len; 104cabdff1aSopenharmony_ci if (s->control_buf_ptr >= s->control_buf_end) { 105cabdff1aSopenharmony_ci len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE); 106cabdff1aSopenharmony_ci if (len < 0) { 107cabdff1aSopenharmony_ci return len; 108cabdff1aSopenharmony_ci } else if (!len) { 109cabdff1aSopenharmony_ci return -1; 110cabdff1aSopenharmony_ci } else { 111cabdff1aSopenharmony_ci s->control_buf_ptr = s->control_buffer; 112cabdff1aSopenharmony_ci s->control_buf_end = s->control_buffer + len; 113cabdff1aSopenharmony_ci } 114cabdff1aSopenharmony_ci } 115cabdff1aSopenharmony_ci return *s->control_buf_ptr++; 116cabdff1aSopenharmony_ci} 117cabdff1aSopenharmony_ci 118cabdff1aSopenharmony_cistatic int ftp_get_line(FTPContext *s, char *line, int line_size) 119cabdff1aSopenharmony_ci{ 120cabdff1aSopenharmony_ci int ch; 121cabdff1aSopenharmony_ci char *q = line; 122cabdff1aSopenharmony_ci 123cabdff1aSopenharmony_ci for (;;) { 124cabdff1aSopenharmony_ci ch = ftp_getc(s); 125cabdff1aSopenharmony_ci if (ch < 0) { 126cabdff1aSopenharmony_ci return ch; 127cabdff1aSopenharmony_ci } 128cabdff1aSopenharmony_ci if (ch == '\n') { 129cabdff1aSopenharmony_ci /* process line */ 130cabdff1aSopenharmony_ci if (q > line && q[-1] == '\r') 131cabdff1aSopenharmony_ci q--; 132cabdff1aSopenharmony_ci *q = '\0'; 133cabdff1aSopenharmony_ci return 0; 134cabdff1aSopenharmony_ci } else { 135cabdff1aSopenharmony_ci if ((q - line) < line_size - 1) 136cabdff1aSopenharmony_ci *q++ = ch; 137cabdff1aSopenharmony_ci } 138cabdff1aSopenharmony_ci } 139cabdff1aSopenharmony_ci} 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci/* 142cabdff1aSopenharmony_ci * This routine returns ftp server response code. 143cabdff1aSopenharmony_ci * Server may send more than one response for a certain command. 144cabdff1aSopenharmony_ci * First expected code is returned. 145cabdff1aSopenharmony_ci */ 146cabdff1aSopenharmony_cistatic int ftp_status(FTPContext *s, char **line, const int response_codes[]) 147cabdff1aSopenharmony_ci{ 148cabdff1aSopenharmony_ci int err, i, dash = 0, result = 0, code_found = 0, linesize; 149cabdff1aSopenharmony_ci char buf[CONTROL_BUFFER_SIZE]; 150cabdff1aSopenharmony_ci AVBPrint line_buffer; 151cabdff1aSopenharmony_ci 152cabdff1aSopenharmony_ci if (line) 153cabdff1aSopenharmony_ci av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC); 154cabdff1aSopenharmony_ci 155cabdff1aSopenharmony_ci while (!code_found || dash) { 156cabdff1aSopenharmony_ci if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) { 157cabdff1aSopenharmony_ci if (line) 158cabdff1aSopenharmony_ci av_bprint_finalize(&line_buffer, NULL); 159cabdff1aSopenharmony_ci return err; 160cabdff1aSopenharmony_ci } 161cabdff1aSopenharmony_ci 162cabdff1aSopenharmony_ci av_log(s, AV_LOG_DEBUG, "%s\n", buf); 163cabdff1aSopenharmony_ci 164cabdff1aSopenharmony_ci linesize = strlen(buf); 165cabdff1aSopenharmony_ci err = 0; 166cabdff1aSopenharmony_ci if (linesize >= 3) { 167cabdff1aSopenharmony_ci for (i = 0; i < 3; ++i) { 168cabdff1aSopenharmony_ci if (buf[i] < '0' || buf[i] > '9') { 169cabdff1aSopenharmony_ci err = 0; 170cabdff1aSopenharmony_ci break; 171cabdff1aSopenharmony_ci } 172cabdff1aSopenharmony_ci err *= 10; 173cabdff1aSopenharmony_ci err += buf[i] - '0'; 174cabdff1aSopenharmony_ci } 175cabdff1aSopenharmony_ci } 176cabdff1aSopenharmony_ci 177cabdff1aSopenharmony_ci if (!code_found) { 178cabdff1aSopenharmony_ci if (err >= 500) { 179cabdff1aSopenharmony_ci code_found = 1; 180cabdff1aSopenharmony_ci result = err; 181cabdff1aSopenharmony_ci } else 182cabdff1aSopenharmony_ci for (i = 0; response_codes[i]; ++i) { 183cabdff1aSopenharmony_ci if (err == response_codes[i]) { 184cabdff1aSopenharmony_ci code_found = 1; 185cabdff1aSopenharmony_ci result = err; 186cabdff1aSopenharmony_ci break; 187cabdff1aSopenharmony_ci } 188cabdff1aSopenharmony_ci } 189cabdff1aSopenharmony_ci } 190cabdff1aSopenharmony_ci if (code_found) { 191cabdff1aSopenharmony_ci if (line) 192cabdff1aSopenharmony_ci av_bprintf(&line_buffer, "%s\r\n", buf); 193cabdff1aSopenharmony_ci if (linesize >= 4) { 194cabdff1aSopenharmony_ci if (!dash && buf[3] == '-') 195cabdff1aSopenharmony_ci dash = err; 196cabdff1aSopenharmony_ci else if (err == dash && buf[3] == ' ') 197cabdff1aSopenharmony_ci dash = 0; 198cabdff1aSopenharmony_ci } 199cabdff1aSopenharmony_ci } 200cabdff1aSopenharmony_ci } 201cabdff1aSopenharmony_ci 202cabdff1aSopenharmony_ci if (line) 203cabdff1aSopenharmony_ci av_bprint_finalize(&line_buffer, line); 204cabdff1aSopenharmony_ci return result; 205cabdff1aSopenharmony_ci} 206cabdff1aSopenharmony_ci 207cabdff1aSopenharmony_cistatic int ftp_send_command(FTPContext *s, const char *command, 208cabdff1aSopenharmony_ci const int response_codes[], char **response) 209cabdff1aSopenharmony_ci{ 210cabdff1aSopenharmony_ci int err; 211cabdff1aSopenharmony_ci 212cabdff1aSopenharmony_ci ff_dlog(s, "%s", command); 213cabdff1aSopenharmony_ci 214cabdff1aSopenharmony_ci if (response) 215cabdff1aSopenharmony_ci *response = NULL; 216cabdff1aSopenharmony_ci 217cabdff1aSopenharmony_ci if (!s->conn_control) 218cabdff1aSopenharmony_ci return AVERROR(EIO); 219cabdff1aSopenharmony_ci 220cabdff1aSopenharmony_ci if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0) 221cabdff1aSopenharmony_ci return err; 222cabdff1aSopenharmony_ci if (!err) 223cabdff1aSopenharmony_ci return -1; 224cabdff1aSopenharmony_ci 225cabdff1aSopenharmony_ci /* return status */ 226cabdff1aSopenharmony_ci if (response_codes) { 227cabdff1aSopenharmony_ci return ftp_status(s, response, response_codes); 228cabdff1aSopenharmony_ci } 229cabdff1aSopenharmony_ci return 0; 230cabdff1aSopenharmony_ci} 231cabdff1aSopenharmony_ci 232cabdff1aSopenharmony_cistatic void ftp_close_data_connection(FTPContext *s) 233cabdff1aSopenharmony_ci{ 234cabdff1aSopenharmony_ci ffurl_closep(&s->conn_data); 235cabdff1aSopenharmony_ci s->state = DISCONNECTED; 236cabdff1aSopenharmony_ci} 237cabdff1aSopenharmony_ci 238cabdff1aSopenharmony_cistatic void ftp_close_both_connections(FTPContext *s) 239cabdff1aSopenharmony_ci{ 240cabdff1aSopenharmony_ci ffurl_closep(&s->conn_control); 241cabdff1aSopenharmony_ci ftp_close_data_connection(s); 242cabdff1aSopenharmony_ci} 243cabdff1aSopenharmony_ci 244cabdff1aSopenharmony_cistatic int ftp_auth(FTPContext *s) 245cabdff1aSopenharmony_ci{ 246cabdff1aSopenharmony_ci char buf[CONTROL_BUFFER_SIZE]; 247cabdff1aSopenharmony_ci int err; 248cabdff1aSopenharmony_ci static const int user_codes[] = {331, 230, 0}; 249cabdff1aSopenharmony_ci static const int pass_codes[] = {230, 0}; 250cabdff1aSopenharmony_ci 251cabdff1aSopenharmony_ci if (strpbrk(s->user, "\r\n")) 252cabdff1aSopenharmony_ci return AVERROR(EINVAL); 253cabdff1aSopenharmony_ci err = snprintf(buf, sizeof(buf), "USER %s\r\n", s->user); 254cabdff1aSopenharmony_ci if (err >= sizeof(buf)) 255cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 256cabdff1aSopenharmony_ci 257cabdff1aSopenharmony_ci err = ftp_send_command(s, buf, user_codes, NULL); 258cabdff1aSopenharmony_ci if (err == 331) { 259cabdff1aSopenharmony_ci if (s->password) { 260cabdff1aSopenharmony_ci if (strpbrk(s->password, "\r\n")) 261cabdff1aSopenharmony_ci return AVERROR(EINVAL); 262cabdff1aSopenharmony_ci err = snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password); 263cabdff1aSopenharmony_ci if (err >= sizeof(buf)) 264cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 265cabdff1aSopenharmony_ci 266cabdff1aSopenharmony_ci err = ftp_send_command(s, buf, pass_codes, NULL); 267cabdff1aSopenharmony_ci } else 268cabdff1aSopenharmony_ci return AVERROR(EACCES); 269cabdff1aSopenharmony_ci } 270cabdff1aSopenharmony_ci if (err != 230) 271cabdff1aSopenharmony_ci return AVERROR(EACCES); 272cabdff1aSopenharmony_ci 273cabdff1aSopenharmony_ci return 0; 274cabdff1aSopenharmony_ci} 275cabdff1aSopenharmony_ci 276cabdff1aSopenharmony_cistatic int ftp_passive_mode_epsv(FTPContext *s) 277cabdff1aSopenharmony_ci{ 278cabdff1aSopenharmony_ci char *res = NULL, *start = NULL, *end = NULL; 279cabdff1aSopenharmony_ci int i; 280cabdff1aSopenharmony_ci static const char d = '|'; 281cabdff1aSopenharmony_ci static const char *command = "EPSV\r\n"; 282cabdff1aSopenharmony_ci static const int epsv_codes[] = {229, 0}; 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res) 285cabdff1aSopenharmony_ci goto fail; 286cabdff1aSopenharmony_ci 287cabdff1aSopenharmony_ci for (i = 0; res[i]; ++i) { 288cabdff1aSopenharmony_ci if (res[i] == '(') { 289cabdff1aSopenharmony_ci start = res + i + 1; 290cabdff1aSopenharmony_ci } else if (res[i] == ')') { 291cabdff1aSopenharmony_ci end = res + i; 292cabdff1aSopenharmony_ci break; 293cabdff1aSopenharmony_ci } 294cabdff1aSopenharmony_ci } 295cabdff1aSopenharmony_ci if (!start || !end) 296cabdff1aSopenharmony_ci goto fail; 297cabdff1aSopenharmony_ci 298cabdff1aSopenharmony_ci *end = '\0'; 299cabdff1aSopenharmony_ci if (strlen(start) < 5) 300cabdff1aSopenharmony_ci goto fail; 301cabdff1aSopenharmony_ci if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d) 302cabdff1aSopenharmony_ci goto fail; 303cabdff1aSopenharmony_ci start += 3; 304cabdff1aSopenharmony_ci end[-1] = '\0'; 305cabdff1aSopenharmony_ci 306cabdff1aSopenharmony_ci s->server_data_port = atoi(start); 307cabdff1aSopenharmony_ci ff_dlog(s, "Server data port: %d\n", s->server_data_port); 308cabdff1aSopenharmony_ci 309cabdff1aSopenharmony_ci av_free(res); 310cabdff1aSopenharmony_ci return 0; 311cabdff1aSopenharmony_ci 312cabdff1aSopenharmony_ci fail: 313cabdff1aSopenharmony_ci av_free(res); 314cabdff1aSopenharmony_ci s->server_data_port = -1; 315cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 316cabdff1aSopenharmony_ci} 317cabdff1aSopenharmony_ci 318cabdff1aSopenharmony_cistatic int ftp_passive_mode(FTPContext *s) 319cabdff1aSopenharmony_ci{ 320cabdff1aSopenharmony_ci char *res = NULL, *start = NULL, *end = NULL; 321cabdff1aSopenharmony_ci int i; 322cabdff1aSopenharmony_ci static const char *command = "PASV\r\n"; 323cabdff1aSopenharmony_ci static const int pasv_codes[] = {227, 0}; 324cabdff1aSopenharmony_ci 325cabdff1aSopenharmony_ci if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res) 326cabdff1aSopenharmony_ci goto fail; 327cabdff1aSopenharmony_ci 328cabdff1aSopenharmony_ci for (i = 0; res[i]; ++i) { 329cabdff1aSopenharmony_ci if (res[i] == '(') { 330cabdff1aSopenharmony_ci start = res + i + 1; 331cabdff1aSopenharmony_ci } else if (res[i] == ')') { 332cabdff1aSopenharmony_ci end = res + i; 333cabdff1aSopenharmony_ci break; 334cabdff1aSopenharmony_ci } 335cabdff1aSopenharmony_ci } 336cabdff1aSopenharmony_ci if (!start || !end) 337cabdff1aSopenharmony_ci goto fail; 338cabdff1aSopenharmony_ci 339cabdff1aSopenharmony_ci *end = '\0'; 340cabdff1aSopenharmony_ci /* skip ip */ 341cabdff1aSopenharmony_ci if (!av_strtok(start, ",", &end)) goto fail; 342cabdff1aSopenharmony_ci if (!av_strtok(NULL, ",", &end)) goto fail; 343cabdff1aSopenharmony_ci if (!av_strtok(NULL, ",", &end)) goto fail; 344cabdff1aSopenharmony_ci if (!av_strtok(NULL, ",", &end)) goto fail; 345cabdff1aSopenharmony_ci 346cabdff1aSopenharmony_ci /* parse port number */ 347cabdff1aSopenharmony_ci start = av_strtok(NULL, ",", &end); 348cabdff1aSopenharmony_ci if (!start) goto fail; 349cabdff1aSopenharmony_ci s->server_data_port = atoi(start) * 256; 350cabdff1aSopenharmony_ci start = av_strtok(NULL, ",", &end); 351cabdff1aSopenharmony_ci if (!start) goto fail; 352cabdff1aSopenharmony_ci s->server_data_port += atoi(start); 353cabdff1aSopenharmony_ci ff_dlog(s, "Server data port: %d\n", s->server_data_port); 354cabdff1aSopenharmony_ci 355cabdff1aSopenharmony_ci av_free(res); 356cabdff1aSopenharmony_ci return 0; 357cabdff1aSopenharmony_ci 358cabdff1aSopenharmony_ci fail: 359cabdff1aSopenharmony_ci av_free(res); 360cabdff1aSopenharmony_ci s->server_data_port = -1; 361cabdff1aSopenharmony_ci return AVERROR(EIO); 362cabdff1aSopenharmony_ci} 363cabdff1aSopenharmony_ci 364cabdff1aSopenharmony_cistatic int ftp_current_dir(FTPContext *s) 365cabdff1aSopenharmony_ci{ 366cabdff1aSopenharmony_ci char *res = NULL, *start = NULL, *end = NULL; 367cabdff1aSopenharmony_ci int i; 368cabdff1aSopenharmony_ci static const char *command = "PWD\r\n"; 369cabdff1aSopenharmony_ci static const int pwd_codes[] = {257, 0}; 370cabdff1aSopenharmony_ci 371cabdff1aSopenharmony_ci if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res) 372cabdff1aSopenharmony_ci goto fail; 373cabdff1aSopenharmony_ci 374cabdff1aSopenharmony_ci for (i = 0; res[i]; ++i) { 375cabdff1aSopenharmony_ci if (res[i] == '"') { 376cabdff1aSopenharmony_ci if (!start) { 377cabdff1aSopenharmony_ci start = res + i + 1; 378cabdff1aSopenharmony_ci continue; 379cabdff1aSopenharmony_ci } 380cabdff1aSopenharmony_ci end = res + i; 381cabdff1aSopenharmony_ci break; 382cabdff1aSopenharmony_ci } 383cabdff1aSopenharmony_ci } 384cabdff1aSopenharmony_ci 385cabdff1aSopenharmony_ci if (!end) 386cabdff1aSopenharmony_ci goto fail; 387cabdff1aSopenharmony_ci 388cabdff1aSopenharmony_ci *end = '\0'; 389cabdff1aSopenharmony_ci s->path = av_strdup(start); 390cabdff1aSopenharmony_ci 391cabdff1aSopenharmony_ci av_free(res); 392cabdff1aSopenharmony_ci 393cabdff1aSopenharmony_ci if (!s->path) 394cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 395cabdff1aSopenharmony_ci return 0; 396cabdff1aSopenharmony_ci 397cabdff1aSopenharmony_ci fail: 398cabdff1aSopenharmony_ci av_free(res); 399cabdff1aSopenharmony_ci return AVERROR(EIO); 400cabdff1aSopenharmony_ci} 401cabdff1aSopenharmony_ci 402cabdff1aSopenharmony_cistatic int ftp_file_size(FTPContext *s) 403cabdff1aSopenharmony_ci{ 404cabdff1aSopenharmony_ci char command[CONTROL_BUFFER_SIZE]; 405cabdff1aSopenharmony_ci char *res = NULL; 406cabdff1aSopenharmony_ci int ret; 407cabdff1aSopenharmony_ci static const int size_codes[] = {213, 0}; 408cabdff1aSopenharmony_ci 409cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "SIZE %s\r\n", s->path); 410cabdff1aSopenharmony_ci if (ret >= sizeof(command)) 411cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 412cabdff1aSopenharmony_ci 413cabdff1aSopenharmony_ci if (ftp_send_command(s, command, size_codes, &res) == 213 && res && strlen(res) > 4) { 414cabdff1aSopenharmony_ci s->filesize = strtoll(&res[4], NULL, 10); 415cabdff1aSopenharmony_ci } else { 416cabdff1aSopenharmony_ci s->filesize = -1; 417cabdff1aSopenharmony_ci av_free(res); 418cabdff1aSopenharmony_ci return AVERROR(EIO); 419cabdff1aSopenharmony_ci } 420cabdff1aSopenharmony_ci 421cabdff1aSopenharmony_ci av_free(res); 422cabdff1aSopenharmony_ci return 0; 423cabdff1aSopenharmony_ci} 424cabdff1aSopenharmony_ci 425cabdff1aSopenharmony_cistatic int ftp_retrieve(FTPContext *s) 426cabdff1aSopenharmony_ci{ 427cabdff1aSopenharmony_ci char command[CONTROL_BUFFER_SIZE]; 428cabdff1aSopenharmony_ci static const int retr_codes[] = {150, 125, 0}; 429cabdff1aSopenharmony_ci int resp_code, ret; 430cabdff1aSopenharmony_ci 431cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "RETR %s\r\n", s->path); 432cabdff1aSopenharmony_ci if (ret >= sizeof(command)) 433cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 434cabdff1aSopenharmony_ci 435cabdff1aSopenharmony_ci resp_code = ftp_send_command(s, command, retr_codes, NULL); 436cabdff1aSopenharmony_ci if (resp_code != 125 && resp_code != 150) 437cabdff1aSopenharmony_ci return AVERROR(EIO); 438cabdff1aSopenharmony_ci 439cabdff1aSopenharmony_ci s->state = DOWNLOADING; 440cabdff1aSopenharmony_ci 441cabdff1aSopenharmony_ci return 0; 442cabdff1aSopenharmony_ci} 443cabdff1aSopenharmony_ci 444cabdff1aSopenharmony_cistatic int ftp_store(FTPContext *s) 445cabdff1aSopenharmony_ci{ 446cabdff1aSopenharmony_ci char command[CONTROL_BUFFER_SIZE]; 447cabdff1aSopenharmony_ci static const int stor_codes[] = {150, 125, 0}; 448cabdff1aSopenharmony_ci int resp_code, ret; 449cabdff1aSopenharmony_ci 450cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "STOR %s\r\n", s->path); 451cabdff1aSopenharmony_ci if (ret >= sizeof(command)) 452cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 453cabdff1aSopenharmony_ci 454cabdff1aSopenharmony_ci resp_code = ftp_send_command(s, command, stor_codes, NULL); 455cabdff1aSopenharmony_ci if (resp_code != 125 && resp_code != 150) 456cabdff1aSopenharmony_ci return AVERROR(EIO); 457cabdff1aSopenharmony_ci 458cabdff1aSopenharmony_ci s->state = UPLOADING; 459cabdff1aSopenharmony_ci 460cabdff1aSopenharmony_ci return 0; 461cabdff1aSopenharmony_ci} 462cabdff1aSopenharmony_ci 463cabdff1aSopenharmony_cistatic int ftp_type(FTPContext *s) 464cabdff1aSopenharmony_ci{ 465cabdff1aSopenharmony_ci static const char *command = "TYPE I\r\n"; 466cabdff1aSopenharmony_ci static const int type_codes[] = {200, 0}; 467cabdff1aSopenharmony_ci 468cabdff1aSopenharmony_ci if (ftp_send_command(s, command, type_codes, NULL) != 200) 469cabdff1aSopenharmony_ci return AVERROR(EIO); 470cabdff1aSopenharmony_ci 471cabdff1aSopenharmony_ci return 0; 472cabdff1aSopenharmony_ci} 473cabdff1aSopenharmony_ci 474cabdff1aSopenharmony_cistatic int ftp_restart(FTPContext *s, int64_t pos) 475cabdff1aSopenharmony_ci{ 476cabdff1aSopenharmony_ci char command[CONTROL_BUFFER_SIZE]; 477cabdff1aSopenharmony_ci static const int rest_codes[] = {350, 0}; 478cabdff1aSopenharmony_ci 479cabdff1aSopenharmony_ci snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos); 480cabdff1aSopenharmony_ci if (ftp_send_command(s, command, rest_codes, NULL) != 350) 481cabdff1aSopenharmony_ci return AVERROR(EIO); 482cabdff1aSopenharmony_ci 483cabdff1aSopenharmony_ci return 0; 484cabdff1aSopenharmony_ci} 485cabdff1aSopenharmony_ci 486cabdff1aSopenharmony_cistatic int ftp_set_dir(FTPContext *s) 487cabdff1aSopenharmony_ci{ 488cabdff1aSopenharmony_ci static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */ 489cabdff1aSopenharmony_ci char command[MAX_URL_SIZE]; 490cabdff1aSopenharmony_ci int ret; 491cabdff1aSopenharmony_ci 492cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "CWD %s\r\n", s->path); 493cabdff1aSopenharmony_ci if (ret >= sizeof(command)) 494cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 495cabdff1aSopenharmony_ci 496cabdff1aSopenharmony_ci if (ftp_send_command(s, command, cwd_codes, NULL) != 250) 497cabdff1aSopenharmony_ci return AVERROR(EIO); 498cabdff1aSopenharmony_ci return 0; 499cabdff1aSopenharmony_ci} 500cabdff1aSopenharmony_ci 501cabdff1aSopenharmony_cistatic int ftp_list_mlsd(FTPContext *s) 502cabdff1aSopenharmony_ci{ 503cabdff1aSopenharmony_ci static const char *command = "MLSD\r\n"; 504cabdff1aSopenharmony_ci static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */ 505cabdff1aSopenharmony_ci 506cabdff1aSopenharmony_ci if (ftp_send_command(s, command, mlsd_codes, NULL) != 150) 507cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 508cabdff1aSopenharmony_ci s->listing_method = MLSD; 509cabdff1aSopenharmony_ci return 0; 510cabdff1aSopenharmony_ci} 511cabdff1aSopenharmony_ci 512cabdff1aSopenharmony_cistatic int ftp_list_nlst(FTPContext *s) 513cabdff1aSopenharmony_ci{ 514cabdff1aSopenharmony_ci static const char *command = "NLST\r\n"; 515cabdff1aSopenharmony_ci static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0}; 516cabdff1aSopenharmony_ci 517cabdff1aSopenharmony_ci if (ftp_send_command(s, command, nlst_codes, NULL) != 226) 518cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 519cabdff1aSopenharmony_ci s->listing_method = NLST; 520cabdff1aSopenharmony_ci return 0; 521cabdff1aSopenharmony_ci} 522cabdff1aSopenharmony_ci 523cabdff1aSopenharmony_cistatic int ftp_list(FTPContext *s) 524cabdff1aSopenharmony_ci{ 525cabdff1aSopenharmony_ci int ret; 526cabdff1aSopenharmony_ci s->state = LISTING_DIR; 527cabdff1aSopenharmony_ci 528cabdff1aSopenharmony_ci if ((ret = ftp_list_mlsd(s)) < 0) 529cabdff1aSopenharmony_ci ret = ftp_list_nlst(s); 530cabdff1aSopenharmony_ci 531cabdff1aSopenharmony_ci return ret; 532cabdff1aSopenharmony_ci} 533cabdff1aSopenharmony_ci 534cabdff1aSopenharmony_cistatic int ftp_has_feature(FTPContext *s, const char *feature_name) 535cabdff1aSopenharmony_ci{ 536cabdff1aSopenharmony_ci if (!s->features) 537cabdff1aSopenharmony_ci return 0; 538cabdff1aSopenharmony_ci 539cabdff1aSopenharmony_ci return av_stristr(s->features, feature_name) != NULL; 540cabdff1aSopenharmony_ci} 541cabdff1aSopenharmony_ci 542cabdff1aSopenharmony_cistatic int ftp_features(FTPContext *s) 543cabdff1aSopenharmony_ci{ 544cabdff1aSopenharmony_ci static const char *feat_command = "FEAT\r\n"; 545cabdff1aSopenharmony_ci static const char *enable_utf8_command = "OPTS UTF8 ON\r\n"; 546cabdff1aSopenharmony_ci static const int feat_codes[] = {211, 0}; 547cabdff1aSopenharmony_ci static const int opts_codes[] = {200, 202, 451, 0}; 548cabdff1aSopenharmony_ci 549cabdff1aSopenharmony_ci av_freep(&s->features); 550cabdff1aSopenharmony_ci if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) { 551cabdff1aSopenharmony_ci av_freep(&s->features); 552cabdff1aSopenharmony_ci } 553cabdff1aSopenharmony_ci 554cabdff1aSopenharmony_ci if (ftp_has_feature(s, "UTF8")) { 555cabdff1aSopenharmony_ci int ret = ftp_send_command(s, enable_utf8_command, opts_codes, NULL); 556cabdff1aSopenharmony_ci if (ret == 200 || ret == 202) 557cabdff1aSopenharmony_ci s->utf8 = 1; 558cabdff1aSopenharmony_ci } 559cabdff1aSopenharmony_ci 560cabdff1aSopenharmony_ci return 0; 561cabdff1aSopenharmony_ci} 562cabdff1aSopenharmony_ci 563cabdff1aSopenharmony_cistatic int ftp_connect_control_connection(URLContext *h) 564cabdff1aSopenharmony_ci{ 565cabdff1aSopenharmony_ci char buf[CONTROL_BUFFER_SIZE], *response = NULL; 566cabdff1aSopenharmony_ci int err; 567cabdff1aSopenharmony_ci AVDictionary *opts = NULL; 568cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 569cabdff1aSopenharmony_ci static const int connect_codes[] = {220, 0}; 570cabdff1aSopenharmony_ci 571cabdff1aSopenharmony_ci if (!s->conn_control) { 572cabdff1aSopenharmony_ci ff_url_join(buf, sizeof(buf), "tcp", NULL, 573cabdff1aSopenharmony_ci s->hostname, s->server_control_port, NULL); 574cabdff1aSopenharmony_ci if (s->rw_timeout != -1) { 575cabdff1aSopenharmony_ci av_dict_set_int(&opts, "timeout", s->rw_timeout, 0); 576cabdff1aSopenharmony_ci } /* if option is not given, don't pass it and let tcp use its own default */ 577cabdff1aSopenharmony_ci err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE, 578cabdff1aSopenharmony_ci &h->interrupt_callback, &opts, 579cabdff1aSopenharmony_ci h->protocol_whitelist, h->protocol_blacklist, h); 580cabdff1aSopenharmony_ci av_dict_free(&opts); 581cabdff1aSopenharmony_ci if (err < 0) { 582cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "Cannot open control connection\n"); 583cabdff1aSopenharmony_ci return err; 584cabdff1aSopenharmony_ci } 585cabdff1aSopenharmony_ci 586cabdff1aSopenharmony_ci /* check if server is ready */ 587cabdff1aSopenharmony_ci if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) { 588cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n"); 589cabdff1aSopenharmony_ci return AVERROR(EACCES); 590cabdff1aSopenharmony_ci } 591cabdff1aSopenharmony_ci 592cabdff1aSopenharmony_ci if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) { 593cabdff1aSopenharmony_ci 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."); 594cabdff1aSopenharmony_ci } 595cabdff1aSopenharmony_ci av_free(response); 596cabdff1aSopenharmony_ci 597cabdff1aSopenharmony_ci if ((err = ftp_auth(s)) < 0) { 598cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "FTP authentication failed\n"); 599cabdff1aSopenharmony_ci return err; 600cabdff1aSopenharmony_ci } 601cabdff1aSopenharmony_ci 602cabdff1aSopenharmony_ci if ((err = ftp_type(s)) < 0) { 603cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "Set content type failed\n"); 604cabdff1aSopenharmony_ci return err; 605cabdff1aSopenharmony_ci } 606cabdff1aSopenharmony_ci 607cabdff1aSopenharmony_ci ftp_features(s); 608cabdff1aSopenharmony_ci } 609cabdff1aSopenharmony_ci return 0; 610cabdff1aSopenharmony_ci} 611cabdff1aSopenharmony_ci 612cabdff1aSopenharmony_cistatic int ftp_connect_data_connection(URLContext *h) 613cabdff1aSopenharmony_ci{ 614cabdff1aSopenharmony_ci int err; 615cabdff1aSopenharmony_ci char buf[CONTROL_BUFFER_SIZE]; 616cabdff1aSopenharmony_ci AVDictionary *opts = NULL; 617cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 618cabdff1aSopenharmony_ci 619cabdff1aSopenharmony_ci if (!s->conn_data) { 620cabdff1aSopenharmony_ci /* Enter passive mode */ 621cabdff1aSopenharmony_ci if (ftp_passive_mode_epsv(s) < 0) { 622cabdff1aSopenharmony_ci /* Use PASV as fallback */ 623cabdff1aSopenharmony_ci if ((err = ftp_passive_mode(s)) < 0) 624cabdff1aSopenharmony_ci return err; 625cabdff1aSopenharmony_ci } 626cabdff1aSopenharmony_ci /* Open data connection */ 627cabdff1aSopenharmony_ci ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL); 628cabdff1aSopenharmony_ci if (s->rw_timeout != -1) { 629cabdff1aSopenharmony_ci av_dict_set_int(&opts, "timeout", s->rw_timeout, 0); 630cabdff1aSopenharmony_ci } /* if option is not given, don't pass it and let tcp use its own default */ 631cabdff1aSopenharmony_ci err = ffurl_open_whitelist(&s->conn_data, buf, h->flags, 632cabdff1aSopenharmony_ci &h->interrupt_callback, &opts, 633cabdff1aSopenharmony_ci h->protocol_whitelist, h->protocol_blacklist, h); 634cabdff1aSopenharmony_ci av_dict_free(&opts); 635cabdff1aSopenharmony_ci if (err < 0) 636cabdff1aSopenharmony_ci return err; 637cabdff1aSopenharmony_ci 638cabdff1aSopenharmony_ci if (s->position) 639cabdff1aSopenharmony_ci if ((err = ftp_restart(s, s->position)) < 0) 640cabdff1aSopenharmony_ci return err; 641cabdff1aSopenharmony_ci } 642cabdff1aSopenharmony_ci s->state = READY; 643cabdff1aSopenharmony_ci return 0; 644cabdff1aSopenharmony_ci} 645cabdff1aSopenharmony_ci 646cabdff1aSopenharmony_cistatic int ftp_abort(URLContext *h) 647cabdff1aSopenharmony_ci{ 648cabdff1aSopenharmony_ci static const char *command = "ABOR\r\n"; 649cabdff1aSopenharmony_ci int err; 650cabdff1aSopenharmony_ci static const int abor_codes[] = {225, 226, 0}; 651cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 652cabdff1aSopenharmony_ci 653cabdff1aSopenharmony_ci /* According to RCF 959: 654cabdff1aSopenharmony_ci "ABOR command tells the server to abort the previous FTP 655cabdff1aSopenharmony_ci service command and any associated transfer of data." 656cabdff1aSopenharmony_ci 657cabdff1aSopenharmony_ci There are FTP server implementations that don't response 658cabdff1aSopenharmony_ci to any commands during data transfer in passive mode (including ABOR). 659cabdff1aSopenharmony_ci 660cabdff1aSopenharmony_ci This implementation closes data connection by force. 661cabdff1aSopenharmony_ci */ 662cabdff1aSopenharmony_ci 663cabdff1aSopenharmony_ci if (ftp_send_command(s, command, NULL, NULL) < 0) { 664cabdff1aSopenharmony_ci ftp_close_both_connections(s); 665cabdff1aSopenharmony_ci if ((err = ftp_connect_control_connection(h)) < 0) { 666cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "Reconnect failed.\n"); 667cabdff1aSopenharmony_ci return err; 668cabdff1aSopenharmony_ci } 669cabdff1aSopenharmony_ci } else { 670cabdff1aSopenharmony_ci ftp_close_data_connection(s); 671cabdff1aSopenharmony_ci if (ftp_status(s, NULL, abor_codes) < 225) { 672cabdff1aSopenharmony_ci /* wu-ftpd also closes control connection after data connection closing */ 673cabdff1aSopenharmony_ci ffurl_closep(&s->conn_control); 674cabdff1aSopenharmony_ci if ((err = ftp_connect_control_connection(h)) < 0) { 675cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "Reconnect failed.\n"); 676cabdff1aSopenharmony_ci return err; 677cabdff1aSopenharmony_ci } 678cabdff1aSopenharmony_ci } 679cabdff1aSopenharmony_ci } 680cabdff1aSopenharmony_ci 681cabdff1aSopenharmony_ci return 0; 682cabdff1aSopenharmony_ci} 683cabdff1aSopenharmony_ci 684cabdff1aSopenharmony_cistatic int ftp_connect(URLContext *h, const char *url) 685cabdff1aSopenharmony_ci{ 686cabdff1aSopenharmony_ci char proto[10], path[MAX_URL_SIZE], credentials[MAX_URL_SIZE], hostname[MAX_URL_SIZE]; 687cabdff1aSopenharmony_ci const char *tok_user = NULL, *tok_pass = NULL; 688cabdff1aSopenharmony_ci char *newpath = NULL; 689cabdff1aSopenharmony_ci int err; 690cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 691cabdff1aSopenharmony_ci 692cabdff1aSopenharmony_ci s->state = DISCONNECTED; 693cabdff1aSopenharmony_ci s->listing_method = UNKNOWN_METHOD; 694cabdff1aSopenharmony_ci s->filesize = -1; 695cabdff1aSopenharmony_ci s->position = 0; 696cabdff1aSopenharmony_ci s->features = NULL; 697cabdff1aSopenharmony_ci 698cabdff1aSopenharmony_ci av_url_split(proto, sizeof(proto), 699cabdff1aSopenharmony_ci credentials, sizeof(credentials), 700cabdff1aSopenharmony_ci hostname, sizeof(hostname), 701cabdff1aSopenharmony_ci &s->server_control_port, 702cabdff1aSopenharmony_ci path, sizeof(path), 703cabdff1aSopenharmony_ci url); 704cabdff1aSopenharmony_ci 705cabdff1aSopenharmony_ci if (!*credentials) { 706cabdff1aSopenharmony_ci if (!s->option_user) { 707cabdff1aSopenharmony_ci tok_user = "anonymous"; 708cabdff1aSopenharmony_ci tok_pass = av_x_if_null(s->anonymous_password, "nopassword"); 709cabdff1aSopenharmony_ci } else { 710cabdff1aSopenharmony_ci tok_user = s->option_user; 711cabdff1aSopenharmony_ci tok_pass = s->option_password; 712cabdff1aSopenharmony_ci } 713cabdff1aSopenharmony_ci s->user = av_strdup(tok_user); 714cabdff1aSopenharmony_ci s->password = av_strdup(tok_pass); 715cabdff1aSopenharmony_ci } else { 716cabdff1aSopenharmony_ci char *pass = strchr(credentials, ':'); 717cabdff1aSopenharmony_ci if (pass) { 718cabdff1aSopenharmony_ci *pass++ = '\0'; 719cabdff1aSopenharmony_ci tok_pass = pass; 720cabdff1aSopenharmony_ci s->password = ff_urldecode(pass, 0); 721cabdff1aSopenharmony_ci } else { 722cabdff1aSopenharmony_ci tok_pass = s->option_password; 723cabdff1aSopenharmony_ci s->password = av_strdup(tok_pass); 724cabdff1aSopenharmony_ci } 725cabdff1aSopenharmony_ci s->user = ff_urldecode(credentials, 0); 726cabdff1aSopenharmony_ci } 727cabdff1aSopenharmony_ci s->hostname = av_strdup(hostname); 728cabdff1aSopenharmony_ci if (!s->hostname || !s->user || (tok_pass && !s->password)) { 729cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 730cabdff1aSopenharmony_ci } 731cabdff1aSopenharmony_ci 732cabdff1aSopenharmony_ci if (s->server_control_port < 0 || s->server_control_port > 65535) 733cabdff1aSopenharmony_ci s->server_control_port = 21; 734cabdff1aSopenharmony_ci 735cabdff1aSopenharmony_ci if ((err = ftp_connect_control_connection(h)) < 0) 736cabdff1aSopenharmony_ci return err; 737cabdff1aSopenharmony_ci 738cabdff1aSopenharmony_ci if ((err = ftp_current_dir(s)) < 0) 739cabdff1aSopenharmony_ci return err; 740cabdff1aSopenharmony_ci 741cabdff1aSopenharmony_ci newpath = av_append_path_component(s->path, path); 742cabdff1aSopenharmony_ci if (!newpath) 743cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 744cabdff1aSopenharmony_ci av_free(s->path); 745cabdff1aSopenharmony_ci s->path = newpath; 746cabdff1aSopenharmony_ci 747cabdff1aSopenharmony_ci return 0; 748cabdff1aSopenharmony_ci} 749cabdff1aSopenharmony_ci 750cabdff1aSopenharmony_cistatic int ftp_open(URLContext *h, const char *url, int flags) 751cabdff1aSopenharmony_ci{ 752cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 753cabdff1aSopenharmony_ci int err; 754cabdff1aSopenharmony_ci 755cabdff1aSopenharmony_ci ff_dlog(h, "ftp protocol open\n"); 756cabdff1aSopenharmony_ci 757cabdff1aSopenharmony_ci if ((err = ftp_connect(h, url)) < 0) 758cabdff1aSopenharmony_ci goto fail; 759cabdff1aSopenharmony_ci 760cabdff1aSopenharmony_ci if (ftp_restart(s, 0) < 0) { 761cabdff1aSopenharmony_ci h->is_streamed = 1; 762cabdff1aSopenharmony_ci } else { 763cabdff1aSopenharmony_ci ftp_file_size(s); 764cabdff1aSopenharmony_ci if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE) 765cabdff1aSopenharmony_ci h->is_streamed = 1; 766cabdff1aSopenharmony_ci } 767cabdff1aSopenharmony_ci 768cabdff1aSopenharmony_ci return 0; 769cabdff1aSopenharmony_ci 770cabdff1aSopenharmony_ci fail: 771cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "FTP open failed\n"); 772cabdff1aSopenharmony_ci ftp_close(h); 773cabdff1aSopenharmony_ci return err; 774cabdff1aSopenharmony_ci} 775cabdff1aSopenharmony_ci 776cabdff1aSopenharmony_cistatic int64_t ftp_seek(URLContext *h, int64_t pos, int whence) 777cabdff1aSopenharmony_ci{ 778cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 779cabdff1aSopenharmony_ci int err; 780cabdff1aSopenharmony_ci int64_t new_pos; 781cabdff1aSopenharmony_ci 782cabdff1aSopenharmony_ci ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence); 783cabdff1aSopenharmony_ci 784cabdff1aSopenharmony_ci switch(whence) { 785cabdff1aSopenharmony_ci case AVSEEK_SIZE: 786cabdff1aSopenharmony_ci return s->filesize; 787cabdff1aSopenharmony_ci case SEEK_SET: 788cabdff1aSopenharmony_ci new_pos = pos; 789cabdff1aSopenharmony_ci break; 790cabdff1aSopenharmony_ci case SEEK_CUR: 791cabdff1aSopenharmony_ci new_pos = s->position + pos; 792cabdff1aSopenharmony_ci break; 793cabdff1aSopenharmony_ci case SEEK_END: 794cabdff1aSopenharmony_ci if (s->filesize < 0) 795cabdff1aSopenharmony_ci return AVERROR(EIO); 796cabdff1aSopenharmony_ci new_pos = s->filesize + pos; 797cabdff1aSopenharmony_ci break; 798cabdff1aSopenharmony_ci default: 799cabdff1aSopenharmony_ci return AVERROR(EINVAL); 800cabdff1aSopenharmony_ci } 801cabdff1aSopenharmony_ci 802cabdff1aSopenharmony_ci if (h->is_streamed) 803cabdff1aSopenharmony_ci return AVERROR(EIO); 804cabdff1aSopenharmony_ci 805cabdff1aSopenharmony_ci if (new_pos < 0) { 806cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n"); 807cabdff1aSopenharmony_ci return AVERROR(EINVAL); 808cabdff1aSopenharmony_ci } 809cabdff1aSopenharmony_ci 810cabdff1aSopenharmony_ci if (new_pos != s->position) { 811cabdff1aSopenharmony_ci if ((err = ftp_abort(h)) < 0) 812cabdff1aSopenharmony_ci return err; 813cabdff1aSopenharmony_ci s->position = new_pos; 814cabdff1aSopenharmony_ci } 815cabdff1aSopenharmony_ci return new_pos; 816cabdff1aSopenharmony_ci} 817cabdff1aSopenharmony_ci 818cabdff1aSopenharmony_cistatic int ftp_read(URLContext *h, unsigned char *buf, int size) 819cabdff1aSopenharmony_ci{ 820cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 821cabdff1aSopenharmony_ci int read, err, retry_done = 0; 822cabdff1aSopenharmony_ci 823cabdff1aSopenharmony_ci ff_dlog(h, "ftp protocol read %d bytes\n", size); 824cabdff1aSopenharmony_ci retry: 825cabdff1aSopenharmony_ci if (s->state == ENDOFFILE) 826cabdff1aSopenharmony_ci return AVERROR_EOF; 827cabdff1aSopenharmony_ci if (s->state == DISCONNECTED) { 828cabdff1aSopenharmony_ci if ((err = ftp_connect_data_connection(h)) < 0) 829cabdff1aSopenharmony_ci return err; 830cabdff1aSopenharmony_ci } 831cabdff1aSopenharmony_ci if (s->state == READY) { 832cabdff1aSopenharmony_ci if ((err = ftp_retrieve(s)) < 0) 833cabdff1aSopenharmony_ci return err; 834cabdff1aSopenharmony_ci } 835cabdff1aSopenharmony_ci if (s->conn_data && s->state == DOWNLOADING) { 836cabdff1aSopenharmony_ci read = ffurl_read(s->conn_data, buf, size); 837cabdff1aSopenharmony_ci if (read >= 0) { 838cabdff1aSopenharmony_ci s->position += read; 839cabdff1aSopenharmony_ci s->filesize = FFMAX(s->filesize, s->position); 840cabdff1aSopenharmony_ci } 841cabdff1aSopenharmony_ci if (read == AVERROR_EOF) { 842cabdff1aSopenharmony_ci static const int retr_codes[] = {226, 250, 425, 426, 451, 0}; 843cabdff1aSopenharmony_ci char *response = NULL; 844cabdff1aSopenharmony_ci err = ftp_status(s, &response, retr_codes); 845cabdff1aSopenharmony_ci if (err == 226) { 846cabdff1aSopenharmony_ci ftp_close_data_connection(s); 847cabdff1aSopenharmony_ci av_freep(&response); 848cabdff1aSopenharmony_ci s->state = ENDOFFILE; 849cabdff1aSopenharmony_ci return AVERROR_EOF; 850cabdff1aSopenharmony_ci } 851cabdff1aSopenharmony_ci /* 250 is not allowed, any other status means some kind of error */ 852cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "FTP transfer failed: %s\n", response ? response : (err < 0 ? av_err2str(err) : "?")); 853cabdff1aSopenharmony_ci av_freep(&response); 854cabdff1aSopenharmony_ci read = AVERROR(EIO); 855cabdff1aSopenharmony_ci } 856cabdff1aSopenharmony_ci if (read <= 0 && !h->is_streamed) { 857cabdff1aSopenharmony_ci /* Server closed connection. Probably due to inactivity */ 858cabdff1aSopenharmony_ci av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n"); 859cabdff1aSopenharmony_ci if ((err = ftp_abort(h)) < 0) 860cabdff1aSopenharmony_ci return err; 861cabdff1aSopenharmony_ci if (!retry_done) { 862cabdff1aSopenharmony_ci retry_done = 1; 863cabdff1aSopenharmony_ci goto retry; 864cabdff1aSopenharmony_ci } 865cabdff1aSopenharmony_ci } 866cabdff1aSopenharmony_ci return read; 867cabdff1aSopenharmony_ci } 868cabdff1aSopenharmony_ci 869cabdff1aSopenharmony_ci av_log(h, AV_LOG_DEBUG, "FTP read failed\n"); 870cabdff1aSopenharmony_ci return AVERROR(EIO); 871cabdff1aSopenharmony_ci} 872cabdff1aSopenharmony_ci 873cabdff1aSopenharmony_cistatic int ftp_write(URLContext *h, const unsigned char *buf, int size) 874cabdff1aSopenharmony_ci{ 875cabdff1aSopenharmony_ci int err; 876cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 877cabdff1aSopenharmony_ci int written; 878cabdff1aSopenharmony_ci 879cabdff1aSopenharmony_ci ff_dlog(h, "ftp protocol write %d bytes\n", size); 880cabdff1aSopenharmony_ci 881cabdff1aSopenharmony_ci if (s->state == DISCONNECTED) { 882cabdff1aSopenharmony_ci if ((err = ftp_connect_data_connection(h)) < 0) 883cabdff1aSopenharmony_ci return err; 884cabdff1aSopenharmony_ci } 885cabdff1aSopenharmony_ci if (s->state == READY) { 886cabdff1aSopenharmony_ci if ((err = ftp_store(s)) < 0) 887cabdff1aSopenharmony_ci return err; 888cabdff1aSopenharmony_ci } 889cabdff1aSopenharmony_ci if (s->conn_data && s->state == UPLOADING) { 890cabdff1aSopenharmony_ci written = ffurl_write(s->conn_data, buf, size); 891cabdff1aSopenharmony_ci if (written > 0) { 892cabdff1aSopenharmony_ci s->position += written; 893cabdff1aSopenharmony_ci s->filesize = FFMAX(s->filesize, s->position); 894cabdff1aSopenharmony_ci } 895cabdff1aSopenharmony_ci return written; 896cabdff1aSopenharmony_ci } 897cabdff1aSopenharmony_ci 898cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "FTP write failed\n"); 899cabdff1aSopenharmony_ci return AVERROR(EIO); 900cabdff1aSopenharmony_ci} 901cabdff1aSopenharmony_ci 902cabdff1aSopenharmony_cistatic int ftp_close(URLContext *h) 903cabdff1aSopenharmony_ci{ 904cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 905cabdff1aSopenharmony_ci 906cabdff1aSopenharmony_ci ff_dlog(h, "ftp protocol close\n"); 907cabdff1aSopenharmony_ci 908cabdff1aSopenharmony_ci ftp_close_both_connections(s); 909cabdff1aSopenharmony_ci av_freep(&s->user); 910cabdff1aSopenharmony_ci av_freep(&s->password); 911cabdff1aSopenharmony_ci av_freep(&s->hostname); 912cabdff1aSopenharmony_ci av_freep(&s->path); 913cabdff1aSopenharmony_ci av_freep(&s->features); 914cabdff1aSopenharmony_ci 915cabdff1aSopenharmony_ci return 0; 916cabdff1aSopenharmony_ci} 917cabdff1aSopenharmony_ci 918cabdff1aSopenharmony_cistatic int ftp_get_file_handle(URLContext *h) 919cabdff1aSopenharmony_ci{ 920cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 921cabdff1aSopenharmony_ci 922cabdff1aSopenharmony_ci ff_dlog(h, "ftp protocol get_file_handle\n"); 923cabdff1aSopenharmony_ci 924cabdff1aSopenharmony_ci if (s->conn_data) 925cabdff1aSopenharmony_ci return ffurl_get_file_handle(s->conn_data); 926cabdff1aSopenharmony_ci 927cabdff1aSopenharmony_ci return AVERROR(EIO); 928cabdff1aSopenharmony_ci} 929cabdff1aSopenharmony_ci 930cabdff1aSopenharmony_cistatic int ftp_shutdown(URLContext *h, int flags) 931cabdff1aSopenharmony_ci{ 932cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 933cabdff1aSopenharmony_ci 934cabdff1aSopenharmony_ci ff_dlog(h, "ftp protocol shutdown\n"); 935cabdff1aSopenharmony_ci 936cabdff1aSopenharmony_ci if (s->conn_data) 937cabdff1aSopenharmony_ci return ffurl_shutdown(s->conn_data, flags); 938cabdff1aSopenharmony_ci 939cabdff1aSopenharmony_ci return AVERROR(EIO); 940cabdff1aSopenharmony_ci} 941cabdff1aSopenharmony_ci 942cabdff1aSopenharmony_cistatic int ftp_open_dir(URLContext *h) 943cabdff1aSopenharmony_ci{ 944cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 945cabdff1aSopenharmony_ci int ret; 946cabdff1aSopenharmony_ci 947cabdff1aSopenharmony_ci if ((ret = ftp_connect(h, h->filename)) < 0) 948cabdff1aSopenharmony_ci goto fail; 949cabdff1aSopenharmony_ci if ((ret = ftp_set_dir(s)) < 0) 950cabdff1aSopenharmony_ci goto fail; 951cabdff1aSopenharmony_ci if ((ret = ftp_connect_data_connection(h)) < 0) 952cabdff1aSopenharmony_ci goto fail; 953cabdff1aSopenharmony_ci if ((ret = ftp_list(s)) < 0) 954cabdff1aSopenharmony_ci goto fail; 955cabdff1aSopenharmony_ci s->dir_buffer = av_malloc(DIR_BUFFER_SIZE); 956cabdff1aSopenharmony_ci if (!s->dir_buffer) { 957cabdff1aSopenharmony_ci ret = AVERROR(ENOMEM); 958cabdff1aSopenharmony_ci goto fail; 959cabdff1aSopenharmony_ci } 960cabdff1aSopenharmony_ci s->dir_buffer[0] = 0; 961cabdff1aSopenharmony_ci if (s->conn_data && s->state == LISTING_DIR) 962cabdff1aSopenharmony_ci return 0; 963cabdff1aSopenharmony_ci fail: 964cabdff1aSopenharmony_ci ffurl_closep(&s->conn_control); 965cabdff1aSopenharmony_ci ffurl_closep(&s->conn_data); 966cabdff1aSopenharmony_ci return ret; 967cabdff1aSopenharmony_ci} 968cabdff1aSopenharmony_ci 969cabdff1aSopenharmony_cistatic int64_t ftp_parse_date(const char *date) 970cabdff1aSopenharmony_ci{ 971cabdff1aSopenharmony_ci struct tm tv; 972cabdff1aSopenharmony_ci memset(&tv, 0, sizeof(struct tm)); 973cabdff1aSopenharmony_ci av_small_strptime(date, "%Y%m%d%H%M%S", &tv); 974cabdff1aSopenharmony_ci return INT64_C(1000000) * av_timegm(&tv); 975cabdff1aSopenharmony_ci} 976cabdff1aSopenharmony_ci 977cabdff1aSopenharmony_cistatic int ftp_parse_entry_nlst(char *line, AVIODirEntry *next) 978cabdff1aSopenharmony_ci{ 979cabdff1aSopenharmony_ci next->name = av_strdup(line); 980cabdff1aSopenharmony_ci return 0; 981cabdff1aSopenharmony_ci} 982cabdff1aSopenharmony_ci 983cabdff1aSopenharmony_cistatic int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next) 984cabdff1aSopenharmony_ci{ 985cabdff1aSopenharmony_ci char *fact, *value; 986cabdff1aSopenharmony_ci char *saveptr = NULL, *p = mlsd; 987cabdff1aSopenharmony_ci ff_dlog(NULL, "%s\n", mlsd); 988cabdff1aSopenharmony_ci while(fact = av_strtok(p, ";", &saveptr)) { 989cabdff1aSopenharmony_ci p = NULL; 990cabdff1aSopenharmony_ci if (fact[0] == ' ') { 991cabdff1aSopenharmony_ci next->name = av_strdup(&fact[1]); 992cabdff1aSopenharmony_ci continue; 993cabdff1aSopenharmony_ci } 994cabdff1aSopenharmony_ci fact = av_strtok(fact, "=", &value); 995cabdff1aSopenharmony_ci if (!fact) 996cabdff1aSopenharmony_ci continue; 997cabdff1aSopenharmony_ci if (!av_strcasecmp(fact, "type")) { 998cabdff1aSopenharmony_ci if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir")) 999cabdff1aSopenharmony_ci return 1; 1000cabdff1aSopenharmony_ci if (!av_strcasecmp(value, "dir")) 1001cabdff1aSopenharmony_ci next->type = AVIO_ENTRY_DIRECTORY; 1002cabdff1aSopenharmony_ci else if (!av_strcasecmp(value, "file")) 1003cabdff1aSopenharmony_ci next->type = AVIO_ENTRY_FILE; 1004cabdff1aSopenharmony_ci else if (!av_strcasecmp(value, "OS.unix=slink:")) 1005cabdff1aSopenharmony_ci next->type = AVIO_ENTRY_SYMBOLIC_LINK; 1006cabdff1aSopenharmony_ci } else if (!av_strcasecmp(fact, "modify")) { 1007cabdff1aSopenharmony_ci next->modification_timestamp = ftp_parse_date(value); 1008cabdff1aSopenharmony_ci } else if (!av_strcasecmp(fact, "UNIX.mode")) { 1009cabdff1aSopenharmony_ci next->filemode = strtoumax(value, NULL, 8); 1010cabdff1aSopenharmony_ci } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner")) 1011cabdff1aSopenharmony_ci next->user_id = strtoumax(value, NULL, 10); 1012cabdff1aSopenharmony_ci else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group")) 1013cabdff1aSopenharmony_ci next->group_id = strtoumax(value, NULL, 10); 1014cabdff1aSopenharmony_ci else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd")) 1015cabdff1aSopenharmony_ci next->size = strtoll(value, NULL, 10); 1016cabdff1aSopenharmony_ci } 1017cabdff1aSopenharmony_ci return 0; 1018cabdff1aSopenharmony_ci} 1019cabdff1aSopenharmony_ci 1020cabdff1aSopenharmony_ci/** 1021cabdff1aSopenharmony_ci * @return 0 on success, negative on error, positive on entry to discard. 1022cabdff1aSopenharmony_ci */ 1023cabdff1aSopenharmony_cistatic int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next) 1024cabdff1aSopenharmony_ci{ 1025cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 1026cabdff1aSopenharmony_ci 1027cabdff1aSopenharmony_ci switch (s->listing_method) { 1028cabdff1aSopenharmony_ci case MLSD: 1029cabdff1aSopenharmony_ci return ftp_parse_entry_mlsd(line, next); 1030cabdff1aSopenharmony_ci case NLST: 1031cabdff1aSopenharmony_ci return ftp_parse_entry_nlst(line, next); 1032cabdff1aSopenharmony_ci case UNKNOWN_METHOD: 1033cabdff1aSopenharmony_ci default: 1034cabdff1aSopenharmony_ci return -1; 1035cabdff1aSopenharmony_ci } 1036cabdff1aSopenharmony_ci} 1037cabdff1aSopenharmony_ci 1038cabdff1aSopenharmony_cistatic int ftp_read_dir(URLContext *h, AVIODirEntry **next) 1039cabdff1aSopenharmony_ci{ 1040cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 1041cabdff1aSopenharmony_ci char *start, *found; 1042cabdff1aSopenharmony_ci int ret, retried; 1043cabdff1aSopenharmony_ci 1044cabdff1aSopenharmony_ci do { 1045cabdff1aSopenharmony_ci retried = 0; 1046cabdff1aSopenharmony_ci start = s->dir_buffer + s->dir_buffer_offset; 1047cabdff1aSopenharmony_ci while (!(found = strstr(start, "\n"))) { 1048cabdff1aSopenharmony_ci if (retried) 1049cabdff1aSopenharmony_ci return AVERROR(EIO); 1050cabdff1aSopenharmony_ci s->dir_buffer_size -= s->dir_buffer_offset; 1051cabdff1aSopenharmony_ci s->dir_buffer_offset = 0; 1052cabdff1aSopenharmony_ci if (s->dir_buffer_size) 1053cabdff1aSopenharmony_ci memmove(s->dir_buffer, start, s->dir_buffer_size); 1054cabdff1aSopenharmony_ci ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1)); 1055cabdff1aSopenharmony_ci if (ret < 0) 1056cabdff1aSopenharmony_ci return ret; 1057cabdff1aSopenharmony_ci if (!ret) { 1058cabdff1aSopenharmony_ci *next = NULL; 1059cabdff1aSopenharmony_ci return 0; 1060cabdff1aSopenharmony_ci } 1061cabdff1aSopenharmony_ci s->dir_buffer_size += ret; 1062cabdff1aSopenharmony_ci s->dir_buffer[s->dir_buffer_size] = 0; 1063cabdff1aSopenharmony_ci start = s->dir_buffer; 1064cabdff1aSopenharmony_ci retried = 1; 1065cabdff1aSopenharmony_ci } 1066cabdff1aSopenharmony_ci s->dir_buffer_offset += (found + 1 - start); 1067cabdff1aSopenharmony_ci found[0] = 0; 1068cabdff1aSopenharmony_ci if (found > start && found[-1] == '\r') 1069cabdff1aSopenharmony_ci found[-1] = 0; 1070cabdff1aSopenharmony_ci 1071cabdff1aSopenharmony_ci *next = ff_alloc_dir_entry(); 1072cabdff1aSopenharmony_ci if (!*next) 1073cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 1074cabdff1aSopenharmony_ci (*next)->utf8 = s->utf8; 1075cabdff1aSopenharmony_ci ret = ftp_parse_entry(h, start, *next); 1076cabdff1aSopenharmony_ci if (ret) { 1077cabdff1aSopenharmony_ci avio_free_directory_entry(next); 1078cabdff1aSopenharmony_ci if (ret < 0) 1079cabdff1aSopenharmony_ci return ret; 1080cabdff1aSopenharmony_ci } 1081cabdff1aSopenharmony_ci } while (ret > 0); 1082cabdff1aSopenharmony_ci return 0; 1083cabdff1aSopenharmony_ci} 1084cabdff1aSopenharmony_ci 1085cabdff1aSopenharmony_cistatic int ftp_close_dir(URLContext *h) 1086cabdff1aSopenharmony_ci{ 1087cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 1088cabdff1aSopenharmony_ci av_freep(&s->dir_buffer); 1089cabdff1aSopenharmony_ci ffurl_closep(&s->conn_control); 1090cabdff1aSopenharmony_ci ffurl_closep(&s->conn_data); 1091cabdff1aSopenharmony_ci return 0; 1092cabdff1aSopenharmony_ci} 1093cabdff1aSopenharmony_ci 1094cabdff1aSopenharmony_cistatic int ftp_delete(URLContext *h) 1095cabdff1aSopenharmony_ci{ 1096cabdff1aSopenharmony_ci FTPContext *s = h->priv_data; 1097cabdff1aSopenharmony_ci char command[MAX_URL_SIZE]; 1098cabdff1aSopenharmony_ci static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0}; 1099cabdff1aSopenharmony_ci static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0}; 1100cabdff1aSopenharmony_ci int ret; 1101cabdff1aSopenharmony_ci 1102cabdff1aSopenharmony_ci if ((ret = ftp_connect(h, h->filename)) < 0) 1103cabdff1aSopenharmony_ci goto cleanup; 1104cabdff1aSopenharmony_ci 1105cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "DELE %s\r\n", s->path); 1106cabdff1aSopenharmony_ci if (ret >= sizeof(command)) { 1107cabdff1aSopenharmony_ci ret = AVERROR(ENOSYS); 1108cabdff1aSopenharmony_ci goto cleanup; 1109cabdff1aSopenharmony_ci } 1110cabdff1aSopenharmony_ci 1111cabdff1aSopenharmony_ci if (ftp_send_command(s, command, del_codes, NULL) == 250) { 1112cabdff1aSopenharmony_ci ret = 0; 1113cabdff1aSopenharmony_ci goto cleanup; 1114cabdff1aSopenharmony_ci } 1115cabdff1aSopenharmony_ci 1116cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "RMD %s\r\n", s->path); 1117cabdff1aSopenharmony_ci if (ret >= sizeof(command)) { 1118cabdff1aSopenharmony_ci ret = AVERROR(ENOSYS); 1119cabdff1aSopenharmony_ci goto cleanup; 1120cabdff1aSopenharmony_ci } 1121cabdff1aSopenharmony_ci 1122cabdff1aSopenharmony_ci if (ftp_send_command(s, command, rmd_codes, NULL) == 250) 1123cabdff1aSopenharmony_ci ret = 0; 1124cabdff1aSopenharmony_ci else 1125cabdff1aSopenharmony_ci ret = AVERROR(EIO); 1126cabdff1aSopenharmony_ci 1127cabdff1aSopenharmony_cicleanup: 1128cabdff1aSopenharmony_ci ftp_close(h); 1129cabdff1aSopenharmony_ci return ret; 1130cabdff1aSopenharmony_ci} 1131cabdff1aSopenharmony_ci 1132cabdff1aSopenharmony_cistatic int ftp_move(URLContext *h_src, URLContext *h_dst) 1133cabdff1aSopenharmony_ci{ 1134cabdff1aSopenharmony_ci FTPContext *s = h_src->priv_data; 1135cabdff1aSopenharmony_ci char command[MAX_URL_SIZE], path[MAX_URL_SIZE]; 1136cabdff1aSopenharmony_ci static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0}; 1137cabdff1aSopenharmony_ci static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0}; 1138cabdff1aSopenharmony_ci int ret; 1139cabdff1aSopenharmony_ci 1140cabdff1aSopenharmony_ci if ((ret = ftp_connect(h_src, h_src->filename)) < 0) 1141cabdff1aSopenharmony_ci goto cleanup; 1142cabdff1aSopenharmony_ci 1143cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "RNFR %s\r\n", s->path); 1144cabdff1aSopenharmony_ci if (ret >= sizeof(command)) { 1145cabdff1aSopenharmony_ci ret = AVERROR(ENOSYS); 1146cabdff1aSopenharmony_ci goto cleanup; 1147cabdff1aSopenharmony_ci } 1148cabdff1aSopenharmony_ci 1149cabdff1aSopenharmony_ci if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) { 1150cabdff1aSopenharmony_ci ret = AVERROR(EIO); 1151cabdff1aSopenharmony_ci goto cleanup; 1152cabdff1aSopenharmony_ci } 1153cabdff1aSopenharmony_ci 1154cabdff1aSopenharmony_ci av_url_split(0, 0, 0, 0, 0, 0, 0, 1155cabdff1aSopenharmony_ci path, sizeof(path), 1156cabdff1aSopenharmony_ci h_dst->filename); 1157cabdff1aSopenharmony_ci ret = snprintf(command, sizeof(command), "RNTO %s\r\n", path); 1158cabdff1aSopenharmony_ci if (ret >= sizeof(command)) { 1159cabdff1aSopenharmony_ci ret = AVERROR(ENOSYS); 1160cabdff1aSopenharmony_ci goto cleanup; 1161cabdff1aSopenharmony_ci } 1162cabdff1aSopenharmony_ci 1163cabdff1aSopenharmony_ci if (ftp_send_command(s, command, rnto_codes, NULL) == 250) 1164cabdff1aSopenharmony_ci ret = 0; 1165cabdff1aSopenharmony_ci else 1166cabdff1aSopenharmony_ci ret = AVERROR(EIO); 1167cabdff1aSopenharmony_ci 1168cabdff1aSopenharmony_cicleanup: 1169cabdff1aSopenharmony_ci ftp_close(h_src); 1170cabdff1aSopenharmony_ci return ret; 1171cabdff1aSopenharmony_ci} 1172cabdff1aSopenharmony_ci 1173cabdff1aSopenharmony_ciconst URLProtocol ff_ftp_protocol = { 1174cabdff1aSopenharmony_ci .name = "ftp", 1175cabdff1aSopenharmony_ci .url_open = ftp_open, 1176cabdff1aSopenharmony_ci .url_read = ftp_read, 1177cabdff1aSopenharmony_ci .url_write = ftp_write, 1178cabdff1aSopenharmony_ci .url_seek = ftp_seek, 1179cabdff1aSopenharmony_ci .url_close = ftp_close, 1180cabdff1aSopenharmony_ci .url_get_file_handle = ftp_get_file_handle, 1181cabdff1aSopenharmony_ci .url_shutdown = ftp_shutdown, 1182cabdff1aSopenharmony_ci .priv_data_size = sizeof(FTPContext), 1183cabdff1aSopenharmony_ci .priv_data_class = &ftp_context_class, 1184cabdff1aSopenharmony_ci .url_open_dir = ftp_open_dir, 1185cabdff1aSopenharmony_ci .url_read_dir = ftp_read_dir, 1186cabdff1aSopenharmony_ci .url_close_dir = ftp_close_dir, 1187cabdff1aSopenharmony_ci .url_delete = ftp_delete, 1188cabdff1aSopenharmony_ci .url_move = ftp_move, 1189cabdff1aSopenharmony_ci .flags = URL_PROTOCOL_FLAG_NETWORK, 1190cabdff1aSopenharmony_ci .default_whitelist = "tcp", 1191cabdff1aSopenharmony_ci}; 1192