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