Skip to content
ssh.c 70.9 KiB
Newer Older
        sshc->actualCode = CURLE_QUOTE_ERROR;
      if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
        sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10);
        if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
          Curl_safefree(sshc->quote_path1);
          sshc->quote_path1 = NULL;
          Curl_safefree(sshc->quote_path2);
          sshc->quote_path2 = NULL;
          failf(data, "Syntax error: chgrp gid not a number");
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualCode = CURLE_QUOTE_ERROR;
      else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
        sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8);
        /* permissions are octal */
        if(sshc->quote_attrs.permissions == 0 &&
            !ISDIGIT(sshc->quote_path1[0])) {
          Curl_safefree(sshc->quote_path1);
          sshc->quote_path1 = NULL;
          Curl_safefree(sshc->quote_path2);
          sshc->quote_path2 = NULL;
          failf(data, "Syntax error: chmod permissions not a number");
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualCode = CURLE_QUOTE_ERROR;
      else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
        sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10);
        if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
          Curl_safefree(sshc->quote_path1);
          sshc->quote_path1 = NULL;
          Curl_safefree(sshc->quote_path2);
          sshc->quote_path2 = NULL;
          failf(data, "Syntax error: chown uid not a number");
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualCode = CURLE_QUOTE_ERROR;
          break;
        }
      }

      /* Now send the completed structure... */
      state(conn, SSH_SFTP_QUOTE_SETSTAT);
      rc = libssh2_sftp_setstat(sshc->sftp_session, sshc->quote_path2,
      } else if(rc != 0) {
        err = libssh2_sftp_last_error(sshc->sftp_session);
        Curl_safefree(sshc->quote_path1);
        sshc->quote_path1 = NULL;
        Curl_safefree(sshc->quote_path2);
        sshc->quote_path2 = NULL;
        failf(data, "Attempt to set SFTP stats failed: %s",
              sftp_libssh2_strerror(err));
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_QUOTE_ERROR;
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_QUOTE_SYMLINK:
      rc = libssh2_sftp_symlink(sshc->sftp_session, sshc->quote_path1,
      else if(rc != 0) {
        err = libssh2_sftp_last_error(sshc->sftp_session);
        Curl_safefree(sshc->quote_path1);
        sshc->quote_path1 = NULL;
        Curl_safefree(sshc->quote_path2);
        sshc->quote_path2 = NULL;
        failf(data, "symlink command failed: %s",
              sftp_libssh2_strerror(err));
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_QUOTE_ERROR;
        break;
      }
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_QUOTE_MKDIR:
      rc = libssh2_sftp_mkdir(sshc->sftp_session, sshc->quote_path1, 0755);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
      else if(rc != 0) {
        err = libssh2_sftp_last_error(sshc->sftp_session);
        Curl_safefree(sshc->quote_path1);
        sshc->quote_path1 = NULL;
        failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_QUOTE_ERROR;
        break;
      }
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_QUOTE_RENAME:
      rc = libssh2_sftp_rename(sshc->sftp_session, sshc->quote_path1,
      }
      else if(rc != 0) {
        err = libssh2_sftp_last_error(sshc->sftp_session);
        Curl_safefree(sshc->quote_path1);
        sshc->quote_path1 = NULL;
        Curl_safefree(sshc->quote_path2);
        sshc->quote_path2 = NULL;
        failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_QUOTE_ERROR;
        break;
      }
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_QUOTE_RMDIR:
      rc = libssh2_sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
      else if(rc != 0) {
        err = libssh2_sftp_last_error(sshc->sftp_session);
        Curl_safefree(sshc->quote_path1);
        sshc->quote_path1 = NULL;
        failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_QUOTE_ERROR;
        break;
      }
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_QUOTE_UNLINK:
      rc = libssh2_sftp_unlink(sshc->sftp_session, sshc->quote_path1);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
      else if(rc != 0) {
        err = libssh2_sftp_last_error(sshc->sftp_session);
        Curl_safefree(sshc->quote_path1);
        sshc->quote_path1 = NULL;
        failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_QUOTE_ERROR;
        break;
      }
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_TRANS_INIT:
      else {
        if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
          state(conn, SSH_SFTP_DOWNLOAD_INIT);
      }
      break;

    case SSH_SFTP_UPLOAD_INIT:
      /*
       * NOTE!!!  libssh2 requires that the destination path is a full path
       *          that includes the destination file and name OR ends in a "/"
       *          If this is not done the destination file will be named the
       *          same name as the last directory in the path.
       */
      sshc->sftp_handle =
        libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
                          LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
                          data->set.new_file_perms);
      if(!sshc->sftp_handle) {
        if(libssh2_session_last_errno(sshc->ssh_session) ==
        }
        else {
          err = libssh2_sftp_last_error(sshc->sftp_session);
          failf(data, "Upload failed: %s", sftp_libssh2_strerror(err));
            state(conn, SSH_SFTP_CLOSE);
            sshc->actualCode = err;
            break;
          }
          else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
                    (err == LIBSSH2_FX_FAILURE) ||
                    (err == LIBSSH2_FX_NO_SUCH_PATH)) &&
                   (data->set.ftp_create_missing_dirs &&
                    (strlen(sftp_scp->path) > 1))) {
            /* try to create the path remotely */
            sshc->secondCreateDirs = 1;
            state(conn, SSH_SFTP_CREATE_DIRS_INIT);
            break;
          }
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualCode = sftp_libssh2_error_to_CURLE(err);
          break;
        }
      }

      /* upload data */
      result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
                                   FIRSTSOCKET, NULL);

        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = result;
      } else {
        state(conn, SSH_STOP);
      }
      break;

    case SSH_SFTP_CREATE_DIRS_INIT:
        sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
        state(conn, SSH_SFTP_CREATE_DIRS);
      } else {
        state(conn, SSH_SFTP_UPLOAD_INIT);
      }
      break;

    case SSH_SFTP_CREATE_DIRS:
      if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) {
        *sshc->slash_pos = 0;

        infof(data, "Creating directory '%s'\n", sftp_scp->path);
        state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
        break;
      } else {
        state(conn, SSH_SFTP_UPLOAD_INIT);
      }
      break;

    case SSH_SFTP_CREATE_DIRS_MKDIR:
      /* 'mode' - parameter is preliminary - default to 0644 */
      rc = libssh2_sftp_mkdir(sshc->sftp_session, sftp_scp->path,
        unsigned int sftp_err = 0;
        /*
         * abort if failure wasn't that the dir already exists or the
         * permission was denied (creation might succeed further
         * down the path) - retry on unspecific FAILURE also
         */
        sftp_err = libssh2_sftp_last_error(sshc->sftp_session);
        if((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
            (sftp_err != LIBSSH2_FX_FAILURE) &&
            (sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) {
          result = sftp_libssh2_error_to_CURLE(sftp_err);
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualCode = result;
          break;
        }
      }
      state(conn, SSH_SFTP_CREATE_DIRS);
      break;

    case SSH_SFTP_READDIR_INIT:
      /*
       * This is a directory that we are trying to get, so produce a
       * directory listing
       */
      sshc->sftp_handle = libssh2_sftp_opendir(sshc->sftp_session,
      if(!sshc->sftp_handle) {
        if(libssh2_session_last_errno(sshc->ssh_session) ==
        }
        else {
          err = libssh2_sftp_last_error(sshc->sftp_session);
          failf(data, "Could not open directory for reading: %s",
                sftp_libssh2_strerror(err));
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualCode = sftp_libssh2_error_to_CURLE(err);
          break;
        }
      }
      if((sshc->readdir_filename = (char *)malloc(PATH_MAX+1)) == NULL) {
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_OUT_OF_MEMORY;
        break;
      }
      if((sshc->readdir_longentry = (char *)malloc(PATH_MAX+1)) == NULL) {
        Curl_safefree(sshc->readdir_filename);
        sshc->readdir_filename = NULL;
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_OUT_OF_MEMORY;
        break;
      }
      state(conn, SSH_SFTP_READDIR);
      break;

    case SSH_SFTP_READDIR:
      sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
                                                  sshc->readdir_filename,
                                                  PATH_MAX,
                                                  sshc->readdir_longentry,
                                                  PATH_MAX,
                                                  &sshc->readdir_attrs);
      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
        sshc->readdir_filename[sshc->readdir_len] = '\0';

          char *tmpLine;

          tmpLine = aprintf("%s\n", sshc->readdir_filename);
            state(conn, SSH_SFTP_CLOSE);
            sshc->actualCode = CURLE_OUT_OF_MEMORY;
            break;
          }
          result = Curl_client_write(conn, CLIENTWRITE_BODY, tmpLine, 0);
          Curl_safefree(tmpLine);

          /* output debug output if that is requested */
            Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
                       sshc->readdir_len, conn);
          }
        } else {
          sshc->readdir_currLen = strlen(sshc->readdir_longentry);
          sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
          sshc->readdir_line = (char *)calloc(sshc->readdir_totalLen, 1);
            Curl_safefree(sshc->readdir_filename);
            sshc->readdir_filename = NULL;
            Curl_safefree(sshc->readdir_longentry);
            sshc->readdir_longentry = NULL;
            state(conn, SSH_SFTP_CLOSE);
            sshc->actualCode = CURLE_OUT_OF_MEMORY;
            break;
          }

          memcpy(sshc->readdir_line, sshc->readdir_longentry,
                 sshc->readdir_currLen);
          if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
              ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
               LIBSSH2_SFTP_S_IFLNK)) {
            sshc->readdir_linkPath = (char *)malloc(PATH_MAX + 1);
            if(sshc->readdir_linkPath == NULL) {
              Curl_safefree(sshc->readdir_filename);
              sshc->readdir_filename = NULL;
              Curl_safefree(sshc->readdir_longentry);
              sshc->readdir_longentry = NULL;
              state(conn, SSH_SFTP_CLOSE);
              sshc->actualCode = CURLE_OUT_OF_MEMORY;
              break;
            }

            snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
                     sshc->readdir_filename);
            state(conn, SSH_SFTP_READDIR_LINK);
            break;
          }
          state(conn, SSH_SFTP_READDIR_BOTTOM);
          break;
        }
      }
        Curl_safefree(sshc->readdir_filename);
        sshc->readdir_filename = NULL;
        Curl_safefree(sshc->readdir_longentry);
        sshc->readdir_longentry = NULL;
        state(conn, SSH_SFTP_READDIR_DONE);
        break;
      }
      else if(sshc->readdir_len <= 0) {
        err = libssh2_sftp_last_error(sshc->sftp_session);
        sshc->actualCode = err;
        failf(data, "Could not open remote file for reading: %s :: %d",
              sftp_libssh2_strerror(err),
              libssh2_session_last_errno(sshc->ssh_session));
        Curl_safefree(sshc->readdir_filename);
        sshc->readdir_filename = NULL;
        Curl_safefree(sshc->readdir_longentry);
        sshc->readdir_longentry = NULL;
        state(conn, SSH_SFTP_CLOSE);
        break;
      }
      break;

    case SSH_SFTP_READDIR_LINK:
      sshc->readdir_len = libssh2_sftp_readlink(sshc->sftp_session,
                                                sshc->readdir_linkPath,
                                                sshc->readdir_filename,
                                                PATH_MAX);
      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      Curl_safefree(sshc->readdir_linkPath);
      sshc->readdir_linkPath = NULL;
      sshc->readdir_line = realloc(sshc->readdir_line,
                                   sshc->readdir_totalLen + 4 +
                                   sshc->readdir_len);
        Curl_safefree(sshc->readdir_filename);
        sshc->readdir_filename = NULL;
        Curl_safefree(sshc->readdir_longentry);
        sshc->readdir_longentry = NULL;
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = CURLE_OUT_OF_MEMORY;
        break;
      }

      sshc->readdir_currLen += snprintf(sshc->readdir_line +
                                        sshc->readdir_currLen,
                                        sshc->readdir_totalLen -
                                        sshc->readdir_currLen,
                                        " -> %s",
                                        sshc->readdir_filename);

      state(conn, SSH_SFTP_READDIR_BOTTOM);
      break;

    case SSH_SFTP_READDIR_BOTTOM:
      sshc->readdir_currLen += snprintf(sshc->readdir_line +
                                        sshc->readdir_currLen,
                                        sshc->readdir_totalLen -
                                        sshc->readdir_currLen, "\n");
      result = Curl_client_write(conn, CLIENTWRITE_BODY,
                                 sshc->readdir_line, 0);

      /* output debug output if that is requested */
        Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
                   sshc->readdir_currLen, conn);
      }
      Curl_safefree(sshc->readdir_line);
      sshc->readdir_line = NULL;
      state(conn, SSH_SFTP_READDIR);
      break;

    case SSH_SFTP_READDIR_DONE:
      if(libssh2_sftp_closedir(sshc->sftp_handle) ==
      Curl_safefree(sshc->readdir_filename);
      sshc->readdir_filename = NULL;
      Curl_safefree(sshc->readdir_longentry);
      sshc->readdir_longentry = NULL;

      /* no data to transfer */
      result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
      state(conn, SSH_STOP);
      break;

    case SSH_SFTP_DOWNLOAD_INIT:
      /*
       * Work on getting the specified file
       */
      sshc->sftp_handle =
        libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
                          LIBSSH2_FXF_READ, data->set.new_file_perms);
      if(!sshc->sftp_handle) {
        if(libssh2_session_last_errno(sshc->ssh_session) ==
            LIBSSH2_ERROR_EAGAIN) {
        }
        else {
          err = libssh2_sftp_last_error(sshc->sftp_session);
          failf(data, "Could not open remote file for reading: %s",
                sftp_libssh2_strerror(err));
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualCode = sftp_libssh2_error_to_CURLE(err);
          break;
        }
      }
      state(conn, SSH_SFTP_DOWNLOAD_STAT);
      break;

    case SSH_SFTP_DOWNLOAD_STAT:
      {
        LIBSSH2_SFTP_ATTRIBUTES attrs;

        rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          /*
           * libssh2_sftp_open() didn't return an error, so maybe the server
           * just doesn't support stat()
           */
          data->reqdata.size = -1;
          data->reqdata.maxdownload = -1;
        } else {
          data->reqdata.size = attrs.filesize;
          data->reqdata.maxdownload = attrs.filesize;
          Curl_pgrsSetDownloadSize(data, attrs.filesize);
        }
      }

      /* Setup the actual download */
      result = Curl_setup_transfer(conn, FIRSTSOCKET, data->reqdata.size,
                                   FALSE, NULL, -1, NULL);
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualCode = result;
      } else {
        state(conn, SSH_STOP);
      }
      break;

    case SSH_SFTP_CLOSE:
      if(sshc->sftp_handle) {
        rc = libssh2_sftp_close(sshc->sftp_handle);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to close libssh2 file\n");
        }
      }
      state(conn, SSH_SFTP_SHUTDOWN);
      break;

    case SSH_SFTP_SHUTDOWN:
      if(sshc->sftp_session) {
        rc = libssh2_sftp_shutdown(sshc->sftp_session);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to stop libssh2 sftp subsystem\n");
        }
      }

      Curl_safefree(sftp_scp->path);
      sftp_scp->path = NULL;

      Curl_safefree(sftp_scp->homedir);
      sftp_scp->homedir = NULL;

      state(conn, SSH_CHANNEL_CLOSE);
      break;

    case SSH_SCP_TRANS_INIT:
      if(data->set.upload) {
        if(data->set.infilesize < 0) {
          failf(data, "SCP requires a known file size for upload");
          sshc->actualCode = CURLE_UPLOAD_FAILED;
          state(conn, SSH_SCP_CHANNEL_FREE);
          break;
        }
        state(conn, SSH_SCP_UPLOAD_INIT);
      } else {
        state(conn, SSH_SCP_DOWNLOAD_INIT);
      }
      break;

    case SSH_SCP_UPLOAD_INIT:
      /*
       * libssh2 requires that the destination path is a full path that
       * includes the destination file and name OR ends in a "/" .  If this is
       * not done the destination file will be named the same name as the last
       * directory in the path.
       */
      sshc->ssh_channel =
                  libssh2_scp_send_ex(sshc->ssh_session, sftp_scp->path,
                                      data->set.new_file_perms,
                                      data->set.infilesize, 0, 0);
      if(!sshc->ssh_channel) {
        if(libssh2_session_last_errno(sshc->ssh_session) ==
            LIBSSH2_ERROR_EAGAIN) {
          break;
        } else {
          int ssh_err;
          char *err_msg;
          ssh_err = libssh2_session_last_error(sshc->ssh_session,
                                               &err_msg, NULL, 0);
          err = libssh2_session_error_to_CURLE(ssh_err);
          failf(conn->data, "%s", err_msg);
          state(conn, SSH_SCP_CHANNEL_FREE);
          sshc->actualCode = err;
          break;
        }
      }

      /* upload data */
      result = Curl_setup_transfer(conn, -1, data->reqdata.size, FALSE, NULL,
                                   FIRSTSOCKET, NULL);

        state(conn, SSH_SCP_CHANNEL_FREE);
        sshc->actualCode = result;
      } else {
        state(conn, SSH_STOP);
      }
      break;

    case SSH_SCP_DOWNLOAD_INIT:
      {
        /*
         * We must check the remote file; if it is a directory no values will
         * be set in sb
         */
        struct stat sb;
        curl_off_t bytecount;

        memset(&sb, 0, sizeof(struct stat));
        sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
        if(!sshc->ssh_channel) {
          if(libssh2_session_last_errno(sshc->ssh_session) ==
             LIBSSH2_ERROR_EAGAIN) {
            break;
          } else {
            int ssh_err;
            char *err_msg;

            ssh_err = libssh2_session_last_error(sshc->ssh_session,
                                                 &err_msg, NULL, 0);
            err = libssh2_session_error_to_CURLE(ssh_err);
            failf(conn->data, "%s", err_msg);
            state(conn, SSH_SCP_CHANNEL_FREE);
            sshc->actualCode = err;
            break;
          }
        }

        /* download data */
        bytecount = (curl_off_t)sb.st_size;
        data->reqdata.maxdownload =  (curl_off_t)sb.st_size;
        result = Curl_setup_transfer(conn, FIRSTSOCKET,
                                     bytecount, FALSE, NULL, -1, NULL);

          state(conn, SSH_SCP_CHANNEL_FREE);
          sshc->actualCode = result;
        } else {
          state(conn, SSH_STOP);
        }
      }
      break;

    case SSH_SCP_DONE:
        state(conn, SSH_SCP_SEND_EOF);
      } else {
        state(conn, SSH_SCP_CHANNEL_FREE);
      }
      break;

    case SSH_SCP_SEND_EOF:
      if(sshc->ssh_channel) {
        rc = libssh2_channel_send_eof(sshc->ssh_channel);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to send libssh2 channel EOF\n");
        }
      }
      state(conn, SSH_SCP_WAIT_EOF);
      break;

    case SSH_SCP_WAIT_EOF:
      if(sshc->ssh_channel) {
        rc = libssh2_channel_wait_eof(sshc->ssh_channel);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to get channel EOF\n");
        }
      }
      state(conn, SSH_SCP_WAIT_CLOSE);
      break;

    case SSH_SCP_WAIT_CLOSE:
      if(sshc->ssh_channel) {
        rc = libssh2_channel_wait_closed(sshc->ssh_channel);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Channel failed to close\n");
        }
      }
      state(conn, SSH_SCP_CHANNEL_FREE);
      break;

    case SSH_SCP_CHANNEL_FREE:
      if(sshc->ssh_channel) {
        rc = libssh2_channel_free(sshc->ssh_channel);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to free libssh2 scp subsystem\n");
        }
      }
      state(conn, SSH_SESSION_DISCONECT);
      break;

    case SSH_CHANNEL_CLOSE:
      if(sshc->ssh_channel) {
        rc = libssh2_channel_close(sshc->ssh_channel);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to stop libssh2 channel subsystem\n");
        }
      }
      state(conn, SSH_SESSION_DISCONECT);
      break;

    case SSH_SESSION_DISCONECT:
      if(sshc->ssh_session) {
        rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to disconnect libssh2 session\n");
        }
      }

      Curl_safefree(sftp_scp->path);
      sftp_scp->path = NULL;

      Curl_safefree(sftp_scp->homedir);
      sftp_scp->homedir = NULL;

      state(conn, SSH_SESSION_FREE);
      break;

    case SSH_SESSION_FREE:
      if(sshc->ssh_session) {
        rc = libssh2_session_free(sshc->ssh_session);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          infof(data, "Failed to free libssh2 session\n");
        }
      state(conn, SSH_STOP);
      result = sshc->actualCode;
      break;
    case SSH_QUIT:
      /* fallthrough, just stop! */
    default:
      /* internal error */
      state(conn, SSH_STOP);
      break;
  }

  return result;
}

