Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | 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 |
||
22 | #include |
||
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; |
||
35 | int rw_timeout; |
||
36 | int trunc; |
||
37 | } LIBSSHContext; |
||
38 | |||
39 | static int libssh_close(URLContext *h) |
||
40 | { |
||
41 | LIBSSHContext *s = h->priv_data; |
||
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; |
||
56 | LIBSSHContext *s = h->priv_data; |
||
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); |
||
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 | |||
212 | static const AVClass libssh_context_class = { |
||
213 | .class_name = "libssh", |
||
214 | .item_name = av_default_item_name, |
||
215 | .option = options, |
||
216 | .version = LIBAVUTIL_VERSION_INT, |
||
217 | }; |
||
218 | |||
219 | URLProtocol ff_libssh_protocol = { |
||
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, |
||
228 | .flags = URL_PROTOCOL_FLAG_NETWORK, |
||
229 | };>>=> |