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