/* called repeatedly until done from multi.c */
static CURLcode Curl_ssh_multi_statemach(struct connectdata *conn, bool *done)
{
  struct ssh_conn *sshc = &conn->proto.sshc;
  CURLcode result = CURLE_OK;

  result = ssh_statemach_act(conn);
  *done = (bool)(sshc->state == SSH_STOP);

  return result;
}

static CURLcode ssh_easy_statemach(struct connectdata *conn)
{
  struct ssh_conn *sshc = &conn->proto.sshc;
  CURLcode result = CURLE_OK;
Daniel Stenberg's avatar
Daniel Stenberg committed
  while(sshc->state != SSH_STOP) {
static CURLcode ssh_init(struct connectdata *conn)
{
  struct SessionHandle *data = conn->data;
  ssh = (struct SSHPROTO *)calloc(sizeof(struct SSHPROTO), 1);
    return CURLE_OUT_OF_MEMORY;

  data->reqdata.proto.ssh = ssh;
  /* get some initial data into the ssh struct */
  ssh->bytecountp = &data->reqdata.keep.bytecount;

  /* no need to duplicate them, this connectdata struct won't change */
  ssh->user = conn->user;
  ssh->passwd = conn->passwd;
 * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
 * do protocol-specific actions at connect-time.
 */
static CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
  curl_socket_t sock;
  CURLcode result;
  struct SessionHandle *data = conn->data;

#if 0
  /* Due to the fact that the state machine always cleans up and kills all
     handles, we cannot currently re-use SCP or SFTP connections... */

  /* We default to persistent connections. We set this already in this connect
     function to make the re-use checks properly be able to check this bit. */
  conn->bits.close = FALSE;
#endif

  /* If there already is a protocol-specific struct allocated for this
     sessionhandle, deal with it */
  Curl_reset_reqproto(conn);

#ifdef CURL_LIBSSH2_DEBUG
    infof(data, "User: %s\n", ssh->user);
    infof(data, "Password: %s\n", ssh->passwd);
  }
#endif /* CURL_LIBSSH2_DEBUG */
  sock = conn->sock[FIRSTSOCKET];
  ssh->ssh_session = libssh2_session_init_ex(libssh2_malloc, libssh2_free,
    failf(data, "Failure initialising ssh session");
    return CURLE_FAILED_INIT;
  }
#ifdef CURL_LIBSSH2_DEBUG
  libssh2_trace(ssh->ssh_session, LIBSSH2_TRACE_CONN|LIBSSH2_TRACE_TRANS|
                LIBSSH2_TRACE_KEX|LIBSSH2_TRACE_AUTH|LIBSSH2_TRACE_SCP|
                LIBSSH2_TRACE_SFTP|LIBSSH2_TRACE_ERROR|
                LIBSSH2_TRACE_PUBLICKEY);
  infof(data, "SSH socket: %d\n", sock);
#endif /* CURL_LIBSSH2_DEBUG */

  state(conn, SSH_S_STARTUP);
  if(data->state.used_interface == Curl_if_multi)
    result = Curl_ssh_multi_statemach(conn, done);
  else {
    result = ssh_easy_statemach(conn);
/*
 ***********************************************************************
 *
 * scp_perform()
 *
 * This is the actual DO function for SCP. Get a file according to
 * the options previously setup.
 */

static
CURLcode scp_perform(struct connectdata *conn,
                      bool *connected,
                      bool *dophase_done)
{
  CURLcode result = CURLE_OK;

  DEBUGF(infof(conn->data, "DO phase starts\n"));

  *dophase_done = FALSE; /* not done yet */

  /* start the first command in the DO phase */
  state(conn, SSH_SCP_TRANS_INIT);

  /* run the state-machine */
  if(conn->data->state.used_interface == Curl_if_multi) {
    result = Curl_ssh_multi_statemach(conn, dophase_done);
  } else {
    result = ssh_easy_statemach(conn);
    *dophase_done = TRUE; /* with the easy interface we are done here */
  }
  *connected = conn->bits.tcpconnect;

    DEBUGF(infof(conn->data, "DO phase is complete\n"));
  }

  return result;
}

/* called from multi.c while DOing */
static CURLcode Curl_scp_doing(struct connectdata *conn,
                               bool *dophase_done)
{
  CURLcode result;
  result = Curl_ssh_multi_statemach(conn, dophase_done);

    DEBUGF(infof(conn->data, "DO phase is complete\n"));
  }
  return result;
}


static CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
  CURLcode res;
  bool connected = 0;
  struct SessionHandle *data = conn->data;

  *done = FALSE; /* default to false */

  /*
   * Since connections can be re-used between SessionHandles, this might be a
   * connection already existing but on a fresh SessionHandle struct so we must
   * make sure we have a good 'struct SSHPROTO' to play with. For new
   * connections, the struct SSHPROTO is allocated and setup in the
   * Curl_ssh_connect() function.
   */
  res = ssh_init(conn);
    return res;
  }

  data->reqdata.size = -1; /* make sure this is unknown at this point */

  Curl_pgrsSetUploadCounter(data, 0);
  Curl_pgrsSetDownloadCounter(data, 0);
  Curl_pgrsSetUploadSize(data, 0);
  Curl_pgrsSetDownloadSize(data, 0);

  res = scp_perform(conn, &connected,  done);

  return res;
static CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
                              bool premature)
  CURLcode result = CURLE_OK;
  bool done = FALSE;
  (void)premature; /* not used */
  (void)status; /* unused */