1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: ssh.c,v 1.2 2007/03/15 19:22:13 andy Exp $
22 ***************************************************************************/
24 /* #define CURL_LIBSSH2_DEBUG */
37 #include <libssh2_sftp.h>
47 #ifdef HAVE_SYS_TYPES_H
48 #include <sys/types.h>
50 #ifdef HAVE_SYS_STAT_H
60 #else /* probably some kind of unix */
61 #ifdef HAVE_SYS_SOCKET_H
62 #include <sys/socket.h>
64 #include <sys/types.h>
65 #ifdef HAVE_NETINET_IN_H
66 #include <netinet/in.h>
68 #ifdef HAVE_ARPA_INET_H
69 #include <arpa/inet.h>
72 #include <sys/utsname.h>
83 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
85 #define in_addr_t unsigned long
88 #include <curl/curl.h>
91 #include "easyif.h" /* for Curl_convert_... prototypes */
98 #include "http.h" /* for HTTP proxy tunnel stuff */
101 #include "speedcheck.h"
104 #include "strtoofft.h"
105 #include "strequal.h"
108 #include "strerror.h"
110 #include "inet_ntop.h"
112 #include "parsedate.h" /* for the week day and month names */
113 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
116 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
117 #include "inet_ntoa_r.h"
120 #define _MPRINTF_REPLACE /* use our functions only */
121 #include <curl/mprintf.h>
123 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
129 #define _MPRINTF_REPLACE /* use our functions only */
130 #include <curl/mprintf.h>
132 /* The last #include file should be: */
134 #include "memdebug.h"
137 #ifndef LIBSSH2_SFTP_S_IRUSR
138 /* Here's a work-around for those of you who happend to run a libssh2 version
139 that is 0.14 or older. We should remove this kludge as soon as we can
140 require a more recent libssh2 release. */
149 #define LIBSSH2_SFTP_S_IRUSR S_IRUSR
150 #define LIBSSH2_SFTP_S_IWUSR S_IWUSR
151 #define LIBSSH2_SFTP_S_IRGRP S_IRGRP
152 #define LIBSSH2_SFTP_S_IROTH S_IROTH
153 #define LIBSSH2_SFTP_S_IRUSR S_IRUSR
154 #define LIBSSH2_SFTP_S_IWUSR S_IWUSR
155 #define LIBSSH2_SFTP_S_IRGRP S_IRGRP
156 #define LIBSSH2_SFTP_S_IROTH S_IROTH
157 #define LIBSSH2_SFTP_S_IFMT S_IFMT
158 #define LIBSSH2_SFTP_S_IFDIR S_IFDIR
159 #define LIBSSH2_SFTP_S_IFLNK S_IFLNK
160 #define LIBSSH2_SFTP_S_IFSOCK S_IFSOCK
161 #define LIBSSH2_SFTP_S_IFCHR S_IFCHR
162 #define LIBSSH2_SFTP_S_IFBLK S_IFBLK
163 #define LIBSSH2_SFTP_S_IXUSR S_IXUSR
164 #define LIBSSH2_SFTP_S_IWGRP S_IWGRP
165 #define LIBSSH2_SFTP_S_IXGRP S_IXGRP
166 #define LIBSSH2_SFTP_S_IWOTH S_IWOTH
167 #define LIBSSH2_SFTP_S_IXOTH S_IXOTH
170 static LIBSSH2_ALLOC_FUNC(libssh2_malloc
);
171 static LIBSSH2_REALLOC_FUNC(libssh2_realloc
);
172 static LIBSSH2_FREE_FUNC(libssh2_free
);
175 kbd_callback(const char *name
, int name_len
, const char *instruction
,
176 int instruction_len
, int num_prompts
,
177 const LIBSSH2_USERAUTH_KBDINT_PROMPT
*prompts
,
178 LIBSSH2_USERAUTH_KBDINT_RESPONSE
*responses
,
181 struct SSHPROTO
*ssh
= (struct SSHPROTO
*)*abstract
;
183 #ifdef CURL_LIBSSH2_DEBUG
184 fprintf(stderr
, "name=%s\n", name
);
185 fprintf(stderr
, "name_len=%d\n", name_len
);
186 fprintf(stderr
, "instruction=%s\n", instruction
);
187 fprintf(stderr
, "instruction_len=%d\n", instruction_len
);
188 fprintf(stderr
, "num_prompts=%d\n", num_prompts
);
193 (void)instruction_len
;
194 #endif /* CURL_LIBSSH2_DEBUG */
195 if (num_prompts
== 1) {
196 responses
[0].text
= strdup(ssh
->passwd
);
197 responses
[0].length
= strlen(ssh
->passwd
);
203 static CURLcode
libssh2_error_to_CURLE(struct connectdata
*conn
)
206 struct SSHPROTO
*scp
= conn
->data
->reqdata
.proto
.ssh
;
208 /* Get the libssh2 error code and string */
209 errorcode
= libssh2_session_last_error(scp
->ssh_session
, &scp
->errorstr
,
211 if (errorcode
== LIBSSH2_FX_OK
)
214 infof(conn
->data
, "libssh2 error %d, '%s'\n", errorcode
, scp
->errorstr
);
216 /* TODO: map some of the libssh2 errors to the more appropriate CURLcode
217 error code, and possibly add a few new SSH-related one. We must however
218 not return or even depend on libssh2 errors in the public libcurl API */
223 static LIBSSH2_ALLOC_FUNC(libssh2_malloc
)
225 return malloc(count
);
229 static LIBSSH2_REALLOC_FUNC(libssh2_realloc
)
231 return realloc(ptr
, count
);
235 static LIBSSH2_FREE_FUNC(libssh2_free
)
241 static CURLcode
ssh_init(struct connectdata
*conn
)
243 struct SessionHandle
*data
= conn
->data
;
244 struct SSHPROTO
*ssh
;
245 if (data
->reqdata
.proto
.ssh
)
248 ssh
= (struct SSHPROTO
*)calloc(sizeof(struct SSHPROTO
), 1);
250 return CURLE_OUT_OF_MEMORY
;
252 data
->reqdata
.proto
.ssh
= ssh
;
254 /* get some initial data into the ssh struct */
255 ssh
->bytecountp
= &data
->reqdata
.keep
.bytecount
;
257 /* no need to duplicate them, this connectdata struct won't change */
258 ssh
->user
= conn
->user
;
259 ssh
->passwd
= conn
->passwd
;
261 ssh
->errorstr
= NULL
;
263 ssh
->ssh_session
= NULL
;
264 ssh
->ssh_channel
= NULL
;
265 ssh
->sftp_session
= NULL
;
266 ssh
->sftp_handle
= NULL
;
272 * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
273 * do protocol-specific actions at connect-time.
275 CURLcode
Curl_ssh_connect(struct connectdata
*conn
, bool *done
)
278 struct SSHPROTO
*ssh
;
279 const char *fingerprint
;
280 const char *authlist
;
282 char rsa_pub
[PATH_MAX
];
284 char tempHome
[PATH_MAX
];
288 int working_path_len
;
291 struct SessionHandle
*data
= conn
->data
;
293 rsa_pub
[0] = rsa
[0] = '\0';
295 result
= ssh_init(conn
);
299 ssh
= data
->reqdata
.proto
.ssh
;
301 working_path
= curl_easy_unescape(data
, data
->reqdata
.path
, 0,
304 return CURLE_OUT_OF_MEMORY
;
306 #ifdef CURL_LIBSSH2_DEBUG
308 infof(data
, "User: %s\n", ssh
->user
);
311 infof(data
, "Password: %s\n", ssh
->passwd
);
313 #endif /* CURL_LIBSSH2_DEBUG */
314 sock
= conn
->sock
[FIRSTSOCKET
];
315 ssh
->ssh_session
= libssh2_session_init_ex(libssh2_malloc
, libssh2_free
,
316 libssh2_realloc
, ssh
);
317 if (ssh
->ssh_session
== NULL
) {
318 failf(data
, "Failure initialising ssh session\n");
319 Curl_safefree(ssh
->path
);
320 return CURLE_FAILED_INIT
;
322 #ifdef CURL_LIBSSH2_DEBUG
323 infof(data
, "SSH socket: %d\n", sock
);
324 #endif /* CURL_LIBSSH2_DEBUG */
326 if (libssh2_session_startup(ssh
->ssh_session
, sock
)) {
327 failf(data
, "Failure establishing ssh session\n");
328 libssh2_session_free(ssh
->ssh_session
);
329 ssh
->ssh_session
= NULL
;
330 Curl_safefree(ssh
->path
);
331 return CURLE_FAILED_INIT
;
335 * Before we authenticate we should check the hostkey's fingerprint against
336 * our known hosts. How that is handled (reading from file, whatever) is
337 * up to us. As for know not much is implemented, besides showing how to
338 * get the fingerprint.
340 fingerprint
= libssh2_hostkey_hash(ssh
->ssh_session
,
341 LIBSSH2_HOSTKEY_HASH_MD5
);
343 #ifdef CURL_LIBSSH2_DEBUG
344 /* The fingerprint points to static storage (!), don't free() it. */
345 infof(data
, "Fingerprint: ");
346 for (i
= 0; i
< 16; i
++) {
347 infof(data
, "%02X ", (unsigned char) fingerprint
[i
]);
350 #endif /* CURL_LIBSSH2_DEBUG */
352 /* TBD - methods to check the host keys need to be done */
355 * Figure out authentication methods
356 * NB: As soon as we have provided a username to an openssh server we must
357 * never change it later. Thus, always specify the correct username here,
358 * even though the libssh2 docs kind of indicate that it should be possible
359 * to get a 'generic' list (not user-specific) of authentication methods,
360 * presumably with a blank username. That won't work in my experience.
361 * So always specify it here.
363 authlist
= libssh2_userauth_list(ssh
->ssh_session
, ssh
->user
,
367 * Check the supported auth types in the order I feel is most secure with the
368 * requested type of authentication
370 if ((data
->set
.ssh_auth_types
& CURLSSH_AUTH_PUBLICKEY
) &&
371 (strstr(authlist
, "publickey") != NULL
)) {
372 /* To ponder about: should really the lib be messing about with the HOME
373 environment variable etc? */
374 home
= curl_getenv("HOME");
376 if (data
->set
.ssh_public_key
)
377 snprintf(rsa_pub
, sizeof(rsa_pub
), "%s", data
->set
.ssh_public_key
);
379 snprintf(rsa_pub
, sizeof(rsa_pub
), "%s/.ssh/id_dsa.pub", home
);
381 if (data
->set
.ssh_private_key
)
382 snprintf(rsa
, sizeof(rsa
), "%s", data
->set
.ssh_private_key
);
384 snprintf(rsa
, sizeof(rsa
), "%s/.ssh/id_dsa", home
);
389 /* The function below checks if the files exists, no need to stat() here.
391 if (libssh2_userauth_publickey_fromfile(ssh
->ssh_session
, ssh
->user
,
392 rsa_pub
, rsa
, "") == 0) {
398 (data
->set
.ssh_auth_types
& CURLSSH_AUTH_PASSWORD
) &&
399 (strstr(authlist
, "password") != NULL
)) {
400 if (!libssh2_userauth_password(ssh
->ssh_session
, ssh
->user
, ssh
->passwd
))
403 if (!authed
&& (data
->set
.ssh_auth_types
& CURLSSH_AUTH_HOST
) &&
404 (strstr(authlist
, "hostbased") != NULL
)) {
406 if (!authed
&& (data
->set
.ssh_auth_types
& CURLSSH_AUTH_KEYBOARD
)
407 && (strstr(authlist
, "keyboard-interactive") != NULL
)) {
408 /* Authentication failed. Continue with keyboard-interactive now. */
409 if (libssh2_userauth_keyboard_interactive_ex(ssh
->ssh_session
, ssh
->user
,
411 &kbd_callback
) == 0) {
417 failf(data
, "Authentication failure\n");
418 libssh2_session_free(ssh
->ssh_session
);
419 ssh
->ssh_session
= NULL
;
420 Curl_safefree(ssh
->path
);
421 return CURLE_FAILED_INIT
;
425 * At this point we have an authenticated ssh session.
428 conn
->writesockfd
= CURL_SOCKET_BAD
;
430 if (conn
->protocol
== PROT_SFTP
) {
432 * Start the libssh2 sftp session
434 ssh
->sftp_session
= libssh2_sftp_init(ssh
->ssh_session
);
435 if (ssh
->sftp_session
== NULL
) {
436 failf(data
, "Failure initialising sftp session\n");
437 libssh2_sftp_shutdown(ssh
->sftp_session
);
438 ssh
->sftp_session
= NULL
;
439 libssh2_session_free(ssh
->ssh_session
);
440 ssh
->ssh_session
= NULL
;
441 return CURLE_FAILED_INIT
;
445 * Get the "home" directory
447 i
= libssh2_sftp_realpath(ssh
->sftp_session
, ".", tempHome
, PATH_MAX
-1);
449 /* It seems that this string is not always NULL terminated */
451 ssh
->homedir
= (char *)strdup(tempHome
);
453 libssh2_sftp_shutdown(ssh
->sftp_session
);
454 ssh
->sftp_session
= NULL
;
455 libssh2_session_free(ssh
->ssh_session
);
456 ssh
->ssh_session
= NULL
;
457 return CURLE_OUT_OF_MEMORY
;
461 /* Return the error type */
462 i
= libssh2_sftp_last_error(ssh
->sftp_session
);
463 DEBUGF(infof(data
, "error = %d\n", i
));
467 /* Check for /~/ , indicating realative to the users home directory */
468 if (conn
->protocol
== PROT_SCP
) {
469 real_path
= (char *)malloc(working_path_len
+1);
470 if (real_path
== NULL
) {
471 Curl_safefree(working_path
);
472 libssh2_session_free(ssh
->ssh_session
);
473 ssh
->ssh_session
= NULL
;
474 return CURLE_OUT_OF_MEMORY
;
476 if (working_path
[1] == '~')
477 /* It is referenced to the home directory, so strip the leading '/' */
478 memcpy(real_path
, working_path
+1, 1 + working_path_len
-1);
480 memcpy(real_path
, working_path
, 1 + working_path_len
);
482 else if (conn
->protocol
== PROT_SFTP
) {
483 if (working_path
[1] == '~') {
484 real_path
= (char *)malloc(strlen(ssh
->homedir
) +
485 working_path_len
+ 1);
486 if (real_path
== NULL
) {
487 libssh2_sftp_shutdown(ssh
->sftp_session
);
488 ssh
->sftp_session
= NULL
;
489 libssh2_session_free(ssh
->ssh_session
);
490 ssh
->ssh_session
= NULL
;
491 Curl_safefree(working_path
);
492 return CURLE_OUT_OF_MEMORY
;
494 /* It is referenced to the home directory, so strip the leading '/' */
495 memcpy(real_path
, ssh
->homedir
, strlen(ssh
->homedir
));
496 real_path
[strlen(ssh
->homedir
)] = '/';
497 real_path
[strlen(ssh
->homedir
)+1] = '\0';
498 if (working_path_len
> 3) {
499 memcpy(real_path
+strlen(ssh
->homedir
)+1, working_path
+ 3,
500 1 + working_path_len
-3);
504 real_path
= (char *)malloc(working_path_len
+1);
505 if (real_path
== NULL
) {
506 libssh2_session_free(ssh
->ssh_session
);
507 ssh
->ssh_session
= NULL
;
508 Curl_safefree(working_path
);
509 return CURLE_OUT_OF_MEMORY
;
511 memcpy(real_path
, working_path
, 1+working_path_len
);
515 return CURLE_FAILED_INIT
;
517 Curl_safefree(working_path
);
518 ssh
->path
= real_path
;
524 CURLcode
Curl_scp_do(struct connectdata
*conn
, bool *done
)
527 struct SSHPROTO
*scp
= conn
->data
->reqdata
.proto
.ssh
;
528 CURLcode res
= CURLE_OK
;
530 *done
= TRUE
; /* unconditionally */
532 if (conn
->data
->set
.upload
) {
534 * NOTE!!! libssh2 requires that the destination path is a full path
535 * that includes the destination file and name OR ends in a "/" .
536 * If this is not done the destination file will be named the
537 * same name as the last directory in the path.
539 scp
->ssh_channel
= libssh2_scp_send_ex(scp
->ssh_session
, scp
->path
,
540 LIBSSH2_SFTP_S_IRUSR
|
541 LIBSSH2_SFTP_S_IWUSR
|
542 LIBSSH2_SFTP_S_IRGRP
|
543 LIBSSH2_SFTP_S_IROTH
,
544 conn
->data
->set
.infilesize
, 0, 0);
545 if (!scp
->ssh_channel
)
546 return CURLE_FAILED_INIT
;
549 res
= Curl_setup_transfer(conn
, -1, -1, FALSE
, NULL
, FIRSTSOCKET
, NULL
);
553 * We must check the remote file, if it is a directory no vaules will
556 curl_off_t bytecount
;
557 memset(&sb
, 0, sizeof(struct stat
));
558 scp
->ssh_channel
= libssh2_scp_recv(scp
->ssh_session
, scp
->path
, &sb
);
559 if (!scp
->ssh_channel
) {
560 if ((sb
.st_mode
== 0) && (sb
.st_atime
== 0) && (sb
.st_mtime
== 0) &&
562 /* Since sb is still empty, it is likely the file was not found */
563 return CURLE_REMOTE_FILE_NOT_FOUND
;
565 return libssh2_error_to_CURLE(conn
);
568 bytecount
= (curl_off_t
) sb
.st_size
;
569 conn
->data
->reqdata
.maxdownload
= (curl_off_t
) sb
.st_size
;
570 res
= Curl_setup_transfer(conn
, FIRSTSOCKET
,
571 bytecount
, FALSE
, NULL
, -1, NULL
);
577 CURLcode
Curl_scp_done(struct connectdata
*conn
, CURLcode status
,
580 struct SSHPROTO
*scp
= conn
->data
->reqdata
.proto
.ssh
;
581 (void)premature
; /* not used */
583 Curl_safefree(scp
->path
);
586 if (scp
->ssh_channel
) {
587 if (libssh2_channel_close(scp
->ssh_channel
) < 0) {
588 infof(conn
->data
, "Failed to stop libssh2 channel subsystem\n");
592 if (scp
->ssh_session
) {
593 libssh2_session_disconnect(scp
->ssh_session
, "Shutdown");
594 libssh2_session_free(scp
->ssh_session
);
595 scp
->ssh_session
= NULL
;
598 free(conn
->data
->reqdata
.proto
.ssh
);
599 conn
->data
->reqdata
.proto
.ssh
= NULL
;
602 (void)status
; /* unused */
607 /* return number of received (decrypted) bytes */
608 ssize_t
Curl_scp_send(struct connectdata
*conn
, int sockindex
,
609 void *mem
, size_t len
)
613 /* libssh2_channel_write() returns int
615 * NOTE: we should not store nor rely on connection-related data to be
616 * in the SessionHandle struct
619 libssh2_channel_write(conn
->data
->reqdata
.proto
.ssh
->ssh_channel
,
626 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
627 * a regular CURLcode value.
629 ssize_t
Curl_scp_recv(struct connectdata
*conn
, int sockindex
,
630 char *mem
, size_t len
)
634 /* libssh2_channel_read() returns int
636 * NOTE: we should not store nor rely on connection-related data to be
637 * in the SessionHandle struct
641 libssh2_channel_read(conn
->data
->reqdata
.proto
.ssh
->ssh_channel
,
648 * =============== SFTP ===============
651 CURLcode
Curl_sftp_do(struct connectdata
*conn
, bool *done
)
653 LIBSSH2_SFTP_ATTRIBUTES attrs
;
654 struct SSHPROTO
*sftp
= conn
->data
->reqdata
.proto
.ssh
;
655 CURLcode res
= CURLE_OK
;
656 struct SessionHandle
*data
= conn
->data
;
657 curl_off_t bytecount
= 0;
658 char *buf
= data
->state
.buffer
;
660 *done
= TRUE
; /* unconditionally */
662 if (data
->set
.upload
) {
664 * NOTE!!! libssh2 requires that the destination path is a full path
665 * that includes the destination file and name OR ends in a "/" .
666 * If this is not done the destination file will be named the
667 * same name as the last directory in the path.
670 libssh2_sftp_open(sftp
->sftp_session
, sftp
->path
,
671 LIBSSH2_FXF_WRITE
|LIBSSH2_FXF_CREAT
,
672 LIBSSH2_SFTP_S_IRUSR
|LIBSSH2_SFTP_S_IWUSR
|
673 LIBSSH2_SFTP_S_IRGRP
|LIBSSH2_SFTP_S_IROTH
);
674 if (!sftp
->sftp_handle
)
675 return CURLE_FAILED_INIT
;
678 res
= Curl_setup_transfer(conn
, -1, -1, FALSE
, NULL
, FIRSTSOCKET
, NULL
);
681 if (sftp
->path
[strlen(sftp
->path
)-1] == '/') {
683 * This is a directory that we are trying to get, so produce a
686 * **BLOCKING behaviour** This should be made into a state machine and
687 * get a separate function called from Curl_sftp_recv() when there is
688 * data to read from the network, instead of "hanging" here.
690 char filename
[PATH_MAX
+1];
691 int len
, totalLen
, currLen
;
695 libssh2_sftp_opendir(sftp
->sftp_session
, sftp
->path
);
696 if (!sftp
->sftp_handle
)
699 while ((len
= libssh2_sftp_readdir(sftp
->sftp_handle
, filename
,
700 PATH_MAX
, &attrs
)) > 0) {
701 filename
[len
] = '\0';
703 if (data
->set
.ftp_list_only
) {
704 if ((attrs
.flags
& LIBSSH2_SFTP_ATTR_PERMISSIONS
) &&
705 ((attrs
.permissions
& LIBSSH2_SFTP_S_IFMT
) ==
706 LIBSSH2_SFTP_S_IFDIR
)) {
707 infof(data
, "%s\n", filename
);
712 line
= (char *)malloc(totalLen
);
714 return CURLE_OUT_OF_MEMORY
;
716 if (!(attrs
.flags
& LIBSSH2_SFTP_ATTR_UIDGID
))
717 attrs
.uid
= attrs
.gid
=0;
719 currLen
= snprintf(line
, totalLen
, "---------- 1 %5d %5d",
720 attrs
.uid
, attrs
.gid
);
722 if (attrs
.flags
& LIBSSH2_SFTP_ATTR_PERMISSIONS
) {
723 if ((attrs
.permissions
& LIBSSH2_SFTP_S_IFMT
) ==
724 LIBSSH2_SFTP_S_IFDIR
) {
727 else if ((attrs
.permissions
& LIBSSH2_SFTP_S_IFMT
) ==
728 LIBSSH2_SFTP_S_IFLNK
) {
731 else if ((attrs
.permissions
& LIBSSH2_SFTP_S_IFMT
) ==
732 LIBSSH2_SFTP_S_IFSOCK
) {
735 else if ((attrs
.permissions
& LIBSSH2_SFTP_S_IFMT
) ==
736 LIBSSH2_SFTP_S_IFCHR
) {
739 else if ((attrs
.permissions
& LIBSSH2_SFTP_S_IFMT
) ==
740 LIBSSH2_SFTP_S_IFBLK
) {
743 if (attrs
.permissions
& LIBSSH2_SFTP_S_IRUSR
) {
746 if (attrs
.permissions
& LIBSSH2_SFTP_S_IWUSR
) {
749 if (attrs
.permissions
& LIBSSH2_SFTP_S_IXUSR
) {
752 if (attrs
.permissions
& LIBSSH2_SFTP_S_IRGRP
) {
755 if (attrs
.permissions
& LIBSSH2_SFTP_S_IWGRP
) {
758 if (attrs
.permissions
& LIBSSH2_SFTP_S_IXGRP
) {
761 if (attrs
.permissions
& LIBSSH2_SFTP_S_IROTH
) {
764 if (attrs
.permissions
& LIBSSH2_SFTP_S_IWOTH
) {
767 if (attrs
.permissions
& LIBSSH2_SFTP_S_IXOTH
) {
771 if (attrs
.flags
& LIBSSH2_SFTP_ATTR_SIZE
) {
772 currLen
+= snprintf(line
+currLen
, totalLen
-currLen
, "%11lld",
775 if (attrs
.flags
& LIBSSH2_SFTP_ATTR_ACMODTIME
) {
776 const char *months
[12] = {
777 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
778 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
780 time_t now
, remoteTime
;
783 remoteTime
= (time_t)attrs
.mtime
;
784 nowParts
= localtime(&remoteTime
);
786 if ((time_t)attrs
.mtime
> (now
- (3600 * 24 * 180))) {
787 currLen
+= snprintf(line
+currLen
, totalLen
-currLen
,
788 " %s %2d %2d:%02d", months
[nowParts
->tm_mon
],
789 nowParts
->tm_mday
, nowParts
->tm_hour
,
793 currLen
+= snprintf(line
+currLen
, totalLen
-currLen
,
794 " %s %2d %5d", months
[nowParts
->tm_mon
],
795 nowParts
->tm_mday
, 1900+nowParts
->tm_year
);
798 currLen
+= snprintf(line
+currLen
, totalLen
-currLen
, " %s", filename
);
799 if ((attrs
.flags
& LIBSSH2_SFTP_ATTR_PERMISSIONS
) &&
800 ((attrs
.permissions
& LIBSSH2_SFTP_S_IFMT
) ==
801 LIBSSH2_SFTP_S_IFLNK
)) {
802 char linkPath
[PATH_MAX
+ 1];
804 snprintf(linkPath
, PATH_MAX
, "%s%s", sftp
->path
, filename
);
805 len
= libssh2_sftp_readlink(sftp
->sftp_session
, linkPath
, filename
,
807 line
= realloc(line
, totalLen
+ 4 + len
);
809 return CURLE_OUT_OF_MEMORY
;
811 currLen
+= snprintf(line
+currLen
, totalLen
-currLen
, " -> %s",
815 currLen
+= snprintf(line
+currLen
, totalLen
-currLen
, "\n");
816 res
= Curl_client_write(conn
, CLIENTWRITE_BODY
, line
, 0);
820 libssh2_sftp_closedir(sftp
->sftp_handle
);
821 sftp
->sftp_handle
= NULL
;
823 /* no data to transfer */
824 res
= Curl_setup_transfer(conn
, -1, -1, FALSE
, NULL
, -1, NULL
);
828 * Work on getting the specified file
831 libssh2_sftp_open(sftp
->sftp_session
, sftp
->path
, LIBSSH2_FXF_READ
,
832 LIBSSH2_SFTP_S_IRUSR
|LIBSSH2_SFTP_S_IWUSR
|
833 LIBSSH2_SFTP_S_IRGRP
|LIBSSH2_SFTP_S_IROTH
);
834 if (!sftp
->sftp_handle
)
837 if (libssh2_sftp_stat(sftp
->sftp_session
, sftp
->path
, &attrs
)) {
839 * libssh2_sftp_open() didn't return an error, so maybe the server
840 * just doesn't support stat()
842 data
->reqdata
.size
= -1;
843 data
->reqdata
.maxdownload
= -1;
846 data
->reqdata
.size
= attrs
.filesize
;
847 data
->reqdata
.maxdownload
= attrs
.filesize
;
848 Curl_pgrsSetDownloadSize(data
, attrs
.filesize
);
851 Curl_pgrsTime(data
, TIMER_STARTTRANSFER
);
853 /* Now download data. The libssh2 0.14 doesn't offer any way to do this
854 without using this BLOCKING approach, so here's room for improvement
855 once libssh2 can return EWOULDBLOCK to us. */
857 /* code left here just because this is what this function will use the
858 day libssh2 is improved */
859 res
= Curl_setup_transfer(conn
, FIRSTSOCKET
,
860 bytecount
, FALSE
, NULL
, -1, NULL
);
862 while (res
== CURLE_OK
) {
864 /* NOTE: most *read() functions return ssize_t but this returns size_t
865 which normally is unsigned! */
866 nread
= libssh2_sftp_read(data
->reqdata
.proto
.ssh
->sftp_handle
,
872 /* this check can be changed to a <= 0 when nread is changed to a
873 signed variable type */
874 if ((nread
== 0) || (nread
== (size_t)~0))
879 res
= Curl_client_write(conn
, CLIENTWRITE_BODY
, buf
, nread
);
883 Curl_pgrsSetDownloadCounter(data
, bytecount
);
885 if(Curl_pgrsUpdate(conn
))
886 res
= CURLE_ABORTED_BY_CALLBACK
;
888 struct timeval now
= Curl_tvnow();
889 res
= Curl_speedcheck(data
, now
);
892 if(Curl_pgrsUpdate(conn
))
893 res
= CURLE_ABORTED_BY_CALLBACK
;
895 /* no (more) data to transfer */
896 res
= Curl_setup_transfer(conn
, -1, -1, FALSE
, NULL
, -1, NULL
);
903 CURLcode
Curl_sftp_done(struct connectdata
*conn
, CURLcode status
,
906 struct SSHPROTO
*sftp
= conn
->data
->reqdata
.proto
.ssh
;
907 (void)premature
; /* not used */
909 Curl_safefree(sftp
->path
);
912 Curl_safefree(sftp
->homedir
);
913 sftp
->homedir
= NULL
;
915 if (sftp
->sftp_handle
) {
916 if (libssh2_sftp_close(sftp
->sftp_handle
) < 0) {
917 infof(conn
->data
, "Failed to close libssh2 file\n");
921 if (sftp
->sftp_session
) {
922 if (libssh2_sftp_shutdown(sftp
->sftp_session
) < 0) {
923 infof(conn
->data
, "Failed to stop libssh2 sftp subsystem\n");
927 if (sftp
->ssh_channel
) {
928 if (libssh2_channel_close(sftp
->ssh_channel
) < 0) {
929 infof(conn
->data
, "Failed to stop libssh2 channel subsystem\n");
933 if (sftp
->ssh_session
) {
934 libssh2_session_disconnect(sftp
->ssh_session
, "Shutdown");
935 libssh2_session_free(sftp
->ssh_session
);
936 sftp
->ssh_session
= NULL
;
939 free(conn
->data
->reqdata
.proto
.ssh
);
940 conn
->data
->reqdata
.proto
.ssh
= NULL
;
943 (void)status
; /* unused */
948 /* return number of received (decrypted) bytes */
949 ssize_t
Curl_sftp_send(struct connectdata
*conn
, int sockindex
,
950 void *mem
, size_t len
)
954 /* libssh2_sftp_write() returns size_t !*/
957 libssh2_sftp_write(conn
->data
->reqdata
.proto
.ssh
->sftp_handle
, mem
, len
);
963 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
964 * a regular CURLcode value.
966 ssize_t
Curl_sftp_recv(struct connectdata
*conn
, int sockindex
,
967 char *mem
, size_t len
)
971 /* libssh2_sftp_read() returns size_t !*/
974 libssh2_sftp_read(conn
->data
->reqdata
.proto
.ssh
->sftp_handle
, mem
, len
);
979 #endif /* USE_LIBSSH2 */