- (djm) Sync sftp and scp stuff from OpenBSD:
   - djm@cvs.openbsd.org     2001/02/07 03:55:13
     [sftp-client.c]
     Don't free handles before we are done with them. Based on work from
     Corinna Vinschen <vinschen@redhat.com>. ok markus@
   - djm@cvs.openbsd.org     2001/02/06 22:32:53
     [sftp.1]
     Punctuation fix from Pekka Savola <pekkas@netcore.fi>
   - deraadt@cvs.openbsd.org 2001/02/07 04:07:29
     [sftp.1]
     pretty up significantly
   - itojun@cvs.openbsd.org  2001/02/07 06:49:42
     [sftp.1]
     .Bl-.El mismatch.  markus ok
   - djm@cvs.openbsd.org     2001/02/07 06:12:30
     [sftp-int.c]
     Check that target is a directory before doing ls; ok markus@
   - itojun@cvs.openbsd.org  2001/02/07 11:01:18
     [scp.c sftp-client.c sftp-server.c]
     unsigned long long -> %llu, not %qu.  markus ok
   - stevesk@cvs.openbsd.org 2001/02/07 11:10:39
     [sftp.1 sftp-int.c]
     more man page cleanup and sync of help text with man page; ok markus@
   - markus@cvs.openbsd.org  2001/02/07 14:58:34
     [sftp-client.c]
     older servers reply with SSH2_FXP_NAME + count==0 instead of EOF
   - djm@cvs.openbsd.org     2001/02/07 15:27:19
     [sftp.c]
     Don't forward agent and X11 in sftp. Suggestion from Roumen Petrov
     <roumen.petrov@skalasoft.com>
   - stevesk@cvs.openbsd.org 2001/02/07 15:36:04
     [sftp-int.c]
     portable; ok markus@
   - stevesk@cvs.openbsd.org 2001/02/07 15:55:47
     [sftp-int.c]
     lowercase cmds[].c also; ok markus@
   - markus@cvs.openbsd.org  2001/02/07 17:04:52
     [pathnames.h sftp.c]
     allow sftp over ssh protocol 1; ok djm@
   - deraadt@cvs.openbsd.org 2001/02/08 07:38:55
     [scp.c]
     memory leak fix, and snprintf throughout
   - deraadt@cvs.openbsd.org 2001/02/08 08:02:02
     [sftp-int.c]
     plug a memory leak
   - stevesk@cvs.openbsd.org 2001/02/08 10:11:23
     [session.c sftp-client.c]
     %i -> %d
   - stevesk@cvs.openbsd.org 2001/02/08 10:57:59
     [sftp-int.c]
     typo
   - stevesk@cvs.openbsd.org 2001/02/08 15:28:07
     [sftp-int.c pathnames.h]
     _PATH_LS; ok markus@
   - djm@cvs.openbsd.org     2001/02/09 04:46:25
     [sftp-int.c]
     Check for NULL attribs for chown, chmod & chgrp operations, only send
     relevant attribs back to server; ok markus@
 - (djm) Update makefile.in for _PATH_SFTP_SERVER
diff --git a/sftp-int.c b/sftp-int.c
index 02e0d38..8b5ae3a 100644
--- a/sftp-int.c
+++ b/sftp-int.c
@@ -24,10 +24,11 @@
 
 /* XXX: finish implementation of all commands */
 /* XXX: do fnmatch() instead of using raw pathname */
+/* XXX: globbed ls */
 /* XXX: recursive operations */
 
 #include "includes.h"
-RCSID("$OpenBSD: sftp-int.c,v 1.7 2001/02/05 00:02:32 deraadt Exp $");
+RCSID("$OpenBSD: sftp-int.c,v 1.19 2001/02/09 11:46:24 djm Exp $");
 
 #include "buffer.h"
 #include "xmalloc.h"
