Skip to content
ssh.c 94.9 KiB
Newer Older
        state(conn, SSH_SFTP_QUOTE);
      }
      else {
        state(conn, SSH_SFTP_TRANS_INIT);
      }
      break;

    case SSH_SFTP_POSTQUOTE_INIT:
      if(data->set.postquote) {
        infof(data, "Sending quote commands\n");
        sshc->quote_item = data->set.postquote;
        state(conn, SSH_SFTP_QUOTE);
      }
      else {
        state(conn, SSH_STOP);
      }
      break;

    case SSH_SFTP_QUOTE:
      /* Send any quote commands */
    {
      const char *cp;

      if(curl_strequal("pwd", sshc->quote_item->data)) {
        /* output debug output if that is requested */
        if(data->set.verbose) {
          char tmp[PATH_MAX+1];

          Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
          snprintf(tmp, PATH_MAX, "257 \"%s\" is current directory.\n",
                   sftp_scp->path);
          Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
        }
        state(conn, SSH_SFTP_NEXT_QUOTE);
        break;
      }
      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;
          break;
        }

        /*
         * also, every command takes at least one argument so we get that
         * first argument right now
         */
        result = get_pathname(&cp, &sshc->quote_path1);
Yang Tse's avatar
Yang Tse committed
        if(result) {
          if(result == CURLE_OUT_OF_MEMORY)
            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 */
          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);
            sshc->actualcode = result;
            break;
          }
          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 */
          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);
            sshc->actualcode = result;
            break;
          }
          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 */
          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);
            sshc->actualcode = result;
            break;
          }
          state(conn, SSH_SFTP_QUOTE_RENAME);
          break;
        }
        else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
          /* delete dir */
          state(conn, SSH_SFTP_QUOTE_RMDIR);
          break;
        }
        else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
          state(conn, SSH_SFTP_QUOTE_UNLINK);
          break;
        }

        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;
    }
    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;
        }
        else {
          state(conn, SSH_SFTP_TRANS_INIT);
        }
      }
      break;

    case SSH_SFTP_QUOTE_STAT:
      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_ex(sshc->sftp_session, sshc->quote_path2,
                                  (unsigned int)strlen(sshc->quote_path2),
                                  LIBSSH2_SFTP_STAT,
                                  &sshc->quote_attrs);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
          break;
        }
        else if(rc != 0) { /* get those attributes */
          err = (int)(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));

      /* Now set the new attributes... */
      if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
Yang Tse's avatar
 
