? .gdb_history
? cURL
? compile
? libssh2_patch.txt
? libssh2_patch.zip
? makePatch.sh
? src/.gdb_history
Index: lib/sendf.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/sendf.c,v
retrieving revision 1.113
diff -u -r1.113 sendf.c
--- lib/sendf.c	6 Nov 2006 18:28:34 -0000	1.113
+++ lib/sendf.c	10 Nov 2006 02:21:14 -0000
@@ -335,6 +335,9 @@
   else if (conn->protocol & PROT_SCP) {
     bytes_written = Curl_scp_send(conn, num, mem, len);
   }
+  else if (conn->protocol & PROT_SFTP) {
+    bytes_written = Curl_sftp_send(conn, num, mem, len);
+  }
 #endif /* !USE_LIBSSH2 */
   else {
     if(conn->sec_complete)
@@ -516,6 +519,9 @@
     /* TODO: return CURLE_OK also for nread <= 0 
              read failures and timeouts ? */
   }
+  else if (conn->protocol & PROT_SFTP) {
+    nread = Curl_sftp_recv(conn, num, conn->master_buffer, bytesfromsocket);
+  }
 #endif /* !USE_LIBSSH2 */
   else {
     if(conn->sec_complete)
Index: lib/ssh.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/ssh.c,v
retrieving revision 1.3
diff -u -r1.3 ssh.c
--- lib/ssh.c	6 Nov 2006 18:26:36 -0000	1.3
+++ lib/ssh.c	10 Nov 2006 02:21:15 -0000
@@ -51,6 +51,10 @@
 #include <sys/stat.h>
 #endif
 
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
 #ifdef WIN32
 
 #else /* probably some kind of unix */
@@ -401,7 +405,7 @@
    */
   conn->sockfd = sock;
   conn->writesockfd = CURL_SOCKET_BAD;
-
+  
   *done = TRUE;
   return CURLE_OK;
 }
