1193 lines · 33.5 KB · C
RawDownload
/* * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t