Yang Tse committed
        sshc->quote_attrs.gid = strtoul(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");
      else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
Yang Tse's avatar
 
Yang Tse committed
        sshc->quote_attrs.permissions = strtoul(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");
          state(conn, SSH_SFTP_CLOSE);
          sshc->actualcode = CURLE_QUOTE_ERROR;
          break;
        }
      else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
Yang Tse's avatar
 
Yang Tse committed
        sshc->quote_attrs.uid = strtoul(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");
          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_stat_ex(sshc->sftp_session, sshc->quote_path2,
                                (unsigned int)strlen(sshc->quote_path2),
                                LIBSSH2_SFTP_SETSTAT,
        err = (int)(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;
      rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
                                   (unsigned int)strlen(sshc->quote_path1),
                                   sshc->quote_path2,
                                   (unsigned int)strlen(sshc->quote_path2),
                                   LIBSSH2_SFTP_SYMLINK);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc != 0) {
        err = (int)(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));
        sshc->actualcode = CURLE_QUOTE_ERROR;
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_QUOTE_MKDIR:
      rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
                                 (unsigned int)strlen(sshc->quote_path1),
                                 0755);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc != 0) {
        err = (int)(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));
        sshc->actualcode = CURLE_QUOTE_ERROR;
      state(conn, SSH_SFTP_NEXT_QUOTE);
      break;

    case SSH_SFTP_QUOTE_RENAME:
      rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
                                  (unsigned int)strlen(sshc->quote_path1),
                                  sshc->quote_path2,
                                  (unsigned int)strlen(sshc->quote_path2),
                                  LIBSSH2_SFTP_RENAME_OVERWRITE |
                                  LIBSSH2_SFTP_RENAME_ATOMIC |
                                  LIBSSH2_SFTP_RENAME_NATIVE);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc != 0) {
        err = (int)(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));
        sshc->actualcode = CURLE_QUOTE_ERROR;
      rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
                                 (unsigned int)strlen(sshc->quote_path1));
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc != 0) {
        err = (int)(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);
      rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
                                  (unsigned int)strlen(sshc->quote_path1));
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc != 0) {
        err = (int)(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);
    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);
      }
    case SSH_SFTP_UPLOAD_INIT:
    {
      unsigned long flags;
      /*
       * 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_ex(sshc->sftp_session, sftp_scp->path,
                                    (unsigned int)strlen(sftp_scp->path),
                                    LIBSSH2_SFTP_STAT, &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;
        libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
                             (unsigned int)strlen(sftp_scp->path),
                             flags, data->set.new_file_perms,
                             LIBSSH2_SFTP_OPENFILE);
      if(!sshc->sftp_handle) {
        rc = libssh2_session_last_errno(sshc->ssh_session);
        if(LIBSSH2_ERROR_EAGAIN == rc)
          break;
        else {
          if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
            /* only when there was an SFTP protocol error can we extract
               the sftp error! */
            err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
          else
            err = -1; /* not an sftp error at all */
          if(sshc->secondCreateDirs) {
            state(conn, SSH_SFTP_CLOSE);
            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));
            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;
          }
          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_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 '/' */
        state(conn, SSH_SFTP_CREATE_DIRS);
    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_ex(sshc->sftp_session, sftp_scp->path,
                                 (unsigned int)strlen(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 = (unsigned int)(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;
    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_open_ex(sshc->sftp_session,
                                               sftp_scp->path,
                                               (unsigned int)
                                               strlen(sftp_scp->path),
                                               0, 0, LIBSSH2_SFTP_OPENDIR);
      if(!sshc->sftp_handle) {
        if(libssh2_session_last_errno(sshc->ssh_session) ==
           LIBSSH2_ERROR_EAGAIN) {
          rc = LIBSSH2_ERROR_EAGAIN;
          err = (int)(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;
        break;
      }
      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) {
        rc = 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);
          Curl_safefree(tmpLine);
          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 = (int)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;
          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);
            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);
      }
      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 = (int)(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);
      sshc->readdir_len =
        libssh2_sftp_symlink_ex(sshc->sftp_session,
                                sshc->readdir_linkPath,
                                (unsigned int) strlen(sshc->readdir_linkPath),
                                sshc->readdir_filename,
                                PATH_MAX, LIBSSH2_SFTP_READLINK);
      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
        rc = 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;
        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,
                                 sshc->readdir_currLen);
        /* 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);
    case SSH_SFTP_READDIR_DONE:
      if(libssh2_sftp_closedir(sshc->sftp_handle) ==
      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);
        libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
                             (unsigned int)strlen(sftp_scp->path),
                             LIBSSH2_FXF_READ, data->set.new_file_perms,
                             LIBSSH2_SFTP_OPENFILE);
      if(!sshc->sftp_handle) {
        if(libssh2_session_last_errno(sshc->ssh_session) ==
           LIBSSH2_ERROR_EAGAIN) {
          rc = LIBSSH2_ERROR_EAGAIN;
          break;
          err = (int)(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);
          result = sftp_libssh2_error_to_CURLE(err);
          sshc->actualcode = result?result:CURLE_SSH;
          break;
    case SSH_SFTP_DOWNLOAD_STAT:
    {
      LIBSSH2_SFTP_ATTRIBUTES attrs;

      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
                                (unsigned int)strlen(sftp_scp->path),
                                LIBSSH2_SFTP_STAT, &attrs);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      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) {
            from = to;
            size = 0;
          }
          else {
            size = to - from + 1;
          }

          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);
      }
    }
    /* Setup the actual download */
    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,
                                   FALSE, NULL, -1, NULL);
      /* 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);
    }