Index: runtime/cmake/modules/FindPackageVersionCheck.cmake
===================================================================
--- runtime/cmake/modules/FindPackageVersionCheck.cmake	(revision 0)
+++ runtime/cmake/modules/FindPackageVersionCheck.cmake	(revision 0)
@@ -0,0 +1,68 @@
+# FIND_PACKAGE_VERSION_CHECK(NAME (DEFAULT_MSG|"Custom failure message"))
+#    This function is intended to be used in FindXXX.cmake modules files.
+#    It handles NAME_FIND_VERSION and NAME_VERSION variables in a Module.
+#
+#    Example:
+#    find_package(LibSSH 0.3.2)
+#
+#    # check for the version and set it
+#    set(LibSSH_VERSION 0.3.0)
+#    find_package_version_check(LibSSH DEFAULT_MSG)
+#
+#
+# Copyright (c) 2009 Andreas Schneider <mail@cynapses.org>
+#
+# Redistribution and use is allowed according to the terms of the New
+# BSD license.
+
+function(FIND_PACKAGE_VERSION_CHECK _NAME _FAIL_MSG)
+  string(TOUPPER ${_NAME} _NAME_UPPER)
+  set(_AGE "old")
+
+  if(${_NAME}_FIND_VERSION_EXACT)
+    if (${_NAME}_FIND_VERSION VERSION_EQUAL ${_NAME}_VERSION)
+      # exact version found
+      set(${_NAME_UPPER}_FOUND TRUE)
+    else (${_NAME}_FIND_VERSION VERSION_EQUAL ${_NAME}_VERSION)
+      # exect version not found
+      set(${_NAME_UPPER}_FOUND FALSE)
+      # check if newer or older
+      if (${_NAME}_FIND_VERSION VERSION_LESS ${_NAME}_VERSION)
+        set(_AGE "new")
+      else (${_NAME}_FIND_VERSION VERSION_LESS ${_NAME}_VERSION)
+        set(_AGE "old")
+      endif (${_NAME}_FIND_VERSION VERSION_LESS ${_NAME}_VERSION)
+    endif (${_NAME}_FIND_VERSION VERSION_EQUAL ${_NAME}_VERSION)
+  else (${_NAME}_FIND_VERSION_EXACT)
+    if (${_NAME}_FIND_VERSION)
+      if (${_NAME}_VERSION VERSION_LESS ${_NAME}_FIND_VERSION)
+        set(${_NAME_UPPER}_FOUND FALSE)
+        set(_AGE "old")
+      else (${_NAME}_VERSION VERSION_LESS ${_NAME}_FIND_VERSION)
+        set(${_NAME_UPPER}_FOUND TRUE)
+     endif (${_NAME}_VERSION VERSION_LESS ${_NAME}_FIND_VERSION)
+    endif (${_NAME}_FIND_VERSION)
+  endif(${_NAME}_FIND_VERSION_EXACT)
+
+  if ("${_FAIL_MSG}" STREQUAL "DEFAULT_MSG")
+    if (${_NAME}_FIND_VERSION_EXACT)
+      set(_FAIL_MESSAGE "The installed ${_NAME} version ${${_NAME}_VERSION} is too ${_AGE}, version ${${_NAME}_FIND_VERSION} is required.")
+    else (${_NAME}_FIND_VERSION_EXACT)
+      set(_FAIL_MESSAGE "The installed ${_NAME} version ${${_NAME}_VERSION} is too ${_AGE}, at least version ${${_NAME}_FIND_VERSION} is required.")
+    endif (${_NAME}_FIND_VERSION_EXACT)
+  else ("${_FAIL_MSG}" STREQUAL "DEFAULT_MSG")
+    set(_FAIL_MESSAGE "${_FAIL_MSG}")
+  endif ("${_FAIL_MSG}" STREQUAL "DEFAULT_MSG")
+
+  if (NOT ${_NAME_UPPER}_FOUND)
+    if (${_NAME}_FIND_REQUIRED)
+      message(FATAL_ERROR "${_FAIL_MESSAGE}")
+    else (${_NAME}_FIND_REQUIRED)
+      if (NOT ${_NAME}_FIND_QUIETLY)
+        message(STATUS "${_FAIL_MESSAGE}")
+      endif (NOT ${_NAME}_FIND_QUIETLY)
+    endif (${_NAME}_FIND_REQUIRED)
+  endif (NOT ${_NAME_UPPER}_FOUND)
+
+  set(${_NAME_UPPER}_FOUND ${${_NAME_UPPER}_FOUND} PARENT_SCOPE)
+endfunction(FIND_PACKAGE_VERSION_CHECK)
Index: runtime/cmake/modules/FindLibSSH.cmake
===================================================================
--- runtime/cmake/modules/FindLibSSH.cmake	(revision 0)
+++ runtime/cmake/modules/FindLibSSH.cmake	(revision 0)
@@ -0,0 +1,92 @@
+# - Try to find LibSSH
+# Once done this will define
+#
+#  LIBSSH_FOUND - system has LibSSH
+#  LIBSSH_INCLUDE_DIRS - the LibSSH include directory
+#  LIBSSH_LIBRARIES - Link these to use LibSSH
+#  LIBSSH_DEFINITIONS - Compiler switches required for using LibSSH
+#
+#  Copyright (c) 2009 Andreas Schneider <mail@cynapses.org>
+#
+#  Redistribution and use is allowed according to the terms of the New
+#  BSD license.
+#  For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+#
+
+if (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS)
+  # in cache already
+  set(LIBSSH_FOUND TRUE)
+else (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS)
+
+  find_path(LIBSSH_INCLUDE_DIR
+    NAMES
+      libssh/libssh.h
+    PATHS
+      /usr/include
+      /usr/local/include
+      /opt/local/include
+      /sw/include
+  )
+
+  find_library(SSH_LIBRARY
+    NAMES
+      ssh
+      libssh
+    PATHS
+      /usr/lib
+      /usr/local/lib
+      /opt/local/lib
+      /sw/lib
+  )
+
+  if (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY)
+    set(SSH_FOUND TRUE)
+  endif (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY)
+
+  set(LIBSSH_INCLUDE_DIRS
+    ${LIBSSH_INCLUDE_DIR}
+  )
+
+  if (SSH_FOUND)
+    set(LIBSSH_LIBRARIES
+      ${LIBSSH_LIBRARIES}
+      ${SSH_LIBRARY}
+    )
+
+    if (LibSSH_FIND_VERSION)
+      file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MAJOR
+        REGEX "#define[ ]+LIBSSH_VERSION_MAJOR[ ]+[0-9]+")
+      # Older versions of libssh like libssh-0.2 have LIBSSH_VERSION but not LIBSSH_VERSION_MAJOR
+      if (LIBSSH_VERSION_MAJOR)
+        string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MAJOR ${LIBSSH_VERSION_MAJOR})
+	file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MINOR
+          REGEX "#define[ ]+LIBSSH_VERSION_MINOR[ ]+[0-9]+")
+	string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MINOR ${LIBSSH_VERSION_MINOR})
+	file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_PATCH
+          REGEX "#define[ ]+LIBSSH_VERSION_MICRO[ ]+[0-9]+")
+	string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_PATCH ${LIBSSH_VERSION_PATCH})
+
+	set(LibSSH_VERSION ${LIBSSH_VERSION_MAJOR}.${LIBSSH_VERSION_MINOR}.${LIBSSH_VERSION_PATCH})
+
+	include(FindPackageVersionCheck)
+	find_package_version_check(LibSSH DEFAULT_MSG)
+      else (LIBSSH_VERSION_MAJOR)
+        message(STATUS "LIBSSH_VERSION_MAJOR not found in ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h, assuming libssh is too old")
+        set(LIBSSH_FOUND FALSE)
+      endif (LIBSSH_VERSION_MAJOR)
+    endif (LibSSH_FIND_VERSION)
+  endif (SSH_FOUND)
+
+  # If the version is too old, but libs and includes are set,
+  # find_package_handle_standard_args will set LIBSSH_FOUND to TRUE again,
+  # so we need this if() here.
+  if (LIBSSH_FOUND)
+    include(FindPackageHandleStandardArgs)
+    find_package_handle_standard_args(LibSSH DEFAULT_MSG LIBSSH_LIBRARIES LIBSSH_INCLUDE_DIRS)
+  endif (LIBSSH_FOUND)
+
+  # show the LIBSSH_INCLUDE_DIRS and LIBSSH_LIBRARIES variables only in the advanced view
+  mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES)
+
+endif (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS)
+
Index: runtime/kioslave/sftp/AUTHORS
===================================================================
--- runtime/kioslave/sftp/AUTHORS	(revision 1026868)
+++ runtime/kioslave/sftp/AUTHORS	(working copy)
@@ -1,3 +1,3 @@
+Andreas Schneider <mail@cynapses.org>
 Dawit Alemayehu <adawit@kde.org>
 Lucas Fisher <ljfisher@iastate.edu>
-
Index: runtime/kioslave/sftp/CHANGELOG
===================================================================
--- runtime/kioslave/sftp/CHANGELOG	(revision 1026868)
+++ runtime/kioslave/sftp/CHANGELOG	(working copy)
@@ -1,3 +1,8 @@
+2009-08-01 - Rewrite with libssh.
+             * Resume support
+             * Support for different details
+             * Mimetype support
+
 - add dialog to ask for username
 - rename() causes SSH to die
 - How to handle overwrite?
Index: runtime/kioslave/sftp/kio_sftp.cpp
===================================================================
--- runtime/kioslave/sftp/kio_sftp.cpp	(revision 1026868)
+++ runtime/kioslave/sftp/kio_sftp.cpp	(working copy)
@@ -1,27 +1,23 @@
-/***************************************************************************
-                          sftp.cpp  -  description
-                             -------------------
-    begin                : Fri Jun 29 23:45:40 CDT 2001
-    copyright            : (C) 2001 by Lucas Fisher
-    email                : ljfisher@purdue.edu
- ***************************************************************************/
-
-/***************************************************************************
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- ***************************************************************************/
-
 /*
-DEBUGGING
-We are pretty much left with kDebug messages for debugging. We can't use a gdb
-as described in the ioslave DEBUG.howto because kdeinit has to run in a terminal.
-Ssh will detect this terminal and ask for a password there, but will just get garbage.
-So we can't connect.
-*/
+ * Copyright (c) 2001      Lucas Fisher <ljfisher@purdue.edu>
+ * Copyright (c) 2009      Andreas Schneider <mail@cynapses.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
 
 #include "kio_sftp.h"
 
@@ -32,25 +28,21 @@
 #include <QtCore/QCoreApplication>
 #include <QtCore/QBuffer>
 #include <QtCore/QByteArray>
+#include <QtCore/QDir>
 #include <QtCore/QFile>
 #include <QtCore/QObject>
 #include <QtCore/QString>
+#include <QtCore/QVarLengthArray>
 
 #include <stdlib.h>
 #include <unistd.h>
-#include <signal.h>
 #include <errno.h>
-#include <ctype.h>
 #include <time.h>
-#include <netdb.h>
 #include <string.h>
 
-#include <netinet/in.h>
-#include <arpa/inet.h>
 #include <sys/time.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 
 #include <kapplication.h>
 #include <kuser.h>
@@ -64,15 +56,11 @@
 #include <kio/ioslave_defaults.h>
 #include <kmimetype.h>
 #include <kde_file.h>
-#include <kremoteencoding.h>
 #include <kconfiggroup.h>
 
-#include "sftp.h"
-#include "atomicio.h"
-#include "sftpfileattr.h"
-#include "ksshprocess.h"
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
 
-
 using namespace KIO;
 extern "C"
 {
@@ -96,2293 +84,1456 @@
   }
 }
 