@@ -432,8 +436,8 @@
   }
   else {
     /*
-     * We must check the remote file, if it is a directory I have no idea
-     * what I will do until the scp "-r" option is supported
+     * We must check the remote file, if it is a directory no vaules will
+     * be set in sb
      */
     memset(&sb, 0, sizeof(struct stat));
     if ((scp->scpChannel = libssh2_scp_recv(scp->scpSession, scp->path, &sb))
@@ -506,4 +510,492 @@
   return nread;
 }
 
+/*
+ * =============== SFTP ===============
+ */
+
+static CURLcode sftp_init(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct SFTPPROTO *sftp;
+  if (data->reqdata.proto.sftp)
+    return CURLE_OK;
+  
+  sftp = (struct SFTPPROTO *)calloc(sizeof(struct SFTPPROTO), 1);
+  if (!sftp)
+    return CURLE_OUT_OF_MEMORY;
+  
+  data->reqdata.proto.sftp = sftp;
+  
+  /* get some initial data into the sftp struct */
+  sftp->bytecountp = &data->reqdata.keep.bytecount;
+  
+  /* no need to duplicate them, this connectdata struct won't change */
+  sftp->user = conn->user;
+  sftp->passwd = conn->passwd;
+  
+  sftp->errorstr = NULL;
+  
+  sftp->sftpSession = NULL;
+  sftp->sftpChannel = NULL;
+  sftp->sftpHandle = NULL;
+  sftp->sftpTransferHandle = NULL;
+  
+  return CURLE_OK;
+}
+
+/*
+ * Curl_sftp_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time.
+ */
+CURLcode Curl_sftp_connect(struct connectdata *conn, bool *done)
+{
+  int i;
+  struct SFTPPROTO *sftp;
+  const char *fingerprint;
+  const char *authlist;
+  char *home;
+  char rsa_pub[PATH_MAX];
+  char rsa[PATH_MAX];
+  char tempHome[PATH_MAX];
+  curl_socket_t sock;
+  char *real_path;
+  char *working_path;
+  bool authed = FALSE;
+  CURLcode result;
+  struct SessionHandle *data = conn->data;
+  
+  result = sftp_init(conn);
+  if (result)
+    return result;
+  
+  rsa_pub[0] = rsa[0] = '\0';
+  
+  sftp = data->reqdata.proto.sftp;
+  
+  working_path = curl_easy_unescape(data, data->reqdata.path, 0, NULL);
+  if (!working_path)
+    return CURLE_OUT_OF_MEMORY;
+  
+#ifdef CURL_LIBSSH2_DEBUG
+  if (sftp->user) {
+    infof(data, "User: %s\n", sftp->user);
+  }
+  if (sftp->passwd) {
+    infof(data, "Password: %s\n", sftp->passwd);
+  }
+#endif /* CURL_LIBSSH2_DEBUG */
+  sock = conn->sock[FIRSTSOCKET];
+  sftp->sftpSession = libssh2_session_init_ex(libssh2_malloc, libssh2_free,
+                                            libssh2_realloc, NULL);
+  if (sftp->sftpSession == NULL) {
+    failf(data, "Failure initialising ssh session\n");
+    return CURLE_FAILED_INIT;
+  }
+#ifdef CURL_LIBSSH2_DEBUG
+  infof(data, "Socket: %d\n", sock);
+#endif /* CURL_LIBSSH2_DEBUG */
+  
+  if (libssh2_session_startup(sftp->sftpSession, sock)) {
+    failf(data, "Failure establishing ssh session\n");
+    return CURLE_FAILED_INIT;
+  }
+  
+  /*
+   * Before we authenticate we should check the hostkey's fingerprint against
+   * our known hosts. How that is handled (reading from file, whatever) is
+   * up to us. As for know not much is implemented, besides showing how to
+   * get the fingerprint.
+   */
+  fingerprint = libssh2_hostkey_hash(sftp->sftpSession,
+                                     LIBSSH2_HOSTKEY_HASH_MD5);
+  
+#ifdef CURL_LIBSSH2_DEBUG
+  /* The fingerprint points to static storage (!), don't free() it. */
+  for (i = 0; i < 16; i++) {
+    infof(data, "%02X ", (unsigned char) fingerprint[i]);
+  }
+  infof(data, "\n");
+#endif /* CURL_LIBSSH2_DEBUG */
+  
+  /* TBD - methods to check the host keys need to be done */
+  
+  /*
+   * Figure out authentication methods
+   * NB: As soon as we have provided a username to an openssh server we must
+   * never change it later. Thus, always specify the correct username here,
+   * even though the libssh2 docs kind of indicate that it should be possible
+   * to get a 'generic' list (not user-specific) of authentication methods,
+   * presumably with a blank username. That won't work in my experience.
+   * So always specify it here.
+   */
+  authlist = libssh2_userauth_list(sftp->sftpSession, sftp->user,
+                                   strlen(sftp->user));
+  
+  /*
+   * Check the supported auth types in the order I feel is most secure with the
+   * requested type of authentication
+   */
+  if ((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
+      (strstr(authlist, "publickey") != NULL)) {
+    /* To ponder about: should really the lib be messing about with the HOME
+    environment variable etc? */
+    home = curl_getenv("HOME");
+    
+    if (data->set.ssh_public_key)
+      snprintf(rsa_pub, sizeof(rsa_pub), "%s", data->set.ssh_public_key);
+    else if (home)
+      snprintf(rsa_pub, sizeof(rsa_pub), "%s/.ssh/id_dsa.pub", home);
+    
+    if(data->set.ssh_private_key)
+      snprintf(rsa, sizeof(rsa), "%s", data->set.ssh_private_key);
+    else if (home)
+      snprintf(rsa, sizeof(rsa), "%s/.ssh/id_dsa", home);
+    
+    curl_free(home);
+    
+    if (rsa_pub[0]) {
+      /* The function below checks if the files exists, no need to stat() here.
+      */
+      if (libssh2_userauth_publickey_fromfile(sftp->sftpSession, sftp->user,
+                                              rsa_pub, rsa, "") == 0) {
+        authed = TRUE;
+      }
+    }
+  }
+  if (!authed &&
+      (data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+      (strstr(authlist, "password") != NULL)) {
+    if (libssh2_userauth_password(sftp->sftpSession, sftp->user, sftp->passwd)
+        == 0) {
+      authed = TRUE;
+    }
+  }
+  if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+      (strstr(authlist, "hostbased") != NULL)) {
+  }
+  if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+      && (strstr(authlist, "keyboard-interactive") != NULL)) {
+    /* Authentication failed. Continue with keyboard-interactive now. */
+    auth.user = sftp->user;
+    auth.pw   = sftp->passwd;
+    if (libssh2_userauth_keyboard_interactive_ex(sftp->sftpSession, sftp->user,
+                                                 strlen(sftp->user),
+                                                 &kbd_callback) == 0) {
+      authed = TRUE;
+    }
+  }
+  
+  if (!authed) {
+    failf(data, "Authentication failure\n");
+    return CURLE_FAILED_INIT;
+  }
+  
+  /*
+   * At this point we have an authenticated ssh session.
+   */
+  conn->sockfd = sock;
+  conn->writesockfd = CURL_SOCKET_BAD;
+  
+  /*
+   * Start the libssh2 sftp session
+   */
+  sftp->sftpHandle = libssh2_sftp_init(sftp->sftpSession);
+  if (sftp->sftpHandle == NULL) {
+    failf(data, "Failure initialising sftp session\n");
+    return CURLE_FAILED_INIT;
+  }
+    
+  /*
+   * Get the "home" directory
+   */
+  i = libssh2_sftp_realpath(sftp->sftpHandle, ".", tempHome, PATH_MAX-1);
+  if (i > 0) {
+    /* It seems that this string is not always NULL terminated */
+    tempHome[i] = '\0';
+    if ((sftp->homeDirectory = (char *)malloc(i + 1)) == NULL) {
+      return CURLE_OUT_OF_MEMORY;
+    }
+    memcpy(sftp->homeDirectory, tempHome, i+1);
+  } else {
+    /* Return the error type */
+    i = libssh2_sftp_last_error(sftp->sftpHandle);
+    fprintf(stderr, "error = %d\n", i);
+  }
+  
+  real_path = (char *)malloc(strlen(working_path)+1);
+  if (real_path == NULL) {
+    Curl_safefree(working_path);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  /* Check for /~/ , indicating realative to the users home directory */
+  if (working_path[1] == '~') {
+    real_path = (char *)malloc(strlen(sftp->homeDirectory) +
+                                   strlen(working_path)+1);
+    if (real_path == NULL) {
+      Curl_safefree(working_path);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    /* It is referenced to the home directory, so strip the leading '/' */
+    memcpy(real_path, sftp->homeDirectory, strlen(sftp->homeDirectory));
+	real_path[strlen(sftp->homeDirectory)] = '/';
+    if (strlen(working_path) > 3) {
+      memcpy(real_path+strlen(sftp->homeDirectory)+1, working_path+3, 
+                     1+strlen(working_path)-3);
+    }
+  }
+  else {
+    real_path = (char *)malloc(strlen(working_path)+1);
+    if (real_path == NULL) {
+      Curl_safefree(working_path);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    memcpy(real_path, working_path, 1+strlen(working_path));
+  }
+  
+  Curl_safefree(working_path);
+  sftp->path = real_path;
+  
+  *done = TRUE;
+  return CURLE_OK;
+}
+
+CURLcode Curl_sftp_do(struct connectdata *conn, bool *done)
+{
+  struct SFTPPROTO *sftp = conn->data->reqdata.proto.sftp;
+  CURLcode res = CURLE_OK;
+  
+  *done = TRUE; /* unconditionally */
+  
+  if (conn->data->set.upload) {
+    /*
+     * NOTE!!!  libssh2 requires that the destination path is a full path
+     *          that includes the destination file and name OR ends in a "/" .
+     *          If this is not done the destination file will be named the
+     *          same name as the last directory in the path.
+     */
+    sftp->sftpTransferHandle = libssh2_sftp_open(sftp->sftpHandle, sftp->path, LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+    if (sftp->sftpTransferHandle == NULL) {
+      return CURLE_FAILED_INIT;
+    }
+    conn->writesockfd = conn->sockfd;
+    conn->sockfd = CURL_SOCKET_BAD;
+  }
+  else {
+    if (sftp->path[strlen(sftp->path)-1] == '/') {
+      /*
+       * This is a directory that we are trying to get, so produce a directory listing
+       */
+      LIBSSH2_SFTP_ATTRIBUTES attrs;
+      char filename[PATH_MAX+1];
+      int len, totalLen, currLen;
+      char *line;
+      
+      if ((sftp->sftpTransferHandle = libssh2_sftp_opendir(sftp->sftpHandle, sftp->path)) == NULL)
+      {
+        return CURLE_SSH;
+      }
+      while ((len = libssh2_sftp_readdir(sftp->sftpTransferHandle, filename, PATH_MAX, &attrs)) > 0)
+      {
+        filename[len] = '\0';
+        
+        totalLen = 80 + len;
+        if ((line = (char *)malloc(totalLen)) == NULL) {
+          return CURLE_OUT_OF_MEMORY;
+        }
+        if (attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID)
+        {
+          currLen = snprintf(line, totalLen, "----------   1 %5d %5d", attrs.uid, attrs.gid);
+        } else {
+          currLen = snprintf(line, totalLen, "----------   1 %5d %5d", 0, 0);
+        }
+        if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS)
+        {
+          if ((attrs.permissions & S_IFMT) == S_IFDIR)
+          {
+            line[0] = 'd';
+          }
+          else if ((attrs.permissions & S_IFMT) == S_IFLNK)
+          {
+            line[0] = 'l';
+          }
+          else if ((attrs.permissions & S_IFMT) == S_IFSOCK)
+          {
+            line[0] = 's';
+          }
+          else if ((attrs.permissions & S_IFMT) == S_IFCHR)
+          {
+            line[0] = 'c';
+          }
+          else if ((attrs.permissions & S_IFMT) == S_IFBLK)
+          {
+            line[0] = 'b';
+          }
+          if (attrs.permissions & S_IRUSR)
+          {
+            line[1] = 'r';
+          }
+          if (attrs.permissions & S_IWUSR)
+          {
+            line[2] = 'w';
+          }
+          if (attrs.permissions & S_IXUSR)
+          {
+            line[3] = 'x';
+          }
+          if (attrs.permissions & S_IRGRP)
+          {
+            line[4] = 'r';
+          }
+          if (attrs.permissions & S_IWGRP)
+          {
+            line[5] = 'w';
+          }
+          if (attrs.permissions & S_IXGRP)
+          {
+            line[6] = 'x';
+          }
+          if (attrs.permissions & S_IROTH)
+          {
+            line[7] = 'r';
+          }
+          if (attrs.permissions & S_IWOTH)
+          {
+            line[8] = 'w';
+          }
+          if (attrs.permissions & S_IXOTH)
+          {
+            line[9] = 'x';
+          }
+        }
+        if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE)
+        {
+          currLen += snprintf(line+currLen, totalLen-currLen, "%11lld", attrs.filesize);
+        }
+        if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME)
+        {
+          const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+          struct tm *nowParts;
+          time_t now, remoteTime;
+          
+          now = time(NULL);
+          remoteTime = (time_t)attrs.mtime;
+          nowParts = localtime(&remoteTime);
+          
+          if ((time_t)attrs.mtime > (now - (3600 * 24 * 180) /* about 6 months */)) {
+            currLen += snprintf(line+currLen, totalLen-currLen, " %s %2d %2d:%02d", months[nowParts->tm_mon], nowParts->tm_mday, nowParts->tm_hour, nowParts->tm_min);
+          } else {
+            currLen += snprintf(line+currLen, totalLen-currLen, " %s %2d %5d", months[nowParts->tm_mon], nowParts->tm_mday, 1900+nowParts->tm_year);
+          }
+        }
+        currLen += snprintf(line+currLen, totalLen-currLen, " %s", filename);
+        if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && ((attrs.permissions & S_IFMT) == S_IFLNK))
+        {
+          char linkPath[PATH_MAX + 1];
+          
+          snprintf(linkPath, PATH_MAX, "%s%s", sftp->path, filename);
+          len = libssh2_sftp_readlink(sftp->sftpHandle, linkPath, filename, PATH_MAX);
+          if ((line = realloc(line, totalLen + 4 + len)) == NULL) {
+            return CURLE_OUT_OF_MEMORY;
+          }
+          currLen += snprintf(line+currLen, totalLen-currLen, " -> %s", filename);
+        }
+        
+        infof(conn->data, "%s\n", line);
+        free(line);
+      }
+      libssh2_sftp_closedir(sftp->sftpTransferHandle);
+      
+      /* no data to transfer */
+      res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+    }
+    else {
+      /*
+       * Work on getting the specified file
+       */
+      sftp->sftpTransferHandle = libssh2_sftp_open(sftp->sftpHandle, sftp->path, LIBSSH2_FXF_READ, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+      if (sftp->sftpTransferHandle == NULL) {
+        return CURLE_SSH;
+      }
+#if 0
+      conn->data->reqdata.size = sb.st_size;
+      conn->data->reqdata.maxdownload = sb.st_size;
+#endif
+      conn->data->reqdata.size = 708;
+      conn->data->reqdata.maxdownload = 708;
+    }
+  }
+  
+  return res;
+}
+
+CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status)
+{
+  struct SFTPPROTO *sftp = conn->data->reqdata.proto.sftp;
+  
+  Curl_safefree(sftp->freepath);
+  sftp->freepath = NULL;
+  
+  Curl_safefree(sftp->homeDirectory);
+  sftp->homeDirectory = NULL;
+  
+  if (sftp->sftpTransferHandle) {
+    if (libssh2_sftp_close(sftp->sftpTransferHandle) < 0) {
+      failf(conn->data, "Failed to close libssh2 file\n");
+    }
+  }
+  
+  if (sftp->sftpHandle) {
+    if (libssh2_sftp_shutdown(sftp->sftpHandle) < 0) {
+      failf(conn->data, "Failed to stop libssh2 sftp subsystem\n");
+    }
+  }
+  
+  if (sftp->sftpChannel) {
+    if (libssh2_channel_close(sftp->sftpChannel) < 0) {
+      failf(conn->data, "Failed to stop libssh2 channel subsystem\n");
+    }
+  }
+  
+  if (sftp->sftpSession) {
+    libssh2_session_disconnect(sftp->sftpSession, "Shutdown");
+    libssh2_session_free(sftp->sftpSession);
+  }
+  
+  free(conn->data->reqdata.proto.sftp);
+  conn->data->reqdata.proto.sftp = NULL;
+  Curl_pgrsDone(conn);
+  
+  (void)status; /* unused */
+  
+  return CURLE_OK;
+}
+
+/* return number of received (decrypted) bytes */
+int Curl_sftp_send(struct connectdata *conn, int sockindex,
+                   void *mem, size_t len)
+{
+  ssize_t nwrite;
+  
+  nwrite = libssh2_sftp_write(conn->data->reqdata.proto.sftp->sftpTransferHandle,
+                                 mem, len);
+  (void)sockindex;
+  return nwrite;
+}
+
+/*
+ * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
+ * a regular CURLcode value.
+ */
+int Curl_sftp_recv(struct connectdata *conn, int sockindex,
+                   char *mem, size_t len)
+{
+  ssize_t nread;
+  
+  nread = libssh2_sftp_read(conn->data->reqdata.proto.sftp->sftpTransferHandle,
+                               mem, len);
+  (void)sockindex;
+  return nread;
+}
+
 #endif /* USE_LIBSSH2 */
Index: lib/ssh.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/ssh.h,v
retrieving revision 1.1
diff -u -r1.1 ssh.h
--- lib/ssh.h	2 Nov 2006 21:56:43 -0000	1.1
+++ lib/ssh.h	10 Nov 2006 02:21:15 -0000
@@ -1,5 +1,5 @@
-#ifndef __SFTP_H
-#define __SFTP_H
+#ifndef __SSH_H
+#define __SSH_H
 
 /***************************************************************************
  *                                  _   _ ____  _
@@ -35,6 +35,15 @@
 int Curl_scp_recv(struct connectdata *conn, int sockindex,
                   char *mem, size_t len);
 
-#endif
+CURLcode Curl_sftp_connect(struct connectdata *conn, bool *done);
+CURLcode Curl_sftp_do(struct connectdata *conn, bool *done);
+CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode);
+
+int Curl_sftp_send(struct connectdata *conn, int sockindex,
+                  void *mem, size_t len);
+int Curl_sftp_recv(struct connectdata *conn, int sockindex,
+                  char *mem, size_t len);
 
 #endif /* USE_LIBSSH2 */
+
+#endif /* __SSH_H */
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.565
diff -u -r1.565 url.c
--- lib/url.c	9 Nov 2006 21:54:33 -0000	1.565
+++ lib/url.c	10 Nov 2006 02:21:18 -0000
@@ -3256,7 +3256,22 @@
     return CURLE_UNSUPPORTED_PROTOCOL;
 #endif
   }
