Newer
Older
* presumably with a blank username. That won't work in my experience.
* So always specify it here.
*/
authlist = libssh2_userauth_list(ssh->ssh_session, ssh->user,
strlen(ssh->user));
if (!authlist) {
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
return CURLE_OUT_OF_MEMORY;
}
infof(data, "SSH authentication methods available: %s\n", authlist);
James Housley
committed
* Check the supported auth types in the order I feel is most secure with
* the requested type of authentication
*/
if ((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
(strstr(authlist, "publickey") != NULL)) {
const char *passphrase;
char rsa_pub[PATH_MAX];
char rsa[PATH_MAX];
rsa_pub[0] = rsa[0] = '\0';
/* To ponder about: should really the lib be messing about with the HOME
environment variable etc? */
home = curl_getenv("HOME");
if (data->set.ssh_public_key)
snprintf(rsa_pub, sizeof(rsa_pub), "%s", data->set.ssh_public_key);
else if (home)
snprintf(rsa_pub, sizeof(rsa_pub), "%s/.ssh/id_dsa.pub", home);
if (data->set.ssh_private_key)
snprintf(rsa, sizeof(rsa), "%s", data->set.ssh_private_key);
else if (home)
snprintf(rsa, sizeof(rsa), "%s/.ssh/id_dsa", home);
passphrase = data->set.key_passwd;
if (!passphrase)
passphrase = "";
James Housley
committed
Curl_safefree(home);
James Housley
committed
infof(data, "Using ssh public key file %s\n", rsa_pub);
infof(data, "Using ssh private key file %s\n", rsa);
if (rsa_pub[0]) {
/* The function below checks if the files exists, no need to stat() here.
if (libssh2_userauth_publickey_fromfile(ssh->ssh_session, ssh->user,
rsa_pub, rsa, passphrase) == 0) {
James Housley
committed
infof(data, "Initialized SSH public key authentication\n");
}
}
}
if (!authed &&
(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
(strstr(authlist, "password") != NULL)) {
if (!libssh2_userauth_password(ssh->ssh_session, ssh->user, ssh->passwd)) {
James Housley
committed
infof(data, "Initialized password authentication\n");
}
if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
(strstr(authlist, "hostbased") != NULL)) {
}
if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
&& (strstr(authlist, "keyboard-interactive") != NULL)) {
/* Authentication failed. Continue with keyboard-interactive now. */
if (!libssh2_userauth_keyboard_interactive_ex(ssh->ssh_session, ssh->user,
strlen(ssh->user),
&kbd_callback)) {
James Housley
committed
infof(data, "Initialized keyboard interactive authentication\n");
Curl_safefree((void *)authlist);
authlist = NULL;
failf(data, "Authentication failure");
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
}
/*
* At this point we have an authenticated ssh session.
*/
James Housley
committed
infof(data, "Authentication complete\n");
conn->sockfd = sock;
conn->writesockfd = CURL_SOCKET_BAD;
if (conn->protocol == PROT_SFTP) {
/*
* Start the libssh2 sftp session
*/
ssh->sftp_session = libssh2_sftp_init(ssh->ssh_session);
if (ssh->sftp_session == NULL) {
failf(data, "Failure initialising sftp session\n");
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
return CURLE_FAILED_INIT;
}
/*
* Get the "home" directory
*/
i = libssh2_sftp_realpath(ssh->sftp_session, ".", tempHome, PATH_MAX-1);
if (i > 0) {
/* It seems that this string is not always NULL terminated */
tempHome[i] = '\0';
ssh->homedir = (char *)strdup(tempHome);
if (!ssh->homedir) {
libssh2_sftp_shutdown(ssh->sftp_session);
ssh->sftp_session = NULL;
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
return CURLE_OUT_OF_MEMORY;
}
}
else {
/* Return the error type */
i = libssh2_sftp_last_error(ssh->sftp_session);
DEBUGF(infof(data, "error = %d\n", i));
}
}
working_path = curl_easy_unescape(data, data->reqdata.path, 0,
&working_path_len);
if (!working_path)
return CURLE_OUT_OF_MEMORY;
/* Check for /~/ , indicating relative to the user's home directory */
if (conn->protocol == PROT_SCP) {
real_path = (char *)malloc(working_path_len+1);
if (real_path == NULL) {
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(working_path);
return CURLE_OUT_OF_MEMORY;
}
if (working_path[1] == '~')
/* It is referenced to the home directory, so strip the leading '/' */
memcpy(real_path, working_path+1, 1 + working_path_len-1);
else
memcpy(real_path, working_path, 1 + working_path_len);
}
else if (conn->protocol == PROT_SFTP) {
if (working_path[1] == '~') {
real_path = (char *)malloc(strlen(ssh->homedir) +
working_path_len + 1);
if (real_path == NULL) {
libssh2_sftp_shutdown(ssh->sftp_session);
ssh->sftp_session = NULL;
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(ssh->homedir);
ssh->homedir = NULL;
Curl_safefree(working_path);
return CURLE_OUT_OF_MEMORY;
}
/* It is referenced to the home directory, so strip the leading '/' */
memcpy(real_path, ssh->homedir, strlen(ssh->homedir));
real_path[strlen(ssh->homedir)] = '/';
real_path[strlen(ssh->homedir)+1] = '\0';
if (working_path_len > 3) {
memcpy(real_path+strlen(ssh->homedir)+1, working_path + 3,
1 + working_path_len -3);
}
}
else {
real_path = (char *)malloc(working_path_len+1);
if (real_path == NULL) {
libssh2_sftp_shutdown(ssh->sftp_session);
ssh->sftp_session = NULL;
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(ssh->homedir);
ssh->homedir = NULL;
Curl_safefree(working_path);
return CURLE_OUT_OF_MEMORY;
}
memcpy(real_path, working_path, 1+working_path_len);
}
}
else {
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(working_path);
return CURLE_FAILED_INIT;
Curl_safefree(working_path);
ssh->path = real_path;
*done = TRUE;
return CURLE_OK;
Guenter Knauf
committed
#endif /* !(LIBSSH2_APINO >= 200706012030L) */
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
James Housley
committed
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
/*
***********************************************************************
*
* scp_perform()
*
* This is the actual DO function for SCP. Get a file according to
* the options previously setup.
*/
static
CURLcode scp_perform(struct connectdata *conn,
bool *connected,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
DEBUGF(infof(conn->data, "DO phase starts\n"));
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
state(conn, SSH_SCP_TRANS_INIT);
/* run the state-machine */
if (conn->data->state.used_interface == Curl_if_multi) {
result = Curl_ssh_multi_statemach(conn, dophase_done);
} else {
result = ssh_easy_statemach(conn);
*dophase_done = TRUE; /* with the easy interface we are done here */
}
*connected = conn->bits.tcpconnect;
if (*dophase_done) {
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/* called from multi.c while DOing */
CURLcode Curl_scp_doing(struct connectdata *conn,
bool *dophase_done)
{
CURLcode result;
result = Curl_ssh_multi_statemach(conn, dophase_done);
if (*dophase_done) {
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
Guenter Knauf
committed
#endif /* !(LIBSSH2_APINO >= 200706012030L) */
James Housley
committed
CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
{
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
James Housley
committed
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
CURLcode res;
bool connected = 0;
struct SessionHandle *data = conn->data;
*done = FALSE; /* default to false */
/*
* Since connections can be re-used between SessionHandles, this might be a
* connection already existing but on a fresh SessionHandle struct so we must
* make sure we have a good 'struct SSHPROTO' to play with. For new
* connections, the struct SSHPROTO is allocated and setup in the
* Curl_ssh_connect() function.
*/
res = ssh_init(conn);
if (res) {
return res;
}
data->reqdata.size = -1; /* make sure this is unknown at this point */
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
res = scp_perform(conn, &connected, done);
if (CURLE_OK == res) {
if (!done) {
/* the DO phase has not completed yet */
return CURLE_OK;
}
}
return res;
Guenter Knauf
committed
#else /* !(LIBSSH2_APINO >= 200706012030L) */
James Housley
committed
struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
CURLcode res = CURLE_OK;
*done = TRUE; /* unconditionally */
if (conn->data->set.upload) {
James Housley
committed
if (conn->data->set.infilesize < 0) {
failf(conn->data, "SCP requires a known file size for upload");
return CURLE_UPLOAD_FAILED;
}
* libssh2 requires that the destination path is a full path that includes
* the destination file and name OR ends in a "/" . If this is not done
* the destination file will be named the same name as the last directory
James Housley
committed
* in the path.
*/
scp->ssh_channel = libssh2_scp_send_ex(scp->ssh_session, scp->path,
conn->data->set.new_file_perms,
if (!scp->ssh_channel)
return CURLE_FAILED_INIT;
/* upload data */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
James Housley
committed
} else {
* We must check the remote file; if it is a directory no values will
* be set in sb
curl_off_t bytecount;
memset(&sb, 0, sizeof(struct stat));
scp->ssh_channel = libssh2_scp_recv(scp->ssh_session, scp->path, &sb);
if (!scp->ssh_channel) {
if ((sb.st_mode == 0) && (sb.st_atime == 0) && (sb.st_mtime == 0) &&
(sb.st_size == 0)) {
/* Since sb is still empty, it is likely the file was not found */
return CURLE_REMOTE_FILE_NOT_FOUND;
}
return libssh2_session_error_to_CURLE(
libssh2_session_last_error(scp->ssh_session, NULL, NULL, 0));
/* download data */
bytecount = (curl_off_t) sb.st_size;
conn->data->reqdata.maxdownload = (curl_off_t) sb.st_size;
res = Curl_setup_transfer(conn, FIRSTSOCKET,
bytecount, FALSE, NULL, -1, NULL);
}
return res;
Guenter Knauf
committed
#endif /* !(LIBSSH2_APINO >= 200706012030L) */
Daniel Stenberg
committed
CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
bool premature)
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
James Housley
committed
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
CURLcode result = CURLE_OK;
bool done = FALSE;
if (status == CURLE_OK) {
state(conn, SSH_SCP_DONE);
/* run the state-machine */
if (conn->data->state.used_interface == Curl_if_multi) {
result = Curl_ssh_multi_statemach(conn, &done);
} else {
result = ssh_easy_statemach(conn);
done = TRUE;
}
} else {
result = status;
done = TRUE;
}
if (done) {
Curl_safefree(conn->data->reqdata.proto.ssh);
conn->data->reqdata.proto.ssh = NULL;
Curl_pgrsDone(conn);
}
return result;
Guenter Knauf
committed
#else /* !(LIBSSH2_APINO >= 200706012030L) */
James Housley
committed
struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
Curl_safefree(scp->path);
scp->path = NULL;
if (scp->ssh_channel) {
if (conn->data->set.upload &&
libssh2_channel_send_eof(scp->ssh_channel) < 0) {
infof(conn->data, "Failed to send libssh2 channel EOF\n");
}
if (libssh2_channel_close(scp->ssh_channel) < 0) {
infof(conn->data, "Failed to stop libssh2 channel subsystem\n");
libssh2_channel_free(scp->ssh_channel);
if (scp->ssh_session) {
libssh2_session_disconnect(scp->ssh_session, "Shutdown");
libssh2_session_free(scp->ssh_session);
scp->ssh_session = NULL;
James Housley
committed
Curl_safefree(conn->data->reqdata.proto.ssh);
conn->data->reqdata.proto.ssh = NULL;
Curl_pgrsDone(conn);
James Housley
committed
return CURLE_OK;
Guenter Knauf
committed
#endif /* !(LIBSSH2_APINO >= 200706012030L) */
James Housley
committed
(void)premature; /* not used */
(void)status; /* unused */
}
/* return number of received (decrypted) bytes */
ssize_t Curl_scp_send(struct connectdata *conn, int sockindex,
void *mem, size_t len)
{
ssize_t nwrite;
/* libssh2_channel_write() returns int
*
* NOTE: we should not store nor rely on connection-related data to be
* in the SessionHandle struct
*/
Guenter Knauf
committed
#if defined(LIBSSH2CHANNEL_EAGAIN) && (LIBSSH2_APINO < 200706012030L)
nwrite = (ssize_t)
libssh2_channel_writenb(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
#else
nwrite = (ssize_t)
libssh2_channel_write(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
if (nwrite == LIBSSH2_ERROR_EAGAIN) {
return 0;
}
#endif
(void)sockindex;
return nwrite;
}
/*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value.
*/
ssize_t Curl_scp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
ssize_t nread;
(void)sockindex; /* we only support SCP on the fixed known primary socket */
/* libssh2_channel_read() returns int
*
* NOTE: we should not store nor rely on connection-related data to be
* in the SessionHandle struct
*/
Guenter Knauf
committed
#if defined(LIBSSH2CHANNEL_EAGAIN) && (LIBSSH2_APINO < 200706012030L)
/* we prefer the non-blocking API but that didn't exist previously */
nread = (ssize_t)
libssh2_channel_readnb(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
#else
nread = (ssize_t)
libssh2_channel_read(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
return nread;
}
/*
* =============== SFTP ===============
*/
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
James Housley
committed
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
/*
***********************************************************************
*
* sftp_perform()
*
* This is the actual DO function for SFTP. Get a file/directory according to
* the options previously setup.
*/
static
CURLcode sftp_perform(struct connectdata *conn,
bool *connected,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
DEBUGF(infof(conn->data, "DO phase starts\n"));
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
state(conn, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
if (conn->data->state.used_interface == Curl_if_multi) {
result = Curl_ssh_multi_statemach(conn, dophase_done);
} else {
result = ssh_easy_statemach(conn);
*dophase_done = TRUE; /* with the easy interface we are done here */
}
*connected = conn->bits.tcpconnect;
if (*dophase_done) {
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/* called from multi.c while DOing */
CURLcode Curl_sftp_doing(struct connectdata *conn,
bool *dophase_done)
{
CURLcode result;
result = Curl_ssh_multi_statemach(conn, dophase_done);
if (*dophase_done) {
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
Guenter Knauf
committed
#endif /* !(LIBSSH2_APINO >= 200706012030L) */
James Housley
committed
CURLcode Curl_sftp_do(struct connectdata *conn, bool *done)
{
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
James Housley
committed
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
CURLcode res;
bool connected = 0;
struct SessionHandle *data = conn->data;
*done = FALSE; /* default to false */
/*
* Since connections can be re-used between SessionHandles, this might be a
* connection already existing but on a fresh SessionHandle struct so we must
* make sure we have a good 'struct SSHPROTO' to play with. For new
* connections, the struct SSHPROTO is allocated and setup in the
* Curl_ssh_connect() function.
*/
res = ssh_init(conn);
if (res) {
return res;
}
data->reqdata.size = -1; /* make sure this is unknown at this point */
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
res = sftp_perform(conn, &connected, done);
if (CURLE_OK == res) {
if (!done) {
/* the DO phase has not completed yet */
return CURLE_OK;
}
}
return res;
Guenter Knauf
committed
#else /* !(LIBSSH2_APINO >= 200706012030L) */
James Housley
committed
LIBSSH2_SFTP_ATTRIBUTES attrs;
struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
CURLcode res = CURLE_OK;
struct SessionHandle *data = conn->data;
curl_off_t bytecount = 0;
char *buf = data->state.buffer;
*done = TRUE; /* unconditionally */
/* Send any quote commands */
James Housley
committed
if (conn->data->set.quote) {
infof(conn->data, "Sending quote commands\n");
res = sftp_sendquote(conn, conn->data->set.quote);
if (res != CURLE_OK)
return res;
}
if (data->set.upload) {
/*
* NOTE!!! libssh2 requires that the destination path is a full path
* that includes the destination file and name OR ends in a "/" .
* If this is not done the destination file will be named the
* same name as the last directory in the path.
*/
sftp->sftp_handle =
libssh2_sftp_open(sftp->sftp_session, sftp->path,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
James Housley
committed
conn->data->set.new_file_perms);
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
if (((err == LIBSSH2_FX_NO_SUCH_FILE) ||
(err == LIBSSH2_FX_FAILURE) ||
(err == LIBSSH2_FX_NO_SUCH_PATH)) &&
(conn->data->set.ftp_create_missing_dirs &&
(strlen(sftp->path) > 1))) {
/* try to create the path remotely */
res = sftp_create_dirs(conn);
if (res == 0) {
sftp->sftp_handle = libssh2_sftp_open(sftp->sftp_session, sftp->path,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
James Housley
committed
conn->data->set.new_file_perms);
}
}
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for writing: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
/* upload data */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
}
else {
if (sftp->path[strlen(sftp->path)-1] == '/') {
/*
* This is a directory that we are trying to get, so produce a
* directory listing
*
* **BLOCKING behaviour** This should be made into a state machine and
* get a separate function called from Curl_sftp_recv() when there is
* data to read from the network, instead of "hanging" here.
*/
char filename[PATH_MAX+1];
int len, totalLen, currLen;
char *line;
sftp->sftp_handle =
libssh2_sftp_opendir(sftp->sftp_session, sftp->path);
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open directory for reading: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
do {
len = libssh2_sftp_readdir(sftp->sftp_handle, filename,
PATH_MAX, &attrs);
if (len > 0) {
filename[len] = '\0';
if (data->set.ftp_list_only) {
char *tmpLine;
tmpLine = aprintf("%s\n", filename);
if (tmpLine == NULL) {
return CURLE_OUT_OF_MEMORY;
}
res = Curl_client_write(conn, CLIENTWRITE_BODY, tmpLine, 0);
Curl_safefree(tmpLine);
James Housley
committed
} else {
totalLen = 80 + len;
line = (char *)malloc(totalLen);
if (!line)
return CURLE_OUT_OF_MEMORY;
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
if (!(attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID))
attrs.uid = attrs.gid =0;
currLen = snprintf(line, totalLen, "---------- 1 %5d %5d",
attrs.uid, attrs.gid);
if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFDIR) {
line[0] = 'd';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK) {
line[0] = 'l';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFSOCK) {
line[0] = 's';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFCHR) {
line[0] = 'c';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFBLK) {
line[0] = 'b';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IRUSR) {
line[1] = 'r';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IWUSR) {
line[2] = 'w';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IXUSR) {
line[3] = 'x';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IRGRP) {
line[4] = 'r';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IWGRP) {
line[5] = 'w';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IXGRP) {
line[6] = 'x';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IROTH) {
line[7] = 'r';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IWOTH) {
line[8] = 'w';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IXOTH) {
line[9] = 'x';
}
if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) {
currLen += snprintf(line+currLen, totalLen-currLen, "%11lld",
attrs.filesize);
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
static const char * const months[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
struct tm *nowParts;
time_t now, remoteTime;
now = time(NULL);
remoteTime = (time_t)attrs.mtime;
nowParts = localtime(&remoteTime);
if ((time_t)attrs.mtime > (now - (3600 * 24 * 180))) {
currLen += snprintf(line+currLen, totalLen-currLen,
" %s %2d %2d:%02d",
months[nowParts->tm_mon],
nowParts->tm_mday, nowParts->tm_hour,
nowParts->tm_min);
}
else {
currLen += snprintf(line+currLen, totalLen-currLen,
" %s %2d %5d", months[nowParts->tm_mon],
nowParts->tm_mday, 1900+nowParts->tm_year);
}
currLen += snprintf(line+currLen, totalLen-currLen, " %s",
filename);
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK)) {
char linkPath[PATH_MAX + 1];
snprintf(linkPath, PATH_MAX, "%s%s", sftp->path, filename);
len = libssh2_sftp_readlink(sftp->sftp_session, linkPath,
filename, PATH_MAX);
line = realloc(line, totalLen + 4 + len);
if (!line)
return CURLE_OUT_OF_MEMORY;
currLen += snprintf(line+currLen, totalLen-currLen, " -> %s",
filename);
currLen += snprintf(line+currLen, totalLen-currLen, "\n");
res = Curl_client_write(conn, CLIENTWRITE_BODY, line, 0);
James Housley
committed
Curl_safefree(line);
}
}
else if (len <= 0) {
break;
}
} while (1);
libssh2_sftp_closedir(sftp->sftp_handle);
sftp->sftp_handle = NULL;
/* no data to transfer */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
}
else {
/*
* Work on getting the specified file
*/
sftp->sftp_handle =
libssh2_sftp_open(sftp->sftp_session, sftp->path, LIBSSH2_FXF_READ,
James Housley
committed
conn->data->set.new_file_perms);
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for reading: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
rc = libssh2_sftp_stat(sftp->sftp_session, sftp->path, &attrs);
if (rc) {
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
/*
* libssh2_sftp_open() didn't return an error, so maybe the server
* just doesn't support stat()
*/
data->reqdata.size = -1;
data->reqdata.maxdownload = -1;
}
else {
data->reqdata.size = attrs.filesize;
data->reqdata.maxdownload = attrs.filesize;
Curl_pgrsSetDownloadSize(data, attrs.filesize);
}
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
/* Now download data. The libssh2 0.14 doesn't offer any way to do this
without using this BLOCKING approach, so here's room for improvement
once libssh2 can return EWOULDBLOCK to us. */
while (res == CURLE_OK) {
size_t nread;
/* NOTE: most *read() functions return ssize_t but this returns size_t
which normally is unsigned! */
nread = libssh2_sftp_read(data->reqdata.proto.ssh->sftp_handle,
buf, BUFSIZE-1);
if (nread > 0)
buf[nread] = 0;
/* this check can be changed to a <= 0 when nread is changed to a
signed variable type */
if ((nread == 0) || (nread == (size_t)~0))
break;
bytecount += nread;
res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
James Housley
committed
if (res)
return res;
Curl_pgrsSetDownloadCounter(data, bytecount);
James Housley
committed
if (Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
else {
struct timeval now = Curl_tvnow();
res = Curl_speedcheck(data, now);
}
}
James Housley
committed
if (Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
/* no (more) data to transfer */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
}
}
return res;
Guenter Knauf
committed
#endif /* !(LIBSSH2_APINO >= 200706012030L) */
Daniel Stenberg
committed
CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status,
bool premature)
James Housley
committed
CURLcode result = CURLE_OK;
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
James Housley
committed
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
bool done = FALSE;
struct ssh_conn *sshc = &conn->proto.sshc;
if (status == CURLE_OK) {
/* Before we shut down, see if there are any post-quote commands to send: */
if (!status && !premature && conn->data->set.postquote) {
sshc->nextState = SSH_SFTP_CLOSE;
state(conn, SSH_SFTP_POSTQUOTE_INIT);
} else {
state(conn, SSH_SFTP_CLOSE);
}
/* run the state-machine */
if (conn->data->state.used_interface == Curl_if_multi) {
result = Curl_ssh_multi_statemach(conn, &done);
} else {
result = ssh_easy_statemach(conn);
done = TRUE;
}
} else {
result = status;
done = TRUE;
}
if (done) {
Curl_safefree(conn->data->reqdata.proto.ssh);
conn->data->reqdata.proto.ssh = NULL;
Curl_pgrsDone(conn);
}
Guenter Knauf
committed
#else /* !(LIBSSH2_APINO >= 200706012030L) */
James Housley
committed
struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
James Housley
committed
/* Before we shut down, see if there are any post-quote commands to send: */
if (!status && !premature && conn->data->set.postquote) {
infof(conn->data, "Sending postquote commands\n");
result = sftp_sendquote(conn, conn->data->set.postquote);
}
Curl_safefree(sftp->path);
sftp->path = NULL;
Curl_safefree(sftp->homedir);
sftp->homedir = NULL;
if (sftp->sftp_handle) {
if (libssh2_sftp_close(sftp->sftp_handle) < 0) {
infof(conn->data, "Failed to close libssh2 file\n");
}
}
if (sftp->sftp_session) {
if (libssh2_sftp_shutdown(sftp->sftp_session) < 0) {
infof(conn->data, "Failed to stop libssh2 sftp subsystem\n");
}
}
if (sftp->ssh_channel) {
if (libssh2_channel_close(sftp->ssh_channel) < 0) {
infof(conn->data, "Failed to stop libssh2 channel subsystem\n");
}
}
if (sftp->ssh_session) {
libssh2_session_disconnect(sftp->ssh_session, "Shutdown");
libssh2_session_free(sftp->ssh_session);
sftp->ssh_session = NULL;
}
James Housley
committed
Curl_safefree(conn->data->reqdata.proto.ssh);
conn->data->reqdata.proto.ssh = NULL;
Curl_pgrsDone(conn);
Guenter Knauf
committed
#endif /* !(LIBSSH2_APINO >= 200706012030L) */
(void)status; /* unused */
James Housley
committed
return result;
}
/* return number of received (decrypted) bytes */
ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex,
void *mem, size_t len)
{
ssize_t nwrite; /* libssh2_sftp_write() used to return size_t in 0.14
but is changed to ssize_t in 0.15! */
Guenter Knauf
committed
#if defined(LIBSSH2SFTP_EAGAIN) && (LIBSSH2_APINO < 200706012030L)
Daniel Stenberg
committed
/* we prefer the non-blocking API but that didn't exist previously */
nwrite = (ssize_t)
libssh2_sftp_writenb(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
#else
nwrite = (ssize_t)
libssh2_sftp_write(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
Guenter Knauf
committed
#if (LIBSSH2_APINO >= 200706012030L)
if (nwrite == LIBSSH2_ERROR_EAGAIN) {
return 0;
}
#endif
Daniel Stenberg
committed
#endif
(void)sockindex;
return nwrite;
}
/*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value.
*/
ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
ssize_t nread;
(void)sockindex;
/* libssh2_sftp_read() returns size_t !*/
Guenter Knauf
committed
#if defined(LIBSSH2SFTP_EAGAIN) && (LIBSSH2_APINO < 200706012030L)
/* we prefer the non-blocking API but that didn't exist previously */
nread = (ssize_t)
libssh2_sftp_readnb(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
#else
nread = (ssize_t)
libssh2_sftp_read(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);