Skip to content
ssh.c 88.4 KiB
Newer Older
    }
    else if(sshc->quote_item->data) {
      /*
       * the arguments following the command must be separated from the
       * command with a space so we can check for it unconditionally
       */
      cp = strchr(sshc->quote_item->data, ' ');
      if(cp == NULL) {
        failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualcode = CURLE_QUOTE_ERROR;

      /*
       * also, every command takes at least one argument so we get that
       * first argument right now
       */
Yang Tse's avatar
Yang Tse committed
      result = get_pathname(&cp, &sshc->quote_path1);
      if(result) {
        if(result == CURLE_OUT_OF_MEMORY)
          failf(data, "Out of memory");
        else
          failf(data, "Syntax error: Bad first parameter");
Yang Tse's avatar
Yang Tse committed
        sshc->actualcode = result;
      /*
       * SFTP is a binary protocol, so we don't send text commands to
       * the server. Instead, we scan for commands for commands used by
       * OpenSSH's sftp program and call the appropriate libssh2
       * functions.
       */
      if(curl_strnequal(sshc->quote_item->data, "chgrp ", 6) ||
         curl_strnequal(sshc->quote_item->data, "chmod ", 6) ||
         curl_strnequal(sshc->quote_item->data, "chown ", 6) ) {
        /* attribute change */

        /* sshc->quote_path1 contains the mode to set */
        /* get the destination */
Yang Tse's avatar
Yang Tse committed
        result = get_pathname(&cp, &sshc->quote_path2);
        if(result) {
          if(result == CURLE_OUT_OF_MEMORY)
            failf(data, "Out of memory");
          else
            failf(data, "Syntax error in chgrp/chmod/chown: "
                  "Bad second parameter");
          Curl_safefree(sshc->quote_path1);
          sshc->quote_path1 = NULL;
          state(conn, SSH_SFTP_CLOSE);
Yang Tse's avatar
Yang Tse committed
          sshc->actualcode = result;
        memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
        state(conn, SSH_SFTP_QUOTE_STAT);
        break;
      else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) ||
              curl_strnequal(sshc->quote_item->data, "symlink ", 8)) {
        /* symbolic linking */
        /* sshc->quote_path1 is the source */
        /* get the destination */
Yang Tse's avatar
Yang Tse committed
        result = get_pathname(&cp, &sshc->quote_path2);
        if(result) {
          if(result == CURLE_OUT_OF_MEMORY)
            failf(data, "Out of memory");
          else
            failf(data,
                  "Syntax error in ln/symlink: Bad second parameter");
          Curl_safefree(sshc->quote_path1);
          sshc->quote_path1 = NULL;
          state(conn, SSH_SFTP_CLOSE);
Yang Tse's avatar
Yang Tse committed
          sshc->actualcode = result;
        state(conn, SSH_SFTP_QUOTE_SYMLINK);
        break;
      else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) {
        /* create dir */
        state(conn, SSH_SFTP_QUOTE_MKDIR);
        break;
      }
      else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) {
        /* rename file */
        /* first param is the source path */
        /* second param is the dest. path */
Yang Tse's avatar
Yang Tse committed
        result = get_pathname(&cp, &sshc->quote_path2);
        if(result) {
          if(result == CURLE_OUT_OF_MEMORY)
            failf(data, "Out of memory");
          else
            failf(data, "Syntax error in rename: Bad second parameter");
          Curl_safefree(sshc->quote_path1);
          sshc->quote_path1 = NULL;
          state(conn, SSH_SFTP_CLOSE);
Yang Tse's avatar
Yang Tse committed
          sshc->actualcode = result;
      }
      else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
        /* delete dir */
        state(conn, SSH_SFTP_QUOTE_RMDIR);
      else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
        state(conn, SSH_SFTP_QUOTE_UNLINK);
      failf(data, "Unknown SFTP command");
      Curl_safefree(sshc->quote_path1);
      sshc->quote_path1 = NULL;
      Curl_safefree(sshc->quote_path2);
      sshc->quote_path2 = NULL;
      state(conn, SSH_SFTP_CLOSE);
      sshc->actualcode = CURLE_QUOTE_ERROR;
      break;
    }
  }
  if(!sshc->quote_item) {
    state(conn, SSH_SFTP_TRANS_INIT);
  }
  break;
  case SSH_SFTP_NEXT_QUOTE:
    if(sshc->quote_path1) {
      Curl_safefree(sshc->quote_path1);
      sshc->quote_path1 = NULL;
    }
    if(sshc->quote_path2) {
      Curl_safefree(sshc->quote_path2);
      sshc->quote_path2 = NULL;
    }

    sshc->quote_item = sshc->quote_item->next;

    if(sshc->quote_item) {
      state(conn, SSH_SFTP_QUOTE);
    }
    else {
      if(sshc->nextstate != SSH_NO_STATE) {
        state(conn, sshc->nextstate);
        sshc->nextstate = SSH_NO_STATE;
    if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
      /* Since chown and chgrp only set owner OR group but libssh2 wants to
       * set them both at once, we need to obtain the current ownership first.
       * This takes an extra protocol round trip.
       */
      rc = libssh2_sftp_stat(sshc->sftp_session, sshc->quote_path2,
      }
      else if(rc != 0) { /* get those attributes */
        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 get SFTP stats failed: %s",
              sftp_libssh2_strerror(err));
        state(conn, SSH_SFTP_CLOSE);
        sshc->actualcode = CURLE_QUOTE_ERROR;
        break;
    /* Now set the new attributes... */
    if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
      sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10);
      sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
      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");
        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);
      sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
      /* 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");
        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);
      sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
      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");
        sshc->actualcode = CURLE_QUOTE_ERROR;
    /* Now send the completed structure... */
    state(conn, SSH_SFTP_QUOTE_SETSTAT);
    break;
  case SSH_SFTP_QUOTE_SETSTAT:
    rc = libssh2_sftp_setstat(sshc->sftp_session, sshc->quote_path2,
                              &sshc->quote_attrs);
    if(rc == LIBSSH2_ERROR_EAGAIN) {
      break;
    }
    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;
      break;
    }
    state(conn, SSH_SFTP_NEXT_QUOTE);
    break;
  case SSH_SFTP_QUOTE_SYMLINK:
    rc = libssh2_sftp_symlink(sshc->sftp_session, sshc->quote_path1,
                              sshc->quote_path2);
    if(rc == LIBSSH2_ERROR_EAGAIN) {
      break;
    }
    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) {
      break;
    }
    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;
  case SSH_SFTP_QUOTE_RENAME:
    rc = libssh2_sftp_rename(sshc->sftp_session, sshc->quote_path1,
                             sshc->quote_path2);
    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;
      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) {
      break;
    }
    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:
    if(data->set.upload)
      state(conn, SSH_SFTP_UPLOAD_INIT);
    else {
      if(data->set.opt_no_body)
        state(conn, SSH_STOP);
      else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
        state(conn, SSH_SFTP_READDIR_INIT);
      else
        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.
     */

    if(data->state.resume_from != 0) {
      LIBSSH2_SFTP_ATTRIBUTES attrs;
      if(data->state.resume_from< 0) {
        rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          break;
        }
        else if(rc) {
          data->state.resume_from = 0;
        }
        else {
          data->state.resume_from = attrs.filesize;
        }
      }
    }

    if(data->set.ftp_append)
      /* Try to open for append, but create if nonexisting */
      flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
    else if (data->state.resume_from > 0)
      /* If we have restart position then open for append */
      flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
    else
      /* Clear file before writing (normal behaviour) */
      flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;

    sshc->sftp_handle =
      libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
      rc = libssh2_session_last_errno(sshc->ssh_session);

      if(LIBSSH2_ERROR_EAGAIN == rc)
        if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
          /* only when there was an SFTP protocol error can we extract
             the sftp error! */
          err = libssh2_sftp_last_error(sshc->sftp_session);
        else
          err = -1; /* not an sftp error at all */

          sshc->actualcode = err>= LIBSSH2_FX_OK?
            sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
          failf(data, "Creating the dir/file failed: %s",
                sftp_libssh2_strerror(err));
        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 = err>= LIBSSH2_FX_OK?
          sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
        if(!sshc->actualcode) {
          /* Sometimes, for some reason libssh2_sftp_last_error() returns zero
             even though libssh2_sftp_open() failed previously! We need to
             work around that! */
          sshc->actualcode = CURLE_SSH;
          err=-1;
        }
        failf(data, "Upload failed: %s (%d/%d)",
              err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error",
              err, rc);
    /* If we have restart point then we need to seek to the correct position. */
    if(data->state.resume_from > 0) {
      /* Let's read off the proper amount of bytes from the input. */
      if(conn->seek_func) {
        seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
                                  SEEK_SET);
      }

      if(seekerr != CURL_SEEKFUNC_OK){
        if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
          failf(data, "Could not seek stream");
          return CURLE_FTP_COULDNT_USE_REST;
        }
        /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
        else {
          curl_off_t passed=0;
          curl_off_t readthisamountnow;
          curl_off_t actuallyread;
          do {
            readthisamountnow = (data->state.resume_from - passed);

            if(readthisamountnow > BUFSIZE)
              readthisamountnow = BUFSIZE;

            actuallyread =
              (curl_off_t) conn->fread_func(data->state.buffer, 1,
                                            (size_t)readthisamountnow,
                                            conn->fread_in);

            passed += actuallyread;
            if((actuallyread <= 0) || (actuallyread > readthisamountnow)) {
              /* this checks for greater-than only to make sure that the
                 CURL_READFUNC_ABORT return code still aborts */
              failf(data, "Failed to read data");
              return CURLE_FTP_COULDNT_USE_REST;
            }
          } while(passed < data->state.resume_from);
        }
      }

      /* now, decrease the size of the read */
      if(data->set.infilesize>0) {
        data->set.infilesize -= data->state.resume_from;
        data->req.size = data->set.infilesize;
        Curl_pgrsSetUploadSize(data, data->set.infilesize);
      }

      SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
    }
    if(data->set.infilesize>0) {
      data->req.size = data->set.infilesize;
      Curl_pgrsSetUploadSize(data, data->set.infilesize);
    }
    /* upload data */
    result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
                                 FIRSTSOCKET, NULL);

    /* not set by Curl_setup_transfer to preserve keepon bits */
    conn->sockfd = conn->writesockfd;

    if(result) {
      state(conn, SSH_SFTP_CLOSE);
      sshc->actualcode = result;
    }
    else {
      /* store this original bitmask setup to use later on if we can't figure
         out a "real" bitmask */
      sshc->orig_waitfor = data->req.keepon;


  case SSH_SFTP_CREATE_DIRS_INIT:
    if(strlen(sftp_scp->path) > 1) {
      sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
    }
    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);
  case SSH_SFTP_CREATE_DIRS_MKDIR:
    /* 'mode' - parameter is preliminary - default to 0644 */
    rc = libssh2_sftp_mkdir(sshc->sftp_session, sftp_scp->path,
                            data->set.new_directory_perms);
    if(rc == LIBSSH2_ERROR_EAGAIN) {
      break;
    }
    *sshc->slash_pos = '/';
    ++sshc->slash_pos;
    if(rc == -1) {
      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);
        sshc->actualcode = result?result:CURLE_SSH;
        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,
                                             sftp_scp->path);
    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 directory for reading: %s",
              sftp_libssh2_strerror(err));
        result = sftp_libssh2_error_to_CURLE(err);
        sshc->actualcode = result?result:CURLE_SSH;
    if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) {
      state(conn, SSH_SFTP_CLOSE);
      sshc->actualcode = CURLE_OUT_OF_MEMORY;
    if((sshc->readdir_longentry = 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) {
      break;
    }
    if(sshc->readdir_len > 0) {
      sshc->readdir_filename[sshc->readdir_len] = '\0';
      if(data->set.ftp_list_only) {
        char *tmpLine;
        tmpLine = aprintf("%s\n", sshc->readdir_filename);
        if(tmpLine == NULL) {
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualcode = CURLE_OUT_OF_MEMORY;
          break;
        }
        result = Curl_client_write(conn, CLIENTWRITE_BODY,
                                   tmpLine, sshc->readdir_len+1);
        if(result) {
          state(conn, SSH_STOP);
          break;
        }
        /* since this counts what we send to the client, we include the newline
           in this counter */
        data->req.bytecount += sshc->readdir_len+1;
        /* output debug output if that is requested */
        if(data->set.verbose) {
          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 = calloc(sshc->readdir_totalLen, 1);
        if(!sshc->readdir_line) {
          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 = malloc(PATH_MAX + 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;
          snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
                   sshc->readdir_filename);
          state(conn, SSH_SFTP_READDIR_LINK);
        state(conn, SSH_SFTP_READDIR_BOTTOM);
    }
    else if(sshc->readdir_len == 0) {
      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);
      result = sftp_libssh2_error_to_CURLE(err);
      sshc->actualcode = result?result:CURLE_SSH;
      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);
    if(!sshc->readdir_line) {
      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;
    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,
                               sshc->readdir_currLen);
    if(result == CURLE_OK) {

      /* output debug output if that is requested */
      if(data->set.verbose) {
        Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
                   sshc->readdir_currLen, conn);
      }
      data->req.bytecount += sshc->readdir_currLen;
    }
    Curl_safefree(sshc->readdir_line);
    sshc->readdir_line = NULL;
    if(result) {
      state(conn, SSH_STOP);
    }
    else
      state(conn, SSH_SFTP_READDIR);
    break;

  case SSH_SFTP_READDIR_DONE:
    if(libssh2_sftp_closedir(sshc->sftp_handle) ==
       LIBSSH2_ERROR_EAGAIN) {
      break;
    }
    sshc->sftp_handle = NULL;
    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));
        result = sftp_libssh2_error_to_CURLE(err);
        sshc->actualcode = result?result:CURLE_SSH;
  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) {
    }
    else if(rc) {
      /*
       * libssh2_sftp_open() didn't return an error, so maybe the server
       * just doesn't support stat()
       */
      data->req.size = -1;
      data->req.maxdownload = -1;
      curl_off_t size;

      size = attrs.filesize;
      if(conn->data->state.use_range) {
        curl_off_t from, to;
        char *ptr;
        char *ptr2;

        from=curlx_strtoofft(conn->data->state.range, &ptr, 0);
        while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
          ptr++;
        to=curlx_strtoofft(ptr, &ptr2, 0);
        if((ptr == ptr2) /* no "to" value given */
            || (to >= size)) {
          to = size - 1;
        if(from < 0) {
          /* from is relative to end of file */
          from += size;
        }
        if(from >= size) {
          failf(data, "Offset (%"
                FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
                from, attrs.filesize);
          return CURLE_BAD_DOWNLOAD_RESUME;
        }
        if(from > to) {
        SFTP_SEEK(conn->proto.sshc.sftp_handle, from);
      }
      data->req.size = size;
      data->req.maxdownload = size;
      Curl_pgrsSetDownloadSize(data, size);
    /* We can resume if we can seek to the resume position */
    if(data->state.resume_from) {
      if(data->state.resume_from< 0) {
        /* We're supposed to download the last abs(from) bytes */
        if((curl_off_t)attrs.filesize < -data->state.resume_from) {
          failf(data, "Offset (%"
                FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
                data->state.resume_from, attrs.filesize);
          return CURLE_BAD_DOWNLOAD_RESUME;
        }
        /* download from where? */
        data->state.resume_from = attrs.filesize - data->state.resume_from;
      }
      else {
        if((curl_off_t)attrs.filesize < data->state.resume_from) {
          failf(data, "Offset (%" FORMAT_OFF_T
                ") was beyond file size (%" FORMAT_OFF_T ")",
                data->state.resume_from, attrs.filesize);
          return CURLE_BAD_DOWNLOAD_RESUME;
        }
      }
      /* Does a completed file need to be seeked and started or closed ? */
      /* Now store the number of bytes we are expected to download */
      data->req.size = attrs.filesize - data->state.resume_from;
      data->req.maxdownload = attrs.filesize - data->state.resume_from;
      Curl_pgrsSetDownloadSize(data,
                               attrs.filesize - data->state.resume_from);
      SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
  if(data->req.size == 0) {
    /* no data to transfer */
    result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
    infof(data, "File already completely downloaded\n");
    state(conn, SSH_STOP);
    break;
  }
  else {
    result = Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,

    /* not set by Curl_setup_transfer to preserve keepon bits */
    conn->writesockfd = conn->sockfd;

    /* FIXME: here should be explained why we need it to start the download */
    conn->cselect_bits = CURL_CSELECT_IN;
  if(result) {
    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) {
      else if(rc < 0) {
        infof(data, "Failed to close libssh2 file\n");
      }
    }
    Curl_safefree(sftp_scp->path);
    sftp_scp->path = NULL;
    DEBUGF(infof(data, "SFTP DONE done\n"));
#if 0 /* PREV */
    state(conn, SSH_SFTP_SHUTDOWN);
#endif
    state(conn, SSH_STOP);
    result = sshc->actualcode;
    break;
    /* during times we get here due to a broken transfer and then the
       sftp_handle might not have been taken down so make sure that is done
       before we proceed */

    if(sshc->sftp_handle) {
      rc = libssh2_sftp_close(sshc->sftp_handle);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc < 0) {
        infof(data, "Failed to close libssh2 file\n");
      }
      sshc->sftp_handle = NULL;
    }
    if(sshc->sftp_session) {
      rc = libssh2_sftp_shutdown(sshc->sftp_session);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      else if(rc < 0) {
        infof(data, "Failed to stop libssh2 sftp subsystem\n");
      }
      sshc->sftp_session = NULL;
    }
    Curl_safefree(sshc->homedir);
    sshc->homedir = NULL;
    state(conn, SSH_SESSION_DISCONNECT);
    break;
  case SSH_SCP_TRANS_INIT:
    result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
    if(result) {
      sshc->actualcode = result;
      state(conn, SSH_STOP);
    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;