-  else {
+  else if (strequal(conn->protostr, "SFTP")) {
+#ifdef USE_LIBSSH2
+    conn->port = PORT_SSH;
+    conn->remote_port = PORT_SSH;
+    conn->protocol = PROT_SFTP;
+    conn->curl_connect = Curl_sftp_connect; /* ssh_connect? */
+    conn->curl_do = Curl_sftp_do;
+    conn->curl_done = Curl_sftp_done;
+    conn->curl_do_more = (Curl_do_more_func)NULL;
+#else
+    failf(data, LIBCURL_NAME
+          " was built without LIBSSH2, scp: not supported!");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+}
+else {
     /* We fell through all checks and thus we don't support the specified
        protocol */
     failf(data, "Unsupported protocol: %s", conn->protostr);
@@ -3422,9 +3437,9 @@
   user[0] =0;   /* to make everything well-defined */
   passwd[0]=0;
 
-  if (conn->protocol & (PROT_FTP|PROT_HTTP|PROT_SCP)) {
-    /* This is a FTP or HTTP URL, we will now try to extract the possible
-     * user+password pair in a string like:
+  if (conn->protocol & (PROT_FTP|PROT_HTTP|PROT_SCP|PROT_SFTP)) {
+    /* This is a FTP, HTTP, SCP or SFTP URL, we will now try to extract the
+     * possible user+password pair in a string like:
      * ftp://user:password@ftp.my.site:8021/README */
     char *ptr=strchr(conn->host.name, '@');
     char *userpass = conn->host.name;
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.308
diff -u -r1.308 urldata.h
--- lib/urldata.h	3 Nov 2006 12:43:56 -0000	1.308
+++ lib/urldata.h	10 Nov 2006 02:21:19 -0000
@@ -413,6 +413,28 @@
 #endif /* USE_LIBSSH2 */
 };
 
+/****************************************************************************
+* SFTP unique setup
+***************************************************************************/
+
+struct SFTPPROTO {
+  curl_off_t *bytecountp;
+  char *user;
+  char *passwd;
+  char *path;                   /* the path we operate on */
+  char *freepath;               /* pointer to the allocated block we must
+                                   free, this might differ from the 'path'
+                                   pointer */
+  char *homeDirectory;
+  char *errorstr;
+#ifdef USE_LIBSSH2
+  LIBSSH2_SESSION       *sftpSession; /* Secure Shell session */
+  LIBSSH2_CHANNEL       *sftpChannel; /* SFTP channel handle */
+  LIBSSH2_SFTP          *sftpHandle;  /* SFTP handle */
+  LIBSSH2_SFTP_HANDLE   *sftpTransferHandle;
+#endif /* USE_LIBSSH2 */
+};
+
 
 /****************************************************************************
  * FILE unique setup
@@ -674,6 +696,7 @@
     void *telnet;        /* private for telnet.c-eyes only */
     void *generic;
     struct SCPPROTO *scp;
+    struct SFTPPROTO *sftp;
   } proto;
 };
 
@@ -709,6 +732,7 @@
 #define PROT_SSL     (1<<10) /* protocol requires SSL */
 #define PROT_TFTP    (1<<11)
 #define PROT_SCP     (1<<12)
+#define PROT_SFTP    (1<<13)
 
   /* 'dns_entry' is the particular host we use. This points to an entry in the
      DNS cache and it will not get pruned while locked. It gets unlocked in
Index: lib/version.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/version.c,v
retrieving revision 1.51
diff -u -r1.51 version.c
--- lib/version.c	2 Nov 2006 21:56:44 -0000	1.51
+++ lib/version.c	10 Nov 2006 02:21:19 -0000
@@ -138,6 +138,7 @@
 
 #ifdef USE_LIBSSH2
   "scp",
+  "sftp",
 #endif
 
   NULL
