Line | Count | Source |
1 | | /* |
2 | | * scp - SSH scp wrapper functions |
3 | | * |
4 | | * This file is part of the SSH Library |
5 | | * |
6 | | * Copyright (c) 2009 by Aris Adamantiadis <aris@0xbadc0de.be> |
7 | | * |
8 | | * The SSH Library is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or (at your |
11 | | * option) any later version. |
12 | | * |
13 | | * The SSH Library is distributed in the hope that it will be useful, but |
14 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
15 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
16 | | * License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with the SSH Library; see the file COPYING. If not, write to |
20 | | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
21 | | * MA 02111-1307, USA. |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | |
26 | | #include <stdio.h> |
27 | | #include <string.h> |
28 | | #include <stdlib.h> |
29 | | |
30 | | #include "libssh/priv.h" |
31 | | #include "libssh/scp.h" |
32 | | #include "libssh/misc.h" |
33 | | #include "libssh/session.h" |
34 | | |
35 | | /** |
36 | | * @defgroup libssh_scp The SSH scp functions |
37 | | * @ingroup libssh |
38 | | * |
39 | | * SCP protocol over SSH functions |
40 | | * |
41 | | * @deprecated Please use SFTP instead |
42 | | * |
43 | | * @{ |
44 | | */ |
45 | | |
46 | | /** |
47 | | * @deprecated Please use SFTP instead |
48 | | * |
49 | | * @brief Create a new scp session. |
50 | | * |
51 | | * @param[in] session The SSH session to use. |
52 | | * |
53 | | * @param[in] mode One of SSH_SCP_WRITE or SSH_SCP_READ, depending if you |
54 | | * need to drop files remotely or read them. |
55 | | * It is not possible to combine read and write. |
56 | | * SSH_SCP_RECURSIVE Flag can be or'ed to this to indicate |
57 | | * that you're going to use recursion. Browsing through |
58 | | * directories is not possible without this. |
59 | | * |
60 | | * @param[in] location The directory in which write or read will be done. Any |
61 | | * push or pull will be relative to this place. |
62 | | * This can also be a pattern of files to download (read). |
63 | | * |
64 | | * @returns A ssh_scp handle, NULL if the creation was impossible. |
65 | | */ |
66 | | ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location) |
67 | 273 | { |
68 | 273 | ssh_scp scp = NULL; |
69 | | |
70 | 273 | if (session == NULL || location == NULL) { |
71 | 0 | goto error; |
72 | 0 | } |
73 | | |
74 | 273 | scp = (ssh_scp)calloc(1, sizeof(struct ssh_scp_struct)); |
75 | 273 | if (scp == NULL) { |
76 | 0 | ssh_set_error(session, SSH_FATAL, |
77 | 0 | "Error allocating memory for ssh_scp"); |
78 | 0 | goto error; |
79 | 0 | } |
80 | | |
81 | 273 | if ((mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE && |
82 | 273 | (mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_READ) |
83 | 0 | { |
84 | 0 | ssh_set_error(session, SSH_FATAL, |
85 | 0 | "Invalid mode %d for ssh_scp_new()", mode); |
86 | 0 | goto error; |
87 | 0 | } |
88 | | |
89 | 273 | if (strlen(location) > 32 * 1024) { |
90 | 0 | ssh_set_error(session, SSH_FATAL, |
91 | 0 | "Location path is too long"); |
92 | 0 | goto error; |
93 | 0 | } |
94 | | |
95 | 273 | scp->location = strdup(location); |
96 | 273 | if (scp->location == NULL) { |
97 | 0 | ssh_set_error(session, SSH_FATAL, |
98 | 0 | "Error allocating memory for ssh_scp"); |
99 | 0 | goto error; |
100 | 0 | } |
101 | | |
102 | 273 | scp->session = session; |
103 | 273 | scp->mode = mode & ~SSH_SCP_RECURSIVE; |
104 | 273 | scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0; |
105 | 273 | scp->channel = NULL; |
106 | 273 | scp->state = SSH_SCP_NEW; |
107 | | |
108 | 273 | return scp; |
109 | | |
110 | 0 | error: |
111 | 0 | ssh_scp_free(scp); |
112 | 0 | return NULL; |
113 | 273 | } |
114 | | |
115 | | /** |
116 | | * @deprecated Please use SFTP instead |
117 | | * |
118 | | * @brief Initialize the scp channel. |
119 | | * |
120 | | * @param[in] scp The scp context to initialize. |
121 | | * |
122 | | * @return SSH_OK on success or an SSH error code. |
123 | | * |
124 | | * @see ssh_scp_new() |
125 | | */ |
126 | | int ssh_scp_init(ssh_scp scp) |
127 | 273 | { |
128 | 273 | int rc; |
129 | 273 | char execbuffer[PATH_MAX] = {0}; |
130 | 273 | char *quoted_location = NULL; |
131 | 273 | size_t quoted_location_len = 0; |
132 | 273 | size_t scp_location_len; |
133 | | |
134 | 273 | if (scp == NULL) { |
135 | 0 | return SSH_ERROR; |
136 | 0 | } |
137 | | |
138 | 273 | if (scp->state != SSH_SCP_NEW) { |
139 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
140 | 0 | "ssh_scp_init called under invalid state"); |
141 | 0 | return SSH_ERROR; |
142 | 0 | } |
143 | | |
144 | 273 | if (scp->location == NULL) { |
145 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
146 | 0 | "Invalid scp context: location is NULL"); |
147 | 0 | return SSH_ERROR; |
148 | 0 | } |
149 | | |
150 | 273 | SSH_LOG(SSH_LOG_DEBUG, "Initializing scp session %s %son location '%s'", |
151 | 273 | scp->mode == SSH_SCP_WRITE?"write":"read", |
152 | 273 | scp->recursive ? "recursive " : "", |
153 | 273 | scp->location); |
154 | | |
155 | 273 | scp->channel = ssh_channel_new(scp->session); |
156 | 273 | if (scp->channel == NULL) { |
157 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
158 | 0 | "Channel creation failed for scp"); |
159 | 0 | scp->state = SSH_SCP_ERROR; |
160 | 0 | return SSH_ERROR; |
161 | 0 | } |
162 | | |
163 | 273 | rc = ssh_channel_open_session(scp->channel); |
164 | 273 | if (rc == SSH_ERROR) { |
165 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
166 | 0 | "Failed to open channel for scp"); |
167 | 0 | scp->state = SSH_SCP_ERROR; |
168 | 0 | return SSH_ERROR; |
169 | 0 | } |
170 | | |
171 | | /* In the worst case, each character would be replaced by 3 plus the string |
172 | | * terminator '\0' */ |
173 | 273 | scp_location_len = strlen(scp->location); |
174 | 273 | quoted_location_len = ((size_t)3 * scp_location_len) + 1; |
175 | | /* Paranoia check */ |
176 | 273 | if (quoted_location_len < scp_location_len) { |
177 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
178 | 0 | "Buffer overflow detected"); |
179 | 0 | scp->state = SSH_SCP_ERROR; |
180 | 0 | return SSH_ERROR; |
181 | 0 | } |
182 | | |
183 | 273 | quoted_location = (char *)calloc(1, quoted_location_len); |
184 | 273 | if (quoted_location == NULL) { |
185 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
186 | 0 | "Failed to allocate memory for quoted location"); |
187 | 0 | scp->state = SSH_SCP_ERROR; |
188 | 0 | return SSH_ERROR; |
189 | 0 | } |
190 | | |
191 | 273 | rc = ssh_quote_file_name(scp->location, quoted_location, |
192 | 273 | quoted_location_len); |
193 | 273 | if (rc <= 0) { |
194 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
195 | 0 | "Failed to single quote command location"); |
196 | 0 | SAFE_FREE(quoted_location); |
197 | 0 | scp->state = SSH_SCP_ERROR; |
198 | 0 | return SSH_ERROR; |
199 | 0 | } |
200 | | |
201 | | /* Some servers do not handle the quoting well. Pass in the raw file |
202 | | * location */ |
203 | 273 | if (scp->session->flags & SSH_SESSION_FLAG_SCP_QUOTING_BROKEN) { |
204 | 0 | free(quoted_location); |
205 | 0 | quoted_location = strdup(scp->location); |
206 | 0 | if (quoted_location == NULL) { |
207 | 0 | ssh_set_error_oom(scp->session); |
208 | 0 | return SSH_ERROR; |
209 | 0 | } |
210 | 0 | } |
211 | | |
212 | 273 | if (scp->mode == SSH_SCP_WRITE) { |
213 | 0 | snprintf(execbuffer, sizeof(execbuffer), "scp -t %s %s", |
214 | 0 | scp->recursive ? "-r" : "", quoted_location); |
215 | 273 | } else { |
216 | 273 | snprintf(execbuffer, sizeof(execbuffer), "scp -f %s %s", |
217 | 273 | scp->recursive ? "-r" : "", quoted_location); |
218 | 273 | } |
219 | | |
220 | 273 | SAFE_FREE(quoted_location); |
221 | | |
222 | 273 | SSH_LOG(SSH_LOG_DEBUG, "Executing command: %s", execbuffer); |
223 | | |
224 | 273 | rc = ssh_channel_request_exec(scp->channel, execbuffer); |
225 | 273 | if (rc == SSH_ERROR){ |
226 | 273 | ssh_set_error(scp->session, SSH_FATAL, |
227 | 273 | "Failed executing command: %s", execbuffer); |
228 | 273 | scp->state = SSH_SCP_ERROR; |
229 | 273 | return SSH_ERROR; |
230 | 273 | } |
231 | | |
232 | 0 | if (scp->mode == SSH_SCP_WRITE) { |
233 | 0 | rc = ssh_scp_response(scp, NULL); |
234 | 0 | if (rc != 0) { |
235 | 0 | return SSH_ERROR; |
236 | 0 | } |
237 | 0 | } else { |
238 | 0 | ssh_channel_write(scp->channel, "", 1); |
239 | 0 | } |
240 | | |
241 | 0 | if (scp->mode == SSH_SCP_WRITE) { |
242 | 0 | scp->state = SSH_SCP_WRITE_INITED; |
243 | 0 | } else { |
244 | 0 | scp->state = SSH_SCP_READ_INITED; |
245 | 0 | } |
246 | |
|
247 | 0 | return SSH_OK; |
248 | 0 | } |
249 | | |
250 | | /** |
251 | | * @deprecated Please use SFTP instead |
252 | | * |
253 | | * @brief Close the scp channel. |
254 | | * |
255 | | * @param[in] scp The scp context to close. |
256 | | * |
257 | | * @return SSH_OK on success or an SSH error code. |
258 | | * |
259 | | * @see ssh_scp_init() |
260 | | */ |
261 | | int ssh_scp_close(ssh_scp scp) |
262 | 273 | { |
263 | 273 | char buffer[128] = {0}; |
264 | 273 | int rc; |
265 | | |
266 | 273 | if (scp == NULL) { |
267 | 0 | return SSH_ERROR; |
268 | 0 | } |
269 | | |
270 | 273 | if (scp->channel != NULL) { |
271 | 273 | if (ssh_channel_send_eof(scp->channel) == SSH_ERROR) { |
272 | 0 | scp->state = SSH_SCP_ERROR; |
273 | 0 | return SSH_ERROR; |
274 | 0 | } |
275 | | /* avoid situations where data are buffered and |
276 | | * not yet stored on disk. This can happen if the close is sent |
277 | | * before we got the EOF back |
278 | | */ |
279 | 273 | while (!ssh_channel_is_eof(scp->channel)) { |
280 | 273 | rc = ssh_channel_read(scp->channel, buffer, sizeof(buffer), 0); |
281 | 273 | if (rc == SSH_ERROR || rc == SSH_AGAIN || rc == 0) { |
282 | 273 | break; |
283 | 273 | } |
284 | 273 | } |
285 | | |
286 | 273 | if (ssh_channel_close(scp->channel) == SSH_ERROR) { |
287 | 0 | scp->state = SSH_SCP_ERROR; |
288 | 0 | return SSH_ERROR; |
289 | 0 | } |
290 | | |
291 | 273 | ssh_channel_free(scp->channel); |
292 | 273 | scp->channel = NULL; |
293 | 273 | } |
294 | | |
295 | 273 | scp->state = SSH_SCP_NEW; |
296 | 273 | return SSH_OK; |
297 | 273 | } |
298 | | |
299 | | /** |
300 | | * @deprecated Please use SFTP instead |
301 | | * |
302 | | * @brief Free a scp context. |
303 | | * |
304 | | * @param[in] scp The context to free. |
305 | | * |
306 | | * @see ssh_scp_new() |
307 | | */ |
308 | | void ssh_scp_free(ssh_scp scp) |
309 | 273 | { |
310 | 273 | if (scp == NULL) { |
311 | 0 | return; |
312 | 0 | } |
313 | | |
314 | 273 | if (scp->state != SSH_SCP_NEW) { |
315 | 0 | ssh_scp_close(scp); |
316 | 0 | } |
317 | | |
318 | 273 | if (scp->channel) { |
319 | 0 | ssh_channel_free(scp->channel); |
320 | 0 | } |
321 | | |
322 | 273 | SAFE_FREE(scp->location); |
323 | 273 | SAFE_FREE(scp->request_name); |
324 | 273 | SAFE_FREE(scp->warning); |
325 | 273 | SAFE_FREE(scp); |
326 | 273 | } |
327 | | |
328 | | /** |
329 | | * @deprecated Please use SFTP instead |
330 | | * |
331 | | * @brief Create a directory in a scp in sink mode. |
332 | | * |
333 | | * @param[in] scp The scp handle. |
334 | | * |
335 | | * @param[in] dirname The name of the directory being created. |
336 | | * |
337 | | * @param[in] mode The UNIX permissions for the new directory, e.g. 0755. |
338 | | * |
339 | | * @returns SSH_OK if the directory has been created, SSH_ERROR if |
340 | | * an error occurred. |
341 | | * |
342 | | * @see ssh_scp_leave_directory() |
343 | | */ |
344 | | int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode) |
345 | 0 | { |
346 | 0 | char buffer[PATH_MAX] = {0}; |
347 | 0 | int rc; |
348 | 0 | char *dir = NULL; |
349 | 0 | char *perms = NULL; |
350 | 0 | char *vis_encoded = NULL; |
351 | 0 | size_t vis_encoded_len; |
352 | |
|
353 | 0 | if (scp == NULL) { |
354 | 0 | return SSH_ERROR; |
355 | 0 | } |
356 | | |
357 | 0 | if (scp->state != SSH_SCP_WRITE_INITED) { |
358 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
359 | 0 | "ssh_scp_push_directory called under invalid state"); |
360 | 0 | return SSH_ERROR; |
361 | 0 | } |
362 | | |
363 | 0 | dir = ssh_basename(dirname); |
364 | 0 | if (dir == NULL) { |
365 | 0 | ssh_set_error_oom(scp->session); |
366 | 0 | return SSH_ERROR; |
367 | 0 | } |
368 | | |
369 | 0 | vis_encoded_len = (2 * strlen(dir)) + 1; |
370 | 0 | vis_encoded = (char *)calloc(1, vis_encoded_len); |
371 | 0 | if (vis_encoded == NULL) { |
372 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
373 | 0 | "Failed to allocate buffer to vis encode directory name"); |
374 | 0 | goto error; |
375 | 0 | } |
376 | | |
377 | 0 | rc = ssh_newline_vis(dir, vis_encoded, vis_encoded_len); |
378 | 0 | if (rc <= 0) { |
379 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
380 | 0 | "Failed to vis encode directory name"); |
381 | 0 | goto error; |
382 | 0 | } |
383 | | |
384 | 0 | perms = ssh_scp_string_mode(mode); |
385 | 0 | if (perms == NULL) { |
386 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
387 | 0 | "Failed to get directory permission string"); |
388 | 0 | goto error; |
389 | 0 | } |
390 | | |
391 | 0 | SSH_LOG(SSH_LOG_DEBUG, |
392 | 0 | "SCP pushing directory %s with permissions '%s'", |
393 | 0 | vis_encoded, perms); |
394 | | |
395 | | /* Use vis encoded directory name */ |
396 | 0 | snprintf(buffer, sizeof(buffer), |
397 | 0 | "D%s 0 %s\n", |
398 | 0 | perms, vis_encoded); |
399 | |
|
400 | 0 | SAFE_FREE(dir); |
401 | 0 | SAFE_FREE(perms); |
402 | 0 | SAFE_FREE(vis_encoded); |
403 | |
|
404 | 0 | rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); |
405 | 0 | if (rc == SSH_ERROR) { |
406 | 0 | scp->state = SSH_SCP_ERROR; |
407 | 0 | return SSH_ERROR; |
408 | 0 | } |
409 | | |
410 | 0 | rc = ssh_scp_response(scp, NULL); |
411 | 0 | if (rc != 0) { |
412 | 0 | return SSH_ERROR; |
413 | 0 | } |
414 | | |
415 | 0 | return SSH_OK; |
416 | | |
417 | 0 | error: |
418 | 0 | SAFE_FREE(dir); |
419 | 0 | SAFE_FREE(perms); |
420 | 0 | SAFE_FREE(vis_encoded); |
421 | |
|
422 | 0 | return SSH_ERROR; |
423 | 0 | } |
424 | | |
425 | | /** |
426 | | * @deprecated Please use SFTP instead |
427 | | * |
428 | | * @brief Leave a directory. |
429 | | * |
430 | | * @returns SSH_OK if the directory has been left, SSH_ERROR if an |
431 | | * error occurred. |
432 | | * |
433 | | * @see ssh_scp_push_directory() |
434 | | */ |
435 | | int ssh_scp_leave_directory(ssh_scp scp) |
436 | 0 | { |
437 | 0 | char buffer[] = "E\n"; |
438 | 0 | int rc; |
439 | |
|
440 | 0 | if (scp == NULL) { |
441 | 0 | return SSH_ERROR; |
442 | 0 | } |
443 | | |
444 | 0 | if (scp->state != SSH_SCP_WRITE_INITED) { |
445 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
446 | 0 | "ssh_scp_leave_directory called under invalid state"); |
447 | 0 | return SSH_ERROR; |
448 | 0 | } |
449 | | |
450 | 0 | rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); |
451 | 0 | if (rc == SSH_ERROR) { |
452 | 0 | scp->state = SSH_SCP_ERROR; |
453 | 0 | return SSH_ERROR; |
454 | 0 | } |
455 | | |
456 | 0 | rc = ssh_scp_response(scp, NULL); |
457 | 0 | if (rc != 0) { |
458 | 0 | return SSH_ERROR; |
459 | 0 | } |
460 | | |
461 | 0 | return SSH_OK; |
462 | 0 | } |
463 | | |
464 | | /** |
465 | | * @deprecated Please use SFTP instead |
466 | | * |
467 | | * @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit |
468 | | * size. |
469 | | * |
470 | | * @param[in] scp The scp handle. |
471 | | * |
472 | | * @param[in] filename The name of the file being sent. It should not contain |
473 | | * any path indicator |
474 | | * |
475 | | * @param[in] size Exact size in bytes of the file being sent. |
476 | | * |
477 | | * @param[in] mode The UNIX permissions for the new file, e.g. 0644. |
478 | | * |
479 | | * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an |
480 | | * error occurred. |
481 | | * |
482 | | * @see ssh_scp_push_file() |
483 | | */ |
484 | | int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, |
485 | | int mode) |
486 | 0 | { |
487 | 0 | char buffer[PATH_MAX] = {0}; |
488 | 0 | int rc; |
489 | 0 | char *file = NULL; |
490 | 0 | char *perms = NULL; |
491 | 0 | char *vis_encoded = NULL; |
492 | 0 | size_t vis_encoded_len; |
493 | |
|
494 | 0 | if (scp == NULL) { |
495 | 0 | return SSH_ERROR; |
496 | 0 | } |
497 | | |
498 | 0 | if (scp->state != SSH_SCP_WRITE_INITED) { |
499 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
500 | 0 | "ssh_scp_push_file called under invalid state"); |
501 | 0 | return SSH_ERROR; |
502 | 0 | } |
503 | | |
504 | 0 | file = ssh_basename(filename); |
505 | 0 | if (file == NULL) { |
506 | 0 | ssh_set_error_oom(scp->session); |
507 | 0 | return SSH_ERROR; |
508 | 0 | } |
509 | | |
510 | 0 | vis_encoded_len = (2 * strlen(file)) + 1; |
511 | 0 | vis_encoded = (char *)calloc(1, vis_encoded_len); |
512 | 0 | if (vis_encoded == NULL) { |
513 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
514 | 0 | "Failed to allocate buffer to vis encode file name"); |
515 | 0 | goto error; |
516 | 0 | } |
517 | | |
518 | 0 | rc = ssh_newline_vis(file, vis_encoded, vis_encoded_len); |
519 | 0 | if (rc <= 0) { |
520 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
521 | 0 | "Failed to vis encode file name"); |
522 | 0 | goto error; |
523 | 0 | } |
524 | | |
525 | 0 | perms = ssh_scp_string_mode(mode); |
526 | 0 | if (perms == NULL) { |
527 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
528 | 0 | "Failed to get file permission string"); |
529 | 0 | goto error; |
530 | 0 | } |
531 | | |
532 | 0 | SSH_LOG(SSH_LOG_DEBUG, |
533 | 0 | "SCP pushing file %s, size %" PRIu64 " with permissions '%s'", |
534 | 0 | vis_encoded, size, perms); |
535 | | |
536 | | /* Use vis encoded file name */ |
537 | 0 | snprintf(buffer, sizeof(buffer), |
538 | 0 | "C%s %" PRIu64 " %s\n", |
539 | 0 | perms, size, vis_encoded); |
540 | |
|
541 | 0 | SAFE_FREE(file); |
542 | 0 | SAFE_FREE(perms); |
543 | 0 | SAFE_FREE(vis_encoded); |
544 | |
|
545 | 0 | rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); |
546 | 0 | if (rc == SSH_ERROR) { |
547 | 0 | scp->state = SSH_SCP_ERROR; |
548 | 0 | return SSH_ERROR; |
549 | 0 | } |
550 | | |
551 | 0 | rc = ssh_scp_response(scp, NULL); |
552 | 0 | if (rc != 0) { |
553 | 0 | return SSH_ERROR; |
554 | 0 | } |
555 | | |
556 | 0 | scp->filelen = size; |
557 | 0 | scp->processed = 0; |
558 | 0 | scp->state = SSH_SCP_WRITE_WRITING; |
559 | |
|
560 | 0 | return SSH_OK; |
561 | | |
562 | 0 | error: |
563 | 0 | SAFE_FREE(file); |
564 | 0 | SAFE_FREE(perms); |
565 | 0 | SAFE_FREE(vis_encoded); |
566 | |
|
567 | 0 | return SSH_ERROR; |
568 | 0 | } |
569 | | |
570 | | /** |
571 | | * @deprecated Please use SFTP instead |
572 | | * |
573 | | * @brief Initialize the sending of a file to a scp in sink mode. |
574 | | * |
575 | | * @param[in] scp The scp handle. |
576 | | * |
577 | | * @param[in] filename The name of the file being sent. It should not contain |
578 | | * any path indicator |
579 | | * |
580 | | * @param[in] size Exact size in bytes of the file being sent. |
581 | | * |
582 | | * @param[in] mode The UNIX permissions for the new file, e.g. 0644. |
583 | | * |
584 | | * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an |
585 | | * error occurred. |
586 | | */ |
587 | | int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode) |
588 | 0 | { |
589 | 0 | return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode); |
590 | 0 | } |
591 | | |
592 | | /** |
593 | | * @internal |
594 | | * |
595 | | * @deprecated Please use SFTP instead |
596 | | * |
597 | | * @brief Wait for a response of the scp server. |
598 | | * |
599 | | * @param[in] scp The scp handle. |
600 | | * |
601 | | * @param[out] response A pointer where the response message must be copied if |
602 | | * any. This pointer must then be free'd. |
603 | | * |
604 | | * @returns The return code, SSH_ERROR a error occurred. |
605 | | */ |
606 | | int ssh_scp_response(ssh_scp scp, char **response) |
607 | 0 | { |
608 | 0 | unsigned char code; |
609 | 0 | int rc; |
610 | 0 | char msg[128] = {0}; |
611 | |
|
612 | 0 | if (scp == NULL) { |
613 | 0 | return SSH_ERROR; |
614 | 0 | } |
615 | | |
616 | 0 | rc = ssh_channel_read(scp->channel, &code, 1, 0); |
617 | 0 | if (rc == SSH_ERROR) { |
618 | 0 | scp->state = SSH_SCP_ERROR; |
619 | 0 | return SSH_ERROR; |
620 | 0 | } |
621 | 0 | if (rc == SSH_AGAIN) { |
622 | 0 | ssh_set_error(scp->session, SSH_FATAL, "SCP: ssh_channel_read timeout"); |
623 | 0 | scp->state = SSH_SCP_ERROR; |
624 | 0 | return SSH_ERROR; |
625 | 0 | } |
626 | | |
627 | 0 | if (code == 0) { |
628 | 0 | return 0; |
629 | 0 | } |
630 | | |
631 | 0 | if (code > 2) { |
632 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
633 | 0 | "SCP: invalid status code %u received", code); |
634 | 0 | scp->state = SSH_SCP_ERROR; |
635 | 0 | return SSH_ERROR; |
636 | 0 | } |
637 | | |
638 | 0 | rc = ssh_scp_read_string(scp, msg, sizeof(msg)); |
639 | 0 | if (rc == SSH_ERROR) { |
640 | 0 | return rc; |
641 | 0 | } |
642 | | |
643 | | /* Warning */ |
644 | 0 | if (code == 1) { |
645 | 0 | ssh_set_error(scp->session, SSH_REQUEST_DENIED, |
646 | 0 | "SCP: Warning: status code 1 received: %s", msg); |
647 | 0 | SSH_LOG(SSH_LOG_RARE, |
648 | 0 | "SCP: Warning: status code 1 received: %s", msg); |
649 | 0 | if (response) { |
650 | 0 | *response = strdup(msg); |
651 | 0 | } |
652 | 0 | return 1; |
653 | 0 | } |
654 | | |
655 | 0 | if (code == 2) { |
656 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
657 | 0 | "SCP: Error: status code 2 received: %s", msg); |
658 | 0 | if (response) { |
659 | 0 | *response = strdup(msg); |
660 | 0 | } |
661 | 0 | return 2; |
662 | 0 | } |
663 | | |
664 | | /* Not reached */ |
665 | 0 | return SSH_ERROR; |
666 | 0 | } |
667 | | |
668 | | /** |
669 | | * @deprecated Please use SFTP instead |
670 | | * |
671 | | * @brief Write into a remote scp file. |
672 | | * |
673 | | * @param[in] scp The scp handle. |
674 | | * |
675 | | * @param[in] buffer The buffer to write. |
676 | | * |
677 | | * @param[in] len The number of bytes to write. |
678 | | * |
679 | | * @returns SSH_OK if the write was successful, SSH_ERROR an error |
680 | | * occurred while writing. |
681 | | */ |
682 | | int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len) |
683 | 0 | { |
684 | 0 | int w; |
685 | 0 | int rc; |
686 | 0 | uint8_t code; |
687 | |
|
688 | 0 | if (scp == NULL) { |
689 | 0 | return SSH_ERROR; |
690 | 0 | } |
691 | | |
692 | 0 | if (scp->state != SSH_SCP_WRITE_WRITING) { |
693 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
694 | 0 | "ssh_scp_write called under invalid state"); |
695 | 0 | return SSH_ERROR; |
696 | 0 | } |
697 | | |
698 | 0 | if (scp->processed + len > scp->filelen) { |
699 | 0 | len = (size_t) (scp->filelen - scp->processed); |
700 | 0 | } |
701 | | |
702 | | /* hack to avoid waiting for window change */ |
703 | 0 | rc = ssh_channel_poll(scp->channel, 0); |
704 | 0 | if (rc == SSH_ERROR) { |
705 | 0 | scp->state = SSH_SCP_ERROR; |
706 | 0 | return SSH_ERROR; |
707 | 0 | } |
708 | | |
709 | 0 | w = ssh_channel_write(scp->channel, buffer, len); |
710 | 0 | if (w != SSH_ERROR) { |
711 | 0 | scp->processed += w; |
712 | 0 | } else { |
713 | 0 | scp->state = SSH_SCP_ERROR; |
714 | 0 | return SSH_ERROR; |
715 | 0 | } |
716 | | |
717 | | /* Far end sometimes send a status message, which we need to read |
718 | | * and handle */ |
719 | 0 | rc = ssh_channel_poll(scp->channel, 0); |
720 | 0 | if (rc > 0) { |
721 | 0 | rc = ssh_scp_response(scp, NULL); |
722 | 0 | if (rc != 0) { |
723 | 0 | return SSH_ERROR; |
724 | 0 | } |
725 | 0 | } |
726 | | |
727 | | /* Check if we arrived at end of file */ |
728 | 0 | if (scp->processed == scp->filelen) { |
729 | 0 | code = 0; |
730 | 0 | w = ssh_channel_write(scp->channel, &code, 1); |
731 | 0 | if (w == SSH_ERROR) { |
732 | 0 | scp->state = SSH_SCP_ERROR; |
733 | 0 | return SSH_ERROR; |
734 | 0 | } |
735 | | |
736 | 0 | scp->processed = scp->filelen = 0; |
737 | 0 | scp->state = SSH_SCP_WRITE_INITED; |
738 | 0 | } |
739 | | |
740 | 0 | return SSH_OK; |
741 | 0 | } |
742 | | |
743 | | /** |
744 | | * @deprecated Please use SFTP instead |
745 | | * |
746 | | * @brief Read a string on a channel, terminated by '\n' |
747 | | * |
748 | | * @param[in] scp The scp handle. |
749 | | * |
750 | | * @param[out] buffer A pointer to a buffer to place the string. |
751 | | * |
752 | | * @param[in] len The size of the buffer in bytes. If the string is bigger |
753 | | * than len-1, only len-1 bytes are read and the string is |
754 | | * null-terminated. |
755 | | * |
756 | | * @returns SSH_OK if the string was read, SSH_ERROR if an error |
757 | | * occurred while reading. |
758 | | */ |
759 | | int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len) |
760 | 0 | { |
761 | 0 | size_t read = 0; |
762 | 0 | int err = SSH_OK; |
763 | |
|
764 | 0 | if (scp == NULL) { |
765 | 0 | return SSH_ERROR; |
766 | 0 | } |
767 | | |
768 | 0 | while (read < len - 1) { |
769 | 0 | err = ssh_channel_read(scp->channel, &buffer[read], 1, 0); |
770 | 0 | if (err == SSH_ERROR) { |
771 | 0 | break; |
772 | 0 | } |
773 | | |
774 | 0 | if (err == 0) { |
775 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
776 | 0 | "End of file while reading string"); |
777 | 0 | err = SSH_ERROR; |
778 | 0 | break; |
779 | 0 | } |
780 | | |
781 | 0 | if (err == SSH_AGAIN) { |
782 | 0 | ssh_set_error(scp->session, |
783 | 0 | SSH_FATAL, |
784 | 0 | "SCP: ssh_channel_read timeout"); |
785 | 0 | err = SSH_ERROR; |
786 | 0 | break; |
787 | 0 | } |
788 | | |
789 | 0 | read++; |
790 | 0 | if (buffer[read - 1] == '\n') { |
791 | 0 | break; |
792 | 0 | } |
793 | 0 | } |
794 | |
|
795 | 0 | buffer[read] = 0; |
796 | 0 | return err; |
797 | 0 | } |
798 | | |
799 | | /** |
800 | | * @deprecated Please use SFTP instead |
801 | | * |
802 | | * @brief Wait for a scp request (file, directory). |
803 | | * |
804 | | * @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending |
805 | | * a file |
806 | | * SSH_SCP_REQUEST_NEWDIR: The other side is sending |
807 | | * a directory |
808 | | * SSH_SCP_REQUEST_ENDDIR: The other side has |
809 | | * finished with the current |
810 | | * directory |
811 | | * SSH_SCP_REQUEST_WARNING: The other side sent us a warning |
812 | | * SSH_SCP_REQUEST_EOF: The other side finished sending us |
813 | | * files and data. |
814 | | * SSH_ERROR: Some error happened |
815 | | * |
816 | | * @see ssh_scp_read() |
817 | | * @see ssh_scp_deny_request() |
818 | | * @see ssh_scp_accept_request() |
819 | | * @see ssh_scp_request_get_warning() |
820 | | */ |
821 | | int ssh_scp_pull_request(ssh_scp scp) |
822 | 0 | { |
823 | 0 | char buffer[PATH_MAX] = {0}; |
824 | 0 | char *mode = NULL; |
825 | 0 | char *p, *tmp; |
826 | 0 | uint64_t size; |
827 | 0 | char *name = NULL; |
828 | 0 | int rc; |
829 | |
|
830 | 0 | if (scp == NULL) { |
831 | 0 | return SSH_ERROR; |
832 | 0 | } |
833 | | |
834 | 0 | if (scp->state != SSH_SCP_READ_INITED) { |
835 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
836 | 0 | "ssh_scp_pull_request called under invalid state"); |
837 | 0 | return SSH_ERROR; |
838 | 0 | } |
839 | | |
840 | 0 | rc = ssh_scp_read_string(scp, buffer, sizeof(buffer)); |
841 | 0 | if (rc == SSH_ERROR) { |
842 | 0 | if (ssh_channel_is_eof(scp->channel)) { |
843 | 0 | scp->state = SSH_SCP_TERMINATED; |
844 | 0 | return SSH_SCP_REQUEST_EOF; |
845 | 0 | } |
846 | 0 | return rc; |
847 | 0 | } |
848 | | |
849 | 0 | p = strchr(buffer, '\n'); |
850 | 0 | if (p != NULL) { |
851 | 0 | *p = '\0'; |
852 | 0 | } |
853 | |
|
854 | 0 | SSH_LOG(SSH_LOG_DEBUG, "Received SCP request: '%s'", buffer); |
855 | 0 | switch(buffer[0]) { |
856 | 0 | case 'C': |
857 | | /* File */ |
858 | 0 | case 'D': |
859 | | /* Directory */ |
860 | 0 | p = strchr(buffer, ' '); |
861 | 0 | if (p == NULL) { |
862 | 0 | goto error; |
863 | 0 | } |
864 | 0 | *p = '\0'; |
865 | 0 | p++; |
866 | | //mode = strdup(&buffer[1]); |
867 | 0 | scp->request_mode = ssh_scp_integer_mode(&buffer[1]); |
868 | 0 | tmp = p; |
869 | 0 | p = strchr(p, ' '); |
870 | 0 | if (p == NULL) { |
871 | 0 | goto error; |
872 | 0 | } |
873 | 0 | *p = 0; |
874 | 0 | size = strtoull(tmp, NULL, 10); |
875 | 0 | p++; |
876 | 0 | name = strdup(p); |
877 | | /* Catch invalid name: |
878 | | * - empty ones |
879 | | * - containing any forward slash -- directory traversal handled |
880 | | * differently |
881 | | * - special names "." and ".." referring to the current and parent |
882 | | * directories -- they are not expected either |
883 | | */ |
884 | 0 | if (name == NULL || name[0] == '\0' || strchr(name, '/') || |
885 | 0 | strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { |
886 | 0 | ssh_set_error(scp->session, |
887 | 0 | SSH_FATAL, |
888 | 0 | "Received invalid filename: %s", |
889 | 0 | name == NULL ? "<NULL>" : name); |
890 | 0 | SAFE_FREE(name); |
891 | 0 | goto error; |
892 | 0 | } |
893 | 0 | SAFE_FREE(scp->request_name); |
894 | 0 | scp->request_name = name; |
895 | 0 | if (buffer[0] == 'C') { |
896 | 0 | scp->filelen = size; |
897 | 0 | scp->request_type = SSH_SCP_REQUEST_NEWFILE; |
898 | 0 | } else { |
899 | 0 | scp->filelen = '0'; |
900 | 0 | scp->request_type = SSH_SCP_REQUEST_NEWDIR; |
901 | 0 | } |
902 | 0 | scp->state = SSH_SCP_READ_REQUESTED; |
903 | 0 | scp->processed = 0; |
904 | 0 | return scp->request_type; |
905 | 0 | break; |
906 | 0 | case 'E': |
907 | 0 | scp->request_type = SSH_SCP_REQUEST_ENDDIR; |
908 | 0 | ssh_channel_write(scp->channel, "", 1); |
909 | 0 | return scp->request_type; |
910 | 0 | case 0x1: |
911 | 0 | ssh_set_error(scp->session, SSH_REQUEST_DENIED, |
912 | 0 | "SCP: Warning: %s", &buffer[1]); |
913 | 0 | scp->request_type = SSH_SCP_REQUEST_WARNING; |
914 | 0 | SAFE_FREE(scp->warning); |
915 | 0 | scp->warning = strdup(&buffer[1]); |
916 | 0 | return scp->request_type; |
917 | 0 | case 0x2: |
918 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
919 | 0 | "SCP: Error: %s", &buffer[1]); |
920 | 0 | return SSH_ERROR; |
921 | 0 | case 'T': |
922 | | /* Timestamp */ |
923 | 0 | default: |
924 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
925 | 0 | "Unhandled message: (%d)%s", buffer[0], buffer); |
926 | 0 | return SSH_ERROR; |
927 | 0 | } |
928 | | |
929 | | /* a parsing error occurred */ |
930 | 0 | error: |
931 | 0 | SAFE_FREE(name); |
932 | 0 | SAFE_FREE(mode); |
933 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
934 | 0 | "Parsing error while parsing message: %s", buffer); |
935 | 0 | return SSH_ERROR; |
936 | 0 | } |
937 | | |
938 | | /** |
939 | | * @deprecated Please use SFTP instead |
940 | | * |
941 | | * @brief Deny the transfer of a file or creation of a directory coming from the |
942 | | * remote party. |
943 | | * |
944 | | * @param[in] scp The scp handle. |
945 | | * @param[in] reason A nul-terminated string with a human-readable |
946 | | * explanation of the deny. |
947 | | * |
948 | | * @returns SSH_OK if the message was sent, SSH_ERROR if the sending |
949 | | * the message failed, or sending it in a bad state. |
950 | | */ |
951 | | int ssh_scp_deny_request(ssh_scp scp, const char *reason) |
952 | 0 | { |
953 | 0 | char *buffer = NULL; |
954 | 0 | size_t len; |
955 | 0 | int rc; |
956 | |
|
957 | 0 | if (scp == NULL) { |
958 | 0 | return SSH_ERROR; |
959 | 0 | } |
960 | | |
961 | 0 | if (scp->state != SSH_SCP_READ_REQUESTED) { |
962 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
963 | 0 | "ssh_scp_deny_request called under invalid state"); |
964 | 0 | return SSH_ERROR; |
965 | 0 | } |
966 | | |
967 | 0 | len = strlen(reason) + 3; |
968 | 0 | buffer = malloc(len); |
969 | 0 | if (buffer == NULL) { |
970 | 0 | return SSH_ERROR; |
971 | 0 | } |
972 | | |
973 | 0 | snprintf(buffer, len, "%c%s\n", 2, reason); |
974 | 0 | rc = ssh_channel_write(scp->channel, buffer, len - 1); |
975 | 0 | free(buffer); |
976 | 0 | if (rc == SSH_ERROR) { |
977 | 0 | return SSH_ERROR; |
978 | 0 | } |
979 | | |
980 | 0 | else { |
981 | 0 | scp->state = SSH_SCP_READ_INITED; |
982 | 0 | return SSH_OK; |
983 | 0 | } |
984 | 0 | } |
985 | | |
986 | | /** |
987 | | * @deprecated Please use SFTP instead |
988 | | * |
989 | | * @brief Accepts transfer of a file or creation of a directory coming from the |
990 | | * remote party. |
991 | | * |
992 | | * @param[in] scp The scp handle. |
993 | | * |
994 | | * @returns SSH_OK if the message was sent, SSH_ERROR if sending the |
995 | | * message failed, or sending it in a bad state. |
996 | | */ |
997 | | int ssh_scp_accept_request(ssh_scp scp) |
998 | 0 | { |
999 | 0 | char buffer[] = {0x00}; |
1000 | 0 | int rc; |
1001 | 0 | if (scp == NULL) { |
1002 | 0 | return SSH_ERROR; |
1003 | 0 | } |
1004 | | |
1005 | 0 | if (scp->state != SSH_SCP_READ_REQUESTED) { |
1006 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
1007 | 0 | "ssh_scp_deny_request called under invalid state"); |
1008 | 0 | return SSH_ERROR; |
1009 | 0 | } |
1010 | | |
1011 | 0 | rc = ssh_channel_write(scp->channel, buffer, 1); |
1012 | 0 | if (rc == SSH_ERROR) { |
1013 | 0 | return SSH_ERROR; |
1014 | 0 | } |
1015 | | |
1016 | 0 | if (scp->request_type == SSH_SCP_REQUEST_NEWFILE) { |
1017 | 0 | scp->state = SSH_SCP_READ_READING; |
1018 | 0 | } else { |
1019 | 0 | scp->state = SSH_SCP_READ_INITED; |
1020 | 0 | } |
1021 | |
|
1022 | 0 | return SSH_OK; |
1023 | 0 | } |
1024 | | |
1025 | | /** |
1026 | | * @deprecated Please use SFTP instead |
1027 | | * |
1028 | | * @brief Read from a remote scp file |
1029 | | * |
1030 | | * @param[in] scp The scp handle. |
1031 | | * |
1032 | | * @param[in] buffer The destination buffer. |
1033 | | * |
1034 | | * @param[in] size The size of the buffer. |
1035 | | * |
1036 | | * @returns The number of bytes read, SSH_ERROR if an error occurred |
1037 | | * while reading. |
1038 | | */ |
1039 | | int ssh_scp_read(ssh_scp scp, void *buffer, size_t size) |
1040 | 0 | { |
1041 | 0 | int rc; |
1042 | 0 | int code; |
1043 | |
|
1044 | 0 | if (scp == NULL) { |
1045 | 0 | return SSH_ERROR; |
1046 | 0 | } |
1047 | | |
1048 | 0 | if (scp->state == SSH_SCP_READ_REQUESTED && |
1049 | 0 | scp->request_type == SSH_SCP_REQUEST_NEWFILE) |
1050 | 0 | { |
1051 | 0 | rc = ssh_scp_accept_request(scp); |
1052 | 0 | if (rc == SSH_ERROR) { |
1053 | 0 | return rc; |
1054 | 0 | } |
1055 | 0 | } |
1056 | | |
1057 | 0 | if (scp->state != SSH_SCP_READ_READING) { |
1058 | 0 | ssh_set_error(scp->session, SSH_FATAL, |
1059 | 0 | "ssh_scp_read called under invalid state"); |
1060 | 0 | return SSH_ERROR; |
1061 | 0 | } |
1062 | | |
1063 | 0 | if (scp->processed + size > scp->filelen) { |
1064 | 0 | size = (size_t) (scp->filelen - scp->processed); |
1065 | 0 | } |
1066 | |
|
1067 | 0 | if (size > 65536) { |
1068 | 0 | size = 65536; /* avoid too large reads */ |
1069 | 0 | } |
1070 | |
|
1071 | 0 | rc = ssh_channel_read(scp->channel, buffer, size, 0); |
1072 | 0 | if (rc == SSH_ERROR) { |
1073 | 0 | scp->state = SSH_SCP_ERROR; |
1074 | 0 | return SSH_ERROR; |
1075 | 0 | } |
1076 | 0 | if (rc == SSH_AGAIN) { |
1077 | 0 | ssh_set_error(scp->session, SSH_FATAL, "SCP: ssh_channel_read timeout"); |
1078 | 0 | scp->state = SSH_SCP_ERROR; |
1079 | 0 | return SSH_ERROR; |
1080 | 0 | } |
1081 | 0 | scp->processed += rc; |
1082 | | |
1083 | | /* Check if we arrived at end of file */ |
1084 | 0 | if (scp->processed == scp->filelen) { |
1085 | 0 | scp->processed = scp->filelen = 0; |
1086 | 0 | ssh_channel_write(scp->channel, "", 1); |
1087 | 0 | code = ssh_scp_response(scp, NULL); |
1088 | 0 | if (code == 0) { |
1089 | 0 | scp->state = SSH_SCP_READ_INITED; |
1090 | 0 | return rc; |
1091 | 0 | } |
1092 | 0 | if (code == 1) { |
1093 | 0 | scp->state = SSH_SCP_READ_INITED; |
1094 | 0 | return SSH_ERROR; |
1095 | 0 | } |
1096 | 0 | scp->state = SSH_SCP_ERROR; |
1097 | 0 | return SSH_ERROR; |
1098 | 0 | } |
1099 | | |
1100 | 0 | return rc; |
1101 | 0 | } |
1102 | | |
1103 | | /** |
1104 | | * @deprecated Please use SFTP instead |
1105 | | * |
1106 | | * @brief Get the name of the directory or file being pushed from the other |
1107 | | * party. |
1108 | | * |
1109 | | * @returns The file name, NULL on error. The string should not be |
1110 | | * freed. |
1111 | | */ |
1112 | | const char *ssh_scp_request_get_filename(ssh_scp scp) |
1113 | 0 | { |
1114 | 0 | if (scp == NULL) { |
1115 | 0 | return NULL; |
1116 | 0 | } |
1117 | | |
1118 | 0 | return scp->request_name; |
1119 | 0 | } |
1120 | | |
1121 | | /** |
1122 | | * @deprecated Please use SFTP instead |
1123 | | * |
1124 | | * @brief Get the permissions of the directory or file being pushed from the |
1125 | | * other party. |
1126 | | * |
1127 | | * @returns The UNIX permission, e.g 0644, -1 on error. |
1128 | | */ |
1129 | | int ssh_scp_request_get_permissions(ssh_scp scp) |
1130 | 0 | { |
1131 | 0 | if (scp == NULL) { |
1132 | 0 | return -1; |
1133 | 0 | } |
1134 | | |
1135 | 0 | return scp->request_mode; |
1136 | 0 | } |
1137 | | |
1138 | | /** |
1139 | | * @deprecated Please use SFTP instead |
1140 | | * |
1141 | | * @brief Get the size of the file being pushed from the other party. |
1142 | | * |
1143 | | * @returns The numeric size of the file being read. |
1144 | | * @warning The real size may not fit in a 32 bits field and may |
1145 | | * be truncated. |
1146 | | * @see ssh_scp_request_get_size64() |
1147 | | */ |
1148 | | size_t ssh_scp_request_get_size(ssh_scp scp) |
1149 | 0 | { |
1150 | 0 | if (scp == NULL) { |
1151 | 0 | return 0; |
1152 | 0 | } |
1153 | 0 | return (size_t)scp->filelen; |
1154 | 0 | } |
1155 | | |
1156 | | /** |
1157 | | * @deprecated Please use SFTP instead |
1158 | | * |
1159 | | * @brief Get the size of the file being pushed from the other party. |
1160 | | * |
1161 | | * @returns The numeric size of the file being read. |
1162 | | */ |
1163 | | uint64_t ssh_scp_request_get_size64(ssh_scp scp) |
1164 | 0 | { |
1165 | 0 | if (scp == NULL) { |
1166 | 0 | return 0; |
1167 | 0 | } |
1168 | 0 | return scp->filelen; |
1169 | 0 | } |
1170 | | |
1171 | | /** |
1172 | | * @deprecated Please use SFTP instead |
1173 | | * |
1174 | | * @brief Convert a scp text mode to an integer. |
1175 | | * |
1176 | | * @param[in] mode The mode to convert, e.g. "0644". |
1177 | | * |
1178 | | * @returns An integer value, e.g. 420 for "0644". |
1179 | | */ |
1180 | | int ssh_scp_integer_mode(const char *mode) |
1181 | 0 | { |
1182 | 0 | int value = strtoul(mode, NULL, 8) & 0xffff; |
1183 | 0 | return value; |
1184 | 0 | } |
1185 | | |
1186 | | /** |
1187 | | * @deprecated Please use SFTP instead |
1188 | | * |
1189 | | * @brief Convert a unix mode into a scp string. |
1190 | | * |
1191 | | * @param[in] mode The mode to convert, e.g. 420 or 0644. |
1192 | | * |
1193 | | * @returns A pointer to a malloc'ed string containing the scp mode, |
1194 | | * e.g. "0644". |
1195 | | */ |
1196 | | char *ssh_scp_string_mode(int mode) |
1197 | 0 | { |
1198 | 0 | char buffer[16] = {0}; |
1199 | 0 | snprintf(buffer, sizeof(buffer), "%.4o", mode); |
1200 | 0 | return strdup(buffer); |
1201 | 0 | } |
1202 | | |
1203 | | /** |
1204 | | * @deprecated Please use SFTP instead |
1205 | | * |
1206 | | * @brief Get the warning string from a scp handle. |
1207 | | * |
1208 | | * @param[in] scp The scp handle. |
1209 | | * |
1210 | | * @returns A warning string, or NULL on error. The string should |
1211 | | * not be freed. |
1212 | | */ |
1213 | | const char *ssh_scp_request_get_warning(ssh_scp scp) |
1214 | 0 | { |
1215 | 0 | if (scp == NULL) { |
1216 | 0 | return NULL; |
1217 | 0 | } |
1218 | | |
1219 | 0 | return scp->warning; |
1220 | 0 | } |
1221 | | |
1222 | | /** @} */ |