Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

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