Index: lib/ssh.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/ssh.c,v
retrieving revision 1.24
diff -u -r1.24 ssh.c
--- lib/ssh.c	24 Mar 2007 17:23:01 -0000	1.24
+++ lib/ssh.c	29 Mar 2007 01:37:38 -0000
@@ -157,6 +157,10 @@
 #define LIBSSH2_SFTP_S_IXOTH S_IXOTH
 #endif
 
+/* Local functions: */
+static CURLcode Curl_sftp_sendquote(struct connectdata *conn,
+                              struct curl_slist *quote);
+
 static LIBSSH2_ALLOC_FUNC(libssh2_malloc);
 static LIBSSH2_REALLOC_FUNC(libssh2_realloc);
 static LIBSSH2_FREE_FUNC(libssh2_free);
@@ -948,6 +952,15 @@
 
   Curl_safefree(sftp->homedir);
   sftp->homedir = NULL;
+  
+  /* Before we shut down, see if there are any post-quote commands to send: */
+  if(!status && !premature && conn->data->set.postquote)
+  {
+	  CURLcode result = Curl_sftp_sendquote(conn, conn->data->set.postquote);
+	  
+	  if (result != CURLE_OK)
+		  return result;
+  }
 
   if (sftp->sftp_handle) {
     if (libssh2_sftp_close(sftp->sftp_handle) < 0) {
@@ -996,6 +1009,235 @@
   return nwrite;
 }
 
+
+/* The get_pathname() function is being borrowed from OpenSSH sftp.c version 4.6p1. */
+/*
+ * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+static int
+get_pathname(const char **cpp, char **path)
+{
+	const char *cp = *cpp, *end;
+	char quot;
+	u_int i, j;
+	const char *WHITESPACE = " \t\r\n";
+	
+	cp += strspn(cp, WHITESPACE);
+	if (!*cp) {
+		*cpp = cp;
+		*path = NULL;
+		return (0);
+	}
+	
+	*path = malloc(strlen(cp) + 1);
+	if (*path == NULL)
+	{
+		return CURLE_OUT_OF_MEMORY;
+	}
+	
+	/* Check for quoted filenames */
+	if (*cp == '\"' || *cp == '\'') {
+		quot = *cp++;
+		
+		/* Search for terminating quote, unescape some chars */
+		for (i = j = 0; i <= strlen(cp); i++) {
+			if (cp[i] == quot) {	/* Found quote */
+				i++;
+				(*path)[j] = '\0';
+				break;
+			}
+			if (cp[i] == '\0') {	/* End of string */
+				/*error("Unterminated quote");*/
+				goto fail;
+			}
+			if (cp[i] == '\\') {	/* Escaped characters */
+				i++;
+				if (cp[i] != '\'' && cp[i] != '\"' &&
+				    cp[i] != '\\') {
+					/*error("Bad escaped character '\\%c'",
+						  cp[i]);*/
+					goto fail;
+				}
+			}
+			(*path)[j++] = cp[i];
+		}
+		
+		if (j == 0) {
+			/*error("Empty quotes");*/
+			goto fail;
+		}
+		*cpp = cp + i + strspn(cp + i, WHITESPACE);
+	} else {
+		/* Read to end of filename */
+		end = strpbrk(cp, WHITESPACE);
+		if (end == NULL)
+			end = strchr(cp, '\0');
+		*cpp = end + strspn(end, WHITESPACE);
+		
+		memcpy(*path, cp, end - cp);
+		(*path)[end - cp] = '\0';
+	}
+	return (0);
+	
+	fail:
+		free(*path);
+		*path = NULL;
+		return CURLE_FTP_QUOTE_ERROR;
+}
+
+
+static CURLcode Curl_sftp_sendquote(struct connectdata *conn,
+									struct curl_slist *quote)
+{
+	struct curl_slist *item;
+	char *path1, *path2;
+	const char *cp;
+	LIBSSH2_SFTP_ATTRIBUTES attrs;
+	int err;
+	
+	item = quote;
+	while (item)
+	{
+		if (item->data)
+		{
+			cp = item->data;
+			path1 = NULL;
+			path2 = NULL;
+			bzero(&attrs, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+			
+			/* SFTP is a binary protocol, so we don't send text commands to the server.
+			   Instead, we scan for commands for commands used by OpenSSH's sftp program
+			   and call the appropriate libssh2 functions. */
+			if (strncasecmp(item->data, "chgrp", 5) == 0 || strncasecmp(item->data, "chmod", 5) == 0 || strncasecmp(item->data, "chown", 5) == 0)	/* attributes change */
+			{
+				cp = strchr(cp, ' ');
+				if (cp == NULL)
+					return CURLE_FTP_QUOTE_ERROR;
+				err = get_pathname(&cp, &path1);	/* "path1" contains the mode to set */
+				if (err)
+					return err;
+				err = get_pathname(&cp, &path2);	/* get the destination */
+				if (err)
+				{
+					free(path1);
+					return err;
+				}
+				if (libssh2_sftp_stat(conn->data->reqdata.proto.ssh->sftp_session, path2, &attrs) != 0)	/* get those attributes */
+				{
+					free(path1);
+					free(path2);
+					return CURLE_FTP_QUOTE_ERROR;
+				}
+				
+				/* Now set the new attributes... */
+				if (strncasecmp(item->data, "chgrp", 5) == 0)
+				{
+					attrs.gid = strtol(path1, NULL, 10);
+				}
+				else if (strncasecmp(item->data, "chmod", 5))
+				{
+					attrs.permissions = strtol(path1, NULL, 8);	/* permissions are octal */
+				}
+				else if (strncasecmp(item->data, "chown", 5))
+				{
+					attrs.uid = strtol(path1, NULL, 10);
+				}
+				
+				/* Now send the completed structure... */
+				if (libssh2_sftp_setstat(conn->data->reqdata.proto.ssh->sftp_session, path2, &attrs) != 0)
+				{
+					free(path1);
+					free(path2);
+					return CURLE_FTP_QUOTE_ERROR;
+				}
+			}
+			else if (strncasecmp(item->data, "ln", 2) == 0 || strncasecmp(item->data, "symlink", 7) != 0)	/* symbolic linking */
+			{
+				cp = strchr(cp, ' ');
+				if (cp == NULL)
+					return CURLE_FTP_QUOTE_ERROR;
+				err = get_pathname(&cp, &path1);	/* get the source */
+				if (err)
+					return err;
+				err = get_pathname(&cp, &path2);	/* get the destination */
+				if (err)
+				{
+					free(path1);
+					return err;
+				}
+				if (libssh2_sftp_symlink(conn->data->reqdata.proto.ssh->sftp_session, path1, path2) != 0)
+				{
+					free(path1);
+					free(path2);
+					return CURLE_FTP_QUOTE_ERROR;
+				}
+			}
+			else if (strncasecmp(item->data, "mkdir", 5) == 0)	/* create new directory */
+			{
+				cp = strchr(cp, ' ');
+				if (cp == NULL)
+					return CURLE_FTP_QUOTE_ERROR;
+				err = get_pathname(&cp, &path1);
+				if (err)
+					return err;
+				if (libssh2_sftp_mkdir(conn->data->reqdata.proto.ssh->sftp_session, path1, 0744) != 0)
+				{
+					free(path1);
+					return CURLE_FTP_QUOTE_ERROR;
+				}
+			}
+			else if (strncasecmp(item->data, "rm", 2) == 0)	/* delete file */
+			{
+				cp = strchr(cp, ' ');
+				if (cp == NULL)
+					return CURLE_FTP_QUOTE_ERROR;
+				err = get_pathname(&cp, &path1);
+				if (err)
+					return err;
+				if (libssh2_sftp_unlink(conn->data->reqdata.proto.ssh->sftp_session, path1) != 0)
+				{
+					free(path1);
+					return CURLE_FTP_QUOTE_ERROR;
+				}
+			}
+			else if (strncasecmp(item->data, "rmdir", 5) == 0)	/* delete directory */
+			{
+				cp = strchr(cp, ' ');
+				if (cp == NULL)
+					return CURLE_FTP_QUOTE_ERROR;
+				err = get_pathname(&cp, &path1);
+				if (err)
+					return err;
+				if (libssh2_sftp_rmdir(conn->data->reqdata.proto.ssh->sftp_session, path1) != 0)
+				{
+					free(path1);
+					return CURLE_FTP_QUOTE_ERROR;
+				}
+			}
+			
+			if (path1)
+				free(path1);
+			if (path2)
+				free(path2);
+		}
+		item = item->next;
+	}
+	return CURLE_OK;
+}
+
+
 /*
  * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
  * a regular CURLcode value.

