xref: /dragonfly/crypto/openssh/sftp.c (revision 956939d5)
1 /* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker Exp $ */
2 /*
3  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include "includes.h"
19 
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
24 #endif
25 #include <sys/param.h>
26 #include <sys/socket.h>
27 #include <sys/wait.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
30 #endif
31 
32 #include <ctype.h>
33 #include <errno.h>
34 
35 #ifdef HAVE_PATHS_H
36 # include <paths.h>
37 #endif
38 #ifdef USE_LIBEDIT
39 #include <histedit.h>
40 #else
41 typedef void EditLine;
42 #endif
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdarg.h>
49 
50 #ifdef HAVE_UTIL_H
51 # include <util.h>
52 #endif
53 
54 #ifdef HAVE_LIBUTIL_H
55 # include <libutil.h>
56 #endif
57 
58 #include "xmalloc.h"
59 #include "log.h"
60 #include "pathnames.h"
61 #include "misc.h"
62 
63 #include "sftp.h"
64 #include "buffer.h"
65 #include "sftp-common.h"
66 #include "sftp-client.h"
67 
68 /* File to read commands from */
69 FILE* infile;
70 
71 /* Are we in batchfile mode? */
72 int batchmode = 0;
73 
74 /* Size of buffer used when copying files */
75 size_t copy_buffer_len = 32768;
76 
77 /* Number of concurrent outstanding requests */
78 size_t num_requests = 64;
79 
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
82 
83 /* This is set to 0 if the progressmeter is not desired. */
84 int showprogress = 1;
85 
86 /* SIGINT received during command processing */
87 volatile sig_atomic_t interrupted = 0;
88 
89 /* I wish qsort() took a separate ctx for the comparison function...*/
90 int sort_flag;
91 
92 int remote_glob(struct sftp_conn *, const char *, int,
93     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
94 
95 extern char *__progname;
96 
97 /* Separators for interactive commands */
98 #define WHITESPACE " \t\r\n"
99 
100 /* ls flags */
101 #define LS_LONG_VIEW	0x01	/* Full view ala ls -l */
102 #define LS_SHORT_VIEW	0x02	/* Single row view ala ls -1 */
103 #define LS_NUMERIC_VIEW	0x04	/* Long view with numeric uid/gid */
104 #define LS_NAME_SORT	0x08	/* Sort by name (default) */
105 #define LS_TIME_SORT	0x10	/* Sort by mtime */
106 #define LS_SIZE_SORT	0x20	/* Sort by file size */
107 #define LS_REVERSE_SORT	0x40	/* Reverse sort order */
108 #define LS_SHOW_ALL	0x80	/* Don't skip filenames starting with '.' */
109 
110 #define VIEW_FLAGS	(LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
111 #define SORT_FLAGS	(LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
112 
113 /* Commands for interactive mode */
114 #define I_CHDIR		1
115 #define I_CHGRP		2
116 #define I_CHMOD		3
117 #define I_CHOWN		4
118 #define I_DF		24
119 #define I_GET		5
120 #define I_HELP		6
121 #define I_LCHDIR	7
122 #define I_LLS		8
123 #define I_LMKDIR	9
124 #define I_LPWD		10
125 #define I_LS		11
126 #define I_LUMASK	12
127 #define I_MKDIR		13
128 #define I_PUT		14
129 #define I_PWD		15
130 #define I_QUIT		16
131 #define I_RENAME	17
132 #define I_RM		18
133 #define I_RMDIR		19
134 #define I_SHELL		20
135 #define I_SYMLINK	21
136 #define I_VERSION	22
137 #define I_PROGRESS	23
138 
139 struct CMD {
140 	const char *c;
141 	const int n;
142 };
143 
144 static const struct CMD cmds[] = {
145 	{ "bye",	I_QUIT },
146 	{ "cd",		I_CHDIR },
147 	{ "chdir",	I_CHDIR },
148 	{ "chgrp",	I_CHGRP },
149 	{ "chmod",	I_CHMOD },
150 	{ "chown",	I_CHOWN },
151 	{ "df",		I_DF },
152 	{ "dir",	I_LS },
153 	{ "exit",	I_QUIT },
154 	{ "get",	I_GET },
155 	{ "mget",	I_GET },
156 	{ "help",	I_HELP },
157 	{ "lcd",	I_LCHDIR },
158 	{ "lchdir",	I_LCHDIR },
159 	{ "lls",	I_LLS },
160 	{ "lmkdir",	I_LMKDIR },
161 	{ "ln",		I_SYMLINK },
162 	{ "lpwd",	I_LPWD },
163 	{ "ls",		I_LS },
164 	{ "lumask",	I_LUMASK },
165 	{ "mkdir",	I_MKDIR },
166 	{ "progress",	I_PROGRESS },
167 	{ "put",	I_PUT },
168 	{ "mput",	I_PUT },
169 	{ "pwd",	I_PWD },
170 	{ "quit",	I_QUIT },
171 	{ "rename",	I_RENAME },
172 	{ "rm",		I_RM },
173 	{ "rmdir",	I_RMDIR },
174 	{ "symlink",	I_SYMLINK },
175 	{ "version",	I_VERSION },
176 	{ "!",		I_SHELL },
177 	{ "?",		I_HELP },
178 	{ NULL,			-1}
179 };
180 
181 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
182 
183 /* ARGSUSED */
184 static void
185 killchild(int signo)
186 {
187 	if (sshpid > 1) {
188 		kill(sshpid, SIGTERM);
189 		waitpid(sshpid, NULL, 0);
190 	}
191 
192 	_exit(1);
193 }
194 
195 /* ARGSUSED */
196 static void
197 cmd_interrupt(int signo)
198 {
199 	const char msg[] = "\rInterrupt  \n";
200 	int olderrno = errno;
201 
202 	write(STDERR_FILENO, msg, sizeof(msg) - 1);
203 	interrupted = 1;
204 	errno = olderrno;
205 }
206 
207 static void
208 help(void)
209 {
210 	printf("Available commands:\n"
211 	    "bye                                Quit sftp\n"
212 	    "cd path                            Change remote directory to 'path'\n"
213 	    "chgrp grp path                     Change group of file 'path' to 'grp'\n"
214 	    "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
215 	    "chown own path                     Change owner of file 'path' to 'own'\n"
216 	    "df [-hi] [path]                    Display statistics for current directory or\n"
217 	    "                                   filesystem containing 'path'\n"
218 	    "exit                               Quit sftp\n"
219 	    "get [-P] remote-path [local-path]  Download file\n"
220 	    "help                               Display this help text\n"
221 	    "lcd path                           Change local directory to 'path'\n"
222 	    "lls [ls-options [path]]            Display local directory listing\n"
223 	    "lmkdir path                        Create local directory\n"
224 	    "ln oldpath newpath                 Symlink remote file\n"
225 	    "lpwd                               Print local working directory\n"
226 	    "ls [-1aflnrSt] [path]              Display remote directory listing\n"
227 	    "lumask umask                       Set local umask to 'umask'\n"
228 	    "mkdir path                         Create remote directory\n"
229 	    "progress                           Toggle display of progress meter\n"
230 	    "put [-P] local-path [remote-path]  Upload file\n"
231 	    "pwd                                Display remote working directory\n"
232 	    "quit                               Quit sftp\n"
233 	    "rename oldpath newpath             Rename remote file\n"
234 	    "rm path                            Delete remote file\n"
235 	    "rmdir path                         Remove remote directory\n"
236 	    "symlink oldpath newpath            Symlink remote file\n"
237 	    "version                            Show SFTP version\n"
238 	    "!command                           Execute 'command' in local shell\n"
239 	    "!                                  Escape to local shell\n"
240 	    "?                                  Synonym for help\n");
241 }
242 
243 static void
244 local_do_shell(const char *args)
245 {
246 	int status;
247 	char *shell;
248 	pid_t pid;
249 
250 	if (!*args)
251 		args = NULL;
252 
253 	if ((shell = getenv("SHELL")) == NULL)
254 		shell = _PATH_BSHELL;
255 
256 	if ((pid = fork()) == -1)
257 		fatal("Couldn't fork: %s", strerror(errno));
258 
259 	if (pid == 0) {
260 		/* XXX: child has pipe fds to ssh subproc open - issue? */
261 		if (args) {
262 			debug3("Executing %s -c \"%s\"", shell, args);
263 			execl(shell, shell, "-c", args, (char *)NULL);
264 		} else {
265 			debug3("Executing %s", shell);
266 			execl(shell, shell, (char *)NULL);
267 		}
268 		fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
269 		    strerror(errno));
270 		_exit(1);
271 	}
272 	while (waitpid(pid, &status, 0) == -1)
273 		if (errno != EINTR)
274 			fatal("Couldn't wait for child: %s", strerror(errno));
275 	if (!WIFEXITED(status))
276 		error("Shell exited abnormally");
277 	else if (WEXITSTATUS(status))
278 		error("Shell exited with status %d", WEXITSTATUS(status));
279 }
280 
281 static void
282 local_do_ls(const char *args)
283 {
284 	if (!args || !*args)
285 		local_do_shell(_PATH_LS);
286 	else {
287 		int len = strlen(_PATH_LS " ") + strlen(args) + 1;
288 		char *buf = xmalloc(len);
289 
290 		/* XXX: quoting - rip quoting code from ftp? */
291 		snprintf(buf, len, _PATH_LS " %s", args);
292 		local_do_shell(buf);
293 		xfree(buf);
294 	}
295 }
296 
297 /* Strip one path (usually the pwd) from the start of another */
298 static char *
299 path_strip(char *path, char *strip)
300 {
301 	size_t len;
302 
303 	if (strip == NULL)
304 		return (xstrdup(path));
305 
306 	len = strlen(strip);
307 	if (strncmp(path, strip, len) == 0) {
308 		if (strip[len - 1] != '/' && path[len] == '/')
309 			len++;
310 		return (xstrdup(path + len));
311 	}
312 
313 	return (xstrdup(path));
314 }
315 
316 static char *
317 path_append(char *p1, char *p2)
318 {
319 	char *ret;
320 	size_t len = strlen(p1) + strlen(p2) + 2;
321 
322 	ret = xmalloc(len);
323 	strlcpy(ret, p1, len);
324 	if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
325 		strlcat(ret, "/", len);
326 	strlcat(ret, p2, len);
327 
328 	return(ret);
329 }
330 
331 static char *
332 make_absolute(char *p, char *pwd)
333 {
334 	char *abs_str;
335 
336 	/* Derelativise */
337 	if (p && p[0] != '/') {
338 		abs_str = path_append(pwd, p);
339 		xfree(p);
340 		return(abs_str);
341 	} else
342 		return(p);
343 }
344 
345 static int
346 infer_path(const char *p, char **ifp)
347 {
348 	char *cp;
349 
350 	cp = strrchr(p, '/');
351 	if (cp == NULL) {
352 		*ifp = xstrdup(p);
353 		return(0);
354 	}
355 
356 	if (!cp[1]) {
357 		error("Invalid path");
358 		return(-1);
359 	}
360 
361 	*ifp = xstrdup(cp + 1);
362 	return(0);
363 }
364 
365 static int
366 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
367 {
368 	extern int opterr, optind, optopt, optreset;
369 	int ch;
370 
371 	optind = optreset = 1;
372 	opterr = 0;
373 
374 	*pflag = 0;
375 	while ((ch = getopt(argc, argv, "Pp")) != -1) {
376 		switch (ch) {
377 		case 'p':
378 		case 'P':
379 			*pflag = 1;
380 			break;
381 		default:
382 			error("%s: Invalid flag -%c", cmd, optopt);
383 			return -1;
384 		}
385 	}
386 
387 	return optind;
388 }
389 
390 static int
391 parse_ls_flags(char **argv, int argc, int *lflag)
392 {
393 	extern int opterr, optind, optopt, optreset;
394 	int ch;
395 
396 	optind = optreset = 1;
397 	opterr = 0;
398 
399 	*lflag = LS_NAME_SORT;
400 	while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
401 		switch (ch) {
402 		case '1':
403 			*lflag &= ~VIEW_FLAGS;
404 			*lflag |= LS_SHORT_VIEW;
405 			break;
406 		case 'S':
407 			*lflag &= ~SORT_FLAGS;
408 			*lflag |= LS_SIZE_SORT;
409 			break;
410 		case 'a':
411 			*lflag |= LS_SHOW_ALL;
412 			break;
413 		case 'f':
414 			*lflag &= ~SORT_FLAGS;
415 			break;
416 		case 'l':
417 			*lflag &= ~VIEW_FLAGS;
418 			*lflag |= LS_LONG_VIEW;
419 			break;
420 		case 'n':
421 			*lflag &= ~VIEW_FLAGS;
422 			*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
423 			break;
424 		case 'r':
425 			*lflag |= LS_REVERSE_SORT;
426 			break;
427 		case 't':
428 			*lflag &= ~SORT_FLAGS;
429 			*lflag |= LS_TIME_SORT;
430 			break;
431 		default:
432 			error("ls: Invalid flag -%c", optopt);
433 			return -1;
434 		}
435 	}
436 
437 	return optind;
438 }
439 
440 static int
441 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
442 {
443 	extern int opterr, optind, optopt, optreset;
444 	int ch;
445 
446 	optind = optreset = 1;
447 	opterr = 0;
448 
449 	*hflag = *iflag = 0;
450 	while ((ch = getopt(argc, argv, "hi")) != -1) {
451 		switch (ch) {
452 		case 'h':
453 			*hflag = 1;
454 			break;
455 		case 'i':
456 			*iflag = 1;
457 			break;
458 		default:
459 			error("%s: Invalid flag -%c", cmd, optopt);
460 			return -1;
461 		}
462 	}
463 
464 	return optind;
465 }
466 
467 static int
468 is_dir(char *path)
469 {
470 	struct stat sb;
471 
472 	/* XXX: report errors? */
473 	if (stat(path, &sb) == -1)
474 		return(0);
475 
476 	return(S_ISDIR(sb.st_mode));
477 }
478 
479 static int
480 remote_is_dir(struct sftp_conn *conn, char *path)
481 {
482 	Attrib *a;
483 
484 	/* XXX: report errors? */
485 	if ((a = do_stat(conn, path, 1)) == NULL)
486 		return(0);
487 	if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
488 		return(0);
489 	return(S_ISDIR(a->perm));
490 }
491 
492 static int
493 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
494 {
495 	char *abs_src = NULL;
496 	char *abs_dst = NULL;
497 	char *tmp;
498 	glob_t g;
499 	int err = 0;
500 	int i;
501 
502 	abs_src = xstrdup(src);
503 	abs_src = make_absolute(abs_src, pwd);
504 
505 	memset(&g, 0, sizeof(g));
506 	debug3("Looking up %s", abs_src);
507 	if (remote_glob(conn, abs_src, 0, NULL, &g)) {
508 		error("File \"%s\" not found.", abs_src);
509 		err = -1;
510 		goto out;
511 	}
512 
513 	/* If multiple matches, dst must be a directory or unspecified */
514 	if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
515 		error("Multiple files match, but \"%s\" is not a directory",
516 		    dst);
517 		err = -1;
518 		goto out;
519 	}
520 
521 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
522 		if (infer_path(g.gl_pathv[i], &tmp)) {
523 			err = -1;
524 			goto out;
525 		}
526 
527 		if (g.gl_matchc == 1 && dst) {
528 			/* If directory specified, append filename */
529 			xfree(tmp);
530 			if (is_dir(dst)) {
531 				if (infer_path(g.gl_pathv[0], &tmp)) {
532 					err = 1;
533 					goto out;
534 				}
535 				abs_dst = path_append(dst, tmp);
536 				xfree(tmp);
537 			} else
538 				abs_dst = xstrdup(dst);
539 		} else if (dst) {
540 			abs_dst = path_append(dst, tmp);
541 			xfree(tmp);
542 		} else
543 			abs_dst = tmp;
544 
545 		printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
546 		if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
547 			err = -1;
548 		xfree(abs_dst);
549 		abs_dst = NULL;
550 	}
551 
552 out:
553 	xfree(abs_src);
554 	globfree(&g);
555 	return(err);
556 }
557 
558 static int
559 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
560 {
561 	char *tmp_dst = NULL;
562 	char *abs_dst = NULL;
563 	char *tmp;
564 	glob_t g;
565 	int err = 0;
566 	int i;
567 	struct stat sb;
568 
569 	if (dst) {
570 		tmp_dst = xstrdup(dst);
571 		tmp_dst = make_absolute(tmp_dst, pwd);
572 	}
573 
574 	memset(&g, 0, sizeof(g));
575 	debug3("Looking up %s", src);
576 	if (glob(src, GLOB_NOCHECK, NULL, &g)) {
577 		error("File \"%s\" not found.", src);
578 		err = -1;
579 		goto out;
580 	}
581 
582 	/* If multiple matches, dst may be directory or unspecified */
583 	if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
584 		error("Multiple files match, but \"%s\" is not a directory",
585 		    tmp_dst);
586 		err = -1;
587 		goto out;
588 	}
589 
590 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
591 		if (stat(g.gl_pathv[i], &sb) == -1) {
592 			err = -1;
593 			error("stat %s: %s", g.gl_pathv[i], strerror(errno));
594 			continue;
595 		}
596 
597 		if (!S_ISREG(sb.st_mode)) {
598 			error("skipping non-regular file %s",
599 			    g.gl_pathv[i]);
600 			continue;
601 		}
602 		if (infer_path(g.gl_pathv[i], &tmp)) {
603 			err = -1;
604 			goto out;
605 		}
606 
607 		if (g.gl_matchc == 1 && tmp_dst) {
608 			/* If directory specified, append filename */
609 			if (remote_is_dir(conn, tmp_dst)) {
610 				if (infer_path(g.gl_pathv[0], &tmp)) {
611 					err = 1;
612 					goto out;
613 				}
614 				abs_dst = path_append(tmp_dst, tmp);
615 				xfree(tmp);
616 			} else
617 				abs_dst = xstrdup(tmp_dst);
618 
619 		} else if (tmp_dst) {
620 			abs_dst = path_append(tmp_dst, tmp);
621 			xfree(tmp);
622 		} else
623 			abs_dst = make_absolute(tmp, pwd);
624 
625 		printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
626 		if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
627 			err = -1;
628 	}
629 
630 out:
631 	if (abs_dst)
632 		xfree(abs_dst);
633 	if (tmp_dst)
634 		xfree(tmp_dst);
635 	globfree(&g);
636 	return(err);
637 }
638 
639 static int
640 sdirent_comp(const void *aa, const void *bb)
641 {
642 	SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
643 	SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
644 	int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
645 
646 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
647 	if (sort_flag & LS_NAME_SORT)
648 		return (rmul * strcmp(a->filename, b->filename));
649 	else if (sort_flag & LS_TIME_SORT)
650 		return (rmul * NCMP(a->a.mtime, b->a.mtime));
651 	else if (sort_flag & LS_SIZE_SORT)
652 		return (rmul * NCMP(a->a.size, b->a.size));
653 
654 	fatal("Unknown ls sort type");
655 }
656 
657 /* sftp ls.1 replacement for directories */
658 static int
659 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
660 {
661 	int n;
662 	u_int c = 1, colspace = 0, columns = 1;
663 	SFTP_DIRENT **d;
664 
665 	if ((n = do_readdir(conn, path, &d)) != 0)
666 		return (n);
667 
668 	if (!(lflag & LS_SHORT_VIEW)) {
669 		u_int m = 0, width = 80;
670 		struct winsize ws;
671 		char *tmp;
672 
673 		/* Count entries for sort and find longest filename */
674 		for (n = 0; d[n] != NULL; n++) {
675 			if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
676 				m = MAX(m, strlen(d[n]->filename));
677 		}
678 
679 		/* Add any subpath that also needs to be counted */
680 		tmp = path_strip(path, strip_path);
681 		m += strlen(tmp);
682 		xfree(tmp);
683 
684 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
685 			width = ws.ws_col;
686 
687 		columns = width / (m + 2);
688 		columns = MAX(columns, 1);
689 		colspace = width / columns;
690 		colspace = MIN(colspace, width);
691 	}
692 
693 	if (lflag & SORT_FLAGS) {
694 		for (n = 0; d[n] != NULL; n++)
695 			;	/* count entries */
696 		sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
697 		qsort(d, n, sizeof(*d), sdirent_comp);
698 	}
699 
700 	for (n = 0; d[n] != NULL && !interrupted; n++) {
701 		char *tmp, *fname;
702 
703 		if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
704 			continue;
705 
706 		tmp = path_append(path, d[n]->filename);
707 		fname = path_strip(tmp, strip_path);
708 		xfree(tmp);
709 
710 		if (lflag & LS_LONG_VIEW) {
711 			if (lflag & LS_NUMERIC_VIEW) {
712 				char *lname;
713 				struct stat sb;
714 
715 				memset(&sb, 0, sizeof(sb));
716 				attrib_to_stat(&d[n]->a, &sb);
717 				lname = ls_file(fname, &sb, 1);
718 				printf("%s\n", lname);
719 				xfree(lname);
720 			} else
721 				printf("%s\n", d[n]->longname);
722 		} else {
723 			printf("%-*s", colspace, fname);
724 			if (c >= columns) {
725 				printf("\n");
726 				c = 1;
727 			} else
728 				c++;
729 		}
730 
731 		xfree(fname);
732 	}
733 
734 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
735 		printf("\n");
736 
737 	free_sftp_dirents(d);
738 	return (0);
739 }
740 
741 /* sftp ls.1 replacement which handles path globs */
742 static int
743 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
744     int lflag)
745 {
746 	glob_t g;
747 	u_int i, c = 1, colspace = 0, columns = 1;
748 	Attrib *a = NULL;
749 
750 	memset(&g, 0, sizeof(g));
751 
752 	if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
753 	    NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
754 		if (g.gl_pathc)
755 			globfree(&g);
756 		error("Can't ls: \"%s\" not found", path);
757 		return (-1);
758 	}
759 
760 	if (interrupted)
761 		goto out;
762 
763 	/*
764 	 * If the glob returns a single match and it is a directory,
765 	 * then just list its contents.
766 	 */
767 	if (g.gl_matchc == 1) {
768 		if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
769 			globfree(&g);
770 			return (-1);
771 		}
772 		if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
773 		    S_ISDIR(a->perm)) {
774 			int err;
775 
776 			err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
777 			globfree(&g);
778 			return (err);
779 		}
780 	}
781 
782 	if (!(lflag & LS_SHORT_VIEW)) {
783 		u_int m = 0, width = 80;
784 		struct winsize ws;
785 
786 		/* Count entries for sort and find longest filename */
787 		for (i = 0; g.gl_pathv[i]; i++)
788 			m = MAX(m, strlen(g.gl_pathv[i]));
789 
790 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
791 			width = ws.ws_col;
792 
793 		columns = width / (m + 2);
794 		columns = MAX(columns, 1);
795 		colspace = width / columns;
796 	}
797 
798 	for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
799 		char *fname;
800 
801 		fname = path_strip(g.gl_pathv[i], strip_path);
802 
803 		if (lflag & LS_LONG_VIEW) {
804 			char *lname;
805 			struct stat sb;
806 
807 			/*
808 			 * XXX: this is slow - 1 roundtrip per path
809 			 * A solution to this is to fork glob() and
810 			 * build a sftp specific version which keeps the
811 			 * attribs (which currently get thrown away)
812 			 * that the server returns as well as the filenames.
813 			 */
814 			memset(&sb, 0, sizeof(sb));
815 			if (a == NULL)
816 				a = do_lstat(conn, g.gl_pathv[i], 1);
817 			if (a != NULL)
818 				attrib_to_stat(a, &sb);
819 			lname = ls_file(fname, &sb, 1);
820 			printf("%s\n", lname);
821 			xfree(lname);
822 		} else {
823 			printf("%-*s", colspace, fname);
824 			if (c >= columns) {
825 				printf("\n");
826 				c = 1;
827 			} else
828 				c++;
829 		}
830 		xfree(fname);
831 	}
832 
833 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
834 		printf("\n");
835 
836  out:
837 	if (g.gl_pathc)
838 		globfree(&g);
839 
840 	return (0);
841 }
842 
843 static int
844 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
845 {
846 	struct sftp_statvfs st;
847 	char s_used[FMT_SCALED_STRSIZE];
848 	char s_avail[FMT_SCALED_STRSIZE];
849 	char s_root[FMT_SCALED_STRSIZE];
850 	char s_total[FMT_SCALED_STRSIZE];
851 
852 	if (do_statvfs(conn, path, &st, 1) == -1)
853 		return -1;
854 	if (iflag) {
855 		printf("     Inodes        Used       Avail      "
856 		    "(root)    %%Capacity\n");
857 		printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
858 		    (unsigned long long)st.f_files,
859 		    (unsigned long long)(st.f_files - st.f_ffree),
860 		    (unsigned long long)st.f_favail,
861 		    (unsigned long long)st.f_ffree,
862 		    (unsigned long long)(100 * (st.f_files - st.f_ffree) /
863 		    st.f_files));
864 	} else if (hflag) {
865 		strlcpy(s_used, "error", sizeof(s_used));
866 		strlcpy(s_avail, "error", sizeof(s_avail));
867 		strlcpy(s_root, "error", sizeof(s_root));
868 		strlcpy(s_total, "error", sizeof(s_total));
869 		fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
870 		fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
871 		fmt_scaled(st.f_bfree * st.f_frsize, s_root);
872 		fmt_scaled(st.f_blocks * st.f_frsize, s_total);
873 		printf("    Size     Used    Avail   (root)    %%Capacity\n");
874 		printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
875 		    s_total, s_used, s_avail, s_root,
876 		    (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
877 		    st.f_blocks));
878 	} else {
879 		printf("        Size         Used        Avail       "
880 		    "(root)    %%Capacity\n");
881 		printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
882 		    (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
883 		    (unsigned long long)(st.f_frsize *
884 		    (st.f_blocks - st.f_bfree) / 1024),
885 		    (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
886 		    (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
887 		    (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
888 		    st.f_blocks));
889 	}
890 	return 0;
891 }
892 
893 /*
894  * Undo escaping of glob sequences in place. Used to undo extra escaping
895  * applied in makeargv() when the string is destined for a function that
896  * does not glob it.
897  */
898 static void
899 undo_glob_escape(char *s)
900 {
901 	size_t i, j;
902 
903 	for (i = j = 0;;) {
904 		if (s[i] == '\0') {
905 			s[j] = '\0';
906 			return;
907 		}
908 		if (s[i] != '\\') {
909 			s[j++] = s[i++];
910 			continue;
911 		}
912 		/* s[i] == '\\' */
913 		++i;
914 		switch (s[i]) {
915 		case '?':
916 		case '[':
917 		case '*':
918 		case '\\':
919 			s[j++] = s[i++];
920 			break;
921 		case '\0':
922 			s[j++] = '\\';
923 			s[j] = '\0';
924 			return;
925 		default:
926 			s[j++] = '\\';
927 			s[j++] = s[i++];
928 			break;
929 		}
930 	}
931 }
932 
933 /*
934  * Split a string into an argument vector using sh(1)-style quoting,
935  * comment and escaping rules, but with some tweaks to handle glob(3)
936  * wildcards.
937  * Returns NULL on error or a NULL-terminated array of arguments.
938  */
939 #define MAXARGS 	128
940 #define MAXARGLEN	8192
941 static char **
942 makeargv(const char *arg, int *argcp)
943 {
944 	int argc, quot;
945 	size_t i, j;
946 	static char argvs[MAXARGLEN];
947 	static char *argv[MAXARGS + 1];
948 	enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
949 
950 	*argcp = argc = 0;
951 	if (strlen(arg) > sizeof(argvs) - 1) {
952  args_too_longs:
953 		error("string too long");
954 		return NULL;
955 	}
956 	state = MA_START;
957 	i = j = 0;
958 	for (;;) {
959 		if (isspace(arg[i])) {
960 			if (state == MA_UNQUOTED) {
961 				/* Terminate current argument */
962 				argvs[j++] = '\0';
963 				argc++;
964 				state = MA_START;
965 			} else if (state != MA_START)
966 				argvs[j++] = arg[i];
967 		} else if (arg[i] == '"' || arg[i] == '\'') {
968 			q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
969 			if (state == MA_START) {
970 				argv[argc] = argvs + j;
971 				state = q;
972 			} else if (state == MA_UNQUOTED)
973 				state = q;
974 			else if (state == q)
975 				state = MA_UNQUOTED;
976 			else
977 				argvs[j++] = arg[i];
978 		} else if (arg[i] == '\\') {
979 			if (state == MA_SQUOTE || state == MA_DQUOTE) {
980 				quot = state == MA_SQUOTE ? '\'' : '"';
981 				/* Unescape quote we are in */
982 				/* XXX support \n and friends? */
983 				if (arg[i + 1] == quot) {
984 					i++;
985 					argvs[j++] = arg[i];
986 				} else if (arg[i + 1] == '?' ||
987 				    arg[i + 1] == '[' || arg[i + 1] == '*') {
988 					/*
989 					 * Special case for sftp: append
990 					 * double-escaped glob sequence -
991 					 * glob will undo one level of
992 					 * escaping. NB. string can grow here.
993 					 */
994 					if (j >= sizeof(argvs) - 5)
995 						goto args_too_longs;
996 					argvs[j++] = '\\';
997 					argvs[j++] = arg[i++];
998 					argvs[j++] = '\\';
999 					argvs[j++] = arg[i];
1000 				} else {
1001 					argvs[j++] = arg[i++];
1002 					argvs[j++] = arg[i];
1003 				}
1004 			} else {
1005 				if (state == MA_START) {
1006 					argv[argc] = argvs + j;
1007 					state = MA_UNQUOTED;
1008 				}
1009 				if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1010 				    arg[i + 1] == '*' || arg[i + 1] == '\\') {
1011 					/*
1012 					 * Special case for sftp: append
1013 					 * escaped glob sequence -
1014 					 * glob will undo one level of
1015 					 * escaping.
1016 					 */
1017 					argvs[j++] = arg[i++];
1018 					argvs[j++] = arg[i];
1019 				} else {
1020 					/* Unescape everything */
1021 					/* XXX support \n and friends? */
1022 					i++;
1023 					argvs[j++] = arg[i];
1024 				}
1025 			}
1026 		} else if (arg[i] == '#') {
1027 			if (state == MA_SQUOTE || state == MA_DQUOTE)
1028 				argvs[j++] = arg[i];
1029 			else
1030 				goto string_done;
1031 		} else if (arg[i] == '\0') {
1032 			if (state == MA_SQUOTE || state == MA_DQUOTE) {
1033 				error("Unterminated quoted argument");
1034 				return NULL;
1035 			}
1036  string_done:
1037 			if (state == MA_UNQUOTED) {
1038 				argvs[j++] = '\0';
1039 				argc++;
1040 			}
1041 			break;
1042 		} else {
1043 			if (state == MA_START) {
1044 				argv[argc] = argvs + j;
1045 				state = MA_UNQUOTED;
1046 			}
1047 			if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1048 			    (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1049 				/*
1050 				 * Special case for sftp: escape quoted
1051 				 * glob(3) wildcards. NB. string can grow
1052 				 * here.
1053 				 */
1054 				if (j >= sizeof(argvs) - 3)
1055 					goto args_too_longs;
1056 				argvs[j++] = '\\';
1057 				argvs[j++] = arg[i];
1058 			} else
1059 				argvs[j++] = arg[i];
1060 		}
1061 		i++;
1062 	}
1063 	*argcp = argc;
1064 	return argv;
1065 }
1066 
1067 static int
1068 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
1069     unsigned long *n_arg, char **path1, char **path2)
1070 {
1071 	const char *cmd, *cp = *cpp;
1072 	char *cp2, **argv;
1073 	int base = 0;
1074 	long l;
1075 	int i, cmdnum, optidx, argc;
1076 
1077 	/* Skip leading whitespace */
1078 	cp = cp + strspn(cp, WHITESPACE);
1079 
1080 	/* Ignore blank lines and lines which begin with comment '#' char */
1081 	if (*cp == '\0' || *cp == '#')
1082 		return (0);
1083 
1084 	/* Check for leading '-' (disable error processing) */
1085 	*iflag = 0;
1086 	if (*cp == '-') {
1087 		*iflag = 1;
1088 		cp++;
1089 	}
1090 
1091 	if ((argv = makeargv(cp, &argc)) == NULL)
1092 		return -1;
1093 
1094 	/* Figure out which command we have */
1095 	for (i = 0; cmds[i].c != NULL; i++) {
1096 		if (strcasecmp(cmds[i].c, argv[0]) == 0)
1097 			break;
1098 	}
1099 	cmdnum = cmds[i].n;
1100 	cmd = cmds[i].c;
1101 
1102 	/* Special case */
1103 	if (*cp == '!') {
1104 		cp++;
1105 		cmdnum = I_SHELL;
1106 	} else if (cmdnum == -1) {
1107 		error("Invalid command.");
1108 		return -1;
1109 	}
1110 
1111 	/* Get arguments and parse flags */
1112 	*lflag = *pflag = *hflag = *n_arg = 0;
1113 	*path1 = *path2 = NULL;
1114 	optidx = 1;
1115 	switch (cmdnum) {
1116 	case I_GET:
1117 	case I_PUT:
1118 		if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
1119 			return -1;
1120 		/* Get first pathname (mandatory) */
1121 		if (argc - optidx < 1) {
1122 			error("You must specify at least one path after a "
1123 			    "%s command.", cmd);
1124 			return -1;
1125 		}
1126 		*path1 = xstrdup(argv[optidx]);
1127 		/* Get second pathname (optional) */
1128 		if (argc - optidx > 1) {
1129 			*path2 = xstrdup(argv[optidx + 1]);
1130 			/* Destination is not globbed */
1131 			undo_glob_escape(*path2);
1132 		}
1133 		break;
1134 	case I_RENAME:
1135 	case I_SYMLINK:
1136 		if (argc - optidx < 2) {
1137 			error("You must specify two paths after a %s "
1138 			    "command.", cmd);
1139 			return -1;
1140 		}
1141 		*path1 = xstrdup(argv[optidx]);
1142 		*path2 = xstrdup(argv[optidx + 1]);
1143 		/* Paths are not globbed */
1144 		undo_glob_escape(*path1);
1145 		undo_glob_escape(*path2);
1146 		break;
1147 	case I_RM:
1148 	case I_MKDIR:
1149 	case I_RMDIR:
1150 	case I_CHDIR:
1151 	case I_LCHDIR:
1152 	case I_LMKDIR:
1153 		/* Get pathname (mandatory) */
1154 		if (argc - optidx < 1) {
1155 			error("You must specify a path after a %s command.",
1156 			    cmd);
1157 			return -1;
1158 		}
1159 		*path1 = xstrdup(argv[optidx]);
1160 		/* Only "rm" globs */
1161 		if (cmdnum != I_RM)
1162 			undo_glob_escape(*path1);
1163 		break;
1164 	case I_DF:
1165 		if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1166 		    iflag)) == -1)
1167 			return -1;
1168 		/* Default to current directory if no path specified */
1169 		if (argc - optidx < 1)
1170 			*path1 = NULL;
1171 		else {
1172 			*path1 = xstrdup(argv[optidx]);
1173 			undo_glob_escape(*path1);
1174 		}
1175 		break;
1176 	case I_LS:
1177 		if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1178 			return(-1);
1179 		/* Path is optional */
1180 		if (argc - optidx > 0)
1181 			*path1 = xstrdup(argv[optidx]);
1182 		break;
1183 	case I_LLS:
1184 		/* Skip ls command and following whitespace */
1185 		cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1186 	case I_SHELL:
1187 		/* Uses the rest of the line */
1188 		break;
1189 	case I_LUMASK:
1190 	case I_CHMOD:
1191 		base = 8;
1192 	case I_CHOWN:
1193 	case I_CHGRP:
1194 		/* Get numeric arg (mandatory) */
1195 		if (argc - optidx < 1)
1196 			goto need_num_arg;
1197 		errno = 0;
1198 		l = strtol(argv[optidx], &cp2, base);
1199 		if (cp2 == argv[optidx] || *cp2 != '\0' ||
1200 		    ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1201 		    l < 0) {
1202  need_num_arg:
1203 			error("You must supply a numeric argument "
1204 			    "to the %s command.", cmd);
1205 			return -1;
1206 		}
1207 		*n_arg = l;
1208 		if (cmdnum == I_LUMASK)
1209 			break;
1210 		/* Get pathname (mandatory) */
1211 		if (argc - optidx < 2) {
1212 			error("You must specify a path after a %s command.",
1213 			    cmd);
1214 			return -1;
1215 		}
1216 		*path1 = xstrdup(argv[optidx + 1]);
1217 		break;
1218 	case I_QUIT:
1219 	case I_PWD:
1220 	case I_LPWD:
1221 	case I_HELP:
1222 	case I_VERSION:
1223 	case I_PROGRESS:
1224 		break;
1225 	default:
1226 		fatal("Command not implemented");
1227 	}
1228 
1229 	*cpp = cp;
1230 	return(cmdnum);
1231 }
1232 
1233 static int
1234 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1235     int err_abort)
1236 {
1237 	char *path1, *path2, *tmp;
1238 	int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1239 	unsigned long n_arg = 0;
1240 	Attrib a, *aa;
1241 	char path_buf[MAXPATHLEN];
1242 	int err = 0;
1243 	glob_t g;
1244 
1245 	path1 = path2 = NULL;
1246 	cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
1247 	    &path1, &path2);
1248 
1249 	if (iflag != 0)
1250 		err_abort = 0;
1251 
1252 	memset(&g, 0, sizeof(g));
1253 
1254 	/* Perform command */
1255 	switch (cmdnum) {
1256 	case 0:
1257 		/* Blank line */
1258 		break;
1259 	case -1:
1260 		/* Unrecognized command */
1261 		err = -1;
1262 		break;
1263 	case I_GET:
1264 		err = process_get(conn, path1, path2, *pwd, pflag);
1265 		break;
1266 	case I_PUT:
1267 		err = process_put(conn, path1, path2, *pwd, pflag);
1268 		break;
1269 	case I_RENAME:
1270 		path1 = make_absolute(path1, *pwd);
1271 		path2 = make_absolute(path2, *pwd);
1272 		err = do_rename(conn, path1, path2);
1273 		break;
1274 	case I_SYMLINK:
1275 		path2 = make_absolute(path2, *pwd);
1276 		err = do_symlink(conn, path1, path2);
1277 		break;
1278 	case I_RM:
1279 		path1 = make_absolute(path1, *pwd);
1280 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1281 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1282 			printf("Removing %s\n", g.gl_pathv[i]);
1283 			err = do_rm(conn, g.gl_pathv[i]);
1284 			if (err != 0 && err_abort)
1285 				break;
1286 		}
1287 		break;
1288 	case I_MKDIR:
1289 		path1 = make_absolute(path1, *pwd);
1290 		attrib_clear(&a);
1291 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1292 		a.perm = 0777;
1293 		err = do_mkdir(conn, path1, &a);
1294 		break;
1295 	case I_RMDIR:
1296 		path1 = make_absolute(path1, *pwd);
1297 		err = do_rmdir(conn, path1);
1298 		break;
1299 	case I_CHDIR:
1300 		path1 = make_absolute(path1, *pwd);
1301 		if ((tmp = do_realpath(conn, path1)) == NULL) {
1302 			err = 1;
1303 			break;
1304 		}
1305 		if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1306 			xfree(tmp);
1307 			err = 1;
1308 			break;
1309 		}
1310 		if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1311 			error("Can't change directory: Can't check target");
1312 			xfree(tmp);
1313 			err = 1;
1314 			break;
1315 		}
1316 		if (!S_ISDIR(aa->perm)) {
1317 			error("Can't change directory: \"%s\" is not "
1318 			    "a directory", tmp);
1319 			xfree(tmp);
1320 			err = 1;
1321 			break;
1322 		}
1323 		xfree(*pwd);
1324 		*pwd = tmp;
1325 		break;
1326 	case I_LS:
1327 		if (!path1) {
1328 			do_globbed_ls(conn, *pwd, *pwd, lflag);
1329 			break;
1330 		}
1331 
1332 		/* Strip pwd off beginning of non-absolute paths */
1333 		tmp = NULL;
1334 		if (*path1 != '/')
1335 			tmp = *pwd;
1336 
1337 		path1 = make_absolute(path1, *pwd);
1338 		err = do_globbed_ls(conn, path1, tmp, lflag);
1339 		break;
1340 	case I_DF:
1341 		/* Default to current directory if no path specified */
1342 		if (path1 == NULL)
1343 			path1 = xstrdup(*pwd);
1344 		path1 = make_absolute(path1, *pwd);
1345 		err = do_df(conn, path1, hflag, iflag);
1346 		break;
1347 	case I_LCHDIR:
1348 		if (chdir(path1) == -1) {
1349 			error("Couldn't change local directory to "
1350 			    "\"%s\": %s", path1, strerror(errno));
1351 			err = 1;
1352 		}
1353 		break;
1354 	case I_LMKDIR:
1355 		if (mkdir(path1, 0777) == -1) {
1356 			error("Couldn't create local directory "
1357 			    "\"%s\": %s", path1, strerror(errno));
1358 			err = 1;
1359 		}
1360 		break;
1361 	case I_LLS:
1362 		local_do_ls(cmd);
1363 		break;
1364 	case I_SHELL:
1365 		local_do_shell(cmd);
1366 		break;
1367 	case I_LUMASK:
1368 		umask(n_arg);
1369 		printf("Local umask: %03lo\n", n_arg);
1370 		break;
1371 	case I_CHMOD:
1372 		path1 = make_absolute(path1, *pwd);
1373 		attrib_clear(&a);
1374 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1375 		a.perm = n_arg;
1376 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1377 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1378 			printf("Changing mode on %s\n", g.gl_pathv[i]);
1379 			err = do_setstat(conn, g.gl_pathv[i], &a);
1380 			if (err != 0 && err_abort)
1381 				break;
1382 		}
1383 		break;
1384 	case I_CHOWN:
1385 	case I_CHGRP:
1386 		path1 = make_absolute(path1, *pwd);
1387 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1388 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1389 			if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1390 				if (err_abort) {
1391 					err = -1;
1392 					break;
1393 				} else
1394 					continue;
1395 			}
1396 			if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1397 				error("Can't get current ownership of "
1398 				    "remote file \"%s\"", g.gl_pathv[i]);
1399 				if (err_abort) {
1400 					err = -1;
1401 					break;
1402 				} else
1403 					continue;
1404 			}
1405 			aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1406 			if (cmdnum == I_CHOWN) {
1407 				printf("Changing owner on %s\n", g.gl_pathv[i]);
1408 				aa->uid = n_arg;
1409 			} else {
1410 				printf("Changing group on %s\n", g.gl_pathv[i]);
1411 				aa->gid = n_arg;
1412 			}
1413 			err = do_setstat(conn, g.gl_pathv[i], aa);
1414 			if (err != 0 && err_abort)
1415 				break;
1416 		}
1417 		break;
1418 	case I_PWD:
1419 		printf("Remote working directory: %s\n", *pwd);
1420 		break;
1421 	case I_LPWD:
1422 		if (!getcwd(path_buf, sizeof(path_buf))) {
1423 			error("Couldn't get local cwd: %s", strerror(errno));
1424 			err = -1;
1425 			break;
1426 		}
1427 		printf("Local working directory: %s\n", path_buf);
1428 		break;
1429 	case I_QUIT:
1430 		/* Processed below */
1431 		break;
1432 	case I_HELP:
1433 		help();
1434 		break;
1435 	case I_VERSION:
1436 		printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1437 		break;
1438 	case I_PROGRESS:
1439 		showprogress = !showprogress;
1440 		if (showprogress)
1441 			printf("Progress meter enabled\n");
1442 		else
1443 			printf("Progress meter disabled\n");
1444 		break;
1445 	default:
1446 		fatal("%d is not implemented", cmdnum);
1447 	}
1448 
1449 	if (g.gl_pathc)
1450 		globfree(&g);
1451 	if (path1)
1452 		xfree(path1);
1453 	if (path2)
1454 		xfree(path2);
1455 
1456 	/* If an unignored error occurs in batch mode we should abort. */
1457 	if (err_abort && err != 0)
1458 		return (-1);
1459 	else if (cmdnum == I_QUIT)
1460 		return (1);
1461 
1462 	return (0);
1463 }
1464 
1465 #ifdef USE_LIBEDIT
1466 static char *
1467 prompt(EditLine *el)
1468 {
1469 	return ("sftp> ");
1470 }
1471 #endif
1472 
1473 int
1474 interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1475 {
1476 	char *pwd;
1477 	char *dir = NULL;
1478 	char cmd[2048];
1479 	struct sftp_conn *conn;
1480 	int err, interactive;
1481 	EditLine *el = NULL;
1482 #ifdef USE_LIBEDIT
1483 	History *hl = NULL;
1484 	HistEvent hev;
1485 	extern char *__progname;
1486 
1487 	if (!batchmode && isatty(STDIN_FILENO)) {
1488 		if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1489 			fatal("Couldn't initialise editline");
1490 		if ((hl = history_init()) == NULL)
1491 			fatal("Couldn't initialise editline history");
1492 		history(hl, &hev, H_SETSIZE, 100);
1493 		el_set(el, EL_HIST, history, hl);
1494 
1495 		el_set(el, EL_PROMPT, prompt);
1496 		el_set(el, EL_EDITOR, "emacs");
1497 		el_set(el, EL_TERMINAL, NULL);
1498 		el_set(el, EL_SIGNAL, 1);
1499 		el_source(el, NULL);
1500 	}
1501 #endif /* USE_LIBEDIT */
1502 
1503 	conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1504 	if (conn == NULL)
1505 		fatal("Couldn't initialise connection to server");
1506 
1507 	pwd = do_realpath(conn, ".");
1508 	if (pwd == NULL)
1509 		fatal("Need cwd");
1510 
1511 	if (file1 != NULL) {
1512 		dir = xstrdup(file1);
1513 		dir = make_absolute(dir, pwd);
1514 
1515 		if (remote_is_dir(conn, dir) && file2 == NULL) {
1516 			printf("Changing to: %s\n", dir);
1517 			snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1518 			if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1519 				xfree(dir);
1520 				xfree(pwd);
1521 				xfree(conn);
1522 				return (-1);
1523 			}
1524 		} else {
1525 			if (file2 == NULL)
1526 				snprintf(cmd, sizeof cmd, "get %s", dir);
1527 			else
1528 				snprintf(cmd, sizeof cmd, "get %s %s", dir,
1529 				    file2);
1530 
1531 			err = parse_dispatch_command(conn, cmd, &pwd, 1);
1532 			xfree(dir);
1533 			xfree(pwd);
1534 			xfree(conn);
1535 			return (err);
1536 		}
1537 		xfree(dir);
1538 	}
1539 
1540 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1541 	setvbuf(stdout, NULL, _IOLBF, 0);
1542 	setvbuf(infile, NULL, _IOLBF, 0);
1543 #else
1544 	setlinebuf(stdout);
1545 	setlinebuf(infile);
1546 #endif
1547 
1548 	interactive = !batchmode && isatty(STDIN_FILENO);
1549 	err = 0;
1550 	for (;;) {
1551 		char *cp;
1552 
1553 		signal(SIGINT, SIG_IGN);
1554 
1555 		if (el == NULL) {
1556 			if (interactive)
1557 				printf("sftp> ");
1558 			if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1559 				if (interactive)
1560 					printf("\n");
1561 				break;
1562 			}
1563 			if (!interactive) { /* Echo command */
1564 				printf("sftp> %s", cmd);
1565 				if (strlen(cmd) > 0 &&
1566 				    cmd[strlen(cmd) - 1] != '\n')
1567 					printf("\n");
1568 			}
1569 		} else {
1570 #ifdef USE_LIBEDIT
1571 			const char *line;
1572 			int count = 0;
1573 
1574 			if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1575 				printf("\n");
1576  				break;
1577 			}
1578 			history(hl, &hev, H_ENTER, line);
1579 			if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1580 				fprintf(stderr, "Error: input line too long\n");
1581 				continue;
1582 			}
1583 #endif /* USE_LIBEDIT */
1584 		}
1585 
1586 		cp = strrchr(cmd, '\n');
1587 		if (cp)
1588 			*cp = '\0';
1589 
1590 		/* Handle user interrupts gracefully during commands */
1591 		interrupted = 0;
1592 		signal(SIGINT, cmd_interrupt);
1593 
1594 		err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1595 		if (err != 0)
1596 			break;
1597 	}
1598 	xfree(pwd);
1599 	xfree(conn);
1600 
1601 #ifdef USE_LIBEDIT
1602 	if (el != NULL)
1603 		el_end(el);
1604 #endif /* USE_LIBEDIT */
1605 
1606 	/* err == 1 signifies normal "quit" exit */
1607 	return (err >= 0 ? 0 : -1);
1608 }
1609 
1610 static void
1611 connect_to_server(char *path, char **args, int *in, int *out)
1612 {
1613 	int c_in, c_out;
1614 
1615 #ifdef USE_PIPES
1616 	int pin[2], pout[2];
1617 
1618 	if ((pipe(pin) == -1) || (pipe(pout) == -1))
1619 		fatal("pipe: %s", strerror(errno));
1620 	*in = pin[0];
1621 	*out = pout[1];
1622 	c_in = pout[0];
1623 	c_out = pin[1];
1624 #else /* USE_PIPES */
1625 	int inout[2];
1626 
1627 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1628 		fatal("socketpair: %s", strerror(errno));
1629 	*in = *out = inout[0];
1630 	c_in = c_out = inout[1];
1631 #endif /* USE_PIPES */
1632 
1633 	if ((sshpid = fork()) == -1)
1634 		fatal("fork: %s", strerror(errno));
1635 	else if (sshpid == 0) {
1636 		if ((dup2(c_in, STDIN_FILENO) == -1) ||
1637 		    (dup2(c_out, STDOUT_FILENO) == -1)) {
1638 			fprintf(stderr, "dup2: %s\n", strerror(errno));
1639 			_exit(1);
1640 		}
1641 		close(*in);
1642 		close(*out);
1643 		close(c_in);
1644 		close(c_out);
1645 
1646 		/*
1647 		 * The underlying ssh is in the same process group, so we must
1648 		 * ignore SIGINT if we want to gracefully abort commands,
1649 		 * otherwise the signal will make it to the ssh process and
1650 		 * kill it too
1651 		 */
1652 		signal(SIGINT, SIG_IGN);
1653 		execvp(path, args);
1654 		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1655 		_exit(1);
1656 	}
1657 
1658 	signal(SIGTERM, killchild);
1659 	signal(SIGINT, killchild);
1660 	signal(SIGHUP, killchild);
1661 	close(c_in);
1662 	close(c_out);
1663 }
1664 
1665 static void
1666 usage(void)
1667 {
1668 	extern char *__progname;
1669 
1670 	fprintf(stderr,
1671 	    "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
1672 	    "            [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
1673 	    "            [-S program] [-s subsystem | sftp_server] host\n"
1674 	    "       %s [user@]host[:file ...]\n"
1675 	    "       %s [user@]host[:dir[/]]\n"
1676 	    "       %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
1677 	exit(1);
1678 }
1679 
1680 int
1681 main(int argc, char **argv)
1682 {
1683 	int in, out, ch, err;
1684 	char *host, *userhost, *cp, *file2 = NULL;
1685 	int debug_level = 0, sshver = 2;
1686 	char *file1 = NULL, *sftp_server = NULL;
1687 	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1688 	LogLevel ll = SYSLOG_LEVEL_INFO;
1689 	arglist args;
1690 	extern int optind;
1691 	extern char *optarg;
1692 
1693 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1694 	sanitise_stdfd();
1695 
1696 	__progname = ssh_get_progname(argv[0]);
1697 	memset(&args, '\0', sizeof(args));
1698 	args.list = NULL;
1699 	addargs(&args, "%s", ssh_program);
1700 	addargs(&args, "-oForwardX11 no");
1701 	addargs(&args, "-oForwardAgent no");
1702 	addargs(&args, "-oPermitLocalCommand no");
1703 	addargs(&args, "-oClearAllForwardings yes");
1704 
1705 	ll = SYSLOG_LEVEL_INFO;
1706 	infile = stdin;
1707 
1708 	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
1709 		switch (ch) {
1710 		case 'C':
1711 			addargs(&args, "-C");
1712 			break;
1713 		case 'v':
1714 			if (debug_level < 3) {
1715 				addargs(&args, "-v");
1716 				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1717 			}
1718 			debug_level++;
1719 			break;
1720 		case 'F':
1721 		case 'o':
1722 			addargs(&args, "-%c%s", ch, optarg);
1723 			break;
1724 		case '1':
1725 			sshver = 1;
1726 			if (sftp_server == NULL)
1727 				sftp_server = _PATH_SFTP_SERVER;
1728 			break;
1729 		case 's':
1730 			sftp_server = optarg;
1731 			break;
1732 		case 'S':
1733 			ssh_program = optarg;
1734 			replacearg(&args, 0, "%s", ssh_program);
1735 			break;
1736 		case 'b':
1737 			if (batchmode)
1738 				fatal("Batch file already specified.");
1739 
1740 			/* Allow "-" as stdin */
1741 			if (strcmp(optarg, "-") != 0 &&
1742 			    (infile = fopen(optarg, "r")) == NULL)
1743 				fatal("%s (%s).", strerror(errno), optarg);
1744 			showprogress = 0;
1745 			batchmode = 1;
1746 			addargs(&args, "-obatchmode yes");
1747 			break;
1748 		case 'P':
1749 			sftp_direct = optarg;
1750 			break;
1751 		case 'B':
1752 			copy_buffer_len = strtol(optarg, &cp, 10);
1753 			if (copy_buffer_len == 0 || *cp != '\0')
1754 				fatal("Invalid buffer size \"%s\"", optarg);
1755 			break;
1756 		case 'R':
1757 			num_requests = strtol(optarg, &cp, 10);
1758 			if (num_requests == 0 || *cp != '\0')
1759 				fatal("Invalid number of requests \"%s\"",
1760 				    optarg);
1761 			break;
1762 		case 'h':
1763 		default:
1764 			usage();
1765 		}
1766 	}
1767 
1768 	if (!isatty(STDERR_FILENO))
1769 		showprogress = 0;
1770 
1771 	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1772 
1773 	if (sftp_direct == NULL) {
1774 		if (optind == argc || argc > (optind + 2))
1775 			usage();
1776 
1777 		userhost = xstrdup(argv[optind]);
1778 		file2 = argv[optind+1];
1779 
1780 		if ((host = strrchr(userhost, '@')) == NULL)
1781 			host = userhost;
1782 		else {
1783 			*host++ = '\0';
1784 			if (!userhost[0]) {
1785 				fprintf(stderr, "Missing username\n");
1786 				usage();
1787 			}
1788 			addargs(&args, "-l%s", userhost);
1789 		}
1790 
1791 		if ((cp = colon(host)) != NULL) {
1792 			*cp++ = '\0';
1793 			file1 = cp;
1794 		}
1795 
1796 		host = cleanhostname(host);
1797 		if (!*host) {
1798 			fprintf(stderr, "Missing hostname\n");
1799 			usage();
1800 		}
1801 
1802 		addargs(&args, "-oProtocol %d", sshver);
1803 
1804 		/* no subsystem if the server-spec contains a '/' */
1805 		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1806 			addargs(&args, "-s");
1807 
1808 		addargs(&args, "%s", host);
1809 		addargs(&args, "%s", (sftp_server != NULL ?
1810 		    sftp_server : "sftp"));
1811 
1812 		if (!batchmode)
1813 			fprintf(stderr, "Connecting to %s...\n", host);
1814 		connect_to_server(ssh_program, args.list, &in, &out);
1815 	} else {
1816 		args.list = NULL;
1817 		addargs(&args, "sftp-server");
1818 
1819 		if (!batchmode)
1820 			fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1821 		connect_to_server(sftp_direct, args.list, &in, &out);
1822 	}
1823 	freeargs(&args);
1824 
1825 	err = interactive_loop(in, out, file1, file2);
1826 
1827 #if !defined(USE_PIPES)
1828 	shutdown(in, SHUT_RDWR);
1829 	shutdown(out, SHUT_RDWR);
1830 #endif
1831 
1832 	close(in);
1833 	close(out);
1834 	if (batchmode)
1835 		fclose(infile);
1836 
1837 	while (waitpid(sshpid, NULL, 0) == -1)
1838 		if (errno != EINTR)
1839 			fatal("Couldn't wait for ssh process: %s",
1840 			    strerror(errno));
1841 
1842 	exit(err == 0 ? 0 : 1);
1843 }
1844