Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl / ssh.c
blob9780d898fbee5a0be1a0e982ddbf99b30feff064
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
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 */
26 #include "setup.h"
28 #ifdef USE_LIBSSH2
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <ctype.h>
34 #include <limits.h>
36 #include <libssh2.h>
37 #include <libssh2_sftp.h>
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
43 #ifdef HAVE_FCNTL_H
44 #include <fcntl.h>
45 #endif
47 #ifdef HAVE_SYS_TYPES_H
48 #include <sys/types.h>
49 #endif
50 #ifdef HAVE_SYS_STAT_H
51 #include <sys/stat.h>
52 #endif
54 #ifdef HAVE_TIME_H
55 #include <time.h>
56 #endif
58 #ifdef WIN32
60 #else /* probably some kind of unix */
61 #ifdef HAVE_SYS_SOCKET_H
62 #include <sys/socket.h>
63 #endif
64 #include <sys/types.h>
65 #ifdef HAVE_NETINET_IN_H
66 #include <netinet/in.h>
67 #endif
68 #ifdef HAVE_ARPA_INET_H
69 #include <arpa/inet.h>
70 #endif
71 #ifdef HAVE_UTSNAME_H
72 #include <sys/utsname.h>
73 #endif
74 #ifdef HAVE_NETDB_H
75 #include <netdb.h>
76 #endif
77 #ifdef VMS
78 #include <in.h>
79 #include <inet.h>
80 #endif
81 #endif
83 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
84 #undef in_addr_t
85 #define in_addr_t unsigned long
86 #endif
88 #include <curl/curl.h>
89 #include "urldata.h"
90 #include "sendf.h"
91 #include "easyif.h" /* for Curl_convert_... prototypes */
93 #include "if2ip.h"
94 #include "hostip.h"
95 #include "progress.h"
96 #include "transfer.h"
97 #include "escape.h"
98 #include "http.h" /* for HTTP proxy tunnel stuff */
99 #include "ssh.h"
100 #include "url.h"
101 #include "speedcheck.h"
102 #include "getinfo.h"
104 #include "strtoofft.h"
105 #include "strequal.h"
106 #include "sslgen.h"
107 #include "connect.h"
108 #include "strerror.h"
109 #include "memory.h"
110 #include "inet_ntop.h"
111 #include "select.h"
112 #include "parsedate.h" /* for the week day and month names */
113 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
114 #include "multiif.h"
116 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
117 #include "inet_ntoa_r.h"
118 #endif
120 #define _MPRINTF_REPLACE /* use our functions only */
121 #include <curl/mprintf.h>
123 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
124 #define DIRSEP '\\'
125 #else
126 #define DIRSEP '/'
127 #endif
129 #define _MPRINTF_REPLACE /* use our functions only */
130 #include <curl/mprintf.h>
132 /* The last #include file should be: */
133 #ifdef CURLDEBUG
134 #include "memdebug.h"
135 #endif
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. */
141 #ifndef S_IRGRP
142 #define S_IRGRP 0
143 #endif
145 #ifndef S_IROTH
146 #define S_IROTH 0
147 #endif
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
168 #endif
170 static LIBSSH2_ALLOC_FUNC(libssh2_malloc);
171 static LIBSSH2_REALLOC_FUNC(libssh2_realloc);
172 static LIBSSH2_FREE_FUNC(libssh2_free);
174 static void
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,
179 void **abstract)
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);
189 #else
190 (void)name;
191 (void)name_len;
192 (void)instruction;
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);
199 (void)prompts;
200 (void)abstract;
201 } /* kbd_callback */
203 static CURLcode libssh2_error_to_CURLE(struct connectdata *conn)
205 int errorcode;
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,
210 NULL, 0);
211 if (errorcode == LIBSSH2_FX_OK)
212 return CURLE_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 */
220 return CURLE_SSH;
223 static LIBSSH2_ALLOC_FUNC(libssh2_malloc)
225 return malloc(count);
226 (void)abstract;
229 static LIBSSH2_REALLOC_FUNC(libssh2_realloc)
231 return realloc(ptr, count);
232 (void)abstract;
235 static LIBSSH2_FREE_FUNC(libssh2_free)
237 free(ptr);
238 (void)abstract;
241 static CURLcode ssh_init(struct connectdata *conn)
243 struct SessionHandle *data = conn->data;
244 struct SSHPROTO *ssh;
245 if (data->reqdata.proto.ssh)
246 return CURLE_OK;
248 ssh = (struct SSHPROTO *)calloc(sizeof(struct SSHPROTO), 1);
249 if (!ssh)
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;
268 return CURLE_OK;
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)
277 int i;
278 struct SSHPROTO *ssh;
279 const char *fingerprint;
280 const char *authlist;
281 char *home;
282 char rsa_pub[PATH_MAX];
283 char rsa[PATH_MAX];
284 char tempHome[PATH_MAX];
285 curl_socket_t sock;
286 char *real_path;
287 char *working_path;
288 int working_path_len;
289 bool authed = FALSE;
290 CURLcode result;
291 struct SessionHandle *data = conn->data;
293 rsa_pub[0] = rsa[0] = '\0';
295 result = ssh_init(conn);
296 if (result)
297 return result;
299 ssh = data->reqdata.proto.ssh;
301 working_path = curl_easy_unescape(data, data->reqdata.path, 0,
302 &working_path_len);
303 if (!working_path)
304 return CURLE_OUT_OF_MEMORY;
306 #ifdef CURL_LIBSSH2_DEBUG
307 if (ssh->user) {
308 infof(data, "User: %s\n", ssh->user);
310 if (ssh->passwd) {
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]);
349 infof(data, "\n");
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,
364 strlen(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);
378 else if (home)
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);
383 else if (home)
384 snprintf(rsa, sizeof(rsa), "%s/.ssh/id_dsa", home);
386 curl_free(home);
388 if (rsa_pub[0]) {
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) {
393 authed = TRUE;
397 if (!authed &&
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))
401 authed = TRUE;
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,
410 strlen(ssh->user),
411 &kbd_callback) == 0) {
412 authed = TRUE;
416 if (!authed) {
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.
427 conn->sockfd = sock;
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);
448 if (i > 0) {
449 /* It seems that this string is not always NULL terminated */
450 tempHome[i] = '\0';
451 ssh->homedir = (char *)strdup(tempHome);
452 if (!ssh->homedir) {
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;
460 else {
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);
479 else
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);
503 else {
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);
514 else
515 return CURLE_FAILED_INIT;
517 Curl_safefree(working_path);
518 ssh->path = real_path;
520 *done = TRUE;
521 return CURLE_OK;
524 CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
526 struct stat sb;
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;
548 /* upload data */
549 res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
551 else {
553 * We must check the remote file, if it is a directory no vaules will
554 * be set in sb
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) &&
561 (sb.st_size == 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);
567 /* download data */
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);
574 return res;
577 CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
578 bool premature)
580 struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
581 (void)premature; /* not used */
583 Curl_safefree(scp->path);
584 scp->path = NULL;
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;
600 Curl_pgrsDone(conn);
602 (void)status; /* unused */
604 return CURLE_OK;
607 /* return number of received (decrypted) bytes */
608 ssize_t Curl_scp_send(struct connectdata *conn, int sockindex,
609 void *mem, size_t len)
611 ssize_t nwrite;
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
618 nwrite = (ssize_t)
619 libssh2_channel_write(conn->data->reqdata.proto.ssh->ssh_channel,
620 mem, len);
621 (void)sockindex;
622 return nwrite;
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)
632 ssize_t nread;
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
640 nread = (ssize_t)
641 libssh2_channel_read(conn->data->reqdata.proto.ssh->ssh_channel,
642 mem, len);
643 (void)sockindex;
644 return nread;
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.
669 sftp->sftp_handle =
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;
677 /* upload data */
678 res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
680 else {
681 if (sftp->path[strlen(sftp->path)-1] == '/') {
683 * This is a directory that we are trying to get, so produce a
684 * directory listing
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;
692 char *line;
694 sftp->sftp_handle =
695 libssh2_sftp_opendir(sftp->sftp_session, sftp->path);
696 if (!sftp->sftp_handle)
697 return CURLE_SSH;
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);
710 else {
711 totalLen = 80 + len;
712 line = (char *)malloc(totalLen);
713 if (!line)
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) {
725 line[0] = 'd';
727 else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
728 LIBSSH2_SFTP_S_IFLNK) {
729 line[0] = 'l';
731 else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
732 LIBSSH2_SFTP_S_IFSOCK) {
733 line[0] = 's';
735 else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
736 LIBSSH2_SFTP_S_IFCHR) {
737 line[0] = 'c';
739 else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
740 LIBSSH2_SFTP_S_IFBLK) {
741 line[0] = 'b';
743 if (attrs.permissions & LIBSSH2_SFTP_S_IRUSR) {
744 line[1] = 'r';
746 if (attrs.permissions & LIBSSH2_SFTP_S_IWUSR) {
747 line[2] = 'w';
749 if (attrs.permissions & LIBSSH2_SFTP_S_IXUSR) {
750 line[3] = 'x';
752 if (attrs.permissions & LIBSSH2_SFTP_S_IRGRP) {
753 line[4] = 'r';
755 if (attrs.permissions & LIBSSH2_SFTP_S_IWGRP) {
756 line[5] = 'w';
758 if (attrs.permissions & LIBSSH2_SFTP_S_IXGRP) {
759 line[6] = 'x';
761 if (attrs.permissions & LIBSSH2_SFTP_S_IROTH) {
762 line[7] = 'r';
764 if (attrs.permissions & LIBSSH2_SFTP_S_IWOTH) {
765 line[8] = 'w';
767 if (attrs.permissions & LIBSSH2_SFTP_S_IXOTH) {
768 line[9] = 'x';
771 if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) {
772 currLen += snprintf(line+currLen, totalLen-currLen, "%11lld",
773 attrs.filesize);
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"};
779 struct tm *nowParts;
780 time_t now, remoteTime;
782 now = time(NULL);
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,
790 nowParts->tm_min);
792 else {
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,
806 PATH_MAX);
807 line = realloc(line, totalLen + 4 + len);
808 if (!line)
809 return CURLE_OUT_OF_MEMORY;
811 currLen += snprintf(line+currLen, totalLen-currLen, " -> %s",
812 filename);
815 currLen += snprintf(line+currLen, totalLen-currLen, "\n");
816 res = Curl_client_write(conn, CLIENTWRITE_BODY, line, 0);
817 free(line);
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);
826 else {
828 * Work on getting the specified file
830 sftp->sftp_handle =
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)
835 return CURLE_SSH;
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;
845 else {
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. */
856 #if 0
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);
861 #endif
862 while (res == CURLE_OK) {
863 size_t nread;
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,
867 buf, BUFSIZE-1);
869 if (nread > 0)
870 buf[nread] = 0;
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))
875 break;
877 bytecount += nread;
879 res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
880 if(res)
881 return res;
883 Curl_pgrsSetDownloadCounter(data, bytecount);
885 if(Curl_pgrsUpdate(conn))
886 res = CURLE_ABORTED_BY_CALLBACK;
887 else {
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);
900 return res;
903 CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status,
904 bool premature)
906 struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
907 (void)premature; /* not used */
909 Curl_safefree(sftp->path);
910 sftp->path = NULL;
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;
941 Curl_pgrsDone(conn);
943 (void)status; /* unused */
945 return CURLE_OK;
948 /* return number of received (decrypted) bytes */
949 ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex,
950 void *mem, size_t len)
952 ssize_t nwrite;
954 /* libssh2_sftp_write() returns size_t !*/
956 nwrite = (ssize_t)
957 libssh2_sftp_write(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
958 (void)sockindex;
959 return nwrite;
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)
969 ssize_t nread;
971 /* libssh2_sftp_read() returns size_t !*/
973 nread = (ssize_t)
974 libssh2_sftp_read(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
975 (void)sockindex;
976 return nread;
979 #endif /* USE_LIBSSH2 */