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, linesize;
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  linesize = strlen(buf);
139  err = 0;
140  if (linesize >= 3) {
141  for (i = 0; i < 3; ++i) {
142  if (buf[i] < '0' || buf[i] > '9') {
143  err = 0;
144  break;
145  }
146  err *= 10;
147  err += buf[i] - '0';
148  }
149  }
150 
151  if (!code_found) {
152  if (err >= 500) {
153  code_found = 1;
154  result = err;
155  } else
156  for (i = 0; response_codes[i]; ++i) {
157  if (err == response_codes[i]) {
158  code_found = 1;
159  result = err;
160  break;
161  }
162  }
163  }
164  if (code_found) {
165  if (line)
166  av_bprintf(&line_buffer, "%s\r\n", buf);
167  if (linesize >= 4) {
168  if (!dash && buf[3] == '-')
169  dash = err;
170  else if (err == dash && buf[3] == ' ')
171  dash = 0;
172  }
173  }
174  }
175 
176  if (line)
177  av_bprint_finalize(&line_buffer, line);
178  return result;
179 }
180 
181 static int ftp_send_command(FTPContext *s, const char *command,
182  const int response_codes[], char **response)
183 {
184  int err;
185 
186  if (response)
187  *response = NULL;
188 
189  if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
190  return err;
191  if (!err)
192  return -1;
193 
194  /* return status */
195  if (response_codes) {
196  return ftp_status(s, response, response_codes);
197  }
198  return 0;
199 }
200 
202 {
203  ffurl_closep(&s->conn_data);
204  s->position = 0;
205  s->state = DISCONNECTED;
206 }
207 
209 {
212 }
213 
214 static int ftp_auth(FTPContext *s)
215 {
216  const char *user = NULL, *pass = NULL;
217  char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
218  int err;
219  static const int user_codes[] = {331, 230, 0};
220  static const int pass_codes[] = {230, 0};
221 
222  /* Authentication may be repeated, original string has to be saved */
223  av_strlcpy(credencials, s->credencials, sizeof(credencials));
224 
225  user = av_strtok(credencials, ":", &end);
226  pass = av_strtok(end, ":", &end);
227 
228  if (!user) {
229  user = "anonymous";
230  pass = s->anonymous_password ? s->anonymous_password : "nopassword";
231  }
232 
233  snprintf(buf, sizeof(buf), "USER %s\r\n", user);
234  err = ftp_send_command(s, buf, user_codes, NULL);
235  if (err == 331) {
236  if (pass) {
237  snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
238  err = ftp_send_command(s, buf, pass_codes, NULL);
239  } else
240  return AVERROR(EACCES);
241  }
242  if (err != 230)
243  return AVERROR(EACCES);
244 
245  return 0;
246 }
247 
249 {
250  char *res = NULL, *start = NULL, *end = NULL;
251  int i;
252  static const char d = '|';
253  static const char *command = "EPSV\r\n";
254  static const int epsv_codes[] = {229, 0};
255 
256  if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
257  goto fail;
258 
259  for (i = 0; res[i]; ++i) {
260  if (res[i] == '(') {
261  start = res + i + 1;
262  } else if (res[i] == ')') {
263  end = res + i;
264  break;
265  }
266  }
267  if (!start || !end)
268  goto fail;
269 
270  *end = '\0';
271  if (strlen(start) < 5)
272  goto fail;
273  if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
274  goto fail;
275  start += 3;
276  end[-1] = '\0';
277 
278  s->server_data_port = atoi(start);
279  av_dlog(s, "Server data port: %d\n", s->server_data_port);
280 
281  av_free(res);
282  return 0;
283 
284  fail:
285  av_free(res);
286  s->server_data_port = -1;
287  return AVERROR(ENOSYS);
288 }
289 
291 {
292  char *res = NULL, *start = NULL, *end = NULL;
293  int i;
294  static const char *command = "PASV\r\n";
295  static const int pasv_codes[] = {227, 0};
296 
297  if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
298  goto fail;
299 
300  for (i = 0; res[i]; ++i) {
301  if (res[i] == '(') {
302  start = res + i + 1;
303  } else if (res[i] == ')') {
304  end = res + i;
305  break;
306  }
307  }
308  if (!start || !end)
309  goto fail;
310 
311  *end = '\0';
312  /* skip ip */
313  if (!av_strtok(start, ",", &end)) goto fail;
314  if (!av_strtok(end, ",", &end)) goto fail;
315  if (!av_strtok(end, ",", &end)) goto fail;
316  if (!av_strtok(end, ",", &end)) goto fail;
317 
318  /* parse port number */
319  start = av_strtok(end, ",", &end);
320  if (!start) goto fail;
321  s->server_data_port = atoi(start) * 256;
322  start = av_strtok(end, ",", &end);
323  if (!start) goto fail;
324  s->server_data_port += atoi(start);
325  av_dlog(s, "Server data port: %d\n", s->server_data_port);
326 
327  av_free(res);
328  return 0;
329 
330  fail:
331  av_free(res);
332  s->server_data_port = -1;
333  return AVERROR(EIO);
334 }
335 
337 {
338  char *res = NULL, *start = NULL, *end = NULL;
339  int i;
340  static const char *command = "PWD\r\n";
341  static const int pwd_codes[] = {257, 0};
342 
343  if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
344  goto fail;
345 
346  for (i = 0; res[i]; ++i) {
347  if (res[i] == '"') {
348  if (!start) {
349  start = res + i + 1;
350  continue;
351  }
352  end = res + i;
353  break;
354  }
355  }
356 
357  if (!end)
358  goto fail;
359 
360  if (end > res && end[-1] == '/') {
361  end[-1] = '\0';
362  } else
363  *end = '\0';
364  av_strlcpy(s->path, start, sizeof(s->path));
365 
366  av_free(res);
367  return 0;
368 
369  fail:
370  av_free(res);
371  return AVERROR(EIO);
372 }
373 
375 {
377  char *res = NULL;
378  static const int size_codes[] = {213, 0};
379 
380  snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
381  if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
382  s->filesize = strtoll(&res[4], NULL, 10);
383  } else {
384  s->filesize = -1;
385  av_free(res);
386  return AVERROR(EIO);
387  }
388 
389  av_free(res);
390  return 0;
391 }
392 
394 {
396  static const int retr_codes[] = {150, 0};
397 
398  snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
399  if (ftp_send_command(s, command, retr_codes, NULL) != 150)
400  return AVERROR(EIO);
401 
402  s->state = DOWNLOADING;
403 
404  return 0;
405 }
406 
407 static int ftp_store(FTPContext *s)
408 {
410  static const int stor_codes[] = {150, 0};
411 
412  snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
413  if (ftp_send_command(s, command, stor_codes, NULL) != 150)
414  return AVERROR(EIO);
415 
416  s->state = UPLOADING;
417 
418  return 0;
419 }
420 
421 static int ftp_type(FTPContext *s)
422 {
423  static const char *command = "TYPE I\r\n";
424  static const int type_codes[] = {200, 0};
425 
426  if (ftp_send_command(s, command, type_codes, NULL) != 200)
427  return AVERROR(EIO);
428 
429  return 0;
430 }
431 
432 static int ftp_restart(FTPContext *s, int64_t pos)
433 {
435  static const int rest_codes[] = {350, 0};
436 
437  snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
438  if (ftp_send_command(s, command, rest_codes, NULL) != 350)
439  return AVERROR(EIO);
440 
441  return 0;
442 }
443 
445 {
446  static const char *feat_command = "FEAT\r\n";
447  static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
448  static const int feat_codes[] = {211, 0};
449  static const int opts_codes[] = {200, 451};
450  char *feat = NULL;
451 
452  if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
453  if (av_stristr(feat, "UTF8"))
454  ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
455  }
456  av_freep(&feat);
457 
458  return 0;
459 }
460 
462 {
463  char buf[CONTROL_BUFFER_SIZE], *response = NULL;
464  int err;
465  AVDictionary *opts = NULL;
466  FTPContext *s = h->priv_data;
467  static const int connect_codes[] = {220, 0};
468 
469  if (!s->conn_control) {
470  ff_url_join(buf, sizeof(buf), "tcp", NULL,
471  s->hostname, s->server_control_port, NULL);
472  if (s->rw_timeout != -1) {
473  av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
474  } /* if option is not given, don't pass it and let tcp use its own default */
476  &h->interrupt_callback, &opts);
477  av_dict_free(&opts);
478  if (err < 0) {
479  av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
480  return err;
481  }
482 
483  /* check if server is ready */
484  if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
485  av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
486  return AVERROR(EACCES);
487  }
488 
489  if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
490  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.");
491  }
492  av_free(response);
493 
494  if ((err = ftp_auth(s)) < 0) {
495  av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
496  return err;
497  }
498 
499  if ((err = ftp_type(s)) < 0) {
500  av_log(h, AV_LOG_ERROR, "Set content type failed\n");
501  return err;
502  }
503 
504  ftp_features(s);
505  }
506  return 0;
507 }
508 
510 {
511  int err;
512  char buf[CONTROL_BUFFER_SIZE];
513  AVDictionary *opts = NULL;
514  FTPContext *s = h->priv_data;
515 
516  if (!s->conn_data) {
517  /* Enter passive mode */
518  if (ftp_passive_mode_epsv(s) < 0) {
519  /* Use PASV as fallback */
520  if ((err = ftp_passive_mode(s)) < 0)
521  return err;
522  }
523  /* Open data connection */
524  ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
525  if (s->rw_timeout != -1) {
526  av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
527  } /* if option is not given, don't pass it and let tcp use its own default */
528  err = ffurl_open(&s->conn_data, buf, h->flags,
529  &h->interrupt_callback, &opts);
530  av_dict_free(&opts);
531  if (err < 0)
532  return err;
533 
534  if (s->position)
535  if ((err = ftp_restart(s, s->position)) < 0)
536  return err;
537  }
538  s->state = READY;
539  return 0;
540 }
541 
542 static int ftp_abort(URLContext *h)
543 {
544  static const char *command = "ABOR\r\n";
545  int err;
546  static const int abor_codes[] = {225, 226, 0};
547  FTPContext *s = h->priv_data;
548 
549  /* According to RCF 959:
550  "ABOR command tells the server to abort the previous FTP
551  service command and any associated transfer of data."
552 
553  There are FTP server implementations that don't response
554  to any commands during data transfer in passive mode (including ABOR).
555 
556  This implementation closes data connection by force.
557  */
558 
559  if (ftp_send_command(s, command, NULL, NULL) < 0) {
561  if ((err = ftp_connect_control_connection(h)) < 0) {
562  av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
563  return err;
564  }
565  } else {
567  if (ftp_status(s, NULL, abor_codes) < 225) {
568  /* wu-ftpd also closes control connection after data connection closing */
570  if ((err = ftp_connect_control_connection(h)) < 0) {
571  av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
572  return err;
573  }
574  }
575  }
576 
577  return 0;
578 }
579 
580 static int ftp_open(URLContext *h, const char *url, int flags)
581 {
582  char proto[10], path[MAX_URL_SIZE];
583  int err;
584  FTPContext *s = h->priv_data;
585 
586  av_dlog(h, "ftp protocol open\n");
587 
588  s->state = DISCONNECTED;
589  s->filesize = -1;
590  s->position = 0;
591 
592  av_url_split(proto, sizeof(proto),
593  s->credencials, sizeof(s->credencials),
594  s->hostname, sizeof(s->hostname),
596  path, sizeof(path),
597  url);
598 
599  if (s->server_control_port < 0 || s->server_control_port > 65535)
600  s->server_control_port = 21;
601 
602  if ((err = ftp_connect_control_connection(h)) < 0)
603  goto fail;
604 
605  if ((err = ftp_current_dir(s)) < 0)
606  goto fail;
607  av_strlcat(s->path, path, sizeof(s->path));
608 
609  if (ftp_restart(s, 0) < 0) {
610  h->is_streamed = 1;
611  } else {
612  if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
613  h->is_streamed = 1;
614  if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
615  h->is_streamed = 1;
616  }
617 
618  return 0;
619 
620  fail:
621  av_log(h, AV_LOG_ERROR, "FTP open failed\n");
623  ffurl_closep(&s->conn_data);
624  return err;
625 }
626 
627 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
628 {
629  FTPContext *s = h->priv_data;
630  int err;
631  int64_t new_pos, fake_pos;
632 
633  av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
634 
635  switch(whence) {
636  case AVSEEK_SIZE:
637  return s->filesize;
638  case SEEK_SET:
639  new_pos = pos;
640  break;
641  case SEEK_CUR:
642  new_pos = s->position + pos;
643  break;
644  case SEEK_END:
645  if (s->filesize < 0)
646  return AVERROR(EIO);
647  new_pos = s->filesize + pos;
648  break;
649  default:
650  return AVERROR(EINVAL);
651  }
652 
653  if (h->is_streamed)
654  return AVERROR(EIO);
655 
656  if (new_pos < 0) {
657  av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
658  return AVERROR(EINVAL);
659  }
660 
661  fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
662  if (fake_pos != s->position) {
663  if ((err = ftp_abort(h)) < 0)
664  return err;
665  s->position = fake_pos;
666  }
667  return new_pos;
668 }
669 
670 static int ftp_read(URLContext *h, unsigned char *buf, int size)
671 {
672  FTPContext *s = h->priv_data;
673  int read, err, retry_done = 0;
674 
675  av_dlog(h, "ftp protocol read %d bytes\n", size);
676  retry:
677  if (s->state == DISCONNECTED) {
678  /* optimization */
679  if (s->position >= s->filesize)
680  return 0;
681  if ((err = ftp_connect_data_connection(h)) < 0)
682  return err;
683  }
684  if (s->state == READY) {
685  if (s->position >= s->filesize)
686  return 0;
687  if ((err = ftp_retrieve(s)) < 0)
688  return err;
689  }
690  if (s->conn_data && s->state == DOWNLOADING) {
691  read = ffurl_read(s->conn_data, buf, size);
692  if (read >= 0) {
693  s->position += read;
694  if (s->position >= s->filesize) {
695  /* server will terminate, but keep current position to avoid madness */
696  /* save position to restart from it */
697  int64_t pos = s->position;
698  if (ftp_abort(h) < 0) {
699  s->position = pos;
700  return AVERROR(EIO);
701  }
702  s->position = pos;
703  }
704  }
705  if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
706  /* Server closed connection. Probably due to inactivity */
707  int64_t pos = s->position;
708  av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
709  if ((err = ftp_abort(h)) < 0)
710  return err;
711  if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
712  av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
713  return err;
714  }
715  if (!retry_done) {
716  retry_done = 1;
717  goto retry;
718  }
719  }
720  return read;
721  }
722 
723  av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
724  return AVERROR(EIO);
725 }
726 
727 static int ftp_write(URLContext *h, const unsigned char *buf, int size)
728 {
729  int err;
730  FTPContext *s = h->priv_data;
731  int written;
732 
733  av_dlog(h, "ftp protocol write %d bytes\n", size);
734 
735  if (s->state == DISCONNECTED) {
736  if ((err = ftp_connect_data_connection(h)) < 0)
737  return err;
738  }
739  if (s->state == READY) {
740  if ((err = ftp_store(s)) < 0)
741  return err;
742  }
743  if (s->conn_data && s->state == UPLOADING) {
744  written = ffurl_write(s->conn_data, buf, size);
745  if (written > 0) {
746  s->position += written;
747  s->filesize = FFMAX(s->filesize, s->position);
748  }
749  return written;
750  }
751 
752  av_log(h, AV_LOG_ERROR, "FTP write failed\n");
753  return AVERROR(EIO);
754 }
755 
756 static int ftp_close(URLContext *h)
757 {
758  av_dlog(h, "ftp protocol close\n");
759 
761 
762  return 0;
763 }
764 
766 {
767  FTPContext *s = h->priv_data;
768 
769  av_dlog(h, "ftp protocol get_file_handle\n");
770 
771  if (s->conn_data)
772  return ffurl_get_file_handle(s->conn_data);
773 
774  return AVERROR(EIO);
775 }
776 
777 static int ftp_shutdown(URLContext *h, int flags)
778 {
779  FTPContext *s = h->priv_data;
780 
781  av_dlog(h, "ftp protocol shutdown\n");
782 
783  if (s->conn_data)
784  return ffurl_shutdown(s->conn_data, flags);
785 
786  return AVERROR(EIO);
787 }
788 
790  .name = "ftp",
791  .url_open = ftp_open,
792  .url_read = ftp_read,
793  .url_write = ftp_write,
794  .url_seek = ftp_seek,
795  .url_close = ftp_close,
796  .url_get_file_handle = ftp_get_file_handle,
797  .url_shutdown = ftp_shutdown,
798  .priv_data_size = sizeof(FTPContext),
799  .priv_data_class = &ftp_context_class,
801 };