+// The callback function for libssh
+int auth_callback(const char *prompt, char *buf, size_t len,
+    int echo, int verify, void *userdata) {
+  if (userdata == NULL) {
+    return -1;
+  }
 
-/*
- * This helper handles some special issues (blocking and interrupted
- * system call) when writing to a file handle.
- *
- * @return 0 on success or an error code on failure (ERR_COULD_NOT_WRITE,
- * ERR_DISK_FULL, ERR_CONNECTION_BROKEN).
- */
-static int writeToFile (int fd, const char *buf, size_t len)
-{
-  while (len > 0)
-  {
-    ssize_t written = ::write(fd, buf, len);
-    if (written >= 0)
-    {
-        buf += written;
-        len -= written;
-        continue;
-    }
+  sftpProtocol *slave = (sftpProtocol *) userdata;
 
-    switch(errno)
-    {
-      case EINTR:
-        continue;
-      case EPIPE:
-        return ERR_CONNECTION_BROKEN;
-      case ENOSPC:
-        return ERR_DISK_FULL;
-      default:
-        return ERR_COULD_NOT_WRITE;
-    }
+  if (slave->auth_callback(prompt, buf, len, echo, verify, userdata) < 0) {
+    return -1;
   }
+
   return 0;
 }
 
-sftpProtocol::sftpProtocol(const QByteArray &pool_socket, const QByteArray &app_socket)
-             : SlaveBase("kio_sftp", pool_socket, app_socket),
-                  mConnected(false), mPort(-1), mMsgId(0) {
-#ifndef Q_WS_WIN
-  kDebug(KIO_SFTP_DB) << "pid = " << getpid();
-#endif
-}
+int sftpProtocol::auth_callback(const char *prompt, char *buf, size_t len,
+    int echo, int verify, void *userdata) {
+  QString i_prompt = QString::fromUtf8(prompt);
 
+  // unused variables
+  (void) echo;
+  (void) verify;
+  (void) userdata;
 
-sftpProtocol::~sftpProtocol() {
-#ifndef Q_WS_WIN
-    kDebug(KIO_SFTP_DB) << "pid = " << getpid();
-#endif
-    closeConnection();
-}
+  kDebug(KIO_SFTP_DB) << "Entering authentication callback, prompt=" << i_prompt;
 
-/**
-  * Type is a sftp packet type found in .sftp.h'.
-  * Example: SSH2_FXP_READLINK, SSH2_FXP_RENAME, etc.
-  *
-  * Returns true if the type is supported by the sftp protocol
-  * version negotiated by the client and server (sftpVersion).
-  */
-bool sftpProtocol::isSupportedOperation(int type) {
-  switch (type) {
-    case SSH2_FXP_VERSION:
-    case SSH2_FXP_STATUS:
-    case SSH2_FXP_HANDLE:
-    case SSH2_FXP_DATA:
-    case SSH2_FXP_NAME:
-    case SSH2_FXP_ATTRS:
-    case SSH2_FXP_INIT:
-    case SSH2_FXP_OPEN:
-    case SSH2_FXP_CLOSE:
-    case SSH2_FXP_READ:
-    case SSH2_FXP_WRITE:
-    case SSH2_FXP_LSTAT:
-    case SSH2_FXP_FSTAT:
-    case SSH2_FXP_SETSTAT:
-    case SSH2_FXP_FSETSTAT:
-    case SSH2_FXP_OPENDIR:
-    case SSH2_FXP_READDIR:
-    case SSH2_FXP_REMOVE:
-    case SSH2_FXP_MKDIR:
-    case SSH2_FXP_RMDIR:
-    case SSH2_FXP_REALPATH:
-    case SSH2_FXP_STAT:
-      return true;
-    case SSH2_FXP_RENAME:
-      return sftpVersion >= 2 ? true : false;
-    case SSH2_FXP_EXTENDED:
-    case SSH2_FXP_EXTENDED_REPLY:
-    case SSH2_FXP_READLINK:
-    case SSH2_FXP_SYMLINK:
-      return sftpVersion >= 3 ? true : false;
-    default:
-      kDebug(KIO_SFTP_DB) << "isSupportedOperation(type:"
-                            << type << "): unrecognized operation type";
-      break;
-  }
+  KIO::AuthInfo info;
 
-  return false;
-}
+  info.url.setProtocol("sftp");
+  info.url.setHost(mHost);
+  info.url.setPort(mPort);
+  info.url.setUser(mUsername);
 
-void sftpProtocol::copy(const KUrl &src, const KUrl &dest, int permissions, KIO::JobFlags flags)
-{
-    kDebug(KIO_SFTP_DB) << src << " -> " << dest;
+  info.comment = "sftp://" + mUsername + "@"  + mHost;
+  info.username = QString("UNUSED");
+  info.readOnly = true;
+  info.prompt = i_prompt;
+  info.keepPassword = false;
 
-    bool srcLocal = src.isLocalFile();
-    bool destLocal = dest.isLocalFile();
+  if (!openPasswordDialog(info)) {
+    kDebug(KIO_SFTP_DB) << "Password dialog failed";
+    return -1;
+  }
 
-    if ( srcLocal && !destLocal ) // Copy file -> sftp
-      sftpCopyPut(src, dest, permissions, flags);
-    else if ( destLocal && !srcLocal ) // Copy sftp -> file
-      sftpCopyGet(dest, src, permissions, flags);
-    else
-      error(ERR_UNSUPPORTED_ACTION, QString());
+  strncpy(buf, info.password.toUtf8().constData(), len - 1);
+
+  info.password.fill('x');
+
+  return 0;
 }
 
-void sftpProtocol::sftpCopyGet(const KUrl& dest, const KUrl& src, int mode, KIO::JobFlags flags)
-{
-    kDebug(KIO_SFTP_DB) << src << " -> " << dest;
+int sftpProtocol::authenticateKeyboardInteractive(AuthInfo &info) {
+  QString name, instruction, prompt;
+  int err = SSH_AUTH_ERROR;
 
-    // Attempt to establish a connection...
-    openConnection();
-    if( !mConnected )
-        return;
+  kDebug(KIO_SFTP_DB) << "Entering keyboard interactive function";
 
-    KDE_struct_stat buff_orig;
-    const QString dest_orig = dest.path();
-    bool origExists = (KDE::lstat( dest_orig, &buff_orig ) != -1);
+  err = ssh_userauth_kbdint(ssh_session, mUsername.toUtf8().constData(), NULL);
+  while (err == SSH_AUTH_INFO) {
+    int n = 0;
+    int i = 0;
 
-    if (origExists)
-    {
-        if (S_ISDIR(buff_orig.st_mode))
-        {
-          error(ERR_IS_DIRECTORY, dest.prettyUrl());
-          return;
-        }
+    name = QString::fromUtf8(ssh_userauth_kbdint_getname(ssh_session));
+    instruction = QString::fromUtf8(ssh_userauth_kbdint_getinstruction(ssh_session));
+    n = ssh_userauth_kbdint_getnprompts(ssh_session);
 
-        if (!(flags & KIO::Overwrite))
-        {
-          error(ERR_FILE_ALREADY_EXIST, dest.prettyUrl());
-          return;
-        }
-    }
+    kDebug(KIO_SFTP_DB) << "name=" << name << " instruction=" << instruction
+      << " prompts" << n;
 
-    KIO::filesize_t offset = 0;
-    QString dest_part(dest_orig + ".part");
+    for (i = 0; i < n; ++i) {
+      char echo;
+      const char *answer = "";
 
-    int fd = -1;
-    bool partExists = false;
-    bool markPartial = config()->readEntry("MarkPartial", true);
+      prompt = QString::fromUtf8(ssh_userauth_kbdint_getprompt(ssh_session, i, &echo));
+      kDebug(KIO_SFTP_DB) << "prompt=" << prompt << " echo=" << QString::number(echo);
+      if (echo) {
+        // See RFC4256 Section 3.3 User Interface
+        QString newPrompt;
+        KIO::AuthInfo infoKbdInt;
 
-    if (markPartial)
-    {
-        KDE_struct_stat buff_part;
-        partExists = (KDE::stat(dest_part, &buff_part ) != -1);
+        infoKbdInt.url.setProtocol("sftp");
+        infoKbdInt.url.setHost(mHost);
+        infoKbdInt.url.setPort(mPort);
 
-        if (partExists && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
-        {
-            if (canResume( buff_part.st_size ))
-            {
-                offset = buff_part.st_size;
-                kDebug(KIO_SFTP_DB) << "Resuming @ " << offset;
-            }
+        infoKbdInt.caption = i18n("SFTP Login");
+        infoKbdInt.comment = "sftp://" + mUsername + "@"  + mHost;
+
+        if (!name.isEmpty()) {
+          infoKbdInt.caption = QString(i18n("SFTP Login") + " - " + name);
         }
 
-        if (offset > 0)
-        {
-            fd = KDE::open(dest_part, O_RDWR);
-            offset = KDE_lseek(fd, 0, SEEK_END);
-            if (offset == 0)
-            {
-              error(ERR_CANNOT_RESUME, dest.prettyUrl());
-              return;
-            }
+        if (!instruction.isEmpty()) {
+          newPrompt = instruction + "\n\n";
         }
-        else
-        {
-            // Set up permissions properly, based on what is done in file io-slave
-            int openFlags = (O_CREAT | O_TRUNC | O_WRONLY);
-            int initialMode = (mode == -1) ? 0666 : (mode | S_IWUSR);
-            fd = KDE::open(dest_part, openFlags, initialMode);
-        }
-    }
-    else
-    {
-        // Set up permissions properly, based on what is done in file io-slave
-        int openFlags = (O_CREAT | O_TRUNC | O_WRONLY);
-        int initialMode = (mode == -1) ? 0666 : (mode | S_IWUSR);
-        fd = KDE::open(dest_orig, openFlags, initialMode);
-    }
 
-    if(fd == -1)
-    {
-      kDebug(KIO_SFTP_DB) << "Unable to open (" << fd << ") for writing.";
-      if (errno == EACCES)
-        error (ERR_WRITE_ACCESS_DENIED, dest.prettyUrl());
-      else
-        error (ERR_CANNOT_OPEN_FOR_WRITING, dest.prettyUrl());
-      return;
-    }
+        newPrompt.append(prompt + "\n\n");
+        infoKbdInt.readOnly = false;
+        infoKbdInt.keepPassword = false;
 
-    Status info = sftpGet(src, offset, fd);
-    if ( info.code != 0 )
-    {
-      // Should we keep the partially downloaded file ??
-      KIO::filesize_t size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
-      if (info.size < size)
-        QFile::remove(dest_part);
+        if (openPasswordDialog(infoKbdInt, i18n("Use the username input field to answer this question!"))) {
+          kDebug(KIO_SFTP_DB) << "Got the answer from the password dialog";
+          answer = info.username.toUtf8().constData();
+        }
 
-      error(info.code, info.text);
-      return;
-    }
+        if (ssh_userauth_kbdint_setanswer(ssh_session, i, answer) < 0) {
+          kDebug(KIO_SFTP_DB) << "An error occured setting the answer: "
+            << ssh_get_error(ssh_session);
+          return SSH_AUTH_ERROR;
+        }
+        break;
+      } else {
+        if (prompt.contains("Password", Qt::CaseInsensitive)) {
+          answer = mPassword.toUtf8().constData();
+        } else {
+          info.readOnly = true; // set username readonly
+          info.prompt = prompt;
+          info.keepPassword = false;
 
-    if (::close(fd) != 0)
-    {
-      error(ERR_COULD_NOT_WRITE, dest.prettyUrl());
-      return;
-    }
+          if (openPasswordDialog(info)) {
+            kDebug(KIO_SFTP_DB) << "Got the answer from the password dialog";
+            answer = info.password.toUtf8().constData();
+          }
+        }
 
-    //
-    if (markPartial)
-    {
-      if (KDE::rename(dest_part, dest_orig) != 0)
-      {
-        error (ERR_CANNOT_RENAME_PARTIAL, dest_part);
-        return;
+        if (ssh_userauth_kbdint_setanswer(ssh_session, i, answer) < 0) {
+          kDebug(KIO_SFTP_DB) << "An error occured setting the answer: "
+            << ssh_get_error(ssh_session);
+          return SSH_AUTH_ERROR;
+        }
       }
     }
+    err = ssh_userauth_kbdint(ssh_session, mUsername.toUtf8().constData(), NULL);
+  }
 
-    data(QByteArray());
-    kDebug(KIO_SFTP_DB) << "emit finished()";
-    finished();
+  return err;
 }
 
-sftpProtocol::Status sftpProtocol::sftpGet( const KUrl& src, KIO::filesize_t offset, int fd, bool abortAfterMimeType )
-{
-    int code;
-    sftpFileAttr attr(remoteEncoding());
+void sftpProtocol::reportError(const KUrl &url, const int err) {
+  kDebug(KIO_SFTP_DB) << "url = " << url << " - err=" << err;
 
-    Status res;
-    res.code = 0;
-    res.size = 0;
+  switch (err) {
+    case SSH_FX_OK:
+      break;
+    case SSH_FX_NO_SUCH_FILE:
+    case SSH_FX_NO_SUCH_PATH:
+      error(ERR_DOES_NOT_EXIST, url.prettyUrl());
+      break;
+    case SSH_FX_PERMISSION_DENIED:
+      error(ERR_ACCESS_DENIED, url.prettyUrl());
+      break;
+    case SSH_FX_FILE_ALREADY_EXISTS:
+      error(ERR_FILE_ALREADY_EXIST, url.prettyUrl());
+      break;
+    case SSH_FX_INVALID_HANDLE:
+      error(ERR_MALFORMED_URL, url.prettyUrl());
+      break;
+    case SSH_FX_OP_UNSUPPORTED:
+      error(ERR_UNSUPPORTED_ACTION, url.prettyUrl());
+      break;
+    case SSH_FX_BAD_MESSAGE:
+      error(ERR_UNKNOWN, url.prettyUrl());
+      break;
+    default:
+      error(ERR_INTERNAL, url.prettyUrl());
+      break;
+  }
+}
 
-    kDebug(KIO_SFTP_DB) << src;
+bool sftpProtocol::createUDSEntry(const QString &filename, const QByteArray &path,
+      UDSEntry &entry, short int details) {
+  mode_t type;
+  mode_t access;
+  char *link;
 
-    // stat the file first to get its size
-    if( (code = sftpStat(src, attr)) != SSH2_FX_OK ) {
-        return doProcessStatus(code, src.prettyUrl());
-    }
+  Q_ASSERT(entry.count() == 0);
 
-    // We cannot get file if it is a directory
-    if( attr.fileType() == S_IFDIR ) {
-        res.text = src.prettyUrl();
-        res.code = ERR_IS_DIRECTORY;
-        return res;
-    }
+  SFTP_ATTRIBUTES *sb = sftp_lstat(sftp_session, path.constData());
+  if (sb == NULL) {
+    return false;
+  }
 
-    KIO::filesize_t fileSize = attr.fileSize();
-    quint32 pflags = SSH2_FXF_READ;
-    attr.clear();
+  entry.insert(KIO::UDSEntry::UDS_NAME, filename);
 
-    QByteArray handle;
-    if( (code = sftpOpen(src, pflags, attr, handle)) != SSH2_FX_OK ) {
-        res.text = src.prettyUrl();
-        res.code = ERR_CANNOT_OPEN_FOR_READING;
-        return res;
+  if (sb->type == SSH_FILEXFER_TYPE_SYMLINK) {
+    entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
+    link = sftp_readlink(sftp_session, path.constData());
+    if (link == NULL) {
+      sftp_attributes_free(sb);
+      return false;
     }
+    entry.insert(KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName(link));
+    delete link;
+    // A symlink -> follow it only if details > 1
+    if (details > 1) {
+      SFTP_ATTRIBUTES *sb2 = sftp_stat(sftp_session, path.constData());
+      if (sb2 == NULL) {
+        // It is a link pointing to nowhere
+        type = S_IFMT - 1;
+        access = S_IRWXU | S_IRWXG | S_IRWXO;
+        entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type);
+        entry.insert( KIO::UDSEntry::UDS_ACCESS, access);
+        entry.insert( KIO::UDSEntry::UDS_SIZE, 0LL );
 
-    // needed for determining mimetype
-    // note: have to emit mimetype before emitting totalsize.
-    QByteArray buff;
-    QByteArray mimeBuffer;
+        goto notype;
+      }
+      sftp_attributes_free(sb);
+      sb = sb2;
+    }
+  }
 
-    unsigned int oldSize;
-    bool foundMimetype = false;
+  switch (sb->type) {
+    case SSH_FILEXFER_TYPE_REGULAR:
+      entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
+      break;
+    case SSH_FILEXFER_TYPE_DIRECTORY:
+      entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
+      break;
+    case SSH_FILEXFER_TYPE_SYMLINK:
+      entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFLNK);
+      break;
+    case SSH_FILEXFER_TYPE_SPECIAL:
+    case SSH_FILEXFER_TYPE_UNKNOWN:
+      entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFMT - 1);
+      break;
+  }
 
-    // How big should each data packet be? Definitely not bigger than 64kb or
-    // you will overflow the 2 byte size variable in a sftp packet.
-    quint32 len = 60*1024;
-    code = SSH2_FX_OK;
+  access = sb->permissions & 07777;
+  entry.insert(KIO::UDSEntry::UDS_ACCESS, access);
 
-    kDebug(KIO_SFTP_DB) << "offset = " << offset;
-    while( code == SSH2_FX_OK ) {
-        if( (code = sftpRead(handle, offset, len, buff)) == SSH2_FX_OK ) {
-            offset += buff.size();
+  entry.insert(KIO::UDSEntry::UDS_SIZE, sb->size);
 
-            // save data for mimetype. Pretty much follows what is in the ftp ioslave
-            if( !foundMimetype ) {
-                oldSize = mimeBuffer.size();
-                mimeBuffer.resize(oldSize + buff.size());
-                memcpy(mimeBuffer.data()+oldSize, buff.data(), buff.size());
-
-                if( mimeBuffer.size() > 1024 ||  offset == fileSize ) {
-                    // determine mimetype
-                    KMimeType::Ptr mime = KMimeType::findByNameAndContent(src.fileName(), mimeBuffer);
-                    kDebug(KIO_SFTP_DB) << "mimetype is " << mime->name();
-                    mimeType(mime->name());
-
-                    if (abortAfterMimeType)
-                        break;
-
-                    // Always send the total size after emitting mime-type...
-                    totalSize(fileSize);
-
-                    if (fd == -1)
-                        data(mimeBuffer);
-                    else
-                    {
-                        if ( (res.code=writeToFile(fd, mimeBuffer.data(), mimeBuffer.size())) != 0 )
-                            return res;
-                    }
-
-                    processedSize(mimeBuffer.size());
-                    mimeBuffer.resize(0);
-                    foundMimetype = true;
-                }
-            }
-            else {
-                if (fd == -1)
-                    data(buff);
-                else
-                {
-                    if ( (res.code= writeToFile(fd, buff.data(), buff.size())) != 0 )
-                        return res;
-                }
-                processedSize(offset);
-            }
-        }
-
-        /*
-          Check if slave was killed.  According to slavebase.h we need to leave
-          the slave methods as soon as possible if the slave is killed. This
-          allows the slave to be cleaned up properly.
-        */
-        if( wasKilled() ) {
-            res.text = i18n("An internal error occurred. Please retry the request again.");
-            res.code = ERR_UNKNOWN;
-            return res;
-        }
+notype:
+  if (details > 0) {
+    if (sb->owner) {
+      entry.insert(KIO::UDSEntry::UDS_USER, sb->owner);
+    } else {
+      entry.insert(KIO::UDSEntry::UDS_USER, mUsername);
     }
 
-    if( code != SSH2_FX_EOF && !abortAfterMimeType ) {
-        res.text = src.prettyUrl();
-        res.code = ERR_COULD_NOT_READ; // return here or still send empty array to indicate end of read?
+    if (sb->group) {
+      entry.insert(KIO::UDSEntry::UDS_GROUP, sb->group);
+    } else {
+      entry.insert(KIO::UDSEntry::UDS_GROUP, "users");
     }
 
-    res.size = offset;
-    sftpClose(handle);
-    processedSize (offset);
-    return res;
-}
+    entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, sb->atime);
+    entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, sb->mtime);
+    entry.insert(KIO::UDSEntry::UDS_CREATION_TIME, sb->createtime);
+  }
 
-void sftpProtocol::get(const KUrl& url) {
-    kDebug(KIO_SFTP_DB) << "get(): " << url;
+  sftp_attributes_free(sb);
 
-    openConnection();
-    if( !mConnected )
-        return;
-
-    // Get resume offset
-    quint64 offset = config()->readEntry("resume",0);
-    if( offset > 0 ) {
-        canResume();
-        kDebug(KIO_SFTP_DB) << "get(): canResume(), offset = " << offset;
-    }
-
-    Status info = sftpGet(url, offset);
-
-    if (info.code != 0)
-    {
-      error(info.code, info.text);
-      return;
-    }
-
-    data(QByteArray());
-    kDebug(KIO_SFTP_DB) << "get(): emit finished()";
-    finished();
+  return true;
 }
 
+QString sftpProtocol::canonicalizePath(const QString &path) {
+  kDebug(KIO_SFTP_DB) << "Path to canonicalize: " << path;
+  QString cPath;
+  char *sPath = NULL;
 
-void sftpProtocol::setHost (const QString& h, quint16 port, const QString& user, const QString& pass)
-{
-    kDebug(KIO_SFTP_DB) << "setHost(): " << user << "@" << h << ":" << port;
+  if (path.isEmpty()) {
+    return cPath;
+  }
 
-    if( mHost != h || mPort != port || user != mUsername || mPassword != pass )
-        closeConnection();
+  sPath = sftp_canonicalize_path(sftp_session, path.toUtf8().constData());
+  if (sPath == NULL) {
+    kDebug(KIO_SFTP_DB) << "Could not canonicalize path: " << path;
+    return cPath;
+  }
 
-    mHost = h;
+  cPath = QFile::decodeName(sPath);
+  delete sPath;
 
-    if( port > 0 )
-        mPort = port;
-    else {
-        struct servent *pse;
-        if( (pse = getservbyname("ssh", "tcp") ) == NULL )
-            mPort = 22;
-        else
-            mPort = ntohs(pse->s_port);
-    }
+  kDebug(KIO_SFTP_DB) << "Canonicalized path: " << cPath;
 
-    mUsername = user;
-    mPassword = pass;
+  return cPath;
+}
 
-    if (user.isEmpty())
-    {
-      KUser u;
-      mUsername = u.loginName();
-    }
+sftpProtocol::sftpProtocol(const QByteArray &pool_socket, const QByteArray &app_socket)
+             : SlaveBase("kio_sftp", pool_socket, app_socket),
+                  mConnected(false), mPort(-1), ssh_session(NULL), sftp_session(NULL) {
+#ifndef Q_WS_WIN
+  kDebug(KIO_SFTP_DB) << "pid = " << getpid();
+#endif
 }
 
+sftpProtocol::~sftpProtocol() {
+#ifndef Q_WS_WIN
+  kDebug(KIO_SFTP_DB) << "pid = " << getpid();
+#endif
+  closeConnection();
 
-void sftpProtocol::openConnection() {
+  /* cleanup and shut down cryto stuff */
+  ssh_finalize();
+}
 
-    if(mConnected)
-      return;
+void sftpProtocol::setHost(const QString& h, quint16 port, const QString& user, const QString& pass) {
+  kDebug(KIO_SFTP_DB) << "setHost(): " << user << "@" << h << ":" << port;
 
-    kDebug(KIO_SFTP_DB) << "username=" << mUsername << ", host=" << mHost << ", port=" << mPort;
+  if (mConnected) {
+    closeConnection();
+  }
 
-    infoMessage( i18n("Opening SFTP connection to host %1:%2", mHost, mPort));
+  mHost = h;
 
-    if( mHost.isEmpty() ) {
-        kDebug(KIO_SFTP_DB) << "openConnection(): Need hostname...";
-        error(ERR_UNKNOWN_HOST, i18n("No hostname specified"));
-        return;
+  if (port > 0) {
+    mPort = port;
+  } else {
+    struct servent *pse;
+    if ((pse = getservbyname("ssh", "tcp") ) == NULL) {
+      mPort = 22;
+    } else {
+      mPort = ntohs(pse->s_port);
     }
+  }
 
-    // Setup AuthInfo for use with password caching and the
-    // password dialog box.
-    AuthInfo info;
-    info.url.setProtocol("sftp");
-    info.url.setHost(mHost);
-    info.url.setPort(mPort);
-    info.url.setUser(mUsername);
-    info.caption = i18n("SFTP Login");
-    info.comment = "sftp://" + mHost + ':' + QString::number(mPort);
-    info.commentLabel = i18n("site:");
-    info.username = mUsername;
-    info.keepPassword = true;
+  kDebug(KIO_SFTP_DB) << "setHost(): mPort=" << mPort;
 
-    // Check for cached authentication info if no password is specified...
-    if( mPassword.isEmpty() ) {
-        kDebug(KIO_SFTP_DB) << "checking cache: info.username = " << info.username
-                            << ", info.url = " << info.url.prettyUrl();
+  mUsername = user;
+  mPassword = pass;
 
-        if( checkCachedAuthentication(info) ) {
-            mUsername = info.username;
-            mPassword = info.password;
-        }
-    }
+  if (user.isEmpty()) {
+    KUser u;
+    mUsername = u.loginName();
+  }
+}
 
-    ///////////////////////////////////////////////////////////////////////////
-    // Now setup our ssh options. If we found a cached username
-    // and password we set the SSH_PASSWORD and SSH_USERNAME
-    // options right away.  Otherwise we wait. The other options are
-    // necessary for running sftp over ssh.
-    KSshProcess::SshOpt opt;   // a ssh option, this can be reused
-    KSshProcess::SshOptList opts; // list of SshOpts
-    KSshProcess::SshOptListIterator passwdIt; // points to the opt in opts that specifies the password
-    KSshProcess::SshOptListIterator usernameIt;
+void sftpProtocol::openConnection() {
 
-//    opt.opt = KSshProcess::SSH_VERBOSE;
-//    opts.append(opt);
-//    opts.append(opt);
+  if (mConnected) {
+    return;
+  }
 
-    // setHost will set a port in any case
-    Q_ASSERT( mPort != -1 );
+  kDebug(KIO_SFTP_DB) << "username=" << mUsername << ", host=" << mHost << ", port=" << mPort;
 
-    opt.opt = KSshProcess::SSH_PORT;
-    opt.num = mPort;
-    opts.append(opt);
+  infoMessage(i18n("Opening SFTP connection to host %1:%2", mHost, mPort));
 
-    opt.opt = KSshProcess::SSH_SUBSYSTEM;
-    opt.str = "sftp";
-    opts.append(opt);
+  if (mHost.isEmpty()) {
+    kDebug(KIO_SFTP_DB) << "openConnection(): Need hostname...";
+    error(ERR_UNKNOWN_HOST, i18n("No hostname specified"));
+    return;
+  }
 
-    opt.opt = KSshProcess::SSH_FORWARDX11;
-    opt.boolean = false;
-    opts.append(opt);
+  // Setup AuthInfo for use with password caching and the
+  // password dialog box.
+  AuthInfo info;
 
-    opt.opt = KSshProcess::SSH_FORWARDAGENT;
-    opt.boolean = false;
-    opts.append(opt);
+  info.url.setProtocol("sftp");
+  info.url.setHost(mHost);
+  info.url.setPort(mPort);
+  info.url.setUser(mUsername);
+  info.caption = i18n("SFTP Login");
+  info.comment = "sftp://" + mHost + ':' + QString::number(mPort);
+  info.commentLabel = i18n("site:");
+  info.username = mUsername;
+  info.keepPassword = true;
 
-    opt.opt = KSshProcess::SSH_PROTOCOL;
-    opt.num = 2;
-    opts.append(opt);
+  // Check for cached authentication info if no password is specified...
+  if (mPassword.isEmpty()) {
+    kDebug(KIO_SFTP_DB) << "checking cache: info.username = " << info.username
+      << ", info.url = " << info.url.prettyUrl();
 
-    opt.opt = KSshProcess::SSH_HOST;
-    opt.str = mHost;
-    opts.append(opt);
-
-    opt.opt = KSshProcess::SSH_ESCAPE_CHAR;
-    opt.num = -1; // don't use any escape character
-    opts.append(opt);
-
-    // set the username and password if we have them
-    if( !mUsername.isEmpty()  ) {
-        opt.opt = KSshProcess::SSH_USERNAME;
-        opt.str = mUsername;
-        opts.append(opt);
-        usernameIt = opts.end()-1;
+    if (checkCachedAuthentication(info)) {
+      mUsername = info.username;
+      mPassword = info.password;
     }
+  }
 
-    if( !mPassword.isEmpty() ) {
-        opt.opt = KSshProcess::SSH_PASSWD;
-        opt.str = mPassword;
-        opts.append(opt);
-        passwdIt = opts.end()-1;
-    }
+  // Create the libssh options
+  SSH_OPTIONS *opt = ssh_options_new();
+  if (opt == NULL) {
+    error(ERR_OUT_OF_MEMORY, QString());
+    return;
+  }
 
-    ssh.setOptions(opts);
-    ssh.printArgs();
+  kDebug(KIO_SFTP_DB) << "Creating the SSH options";
 
-    ///////////////////////////////////////////////////////////////////////////
-    // Start the ssh connection process.
-    //
+  // Allow SSH1 only
+  ssh_options_allow_ssh1(opt, 0);
+  ssh_options_allow_ssh2(opt, 1);
 
-    int err;           // error code from KSshProcess
-    QString msg;       // msg for dialog box
-    QString caption;   // dialog box caption
-    bool firstTime = true;
-    bool dlgResult;
+  ssh_options_set_timeout(opt, 10, 0);
 
-    while( !(mConnected = ssh.connect()) ) {
-        err = ssh.error();
-        kDebug(KIO_SFTP_DB) << "Got " << err << " from KSshProcess::connect()";
+  // Don't use any compression
+  ssh_options_set_wanted_algos(opt, SSH_COMP_C_S, "none");
+  ssh_options_set_wanted_algos(opt, SSH_COMP_S_C, "none");
 
-        switch(err) {
-        case KSshProcess::ERR_NEED_PASSWD:
-        case KSshProcess::ERR_NEED_PASSPHRASE:
-            // At this point we know that either we didn't set
-            // an username or password in the ssh options list,
-            // or what we did pass did not work. Therefore we
-            // must prompt the user.
-            if( err == KSshProcess::ERR_NEED_PASSPHRASE )
-                info.prompt = i18n("Please enter your username and key passphrase.");
-            else
-                info.prompt = i18n("Please enter your username and password.");
+  // Set host and port
+  ssh_options_set_host(opt, mHost.toUtf8().constData());
+  if (mPort > 0) {
+    ssh_options_set_port(opt, mPort);
+  }
 
-            kDebug(KIO_SFTP_DB) << "info.username = " << info.username
-                                 << ", info.url = " << info.url.prettyUrl();
+  // Set the username
+  if (!mUsername.isEmpty()) {
+    ssh_options_set_username(opt, mUsername.toUtf8().constData());
+  }
 
-            if( firstTime )
-                dlgResult = openPasswordDialog(info);
-            else
-                dlgResult = openPasswordDialog(info, i18n("Incorrect username or password"));
+  ssh_options_set_auth_callback(opt, ::auth_callback, this);
 
-            // handle user canceled or dialog failed to open...
-            if( !dlgResult ) {
-                error(ERR_USER_CANCELED, QString());
-                kDebug(KIO_SFTP_DB) << "openConnection(): user canceled, dlgResult = " << dlgResult;
-                closeConnection();
-                return;
-            }
+  // Start the ssh connection.
+  unsigned char *hash = NULL; // the server hash
+  char *hexa;
+  QString msg;     // msg for dialog box
+  QString caption; // dialog box caption
+  int rc, state, hlen;
 
-            firstTime = false;
+  ssh_session = ssh_new();
+  if (ssh_session == NULL) {
+    ssh_options_free(opt);
+    error(ERR_OUT_OF_MEMORY, QString());
+    return;
+  }
 
-            // Check if the username has changed. SSH only accepts
-            // the username at startup. If the username has changed
-            // we must disconnect ssh, change the SSH_USERNAME
-            // option, and reset the option list. We will also set
-            // the password option so the user is not prompted for
-            // it again.
-            if( mUsername != info.username ) {
-                kDebug(KIO_SFTP_DB) << "Username changed from " << mUsername 
-                                    << " to " << info.username;
+  kDebug(KIO_SFTP_DB) << "Creating the SSH session";
 
-                ssh.disconnect();
+  ssh_set_options(ssh_session, opt);
 
-                // if we haven't yet added the username
-                // or password option to the ssh options list then
-                // the iterators will be equal to the empty iterator.
-                // Create the opts now and add them to the opt list.
-                if( usernameIt == KSshProcess::SshOptListIterator() ) {
-                    kDebug(KIO_SFTP_DB) << "Adding username to options list";
-                    opt.opt = KSshProcess::SSH_USERNAME;
-                    opts.append(opt);
-                    usernameIt = opts.end()-1;
-                }
+  kDebug(KIO_SFTP_DB) << "Trying to connect to the SSH server";
 
-                if( passwdIt == KSshProcess::SshOptListIterator() ) {
-                    kDebug(KIO_SFTP_DB) << "Adding password to options list";
-                    opt.opt = KSshProcess::SSH_PASSWD;
-                    opts.append(opt);
-                    passwdIt = opts.end()-1;
-                }
+  /* try to connect */
+  rc = ssh_connect(ssh_session);
+  if (rc < 0) {
+    error(ERR_COULD_NOT_CONNECT, QString(ssh_get_error(ssh_session)));
+    closeConnection();
+    return;
+  }
 
-                (*usernameIt).str = info.username;
-                (*passwdIt).str = info.password;
-                ssh.setOptions(opts);
-                ssh.printArgs();
-            }
-            else { // just set the password
-                ssh.setPassword(info.password);
-            }
+  kDebug(KIO_SFTP_DB) << "Getting the SSH server hash";
 
-            mUsername = info.username;
-            mPassword = info.password;
+  /* get the hash */
+  hlen = ssh_get_pubkey_hash(ssh_session, &hash);
+  if (hlen < 0) {
+    error(ERR_COULD_NOT_CONNECT, QString(ssh_get_error(ssh_session)));
+    closeConnection();
+    return;
+  }
 
-            break;
+  kDebug(KIO_SFTP_DB) << "Checking if the SSH server is known";
 
-        case KSshProcess::ERR_NEW_HOST_KEY:
-            caption = i18n("Warning: Cannot verify host's identity.");
-            msg = ssh.errorMsg();
-            if( KMessageBox::Yes != messageBox(WarningYesNo, msg, caption) ) {
-                closeConnection();
-                error(ERR_USER_CANCELED, QString());
-                return;
-            }
-            ssh.acceptHostKey(true);
-            break;
+  /* check the server public key hash */
+  state = ssh_is_server_known(ssh_session);
+  switch (state) {
+    case SSH_SERVER_KNOWN_OK:
+      break;
+    case SSH_SERVER_FOUND_OTHER:
+      delete hash;
+      error(ERR_CONNECTION_BROKEN, i18n("The host key for this server was "
+            "not found but an other type of key exists.\n"
+            "An attacker might change the default server key to confuse your "
+            "client into thinking the key does not exist\n"
+            "Please contact your system administrator.\n%1", ssh_get_error(ssh_session)));
+      closeConnection();
+      return;
+    case SSH_SERVER_KNOWN_CHANGED:
+      hexa = ssh_get_hexa(hash, hlen);
+      delete hash;
+      /* TODO print known_hosts file, port? */
+      error(ERR_CONNECTION_BROKEN, i18n("The host key for the server %1 has changed.\n"
+          "This could either mean that DNS SPOOFING is happening or the IP "
+          "address for the host and its host key have changed at the same time.\n"
+          "The fingerprint for the key sent by the remote host is:\n %2\n"
+          "Please contact your system administrator.\n%3", mHost, hexa, ssh_get_error(ssh_session)));
+      delete hexa;
+      closeConnection();
+      return;
+    case SSH_SERVER_FILE_NOT_FOUND:
+    case SSH_SERVER_NOT_KNOWN:
+      hexa = ssh_get_hexa(hash, hlen);
+      delete hash;
+      caption = i18n("Warning: Cannot verify host's identity.");
+      msg = i18n("The authenticity of host %1 can't be established.\n"
+        "The key fingerprint is: %2\n"
+        "Are you sure you want to continue connecting?", mHost, hexa);
+      delete hexa;
 
-        case KSshProcess::ERR_DIFF_HOST_KEY:
-            caption = i18n("Warning: Host's identity changed.");
-            msg = ssh.errorMsg();
-            if( KMessageBox::Yes != messageBox(WarningYesNo, msg, caption) ) {
-                closeConnection();
-                error(ERR_USER_CANCELED, QString());
-                return;
-            }
-            ssh.acceptHostKey(true);
-            break;
-
-        case KSshProcess::ERR_AUTH_FAILED:
-            infoMessage(i18n("Authentication failed."));
-            error(ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
-            return;
-
-        case KSshProcess::ERR_AUTH_FAILED_NEW_KEY:
-            msg = ssh.errorMsg();
-            error(ERR_COULD_NOT_LOGIN, msg);
-            return;
-
-        case KSshProcess::ERR_AUTH_FAILED_DIFF_KEY:
-            msg = ssh.errorMsg();
-            error(ERR_COULD_NOT_LOGIN, msg);
-            return;
-
-        case KSshProcess::ERR_CLOSED_BY_REMOTE_HOST:
-            infoMessage(i18n("Connection failed."));
-            caption = i18n("Connection closed by remote host.");
-            msg = ssh.errorMsg();
-            if (!msg.isEmpty()) {
-                caption += '\n';
-                caption += msg;
-            }
-            closeConnection();
-            error(ERR_COULD_NOT_LOGIN, caption);
-            return;
-
-        case KSshProcess::ERR_INTERACT:
-        case KSshProcess::ERR_INTERNAL:
-        case KSshProcess::ERR_UNKNOWN:
-        case KSshProcess::ERR_INVALID_STATE:
-        case KSshProcess::ERR_CANNOT_LAUNCH:
-        case KSshProcess::ERR_HOST_KEY_REJECTED:
-        default:
-            infoMessage(i18n("Connection failed."));
-            // Don't call messageBox! Leave GUI handling to the apps (#108812)
-            caption = i18n("unexpected SFTP error: %1", err);
-            msg = ssh.errorMsg();
-            if (!msg.isEmpty()) {
-                caption += '\n';
-                caption += msg;
-            }
-            closeConnection();
-            error(ERR_UNKNOWN, caption);
-            return;
-        }
-    }
-
-    // catch all in case we did something wrong above
-    if( !mConnected ) {
-        error(ERR_INTERNAL, QString());
+      if (KMessageBox::Yes != messageBox(WarningYesNo, msg, caption)) {
+        closeConnection();
+        error(ERR_USER_CANCELED, QString());
         return;
-    }
+      }
 
-    // Now send init packet.
-    kDebug(KIO_SFTP_DB) << "openConnection(): Sending SSH2_FXP_INIT packet.";
-    QByteArray p;
-    QDataStream packet(&p, QIODevice::WriteOnly);
-    packet << (quint32)5;                     // packet length
-    packet << (quint8) SSH2_FXP_INIT;         // packet type
-    packet << (quint32)SSH2_FILEXFER_VERSION; // client version
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream s(p);
-    quint32 version;
-    quint8  type;
-    s >> type;
-    kDebug(KIO_SFTP_DB) << "openConnection(): Got type " << type;
-
-    if( type == SSH2_FXP_VERSION ) {
-        s >> version;
-        kDebug(KIO_SFTP_DB) << "openConnection(): Got server version " << version;
-
-        // XXX Get extensions here
-        sftpVersion = version;
-
-        /* Server should return lowest common version supported by
-         * client and server, but double check just in case.
-         */
-        if( sftpVersion > SSH2_FILEXFER_VERSION ) {
-            error(ERR_UNSUPPORTED_PROTOCOL,
-                i18n("SFTP version %1", version));
-            closeConnection();
-            return;
-        }
-    }
-    else {
-        error(ERR_UNKNOWN, i18n("Protocol error."));
+      /* write the known_hosts file */
+      kDebug(KIO_SFTP_DB) << "Adding server to known_hosts file.";
+      if (ssh_write_knownhost(ssh_session) < 0) {
+        error(ERR_USER_CANCELED, QString(ssh_get_error(ssh_session)));
         closeConnection();
         return;
-    }
+      }
+      break;
+    case SSH_SERVER_ERROR:
+      delete hash;
+      error(ERR_COULD_NOT_CONNECT, QString(ssh_get_error(ssh_session)));
+      return;
+  }
 
-    // Login succeeded!
-    infoMessage(i18n("Successfully connected to %1", mHost));
-    info.url.setProtocol("sftp");
-    info.url.setHost(mHost);
-    info.url.setPort(mPort);
-    info.url.setUser(mUsername);
-    info.username = mUsername;
-    info.password = mPassword;
-    kDebug(KIO_SFTP_DB) << "Caching info.username = " << info.username 
-                        << ", info.url = " << info.url.prettyUrl();
-    cacheAuthentication(info);
-    mConnected = true;
-    connected();
+  kDebug(KIO_SFTP_DB) << "Trying to authenticate with the server";
 
-    mPassword.fill('x');
-    info.password.fill('x');
-
+  // Try to authenticate
+  rc = ssh_userauth_none(ssh_session, NULL);
+  if (rc == SSH_AUTH_ERROR) {
+    closeConnection();
+    error(ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
     return;
-}
+  }
 
-#define _DEBUG kDebug(KIO_SFTP_DB)
+  int method = ssh_auth_list(ssh_session);
+  bool firstTime = true;
+  bool dlgResult;
+  while (rc != SSH_AUTH_SUCCESS) {
 
-void sftpProtocol::open(const KUrl &url, QIODevice::OpenMode mode)
-{
-    _DEBUG << url;
-    openConnection();
-    if (!mConnected) {
-        error(KIO::ERR_CONNECTION_BROKEN, url.prettyUrl());
+    // Try to authenticate with public key first
+    kDebug(KIO_SFTP_DB) << "Trying to authenticate public key";
+    if (method & SSH_AUTH_METHOD_PUBLICKEY) {
+      rc = ssh_userauth_autopubkey(ssh_session, NULL);
+      if (rc == SSH_AUTH_ERROR) {
+        closeConnection();
+        error(ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
         return;
+      } else if (rc == SSH_AUTH_SUCCESS) {
+        break;
+      }
     }
 
-    int code;
-    sftpFileAttr attr(remoteEncoding());
-
-    if ((code = sftpStat(url, attr)) != SSH2_FX_OK) {
-        _DEBUG << "stat error";
-        processStatus(code, url.prettyUrl());
-        return;
+    info.caption = i18n("SFTP Login");
+    info.prompt = i18n("Please enter your username and password.");
+    info.readOnly = false;
+    if (firstTime) {
+      dlgResult = openPasswordDialog(info);
+    } else {
+      dlgResult = openPasswordDialog(info, i18n("Incorrect username or password"));
     }
 
-    // don't open a directory
-    if (attr.fileType() == S_IFDIR) {
-        _DEBUG << "a directory";
-        error(KIO::ERR_IS_DIRECTORY, url.prettyUrl());
-        return;
+    // Handle user canceled or dialog failed to open...
+    if (!dlgResult) {
+      kDebug(KIO_SFTP_DB) << "User canceled, dlgResult = " << dlgResult;
+      closeConnection();
+      error(ERR_USER_CANCELED, QString());
+      return;
     }
-    if (attr.fileType() != S_IFREG) {
-        _DEBUG << "not a regular file";
-        error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyUrl());
-        return;
-    }
 
-    KIO::filesize_t fileSize = attr.fileSize();
-    attr.clear();
+    firstTime = false;
 
-    quint32 pflags = 0;
-
-    if (mode & QIODevice::ReadOnly) {
-        if (mode & QIODevice::WriteOnly) {
-            pflags = SSH2_FXF_READ | SSH2_FXF_WRITE | SSH2_FXF_CREAT;
-        } else {
-            pflags = SSH2_FXF_READ;
-        }
-    } else if (mode & QIODevice::WriteOnly) {
-        pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT;
+    if (mUsername != info.username) {
+      kDebug(KIO_SFTP_DB) << "Username changed from " << mUsername
+                                    << " to " << info.username;
     }
+    mUsername = info.username;
+    mPassword = info.password;
 
-    if (mode & QIODevice::Append) {
-        pflags |= SSH2_FXF_APPEND;
-    } else if (mode & QIODevice::Truncate) {
-        pflags |= SSH2_FXF_TRUNC;
+    // Try to authenticate with keyboard interactive
+    kDebug(KIO_SFTP_DB) << "Trying to authenticate with keyboard interactive";
+    if (method & SSH_AUTH_METHOD_INTERACTIVE) {
+      rc = authenticateKeyboardInteractive(info);
+      if (rc == SSH_AUTH_ERROR) {
+        closeConnection();
+        error(ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
+        return;
+      } else if (rc == SSH_AUTH_SUCCESS) {
+        break;
+      }
     }
 
-    code = sftpOpen(url, pflags, attr, openHandle);
-    if (code != SSH2_FX_OK) {
-        _DEBUG << "Got error code " << code;
-        processStatus(code, url.prettyUrl());
+    // Try to authenticate with password
+    kDebug(KIO_SFTP_DB) << "Trying to authenticate with password";
+    if (method & SSH_AUTH_METHOD_PASSWORD) {
+      rc = ssh_userauth_password(ssh_session, mUsername.toUtf8().constData(),
+          mPassword.toUtf8().constData());
+      if (rc == SSH_AUTH_ERROR) {
+        closeConnection();
+        error(ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
         return;
+      } else if (rc == SSH_AUTH_SUCCESS) {
+        break;
+      }
     }
+  }
 
-    // Determine the mimetype of the file to be retrieved, and emit it.
-    // This is mandatory in all slaves (for KRun/BrowserRun to work).
-    // If we're not opening the file ReadOnly or ReadWrite, don't attempt to
-    // read the file and send the mimetype.
-    if (mode & QIODevice::ReadOnly){
-        QByteArray buffer;
-        code = sftpRead(openHandle, 0, 1024, buffer);
-        if ((code != SSH2_FX_OK) && (code != SSH2_FX_EOF)){
-            _DEBUG << "error on mime type detection";
-            processStatus(code, url.prettyUrl());
-            close();
-            return;
-        }
-        KMimeType::Ptr mime = KMimeType::findByNameAndContent(url.fileName(), buffer);
-        mimeType(mime->name());
-    }
+  // start sftp session
+  kDebug(KIO_SFTP_DB) << "Trying to request the sftp session";
+  sftp_session = sftp_new(ssh_session);
+  if (sftp_session == NULL) {
+    closeConnection();
+    error(ERR_COULD_NOT_LOGIN, i18n("Unable to request the SFTP subsystem. "
+          "Make sure SFTP is enabled on the server"));
+    return;
+  }
 
-    openUrl = url;
-    openOffset = 0;
-    totalSize(fileSize);
-    position(0);
-    opened();
-}
+  kDebug(KIO_SFTP_DB) << "Trying to initialize the sftp session";
+  if (sftp_init(sftp_session) < 0) {
+    closeConnection();
+    error(ERR_COULD_NOT_LOGIN, i18n("Could not initialize the SFTP session."));
+    return;
+  }
 
-void sftpProtocol::read(KIO::filesize_t bytes)
-{
-    _DEBUG << "read, offset = " << openOffset << ", bytes = " << bytes;
-    QByteArray buffer;
-    int code = sftpRead(openHandle, openOffset, bytes, buffer);
-    if ((code == SSH2_FX_OK) || (code == SSH2_FX_EOF)) {
-        openOffset += buffer.size();
-        data(buffer);
-    } else {
-        processStatus(code, openUrl.prettyUrl());
-        close();
-    }
-}
+  // Login succeeded!
+  infoMessage(i18n("Successfully connected to %1", mHost));
+  info.url.setProtocol("sftp");
+  info.url.setHost(mHost);
+  info.url.setPort(mPort);
+  info.url.setUser(mUsername);
+  info.username = mUsername;
+  info.password = mPassword;
 
-void sftpProtocol::write(const QByteArray &data)
-{
-    _DEBUG << "write";
-    int code = sftpWrite(openHandle, openOffset, data);
-    if (code == SSH2_FX_OK) {
-        openOffset += data.size();
-        written(data.size());
-    } else {
-        processStatus(code, openUrl.prettyUrl());
-        close();
-    }
-}
+  kDebug(KIO_SFTP_DB) << "Caching info.username = " << info.username
+    << ", info.url = " << info.url.prettyUrl();
 
-void sftpProtocol::seek(KIO::filesize_t offset)
-{
-    _DEBUG << "seek, offset = " << offset;
-    openOffset = offset;
-    position(offset);
-}
+  cacheAuthentication(info);
 
-void sftpProtocol::close()
-{
-    sftpClose(openHandle);
-    _DEBUG << "emitting finished";
-    finished();
+  mConnected = true;
+  connected();
+
+  mPassword.fill('x');
+  mPassword.clear();
+  info.password.fill('x');
+  info.password.clear();
+
+  return;
 }
-#undef _DEBUG
 
 void sftpProtocol::closeConnection() {
-    kDebug(KIO_SFTP_DB) << "closeConnection()";
-    ssh.disconnect();
-    mConnected = false;
-}
+  kDebug(KIO_SFTP_DB) << "closeConnection()";
 
-void sftpProtocol::sftpCopyPut(const KUrl& src, const KUrl& dest, int permissions, KIO::JobFlags flags) {
-    KDE_struct_stat buff;
-    QByteArray file (QFile::encodeName(src.path()));
+  sftp_free(sftp_session);
+  sftp_session = NULL;
 
-    if (KDE_lstat(file.data(), &buff) == -1) {
-        error (ERR_DOES_NOT_EXIST, src.prettyUrl());
-        return;
-    }
+  ssh_disconnect(ssh_session);
+  ssh_session = NULL;
 
-    if (S_ISDIR (buff.st_mode)) {
-        error (ERR_IS_DIRECTORY, src.prettyUrl());
-        return;
-    }
-
-    int fd = KDE_open (file.data(), O_RDONLY);
-    if (fd == -1) {
-        error (ERR_CANNOT_OPEN_FOR_READING, src.prettyUrl());
-        return;
-    }
-
-    totalSize (buff.st_size);
-
-    sftpPut (dest, permissions, (flags & ~KIO::Resume), fd);
-
-    // Close the file descriptor...
-    ::close( fd );
+  mConnected = false;
 }
 
-void sftpProtocol::sftpPut( const KUrl& dest, int permissions, KIO::JobFlags flags, int fd ) {
+void sftpProtocol::open(const KUrl &url, QIODevice::OpenMode mode) {
+  kDebug(KIO_SFTP_DB) << "open: " << url;
 
-    openConnection();
-    if( !mConnected )
-        return;
+  openConnection();
+  if (!mConnected) {
+    error(KIO::ERR_CONNECTION_BROKEN, url.prettyUrl());
+    return;
+  }
 
-    kDebug(KIO_SFTP_DB) << dest << ", resume=" << (flags & KIO::Resume) << ", overwrite=" << (flags & KIO::Overwrite);
+  const QString path = url.path();
+  const QByteArray path_c = path.toUtf8();
 
-    KUrl origUrl( dest );
-    sftpFileAttr origAttr(remoteEncoding());
-    bool origExists = false;
+  SFTP_ATTRIBUTES *sb = sftp_lstat(sftp_session, path_c.constData());
+  if (sb == NULL) {
+    reportError(url, sftp_get_error(sftp_session));
+    return;
+  }
 
-    // Stat original (without part ext)  to see if it already exists
-    int code = sftpStat(origUrl, origAttr);
+  switch (sb->type) {
+    case SSH_FILEXFER_TYPE_DIRECTORY:
+      error(KIO::ERR_IS_DIRECTORY, url.prettyUrl());
+      sftp_attributes_free(sb);
+      return;
+    case SSH_FILEXFER_TYPE_SPECIAL:
+    case SSH_FILEXFER_TYPE_UNKNOWN:
+      error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyUrl());
+      sftp_attributes_free(sb);
+      return;
+    case SSH_FILEXFER_TYPE_SYMLINK:
+    case SSH_FILEXFER_TYPE_REGULAR:
+      break;
+  }
 
-    if( code == SSH2_FX_OK ) {
-        kDebug(KIO_SFTP_DB) << qPrintable(origUrl.url()) << " already exists!";
+  KIO::filesize_t fileSize = sb->size;
+  sftp_attributes_free(sb);
 
-        // Delete remote file if its size is zero
-        if( origAttr.fileSize() == 0 ) {
-            if( sftpRemove(origUrl, true) != SSH2_FX_OK ) {
-                error(ERR_CANNOT_DELETE_ORIGINAL, origUrl.prettyUrl());
-                return;
-            }
-        }
-        else {
-            origExists = true;
-        }
-    }
-    else if( code != SSH2_FX_NO_SUCH_FILE ) {
-        processStatus(code, origUrl.prettyUrl());
-        return;
-    }
+  int flags = 0;
 
-    // Do not waste time/resources with more remote stat calls if the file exists
-    // and we weren't instructed to overwrite it...
-    if( origExists && !(flags & KIO::Overwrite) ) {
-        error(ERR_FILE_ALREADY_EXIST, origUrl.prettyUrl());
-        return;
+  if (mode & QIODevice::ReadOnly) {
+    if (mode & QIODevice::WriteOnly) {
+      flags = O_RDWR | O_CREAT;
+    } else {
+      flags = O_RDONLY;
     }
+  } else if (mode & QIODevice::WriteOnly) {
+    flags = O_WRONLY | O_CREAT;
+  }
 
-    // Stat file with part ext to see if it already exists...
-    KUrl partUrl( origUrl );
-    partUrl.setFileName( partUrl.fileName() + ".part" );
+  if (mode & QIODevice::Append) {
+    flags |= O_APPEND;
+  } else if (mode & QIODevice::Truncate) {
+    flags |= O_TRUNC;
+  }
 
-    quint64 offset = 0;
-    bool partExists = false;
-    bool markPartial = config()->readEntry("MarkPartial", true);
+  if (flags & O_CREAT) {
+    mOpenFile = sftp_open(sftp_session, path_c.constData(), flags, 0666);
+  } else {
+    mOpenFile = sftp_open(sftp_session, path_c.constData(), flags, 0);
+  }
 
-    if( markPartial ) {
+  if (mOpenFile == NULL) {
+    error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
+    return;
+  }
 
-        sftpFileAttr partAttr(remoteEncoding());
-        code = sftpStat(partUrl, partAttr);
+  // Determine the mimetype of the file to be retrieved, and emit it.
+  // This is mandatory in all slaves (for KRun/BrowserRun to work).
+  // If we're not opening the file ReadOnly or ReadWrite, don't attempt to
+  // read the file and send the mimetype.
+  if (mode & QIODevice::ReadOnly) {
+    size_t bytesRequested = 1024;
+    ssize_t bytesRead = 0;
+    QVarLengthArray<char> buffer(bytesRequested);
 
-        if( code == SSH2_FX_OK ) {
-            kDebug(KIO_SFTP_DB) << ".part file already exists";
-            partExists = true;
-            offset = partAttr.fileSize();
+    bytesRead = sftp_read(mOpenFile, buffer.data(), bytesRequested);
+    if (bytesRead < 0) {
+      error(KIO::ERR_COULD_NOT_READ, mOpenUrl.prettyUrl());
+      close();
+      return;
+    } else {
+      QByteArray fileData = QByteArray::fromRawData(buffer.data(), bytesRead);
+      KMimeType::Ptr p_mimeType = KMimeType::findByNameAndContent(mOpenUrl.fileName(), fileData);
+      emit mimeType(p_mimeType->name());
 
-            // If for some reason, both the original and partial files exist,
-            // skip resumption just like we would if the size of the partial
-            // file is zero...
-            if( origExists || offset == 0 )
-            {
-                if( sftpRemove(partUrl, true) != SSH2_FX_OK ) {
-                    error(ERR_CANNOT_DELETE_PARTIAL, partUrl.prettyUrl());
-                    return;
-                }
-
-                if( sftpRename(origUrl, partUrl) != SSH2_FX_OK ) {
-                    error(ERR_CANNOT_RENAME_ORIGINAL, origUrl.prettyUrl());
-                    return;
-                }
-
-                offset = 0;
-            }
-            else if( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) ) {
-                    if (fd != -1)
-                        flags |= (KDE_lseek(fd, offset, SEEK_SET) != -1) ? KIO::Resume : KIO::DefaultFlags;
-                    else
-                        flags |= canResume( offset ) ? KIO::Resume : KIO::DefaultFlags;
-
-                    kDebug(KIO_SFTP_DB) << "can resume = " << (flags & KIO::Resume)
-                                         << ", offset = " << offset;
-
-                    if( !(flags & KIO::Resume) ) {
-                      error(ERR_FILE_ALREADY_EXIST, partUrl.prettyUrl());
-                      return;
-                    }
-            }
-            else {
-                offset = 0;
-            }
-        }
-        else if( code == SSH2_FX_NO_SUCH_FILE ) {
-            if( origExists && sftpRename(origUrl, partUrl) != SSH2_FX_OK ) {
-              error(ERR_CANNOT_RENAME_ORIGINAL, origUrl.prettyUrl());
-              return;
-            }
-        }
-        else {
-            processStatus(code, partUrl.prettyUrl());
-            return;
-        }
+      // Go back to the beginning of the file.
+      sftp_rewind(mOpenFile);
     }
+  }
 
-    // Determine the url we will actually write to...
-    KUrl writeUrl (markPartial ? partUrl:origUrl);
+  mOpenUrl = url;
 
-    quint32 pflags = 0;
-    if( (flags & KIO::Overwrite) && !(flags & KIO::Resume) )
-        pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT | SSH2_FXF_TRUNC;
-    else if( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
-        pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT | SSH2_FXF_EXCL;
-    else if( (flags & KIO::Overwrite) && (flags & KIO::Resume) )
-        pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT;
-    else if( !(flags & KIO::Overwrite) && (flags & KIO::Resume) )
-        pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT | SSH2_FXF_APPEND;
+  openOffset = 0;
+  totalSize(fileSize);
+  position(0);
+  opened();
+}
 
-    sftpFileAttr attr(remoteEncoding());
-    QByteArray handle;
+void sftpProtocol::read(KIO::filesize_t bytes) {
+  kDebug(KIO_SFTP_DB) << "read, offset = " << openOffset << ", bytes = " << bytes;
 
-    // Set the permissions of the file we write to if it didn't already exist
-    // and the permission info is supplied, i.e it is not -1
-    if( !partExists && !origExists && permissions != -1)
-        attr.setPermissions(permissions);
+  Q_ASSERT(mOpenFile != NULL);
 
-    code = sftpOpen( writeUrl, pflags, attr, handle );
-    if( code != SSH2_FX_OK ) {
+  QVarLengthArray<char> buffer(bytes);
 
-        // Rename the file back to its original name if a
-        // put fails due to permissions problems...
-        if( markPartial && (flags & KIO::Overwrite) ) {
-            (void) sftpRename(partUrl, origUrl);
-            writeUrl = origUrl;
-        }
+  ssize_t bytesRead = sftp_read(mOpenFile, buffer.data(), bytes);
+  Q_ASSERT(bytesRead <= static_cast<ssize_t>(bytes));
 
-        if( code == SSH2_FX_FAILURE ) { // assume failure means file exists
-            error(ERR_FILE_ALREADY_EXIST, writeUrl.prettyUrl());
-            return;
-        }
-        else {
-            processStatus(code, writeUrl.prettyUrl());
-            return;
-        }
-    }
+  if (bytesRead < 0) {
+    kDebug(KIO_SFTP_DB) << "Could not read " << mOpenUrl;
+    error(KIO::ERR_COULD_NOT_READ, mOpenUrl.prettyUrl());
+    close();
+    return;
+  }
 
-    long nbytes;
-    QByteArray buff;
+  QByteArray fileData = QByteArray::fromRawData(buffer.data(), bytesRead);
+  data(fileData);
+}
 
-    do {
+void sftpProtocol::write(const QByteArray &data) {
+  kDebug(KIO_SFTP_DB) << "write, offset = " << openOffset << ", bytes = " << data.size();
 
-        if( fd != -1 ) {
-            buff.resize( 16*1024 );
-            if ( (nbytes = ::read(fd, buff.data(), buff.size())) > -1 )
-                buff.resize( nbytes );
-        }
-        else {
-            dataReq();
-            nbytes = readData( buff );
-        }
+  Q_ASSERT(mOpenFile != NULL);
 
-        if( nbytes > 0 ) {
-            if( (code = sftpWrite(handle, offset, buff)) != SSH2_FX_OK ) {
-                error(ERR_COULD_NOT_WRITE, dest.prettyUrl());
-                return;
-            }
+  ssize_t bytesWritten = sftp_write(mOpenFile, data.data(), data.size());
+  if (bytesWritten < 0) {
+    kDebug(KIO_SFTP_DB) << "Could not write to " << mOpenUrl;
+    error(KIO::ERR_COULD_NOT_WRITE, mOpenUrl.prettyUrl());
+    close();
+    return;
+  }
 
-            offset += nbytes;
-            processedSize(offset);
+  written(bytesWritten);
+}
 
-            /* Check if slave was killed.  According to slavebase.h we
-              * need to leave the slave methods as soon as possible if
-              * the slave is killed. This allows the slave to be cleaned
-              * up properly.
-              */
-            if( wasKilled() ) {
-                sftpClose(handle);
-                closeConnection();
-                error(ERR_UNKNOWN, i18n("An internal error occurred. Please try again."));
-                return;
-            }
-        }
+void sftpProtocol::seek(KIO::filesize_t offset) {
+  kDebug(KIO_SFTP_DB) << "seek, offset = " << offset;
 
-    } while( nbytes > 0 );
+  Q_ASSERT(mOpenFile != NULL);
 
-    if( nbytes < 0 ) {
-        sftpClose(handle);
+  if (sftp_seek64(mOpenFile, static_cast<uint64_t>(offset)) < 0) {
+    error(KIO::ERR_COULD_NOT_SEEK, mOpenUrl.path());
+    close();
+  }
 
-        if( markPartial ) {
-            // Remove remote file if it smaller than our keep size
-            uint minKeepSize = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
-
-            if( sftpStat(writeUrl, attr) == SSH2_FX_OK ) {
-                if( attr.fileSize() < minKeepSize ) {
-                    sftpRemove(writeUrl, true);
-                }
-            }
-        }
-
-        error( ERR_UNKNOWN, i18n("Unknown error was encountered while copying the file "
-                                 "to '%1'. Please try again.", dest.host()) );
-        return;
-    }
-
-    if( (code = sftpClose(handle)) != SSH2_FX_OK ) {
-        error(ERR_COULD_NOT_WRITE, writeUrl.prettyUrl());
-        return;
-    }
-
-    // If wrote to a partial file, then remove the part ext
-    if( markPartial ) {
-        if( sftpRename(partUrl, origUrl) != SSH2_FX_OK ) {
-            error(ERR_CANNOT_RENAME_PARTIAL, origUrl.prettyUrl());
-            return;
-        }
-    }
-
-    finished();
+  position(sftp_tell64(mOpenFile));
 }
 
-void sftpProtocol::put ( const KUrl& url, int permissions, KIO::JobFlags flags ) {
-    kDebug(KIO_SFTP_DB) << "put(): " << url << ", overwrite = " << (flags & KIO::Overwrite)
-                         << ", resume = " << (flags & KIO::Resume);
+void sftpProtocol::close() {
+  sftp_close(mOpenFile);
 
-    sftpPut( url, permissions, flags );
+  mOpenFile = NULL;
+  finished();
 }
 
-void sftpProtocol::stat ( const KUrl& url ){
-    kDebug(KIO_SFTP_DB) << url;
+void sftpProtocol::get(const KUrl& url) {
+  kDebug(KIO_SFTP_DB) << "get(): " << url;
 
-    openConnection();
-    if( !mConnected )
-        return;
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    // If the stat URL has no path, do not attempt to determine the real
-    // path and do a redirect. KRun will simply ignore such requests.
-    // Instead, simply return the mime-type as a directory...
-    if( !url.hasPath() ) {
-        UDSEntry entry;
+  QByteArray path = url.path().toUtf8();
 
-        entry.insert( KIO::UDSEntry::UDS_NAME, QString::fromLatin1(".") );
-        entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
-        entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
-        entry.insert( KIO::UDSEntry::UDS_USER, mUsername );
-        entry.insert( KIO::UDSEntry::UDS_GROUP, mUsername );
+  char buf[MAX_XFER_BUF_SIZE] = {0};
+  SFTP_FILE *file = NULL;
+  ssize_t bytesread = 0;
+  // time_t curtime = 0;
+  time_t lasttime = 0;
+  time_t starttime = 0;
+  KIO::filesize_t totalbytesread  = 0;
+  QByteArray  filedata;
 
-        // no size
-        statEntry( entry );
-        finished();
-        return;
-    }
+  SFTP_ATTRIBUTES *sb = sftp_lstat(sftp_session, path.constData());
+  if (sb == NULL) {
+    reportError(url, sftp_get_error(sftp_session));
+    return;
+  }
 
-    int code;
-    sftpFileAttr attr(remoteEncoding());
-    if( (code = sftpStat(url, attr)) != SSH2_FX_OK ) {
-        processStatus(code, url.prettyUrl());
-        return;
-    }
-    else {
-        //kDebug() << "We sent and received stat packet ok";
-        //attr.setFilename(url.fileName());
-        statEntry(attr.entry());
-    }
+  if (sb->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+    error(KIO::ERR_IS_DIRECTORY, url.prettyUrl());
+    sftp_attributes_free(sb);
+    return;
+  }
+  if (sb->type != SSH_FILEXFER_TYPE_REGULAR) {
+    error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyUrl());
+    sftp_attributes_free(sb);
+    return;
+  }
 
-    finished();
-
-    //kDebug(KIO_SFTP_DB) << "END";
+  // Open file
+  file = sftp_open(sftp_session, path.constData(), O_RDONLY, 0);
+  if (file == NULL) {
+    error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyUrl());
+    sftp_attributes_free(sb);
     return;
-}
+  }
 
+  // Determine the mimetype of the file to be retrieved, and emit it.
+  // This is mandatory in all slaves (for KRun/BrowserRun to work)
+  // In real "remote" slaves, this is usually done using findByNameAndContent
+  // after receiving some data. But we don't know how much data the mimemagic rules
+  // need, so for local files, better use findByUrl with localUrl=true.
+  KMimeType::Ptr mt = KMimeType::findByUrl( url, sb->permissions, false /* remote URL */ );
+  emit mimeType( mt->name() ); // FIXME test me
 
-void sftpProtocol::mimetype ( const KUrl& url ){
-    kDebug(KIO_SFTP_DB) << url;
+  // Set the total size
+  totalSize(sb->size);
 
-    openConnection();
-    if( !mConnected )
-        return;
-
-    Status info = sftpGet(url, 0 /*offset*/, -1, true /*only emit mimetype*/);
-
-    if (info.code != 0)
+  const QString resumeOffset = metaData(QLatin1String("resume"));
+  if (!resumeOffset.isEmpty()) {
+    bool ok;
+    KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
+    if (ok && (offset > 0) && ((unsigned long long) offset < sb->size))
     {
-      error(info.code, info.text);
-      return;
+      if (sftp_seek64(file, offset) == 0) {
+        canResume();
+        totalbytesread = offset;
+        kDebug(KIO_SFTP_DB) << "Resume offset: " << QString::number(offset);
+      }
     }
+  }
 
-    finished();
-}
+  if (file != NULL) {
+    bool isFirstPacket = true;
+    lasttime = starttime = time(NULL);
 
-
-void sftpProtocol::listDir(const KUrl& url) {
-    kDebug(KIO_SFTP_DB) << url;
-
-    openConnection();
-    if( !mConnected )
+    for (;;) {
+      bytesread = sftp_read(file, buf, MAX_XFER_BUF_SIZE);
+      kDebug(KIO_SFTP_DB) << "bytesread=" << QString::number(bytesread);
+      if (bytesread == 0) {
+        // All done reading
+        break;
+      } else if (bytesread < 0) {
+        error( KIO::ERR_COULD_NOT_READ, url.prettyUrl());
+        sftp_attributes_free(sb);
         return;
+      }
 
-    if( !url.hasPath() ) {
-        KUrl newUrl ( url );
-        if( sftpRealPath(url, newUrl) == SSH2_FX_OK ) {
-            kDebug(KIO_SFTP_DB) << "listDir: Redirecting to " << newUrl;
-            redirection(newUrl);
-            finished();
-            return;
-        }
-    }
+      filedata = QByteArray::fromRawData(buf, bytesread);
+      if (isFirstPacket) {
+        KMimeType::Ptr p_mimeType = KMimeType::findByNameAndContent(url.fileName(), filedata);
+        mimeType(p_mimeType->name());
+        isFirstPacket = false;
+      }
+      data(filedata);
+      filedata.clear();
 
-    int code;
-    QByteArray handle;
+      // increment total bytes read
+      totalbytesread += bytesread;
 
-    if( (code = sftpOpenDirectory(url, handle)) != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "listDir(): open directory failed";
-        processStatus(code, url.prettyUrl());
-        return;
+      processedSize(totalbytesread);
     }
 
+    sftp_close(file);
+    data(QByteArray());
+    processedSize(static_cast<KIO::filesize_t>(sb->size));
+  }
 
-    code = SSH2_FX_OK;
-    while( code == SSH2_FX_OK ) {
-        code = sftpReadDir(handle, url);
-        if( code != SSH2_FX_OK && code != SSH2_FX_EOF )
-            processStatus(code, url.prettyUrl());
-        kDebug(KIO_SFTP_DB) << "listDir(): return code = " << code;
-    }
-
-    if( (code = sftpClose(handle)) != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "listdir(): closing of directory failed";
-        processStatus(code, url.prettyUrl());
-        return;
-    }
-
-    finished();
-    kDebug(KIO_SFTP_DB) << "listDir(): END";
+  sftp_attributes_free(sb);
+  finished();
 }
 
-/** Make a directory.
-    OpenSSH does not follow the internet draft for sftp in this case.
-    The format of the mkdir request expected by OpenSSH sftp server is:
-        uint32 id
-        string path
-        ATTR   attr
- */
-void sftpProtocol::mkdir(const KUrl&url, int permissions){
+void sftpProtocol::put(const KUrl& url, int permissions, KIO::JobFlags flags) {
+  kDebug(KIO_SFTP_DB) << "put(): " << url
+                      << " , permissions = " << QString::number(permissions)
+                      << ", overwrite = " << (flags & KIO::Overwrite)
+                      << ", resume = " << (flags & KIO::Resume);
 
-    kDebug(KIO_SFTP_DB) << "create directory: " << url.path();
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    openConnection();
-    if( !mConnected )
-        return;
+  const QString dest_orig = url.path();
+  const QByteArray dest_orig_c = dest_orig.toUtf8();
+  const QString dest_part = dest_orig + ".part";
+  const QByteArray dest_part_c = dest_part.toUtf8();
 
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
+  SFTP_ATTRIBUTES *sb = sftp_lstat(sftp_session, dest_orig_c.constData());
+  const bool bOrigExists = (sb != NULL);
+  bool bPartExists = false;
+  const bool bMarkPartial = config()->readEntry("MarkPartial", true);
 
-    sftpFileAttr attr(remoteEncoding());
+  if (bMarkPartial) {
+    SFTP_ATTRIBUTES *sbPart = sftp_lstat(sftp_session, dest_part_c.constData());
+    bPartExists = (sbPart != NULL);
 
-    if (permissions != -1)
-      attr.setPermissions(permissions);
+    if (bPartExists && !(flags & KIO::Resume) && !(flags & KIO::Overwrite) &&
+        sbPart->size > 0 && sbPart->type == SSH_FILEXFER_TYPE_REGULAR) {
+      kDebug(KIO_SFTP_DB) << "put : calling canResume with "
+        << QString::number(sbPart->size);
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
+      // Maybe we can use this partial file for resuming
+      // Tell about the size we have, and the app will tell us
+      // if it's ok to resume or not.
+      flags |= canResume(sbPart->size) ? KIO::Resume : KIO::DefaultFlags;
 
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << quint32(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len + attr.size());
-    s << (quint8)SSH2_FXP_MKDIR;
-    s << id;
-    s.writeBytes(path.data(), len);
-    s << attr;
+      kDebug(KIO_SFTP_DB) << "put got answer " << (flags & KIO::Resume);
 
-    kDebug(KIO_SFTP_DB) << "mkdir(): packet size is " << p.size();
-
-    putPacket(p);
-    getPacket(p);
-
-    quint8 type;
-    QDataStream r(p);
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "mkdir: sftp packet id mismatch";
-        error(ERR_COULD_NOT_MKDIR, url.path());
-        finished();
-        return;
+      delete sbPart;
     }
+  }
 
-    if( type != SSH2_FXP_STATUS ) {
-        kError(KIO_SFTP_DB) << "mkdir(): unexpected packet type of" << type;
-        error(ERR_COULD_NOT_MKDIR, url.path());
-        finished();
-        return;
+  if (bOrigExists && !(flags & KIO::Overwrite) && !(flags & KIO::Resume)) {
+    if (sb->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+      error(KIO::ERR_DIR_ALREADY_EXIST, dest_orig);
+    } else {
+      error(KIO::ERR_FILE_ALREADY_EXIST, dest_orig);
     }
+    sftp_attributes_free(sb);
+    return;
+  }
 
-    int code;
-    r >> code;
-    if( code != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "mkdir(): failed with code " << code;
+  int result;
+  QByteArray dest;
+  SFTP_FILE *file = NULL;
 
-        // Check if mkdir failed because the directory already exists so that
-        // we can return the appropriate message...
-        sftpFileAttr dirAttr(remoteEncoding());
-        if ( sftpStat(url, dirAttr) == SSH2_FX_OK )
-        {
-          error( ERR_DIR_ALREADY_EXIST, url.prettyUrl() );
-          return;
+  // Loop until we got 0 (end of data)
+  do {
+    QByteArray buffer;
+    dataReq(); // Request for data
+    result = readData(buffer);
+
+    if (result >= 0) {
+      if (dest.isEmpty()) {
+        if (bMarkPartial) {
+          kDebug(KIO_SFTP_DB) << "Appending .part extension to " << dest_orig;
+          dest = dest_part_c;
+          if (bPartExists && !(flags & KIO::Resume)) {
+            kDebug(KIO_SFTP_DB) << "Deleting partial file " << dest_part;
+            sftp_unlink(sftp_session, dest_part_c.constData());
+            // Catch errors when we try to open the file.
+          }
+        } else {
+          dest = dest_orig_c;
+          if (bOrigExists && !(flags & KIO::Resume)) {
+            kDebug(KIO_SFTP_DB) << "Deleting destination file " << dest_orig;
+            sftp_unlink(sftp_session, dest_orig_c.constData());
+            // Catch errors when we try to open the file.
+          }
         }
 
-        error(ERR_COULD_NOT_MKDIR, url.path());
-    }
+        if ((flags & KIO::Resume)) {
+          file = sftp_open(sftp_session, dest.constData(), O_RDWR, 0);  // append if resuming
+          SFTP_ATTRIBUTES *fstat = sftp_fstat(file);
+          if (fstat == NULL) {
+            reportError(url, sftp_get_error(sftp_session));
+            sftp_attributes_free(sb);
+            return;
+          }
+          sftp_seek64(file, fstat->size); // Seek to end TODO
+          sftp_attributes_free(fstat);
+        } else {
+          mode_t initialMode;
+          if (permissions != -1) {
+            initialMode = permissions | S_IWUSR | S_IRUSR;
+          } else {
+            initialMode = 0666;
+          }
 
-    finished();
-}
-
-void sftpProtocol::rename(const KUrl& src, const KUrl& dest, KIO::JobFlags flags) {
-    kDebug(KIO_SFTP_DB) << src << " -> " << dest;
-
-    if (!isSupportedOperation(SSH2_FXP_RENAME)) {
-      error(ERR_UNSUPPORTED_ACTION,
-          i18n("The remote host does not support renaming files."));
-      return;
-    }
-
-    openConnection();
-    if( !mConnected )
-        return;
-
-    // Always stat the destination before attempting to rename
-    // a file or a directory...
-    sftpFileAttr attr(remoteEncoding());
-    int code = sftpStat(dest, attr);
-
-    // If the destination directory, exists tell it to the job
-    // so it the proper action can be presented to the user...
-    if( code == SSH2_FX_OK )
-    {
-      if (!(flags & KIO::Overwrite))
-      {
-        if ( S_ISDIR(attr.permissions()) )
-          error( KIO::ERR_DIR_ALREADY_EXIST, dest.url() );
-        else
-          error( KIO::ERR_FILE_ALREADY_EXIST, dest.url() );
-        return;
+          kDebug(KIO_SFTP_DB) << "Trying to open: " << dest << ", mode=" << QString::number(initialMode);
+          file = sftp_open(sftp_session, dest.constData(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
         }
 
-      // If overwrite is specified, then simply remove the existing file/dir first...
-      if( (code = sftpRemove( dest, !S_ISDIR(attr.permissions()) )) != SSH2_FX_OK )
-      {
-        processStatus(code);
-            return;
+        if (file == NULL) {
+          kDebug(KIO_SFTP_DB) << "####################### COULD NOT WRITE " << dest << " permissions=" << permissions;
+          if (sftp_get_error(sftp_session) == SSH_FX_PERMISSION_DENIED) {
+              error(KIO::ERR_WRITE_ACCESS_DENIED, QString::fromUtf8(dest));
+          } else {
+            error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, QString::fromUtf8(dest));
+          }
+          sftp_attributes_free(sb);
+          return;
         }
-    }
+      }
 
-    // Do the renaming...
-    if( (code = sftpRename(src, dest)) != SSH2_FX_OK ) {
-        processStatus(code);
-        return;
+      ssize_t bytesWritten = sftp_write(file, buffer.data(), buffer.size());
+      if (bytesWritten < 0) {
+        error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
+        result = -1;
+      }
     }
+  } while (result > 0);
+  sftp_attributes_free(sb);
 
-    finished();
-    kDebug(KIO_SFTP_DB) << "END";
-}
+  // An error occurred deal with it.
+  if (result < 0) {
+    kDebug(KIO_SFTP_DB) << "Error during 'put'. Aborting.";
 
-void sftpProtocol::symlink(const QString& target, const KUrl& dest, KIO::JobFlags flags) {
-    kDebug(KIO_SFTP_DB) << "link " << target << "->" << dest;
+    if (file != NULL) {
+      sftp_close(file);
 
-    if (!isSupportedOperation(SSH2_FXP_SYMLINK)) {
-      error(ERR_UNSUPPORTED_ACTION,
-          i18n("The remote host does not support creating symbolic links."));
-      return;
-    }
-
-    openConnection();
-    if( !mConnected )
-        return;
-
-    int code;
-    bool failed = false;
-    if( (code = sftpSymLink(target, dest)) != SSH2_FX_OK ) {
-        if( flags & KIO::Overwrite ) { // try to delete the destination
-            sftpFileAttr attr(remoteEncoding());
-            if( (code = sftpStat(dest, attr)) != SSH2_FX_OK ) {
-                failed = true;
-            }
-            else {
-                if( (code = sftpRemove(dest, !S_ISDIR(attr.permissions())) ) != SSH2_FX_OK ) {
-                    failed = true;
-                }
-                else {
-                    // XXX what if rename fails again? We have lost the file.
-                    // Maybe rename dest to a temporary name first? If rename is
-                    // successful, then delete?
-                    if( (code = sftpSymLink(target, dest)) != SSH2_FX_OK )
-                        failed = true;
-                }
-            }
+      SFTP_ATTRIBUTES *attr = sftp_stat(sftp_session, dest.constData());
+      if (bMarkPartial && attr != NULL) {
+        size_t size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
+        if (attr->size < size) {
+          sftp_unlink(sftp_session, dest.constData());
         }
-        else if( code == SSH2_FX_FAILURE ) {
-            error(ERR_FILE_ALREADY_EXIST, dest.prettyUrl());
-            return;
-        }
-        else
-            failed = true;
+      }
+      delete attr;
+      sftp_attributes_free(attr);
     }
 
-    // What error code do we return? Code for the original symlink command
-    // or for the last command or for both? The second one is implemented here.
-    if( failed )
-        processStatus(code);
-
+    //::exit(255);
     finished();
-}
+    return;
+  }
 
-void sftpProtocol::chmod(const KUrl& url, int permissions){
-    QString perms;
-    perms.setNum(permissions, 8);
-
-    kDebug(KIO_SFTP_DB) << "change permission of " << url << " to " << perms;
-
-    openConnection();
-    if( !mConnected )
-        return;
-
-    sftpFileAttr attr(remoteEncoding());
-
-    if (permissions != -1)
-      attr.setPermissions(permissions);
-
-    int code;
-    if( (code = sftpSetStat(url, attr)) != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "cannot stat failed with error " << code;
-        if( code == SSH2_FX_FAILURE )
-            error(ERR_CANNOT_CHMOD, QString());
-        else
-            processStatus(code, url.prettyUrl());
-    }
+  if (file == NULL) { // we got nothing to write out, so we never opened the file
     finished();
-}
+    return;
+  }
 
+  if (sftp_close(file) < 0) {
+    kWarning(KIO_SFTP_DB) << "Error when closing file descriptor";
+    error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
+    return;
+  }
 
-void sftpProtocol::del(const KUrl &url, bool isfile){
-    kDebug(KIO_SFTP_DB) << "delete " << (isfile ? "file: " : "directory: ") << url;
-
-    openConnection();
-    if( !mConnected )
-        return;
-
-    int code;
-    if( (code = sftpRemove(url, isfile)) != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "delete failed with error code " << code;
-        processStatus(code, url.prettyUrl());
+  // after full download rename the file back to original name
+  if (bMarkPartial) {
+    // If the original URL is a symlink and we were asked to overwrite it,
+    // remove the symlink first. This ensures that we do not overwrite the
+    // current source if the symlink points to it.
+    if ((flags & KIO::Overwrite)) {
+      sftp_unlink(sftp_session, dest_orig_c.constData());
     }
-    finished();
-}
 
-void sftpProtocol::slave_status() {
-    kDebug(KIO_SFTP_DB) << "connected to " <<  mHost << "? " << mConnected;
-    slaveStatus ((mConnected ? mHost : QString()), mConnected);
-}
-
-bool sftpProtocol::getPacket(QByteArray& msg) {
-    QByteArray buf(4096, '\0');
-
-#ifdef Q_WS_WIN
-    ssize_t len;
-    if(ssh.pty()->waitForReadyRead(2000)) {
-        len = ssh.pty()->read(buf.data(), 4);
+    if (sftp_rename(sftp_session, dest.constData(), dest_orig_c.constData()) < 0) {
+      kWarning(KIO_SFTP_DB) << " Couldn't rename " << dest << " to " << dest_orig;
+      error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig);
+      return;
     }
-#else
-    // Get the message length...
-    ssize_t len = atomicio(ssh.stdioFd(), buf.data(), 4, true /*read*/);
-#endif
-    if( len == 0 || len == -1 ) {
-        kDebug(KIO_SFTP_DB) << "read of packet length failed, ret = "
-                             << len << ", error =" << strerror(errno);
-        closeConnection();
-        error( ERR_CONNECTION_BROKEN, mHost);
-        msg.resize(0);
-        return false;
-    }
+  }
 
-    int msgLen;
-    QDataStream s(buf);
-    s >> msgLen;
-
-    //kDebug(KIO_SFTP_DB) << "message size = " << msgLen;
-
-    msg.resize(0);
-
-    QBuffer b( &msg );
-    b.open( QIODevice::WriteOnly );
-
-    while( msgLen ) {
-#ifdef Q_WS_WIN
-        len = ssh.pty()->read(buf.data(), qMin(buf.size(), msgLen));
-#else
-        len = atomicio(ssh.stdioFd(), buf.data(), qMin(buf.size(), msgLen), true /*read*/);
-#endif
-
-        if( len == 0 || len == -1) {
-            QString errmsg;
-            if (len == 0)
-              errmsg = i18n("Connection closed");
-            else
-              errmsg = i18n("Could not read SFTP packet");
-            kDebug(KIO_SFTP_DB) << "nothing to read, ret = " << len << ", error =" << strerror(errno);
-            closeConnection();
-            error(ERR_CONNECTION_BROKEN, errmsg);
-            b.close();
-            return false;
-        }
-
-        b.write(buf.data(), len);
-
-        //kDebug(KIO_SFTP_DB) << "read Message size = " << len;
-        //kDebug(KIO_SFTP_DB) << "copy Message size = " << msg.size();
-
-        msgLen -= len;
+  // set final permissions
+  if (permissions != -1 && !(flags & KIO::Resume)) {
+    if (sftp_chmod(sftp_session, dest_orig_c.constData(), permissions) < 0) {
+      kDebug(KIO_SFTP_DB) << "Could not change permissions for " << dest_orig;
+      //warning(i18n( "Could not change permissions for\n%1", dest_orig));
     }
+  }
 
-    b.close();
+  // set modification time
+  const QString mtimeStr = metaData("modified");
+  if (!mtimeStr.isEmpty()) {
+    QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate);
+    if (dt.isValid()) {
+      struct timeval times[2];
 
-    return true;
-}
+      SFTP_ATTRIBUTES *attr = sftp_lstat(sftp_session, dest_orig_c.constData());
+      if (attr != NULL) {
+        times[0].tv_sec = attr->atime; //// access time, unchanged
+        times[1].tv_sec =  dt.toTime_t(); // modification time
+        times[0].tv_usec = times[1].tv_usec = 0;
 
-/** Send an sftp packet to stdin of the ssh process. */
-bool sftpProtocol::putPacket(QByteArray& p){
-//    kDebug(KIO_SFTP_DB) << "putPacket(): size == " << p.size();
-    int ret;
-#ifdef Q_WS_WIN
-    ret = ssh.pty()->write(p.data(), p.size());
-#else
-    ret = atomicio(ssh.stdioFd(), p.data(), p.size(), false /*write*/);
-#endif
-    if( ret <= 0 ) {
-        kDebug(KIO_SFTP_DB) << "write failed, ret =" << ret << ", error = " << strerror(errno);
-        return false;
+        sftp_utimes(sftp_session, dest_orig_c.constData(), times);
+        sftp_attributes_free(attr);
+      }
     }
+  }
 
-    return true;
+  // We have done our job => finish
+  finished();
 }
 
-/** Used to have the server canonicalize any given path name to an absolute path.
-This is useful for converting path names containing ".." components or relative
-pathnames without a leading slash into absolute paths.
-Returns the canonicalized url. */
-int sftpProtocol::sftpRealPath(const KUrl& url, KUrl& newUrl){
-
-    kDebug(KIO_SFTP_DB) << "get the real path of " << url;
-
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
-
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << quint32(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
-    s << (quint8)SSH2_FXP_REALPATH;
-    s << id;
-    s.writeBytes(path.data(), len);
-
-    putPacket(p);
-    getPacket(p);
-
-    quint8 type;
-    QDataStream r(p);
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
-    }
-
-    if( type == SSH2_FXP_STATUS ) {
-        quint32 code;
-        r >> code;
-        return code;
-    }
-
-    if( type != SSH2_FXP_NAME ) {
-        kError(KIO_SFTP_DB) << "unexpected packet type of " << type;
-        return -1;
-    }
-
-    quint32 count;
-    r >> count;
-    if( count != 1 ) {
-        kError(KIO_SFTP_DB) << "bad number of file attributes for realpath command";
-        return -1;
-    }
-
-    QByteArray newPath;
-    r >> newPath;
-
-    newPath.truncate(newPath.size());
-    if (newPath.isEmpty())
-      newPath = "/";
-    newUrl.setPath(remoteEncoding()->decode(newPath));
-
-    kDebug(KIO_SFTP_DB) << "real path is " << newUrl;
-    return SSH2_FX_OK;
-}
-
-sftpProtocol::Status sftpProtocol::doProcessStatus(quint8 code, const QString& message)
+void sftpProtocol::copy(const KUrl &src, const KUrl &dest, int permissions, KIO::JobFlags flags)
 {
-    Status res;
-    res.code = 0;
-    res.size = 0;
-    res.text = message;
+  kDebug(KIO_SFTP_DB) << src << " -> " << dest << " , permissions = " << QString::number(permissions)
+                                      << ", overwrite = " << (flags & KIO::Overwrite)
+                                      << ", resume = " << (flags & KIO::Resume);
 
-    switch(code)
-    {
-      case SSH2_FX_OK:
-      case SSH2_FX_EOF:
-          res.text = i18n("End of file.");
-          break;
-      case SSH2_FX_NO_SUCH_FILE:
-          res.code = ERR_DOES_NOT_EXIST;
-          break;
-      case SSH2_FX_PERMISSION_DENIED:
-          res.code = ERR_ACCESS_DENIED;
-          break;
-      case SSH2_FX_FAILURE:
-          res.text = i18n("SFTP command failed for an unknown reason.");
-          res.code = ERR_UNKNOWN;
-          break;
-      case SSH2_FX_BAD_MESSAGE:
-          res.text = i18n("The SFTP server received a bad message.");
-          res.code = ERR_UNKNOWN;
-          break;
-      case SSH2_FX_OP_UNSUPPORTED:
-          res.text = i18n("You attempted an operation unsupported by the SFTP server.");
-          res.code = ERR_UNKNOWN;
-          break;
-      default:
-          res.text = i18n("Error code: %1", code);
-          res.code = ERR_UNKNOWN;
-    }
-
-    return res;
+  error(ERR_UNSUPPORTED_ACTION, QString());
 }
 
-/** Process SSH_FXP_STATUS packets. */
-void sftpProtocol::processStatus(quint8 code, const QString& message){
-    Status st = doProcessStatus( code, message );
-    if( st.code != 0 ){
-        error( st.code, st.text );
-    }
-}
+void sftpProtocol::stat(const KUrl& url) {
+  kDebug(KIO_SFTP_DB) << url;
 
-/** Opens a directory handle for url.path. Returns true if succeeds. */
-int sftpProtocol::sftpOpenDirectory(const KUrl& url, QByteArray& handle){
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    kDebug(KIO_SFTP_DB) << " open directory " << url;
+  if (! url.hasPath() || QDir::isRelativePath(url.path()) ||
+      url.path().contains("/./") || url.path().contains("/../")) {
+    QString cPath;
 
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
-
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
-    s << (quint8)SSH2_FXP_OPENDIR;
-    s << (quint32)id;
-    s.writeBytes(path.data(), len);
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch: " <<
-            "expected " << expectedId << ", got " << id;
-        return -1;
+    if (url.hasPath()) {
+      cPath = canonicalizePath(url.path());
+    } else {
+      cPath = canonicalizePath(QString("."));
     }
 
-    if( type == SSH2_FXP_STATUS ) {
-        quint32 errCode;
-        r >> errCode;
-        return errCode;
+    if (cPath.isEmpty()) {
+      error(ERR_MALFORMED_URL, url.prettyUrl());
+      return;
     }
+    KUrl redir(url);
+    redir.setPath(cPath);
+    redirection(redir);
 
-    if( type != SSH2_FXP_HANDLE ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
+    kDebug(KIO_SFTP_DB) << "redirecting to " << redir.url();
 
-    r >> handle;
-    if( handle.size() > 256 ) {
-        kError(KIO_SFTP_DB) << "Handle exceeds max length";
-        return -1;
-    }
+    finished();
+    return;
+  }
 
-    kDebug(KIO_SFTP_DB) << "directory handle (" << handle.size() << "): [" << handle << "]";
-    return SSH2_FX_OK;
-}
+  QByteArray path = url.path().toUtf8();
 
-/** Closes a directory or file handle. */
-int sftpProtocol::sftpClose(const QByteArray& handle){
+  const QString sDetails = metaData(QLatin1String("details"));
+  const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
+  UDSEntry entry;
+  entry.clear();
+  if (!createUDSEntry(url.fileName(), path, entry, details)) {
+    error(ERR_DOES_NOT_EXIST, url.prettyUrl());
+    return;
+  }
 
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + handle.size());
-    s << (quint8)SSH2_FXP_CLOSE;
-    s << (quint32)id;
-    s << handle;
+  statEntry(entry);
 
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
-    }
-
-    if( type != SSH2_FXP_STATUS ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
-
-    quint32 code;
-    r >> code;
-    if( code != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "close failed with err code " << code;
-    }
-
-    return code;
+  finished();
 }
 
-/** Set a files attributes. */
-int sftpProtocol::sftpSetStat(const KUrl& url, const sftpFileAttr& attr){
+void sftpProtocol::mimetype(const KUrl& url){
+  kDebug(KIO_SFTP_DB) << url;
 
-    kDebug(KIO_SFTP_DB) << "stating url " << url;
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
+  // open() feeds the mimetype
+  open(url, QIODevice::ReadOnly);
+  close();
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len + attr.size());
-    s << (quint8)SSH2_FXP_SETSTAT;
-    s << (quint32)id;
-    s.writeBytes(path.data(), len);
-    s << attr;
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
-        // XXX How do we do a fatal error?
-    }
-
-    if( type != SSH2_FXP_STATUS ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
-
-    quint32 code;
-    r >> code;
-    if( code != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "set stat failed with err code " << code;
-    }
-
-    return code;
+  finished();
 }
 
-/** Sends a sftp command to remove a file or directory. */
-int sftpProtocol::sftpRemove(const KUrl& url, bool isfile){
+void sftpProtocol::listDir(const KUrl& url) {
+  kDebug(KIO_SFTP_DB) << "list directory: " << url;
 
-    kDebug(KIO_SFTP_DB) << "deleting " << (isfile ? "file " : "directory ") << url;
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
+  if (! url.hasPath() || QDir::isRelativePath(url.path()) ||
+      url.path().contains("/./") || url.path().contains("/../")) {
+    QString cPath;
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
-    s << (quint8)(isfile ? SSH2_FXP_REMOVE : SSH2_FXP_RMDIR);
-    s << (quint32)id;
-    s.writeBytes(path.data(), len);
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
+    if (url.hasPath()) {
+      cPath = canonicalizePath(url.path());
+    } else {
+      cPath = canonicalizePath(QString("."));
     }
 
-    if( type != SSH2_FXP_STATUS ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
+    if (cPath.isEmpty()) {
+      error(ERR_MALFORMED_URL, url.prettyUrl());
+      return;
     }
+    KUrl redir(url);
+    redir.setPath(cPath);
+    redirection(redir);
 
-    quint32 code;
-    r >> code;
-    if( code != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "delete failed with error code " << code;
-    }
+    kDebug(KIO_SFTP_DB) << "redirecting to " << redir.url();
 
-    return code;
-}
+    finished();
+    return;
+  }
 
-/** Send a sftp command to rename a file or directory. */
-int sftpProtocol::sftpRename(const KUrl& src, const KUrl& dest){
+  QByteArray path = url.path().toUtf8();
 
-    kDebug(KIO_SFTP_DB) << src << " -> " << dest;
+  SFTP_DIR *dp = sftp_opendir(sftp_session, path.constData());
+  if (dp == NULL) {
+    reportError(url, sftp_get_error(sftp_session));
+    return;
+  }
 
-    QByteArray srcPath = remoteEncoding()->encode(src.path());
-    QByteArray destPath = remoteEncoding()->encode(dest.path());
+  SFTP_ATTRIBUTES *dirent = NULL;
+  const QString sDetails = metaData(QLatin1String("details"));
+  const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
+  QList<QByteArray> entryNames;
+  UDSEntry entry;
 
-    uint slen = srcPath.length();
-    uint dlen = destPath.length();
+  kDebug(KIO_SFTP_DB) << "readdir: " << path << ", details: " << QString::number(details);
+  if (details == 0) {
+    for (;;) {
+      dirent = sftp_readdir(sftp_session, dp);
+      if (dirent == NULL) {
+        break;
+      }
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
+      entry.clear();
+      entry.insert(KIO::UDSEntry::UDS_NAME, QFile::decodeName(dirent->name));
 
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ +
-                    4 /*str length*/ + slen +
-                    4 /*str length*/ + dlen);
-    s << (quint8)SSH2_FXP_RENAME;
-    s << (quint32)id;
-    s.writeBytes(srcPath.data(), slen);
-    s.writeBytes(destPath.data(), dlen);
+      switch (dirent->type) {
+        case SSH_FILEXFER_TYPE_REGULAR:
+          entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
+          break;
+        case SSH_FILEXFER_TYPE_DIRECTORY:
+          entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
+          break;
+        case SSH_FILEXFER_TYPE_SYMLINK:
+          entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
+          entry.insert(KIO::UDSEntry::UDS_LINK_DEST, QLatin1String("Dummy Link Target"));
+          break;
+        case SSH_FILEXFER_TYPE_SPECIAL:
+        case SSH_FILEXFER_TYPE_UNKNOWN:
+          break;
+      }
 
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
+      sftp_attributes_free(dirent);
+      listEntry(entry, false);
     }
+    sftp_closedir(dp);
+    listEntry(entry, true); // ready
+  } else {
+    for (;;) {
+      dirent = sftp_readdir(sftp_session, dp);
+      if (dirent == NULL) {
+        break;
+      }
 
-    if( type != SSH2_FXP_STATUS ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
+      entryNames.append(dirent->name);
+      sftp_attributes_free(dirent);
     }
 
-    int code;
-    r >> code;
-    if( code != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "rename failed with err code " << code;
-    }
+    sftp_closedir(dp);
+    totalSize(entryNames.count());
 
-    return code;
-}
-/** Get directory listings. */
-int sftpProtocol::sftpReadDir(const QByteArray& handle, const KUrl& url){
-    // url is needed so we can lookup the link destination
-    kDebug(KIO_SFTP_DB) << url;
+    QList<QByteArray>::ConstIterator it = entryNames.constBegin();
+    QList<QByteArray>::ConstIterator end = entryNames.constEnd();
 
-    quint32 id, expectedId, count;
-    quint8 type;
-
-    sftpFileAttr attr (remoteEncoding());
-    attr.setDirAttrsFlag(true);
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    id = expectedId = mMsgId++;
-    s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + handle.size());
-    s << (quint8)SSH2_FXP_READDIR;
-    s << (quint32)id;
-    s << handle;
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    r >> type >> id;
-
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
+    for (; it != end; ++it) {
+      entry.clear();
+      if (createUDSEntry(QFile::decodeName(*it), QByteArray(path + "/" + *it).constData(), entry, details)) {
+        listEntry(entry, false);
+      }
     }
+    listEntry(entry, true); // ready
+  }
 
-    int code;
-    if( type == SSH2_FXP_STATUS ) {
-        r >> code;
-        return code;
-    }
-
-    if( type != SSH2_FXP_NAME ) {
-        kError(KIO_SFTP_DB) << "unexpected message";
-        return -1;
-    }
-
-    r >> count;
-    kDebug(KIO_SFTP_DB) << "got " << count << " entries";
-
-    while(count--) {
-        r >> attr;
-
-        if( S_ISLNK(attr.permissions()) ) {
-             KUrl myurl ( url );
-             myurl.addPath(attr.filename());
-
-             // Stat the symlink to find out its type...
-             sftpFileAttr attr2 (remoteEncoding());
-             (void) sftpStat(myurl, attr2);
-
-             attr.setLinkType(attr2.linkType());
-             attr.setLinkDestination(attr2.linkDestination());
-        }
-
-        listEntry(attr.entry(), false);
-    }
-
-    listEntry(attr.entry(), true);
-
-    return SSH2_FX_OK;
+  finished();
 }
 
-int sftpProtocol::sftpReadLink(const KUrl& url, QString& target){
+void sftpProtocol::mkdir(const KUrl &url, int permissions) {
+  kDebug(KIO_SFTP_DB) << "create directory: " << url;
 
-    kDebug(KIO_SFTP_DB) << url;
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
+  if (url.path().isEmpty()) {
+    error(ERR_MALFORMED_URL, url.prettyUrl());
+    return;
+  }
+  const QString path = url.path();
+  const QByteArray path_c = path.toUtf8();
 
-    //kDebug(KIO_SFTP_DB) << "Encoded Path: " << path;
-    //kDebug(KIO_SFTP_DB) << "Encoded Size: " << len;
+  // Remove existing file or symlink, if requested.
+  if (metaData(QLatin1String("overwrite")) == QLatin1String("true")) {
+    kDebug(KIO_SFTP_DB) << "overwrite set, remove existing file or symlink: " << url;
+    sftp_unlink(sftp_session, path_c.constData());
+  }
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
-    s << (quint8)SSH2_FXP_READLINK;
-    s << id;
-    s.writeBytes(path.data(), len);
-
-
-    putPacket(p);
-    getPacket(p);
-
-    quint8 type;
-    QDataStream r(p);
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
+  kDebug(KIO_SFTP_DB) << "Trying to create directory: " << path;
+  SFTP_ATTRIBUTES *sb = sftp_lstat(sftp_session, path_c.constData());
+  if (sb == NULL) {
+    if (sftp_mkdir(sftp_session, path_c.constData(), 0777) < 0) {
+      reportError(url, sftp_get_error(sftp_session));
+      sftp_attributes_free(sb);
+      return;
+    } else {
+      kDebug(KIO_SFTP_DB) << "Successfully created directory: " << url;
+      if (permissions != -1) {
+        chmod(url, permissions);
+      } else {
+        finished();
+      }
+      sftp_attributes_free(sb);
+      return;
     }
+  }
 
-    if( type == SSH2_FXP_STATUS ) {
-        quint32 code;
-        r >> code;
-        kDebug(KIO_SFTP_DB) << "read link failed with code " << code;
-        return code;
-    }
+  if (sb->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+    error(KIO::ERR_DIR_ALREADY_EXIST, path);
+  } else {
+    error(KIO::ERR_FILE_ALREADY_EXIST, path);
+  }
 
-    if( type != SSH2_FXP_NAME ) {
-        kError(KIO_SFTP_DB) << "unexpected packet type of " << type;
-        return -1;
-    }
-
-    quint32 count;
-    r >> count;
-    if( count != 1 ) {
-        kError(KIO_SFTP_DB) << "bad number of file attributes for realpath command";
-        return -1;
-    }
-
-    QByteArray linkAddress;
-    r >> linkAddress;
-
-    linkAddress.truncate(linkAddress.size());
-    kDebug(KIO_SFTP_DB) << "link address: " << linkAddress;
-
-    target = remoteEncoding()->decode(linkAddress);
-
-    return SSH2_FX_OK;
+  sftp_attributes_free(sb);
+  return;
 }
 
-int sftpProtocol::sftpSymLink(const QString& _target, const KUrl& dest){
+void sftpProtocol::rename(const KUrl& src, const KUrl& dest, KIO::JobFlags flags) {
+  kDebug(KIO_SFTP_DB) << "rename " << src << " to " << dest;
 
-    QByteArray destPath = remoteEncoding()->encode(dest.path());
-    QByteArray target = remoteEncoding()->encode(_target);
-    uint dlen = destPath.length();
-    uint tlen = target.length();
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    kDebug(KIO_SFTP_DB) << "(" << target << " -> " << destPath << ")";
+  QByteArray qsrc = src.path().toUtf8();
+  QByteArray qdest = dest.path().toUtf8();
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ +
-                    4 /*str length*/ + tlen +
-                    4 /*str length*/ + dlen);
-    s << (quint8)SSH2_FXP_SYMLINK;
-    s << (quint32)id;
-    s.writeBytes(target.data(), tlen);
-    s.writeBytes(destPath.data(), dlen);
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
+  SFTP_ATTRIBUTES *sb = sftp_lstat(sftp_session, qdest.constData());
+  if (sb != NULL) {
+    if (!(flags & KIO::Overwrite)) {
+      if (sb->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+        error(KIO::ERR_DIR_ALREADY_EXIST, dest.url());
+      } else {
+        error(KIO::ERR_FILE_ALREADY_EXIST, dest.url());
+      }
+      sftp_attributes_free(sb);
+      return;
     }
 
-    if( type != SSH2_FXP_STATUS ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
+    del(dest, sb->type == SSH_FILEXFER_TYPE_DIRECTORY ? true : false);
+  }
+  sftp_attributes_free(sb);
 
-    quint32 code;
-    r >> code;
-    if( code != SSH2_FX_OK ) {
-        kError(KIO_SFTP_DB) << "rename failed with err code " << code;
-    }
+  if (sftp_rename(sftp_session, qsrc.constData(), qdest.constData()) < 0) {
+    reportError(dest, sftp_get_error(sftp_session));
+    return;
+  }
 
-    return code;
+  finished();
 }
 
-/** Stats a file. */
-int sftpProtocol::sftpStat(const KUrl& url, sftpFileAttr& attr) {
+void sftpProtocol::symlink(const QString &target, const KUrl &dest, KIO::JobFlags flags) {
+  kDebug(KIO_SFTP_DB) << "link " << target << "->" << dest
+                      << ", overwrite = " << (flags & KIO::Overwrite)
+                      << ", resume = " << (flags & KIO::Resume);
 
-    kDebug(KIO_SFTP_DB) << url;
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
+  QByteArray t = target.toUtf8();
+  QByteArray d = dest.path().toUtf8();
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
-    s << (quint8)SSH2_FXP_LSTAT;
-    s << (quint32)id;
-    s.writeBytes(path.data(), len);
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
-    }
-
-    if( type == SSH2_FXP_STATUS ) {
-        quint32 errCode;
-        r >> errCode;
-        kError(KIO_SFTP_DB) << "stat failed with code " << errCode;
-        return errCode;
-    }
-
-    if( type != SSH2_FXP_ATTRS ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
-
-    r >> attr;
-    attr.setFilename(url.fileName());
-    kDebug(KIO_SFTP_DB) << attr;
-
-    // If the stat'ed resource is a symlink, perform a recursive stat
-    // to determine the actual destination's type (file/dir).
-    if( S_ISLNK(attr.permissions()) && isSupportedOperation(SSH2_FXP_READLINK) ) {
-
-        QString target;
-        int code = sftpReadLink( url, target );
-
-        if ( code != SSH2_FX_OK ) {
-            kError(KIO_SFTP_DB) << "unable to stat symlink destination";
-            return -1;
+  bool failed = false;
+  if (sftp_symlink(sftp_session, t.constData(), d.constData()) < 0) {
+    if (flags == KIO::Overwrite) {
+      SFTP_ATTRIBUTES *sb = sftp_lstat(sftp_session, d.constData());
+      if (sb == NULL) {
+        failed = true;
+      } else {
+        if (sftp_unlink(sftp_session, d.constData()) < 0) {
+          failed = true;
+        } else {
+          if (sftp_symlink(sftp_session, t.constData(), d.constData()) < 0) {
+            failed = true;
+          }
         }
-
-        kDebug(KIO_SFTP_DB) << "resource is a symlink that points to " << target;
-
-        KUrl dest( url );
-        if( target[0] == '/' )
-            dest.setPath(target);
-        else
-            dest.setFileName(target);
-
-        dest.cleanPath();
-
-        // Ignore symlinks that point to themselves...
-        if ( dest != url ) {
-
-            sftpFileAttr attr2 (remoteEncoding());
-            (void) sftpStat(dest, attr2);
-
-            if (attr2.linkType() == 0)
-                attr.setLinkType(attr2.fileType());
-            else
-                attr.setLinkType(attr2.linkType());
-
-            attr.setLinkDestination(target);
-            attr.setPermissions(attr2.permissions());
-        }
+      }
+      sftp_attributes_free(sb);
     }
+  }
 
-    return SSH2_FX_OK;
+  if (failed) {
+    reportError(dest, sftp_get_error(sftp_session));
+    return;
+  }
+
+  finished();
 }
 
+void sftpProtocol::chmod(const KUrl& url, int permissions) {
+  kDebug(KIO_SFTP_DB) << "change permission of " << url << " to " << QString::number(permissions);
 
-int sftpProtocol::sftpOpen(const KUrl& url, const quint32 pflags,
-                           const sftpFileAttr& attr, QByteArray& handle) {
-    kDebug(KIO_SFTP_DB) << url;
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    QByteArray path = remoteEncoding()->encode(url.path());
-    uint len = path.length();
+  QByteArray path = url.path().toUtf8();
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
+  if (sftp_chmod(sftp_session, path.constData(), permissions) < 0) {
+    reportError(url, sftp_get_error(sftp_session));
+    return;
+  }
 
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-    s << (quint32)(1 /*type*/ + 4 /*id*/ +
-                    4 /*str length*/ + len +
-                    4 /*pflags*/ + attr.size());
-    s << (quint8)SSH2_FXP_OPEN;
-    s << (quint32)id;
-    s.writeBytes(path.data(), len);
-    s << pflags;
-    s << attr;
-
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
-    }
-
-    if( type == SSH2_FXP_STATUS ) {
-        quint32 errCode;
-        r >> errCode;
-        return errCode;
-    }
-
-    if( type != SSH2_FXP_HANDLE ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
-
-    r >> handle;
-    if( handle.size() > 256 ) {
-        kError(KIO_SFTP_DB) << "handle exceeds max length";
-        return -1;
-    }
-
-    kDebug(KIO_SFTP_DB) << "URL handle (" << handle.size() << "): [" << handle << "]";
-    return SSH2_FX_OK;
+  finished();
 }
 
+void sftpProtocol::del(const KUrl &url, bool isfile){
+  kDebug(KIO_SFTP_DB) << "deleting " << (isfile ? "file: " : "directory: ") << url;
 
-int sftpProtocol::sftpRead(const QByteArray& handle, KIO::filesize_t offset, quint32 len, QByteArray& data)
-{
- //   kDebug(KIO_SFTP_DB) << "( offset = " << offset << ", len = " << len << ")";
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
+  openConnection();
+  if (!mConnected) {
+    return;
+  }
 
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-    s << (quint32)(1 /*type*/ + 4 /*id*/ +
-                    4 /*str length*/ + handle.size() +
-                    8 /*offset*/ + 4 /*length*/);
-    s << (quint8)SSH2_FXP_READ;
-    s << (quint32)id;
-    s << handle;
-    s << offset; // we don't have a convienient 64 bit int so set upper int to zero
-    s << len;
+  QByteArray path = url.path().toUtf8();
 
-    putPacket(p);
-    getPacket(p);
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch";
-        return -1;
+  if (isfile) {
+    if (sftp_unlink(sftp_session, path.constData()) < 0) {
+      reportError(url, sftp_get_error(sftp_session));
+      return;
     }
-
-    if( type == SSH2_FXP_STATUS ) {
-        quint32 errCode;
-        r >> errCode;
-        kError(KIO_SFTP_DB) << "Read failed with code " << errCode;
-        return errCode;
+  } else {
+    if (sftp_rmdir(sftp_session, path.constData()) < 0) {
+      reportError(url, sftp_get_error(sftp_session));
+      return;
     }
+  }
 
-    if( type != SSH2_FXP_DATA ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
-
-    r >> data;
-
-    return SSH2_FX_OK;
+  finished();
 }
 
-
-int sftpProtocol::sftpWrite(const QByteArray& handle, KIO::filesize_t offset, const QByteArray& data){
-//    kDebug(KIO_SFTP_DB) << "sftpWrite( offset = " << offset <<
-//        ", data sz = " << data.size() << ")";
-    QByteArray p;
-    QDataStream s(&p, QIODevice::WriteOnly);
-
-    quint32 id, expectedId;
-    id = expectedId = mMsgId++;
-    s << (quint32)(1 /*type*/ + 4 /*id*/ +
-                    4 /*str length*/ + handle.size() +
-                    8 /*offset*/ +
-                    4 /* data size */ + data.size());
-    s << (quint8)SSH2_FXP_WRITE;
-    s << (quint32)id;
-    s << handle;
-    s << offset; // we don't have a convienient 64 bit int so set upper int to zero
-    s << data;
-
-//    kDebug(KIO_SFTP_DB) << "SSH2_FXP_WRITE, id:"
-//        << id << ", handle:" << handle << ", offset:" << offset << ", some data";
-
-//    kDebug(KIO_SFTP_DB) << "Send packet [" << p << "]";
-
-    putPacket(p);
-    getPacket(p);
-
-//    kDebug(KIO_SFTP_DB) << "Received packet [" << p << "]";
-
-    QDataStream r(p);
-    quint8 type;
-
-    r >> type >> id;
-    if( id != expectedId ) {
-        kError(KIO_SFTP_DB) << "sftp packet id mismatch, got "
-            << id << ", expected " << expectedId;
-        return -1;
-    }
-
-    if( type != SSH2_FXP_STATUS ) {
-        kError(KIO_SFTP_DB) << "unexpected message type of " << type;
-        return -1;
-    }
-
-    quint32 code;
-    r >> code;
-    return code;
+void sftpProtocol::slave_status() {
+  kDebug(KIO_SFTP_DB) << "connected to " << mHost << "?: " << mConnected;
+  slaveStatus((mConnected ? mHost : QString()), mConnected);
 }
+
Index: runtime/kioslave/sftp/kio_sftp.h
===================================================================
--- runtime/kioslave/sftp/kio_sftp.h	(revision 1026868)
+++ runtime/kioslave/sftp/kio_sftp.h	(working copy)
@@ -1,36 +1,43 @@
-/***************************************************************************
-                          sftpProtocol.h  -  description
-                             -------------------
-    begin                : Sat Jun 30 20:08:47 CDT 2001
-    copyright            : (C) 2001 by Lucas Fisher
-    email                : ljfisher@purdue.edu
-***************************************************************************/
+/*
+ * Copyright (c) 2001      Lucas Fisher <ljfisher@purdue.edu>
+ * Copyright (c) 2009      Andreas Schneider <mail@cynapses.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
 
-/***************************************************************************
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- ***************************************************************************/
 #ifndef __kio_sftp_h__
 #define __kio_sftp_h__
 
-
-#include <QObject>
-
 #include <kurl.h>
 #include <kio/global.h>
 #include <kio/slavebase.h>
 #include <kdebug.h>
 
-#include "process.h"
-#include "sftpfileattr.h"
-#include "ksshprocess.h"
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
 
+// How big should each data packet be? Definitely not bigger than 64kb or
+// you will overflow the 2 byte size variable in a sftp packet.
+#define MAX_XFER_BUF_SIZE 60 * 1024
 #define KIO_SFTP_DB 7120
 
+namespace KIO {
+  class AuthInfo;
+}
 
 class sftpProtocol : public KIO::SlaveBase
 {
@@ -38,20 +45,20 @@
 public:
   sftpProtocol(const QByteArray &pool_socket, const QByteArray &app_socket);
   virtual ~sftpProtocol();
-  virtual void setHost(const QString& h, quint16 port, const QString& user, const QString& pass);
-  virtual void get(const KUrl& url);
-  virtual void listDir(const KUrl& url) ;
-  virtual void mimetype(const KUrl& url);
-  virtual void stat(const KUrl& url);
+  virtual void setHost(const QString &h, quint16 port, const QString& user, const QString& pass);
+  virtual void get(const KUrl &url);
+  virtual void listDir(const KUrl &url) ;
+  virtual void mimetype(const KUrl &url);
+  virtual void stat(const KUrl &url);
   virtual void copy(const KUrl &src, const KUrl &dest, int permissions, KIO::JobFlags flags);
-  virtual void put(const KUrl& url, int permissions, KIO::JobFlags flags);
+  virtual void put(const KUrl &url, int permissions, KIO::JobFlags flags);
   virtual void closeConnection();
   virtual void slave_status();
   virtual void del(const KUrl &url, bool isfile);
-  virtual void chmod(const KUrl& url, int permissions);
-  virtual void symlink(const QString& target, const KUrl& dest, KIO::JobFlags flags);
-  virtual void rename(const KUrl& src, const KUrl& dest, KIO::JobFlags flags);
-  virtual void mkdir(const KUrl&url, int permissions);
+  virtual void chmod(const KUrl &url, int permissions);
+  virtual void symlink(const QString &target, const KUrl &dest, KIO::JobFlags flags);
+  virtual void rename(const KUrl &src, const KUrl &dest, KIO::JobFlags flags);
+  virtual void mkdir(const KUrl &url, int permissions);
   virtual void openConnection();
 
   // KIO::FileJob interface
@@ -61,6 +68,10 @@
   virtual void seek(KIO::filesize_t offset);
   virtual void close();
 
+  // libssh authentication callback (note that this is called by the
+  // global ::auth_callback() call.
+  int auth_callback(const char *prompt, char *buf, size_t len,
+    int echo, int verify, void *userdata);
 private: // Private variables
   /** True if ioslave is connected to sftp server. */
   bool mConnected;
@@ -71,20 +82,23 @@
   /** Port we are connected to. */
   int mPort;
 
-  /** Ssh process to which we send the sftp packets. */
-  KSshProcess ssh;
+  /** The ssh session for the connection */
+  SSH_SESSION *ssh_session;
 
+  /** The sftp session for the connection */
+  SFTP_SESSION *sftp_session;
+
   /** Username to use when connecting */
   QString mUsername;
 
   /** User's password */
   QString mPassword;
 
-  /** Message id of the last sftp packet we sent. */
-  unsigned int mMsgId;
+  /** The open file */
+  SFTP_FILE *mOpenFile;
 
-  /** Type of packet we are expecting to receive next. */
-  unsigned char mExpected;
+  /** The open URL */
+  KUrl mOpenUrl;
 
   /** Version of the sftp protocol we are using. */
   int sftpVersion;
@@ -103,60 +117,15 @@
   KIO::filesize_t openOffset;
 
 private: // private methods
-  bool getPacket(QByteArray& msg);
 
-   /* Type is a sftp packet type found in .sftp.h'.
-   * Example: SSH2_FXP_READLINK, SSH2_FXP_RENAME, etc.
-   *
-   * Returns true if the type is supported by the sftp protocol
-   * version negotiated by the client and server (sftpVersion).
-   */
-  bool isSupportedOperation(int type);
-  /** Used to have the server canonicalize any given path name to an absolute path.
-      This is useful for converting path names containing ".." components or relative
-      pathnames without a leading slash into absolute paths.
-      Returns the canonicalized url. */
-  int sftpRealPath(const KUrl& url, KUrl& newUrl);
+  int authenticateKeyboardInteractive(KIO::AuthInfo &info);
 
-  /** Send an sftp packet to stdin of the ssh process. */
-  bool putPacket(QByteArray& p);
-  /** Process SSH_FXP_STATUS packets. */
-  void processStatus(quint8, const QString& message = QString());
-  /** Process SSH_FXP_STATUS packes and return the result. */
-  Status doProcessStatus(quint8, const QString& message = QString());
-  /** Opens a directory handle for url.path. Returns true if succeeds. */
-  int sftpOpenDirectory(const KUrl& url, QByteArray& handle);
-  /** Closes a directory or file handle. */
-  int sftpClose(const QByteArray& handle);
-  /** Send a sftp command to rename a file or directory. */
-  int sftpRename(const KUrl& src, const KUrl& dest);
-  /** Set a files attributes. */
-  int sftpSetStat(const KUrl& url, const sftpFileAttr& attr);
-  /** Sends a sftp command to remove a file or directory. */
-  int sftpRemove(const KUrl& url, bool isfile);
-  /** Creates a symlink named dest to target. */
-  int sftpSymLink(const QString& target, const KUrl& dest);
-  /** Get directory listings. */
-  int sftpReadDir(const QByteArray& handle, const KUrl& url);
-  /** Retrieves the destination of a link. */
-  int sftpReadLink(const KUrl& url, QString& target);
-  /** Stats a file. */
-  int sftpStat(const KUrl& url, sftpFileAttr& attr);
-  /** No descriptions */
-  int sftpOpen(const KUrl& url, const quint32 pflags, const sftpFileAttr& attr, QByteArray& handle);
-  /** No descriptions */
-  int sftpRead(const QByteArray& handle, KIO::filesize_t offset, quint32 len, QByteArray& data);
-  /** No descriptions */
-  int sftpWrite(const QByteArray& handle, KIO::filesize_t offset, const QByteArray& data);
+  void reportError(const KUrl &url, const int err);
 
-  /** Performs faster upload when the source is a local file... */
-  void sftpCopyPut(const KUrl& src, const KUrl& dest, int mode, KIO::JobFlags flags);
-  /** Performs faster download when the destination is a local file... */
-  void sftpCopyGet(const KUrl& dest, const KUrl& src, int mode, KIO::JobFlags flags);
+  bool createUDSEntry(const QString &filename, const QByteArray &path,
+      KIO::UDSEntry &entry, short int details);
 
-  /** Read a file. This is used by get(), copy(), and mimetype(). */
-  Status sftpGet( const KUrl& src, KIO::filesize_t offset = 0, int fd = -1, bool abortAfterMimeType = false);
-  /** Write a file */
-  void sftpPut( const KUrl& dest, int permissions, KIO::JobFlags flags, int fd = -1);
+  QString canonicalizePath(const QString &path);
 };
+
 #endif
Index: runtime/kioslave/sftp/TODO
===================================================================
--- runtime/kioslave/sftp/TODO	(revision 1026868)
+++ runtime/kioslave/sftp/TODO	(working copy)
@@ -1,51 +1,4 @@
 TODO:
-- Password caching doesn't work. We get a new dialog box every time a new kio_sftp process starts.
-  Very annoying.
-  
-- Support for use of public keys, maybe ssh-agent, a key management app, etc.
-
-- bug: when changing hostname in locationbar, password dialog pops up at
-  every change even without pressing enter
-  - Bug report submitted to konqueror
-
-- bug: kio_sftp crashes KDE completely when one or more of konq, kio and
-  kio_sftp are compiled with the objprelink patches. None of the other
-  kioslaves do this for me, but it also happened when I recompiled kio_sftp
-  without the preloading, so I'm unsure whether it's a kio_sftp or kio or
-  konq bug. Will investigate some more. (Rob)
-
-COMPLETED
-=========
-
-- Orphaned ssh processes are bad. Shame on me. - Done 
-  Use SIGKILL to kill the ssh process. The ssh client seems to catch 
-  SIGTERM from its parent and ignore them.
-
-- Continuing partial upload - doesn't create .part file.d
-  fixed I think, but I am unable to test resume. kio denies my resume request - Done
-  
-- When typing sftp url into konqi, konqi tries to connect before we finish
-  typing url. Konqi is trying to do directory completion, but we don't 
-  want this when connecting to a remote server the first time. kio_ftp
-  doesn't do this...why? - Done
-  
-- When a connection gets canceled we are leaving ssh processes behind - Done
-
-- Notify the user when a new host key is received. - Done
-
-- Warn the user when the host key changes. - Done
-
-- Add support for other versions of OpenSSH and SSH. - Done
-
-- Store passwords per user/host pair, not per connection
-  (multiple users per host work now, but when switching views one has to
-   re-enter the login info every single time) - Done! This was a problem with 
-   caching in the ioslave base class and has been fixed.
-
-- Do not move items to trash when deleting (probably a generic kio_slave
-  problem, though, makes no sense to download everything you want to delete)
-  - Done! Fixing in konqueror
-
-- Support for different ports (sftp://user@host:port), this can be done by
-  adding the argument -oPort=7022 (or whatever port) to sftp, could not get
-  it to work properly though - Done
+  Improve keyboard interactive authentication
+  Improve password dialog for passphrase
+  Support for statvfs with libssh 0.4
Index: runtime/kioslave/sftp/CMakeLists.txt
===================================================================
--- runtime/kioslave/sftp/CMakeLists.txt	(revision 1026868)
+++ runtime/kioslave/sftp/CMakeLists.txt	(working copy)
@@ -1,20 +1,13 @@
-add_subdirectory(tests)
-
 ########### next target ###############
 
-set(kio_sftp_PART_SRCS 
-   process.cpp 
-   atomicio.cpp 
-   kio_sftp.cpp 
-   sftpfileattr.cpp 
-   ksshprocess.cpp )
+set(kio_sftp_PART_SRCS
+   kio_sftp.cpp
+)
 
+include_directories(${LIBSSH_INCLUDE_DIRS})
 
 kde4_add_plugin(kio_sftp ${kio_sftp_PART_SRCS})
-target_link_libraries(kio_sftp ${KDE4_KIO_LIBS} )
-if(NOT WIN32)
-    target_link_libraries(kio_sftp ${KDE4_KPTY_LIBS})
-endif(NOT WIN32)
+target_link_libraries(kio_sftp ${KDE4_KIO_LIBS} ${LIBSSH_LIBRARIES})
 
 install(TARGETS kio_sftp  DESTINATION ${PLUGIN_INSTALL_DIR} )
 
Index: runtime/kioslave/CMakeLists.txt
===================================================================
--- runtime/kioslave/CMakeLists.txt	(revision 1026868)
+++ runtime/kioslave/CMakeLists.txt	(working copy)
@@ -7,6 +7,9 @@
 macro_log_feature(SAMBA_FOUND "Samba" "the SMB client library, a version with smbc_set_context() and smbc_option_set()" "http://www.samba.org" FALSE "" "Needed to build the SMB kioslave")
 endif(NOT WIN32)
 
+macro_optional_find_package(LibSSH 0.3.4)
+macro_log_feature(LIBSSH_FOUND "libssh" "the SSH library with SFTP support" "http://www.libssh.org/" FALSE "" "Needed to build the SFTP kioslave")
+
 add_subdirectory( about )
 add_subdirectory( bookmarks )
 add_subdirectory( cgi )
@@ -21,18 +24,22 @@
 endif(NOT WIN32)
 add_subdirectory( remote )
 add_subdirectory( desktop )
-add_subdirectory( sftp )
 add_subdirectory( fish )
 add_subdirectory( thumbnail )
 add_subdirectory( docfilter )
+# msvc doesn't compile
+if (LIBSSH_FOUND AND NOT MSVC)
+  add_subdirectory(sftp)
+endif (LIBSSH_FOUND AND NOT MSVC)
 
 if(NOT WIN32)
    add_subdirectory( floppy )
    add_subdirectory( finger )
    add_subdirectory( man )
    add_subdirectory( nfs )  
+endif(NOT WIN32)
 
-   if(SAMBA_FOUND)
-      add_subdirectory(smb)
-   endif(SAMBA_FOUND)
-endif(NOT WIN32)
+if(SAMBA_FOUND OR WIN32)
+    add_subdirectory(smb)
+endif(SAMBA_FOUND OR WIN32)
+
