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>