FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
libssh.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 <fcntl.h>
22 #include <libssh/sftp.h>
23 #include "libavutil/avstring.h"
24 #include "libavutil/opt.h"
25 #include "avformat.h"
26 #include "internal.h"
27 #include "url.h"
28 
29 typedef struct {
30  const AVClass *class;
31  ssh_session session;
32  sftp_session sftp;
33  sftp_file file;
34  int64_t filesize;
36  int trunc;
38 
39 static int libssh_close(URLContext *h)
40 {
42  if (s->file)
43  sftp_close(s->file);
44  if (s->sftp)
45  sftp_free(s->sftp);
46  if (s->session) {
47  ssh_disconnect(s->session);
48  ssh_free(s->session);
49  }
50  return 0;
51 }
52 
53 static int libssh_open(URLContext *h, const char *url, int flags)
54 {
55  static const int verbosity = SSH_LOG_NOLOG;
57  char proto[10], path[MAX_URL_SIZE], hostname[1024], credencials[1024];
58  int port = 22, access, ret;
59  long timeout = s->rw_timeout * 1000;
60  const char *user = NULL, *pass = NULL;
61  char *end = NULL;
62  sftp_attributes stat;
63 
64  av_url_split(proto, sizeof(proto),
65  credencials, sizeof(credencials),
66  hostname, sizeof(hostname),
67  &port,
68  path, sizeof(path),
69  url);
70 
71  if (port <= 0 || port > 65535)
72  port = 22;
73 
74  if (!(s->session = ssh_new())) {
75  ret = AVERROR(ENOMEM);
76  goto fail;
77  }
78  user = av_strtok(credencials, ":", &end);
79  pass = av_strtok(end, ":", &end);
80  ssh_options_set(s->session, SSH_OPTIONS_HOST, hostname);
81  ssh_options_set(s->session, SSH_OPTIONS_PORT, &port);
82  ssh_options_set(s->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
83  if (timeout > 0)
84  ssh_options_set(s->session, SSH_OPTIONS_TIMEOUT_USEC, &timeout);
85  if (user)
86  ssh_options_set(s->session, SSH_OPTIONS_USER, user);
87 
88  if (ssh_connect(s->session) != SSH_OK) {
89  av_log(h, AV_LOG_ERROR, "Connection failed. %s\n", ssh_get_error(s->session));
90  ret = AVERROR(EIO);
91  goto fail;
92  }
93 
94  if (pass && ssh_userauth_password(s->session, NULL, pass) != SSH_AUTH_SUCCESS) {
95  av_log(h, AV_LOG_ERROR, "Error authenticating with password: %s\n", ssh_get_error(s->session));
96  ret = AVERROR(EACCES);
97  goto fail;
98  }
99 
100  if (!(s->sftp = sftp_new(s->session))) {
101  av_log(h, AV_LOG_ERROR, "SFTP session creation failed: %s\n", ssh_get_error(s->session));
102  ret = AVERROR(ENOMEM);
103  goto fail;
104  }
105 
106  if (sftp_init(s->sftp) != SSH_OK) {
107  av_log(h, AV_LOG_ERROR, "Error initializing sftp session: %s\n", ssh_get_error(s->session));
108  ret = AVERROR(EIO);
109  goto fail;
110  }
111 
112  if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) {
113  access = O_CREAT | O_RDWR;
114  if (s->trunc)
115  access |= O_TRUNC;
116  } else if (flags & AVIO_FLAG_WRITE) {
117  access = O_CREAT | O_WRONLY;
118  if (s->trunc)
119  access |= O_TRUNC;
120  } else {
121  access = O_RDONLY;
122  }
123 
124  if (!(s->file = sftp_open(s->sftp, path, access, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH))) {
125  av_log(h, AV_LOG_ERROR, "Error opening sftp file: %s\n", ssh_get_error(s->session));
126  ret = AVERROR(EIO);
127  goto fail;
128  }
129 
130  if (!(stat = sftp_fstat(s->file))) {
131  av_log(h, AV_LOG_WARNING, "Cannot stat remote file %s.\n", path);
132  s->filesize = -1;
133  } else {
134  s->filesize = stat->size;
135  sftp_attributes_free(stat);
136  }
137 
138  return 0;
139 
140  fail:
141  libssh_close(h);
142  return ret;
143 }
144 
145 static int64_t libssh_seek(URLContext *h, int64_t pos, int whence)
146 {
147  LIBSSHContext *s = h->priv_data;
148  int64_t newpos;
149 
150  if (s->filesize == -1 && (whence == AVSEEK_SIZE || whence == SEEK_END)) {
151  av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
152  return AVERROR(EIO);
153  }
154 
155  switch(whence) {
156  case AVSEEK_SIZE:
157  return s->filesize;
158  case SEEK_SET:
159  newpos = pos;
160  break;
161  case SEEK_CUR:
162  newpos = sftp_tell64(s->file) + pos;
163  break;
164  case SEEK_END:
165  newpos = s->filesize + pos;
166  break;
167  default:
168  return AVERROR(EINVAL);
169  }
170 
171  if (sftp_seek64(s->file, newpos)) {
172  av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
173  return AVERROR(EIO);
174  }
175 
176  return newpos;
177 }
178 
179 static int libssh_read(URLContext *h, unsigned char *buf, int size)
180 {
181  LIBSSHContext *s = h->priv_data;
182  int bytes_read;
183 
184  if ((bytes_read = sftp_read(s->file, buf, size)) < 0) {
185  av_log(h, AV_LOG_ERROR, "Read error.\n");
186  return AVERROR(EIO);
187  }
188  return bytes_read;
189 }
190 
191 static int libssh_write(URLContext *h, const unsigned char *buf, int size)
192 {
193  LIBSSHContext *s = h->priv_data;
194  int bytes_written;
195 
196  if ((bytes_written = sftp_write(s->file, buf, size)) < 0) {
197  av_log(h, AV_LOG_ERROR, "Write error.\n");
198  return AVERROR(EIO);
199  }
200  return bytes_written;
201 }
202 
203 #define OFFSET(x) offsetof(LIBSSHContext, x)
204 #define D AV_OPT_FLAG_DECODING_PARAM
205 #define E AV_OPT_FLAG_ENCODING_PARAM
206 static const AVOption options[] = {
207  {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
208  {"truncate", "Truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
209  {NULL}
210 };
211 
213  .class_name = "libssh",
214  .item_name = av_default_item_name,
215  .option = options,
216  .version = LIBAVUTIL_VERSION_INT,
217 };
218 
220  .name = "sftp",
221  .url_open = libssh_open,
222  .url_read = libssh_read,
223  .url_write = libssh_write,
224  .url_seek = libssh_seek,
225  .url_close = libssh_close,
226  .priv_data_size = sizeof(LIBSSHContext),
227  .priv_data_class = &libssh_context_class,
229 };