FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ftp.c
Go to the documentation of this file.
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 "libavutil/avstring.h"
22 #include "avformat.h"
23 #include "internal.h"
24 #include "url.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/bprint.h"
27 
28 #define CONTROL_BUFFER_SIZE 1024
29 #define CREDENTIALS_BUFFER_SIZE 128
30 
31 typedef enum {
37 } FTPState;
38 
39 typedef struct {
40  const AVClass *class;
41  URLContext *conn_control; /**< Control connection */
42  URLContext *conn_data; /**< Data connection, NULL when not connected */
43  uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
44  uint8_t *control_buf_ptr, *control_buf_end;
45  int server_data_port; /**< Data connection port opened by server, -1 on error. */
46  int server_control_port; /**< Control connection port, default is 21 */
47  char hostname[512]; /**< Server address. */
48  char credencials[CREDENTIALS_BUFFER_SIZE]; /**< Authentication data */
49  char path[MAX_URL_SIZE]; /**< Path to resource on server. */
50  int64_t filesize; /**< Size of file on server, -1 on error. */
51  int64_t position; /**< Current position, calculated. */
52  int rw_timeout; /**< Network timeout. */
53  const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
54  int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
55  FTPState state; /**< State of data connection */
56 } FTPContext;
57 
58 #define OFFSET(x) offsetof(FTPContext, x)
59 #define D AV_OPT_FLAG_DECODING_PARAM
60 #define E AV_OPT_FLAG_ENCODING_PARAM
61 static const AVOption options[] = {
62  {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
63  {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
64  {"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 },
65  {NULL}
66 };
67 
68 static const AVClass ftp_context_class = {
69  .class_name = "ftp",
70  .item_name = av_default_item_name,
71  .option = options,
72  .version = LIBAVUTIL_VERSION_INT,
73 };
74 
75 static int ftp_getc(FTPContext *s)
76 {
77  int len;
78  if (s->control_buf_ptr >= s->control_buf_end) {
80  if (len < 0) {
81  return len;
82  } else if (!len) {
83  return -1;
84  } else {
87  }
88  }
89  return *s->control_buf_ptr++;
90 }
91 
92 static int ftp_get_line(FTPContext *s, char *line, int line_size)
93 {
94  int ch;
95  char *q = line;
96 
97  for (;;) {
98  ch = ftp_getc(s);
99  if (ch < 0) {
100  return ch;
101  }
102  if (ch == '\n') {
103  /* process line */
104  if (q > line && q[-1] == '\r')
105  q--;
106  *q = '\0';
107  return 0;
108  } else {
109  if ((q - line) < line_size - 1)
110  *q++ = ch;
111  }
112  }
113 }
114 
115 /*
116  * This routine returns ftp server response code.
117  * Server may send more than one response for a certain command.
118  * First expected code is returned.
119  */
120 static int ftp_status(FTPContext *s, char **line, const int response_codes[])
121 {
122  int err, i, dash = 0, result = 0, code_found = 0;
123  char buf[CONTROL_BUFFER_SIZE];
124  AVBPrint line_buffer;
125 
126  if (line)
127  av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
128 
129  while (!code_found || dash) {
130  if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
131  if (line)
132  av_bprint_finalize(&line_buffer, NULL);
133  return err;
134  }
135 
136  av_log(s, AV_LOG_DEBUG, "%s\n", buf);
137 
138  if (strlen(buf) < 4)
139  continue;
140 
141  err = 0;
142  for (i = 0; i < 3; ++i) {
143  if (buf[i] < '0' || buf[i] > '9')
144  continue;
145  err *= 10;
146  err += buf[i] - '0';
147  }
148  dash = !!(buf[3] == '-');
149 
150  for (i = 0; response_codes[i]; ++i) {
151  if (err == response_codes[i]) {
152  if (line)
153  av_bprintf(&line_buffer, "%s", buf);
154  code_found = 1;
155  result = err;
156  break;
157  }
158  }
159  }
160 
161  if (line)
162  av_bprint_finalize(&line_buffer, line);
163  return result;
164 }
165 
166 static int ftp_send_command(FTPContext *s, const char *command,
167  const int response_codes[], char **response)
168 {
169  int err;
170 
171  if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
172  return err;
173  if (!err)
174  return -1;
175 
176  /* return status */
177  if (response_codes) {
178  return ftp_status(s, response, response_codes);
179  }
180  return 0;
181 }
182 
184 {
185  ffurl_closep(&s->conn_data);
186  s->position = 0;
187  s->state = DISCONNECTED;
188 }
189 
191 {
194 }
195 
196 static int ftp_auth(FTPContext *s)
197 {
198  const char *user = NULL, *pass = NULL;
199  char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
200  int err;
201  static const int user_codes[] = {331, 230, 500, 530, 0}; /* 500, 530 are incorrect codes */
202  static const int pass_codes[] = {230, 503, 530, 0}; /* 503, 530 are incorrect codes */
203 
204  /* Authentication may be repeated, original string has to be saved */
205  av_strlcpy(credencials, s->credencials, sizeof(credencials));
206 
207  user = av_strtok(credencials, ":", &end);
208  pass = av_strtok(end, ":", &end);
209 
210  if (!user) {
211  user = "anonymous";
212  pass = s->anonymous_password ? s->anonymous_password : "nopassword";
213  }
214 
215  snprintf(buf, sizeof(buf), "USER %s\r\n", user);
216  err = ftp_send_command(s, buf, user_codes, NULL);
217  if (err == 331) {
218  if (pass) {
219  snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
220  err = ftp_send_command(s, buf, pass_codes, NULL);
221  } else
222  return AVERROR(EACCES);
223  }
224  if (err != 230)
225  return AVERROR(EACCES);
226 
227  return 0;
228 }
229 
231 {
232  char *res = NULL, *start = NULL, *end = NULL;
233  int i;
234  static const char *command = "PASV\r\n";
235  static const int pasv_codes[] = {227, 501, 0}; /* 501 is incorrect code */
236 
237  if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
238  goto fail;
239 
240  for (i = 0; res[i]; ++i) {
241  if (res[i] == '(') {
242  start = res + i + 1;
243  } else if (res[i] == ')') {
244  end = res + i;
245  break;
246  }
247  }
248  if (!start || !end)
249  goto fail;
250 
251  *end = '\0';
252  /* skip ip */
253  if (!av_strtok(start, ",", &end)) goto fail;
254  if (!av_strtok(end, ",", &end)) goto fail;
255  if (!av_strtok(end, ",", &end)) goto fail;
256  if (!av_strtok(end, ",", &end)) goto fail;
257 
258  /* parse port number */
259  start = av_strtok(end, ",", &end);
260  if (!start) goto fail;
261  s->server_data_port = atoi(start) * 256;
262  start = av_strtok(end, ",", &end);
263  if (!start) goto fail;
264  s->server_data_port += atoi(start);
265  av_dlog(s, "Server data port: %d\n", s->server_data_port);
266 
267  av_free(res);
268  return 0;
269 
270  fail:
271  av_free(res);
272  s->server_data_port = -1;
273  av_log(s, AV_LOG_ERROR, "Set passive mode failed\n"
274  "Your FTP server may use IPv6 which is not supported yet.\n");
275  return AVERROR(EIO);
276 }
277 
279 {
280  char *res = NULL, *start = NULL, *end = NULL;
281  int i;
282  static const char *command = "PWD\r\n";
283  static const int pwd_codes[] = {257, 0};
284 
285  if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
286  goto fail;
287 
288  for (i = 0; res[i]; ++i) {
289  if (res[i] == '"') {
290  if (!start) {
291  start = res + i + 1;
292  continue;
293  }
294  end = res + i;
295  break;
296  }
297  }
298 
299  if (!end)
300  goto fail;
301 
302  if (end > res && end[-1] == '/') {
303  end[-1] = '\0';
304  } else
305  *end = '\0';
306  av_strlcpy(s->path, start, sizeof(s->path));
307 
308  av_free(res);
309  return 0;
310 
311  fail:
312  av_free(res);
313  return AVERROR(EIO);
314 }
315 
317 {
319  char *res = NULL;
320  static const int size_codes[] = {213, 501, 550, 0}; /* 501, 550 are incorrect codes */
321 
322  snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
323  if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
324  s->filesize = strtoll(&res[4], NULL, 10);
325  } else {
326  s->filesize = -1;
327  av_free(res);
328  return AVERROR(EIO);
329  }
330 
331  av_free(res);
332  return 0;
333 }
334 
336 {
338  static const int retr_codes[] = {150, 550, 554, 0}; /* 550, 554 are incorrect codes */
339 
340  snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
341  if (ftp_send_command(s, command, retr_codes, NULL) != 150)
342  return AVERROR(EIO);
343 
344  s->state = DOWNLOADING;
345 
346  return 0;
347 }
348 
349 static int ftp_store(FTPContext *s)
350 {
352  static const int stor_codes[] = {150, 0};
353 
354  snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
355  if (ftp_send_command(s, command, stor_codes, NULL) != 150)
356  return AVERROR(EIO);
357 
358  s->state = UPLOADING;
359 
360  return 0;
361 }
362 
363 static int ftp_type(FTPContext *s)
364 {
365  static const char *command = "TYPE I\r\n";
366  static const int type_codes[] = {200, 500, 504, 0}; /* 500, 504 are incorrect codes */
367 
368  if (ftp_send_command(s, command, type_codes, NULL) != 200)
369  return AVERROR(EIO);
370 
371  return 0;
372 }
373 
374 static int ftp_restart(FTPContext *s, int64_t pos)
375 {
377  static const int rest_codes[] = {350, 500, 501, 0}; /* 500, 501 are incorrect codes */
378 
379  snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
380  if (ftp_send_command(s, command, rest_codes, NULL) != 350)
381  return AVERROR(EIO);
382 
383  return 0;
384 }
385 
387 {
388  char buf[CONTROL_BUFFER_SIZE], opts_format[20], *response = NULL;
389  int err;
390  AVDictionary *opts = NULL;
391  FTPContext *s = h->priv_data;
392  static const int connect_codes[] = {220, 0};
393 
394  if (!s->conn_control) {
395  ff_url_join(buf, sizeof(buf), "tcp", NULL,
396  s->hostname, s->server_control_port, NULL);
397  if (s->rw_timeout != -1) {
398  snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
399  av_dict_set(&opts, "timeout", opts_format, 0);
400  } /* if option is not given, don't pass it and let tcp use its own default */
402  &h->interrupt_callback, &opts);
403  av_dict_free(&opts);
404  if (err < 0) {
405  av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
406  return err;
407  }
408 
409  /* check if server is ready */
410  if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
411  av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
412  return AVERROR(EACCES);
413  }
414 
415  if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
416  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.");
417  }
418  av_free(response);
419 
420  if ((err = ftp_auth(s)) < 0) {
421  av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
422  return err;
423  }
424 
425  if ((err = ftp_type(s)) < 0) {
426  av_log(h, AV_LOG_ERROR, "Set content type failed\n");
427  return err;
428  }
429  }
430  return 0;
431 }
432 
434 {
435  int err;
436  char buf[CONTROL_BUFFER_SIZE], opts_format[20];
437  AVDictionary *opts = NULL;
438  FTPContext *s = h->priv_data;
439 
440  if (!s->conn_data) {
441  /* Enter passive mode */
442  if ((err = ftp_passive_mode(s)) < 0)
443  return err;
444  /* Open data connection */
445  ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
446  if (s->rw_timeout != -1) {
447  snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout);
448  av_dict_set(&opts, "timeout", opts_format, 0);
449  } /* if option is not given, don't pass it and let tcp use its own default */
450  err = ffurl_open(&s->conn_data, buf, h->flags,
451  &h->interrupt_callback, &opts);
452  av_dict_free(&opts);
453  if (err < 0)
454  return err;
455 
456  if (s->position)
457  if ((err = ftp_restart(s, s->position)) < 0)
458  return err;
459  }
460  s->state = READY;
461  return 0;
462 }
463 
464 static int ftp_abort(URLContext *h)
465 {
466  static const char *command = "ABOR\r\n";
467  int err;
468  static const int abor_codes[] = {225, 226, 0};
469  FTPContext *s = h->priv_data;
470 
471  /* According to RCF 959:
472  "ABOR command tells the server to abort the previous FTP
473  service command and any associated transfer of data."
474 
475  There are FTP server implementations that don't response
476  to any commands during data transfer in passive mode (including ABOR).
477 
478  This implementation closes data connection by force.
479  */
480 
481  if (ftp_send_command(s, command, NULL, NULL) < 0) {
483  if ((err = ftp_connect_control_connection(h)) < 0) {
484  av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
485  return err;
486  }
487  } else {
489  if (ftp_status(s, NULL, abor_codes) < 225) {
490  /* wu-ftpd also closes control connection after data connection closing */
492  if ((err = ftp_connect_control_connection(h)) < 0) {
493  av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
494  return err;
495  }
496  }
497  }
498 
499  return 0;
500 }
501 
502 static int ftp_open(URLContext *h, const char *url, int flags)
503 {
504  char proto[10], path[MAX_URL_SIZE];
505  int err;
506  FTPContext *s = h->priv_data;
507 
508  av_dlog(h, "ftp protocol open\n");
509 
510  s->state = DISCONNECTED;
511  s->filesize = -1;
512  s->position = 0;
513 
514  av_url_split(proto, sizeof(proto),
515  s->credencials, sizeof(s->credencials),
516  s->hostname, sizeof(s->hostname),
518  path, sizeof(path),
519  url);
520 
521  if (s->server_control_port < 0 || s->server_control_port > 65535)
522  s->server_control_port = 21;
523 
524  if ((err = ftp_connect_control_connection(h)) < 0)
525  goto fail;
526 
527  if ((err = ftp_current_dir(s)) < 0)
528  goto fail;
529  av_strlcat(s->path, path, sizeof(s->path));
530 
531  if (ftp_restart(s, 0) < 0) {
532  h->is_streamed = 1;
533  } else {
534  if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
535  h->is_streamed = 1;
536  if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
537  h->is_streamed = 1;
538  }
539 
540  return 0;
541 
542  fail:
543  av_log(h, AV_LOG_ERROR, "FTP open failed\n");
545  ffurl_closep(&s->conn_data);
546  return err;
547 }
548 
549 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
550 {
551  FTPContext *s = h->priv_data;
552  int err;
553  int64_t new_pos, fake_pos;
554 
555  av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
556 
557  switch(whence) {
558  case AVSEEK_SIZE:
559  return s->filesize;
560  case SEEK_SET:
561  new_pos = pos;
562  break;
563  case SEEK_CUR:
564  new_pos = s->position + pos;
565  break;
566  case SEEK_END:
567  if (s->filesize < 0)
568  return AVERROR(EIO);
569  new_pos = s->filesize + pos;
570  break;
571  default:
572  return AVERROR(EINVAL);
573  }
574 
575  if (h->is_streamed)
576  return AVERROR(EIO);
577 
578  /* XXX: Simulate behaviour of lseek in file protocol, which could be treated as a reference */
579  new_pos = FFMAX(0, new_pos);
580  fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
581 
582  if (fake_pos != s->position) {
583  if ((err = ftp_abort(h)) < 0)
584  return err;
585  s->position = fake_pos;
586  }
587  return new_pos;
588 }
589 
590 static int ftp_read(URLContext *h, unsigned char *buf, int size)
591 {
592  FTPContext *s = h->priv_data;
593  int read, err, retry_done = 0;
594 
595  av_dlog(h, "ftp protocol read %d bytes\n", size);
596  retry:
597  if (s->state == DISCONNECTED) {
598  /* optimization */
599  if (s->position >= s->filesize)
600  return 0;
601  if ((err = ftp_connect_data_connection(h)) < 0)
602  return err;
603  }
604  if (s->state == READY) {
605  if (s->position >= s->filesize)
606  return 0;
607  if ((err = ftp_retrieve(s)) < 0)
608  return err;
609  }
610  if (s->conn_data && s->state == DOWNLOADING) {
611  read = ffurl_read(s->conn_data, buf, size);
612  if (read >= 0) {
613  s->position += read;
614  if (s->position >= s->filesize) {
615  /* server will terminate, but keep current position to avoid madness */
616  /* save position to restart from it */
617  int64_t pos = s->position;
618  if (ftp_abort(h) < 0) {
619  s->position = pos;
620  return AVERROR(EIO);
621  }
622  s->position = pos;
623  }
624  }
625  if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
626  /* Server closed connection. Probably due to inactivity */
627  int64_t pos = s->position;
628  av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
629  if ((err = ftp_abort(h)) < 0)
630  return err;
631  if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
632  av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
633  return err;
634  }
635  if (!retry_done) {
636  retry_done = 1;
637  goto retry;
638  }
639  }
640  return read;
641  }
642 
643  av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
644  return AVERROR(EIO);
645 }
646 
647 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
648 {
649  int err;
650  FTPContext *s = h->priv_data;
651  int written;
652 
653  av_dlog(h, "ftp protocol write %d bytes\n", size);
654 
655  if (s->state == DISCONNECTED) {
656  if ((err = ftp_connect_data_connection(h)) < 0)
657  return err;
658  }
659  if (s->state == READY) {
660  if ((err = ftp_store(s)) < 0)
661  return err;
662  }
663  if (s->conn_data && s->state == UPLOADING) {
664  written = ffurl_write(s->conn_data, buf, size);
665  if (written > 0) {
666  s->position += written;
667  s->filesize = FFMAX(s->filesize, s->position);
668  }
669  return written;
670  }
671 
672  av_log(h, AV_LOG_ERROR, "FTP write failed\n");
673  return AVERROR(EIO);
674 }
675 
676 static int ftp_close(URLContext *h)
677 {
678  av_dlog(h, "ftp protocol close\n");
679 
681 
682  return 0;
683 }
684 
686 {
687  FTPContext *s = h->priv_data;
688 
689  av_dlog(h, "ftp protocol get_file_handle\n");
690 
691  if (s->conn_data)
692  return ffurl_get_file_handle(s->conn_data);
693 
694  return AVERROR(EIO);
695 }
696 
697 static int ftp_shutdown(URLContext *h, int flags)
698 {
699  FTPContext *s = h->priv_data;
700 
701  av_dlog(h, "ftp protocol shutdown\n");
702 
703  if (s->conn_data)
704  return ffurl_shutdown(s->conn_data, flags);
705 
706  return AVERROR(EIO);
707 }
708 
710  .name = "ftp",
711  .url_open = ftp_open,
712  .url_read = ftp_read,
713  .url_write = ftp_write,
714  .url_seek = ftp_seek,
715  .url_close = ftp_close,
716  .url_get_file_handle = ftp_get_file_handle,
717  .url_shutdown = ftp_shutdown,
718  .priv_data_size = sizeof(FTPContext),
719  .priv_data_class = &ftp_context_class,
721 };