@@ -70,28 +71,29 @@
 };
 
 const struct CMD cmds[] = {
-	{ "CD",		I_CHDIR },
-	{ "CHDIR",	I_CHDIR },
-	{ "CHGRP",	I_CHGRP },
-	{ "CHMOD",	I_CHMOD },
-	{ "CHOWN",	I_CHOWN },
-	{ "EXIT",	I_QUIT },
-	{ "GET",	I_GET },
-	{ "HELP",	I_HELP },
-	{ "LCD",	I_LCHDIR },
-	{ "LCHDIR",	I_LCHDIR },
-	{ "LLS",	I_LLS },
-	{ "LMKDIR",	I_LMKDIR },
-	{ "LPWD",	I_LPWD },
-	{ "LS",		I_LS },
-	{ "LUMASK",	I_LUMASK },
-	{ "MKDIR",	I_MKDIR },
-	{ "PUT",	I_PUT },
-	{ "PWD",	I_PWD },
-	{ "QUIT",	I_QUIT },
-	{ "RENAME",	I_RENAME },
-	{ "RM",		I_RM },
-	{ "RMDIR",	I_RMDIR },
+	{ "cd",		I_CHDIR },
+	{ "chdir",	I_CHDIR },
+	{ "chgrp",	I_CHGRP },
+	{ "chmod",	I_CHMOD },
+	{ "chown",	I_CHOWN },
+	{ "dir",	I_LS },
+	{ "exit",	I_QUIT },
+	{ "get",	I_GET },
+	{ "help",	I_HELP },
+	{ "lcd",	I_LCHDIR },
+	{ "lchdir",	I_LCHDIR },
+	{ "lls",	I_LLS },
+	{ "lmkdir",	I_LMKDIR },
+	{ "lpwd",	I_LPWD },
+	{ "ls",		I_LS },
+	{ "lumask",	I_LUMASK },
+	{ "mkdir",	I_MKDIR },
+	{ "put",	I_PUT },
+	{ "pwd",	I_PWD },
+	{ "quit",	I_QUIT },
+	{ "rename",	I_RENAME },
+	{ "rm",		I_RM },
+	{ "rmdir",	I_RMDIR },
 	{ "!",		I_SHELL },
 	{ "?",		I_HELP },
 	{ NULL,			-1}
@@ -101,28 +103,29 @@
 help(void)
 {
 	printf("Available commands:\n");
-	printf("CD path                       Change remote directory to 'path'\n");
-	printf("LCD path                      Change local directory to 'path'\n");
-	printf("CHGRP grp path                Change group of file 'path' to 'grp'\n");
-	printf("CHMOD mode path               Change permissions of file 'path' to 'mode'\n");
-	printf("CHOWN own path                Change owner of file 'path' to 'own'\n");
-	printf("HELP                          Display this help text\n");
-	printf("GET remote-path [local-path]  Download file\n");
-	printf("LLS [ls options] [path]       Display local directory listing\n");
-	printf("LMKDIR path                   Create local directory\n");
-	printf("LPWD                          Print local working directory\n");
-	printf("LS [path]                     Display remote directory listing\n");
-	printf("LUMASK umask                  Set local umask to 'umask'\n");
-	printf("MKDIR path                    Create remote directory\n");
-	printf("PUT local-path [remote-path]  Upload file\n");
-	printf("PWD                           Display remote working directory\n");
-	printf("EXIT                          Quit sftp\n");
-	printf("QUIT                          Quit sftp\n");
-	printf("RENAME oldpath newpath        Rename remote file\n");
-	printf("RMDIR path                    Remove remote directory\n");
-	printf("RM path                       Delete remote file\n");
+	printf("cd path                       Change remote directory to 'path'\n");
+	printf("lcd path                      Change local directory to 'path'\n");
+	printf("chgrp grp path                Change group of file 'path' to 'grp'\n");
+	printf("chmod mode path               Change permissions of file 'path' to 'mode'\n");
+	printf("chown own path                Change owner of file 'path' to 'own'\n");
+	printf("help                          Display this help text\n");
+	printf("get remote-path [local-path]  Download file\n");
+	printf("lls [ls-options [path]]       Display local directory listing\n");
+	printf("lmkdir path                   Create local directory\n");
+	printf("lpwd                          Print local working directory\n");
+	printf("ls [path]                     Display remote directory listing\n");
+	printf("lumask umask                  Set local umask to 'umask'\n");
+	printf("mkdir path                    Create remote directory\n");
+	printf("put local-path [remote-path]  Upload file\n");
+	printf("pwd                           Display remote working directory\n");
+	printf("exit                          Quit sftp\n");
+	printf("quit                          Quit sftp\n");
+	printf("rename oldpath newpath        Rename remote file\n");
+	printf("rmdir path                    Remove remote directory\n");
+	printf("rm path                       Delete remote file\n");
 	printf("!command                      Execute 'command' in local shell\n");
 	printf("!                             Escape to local shell\n");
+	printf("?                             Synonym for help\n");
 }
 
 void
@@ -166,13 +169,15 @@
 local_do_ls(const char *args)
 {
 	if (!args || !*args)
-		local_do_shell("ls");
+		local_do_shell(_PATH_LS);
 	else {
-		char *buf = xmalloc(8 + strlen(args) + 1);
+		int len = strlen(_PATH_LS " ") + strlen(args) + 1;
+		char *buf = xmalloc(len);
 
 		/* XXX: quoting - rip quoting code from ftp? */
-		sprintf(buf, "/bin/ls %s", args);
+		snprintf(buf, len, _PATH_LS " %s", args);
 		local_do_shell(buf);
+		xfree(buf);
 	}
 }
 
@@ -198,7 +203,7 @@
 
 	/* Check for flags */
 	if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
-		switch (*cp) {
+		switch (cp[1]) {
 		case 'P':
 			*pflag = 1;
 			break;
@@ -216,50 +221,49 @@
 int
 get_pathname(const char **cpp, char **path)
 {
-	const char *quot, *cp = *cpp;
+	const char *cp = *cpp, *end;
+	char quot;
 	int i;
 
 	cp += strspn(cp, WHITESPACE);
 	if (!*cp) {
 		*cpp = cp;
 		*path = NULL;
-		return(0);
+		return (0);
 	}
 
 	/* Check for quoted filenames */
 	if (*cp == '\"' || *cp == '\'') {
-		quot = cp++;
-		for(i = 0; cp[i] && cp[i] != *quot; i++)
-			;
-		if (!cp[i]) {
+		quot = *cp++;
+		
+		end = strchr(cp, quot);
+		if (end == NULL) {
 			error("Unterminated quote");
-			*path = NULL;
-			return(-1);
+			goto fail;
 		}
-		if (i == 0) {
+		if (cp == end) {
 			error("Empty quotes");
-			*path = NULL;
-			return(-1);
+			goto fail;
 		}
-		*path = xmalloc(i + 1);
-		memcpy(*path, cp, i);
-		(*path)[i] = '\0';
-		cp += i + 1;
-		*cpp = cp + strspn(cp, WHITESPACE);
-		return(0);
+		*cpp = end + 1 + strspn(end + 1, WHITESPACE);
+	} else {
+		/* Read to end of filename */
+		end = strpbrk(cp, WHITESPACE);
+		if (end == NULL)
+			end = strchr(cp, '\0');
+		*cpp = end + strspn(end, WHITESPACE);
 	}
 
-	/* Read to end of filename */
-	for(i = 0; cp[i] && cp[i] != ' '; i++)
-		;
+	i = end - cp;
 
 	*path = xmalloc(i + 1);
 	memcpy(*path, cp, i);
 	(*path)[i] = '\0';
-	cp += i;
-	*cpp = cp + strspn(cp, WHITESPACE);
-
 	return(0);
+
+ fail:
+	*path = NULL;
+	return (-1);
 }
 
 int
@@ -270,7 +274,6 @@
 	debug("XXX: P = \"%s\"", p);
 
 	cp = strrchr(p, '/');
-
 	if (cp == NULL) {
 		*ifp = xstrdup(p);
 		return(0);
@@ -421,14 +424,13 @@
 	}
 
 	*cpp = cp;
-
 	return(cmdnum);
 }
 
 int
 parse_dispatch_command(int in, int out, const char *cmd, char **pwd)
 {
-	char *path1, *path2;
+	char *path1, *path2, *tmp;
 	int pflag, cmdnum;
 	unsigned long n_arg;
 	Attrib a, *aa;
@@ -471,12 +473,44 @@
 		break;
 	case I_CHDIR:
 		path1 = make_absolute(path1, *pwd);
+		if ((tmp = do_realpath(in, out, path1)) == NULL)
+			break;
+		if ((aa = do_stat(in, out, tmp)) == NULL) {
+			xfree(tmp);
+			break;
+		}
+		if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
+			error("Can't change directory: Can't check target");
+			xfree(tmp);
+			break;
+		}
+		if (!S_ISDIR(aa->perm)) {
+			error("Can't change directory: \"%s\" is not "
+			    "a directory", tmp);
+			xfree(tmp);
+			break;
+		}
 		xfree(*pwd);
-		*pwd = do_realpath(in, out, path1);
+		*pwd = tmp;
 		break;
 	case I_LS:
+		if (!path1) {
+			do_ls(in, out, *pwd);
+			break;
+		}
 		path1 = make_absolute(path1, *pwd);
-		do_ls(in, out, path1?path1:*pwd);
+		if ((tmp = do_realpath(in, out, path1)) == NULL)
+			break;
+		xfree(path1);
+		path1 = tmp;
+		if ((aa = do_stat(in, out, path1)) == NULL)
+			break;
+		if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 
+		    !S_ISDIR(aa->perm)) {
+			error("Can't ls: \"%s\" is not a directory", path1);
+			break;
+		}
+		do_ls(in, out, path1);
 		break;
 	case I_LCHDIR:
 		if (chdir(path1) == -1)
@@ -485,7 +519,7 @@
 		break;
 	case I_LMKDIR:
 		if (mkdir(path1, 0777) == -1)
-			error("Couldn't create local directory to "
+			error("Couldn't create local directory "
 			    "\"%s\": %s", path1, strerror(errno));
 		break;
 	case I_LLS:
@@ -506,23 +540,27 @@
 		break;
 	case I_CHOWN:
 		path1 = make_absolute(path1, *pwd);
-		aa = do_stat(in, out, path1);
+		if (!(aa = do_stat(in, out, path1)))
+			break;
 		if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
 			error("Can't get current ownership of "
 			    "remote file \"%s\"", path1);
 			break;
 		}
+		aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
 		aa->uid = n_arg;
 		do_setstat(in, out, path1, aa);
 		break;
 	case I_CHGRP:
 		path1 = make_absolute(path1, *pwd);
-		aa = do_stat(in, out, path1);
+		if (!(aa = do_stat(in, out, path1)))
+			break;
 		if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
 			error("Can't get current ownership of "
 			    "remote file \"%s\"", path1);
 			break;
 		}
+		aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
 		aa->gid = n_arg;
 		do_setstat(in, out, path1, aa);
 		break;
@@ -550,7 +588,6 @@
 		xfree(path1);
 	if (path2)
 		xfree(path2);
-
 	return(0);
 }
 
@@ -564,8 +601,8 @@
 	if (pwd == NULL)
 		fatal("Need cwd");
 
-	setvbuf(stdout, (char *)NULL, _IOLBF, 0);
-	setvbuf(stdin, (char *)NULL, _IOLBF, 0);
+	setvbuf(stdout, NULL, _IOLBF, 0);
+	setvbuf(stdin, NULL, _IOLBF, 0);
 
 	for(;;) {
 		char *cp;