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