Annotation of embedaddon/curl/lib/vssh/libssh.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 2017 - 2020 Red Hat, Inc.
! 9: *
! 10: * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek,
! 11: * Robert Kolcun, Andreas Schneider
! 12: *
! 13: * This software is licensed as described in the file COPYING, which
! 14: * you should have received as part of this distribution. The terms
! 15: * are also available at https://curl.haxx.se/docs/copyright.html.
! 16: *
! 17: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
! 18: * copies of the Software, and permit persons to whom the Software is
! 19: * furnished to do so, under the terms of the COPYING file.
! 20: *
! 21: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
! 22: * KIND, either express or implied.
! 23: *
! 24: ***************************************************************************/
! 25:
! 26: #include "curl_setup.h"
! 27:
! 28: #ifdef USE_LIBSSH
! 29:
! 30: #include <limits.h>
! 31:
! 32: #include <libssh/libssh.h>
! 33: #include <libssh/sftp.h>
! 34:
! 35: #ifdef HAVE_FCNTL_H
! 36: #include <fcntl.h>
! 37: #endif
! 38:
! 39: #ifdef HAVE_NETINET_IN_H
! 40: #include <netinet/in.h>
! 41: #endif
! 42: #ifdef HAVE_ARPA_INET_H
! 43: #include <arpa/inet.h>
! 44: #endif
! 45: #ifdef HAVE_UTSNAME_H
! 46: #include <sys/utsname.h>
! 47: #endif
! 48: #ifdef HAVE_NETDB_H
! 49: #include <netdb.h>
! 50: #endif
! 51: #ifdef __VMS
! 52: #include <in.h>
! 53: #include <inet.h>
! 54: #endif
! 55:
! 56: #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
! 57: #undef in_addr_t
! 58: #define in_addr_t unsigned long
! 59: #endif
! 60:
! 61: #include <curl/curl.h>
! 62: #include "urldata.h"
! 63: #include "sendf.h"
! 64: #include "hostip.h"
! 65: #include "progress.h"
! 66: #include "transfer.h"
! 67: #include "escape.h"
! 68: #include "http.h" /* for HTTP proxy tunnel stuff */
! 69: #include "ssh.h"
! 70: #include "url.h"
! 71: #include "speedcheck.h"
! 72: #include "getinfo.h"
! 73: #include "strdup.h"
! 74: #include "strcase.h"
! 75: #include "vtls/vtls.h"
! 76: #include "connect.h"
! 77: #include "strerror.h"
! 78: #include "inet_ntop.h"
! 79: #include "parsedate.h" /* for the week day and month names */
! 80: #include "sockaddr.h" /* required for Curl_sockaddr_storage */
! 81: #include "strtoofft.h"
! 82: #include "multiif.h"
! 83: #include "select.h"
! 84: #include "warnless.h"
! 85:
! 86: /* for permission and open flags */
! 87: #include <sys/types.h>
! 88: #include <sys/stat.h>
! 89: #include <unistd.h>
! 90: #include <fcntl.h>
! 91:
! 92: /* The last 3 #include files should be in this order */
! 93: #include "curl_printf.h"
! 94: #include "curl_memory.h"
! 95: #include "memdebug.h"
! 96: #include "curl_path.h"
! 97:
! 98: /* A recent macro provided by libssh. Or make our own. */
! 99: #ifndef SSH_STRING_FREE_CHAR
! 100: #define SSH_STRING_FREE_CHAR(x) \
! 101: do { \
! 102: if(x) { \
! 103: ssh_string_free_char(x); \
! 104: x = NULL; \
! 105: } \
! 106: } while(0)
! 107: #endif
! 108:
! 109: /* Local functions: */
! 110: static CURLcode myssh_connect(struct connectdata *conn, bool *done);
! 111: static CURLcode myssh_multi_statemach(struct connectdata *conn,
! 112: bool *done);
! 113: static CURLcode myssh_do_it(struct connectdata *conn, bool *done);
! 114:
! 115: static CURLcode scp_done(struct connectdata *conn,
! 116: CURLcode, bool premature);
! 117: static CURLcode scp_doing(struct connectdata *conn, bool *dophase_done);
! 118: static CURLcode scp_disconnect(struct connectdata *conn,
! 119: bool dead_connection);
! 120:
! 121: static CURLcode sftp_done(struct connectdata *conn,
! 122: CURLcode, bool premature);
! 123: static CURLcode sftp_doing(struct connectdata *conn,
! 124: bool *dophase_done);
! 125: static CURLcode sftp_disconnect(struct connectdata *conn, bool dead);
! 126: static
! 127: CURLcode sftp_perform(struct connectdata *conn,
! 128: bool *connected,
! 129: bool *dophase_done);
! 130:
! 131: static void sftp_quote(struct connectdata *conn);
! 132: static void sftp_quote_stat(struct connectdata *conn);
! 133: static int myssh_getsock(struct connectdata *conn, curl_socket_t *sock);
! 134: static int myssh_perform_getsock(const struct connectdata *conn,
! 135: curl_socket_t *sock);
! 136:
! 137: static CURLcode myssh_setup_connection(struct connectdata *conn);
! 138:
! 139: /*
! 140: * SCP protocol handler.
! 141: */
! 142:
! 143: const struct Curl_handler Curl_handler_scp = {
! 144: "SCP", /* scheme */
! 145: myssh_setup_connection, /* setup_connection */
! 146: myssh_do_it, /* do_it */
! 147: scp_done, /* done */
! 148: ZERO_NULL, /* do_more */
! 149: myssh_connect, /* connect_it */
! 150: myssh_multi_statemach, /* connecting */
! 151: scp_doing, /* doing */
! 152: myssh_getsock, /* proto_getsock */
! 153: myssh_getsock, /* doing_getsock */
! 154: ZERO_NULL, /* domore_getsock */
! 155: myssh_perform_getsock, /* perform_getsock */
! 156: scp_disconnect, /* disconnect */
! 157: ZERO_NULL, /* readwrite */
! 158: ZERO_NULL, /* connection_check */
! 159: PORT_SSH, /* defport */
! 160: CURLPROTO_SCP, /* protocol */
! 161: PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
! 162: };
! 163:
! 164: /*
! 165: * SFTP protocol handler.
! 166: */
! 167:
! 168: const struct Curl_handler Curl_handler_sftp = {
! 169: "SFTP", /* scheme */
! 170: myssh_setup_connection, /* setup_connection */
! 171: myssh_do_it, /* do_it */
! 172: sftp_done, /* done */
! 173: ZERO_NULL, /* do_more */
! 174: myssh_connect, /* connect_it */
! 175: myssh_multi_statemach, /* connecting */
! 176: sftp_doing, /* doing */
! 177: myssh_getsock, /* proto_getsock */
! 178: myssh_getsock, /* doing_getsock */
! 179: ZERO_NULL, /* domore_getsock */
! 180: myssh_perform_getsock, /* perform_getsock */
! 181: sftp_disconnect, /* disconnect */
! 182: ZERO_NULL, /* readwrite */
! 183: ZERO_NULL, /* connection_check */
! 184: PORT_SSH, /* defport */
! 185: CURLPROTO_SFTP, /* protocol */
! 186: PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
! 187: | PROTOPT_NOURLQUERY /* flags */
! 188: };
! 189:
! 190: static CURLcode sftp_error_to_CURLE(int err)
! 191: {
! 192: switch(err) {
! 193: case SSH_FX_OK:
! 194: return CURLE_OK;
! 195:
! 196: case SSH_FX_NO_SUCH_FILE:
! 197: case SSH_FX_NO_SUCH_PATH:
! 198: return CURLE_REMOTE_FILE_NOT_FOUND;
! 199:
! 200: case SSH_FX_PERMISSION_DENIED:
! 201: case SSH_FX_WRITE_PROTECT:
! 202: return CURLE_REMOTE_ACCESS_DENIED;
! 203:
! 204: case SSH_FX_FILE_ALREADY_EXISTS:
! 205: return CURLE_REMOTE_FILE_EXISTS;
! 206:
! 207: default:
! 208: break;
! 209: }
! 210:
! 211: return CURLE_SSH;
! 212: }
! 213:
! 214: #ifndef DEBUGBUILD
! 215: #define state(x,y) mystate(x,y)
! 216: #else
! 217: #define state(x,y) mystate(x,y, __LINE__)
! 218: #endif
! 219:
! 220: /*
! 221: * SSH State machine related code
! 222: */
! 223: /* This is the ONLY way to change SSH state! */
! 224: static void mystate(struct connectdata *conn, sshstate nowstate
! 225: #ifdef DEBUGBUILD
! 226: , int lineno
! 227: #endif
! 228: )
! 229: {
! 230: struct ssh_conn *sshc = &conn->proto.sshc;
! 231: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
! 232: /* for debug purposes */
! 233: static const char *const names[] = {
! 234: "SSH_STOP",
! 235: "SSH_INIT",
! 236: "SSH_S_STARTUP",
! 237: "SSH_HOSTKEY",
! 238: "SSH_AUTHLIST",
! 239: "SSH_AUTH_PKEY_INIT",
! 240: "SSH_AUTH_PKEY",
! 241: "SSH_AUTH_PASS_INIT",
! 242: "SSH_AUTH_PASS",
! 243: "SSH_AUTH_AGENT_INIT",
! 244: "SSH_AUTH_AGENT_LIST",
! 245: "SSH_AUTH_AGENT",
! 246: "SSH_AUTH_HOST_INIT",
! 247: "SSH_AUTH_HOST",
! 248: "SSH_AUTH_KEY_INIT",
! 249: "SSH_AUTH_KEY",
! 250: "SSH_AUTH_GSSAPI",
! 251: "SSH_AUTH_DONE",
! 252: "SSH_SFTP_INIT",
! 253: "SSH_SFTP_REALPATH",
! 254: "SSH_SFTP_QUOTE_INIT",
! 255: "SSH_SFTP_POSTQUOTE_INIT",
! 256: "SSH_SFTP_QUOTE",
! 257: "SSH_SFTP_NEXT_QUOTE",
! 258: "SSH_SFTP_QUOTE_STAT",
! 259: "SSH_SFTP_QUOTE_SETSTAT",
! 260: "SSH_SFTP_QUOTE_SYMLINK",
! 261: "SSH_SFTP_QUOTE_MKDIR",
! 262: "SSH_SFTP_QUOTE_RENAME",
! 263: "SSH_SFTP_QUOTE_RMDIR",
! 264: "SSH_SFTP_QUOTE_UNLINK",
! 265: "SSH_SFTP_QUOTE_STATVFS",
! 266: "SSH_SFTP_GETINFO",
! 267: "SSH_SFTP_FILETIME",
! 268: "SSH_SFTP_TRANS_INIT",
! 269: "SSH_SFTP_UPLOAD_INIT",
! 270: "SSH_SFTP_CREATE_DIRS_INIT",
! 271: "SSH_SFTP_CREATE_DIRS",
! 272: "SSH_SFTP_CREATE_DIRS_MKDIR",
! 273: "SSH_SFTP_READDIR_INIT",
! 274: "SSH_SFTP_READDIR",
! 275: "SSH_SFTP_READDIR_LINK",
! 276: "SSH_SFTP_READDIR_BOTTOM",
! 277: "SSH_SFTP_READDIR_DONE",
! 278: "SSH_SFTP_DOWNLOAD_INIT",
! 279: "SSH_SFTP_DOWNLOAD_STAT",
! 280: "SSH_SFTP_CLOSE",
! 281: "SSH_SFTP_SHUTDOWN",
! 282: "SSH_SCP_TRANS_INIT",
! 283: "SSH_SCP_UPLOAD_INIT",
! 284: "SSH_SCP_DOWNLOAD_INIT",
! 285: "SSH_SCP_DOWNLOAD",
! 286: "SSH_SCP_DONE",
! 287: "SSH_SCP_SEND_EOF",
! 288: "SSH_SCP_WAIT_EOF",
! 289: "SSH_SCP_WAIT_CLOSE",
! 290: "SSH_SCP_CHANNEL_FREE",
! 291: "SSH_SESSION_DISCONNECT",
! 292: "SSH_SESSION_FREE",
! 293: "QUIT"
! 294: };
! 295:
! 296:
! 297: if(sshc->state != nowstate) {
! 298: infof(conn->data, "SSH %p state change from %s to %s (line %d)\n",
! 299: (void *) sshc, names[sshc->state], names[nowstate],
! 300: lineno);
! 301: }
! 302: #endif
! 303:
! 304: sshc->state = nowstate;
! 305: }
! 306:
! 307: /* Multiple options:
! 308: * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5
! 309: * hash (90s style auth, not sure we should have it here)
! 310: * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first
! 311: * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE
! 312: * is returned by it.
! 313: * 3. none of the above. We only accept if it is present on known hosts.
! 314: *
! 315: * Returns SSH_OK or SSH_ERROR.
! 316: */
! 317: static int myssh_is_known(struct connectdata *conn)
! 318: {
! 319: int rc;
! 320: struct Curl_easy *data = conn->data;
! 321: struct ssh_conn *sshc = &conn->proto.sshc;
! 322: ssh_key pubkey;
! 323: size_t hlen;
! 324: unsigned char *hash = NULL;
! 325: char *found_base64 = NULL;
! 326: char *known_base64 = NULL;
! 327: int vstate;
! 328: enum curl_khmatch keymatch;
! 329: struct curl_khkey foundkey;
! 330: struct curl_khkey *knownkeyp = NULL;
! 331: curl_sshkeycallback func =
! 332: data->set.ssh_keyfunc;
! 333:
! 334: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
! 335: struct ssh_knownhosts_entry *knownhostsentry = NULL;
! 336: struct curl_khkey knownkey;
! 337: #endif
! 338:
! 339: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
! 340: rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
! 341: #else
! 342: rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
! 343: #endif
! 344: if(rc != SSH_OK)
! 345: return rc;
! 346:
! 347: if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
! 348: int i;
! 349: char md5buffer[33];
! 350: const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
! 351:
! 352: rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5,
! 353: &hash, &hlen);
! 354: if(rc != SSH_OK || hlen != 16) {
! 355: failf(data,
! 356: "Denied establishing ssh session: md5 fingerprint not available");
! 357: goto cleanup;
! 358: }
! 359:
! 360: for(i = 0; i < 16; i++)
! 361: msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]);
! 362:
! 363: infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
! 364:
! 365: if(!strcasecompare(md5buffer, pubkey_md5)) {
! 366: failf(data,
! 367: "Denied establishing ssh session: mismatch md5 fingerprint. "
! 368: "Remote %s is not equal to %s", md5buffer, pubkey_md5);
! 369: rc = SSH_ERROR;
! 370: goto cleanup;
! 371: }
! 372:
! 373: rc = SSH_OK;
! 374: goto cleanup;
! 375: }
! 376:
! 377: if(data->set.ssl.primary.verifyhost != TRUE) {
! 378: rc = SSH_OK;
! 379: goto cleanup;
! 380: }
! 381:
! 382: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
! 383: /* Get the known_key from the known hosts file */
! 384: vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
! 385: &knownhostsentry);
! 386:
! 387: /* Case an entry was found in a known hosts file */
! 388: if(knownhostsentry) {
! 389: if(knownhostsentry->publickey) {
! 390: rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey,
! 391: &known_base64);
! 392: if(rc != SSH_OK) {
! 393: goto cleanup;
! 394: }
! 395: knownkey.key = known_base64;
! 396: knownkey.len = strlen(known_base64);
! 397:
! 398: switch(ssh_key_type(knownhostsentry->publickey)) {
! 399: case SSH_KEYTYPE_RSA:
! 400: knownkey.keytype = CURLKHTYPE_RSA;
! 401: break;
! 402: case SSH_KEYTYPE_RSA1:
! 403: knownkey.keytype = CURLKHTYPE_RSA1;
! 404: break;
! 405: case SSH_KEYTYPE_ECDSA:
! 406: case SSH_KEYTYPE_ECDSA_P256:
! 407: case SSH_KEYTYPE_ECDSA_P384:
! 408: case SSH_KEYTYPE_ECDSA_P521:
! 409: knownkey.keytype = CURLKHTYPE_ECDSA;
! 410: break;
! 411: case SSH_KEYTYPE_ED25519:
! 412: knownkey.keytype = CURLKHTYPE_ED25519;
! 413: break;
! 414: case SSH_KEYTYPE_DSS:
! 415: knownkey.keytype = CURLKHTYPE_DSS;
! 416: break;
! 417: default:
! 418: rc = SSH_ERROR;
! 419: goto cleanup;
! 420: }
! 421: knownkeyp = &knownkey;
! 422: }
! 423: }
! 424:
! 425: switch(vstate) {
! 426: case SSH_KNOWN_HOSTS_OK:
! 427: keymatch = CURLKHMATCH_OK;
! 428: break;
! 429: case SSH_KNOWN_HOSTS_OTHER:
! 430: /* fallthrough */
! 431: case SSH_KNOWN_HOSTS_NOT_FOUND:
! 432: /* fallthrough */
! 433: case SSH_KNOWN_HOSTS_UNKNOWN:
! 434: /* fallthrough */
! 435: case SSH_KNOWN_HOSTS_ERROR:
! 436: keymatch = CURLKHMATCH_MISSING;
! 437: break;
! 438: default:
! 439: keymatch = CURLKHMATCH_MISMATCH;
! 440: break;
! 441: }
! 442:
! 443: #else
! 444: vstate = ssh_is_server_known(sshc->ssh_session);
! 445: switch(vstate) {
! 446: case SSH_SERVER_KNOWN_OK:
! 447: keymatch = CURLKHMATCH_OK;
! 448: break;
! 449: case SSH_SERVER_FILE_NOT_FOUND:
! 450: /* fallthrough */
! 451: case SSH_SERVER_NOT_KNOWN:
! 452: keymatch = CURLKHMATCH_MISSING;
! 453: break;
! 454: default:
! 455: keymatch = CURLKHMATCH_MISMATCH;
! 456: break;
! 457: }
! 458: #endif
! 459:
! 460: if(func) { /* use callback to determine action */
! 461: rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
! 462: if(rc != SSH_OK)
! 463: goto cleanup;
! 464:
! 465: foundkey.key = found_base64;
! 466: foundkey.len = strlen(found_base64);
! 467:
! 468: switch(ssh_key_type(pubkey)) {
! 469: case SSH_KEYTYPE_RSA:
! 470: foundkey.keytype = CURLKHTYPE_RSA;
! 471: break;
! 472: case SSH_KEYTYPE_RSA1:
! 473: foundkey.keytype = CURLKHTYPE_RSA1;
! 474: break;
! 475: case SSH_KEYTYPE_ECDSA:
! 476: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
! 477: case SSH_KEYTYPE_ECDSA_P256:
! 478: case SSH_KEYTYPE_ECDSA_P384:
! 479: case SSH_KEYTYPE_ECDSA_P521:
! 480: #endif
! 481: foundkey.keytype = CURLKHTYPE_ECDSA;
! 482: break;
! 483: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
! 484: case SSH_KEYTYPE_ED25519:
! 485: foundkey.keytype = CURLKHTYPE_ED25519;
! 486: break;
! 487: #endif
! 488: case SSH_KEYTYPE_DSS:
! 489: foundkey.keytype = CURLKHTYPE_DSS;
! 490: break;
! 491: default:
! 492: rc = SSH_ERROR;
! 493: goto cleanup;
! 494: }
! 495:
! 496: Curl_set_in_callback(data, true);
! 497: rc = func(data, knownkeyp, /* from the knownhosts file */
! 498: &foundkey, /* from the remote host */
! 499: keymatch, data->set.ssh_keyfunc_userp);
! 500: Curl_set_in_callback(data, false);
! 501:
! 502: switch(rc) {
! 503: case CURLKHSTAT_FINE_ADD_TO_FILE:
! 504: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
! 505: rc = ssh_session_update_known_hosts(sshc->ssh_session);
! 506: #else
! 507: rc = ssh_write_knownhost(sshc->ssh_session);
! 508: #endif
! 509: if(rc != SSH_OK) {
! 510: goto cleanup;
! 511: }
! 512: break;
! 513: case CURLKHSTAT_FINE:
! 514: break;
! 515: default: /* REJECT/DEFER */
! 516: rc = SSH_ERROR;
! 517: goto cleanup;
! 518: }
! 519: }
! 520: else {
! 521: if(keymatch != CURLKHMATCH_OK) {
! 522: rc = SSH_ERROR;
! 523: goto cleanup;
! 524: }
! 525: }
! 526: rc = SSH_OK;
! 527:
! 528: cleanup:
! 529: if(found_base64) {
! 530: free(found_base64);
! 531: }
! 532: if(known_base64) {
! 533: free(known_base64);
! 534: }
! 535: if(hash)
! 536: ssh_clean_pubkey_hash(&hash);
! 537: ssh_key_free(pubkey);
! 538: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
! 539: if(knownhostsentry) {
! 540: ssh_knownhosts_entry_free(knownhostsentry);
! 541: }
! 542: #endif
! 543: return rc;
! 544: }
! 545:
! 546: #define MOVE_TO_ERROR_STATE(_r) { \
! 547: state(conn, SSH_SESSION_DISCONNECT); \
! 548: sshc->actualcode = _r; \
! 549: rc = SSH_ERROR; \
! 550: break; \
! 551: }
! 552:
! 553: #define MOVE_TO_SFTP_CLOSE_STATE() { \
! 554: state(conn, SSH_SFTP_CLOSE); \
! 555: sshc->actualcode = sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \
! 556: rc = SSH_ERROR; \
! 557: break; \
! 558: }
! 559:
! 560: #define MOVE_TO_LAST_AUTH \
! 561: if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
! 562: rc = SSH_OK; \
! 563: state(conn, SSH_AUTH_PASS_INIT); \
! 564: break; \
! 565: } \
! 566: else { \
! 567: MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \
! 568: }
! 569:
! 570: #define MOVE_TO_TERTIARY_AUTH \
! 571: if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \
! 572: rc = SSH_OK; \
! 573: state(conn, SSH_AUTH_KEY_INIT); \
! 574: break; \
! 575: } \
! 576: else { \
! 577: MOVE_TO_LAST_AUTH; \
! 578: }
! 579:
! 580: #define MOVE_TO_SECONDARY_AUTH \
! 581: if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \
! 582: rc = SSH_OK; \
! 583: state(conn, SSH_AUTH_GSSAPI); \
! 584: break; \
! 585: } \
! 586: else { \
! 587: MOVE_TO_TERTIARY_AUTH; \
! 588: }
! 589:
! 590: static
! 591: int myssh_auth_interactive(struct connectdata *conn)
! 592: {
! 593: int rc;
! 594: struct ssh_conn *sshc = &conn->proto.sshc;
! 595: int nprompts;
! 596:
! 597: restart:
! 598: switch(sshc->kbd_state) {
! 599: case 0:
! 600: rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
! 601: if(rc == SSH_AUTH_AGAIN)
! 602: return SSH_AGAIN;
! 603:
! 604: if(rc != SSH_AUTH_INFO)
! 605: return SSH_ERROR;
! 606:
! 607: nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
! 608: if(nprompts != 1)
! 609: return SSH_ERROR;
! 610:
! 611: rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd);
! 612: if(rc < 0)
! 613: return SSH_ERROR;
! 614:
! 615: /* FALLTHROUGH */
! 616: case 1:
! 617: sshc->kbd_state = 1;
! 618:
! 619: rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
! 620: if(rc == SSH_AUTH_AGAIN)
! 621: return SSH_AGAIN;
! 622: else if(rc == SSH_AUTH_SUCCESS)
! 623: rc = SSH_OK;
! 624: else if(rc == SSH_AUTH_INFO) {
! 625: nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
! 626: if(nprompts != 0)
! 627: return SSH_ERROR;
! 628:
! 629: sshc->kbd_state = 2;
! 630: goto restart;
! 631: }
! 632: else
! 633: rc = SSH_ERROR;
! 634: break;
! 635: case 2:
! 636: sshc->kbd_state = 2;
! 637:
! 638: rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
! 639: if(rc == SSH_AUTH_AGAIN)
! 640: return SSH_AGAIN;
! 641: else if(rc == SSH_AUTH_SUCCESS)
! 642: rc = SSH_OK;
! 643: else
! 644: rc = SSH_ERROR;
! 645:
! 646: break;
! 647: default:
! 648: return SSH_ERROR;
! 649: }
! 650:
! 651: sshc->kbd_state = 0;
! 652: return rc;
! 653: }
! 654:
! 655: /*
! 656: * ssh_statemach_act() runs the SSH state machine as far as it can without
! 657: * blocking and without reaching the end. The data the pointer 'block' points
! 658: * to will be set to TRUE if the libssh function returns SSH_AGAIN
! 659: * meaning it wants to be called again when the socket is ready
! 660: */
! 661: static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
! 662: {
! 663: CURLcode result = CURLE_OK;
! 664: struct Curl_easy *data = conn->data;
! 665: struct SSHPROTO *protop = data->req.protop;
! 666: struct ssh_conn *sshc = &conn->proto.sshc;
! 667: curl_socket_t sock = conn->sock[FIRSTSOCKET];
! 668: int rc = SSH_NO_ERROR, err;
! 669: char *new_readdir_line;
! 670: int seekerr = CURL_SEEKFUNC_OK;
! 671: const char *err_msg;
! 672: *block = 0; /* we're not blocking by default */
! 673:
! 674: do {
! 675:
! 676: switch(sshc->state) {
! 677: case SSH_INIT:
! 678: sshc->secondCreateDirs = 0;
! 679: sshc->nextstate = SSH_NO_STATE;
! 680: sshc->actualcode = CURLE_OK;
! 681:
! 682: #if 0
! 683: ssh_set_log_level(SSH_LOG_PROTOCOL);
! 684: #endif
! 685:
! 686: /* Set libssh to non-blocking, since everything internally is
! 687: non-blocking */
! 688: ssh_set_blocking(sshc->ssh_session, 0);
! 689:
! 690: state(conn, SSH_S_STARTUP);
! 691: /* FALLTHROUGH */
! 692:
! 693: case SSH_S_STARTUP:
! 694: rc = ssh_connect(sshc->ssh_session);
! 695: if(rc == SSH_AGAIN)
! 696: break;
! 697:
! 698: if(rc != SSH_OK) {
! 699: failf(data, "Failure establishing ssh session");
! 700: MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
! 701: }
! 702:
! 703: state(conn, SSH_HOSTKEY);
! 704:
! 705: /* FALLTHROUGH */
! 706: case SSH_HOSTKEY:
! 707:
! 708: rc = myssh_is_known(conn);
! 709: if(rc != SSH_OK) {
! 710: MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
! 711: }
! 712:
! 713: state(conn, SSH_AUTHLIST);
! 714: /* FALLTHROUGH */
! 715: case SSH_AUTHLIST:{
! 716: sshc->authed = FALSE;
! 717:
! 718: rc = ssh_userauth_none(sshc->ssh_session, NULL);
! 719: if(rc == SSH_AUTH_AGAIN) {
! 720: rc = SSH_AGAIN;
! 721: break;
! 722: }
! 723:
! 724: if(rc == SSH_AUTH_SUCCESS) {
! 725: sshc->authed = TRUE;
! 726: infof(data, "Authenticated with none\n");
! 727: state(conn, SSH_AUTH_DONE);
! 728: break;
! 729: }
! 730: else if(rc == SSH_AUTH_ERROR) {
! 731: MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
! 732: }
! 733:
! 734: sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL);
! 735: if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
! 736: state(conn, SSH_AUTH_PKEY_INIT);
! 737: infof(data, "Authentication using SSH public key file\n");
! 738: }
! 739: else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
! 740: state(conn, SSH_AUTH_GSSAPI);
! 741: }
! 742: else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
! 743: state(conn, SSH_AUTH_KEY_INIT);
! 744: }
! 745: else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
! 746: state(conn, SSH_AUTH_PASS_INIT);
! 747: }
! 748: else { /* unsupported authentication method */
! 749: MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
! 750: }
! 751:
! 752: break;
! 753: }
! 754: case SSH_AUTH_PKEY_INIT:
! 755: if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
! 756: MOVE_TO_SECONDARY_AUTH;
! 757: }
! 758:
! 759: /* Two choices, (1) private key was given on CMD,
! 760: * (2) use the "default" keys. */
! 761: if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
! 762: if(sshc->pubkey && !data->set.ssl.key_passwd) {
! 763: rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
! 764: sshc->pubkey);
! 765: if(rc == SSH_AUTH_AGAIN) {
! 766: rc = SSH_AGAIN;
! 767: break;
! 768: }
! 769:
! 770: if(rc != SSH_OK) {
! 771: MOVE_TO_SECONDARY_AUTH;
! 772: }
! 773: }
! 774:
! 775: rc = ssh_pki_import_privkey_file(data->
! 776: set.str[STRING_SSH_PRIVATE_KEY],
! 777: data->set.ssl.key_passwd, NULL,
! 778: NULL, &sshc->privkey);
! 779: if(rc != SSH_OK) {
! 780: failf(data, "Could not load private key file %s",
! 781: data->set.str[STRING_SSH_PRIVATE_KEY]);
! 782: MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
! 783: break;
! 784: }
! 785:
! 786: state(conn, SSH_AUTH_PKEY);
! 787: break;
! 788:
! 789: }
! 790: else {
! 791: rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
! 792: data->set.ssl.key_passwd);
! 793: if(rc == SSH_AUTH_AGAIN) {
! 794: rc = SSH_AGAIN;
! 795: break;
! 796: }
! 797: if(rc == SSH_AUTH_SUCCESS) {
! 798: rc = SSH_OK;
! 799: sshc->authed = TRUE;
! 800: infof(data, "Completed public key authentication\n");
! 801: state(conn, SSH_AUTH_DONE);
! 802: break;
! 803: }
! 804:
! 805: MOVE_TO_SECONDARY_AUTH;
! 806: }
! 807: break;
! 808: case SSH_AUTH_PKEY:
! 809: rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
! 810: if(rc == SSH_AUTH_AGAIN) {
! 811: rc = SSH_AGAIN;
! 812: break;
! 813: }
! 814:
! 815: if(rc == SSH_AUTH_SUCCESS) {
! 816: sshc->authed = TRUE;
! 817: infof(data, "Completed public key authentication\n");
! 818: state(conn, SSH_AUTH_DONE);
! 819: break;
! 820: }
! 821: else {
! 822: infof(data, "Failed public key authentication (rc: %d)\n", rc);
! 823: MOVE_TO_SECONDARY_AUTH;
! 824: }
! 825: break;
! 826:
! 827: case SSH_AUTH_GSSAPI:
! 828: if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) {
! 829: MOVE_TO_TERTIARY_AUTH;
! 830: }
! 831:
! 832: rc = ssh_userauth_gssapi(sshc->ssh_session);
! 833: if(rc == SSH_AUTH_AGAIN) {
! 834: rc = SSH_AGAIN;
! 835: break;
! 836: }
! 837:
! 838: if(rc == SSH_AUTH_SUCCESS) {
! 839: rc = SSH_OK;
! 840: sshc->authed = TRUE;
! 841: infof(data, "Completed gssapi authentication\n");
! 842: state(conn, SSH_AUTH_DONE);
! 843: break;
! 844: }
! 845:
! 846: MOVE_TO_TERTIARY_AUTH;
! 847: break;
! 848:
! 849: case SSH_AUTH_KEY_INIT:
! 850: if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
! 851: state(conn, SSH_AUTH_KEY);
! 852: }
! 853: else {
! 854: MOVE_TO_LAST_AUTH;
! 855: }
! 856: break;
! 857:
! 858: case SSH_AUTH_KEY:
! 859:
! 860: /* Authentication failed. Continue with keyboard-interactive now. */
! 861: rc = myssh_auth_interactive(conn);
! 862: if(rc == SSH_AGAIN) {
! 863: break;
! 864: }
! 865: if(rc == SSH_OK) {
! 866: sshc->authed = TRUE;
! 867: infof(data, "completed keyboard interactive authentication\n");
! 868: }
! 869: state(conn, SSH_AUTH_DONE);
! 870: break;
! 871:
! 872: case SSH_AUTH_PASS_INIT:
! 873: if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) {
! 874: /* Host key authentication is intentionally not implemented */
! 875: MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
! 876: }
! 877: state(conn, SSH_AUTH_PASS);
! 878: /* FALLTHROUGH */
! 879:
! 880: case SSH_AUTH_PASS:
! 881: rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd);
! 882: if(rc == SSH_AUTH_AGAIN) {
! 883: rc = SSH_AGAIN;
! 884: break;
! 885: }
! 886:
! 887: if(rc == SSH_AUTH_SUCCESS) {
! 888: sshc->authed = TRUE;
! 889: infof(data, "Completed password authentication\n");
! 890: state(conn, SSH_AUTH_DONE);
! 891: }
! 892: else {
! 893: MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
! 894: }
! 895: break;
! 896:
! 897: case SSH_AUTH_DONE:
! 898: if(!sshc->authed) {
! 899: failf(data, "Authentication failure");
! 900: MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
! 901: break;
! 902: }
! 903:
! 904: /*
! 905: * At this point we have an authenticated ssh session.
! 906: */
! 907: infof(data, "Authentication complete\n");
! 908:
! 909: Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */
! 910:
! 911: conn->sockfd = sock;
! 912: conn->writesockfd = CURL_SOCKET_BAD;
! 913:
! 914: if(conn->handler->protocol == CURLPROTO_SFTP) {
! 915: state(conn, SSH_SFTP_INIT);
! 916: break;
! 917: }
! 918: infof(data, "SSH CONNECT phase done\n");
! 919: state(conn, SSH_STOP);
! 920: break;
! 921:
! 922: case SSH_SFTP_INIT:
! 923: ssh_set_blocking(sshc->ssh_session, 1);
! 924:
! 925: sshc->sftp_session = sftp_new(sshc->ssh_session);
! 926: if(!sshc->sftp_session) {
! 927: failf(data, "Failure initializing sftp session: %s",
! 928: ssh_get_error(sshc->ssh_session));
! 929: MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
! 930: break;
! 931: }
! 932:
! 933: rc = sftp_init(sshc->sftp_session);
! 934: if(rc != SSH_OK) {
! 935: rc = sftp_get_error(sshc->sftp_session);
! 936: failf(data, "Failure initializing sftp session: %s",
! 937: ssh_get_error(sshc->ssh_session));
! 938: MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(rc));
! 939: break;
! 940: }
! 941: state(conn, SSH_SFTP_REALPATH);
! 942: /* FALLTHROUGH */
! 943: case SSH_SFTP_REALPATH:
! 944: /*
! 945: * Get the "home" directory
! 946: */
! 947: sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, ".");
! 948: if(sshc->homedir == NULL) {
! 949: MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
! 950: }
! 951: conn->data->state.most_recent_ftp_entrypath = sshc->homedir;
! 952:
! 953: /* This is the last step in the SFTP connect phase. Do note that while
! 954: we get the homedir here, we get the "workingpath" in the DO action
! 955: since the homedir will remain the same between request but the
! 956: working path will not. */
! 957: DEBUGF(infof(data, "SSH CONNECT phase done\n"));
! 958: state(conn, SSH_STOP);
! 959: break;
! 960:
! 961: case SSH_SFTP_QUOTE_INIT:
! 962:
! 963: result = Curl_getworkingpath(conn, sshc->homedir, &protop->path);
! 964: if(result) {
! 965: sshc->actualcode = result;
! 966: state(conn, SSH_STOP);
! 967: break;
! 968: }
! 969:
! 970: if(data->set.quote) {
! 971: infof(data, "Sending quote commands\n");
! 972: sshc->quote_item = data->set.quote;
! 973: state(conn, SSH_SFTP_QUOTE);
! 974: }
! 975: else {
! 976: state(conn, SSH_SFTP_GETINFO);
! 977: }
! 978: break;
! 979:
! 980: case SSH_SFTP_POSTQUOTE_INIT:
! 981: if(data->set.postquote) {
! 982: infof(data, "Sending quote commands\n");
! 983: sshc->quote_item = data->set.postquote;
! 984: state(conn, SSH_SFTP_QUOTE);
! 985: }
! 986: else {
! 987: state(conn, SSH_STOP);
! 988: }
! 989: break;
! 990:
! 991: case SSH_SFTP_QUOTE:
! 992: /* Send any quote commands */
! 993: sftp_quote(conn);
! 994: break;
! 995:
! 996: case SSH_SFTP_NEXT_QUOTE:
! 997: Curl_safefree(sshc->quote_path1);
! 998: Curl_safefree(sshc->quote_path2);
! 999:
! 1000: sshc->quote_item = sshc->quote_item->next;
! 1001:
! 1002: if(sshc->quote_item) {
! 1003: state(conn, SSH_SFTP_QUOTE);
! 1004: }
! 1005: else {
! 1006: if(sshc->nextstate != SSH_NO_STATE) {
! 1007: state(conn, sshc->nextstate);
! 1008: sshc->nextstate = SSH_NO_STATE;
! 1009: }
! 1010: else {
! 1011: state(conn, SSH_SFTP_GETINFO);
! 1012: }
! 1013: }
! 1014: break;
! 1015:
! 1016: case SSH_SFTP_QUOTE_STAT:
! 1017: sftp_quote_stat(conn);
! 1018: break;
! 1019:
! 1020: case SSH_SFTP_QUOTE_SETSTAT:
! 1021: rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
! 1022: sshc->quote_attrs);
! 1023: if(rc != 0 && !sshc->acceptfail) {
! 1024: Curl_safefree(sshc->quote_path1);
! 1025: Curl_safefree(sshc->quote_path2);
! 1026: failf(data, "Attempt to set SFTP stats failed: %s",
! 1027: ssh_get_error(sshc->ssh_session));
! 1028: state(conn, SSH_SFTP_CLOSE);
! 1029: sshc->nextstate = SSH_NO_STATE;
! 1030: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1031: /* sshc->actualcode = sftp_error_to_CURLE(err);
! 1032: * we do not send the actual error; we return
! 1033: * the error the libssh2 backend is returning */
! 1034: break;
! 1035: }
! 1036: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1037: break;
! 1038:
! 1039: case SSH_SFTP_QUOTE_SYMLINK:
! 1040: rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2,
! 1041: sshc->quote_path1);
! 1042: if(rc != 0 && !sshc->acceptfail) {
! 1043: Curl_safefree(sshc->quote_path1);
! 1044: Curl_safefree(sshc->quote_path2);
! 1045: failf(data, "symlink command failed: %s",
! 1046: ssh_get_error(sshc->ssh_session));
! 1047: state(conn, SSH_SFTP_CLOSE);
! 1048: sshc->nextstate = SSH_NO_STATE;
! 1049: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1050: break;
! 1051: }
! 1052: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1053: break;
! 1054:
! 1055: case SSH_SFTP_QUOTE_MKDIR:
! 1056: rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1,
! 1057: (mode_t)data->set.new_directory_perms);
! 1058: if(rc != 0 && !sshc->acceptfail) {
! 1059: Curl_safefree(sshc->quote_path1);
! 1060: failf(data, "mkdir command failed: %s",
! 1061: ssh_get_error(sshc->ssh_session));
! 1062: state(conn, SSH_SFTP_CLOSE);
! 1063: sshc->nextstate = SSH_NO_STATE;
! 1064: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1065: break;
! 1066: }
! 1067: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1068: break;
! 1069:
! 1070: case SSH_SFTP_QUOTE_RENAME:
! 1071: rc = sftp_rename(sshc->sftp_session, sshc->quote_path1,
! 1072: sshc->quote_path2);
! 1073: if(rc != 0 && !sshc->acceptfail) {
! 1074: Curl_safefree(sshc->quote_path1);
! 1075: Curl_safefree(sshc->quote_path2);
! 1076: failf(data, "rename command failed: %s",
! 1077: ssh_get_error(sshc->ssh_session));
! 1078: state(conn, SSH_SFTP_CLOSE);
! 1079: sshc->nextstate = SSH_NO_STATE;
! 1080: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1081: break;
! 1082: }
! 1083: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1084: break;
! 1085:
! 1086: case SSH_SFTP_QUOTE_RMDIR:
! 1087: rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
! 1088: if(rc != 0 && !sshc->acceptfail) {
! 1089: Curl_safefree(sshc->quote_path1);
! 1090: failf(data, "rmdir command failed: %s",
! 1091: ssh_get_error(sshc->ssh_session));
! 1092: state(conn, SSH_SFTP_CLOSE);
! 1093: sshc->nextstate = SSH_NO_STATE;
! 1094: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1095: break;
! 1096: }
! 1097: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1098: break;
! 1099:
! 1100: case SSH_SFTP_QUOTE_UNLINK:
! 1101: rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1);
! 1102: if(rc != 0 && !sshc->acceptfail) {
! 1103: Curl_safefree(sshc->quote_path1);
! 1104: failf(data, "rm command failed: %s",
! 1105: ssh_get_error(sshc->ssh_session));
! 1106: state(conn, SSH_SFTP_CLOSE);
! 1107: sshc->nextstate = SSH_NO_STATE;
! 1108: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1109: break;
! 1110: }
! 1111: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1112: break;
! 1113:
! 1114: case SSH_SFTP_QUOTE_STATVFS:
! 1115: {
! 1116: sftp_statvfs_t statvfs;
! 1117:
! 1118: statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1);
! 1119: if(!statvfs && !sshc->acceptfail) {
! 1120: Curl_safefree(sshc->quote_path1);
! 1121: failf(data, "statvfs command failed: %s",
! 1122: ssh_get_error(sshc->ssh_session));
! 1123: state(conn, SSH_SFTP_CLOSE);
! 1124: sshc->nextstate = SSH_NO_STATE;
! 1125: sshc->actualcode = CURLE_QUOTE_ERROR;
! 1126: break;
! 1127: }
! 1128: else if(statvfs) {
! 1129: char *tmp = aprintf("statvfs:\n"
! 1130: "f_bsize: %llu\n" "f_frsize: %llu\n"
! 1131: "f_blocks: %llu\n" "f_bfree: %llu\n"
! 1132: "f_bavail: %llu\n" "f_files: %llu\n"
! 1133: "f_ffree: %llu\n" "f_favail: %llu\n"
! 1134: "f_fsid: %llu\n" "f_flag: %llu\n"
! 1135: "f_namemax: %llu\n",
! 1136: statvfs->f_bsize, statvfs->f_frsize,
! 1137: statvfs->f_blocks, statvfs->f_bfree,
! 1138: statvfs->f_bavail, statvfs->f_files,
! 1139: statvfs->f_ffree, statvfs->f_favail,
! 1140: statvfs->f_fsid, statvfs->f_flag,
! 1141: statvfs->f_namemax);
! 1142: sftp_statvfs_free(statvfs);
! 1143:
! 1144: if(!tmp) {
! 1145: result = CURLE_OUT_OF_MEMORY;
! 1146: state(conn, SSH_SFTP_CLOSE);
! 1147: sshc->nextstate = SSH_NO_STATE;
! 1148: break;
! 1149: }
! 1150:
! 1151: result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp));
! 1152: free(tmp);
! 1153: if(result) {
! 1154: state(conn, SSH_SFTP_CLOSE);
! 1155: sshc->nextstate = SSH_NO_STATE;
! 1156: sshc->actualcode = result;
! 1157: }
! 1158: }
! 1159: state(conn, SSH_SFTP_NEXT_QUOTE);
! 1160: break;
! 1161: }
! 1162:
! 1163: case SSH_SFTP_GETINFO:
! 1164: if(data->set.get_filetime) {
! 1165: state(conn, SSH_SFTP_FILETIME);
! 1166: }
! 1167: else {
! 1168: state(conn, SSH_SFTP_TRANS_INIT);
! 1169: }
! 1170: break;
! 1171:
! 1172: case SSH_SFTP_FILETIME:
! 1173: {
! 1174: sftp_attributes attrs;
! 1175:
! 1176: attrs = sftp_stat(sshc->sftp_session, protop->path);
! 1177: if(attrs != 0) {
! 1178: data->info.filetime = attrs->mtime;
! 1179: sftp_attributes_free(attrs);
! 1180: }
! 1181:
! 1182: state(conn, SSH_SFTP_TRANS_INIT);
! 1183: break;
! 1184: }
! 1185:
! 1186: case SSH_SFTP_TRANS_INIT:
! 1187: if(data->set.upload)
! 1188: state(conn, SSH_SFTP_UPLOAD_INIT);
! 1189: else {
! 1190: if(protop->path[strlen(protop->path)-1] == '/')
! 1191: state(conn, SSH_SFTP_READDIR_INIT);
! 1192: else
! 1193: state(conn, SSH_SFTP_DOWNLOAD_INIT);
! 1194: }
! 1195: break;
! 1196:
! 1197: case SSH_SFTP_UPLOAD_INIT:
! 1198: {
! 1199: int flags;
! 1200:
! 1201: if(data->state.resume_from != 0) {
! 1202: sftp_attributes attrs;
! 1203:
! 1204: if(data->state.resume_from < 0) {
! 1205: attrs = sftp_stat(sshc->sftp_session, protop->path);
! 1206: if(attrs != 0) {
! 1207: curl_off_t size = attrs->size;
! 1208: if(size < 0) {
! 1209: failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
! 1210: MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
! 1211: }
! 1212: data->state.resume_from = attrs->size;
! 1213:
! 1214: sftp_attributes_free(attrs);
! 1215: }
! 1216: else {
! 1217: data->state.resume_from = 0;
! 1218: }
! 1219: }
! 1220: }
! 1221:
! 1222: if(data->set.ftp_append)
! 1223: /* Try to open for append, but create if nonexisting */
! 1224: flags = O_WRONLY|O_CREAT|O_APPEND;
! 1225: else if(data->state.resume_from > 0)
! 1226: /* If we have restart position then open for append */
! 1227: flags = O_WRONLY|O_APPEND;
! 1228: else
! 1229: /* Clear file before writing (normal behaviour) */
! 1230: flags = O_WRONLY|O_CREAT|O_TRUNC;
! 1231:
! 1232: if(sshc->sftp_file)
! 1233: sftp_close(sshc->sftp_file);
! 1234: sshc->sftp_file =
! 1235: sftp_open(sshc->sftp_session, protop->path,
! 1236: flags, (mode_t)data->set.new_file_perms);
! 1237: if(!sshc->sftp_file) {
! 1238: err = sftp_get_error(sshc->sftp_session);
! 1239:
! 1240: if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
! 1241: err == SSH_FX_NO_SUCH_PATH)) &&
! 1242: (data->set.ftp_create_missing_dirs &&
! 1243: (strlen(protop->path) > 1))) {
! 1244: /* try to create the path remotely */
! 1245: rc = 0;
! 1246: sshc->secondCreateDirs = 1;
! 1247: state(conn, SSH_SFTP_CREATE_DIRS_INIT);
! 1248: break;
! 1249: }
! 1250: else {
! 1251: MOVE_TO_SFTP_CLOSE_STATE();
! 1252: }
! 1253: }
! 1254:
! 1255: /* If we have a restart point then we need to seek to the correct
! 1256: position. */
! 1257: if(data->state.resume_from > 0) {
! 1258: /* Let's read off the proper amount of bytes from the input. */
! 1259: if(conn->seek_func) {
! 1260: Curl_set_in_callback(data, true);
! 1261: seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
! 1262: SEEK_SET);
! 1263: Curl_set_in_callback(data, false);
! 1264: }
! 1265:
! 1266: if(seekerr != CURL_SEEKFUNC_OK) {
! 1267: curl_off_t passed = 0;
! 1268:
! 1269: if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
! 1270: failf(data, "Could not seek stream");
! 1271: return CURLE_FTP_COULDNT_USE_REST;
! 1272: }
! 1273: /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
! 1274: do {
! 1275: size_t readthisamountnow =
! 1276: (data->state.resume_from - passed > data->set.buffer_size) ?
! 1277: (size_t)data->set.buffer_size :
! 1278: curlx_sotouz(data->state.resume_from - passed);
! 1279:
! 1280: size_t actuallyread =
! 1281: data->state.fread_func(data->state.buffer, 1,
! 1282: readthisamountnow, data->state.in);
! 1283:
! 1284: passed += actuallyread;
! 1285: if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
! 1286: /* this checks for greater-than only to make sure that the
! 1287: CURL_READFUNC_ABORT return code still aborts */
! 1288: failf(data, "Failed to read data");
! 1289: MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
! 1290: }
! 1291: } while(passed < data->state.resume_from);
! 1292: }
! 1293:
! 1294: /* now, decrease the size of the read */
! 1295: if(data->state.infilesize > 0) {
! 1296: data->state.infilesize -= data->state.resume_from;
! 1297: data->req.size = data->state.infilesize;
! 1298: Curl_pgrsSetUploadSize(data, data->state.infilesize);
! 1299: }
! 1300:
! 1301: rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
! 1302: if(rc != 0) {
! 1303: MOVE_TO_SFTP_CLOSE_STATE();
! 1304: }
! 1305: }
! 1306: if(data->state.infilesize > 0) {
! 1307: data->req.size = data->state.infilesize;
! 1308: Curl_pgrsSetUploadSize(data, data->state.infilesize);
! 1309: }
! 1310: /* upload data */
! 1311: Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
! 1312:
! 1313: /* not set by Curl_setup_transfer to preserve keepon bits */
! 1314: conn->sockfd = conn->writesockfd;
! 1315:
! 1316: /* store this original bitmask setup to use later on if we can't
! 1317: figure out a "real" bitmask */
! 1318: sshc->orig_waitfor = data->req.keepon;
! 1319:
! 1320: /* we want to use the _sending_ function even when the socket turns
! 1321: out readable as the underlying libssh sftp send function will deal
! 1322: with both accordingly */
! 1323: conn->cselect_bits = CURL_CSELECT_OUT;
! 1324:
! 1325: /* since we don't really wait for anything at this point, we want the
! 1326: state machine to move on as soon as possible so we set a very short
! 1327: timeout here */
! 1328: Curl_expire(data, 0, EXPIRE_RUN_NOW);
! 1329:
! 1330: state(conn, SSH_STOP);
! 1331: break;
! 1332: }
! 1333:
! 1334: case SSH_SFTP_CREATE_DIRS_INIT:
! 1335: if(strlen(protop->path) > 1) {
! 1336: sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
! 1337: state(conn, SSH_SFTP_CREATE_DIRS);
! 1338: }
! 1339: else {
! 1340: state(conn, SSH_SFTP_UPLOAD_INIT);
! 1341: }
! 1342: break;
! 1343:
! 1344: case SSH_SFTP_CREATE_DIRS:
! 1345: sshc->slash_pos = strchr(sshc->slash_pos, '/');
! 1346: if(sshc->slash_pos) {
! 1347: *sshc->slash_pos = 0;
! 1348:
! 1349: infof(data, "Creating directory '%s'\n", protop->path);
! 1350: state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
! 1351: break;
! 1352: }
! 1353: state(conn, SSH_SFTP_UPLOAD_INIT);
! 1354: break;
! 1355:
! 1356: case SSH_SFTP_CREATE_DIRS_MKDIR:
! 1357: /* 'mode' - parameter is preliminary - default to 0644 */
! 1358: rc = sftp_mkdir(sshc->sftp_session, protop->path,
! 1359: (mode_t)data->set.new_directory_perms);
! 1360: *sshc->slash_pos = '/';
! 1361: ++sshc->slash_pos;
! 1362: if(rc < 0) {
! 1363: /*
! 1364: * Abort if failure wasn't that the dir already exists or the
! 1365: * permission was denied (creation might succeed further down the
! 1366: * path) - retry on unspecific FAILURE also
! 1367: */
! 1368: err = sftp_get_error(sshc->sftp_session);
! 1369: if((err != SSH_FX_FILE_ALREADY_EXISTS) &&
! 1370: (err != SSH_FX_FAILURE) &&
! 1371: (err != SSH_FX_PERMISSION_DENIED)) {
! 1372: MOVE_TO_SFTP_CLOSE_STATE();
! 1373: }
! 1374: rc = 0; /* clear rc and continue */
! 1375: }
! 1376: state(conn, SSH_SFTP_CREATE_DIRS);
! 1377: break;
! 1378:
! 1379: case SSH_SFTP_READDIR_INIT:
! 1380: Curl_pgrsSetDownloadSize(data, -1);
! 1381: if(data->set.opt_no_body) {
! 1382: state(conn, SSH_STOP);
! 1383: break;
! 1384: }
! 1385:
! 1386: /*
! 1387: * This is a directory that we are trying to get, so produce a directory
! 1388: * listing
! 1389: */
! 1390: sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
! 1391: protop->path);
! 1392: if(!sshc->sftp_dir) {
! 1393: failf(data, "Could not open directory for reading: %s",
! 1394: ssh_get_error(sshc->ssh_session));
! 1395: MOVE_TO_SFTP_CLOSE_STATE();
! 1396: }
! 1397: state(conn, SSH_SFTP_READDIR);
! 1398: break;
! 1399:
! 1400: case SSH_SFTP_READDIR:
! 1401:
! 1402: if(sshc->readdir_attrs)
! 1403: sftp_attributes_free(sshc->readdir_attrs);
! 1404:
! 1405: sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir);
! 1406: if(sshc->readdir_attrs) {
! 1407: sshc->readdir_filename = sshc->readdir_attrs->name;
! 1408: sshc->readdir_longentry = sshc->readdir_attrs->longname;
! 1409: sshc->readdir_len = strlen(sshc->readdir_filename);
! 1410:
! 1411: if(data->set.ftp_list_only) {
! 1412: char *tmpLine;
! 1413:
! 1414: tmpLine = aprintf("%s\n", sshc->readdir_filename);
! 1415: if(tmpLine == NULL) {
! 1416: state(conn, SSH_SFTP_CLOSE);
! 1417: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 1418: break;
! 1419: }
! 1420: result = Curl_client_write(conn, CLIENTWRITE_BODY,
! 1421: tmpLine, sshc->readdir_len + 1);
! 1422: free(tmpLine);
! 1423:
! 1424: if(result) {
! 1425: state(conn, SSH_STOP);
! 1426: break;
! 1427: }
! 1428: /* since this counts what we send to the client, we include the
! 1429: newline in this counter */
! 1430: data->req.bytecount += sshc->readdir_len + 1;
! 1431:
! 1432: /* output debug output if that is requested */
! 1433: if(data->set.verbose) {
! 1434: Curl_debug(data, CURLINFO_DATA_OUT,
! 1435: (char *)sshc->readdir_filename,
! 1436: sshc->readdir_len);
! 1437: }
! 1438: }
! 1439: else {
! 1440: sshc->readdir_currLen = strlen(sshc->readdir_longentry);
! 1441: sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
! 1442: sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
! 1443: if(!sshc->readdir_line) {
! 1444: state(conn, SSH_SFTP_CLOSE);
! 1445: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 1446: break;
! 1447: }
! 1448:
! 1449: memcpy(sshc->readdir_line, sshc->readdir_longentry,
! 1450: sshc->readdir_currLen);
! 1451: if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
! 1452: ((sshc->readdir_attrs->permissions & S_IFMT) ==
! 1453: S_IFLNK)) {
! 1454: sshc->readdir_linkPath = malloc(PATH_MAX + 1);
! 1455: if(sshc->readdir_linkPath == NULL) {
! 1456: state(conn, SSH_SFTP_CLOSE);
! 1457: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 1458: break;
! 1459: }
! 1460:
! 1461: msnprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", protop->path,
! 1462: sshc->readdir_filename);
! 1463:
! 1464: state(conn, SSH_SFTP_READDIR_LINK);
! 1465: break;
! 1466: }
! 1467: state(conn, SSH_SFTP_READDIR_BOTTOM);
! 1468: break;
! 1469: }
! 1470: }
! 1471: else if(sftp_dir_eof(sshc->sftp_dir)) {
! 1472: state(conn, SSH_SFTP_READDIR_DONE);
! 1473: break;
! 1474: }
! 1475: else {
! 1476: failf(data, "Could not open remote file for reading: %s",
! 1477: ssh_get_error(sshc->ssh_session));
! 1478: MOVE_TO_SFTP_CLOSE_STATE();
! 1479: break;
! 1480: }
! 1481: break;
! 1482:
! 1483: case SSH_SFTP_READDIR_LINK:
! 1484: if(sshc->readdir_link_attrs)
! 1485: sftp_attributes_free(sshc->readdir_link_attrs);
! 1486:
! 1487: sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session,
! 1488: sshc->readdir_linkPath);
! 1489: if(sshc->readdir_link_attrs == 0) {
! 1490: failf(data, "Could not read symlink for reading: %s",
! 1491: ssh_get_error(sshc->ssh_session));
! 1492: MOVE_TO_SFTP_CLOSE_STATE();
! 1493: }
! 1494:
! 1495: if(sshc->readdir_link_attrs->name == NULL) {
! 1496: sshc->readdir_tmp = sftp_readlink(sshc->sftp_session,
! 1497: sshc->readdir_linkPath);
! 1498: if(sshc->readdir_filename == NULL)
! 1499: sshc->readdir_len = 0;
! 1500: else
! 1501: sshc->readdir_len = strlen(sshc->readdir_tmp);
! 1502: sshc->readdir_longentry = NULL;
! 1503: sshc->readdir_filename = sshc->readdir_tmp;
! 1504: }
! 1505: else {
! 1506: sshc->readdir_len = strlen(sshc->readdir_link_attrs->name);
! 1507: sshc->readdir_filename = sshc->readdir_link_attrs->name;
! 1508: sshc->readdir_longentry = sshc->readdir_link_attrs->longname;
! 1509: }
! 1510:
! 1511: Curl_safefree(sshc->readdir_linkPath);
! 1512:
! 1513: /* get room for the filename and extra output */
! 1514: sshc->readdir_totalLen += 4 + sshc->readdir_len;
! 1515: new_readdir_line = Curl_saferealloc(sshc->readdir_line,
! 1516: sshc->readdir_totalLen);
! 1517: if(!new_readdir_line) {
! 1518: sshc->readdir_line = NULL;
! 1519: state(conn, SSH_SFTP_CLOSE);
! 1520: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 1521: break;
! 1522: }
! 1523: sshc->readdir_line = new_readdir_line;
! 1524:
! 1525: sshc->readdir_currLen += msnprintf(sshc->readdir_line +
! 1526: sshc->readdir_currLen,
! 1527: sshc->readdir_totalLen -
! 1528: sshc->readdir_currLen,
! 1529: " -> %s",
! 1530: sshc->readdir_filename);
! 1531:
! 1532: sftp_attributes_free(sshc->readdir_link_attrs);
! 1533: sshc->readdir_link_attrs = NULL;
! 1534: sshc->readdir_filename = NULL;
! 1535: sshc->readdir_longentry = NULL;
! 1536:
! 1537: state(conn, SSH_SFTP_READDIR_BOTTOM);
! 1538: /* FALLTHROUGH */
! 1539: case SSH_SFTP_READDIR_BOTTOM:
! 1540: sshc->readdir_currLen += msnprintf(sshc->readdir_line +
! 1541: sshc->readdir_currLen,
! 1542: sshc->readdir_totalLen -
! 1543: sshc->readdir_currLen, "\n");
! 1544: result = Curl_client_write(conn, CLIENTWRITE_BODY,
! 1545: sshc->readdir_line,
! 1546: sshc->readdir_currLen);
! 1547:
! 1548: if(!result) {
! 1549:
! 1550: /* output debug output if that is requested */
! 1551: if(data->set.verbose) {
! 1552: Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
! 1553: sshc->readdir_currLen);
! 1554: }
! 1555: data->req.bytecount += sshc->readdir_currLen;
! 1556: }
! 1557: Curl_safefree(sshc->readdir_line);
! 1558: ssh_string_free_char(sshc->readdir_tmp);
! 1559: sshc->readdir_tmp = NULL;
! 1560:
! 1561: if(result) {
! 1562: state(conn, SSH_STOP);
! 1563: }
! 1564: else
! 1565: state(conn, SSH_SFTP_READDIR);
! 1566: break;
! 1567:
! 1568: case SSH_SFTP_READDIR_DONE:
! 1569: sftp_closedir(sshc->sftp_dir);
! 1570: sshc->sftp_dir = NULL;
! 1571:
! 1572: /* no data to transfer */
! 1573: Curl_setup_transfer(data, -1, -1, FALSE, -1);
! 1574: state(conn, SSH_STOP);
! 1575: break;
! 1576:
! 1577: case SSH_SFTP_DOWNLOAD_INIT:
! 1578: /*
! 1579: * Work on getting the specified file
! 1580: */
! 1581: if(sshc->sftp_file)
! 1582: sftp_close(sshc->sftp_file);
! 1583:
! 1584: sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
! 1585: O_RDONLY, (mode_t)data->set.new_file_perms);
! 1586: if(!sshc->sftp_file) {
! 1587: failf(data, "Could not open remote file for reading: %s",
! 1588: ssh_get_error(sshc->ssh_session));
! 1589:
! 1590: MOVE_TO_SFTP_CLOSE_STATE();
! 1591: }
! 1592:
! 1593: state(conn, SSH_SFTP_DOWNLOAD_STAT);
! 1594: break;
! 1595:
! 1596: case SSH_SFTP_DOWNLOAD_STAT:
! 1597: {
! 1598: sftp_attributes attrs;
! 1599: curl_off_t size;
! 1600:
! 1601: attrs = sftp_fstat(sshc->sftp_file);
! 1602: if(!attrs ||
! 1603: !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
! 1604: (attrs->size == 0)) {
! 1605: /*
! 1606: * sftp_fstat didn't return an error, so maybe the server
! 1607: * just doesn't support stat()
! 1608: * OR the server doesn't return a file size with a stat()
! 1609: * OR file size is 0
! 1610: */
! 1611: data->req.size = -1;
! 1612: data->req.maxdownload = -1;
! 1613: Curl_pgrsSetDownloadSize(data, -1);
! 1614: size = 0;
! 1615: }
! 1616: else {
! 1617: size = attrs->size;
! 1618:
! 1619: sftp_attributes_free(attrs);
! 1620:
! 1621: if(size < 0) {
! 1622: failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
! 1623: return CURLE_BAD_DOWNLOAD_RESUME;
! 1624: }
! 1625: if(conn->data->state.use_range) {
! 1626: curl_off_t from, to;
! 1627: char *ptr;
! 1628: char *ptr2;
! 1629: CURLofft to_t;
! 1630: CURLofft from_t;
! 1631:
! 1632: from_t = curlx_strtoofft(conn->data->state.range, &ptr, 0, &from);
! 1633: if(from_t == CURL_OFFT_FLOW) {
! 1634: return CURLE_RANGE_ERROR;
! 1635: }
! 1636: while(*ptr && (ISSPACE(*ptr) || (*ptr == '-')))
! 1637: ptr++;
! 1638: to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
! 1639: if(to_t == CURL_OFFT_FLOW) {
! 1640: return CURLE_RANGE_ERROR;
! 1641: }
! 1642: if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
! 1643: || (to >= size)) {
! 1644: to = size - 1;
! 1645: }
! 1646: if(from_t) {
! 1647: /* from is relative to end of file */
! 1648: from = size - to;
! 1649: to = size - 1;
! 1650: }
! 1651: if(from > size) {
! 1652: failf(data, "Offset (%"
! 1653: CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
! 1654: CURL_FORMAT_CURL_OFF_T ")", from, size);
! 1655: return CURLE_BAD_DOWNLOAD_RESUME;
! 1656: }
! 1657: if(from > to) {
! 1658: from = to;
! 1659: size = 0;
! 1660: }
! 1661: else {
! 1662: size = to - from + 1;
! 1663: }
! 1664:
! 1665: rc = sftp_seek64(sshc->sftp_file, from);
! 1666: if(rc != 0) {
! 1667: MOVE_TO_SFTP_CLOSE_STATE();
! 1668: }
! 1669: }
! 1670: data->req.size = size;
! 1671: data->req.maxdownload = size;
! 1672: Curl_pgrsSetDownloadSize(data, size);
! 1673: }
! 1674:
! 1675: /* We can resume if we can seek to the resume position */
! 1676: if(data->state.resume_from) {
! 1677: if(data->state.resume_from < 0) {
! 1678: /* We're supposed to download the last abs(from) bytes */
! 1679: if((curl_off_t)size < -data->state.resume_from) {
! 1680: failf(data, "Offset (%"
! 1681: CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
! 1682: CURL_FORMAT_CURL_OFF_T ")",
! 1683: data->state.resume_from, size);
! 1684: return CURLE_BAD_DOWNLOAD_RESUME;
! 1685: }
! 1686: /* download from where? */
! 1687: data->state.resume_from += size;
! 1688: }
! 1689: else {
! 1690: if((curl_off_t)size < data->state.resume_from) {
! 1691: failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
! 1692: ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
! 1693: data->state.resume_from, size);
! 1694: return CURLE_BAD_DOWNLOAD_RESUME;
! 1695: }
! 1696: }
! 1697: /* Now store the number of bytes we are expected to download */
! 1698: data->req.size = size - data->state.resume_from;
! 1699: data->req.maxdownload = size - data->state.resume_from;
! 1700: Curl_pgrsSetDownloadSize(data,
! 1701: size - data->state.resume_from);
! 1702:
! 1703: rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
! 1704: if(rc != 0) {
! 1705: MOVE_TO_SFTP_CLOSE_STATE();
! 1706: }
! 1707: }
! 1708: }
! 1709:
! 1710: /* Setup the actual download */
! 1711: if(data->req.size == 0) {
! 1712: /* no data to transfer */
! 1713: Curl_setup_transfer(data, -1, -1, FALSE, -1);
! 1714: infof(data, "File already completely downloaded\n");
! 1715: state(conn, SSH_STOP);
! 1716: break;
! 1717: }
! 1718: Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
! 1719:
! 1720: /* not set by Curl_setup_transfer to preserve keepon bits */
! 1721: conn->writesockfd = conn->sockfd;
! 1722:
! 1723: /* we want to use the _receiving_ function even when the socket turns
! 1724: out writableable as the underlying libssh recv function will deal
! 1725: with both accordingly */
! 1726: conn->cselect_bits = CURL_CSELECT_IN;
! 1727:
! 1728: if(result) {
! 1729: /* this should never occur; the close state should be entered
! 1730: at the time the error occurs */
! 1731: state(conn, SSH_SFTP_CLOSE);
! 1732: sshc->actualcode = result;
! 1733: }
! 1734: else {
! 1735: sshc->sftp_recv_state = 0;
! 1736: state(conn, SSH_STOP);
! 1737: }
! 1738: break;
! 1739:
! 1740: case SSH_SFTP_CLOSE:
! 1741: if(sshc->sftp_file) {
! 1742: sftp_close(sshc->sftp_file);
! 1743: sshc->sftp_file = NULL;
! 1744: }
! 1745: Curl_safefree(protop->path);
! 1746:
! 1747: DEBUGF(infof(data, "SFTP DONE done\n"));
! 1748:
! 1749: /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
! 1750: After nextstate is executed, the control should come back to
! 1751: SSH_SFTP_CLOSE to pass the correct result back */
! 1752: if(sshc->nextstate != SSH_NO_STATE &&
! 1753: sshc->nextstate != SSH_SFTP_CLOSE) {
! 1754: state(conn, sshc->nextstate);
! 1755: sshc->nextstate = SSH_SFTP_CLOSE;
! 1756: }
! 1757: else {
! 1758: state(conn, SSH_STOP);
! 1759: result = sshc->actualcode;
! 1760: }
! 1761: break;
! 1762:
! 1763: case SSH_SFTP_SHUTDOWN:
! 1764: /* during times we get here due to a broken transfer and then the
! 1765: sftp_handle might not have been taken down so make sure that is done
! 1766: before we proceed */
! 1767:
! 1768: if(sshc->sftp_file) {
! 1769: sftp_close(sshc->sftp_file);
! 1770: sshc->sftp_file = NULL;
! 1771: }
! 1772:
! 1773: if(sshc->sftp_session) {
! 1774: sftp_free(sshc->sftp_session);
! 1775: sshc->sftp_session = NULL;
! 1776: }
! 1777:
! 1778: SSH_STRING_FREE_CHAR(sshc->homedir);
! 1779: conn->data->state.most_recent_ftp_entrypath = NULL;
! 1780:
! 1781: state(conn, SSH_SESSION_DISCONNECT);
! 1782: break;
! 1783:
! 1784:
! 1785: case SSH_SCP_TRANS_INIT:
! 1786: result = Curl_getworkingpath(conn, sshc->homedir, &protop->path);
! 1787: if(result) {
! 1788: sshc->actualcode = result;
! 1789: state(conn, SSH_STOP);
! 1790: break;
! 1791: }
! 1792:
! 1793: /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */
! 1794: ssh_set_blocking(sshc->ssh_session, 1);
! 1795:
! 1796: if(data->set.upload) {
! 1797: if(data->state.infilesize < 0) {
! 1798: failf(data, "SCP requires a known file size for upload");
! 1799: sshc->actualcode = CURLE_UPLOAD_FAILED;
! 1800: MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
! 1801: }
! 1802:
! 1803: sshc->scp_session =
! 1804: ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path);
! 1805: state(conn, SSH_SCP_UPLOAD_INIT);
! 1806: }
! 1807: else {
! 1808: sshc->scp_session =
! 1809: ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path);
! 1810: state(conn, SSH_SCP_DOWNLOAD_INIT);
! 1811: }
! 1812:
! 1813: if(!sshc->scp_session) {
! 1814: err_msg = ssh_get_error(sshc->ssh_session);
! 1815: failf(conn->data, "%s", err_msg);
! 1816: MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
! 1817: }
! 1818:
! 1819: break;
! 1820:
! 1821: case SSH_SCP_UPLOAD_INIT:
! 1822:
! 1823: rc = ssh_scp_init(sshc->scp_session);
! 1824: if(rc != SSH_OK) {
! 1825: err_msg = ssh_get_error(sshc->ssh_session);
! 1826: failf(conn->data, "%s", err_msg);
! 1827: MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
! 1828: }
! 1829:
! 1830: rc = ssh_scp_push_file(sshc->scp_session, protop->path,
! 1831: data->state.infilesize,
! 1832: (int)data->set.new_file_perms);
! 1833: if(rc != SSH_OK) {
! 1834: err_msg = ssh_get_error(sshc->ssh_session);
! 1835: failf(conn->data, "%s", err_msg);
! 1836: MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
! 1837: }
! 1838:
! 1839: /* upload data */
! 1840: Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET);
! 1841:
! 1842: /* not set by Curl_setup_transfer to preserve keepon bits */
! 1843: conn->sockfd = conn->writesockfd;
! 1844:
! 1845: /* store this original bitmask setup to use later on if we can't
! 1846: figure out a "real" bitmask */
! 1847: sshc->orig_waitfor = data->req.keepon;
! 1848:
! 1849: /* we want to use the _sending_ function even when the socket turns
! 1850: out readable as the underlying libssh scp send function will deal
! 1851: with both accordingly */
! 1852: conn->cselect_bits = CURL_CSELECT_OUT;
! 1853:
! 1854: state(conn, SSH_STOP);
! 1855:
! 1856: break;
! 1857:
! 1858: case SSH_SCP_DOWNLOAD_INIT:
! 1859:
! 1860: rc = ssh_scp_init(sshc->scp_session);
! 1861: if(rc != SSH_OK) {
! 1862: err_msg = ssh_get_error(sshc->ssh_session);
! 1863: failf(conn->data, "%s", err_msg);
! 1864: MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
! 1865: }
! 1866: state(conn, SSH_SCP_DOWNLOAD);
! 1867: /* FALLTHROUGH */
! 1868:
! 1869: case SSH_SCP_DOWNLOAD:{
! 1870: curl_off_t bytecount;
! 1871:
! 1872: rc = ssh_scp_pull_request(sshc->scp_session);
! 1873: if(rc != SSH_SCP_REQUEST_NEWFILE) {
! 1874: err_msg = ssh_get_error(sshc->ssh_session);
! 1875: failf(conn->data, "%s", err_msg);
! 1876: MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND);
! 1877: break;
! 1878: }
! 1879:
! 1880: /* download data */
! 1881: bytecount = ssh_scp_request_get_size(sshc->scp_session);
! 1882: data->req.maxdownload = (curl_off_t) bytecount;
! 1883: Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
! 1884:
! 1885: /* not set by Curl_setup_transfer to preserve keepon bits */
! 1886: conn->writesockfd = conn->sockfd;
! 1887:
! 1888: /* we want to use the _receiving_ function even when the socket turns
! 1889: out writableable as the underlying libssh recv function will deal
! 1890: with both accordingly */
! 1891: conn->cselect_bits = CURL_CSELECT_IN;
! 1892:
! 1893: state(conn, SSH_STOP);
! 1894: break;
! 1895: }
! 1896: case SSH_SCP_DONE:
! 1897: if(data->set.upload)
! 1898: state(conn, SSH_SCP_SEND_EOF);
! 1899: else
! 1900: state(conn, SSH_SCP_CHANNEL_FREE);
! 1901: break;
! 1902:
! 1903: case SSH_SCP_SEND_EOF:
! 1904: if(sshc->scp_session) {
! 1905: rc = ssh_scp_close(sshc->scp_session);
! 1906: if(rc == SSH_AGAIN) {
! 1907: /* Currently the ssh_scp_close handles waiting for EOF in
! 1908: * blocking way.
! 1909: */
! 1910: break;
! 1911: }
! 1912: if(rc != SSH_OK) {
! 1913: infof(data, "Failed to close libssh scp channel: %s\n",
! 1914: ssh_get_error(sshc->ssh_session));
! 1915: }
! 1916: }
! 1917:
! 1918: state(conn, SSH_SCP_CHANNEL_FREE);
! 1919: break;
! 1920:
! 1921: case SSH_SCP_CHANNEL_FREE:
! 1922: if(sshc->scp_session) {
! 1923: ssh_scp_free(sshc->scp_session);
! 1924: sshc->scp_session = NULL;
! 1925: }
! 1926: DEBUGF(infof(data, "SCP DONE phase complete\n"));
! 1927:
! 1928: ssh_set_blocking(sshc->ssh_session, 0);
! 1929:
! 1930: state(conn, SSH_SESSION_DISCONNECT);
! 1931: /* FALLTHROUGH */
! 1932:
! 1933: case SSH_SESSION_DISCONNECT:
! 1934: /* during weird times when we've been prematurely aborted, the channel
! 1935: is still alive when we reach this state and we MUST kill the channel
! 1936: properly first */
! 1937: if(sshc->scp_session) {
! 1938: ssh_scp_free(sshc->scp_session);
! 1939: sshc->scp_session = NULL;
! 1940: }
! 1941:
! 1942: ssh_disconnect(sshc->ssh_session);
! 1943:
! 1944: SSH_STRING_FREE_CHAR(sshc->homedir);
! 1945: conn->data->state.most_recent_ftp_entrypath = NULL;
! 1946:
! 1947: state(conn, SSH_SESSION_FREE);
! 1948: /* FALLTHROUGH */
! 1949: case SSH_SESSION_FREE:
! 1950: if(sshc->ssh_session) {
! 1951: ssh_free(sshc->ssh_session);
! 1952: sshc->ssh_session = NULL;
! 1953: }
! 1954:
! 1955: /* worst-case scenario cleanup */
! 1956:
! 1957: DEBUGASSERT(sshc->ssh_session == NULL);
! 1958: DEBUGASSERT(sshc->scp_session == NULL);
! 1959:
! 1960: if(sshc->readdir_tmp) {
! 1961: ssh_string_free_char(sshc->readdir_tmp);
! 1962: sshc->readdir_tmp = NULL;
! 1963: }
! 1964:
! 1965: if(sshc->quote_attrs)
! 1966: sftp_attributes_free(sshc->quote_attrs);
! 1967:
! 1968: if(sshc->readdir_attrs)
! 1969: sftp_attributes_free(sshc->readdir_attrs);
! 1970:
! 1971: if(sshc->readdir_link_attrs)
! 1972: sftp_attributes_free(sshc->readdir_link_attrs);
! 1973:
! 1974: if(sshc->privkey)
! 1975: ssh_key_free(sshc->privkey);
! 1976: if(sshc->pubkey)
! 1977: ssh_key_free(sshc->pubkey);
! 1978:
! 1979: Curl_safefree(sshc->rsa_pub);
! 1980: Curl_safefree(sshc->rsa);
! 1981: Curl_safefree(sshc->quote_path1);
! 1982: Curl_safefree(sshc->quote_path2);
! 1983: Curl_safefree(sshc->readdir_line);
! 1984: Curl_safefree(sshc->readdir_linkPath);
! 1985: SSH_STRING_FREE_CHAR(sshc->homedir);
! 1986:
! 1987: /* the code we are about to return */
! 1988: result = sshc->actualcode;
! 1989:
! 1990: memset(sshc, 0, sizeof(struct ssh_conn));
! 1991:
! 1992: connclose(conn, "SSH session free");
! 1993: sshc->state = SSH_SESSION_FREE; /* current */
! 1994: sshc->nextstate = SSH_NO_STATE;
! 1995: state(conn, SSH_STOP);
! 1996: break;
! 1997:
! 1998: case SSH_QUIT:
! 1999: /* fallthrough, just stop! */
! 2000: default:
! 2001: /* internal error */
! 2002: sshc->nextstate = SSH_NO_STATE;
! 2003: state(conn, SSH_STOP);
! 2004: break;
! 2005:
! 2006: }
! 2007: } while(!rc && (sshc->state != SSH_STOP));
! 2008:
! 2009:
! 2010: if(rc == SSH_AGAIN) {
! 2011: /* we would block, we need to wait for the socket to be ready (in the
! 2012: right direction too)! */
! 2013: *block = TRUE;
! 2014: }
! 2015:
! 2016: return result;
! 2017: }
! 2018:
! 2019:
! 2020: /* called by the multi interface to figure out what socket(s) to wait for and
! 2021: for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
! 2022: static int myssh_perform_getsock(const struct connectdata *conn,
! 2023: curl_socket_t *sock)
! 2024: {
! 2025: int bitmap = GETSOCK_BLANK;
! 2026: sock[0] = conn->sock[FIRSTSOCKET];
! 2027:
! 2028: if(conn->waitfor & KEEP_RECV)
! 2029: bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
! 2030:
! 2031: if(conn->waitfor & KEEP_SEND)
! 2032: bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
! 2033:
! 2034: return bitmap;
! 2035: }
! 2036:
! 2037: /* Generic function called by the multi interface to figure out what socket(s)
! 2038: to wait for and for what actions during the DOING and PROTOCONNECT states*/
! 2039: static int myssh_getsock(struct connectdata *conn,
! 2040: curl_socket_t *sock)
! 2041: {
! 2042: /* if we know the direction we can use the generic *_getsock() function even
! 2043: for the protocol_connect and doing states */
! 2044: return myssh_perform_getsock(conn, sock);
! 2045: }
! 2046:
! 2047: static void myssh_block2waitfor(struct connectdata *conn, bool block)
! 2048: {
! 2049: struct ssh_conn *sshc = &conn->proto.sshc;
! 2050:
! 2051: /* If it didn't block, or nothing was returned by ssh_get_poll_flags
! 2052: * have the original set */
! 2053: conn->waitfor = sshc->orig_waitfor;
! 2054:
! 2055: if(block) {
! 2056: int dir = ssh_get_poll_flags(sshc->ssh_session);
! 2057: if(dir & SSH_READ_PENDING) {
! 2058: /* translate the libssh define bits into our own bit defines */
! 2059: conn->waitfor = KEEP_RECV;
! 2060: }
! 2061: else if(dir & SSH_WRITE_PENDING) {
! 2062: conn->waitfor = KEEP_SEND;
! 2063: }
! 2064: }
! 2065: }
! 2066:
! 2067: /* called repeatedly until done from multi.c */
! 2068: static CURLcode myssh_multi_statemach(struct connectdata *conn,
! 2069: bool *done)
! 2070: {
! 2071: struct ssh_conn *sshc = &conn->proto.sshc;
! 2072: bool block; /* we store the status and use that to provide a ssh_getsock()
! 2073: implementation */
! 2074: CURLcode result = myssh_statemach_act(conn, &block);
! 2075:
! 2076: *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
! 2077: myssh_block2waitfor(conn, block);
! 2078:
! 2079: return result;
! 2080: }
! 2081:
! 2082: static CURLcode myssh_block_statemach(struct connectdata *conn,
! 2083: bool disconnect)
! 2084: {
! 2085: struct ssh_conn *sshc = &conn->proto.sshc;
! 2086: CURLcode result = CURLE_OK;
! 2087: struct Curl_easy *data = conn->data;
! 2088:
! 2089: while((sshc->state != SSH_STOP) && !result) {
! 2090: bool block;
! 2091: timediff_t left = 1000;
! 2092: struct curltime now = Curl_now();
! 2093:
! 2094: result = myssh_statemach_act(conn, &block);
! 2095: if(result)
! 2096: break;
! 2097:
! 2098: if(!disconnect) {
! 2099: if(Curl_pgrsUpdate(conn))
! 2100: return CURLE_ABORTED_BY_CALLBACK;
! 2101:
! 2102: result = Curl_speedcheck(data, now);
! 2103: if(result)
! 2104: break;
! 2105:
! 2106: left = Curl_timeleft(data, NULL, FALSE);
! 2107: if(left < 0) {
! 2108: failf(data, "Operation timed out");
! 2109: return CURLE_OPERATION_TIMEDOUT;
! 2110: }
! 2111: }
! 2112:
! 2113: if(block) {
! 2114: curl_socket_t fd_read = conn->sock[FIRSTSOCKET];
! 2115: /* wait for the socket to become ready */
! 2116: (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD,
! 2117: CURL_SOCKET_BAD, left > 1000 ? 1000 : left);
! 2118: }
! 2119:
! 2120: }
! 2121:
! 2122: return result;
! 2123: }
! 2124:
! 2125: /*
! 2126: * SSH setup connection
! 2127: */
! 2128: static CURLcode myssh_setup_connection(struct connectdata *conn)
! 2129: {
! 2130: struct SSHPROTO *ssh;
! 2131:
! 2132: conn->data->req.protop = ssh = calloc(1, sizeof(struct SSHPROTO));
! 2133: if(!ssh)
! 2134: return CURLE_OUT_OF_MEMORY;
! 2135:
! 2136: return CURLE_OK;
! 2137: }
! 2138:
! 2139: static Curl_recv scp_recv, sftp_recv;
! 2140: static Curl_send scp_send, sftp_send;
! 2141:
! 2142: /*
! 2143: * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
! 2144: * do protocol-specific actions at connect-time.
! 2145: */
! 2146: static CURLcode myssh_connect(struct connectdata *conn, bool *done)
! 2147: {
! 2148: struct ssh_conn *ssh;
! 2149: CURLcode result;
! 2150: curl_socket_t sock = conn->sock[FIRSTSOCKET];
! 2151: struct Curl_easy *data = conn->data;
! 2152: int rc;
! 2153:
! 2154: /* initialize per-handle data if not already */
! 2155: if(!data->req.protop)
! 2156: myssh_setup_connection(conn);
! 2157:
! 2158: /* We default to persistent connections. We set this already in this connect
! 2159: function to make the re-use checks properly be able to check this bit. */
! 2160: connkeep(conn, "SSH default");
! 2161:
! 2162: if(conn->handler->protocol & CURLPROTO_SCP) {
! 2163: conn->recv[FIRSTSOCKET] = scp_recv;
! 2164: conn->send[FIRSTSOCKET] = scp_send;
! 2165: }
! 2166: else {
! 2167: conn->recv[FIRSTSOCKET] = sftp_recv;
! 2168: conn->send[FIRSTSOCKET] = sftp_send;
! 2169: }
! 2170:
! 2171: ssh = &conn->proto.sshc;
! 2172:
! 2173: ssh->ssh_session = ssh_new();
! 2174: if(ssh->ssh_session == NULL) {
! 2175: failf(data, "Failure initialising ssh session");
! 2176: return CURLE_FAILED_INIT;
! 2177: }
! 2178:
! 2179: rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
! 2180: if(rc != SSH_OK) {
! 2181: failf(data, "Could not set remote host");
! 2182: return CURLE_FAILED_INIT;
! 2183: }
! 2184:
! 2185: rc = ssh_options_parse_config(ssh->ssh_session, NULL);
! 2186: if(rc != SSH_OK) {
! 2187: infof(data, "Could not parse SSH configuration files");
! 2188: /* ignore */
! 2189: }
! 2190:
! 2191: rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
! 2192: if(rc != SSH_OK) {
! 2193: failf(data, "Could not set socket");
! 2194: return CURLE_FAILED_INIT;
! 2195: }
! 2196:
! 2197: if(conn->user && conn->user[0] != '\0') {
! 2198: infof(data, "User: %s\n", conn->user);
! 2199: rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
! 2200: if(rc != SSH_OK) {
! 2201: failf(data, "Could not set user");
! 2202: return CURLE_FAILED_INIT;
! 2203: }
! 2204: }
! 2205:
! 2206: if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
! 2207: infof(data, "Known hosts: %s\n", data->set.str[STRING_SSH_KNOWNHOSTS]);
! 2208: rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
! 2209: data->set.str[STRING_SSH_KNOWNHOSTS]);
! 2210: if(rc != SSH_OK) {
! 2211: failf(data, "Could not set known hosts file path");
! 2212: return CURLE_FAILED_INIT;
! 2213: }
! 2214: }
! 2215:
! 2216: if(conn->remote_port) {
! 2217: rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT,
! 2218: &conn->remote_port);
! 2219: if(rc != SSH_OK) {
! 2220: failf(data, "Could not set remote port");
! 2221: return CURLE_FAILED_INIT;
! 2222: }
! 2223: }
! 2224:
! 2225: if(data->set.ssh_compression) {
! 2226: rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
! 2227: "zlib,zlib@openssh.com,none");
! 2228: if(rc != SSH_OK) {
! 2229: failf(data, "Could not set compression");
! 2230: return CURLE_FAILED_INIT;
! 2231: }
! 2232: }
! 2233:
! 2234: ssh->privkey = NULL;
! 2235: ssh->pubkey = NULL;
! 2236:
! 2237: if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
! 2238: rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
! 2239: &ssh->pubkey);
! 2240: if(rc != SSH_OK) {
! 2241: failf(data, "Could not load public key file");
! 2242: return CURLE_FAILED_INIT;
! 2243: }
! 2244: }
! 2245:
! 2246: /* we do not verify here, we do it at the state machine,
! 2247: * after connection */
! 2248:
! 2249: state(conn, SSH_INIT);
! 2250:
! 2251: result = myssh_multi_statemach(conn, done);
! 2252:
! 2253: return result;
! 2254: }
! 2255:
! 2256: /* called from multi.c while DOing */
! 2257: static CURLcode scp_doing(struct connectdata *conn, bool *dophase_done)
! 2258: {
! 2259: CURLcode result;
! 2260:
! 2261: result = myssh_multi_statemach(conn, dophase_done);
! 2262:
! 2263: if(*dophase_done) {
! 2264: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 2265: }
! 2266: return result;
! 2267: }
! 2268:
! 2269: /*
! 2270: ***********************************************************************
! 2271: *
! 2272: * scp_perform()
! 2273: *
! 2274: * This is the actual DO function for SCP. Get a file according to
! 2275: * the options previously setup.
! 2276: */
! 2277:
! 2278: static
! 2279: CURLcode scp_perform(struct connectdata *conn,
! 2280: bool *connected, bool *dophase_done)
! 2281: {
! 2282: CURLcode result = CURLE_OK;
! 2283:
! 2284: DEBUGF(infof(conn->data, "DO phase starts\n"));
! 2285:
! 2286: *dophase_done = FALSE; /* not done yet */
! 2287:
! 2288: /* start the first command in the DO phase */
! 2289: state(conn, SSH_SCP_TRANS_INIT);
! 2290:
! 2291: result = myssh_multi_statemach(conn, dophase_done);
! 2292:
! 2293: *connected = conn->bits.tcpconnect[FIRSTSOCKET];
! 2294:
! 2295: if(*dophase_done) {
! 2296: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 2297: }
! 2298:
! 2299: return result;
! 2300: }
! 2301:
! 2302: static CURLcode myssh_do_it(struct connectdata *conn, bool *done)
! 2303: {
! 2304: CURLcode result;
! 2305: bool connected = 0;
! 2306: struct Curl_easy *data = conn->data;
! 2307: struct ssh_conn *sshc = &conn->proto.sshc;
! 2308:
! 2309: *done = FALSE; /* default to false */
! 2310:
! 2311: data->req.size = -1; /* make sure this is unknown at this point */
! 2312:
! 2313: sshc->actualcode = CURLE_OK; /* reset error code */
! 2314: sshc->secondCreateDirs = 0; /* reset the create dir attempt state
! 2315: variable */
! 2316:
! 2317: Curl_pgrsSetUploadCounter(data, 0);
! 2318: Curl_pgrsSetDownloadCounter(data, 0);
! 2319: Curl_pgrsSetUploadSize(data, -1);
! 2320: Curl_pgrsSetDownloadSize(data, -1);
! 2321:
! 2322: if(conn->handler->protocol & CURLPROTO_SCP)
! 2323: result = scp_perform(conn, &connected, done);
! 2324: else
! 2325: result = sftp_perform(conn, &connected, done);
! 2326:
! 2327: return result;
! 2328: }
! 2329:
! 2330: /* BLOCKING, but the function is using the state machine so the only reason
! 2331: this is still blocking is that the multi interface code has no support for
! 2332: disconnecting operations that takes a while */
! 2333: static CURLcode scp_disconnect(struct connectdata *conn,
! 2334: bool dead_connection)
! 2335: {
! 2336: CURLcode result = CURLE_OK;
! 2337: struct ssh_conn *ssh = &conn->proto.sshc;
! 2338: (void) dead_connection;
! 2339:
! 2340: if(ssh->ssh_session) {
! 2341: /* only if there's a session still around to use! */
! 2342:
! 2343: state(conn, SSH_SESSION_DISCONNECT);
! 2344:
! 2345: result = myssh_block_statemach(conn, TRUE);
! 2346: }
! 2347:
! 2348: return result;
! 2349: }
! 2350:
! 2351: /* generic done function for both SCP and SFTP called from their specific
! 2352: done functions */
! 2353: static CURLcode myssh_done(struct connectdata *conn, CURLcode status)
! 2354: {
! 2355: CURLcode result = CURLE_OK;
! 2356: struct SSHPROTO *protop = conn->data->req.protop;
! 2357:
! 2358: if(!status) {
! 2359: /* run the state-machine */
! 2360: result = myssh_block_statemach(conn, FALSE);
! 2361: }
! 2362: else
! 2363: result = status;
! 2364:
! 2365: if(protop)
! 2366: Curl_safefree(protop->path);
! 2367: if(Curl_pgrsDone(conn))
! 2368: return CURLE_ABORTED_BY_CALLBACK;
! 2369:
! 2370: conn->data->req.keepon = 0; /* clear all bits */
! 2371: return result;
! 2372: }
! 2373:
! 2374:
! 2375: static CURLcode scp_done(struct connectdata *conn, CURLcode status,
! 2376: bool premature)
! 2377: {
! 2378: (void) premature; /* not used */
! 2379:
! 2380: if(!status)
! 2381: state(conn, SSH_SCP_DONE);
! 2382:
! 2383: return myssh_done(conn, status);
! 2384:
! 2385: }
! 2386:
! 2387: static ssize_t scp_send(struct connectdata *conn, int sockindex,
! 2388: const void *mem, size_t len, CURLcode *err)
! 2389: {
! 2390: int rc;
! 2391: (void) sockindex; /* we only support SCP on the fixed known primary socket */
! 2392: (void) err;
! 2393:
! 2394: rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
! 2395:
! 2396: #if 0
! 2397: /* The following code is misleading, mostly added as wishful thinking
! 2398: * that libssh at some point will implement non-blocking ssh_scp_write/read.
! 2399: * Currently rc can only be number of bytes read or SSH_ERROR. */
! 2400: myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE);
! 2401:
! 2402: if(rc == SSH_AGAIN) {
! 2403: *err = CURLE_AGAIN;
! 2404: return 0;
! 2405: }
! 2406: else
! 2407: #endif
! 2408: if(rc != SSH_OK) {
! 2409: *err = CURLE_SSH;
! 2410: return -1;
! 2411: }
! 2412:
! 2413: return len;
! 2414: }
! 2415:
! 2416: static ssize_t scp_recv(struct connectdata *conn, int sockindex,
! 2417: char *mem, size_t len, CURLcode *err)
! 2418: {
! 2419: ssize_t nread;
! 2420: (void) err;
! 2421: (void) sockindex; /* we only support SCP on the fixed known primary socket */
! 2422:
! 2423: /* libssh returns int */
! 2424: nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
! 2425:
! 2426: #if 0
! 2427: /* The following code is misleading, mostly added as wishful thinking
! 2428: * that libssh at some point will implement non-blocking ssh_scp_write/read.
! 2429: * Currently rc can only be SSH_OK or SSH_ERROR. */
! 2430:
! 2431: myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE);
! 2432: if(nread == SSH_AGAIN) {
! 2433: *err = CURLE_AGAIN;
! 2434: nread = -1;
! 2435: }
! 2436: #endif
! 2437:
! 2438: return nread;
! 2439: }
! 2440:
! 2441: /*
! 2442: * =============== SFTP ===============
! 2443: */
! 2444:
! 2445: /*
! 2446: ***********************************************************************
! 2447: *
! 2448: * sftp_perform()
! 2449: *
! 2450: * This is the actual DO function for SFTP. Get a file/directory according to
! 2451: * the options previously setup.
! 2452: */
! 2453:
! 2454: static
! 2455: CURLcode sftp_perform(struct connectdata *conn,
! 2456: bool *connected,
! 2457: bool *dophase_done)
! 2458: {
! 2459: CURLcode result = CURLE_OK;
! 2460:
! 2461: DEBUGF(infof(conn->data, "DO phase starts\n"));
! 2462:
! 2463: *dophase_done = FALSE; /* not done yet */
! 2464:
! 2465: /* start the first command in the DO phase */
! 2466: state(conn, SSH_SFTP_QUOTE_INIT);
! 2467:
! 2468: /* run the state-machine */
! 2469: result = myssh_multi_statemach(conn, dophase_done);
! 2470:
! 2471: *connected = conn->bits.tcpconnect[FIRSTSOCKET];
! 2472:
! 2473: if(*dophase_done) {
! 2474: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 2475: }
! 2476:
! 2477: return result;
! 2478: }
! 2479:
! 2480: /* called from multi.c while DOing */
! 2481: static CURLcode sftp_doing(struct connectdata *conn,
! 2482: bool *dophase_done)
! 2483: {
! 2484: CURLcode result = myssh_multi_statemach(conn, dophase_done);
! 2485: if(*dophase_done) {
! 2486: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 2487: }
! 2488: return result;
! 2489: }
! 2490:
! 2491: /* BLOCKING, but the function is using the state machine so the only reason
! 2492: this is still blocking is that the multi interface code has no support for
! 2493: disconnecting operations that takes a while */
! 2494: static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection)
! 2495: {
! 2496: CURLcode result = CURLE_OK;
! 2497: (void) dead_connection;
! 2498:
! 2499: DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n"));
! 2500:
! 2501: if(conn->proto.sshc.ssh_session) {
! 2502: /* only if there's a session still around to use! */
! 2503: state(conn, SSH_SFTP_SHUTDOWN);
! 2504: result = myssh_block_statemach(conn, TRUE);
! 2505: }
! 2506:
! 2507: DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n"));
! 2508:
! 2509: return result;
! 2510:
! 2511: }
! 2512:
! 2513: static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
! 2514: bool premature)
! 2515: {
! 2516: struct ssh_conn *sshc = &conn->proto.sshc;
! 2517:
! 2518: if(!status) {
! 2519: /* Post quote commands are executed after the SFTP_CLOSE state to avoid
! 2520: errors that could happen due to open file handles during POSTQUOTE
! 2521: operation */
! 2522: if(!premature && conn->data->set.postquote && !conn->bits.retry)
! 2523: sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
! 2524: state(conn, SSH_SFTP_CLOSE);
! 2525: }
! 2526: return myssh_done(conn, status);
! 2527: }
! 2528:
! 2529: /* return number of sent bytes */
! 2530: static ssize_t sftp_send(struct connectdata *conn, int sockindex,
! 2531: const void *mem, size_t len, CURLcode *err)
! 2532: {
! 2533: ssize_t nwrite;
! 2534: (void)sockindex;
! 2535:
! 2536: nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
! 2537:
! 2538: myssh_block2waitfor(conn, FALSE);
! 2539:
! 2540: #if 0 /* not returned by libssh on write */
! 2541: if(nwrite == SSH_AGAIN) {
! 2542: *err = CURLE_AGAIN;
! 2543: nwrite = 0;
! 2544: }
! 2545: else
! 2546: #endif
! 2547: if(nwrite < 0) {
! 2548: *err = CURLE_SSH;
! 2549: nwrite = -1;
! 2550: }
! 2551:
! 2552: return nwrite;
! 2553: }
! 2554:
! 2555: /*
! 2556: * Return number of received (decrypted) bytes
! 2557: * or <0 on error
! 2558: */
! 2559: static ssize_t sftp_recv(struct connectdata *conn, int sockindex,
! 2560: char *mem, size_t len, CURLcode *err)
! 2561: {
! 2562: ssize_t nread;
! 2563: (void)sockindex;
! 2564:
! 2565: DEBUGASSERT(len < CURL_MAX_READ_SIZE);
! 2566:
! 2567: switch(conn->proto.sshc.sftp_recv_state) {
! 2568: case 0:
! 2569: conn->proto.sshc.sftp_file_index =
! 2570: sftp_async_read_begin(conn->proto.sshc.sftp_file,
! 2571: (uint32_t)len);
! 2572: if(conn->proto.sshc.sftp_file_index < 0) {
! 2573: *err = CURLE_RECV_ERROR;
! 2574: return -1;
! 2575: }
! 2576:
! 2577: /* FALLTHROUGH */
! 2578: case 1:
! 2579: conn->proto.sshc.sftp_recv_state = 1;
! 2580:
! 2581: nread = sftp_async_read(conn->proto.sshc.sftp_file,
! 2582: mem, (uint32_t)len,
! 2583: conn->proto.sshc.sftp_file_index);
! 2584:
! 2585: myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE);
! 2586:
! 2587: if(nread == SSH_AGAIN) {
! 2588: *err = CURLE_AGAIN;
! 2589: return -1;
! 2590: }
! 2591: else if(nread < 0) {
! 2592: *err = CURLE_RECV_ERROR;
! 2593: return -1;
! 2594: }
! 2595:
! 2596: conn->proto.sshc.sftp_recv_state = 0;
! 2597: return nread;
! 2598:
! 2599: default:
! 2600: /* we never reach here */
! 2601: return -1;
! 2602: }
! 2603: }
! 2604:
! 2605: static void sftp_quote(struct connectdata *conn)
! 2606: {
! 2607: const char *cp;
! 2608: struct Curl_easy *data = conn->data;
! 2609: struct SSHPROTO *protop = data->req.protop;
! 2610: struct ssh_conn *sshc = &conn->proto.sshc;
! 2611: CURLcode result;
! 2612:
! 2613: /*
! 2614: * Support some of the "FTP" commands
! 2615: */
! 2616: char *cmd = sshc->quote_item->data;
! 2617: sshc->acceptfail = FALSE;
! 2618:
! 2619: /* if a command starts with an asterisk, which a legal SFTP command never
! 2620: can, the command will be allowed to fail without it causing any
! 2621: aborts or cancels etc. It will cause libcurl to act as if the command
! 2622: is successful, whatever the server reponds. */
! 2623:
! 2624: if(cmd[0] == '*') {
! 2625: cmd++;
! 2626: sshc->acceptfail = TRUE;
! 2627: }
! 2628:
! 2629: if(strcasecompare("pwd", cmd)) {
! 2630: /* output debug output if that is requested */
! 2631: char *tmp = aprintf("257 \"%s\" is current directory.\n",
! 2632: protop->path);
! 2633: if(!tmp) {
! 2634: sshc->actualcode = CURLE_OUT_OF_MEMORY;
! 2635: state(conn, SSH_SFTP_CLOSE);
! 2636: sshc->nextstate = SSH_NO_STATE;
! 2637: return;
! 2638: }
! 2639: if(data->set.verbose) {
! 2640: Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
! 2641: Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
! 2642: }
! 2643: /* this sends an FTP-like "header" to the header callback so that the
! 2644: current directory can be read very similar to how it is read when
! 2645: using ordinary FTP. */
! 2646: result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp));
! 2647: free(tmp);
! 2648: if(result) {
! 2649: state(conn, SSH_SFTP_CLOSE);
! 2650: sshc->nextstate = SSH_NO_STATE;
! 2651: sshc->actualcode = result;
! 2652: }
! 2653: else
! 2654: state(conn, SSH_SFTP_NEXT_QUOTE);
! 2655: return;
! 2656: }
! 2657:
! 2658: /*
! 2659: * the arguments following the command must be separated from the
! 2660: * command with a space so we can check for it unconditionally
! 2661: */
! 2662: cp = strchr(cmd, ' ');
! 2663: if(cp == NULL) {
! 2664: failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
! 2665: state(conn, SSH_SFTP_CLOSE);
! 2666: sshc->nextstate = SSH_NO_STATE;
! 2667: sshc->actualcode = CURLE_QUOTE_ERROR;
! 2668: return;
! 2669: }
! 2670:
! 2671: /*
! 2672: * also, every command takes at least one argument so we get that
! 2673: * first argument right now
! 2674: */
! 2675: result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
! 2676: if(result) {
! 2677: if(result == CURLE_OUT_OF_MEMORY)
! 2678: failf(data, "Out of memory");
! 2679: else
! 2680: failf(data, "Syntax error: Bad first parameter");
! 2681: state(conn, SSH_SFTP_CLOSE);
! 2682: sshc->nextstate = SSH_NO_STATE;
! 2683: sshc->actualcode = result;
! 2684: return;
! 2685: }
! 2686:
! 2687: /*
! 2688: * SFTP is a binary protocol, so we don't send text commands
! 2689: * to the server. Instead, we scan for commands used by
! 2690: * OpenSSH's sftp program and call the appropriate libssh
! 2691: * functions.
! 2692: */
! 2693: if(strncasecompare(cmd, "chgrp ", 6) ||
! 2694: strncasecompare(cmd, "chmod ", 6) ||
! 2695: strncasecompare(cmd, "chown ", 6)) {
! 2696: /* attribute change */
! 2697:
! 2698: /* sshc->quote_path1 contains the mode to set */
! 2699: /* get the destination */
! 2700: result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
! 2701: if(result) {
! 2702: if(result == CURLE_OUT_OF_MEMORY)
! 2703: failf(data, "Out of memory");
! 2704: else
! 2705: failf(data, "Syntax error in chgrp/chmod/chown: "
! 2706: "Bad second parameter");
! 2707: Curl_safefree(sshc->quote_path1);
! 2708: state(conn, SSH_SFTP_CLOSE);
! 2709: sshc->nextstate = SSH_NO_STATE;
! 2710: sshc->actualcode = result;
! 2711: return;
! 2712: }
! 2713: sshc->quote_attrs = NULL;
! 2714: state(conn, SSH_SFTP_QUOTE_STAT);
! 2715: return;
! 2716: }
! 2717: if(strncasecompare(cmd, "ln ", 3) ||
! 2718: strncasecompare(cmd, "symlink ", 8)) {
! 2719: /* symbolic linking */
! 2720: /* sshc->quote_path1 is the source */
! 2721: /* get the destination */
! 2722: result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
! 2723: if(result) {
! 2724: if(result == CURLE_OUT_OF_MEMORY)
! 2725: failf(data, "Out of memory");
! 2726: else
! 2727: failf(data, "Syntax error in ln/symlink: Bad second parameter");
! 2728: Curl_safefree(sshc->quote_path1);
! 2729: state(conn, SSH_SFTP_CLOSE);
! 2730: sshc->nextstate = SSH_NO_STATE;
! 2731: sshc->actualcode = result;
! 2732: return;
! 2733: }
! 2734: state(conn, SSH_SFTP_QUOTE_SYMLINK);
! 2735: return;
! 2736: }
! 2737: else if(strncasecompare(cmd, "mkdir ", 6)) {
! 2738: /* create dir */
! 2739: state(conn, SSH_SFTP_QUOTE_MKDIR);
! 2740: return;
! 2741: }
! 2742: else if(strncasecompare(cmd, "rename ", 7)) {
! 2743: /* rename file */
! 2744: /* first param is the source path */
! 2745: /* second param is the dest. path */
! 2746: result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
! 2747: if(result) {
! 2748: if(result == CURLE_OUT_OF_MEMORY)
! 2749: failf(data, "Out of memory");
! 2750: else
! 2751: failf(data, "Syntax error in rename: Bad second parameter");
! 2752: Curl_safefree(sshc->quote_path1);
! 2753: state(conn, SSH_SFTP_CLOSE);
! 2754: sshc->nextstate = SSH_NO_STATE;
! 2755: sshc->actualcode = result;
! 2756: return;
! 2757: }
! 2758: state(conn, SSH_SFTP_QUOTE_RENAME);
! 2759: return;
! 2760: }
! 2761: else if(strncasecompare(cmd, "rmdir ", 6)) {
! 2762: /* delete dir */
! 2763: state(conn, SSH_SFTP_QUOTE_RMDIR);
! 2764: return;
! 2765: }
! 2766: else if(strncasecompare(cmd, "rm ", 3)) {
! 2767: state(conn, SSH_SFTP_QUOTE_UNLINK);
! 2768: return;
! 2769: }
! 2770: #ifdef HAS_STATVFS_SUPPORT
! 2771: else if(strncasecompare(cmd, "statvfs ", 8)) {
! 2772: state(conn, SSH_SFTP_QUOTE_STATVFS);
! 2773: return;
! 2774: }
! 2775: #endif
! 2776:
! 2777: failf(data, "Unknown SFTP command");
! 2778: Curl_safefree(sshc->quote_path1);
! 2779: Curl_safefree(sshc->quote_path2);
! 2780: state(conn, SSH_SFTP_CLOSE);
! 2781: sshc->nextstate = SSH_NO_STATE;
! 2782: sshc->actualcode = CURLE_QUOTE_ERROR;
! 2783: }
! 2784:
! 2785: static void sftp_quote_stat(struct connectdata *conn)
! 2786: {
! 2787: struct Curl_easy *data = conn->data;
! 2788: struct ssh_conn *sshc = &conn->proto.sshc;
! 2789: char *cmd = sshc->quote_item->data;
! 2790: sshc->acceptfail = FALSE;
! 2791:
! 2792: /* if a command starts with an asterisk, which a legal SFTP command never
! 2793: can, the command will be allowed to fail without it causing any
! 2794: aborts or cancels etc. It will cause libcurl to act as if the command
! 2795: is successful, whatever the server reponds. */
! 2796:
! 2797: if(cmd[0] == '*') {
! 2798: cmd++;
! 2799: sshc->acceptfail = TRUE;
! 2800: }
! 2801:
! 2802: /* We read the file attributes, store them in sshc->quote_attrs
! 2803: * and modify them accordingly to command. Then we switch to
! 2804: * QUOTE_SETSTAT state to write new ones.
! 2805: */
! 2806:
! 2807: if(sshc->quote_attrs)
! 2808: sftp_attributes_free(sshc->quote_attrs);
! 2809: sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2);
! 2810: if(sshc->quote_attrs == NULL) {
! 2811: Curl_safefree(sshc->quote_path1);
! 2812: Curl_safefree(sshc->quote_path2);
! 2813: failf(data, "Attempt to get SFTP stats failed: %d",
! 2814: sftp_get_error(sshc->sftp_session));
! 2815: state(conn, SSH_SFTP_CLOSE);
! 2816: sshc->nextstate = SSH_NO_STATE;
! 2817: sshc->actualcode = CURLE_QUOTE_ERROR;
! 2818: return;
! 2819: }
! 2820:
! 2821: /* Now set the new attributes... */
! 2822: if(strncasecompare(cmd, "chgrp", 5)) {
! 2823: sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
! 2824: if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
! 2825: !sshc->acceptfail) {
! 2826: Curl_safefree(sshc->quote_path1);
! 2827: Curl_safefree(sshc->quote_path2);
! 2828: failf(data, "Syntax error: chgrp gid not a number");
! 2829: state(conn, SSH_SFTP_CLOSE);
! 2830: sshc->nextstate = SSH_NO_STATE;
! 2831: sshc->actualcode = CURLE_QUOTE_ERROR;
! 2832: return;
! 2833: }
! 2834: sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
! 2835: }
! 2836: else if(strncasecompare(cmd, "chmod", 5)) {
! 2837: mode_t perms;
! 2838: perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8);
! 2839: /* permissions are octal */
! 2840: if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) {
! 2841: Curl_safefree(sshc->quote_path1);
! 2842: Curl_safefree(sshc->quote_path2);
! 2843: failf(data, "Syntax error: chmod permissions not a number");
! 2844: state(conn, SSH_SFTP_CLOSE);
! 2845: sshc->nextstate = SSH_NO_STATE;
! 2846: sshc->actualcode = CURLE_QUOTE_ERROR;
! 2847: return;
! 2848: }
! 2849: sshc->quote_attrs->permissions = perms;
! 2850: sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
! 2851: }
! 2852: else if(strncasecompare(cmd, "chown", 5)) {
! 2853: sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
! 2854: if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
! 2855: !sshc->acceptfail) {
! 2856: Curl_safefree(sshc->quote_path1);
! 2857: Curl_safefree(sshc->quote_path2);
! 2858: failf(data, "Syntax error: chown uid not a number");
! 2859: state(conn, SSH_SFTP_CLOSE);
! 2860: sshc->nextstate = SSH_NO_STATE;
! 2861: sshc->actualcode = CURLE_QUOTE_ERROR;
! 2862: return;
! 2863: }
! 2864: sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
! 2865: }
! 2866:
! 2867: /* Now send the completed structure... */
! 2868: state(conn, SSH_SFTP_QUOTE_SETSTAT);
! 2869: return;
! 2870: }
! 2871:
! 2872: CURLcode Curl_ssh_init(void)
! 2873: {
! 2874: if(ssh_init()) {
! 2875: DEBUGF(fprintf(stderr, "Error: libssh_init failed\n"));
! 2876: return CURLE_FAILED_INIT;
! 2877: }
! 2878: return CURLE_OK;
! 2879: }
! 2880:
! 2881: void Curl_ssh_cleanup(void)
! 2882: {
! 2883: (void)ssh_finalize();
! 2884: }
! 2885:
! 2886: size_t Curl_ssh_version(char *buffer, size_t buflen)
! 2887: {
! 2888: return msnprintf(buffer, buflen, "libssh/%s", CURL_LIBSSH_VERSION);
! 2889: }
! 2890:
! 2891: #endif /* USE_LIBSSH */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>