sftp.c revision 294693
1/* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt 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 HAVE_LIBGEN_H
39#include <libgen.h>
40#endif
41#ifdef HAVE_LOCALE_H
42# include <locale.h>
43#endif
44#ifdef USE_LIBEDIT
45#include <histedit.h>
46#else
47typedef void EditLine;
48#endif
49#include <signal.h>
50#include <stdlib.h>
51#include <stdio.h>
52#include <string.h>
53#include <unistd.h>
54#include <stdarg.h>
55
56#ifdef HAVE_UTIL_H
57# include <util.h>
58#endif
59
60#include "xmalloc.h"
61#include "log.h"
62#include "pathnames.h"
63#include "misc.h"
64
65#include "sftp.h"
66#include "buffer.h"
67#include "sftp-common.h"
68#include "sftp-client.h"
69
70#define DEFAULT_COPY_BUFLEN	32768	/* Size of buffer for up/download */
71#define DEFAULT_NUM_REQUESTS	64	/* # concurrent outstanding requests */
72
73/* File to read commands from */
74FILE* infile;
75
76/* Are we in batchfile mode? */
77int batchmode = 0;
78
79/* PID of ssh transport process */
80static pid_t sshpid = -1;
81
82/* Suppress diagnositic messages */
83int quiet = 0;
84
85/* This is set to 0 if the progressmeter is not desired. */
86int showprogress = 1;
87
88/* When this option is set, we always recursively download/upload directories */
89int global_rflag = 0;
90
91/* When this option is set, we resume download if possible */
92int global_aflag = 0;
93
94/* When this option is set, the file transfers will always preserve times */
95int global_pflag = 0;
96
97/* When this option is set, transfers will have fsync() called on each file */
98int global_fflag = 0;
99
100/* SIGINT received during command processing */
101volatile sig_atomic_t interrupted = 0;
102
103/* I wish qsort() took a separate ctx for the comparison function...*/
104int sort_flag;
105
106/* Context used for commandline completion */
107struct complete_ctx {
108	struct sftp_conn *conn;
109	char **remote_pathp;
110};
111
112int remote_glob(struct sftp_conn *, const char *, int,
113    int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
114
115extern char *__progname;
116
117/* Separators for interactive commands */
118#define WHITESPACE " \t\r\n"
119
120/* ls flags */
121#define LS_LONG_VIEW	0x0001	/* Full view ala ls -l */
122#define LS_SHORT_VIEW	0x0002	/* Single row view ala ls -1 */
123#define LS_NUMERIC_VIEW	0x0004	/* Long view with numeric uid/gid */
124#define LS_NAME_SORT	0x0008	/* Sort by name (default) */
125#define LS_TIME_SORT	0x0010	/* Sort by mtime */
126#define LS_SIZE_SORT	0x0020	/* Sort by file size */
127#define LS_REVERSE_SORT	0x0040	/* Reverse sort order */
128#define LS_SHOW_ALL	0x0080	/* Don't skip filenames starting with '.' */
129#define LS_SI_UNITS	0x0100	/* Display sizes as K, M, G, etc. */
130
131#define VIEW_FLAGS	(LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
132#define SORT_FLAGS	(LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
133
134/* Commands for interactive mode */
135enum sftp_command {
136	I_CHDIR = 1,
137	I_CHGRP,
138	I_CHMOD,
139	I_CHOWN,
140	I_DF,
141	I_GET,
142	I_HELP,
143	I_LCHDIR,
144	I_LINK,
145	I_LLS,
146	I_LMKDIR,
147	I_LPWD,
148	I_LS,
149	I_LUMASK,
150	I_MKDIR,
151	I_PUT,
152	I_PWD,
153	I_QUIT,
154	I_RENAME,
155	I_RM,
156	I_RMDIR,
157	I_SHELL,
158	I_SYMLINK,
159	I_VERSION,
160	I_PROGRESS,
161	I_REGET,
162};
163
164struct CMD {
165	const char *c;
166	const int n;
167	const int t;
168};
169
170/* Type of completion */
171#define NOARGS	0
172#define REMOTE	1
173#define LOCAL	2
174
175static const struct CMD cmds[] = {
176	{ "bye",	I_QUIT,		NOARGS	},
177	{ "cd",		I_CHDIR,	REMOTE	},
178	{ "chdir",	I_CHDIR,	REMOTE	},
179	{ "chgrp",	I_CHGRP,	REMOTE	},
180	{ "chmod",	I_CHMOD,	REMOTE	},
181	{ "chown",	I_CHOWN,	REMOTE	},
182	{ "df",		I_DF,		REMOTE	},
183	{ "dir",	I_LS,		REMOTE	},
184	{ "exit",	I_QUIT,		NOARGS	},
185	{ "get",	I_GET,		REMOTE	},
186	{ "help",	I_HELP,		NOARGS	},
187	{ "lcd",	I_LCHDIR,	LOCAL	},
188	{ "lchdir",	I_LCHDIR,	LOCAL	},
189	{ "lls",	I_LLS,		LOCAL	},
190	{ "lmkdir",	I_LMKDIR,	LOCAL	},
191	{ "ln",		I_LINK,		REMOTE	},
192	{ "lpwd",	I_LPWD,		LOCAL	},
193	{ "ls",		I_LS,		REMOTE	},
194	{ "lumask",	I_LUMASK,	NOARGS	},
195	{ "mkdir",	I_MKDIR,	REMOTE	},
196	{ "mget",	I_GET,		REMOTE	},
197	{ "mput",	I_PUT,		LOCAL	},
198	{ "progress",	I_PROGRESS,	NOARGS	},
199	{ "put",	I_PUT,		LOCAL	},
200	{ "pwd",	I_PWD,		REMOTE	},
201	{ "quit",	I_QUIT,		NOARGS	},
202	{ "reget",	I_REGET,	REMOTE	},
203	{ "rename",	I_RENAME,	REMOTE	},
204	{ "rm",		I_RM,		REMOTE	},
205	{ "rmdir",	I_RMDIR,	REMOTE	},
206	{ "symlink",	I_SYMLINK,	REMOTE	},
207	{ "version",	I_VERSION,	NOARGS	},
208	{ "!",		I_SHELL,	NOARGS	},
209	{ "?",		I_HELP,		NOARGS	},
210	{ NULL,		-1,		-1	}
211};
212
213int interactive_loop(struct sftp_conn *, char *file1, char *file2);
214
215/* ARGSUSED */
216static void
217killchild(int signo)
218{
219	if (sshpid > 1) {
220		kill(sshpid, SIGTERM);
221		waitpid(sshpid, NULL, 0);
222	}
223
224	_exit(1);
225}
226
227/* ARGSUSED */
228static void
229cmd_interrupt(int signo)
230{
231	const char msg[] = "\rInterrupt  \n";
232	int olderrno = errno;
233
234	(void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
235	interrupted = 1;
236	errno = olderrno;
237}
238
239static void
240help(void)
241{
242	printf("Available commands:\n"
243	    "bye                                Quit sftp\n"
244	    "cd path                            Change remote directory to 'path'\n"
245	    "chgrp grp path                     Change group of file 'path' to 'grp'\n"
246	    "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
247	    "chown own path                     Change owner of file 'path' to 'own'\n"
248	    "df [-hi] [path]                    Display statistics for current directory or\n"
249	    "                                   filesystem containing 'path'\n"
250	    "exit                               Quit sftp\n"
251	    "get [-Ppr] remote [local]          Download file\n"
252	    "reget remote [local]		Resume download file\n"
253	    "help                               Display this help text\n"
254	    "lcd path                           Change local directory to 'path'\n"
255	    "lls [ls-options [path]]            Display local directory listing\n"
256	    "lmkdir path                        Create local directory\n"
257	    "ln [-s] oldpath newpath            Link remote file (-s for symlink)\n"
258	    "lpwd                               Print local working directory\n"
259	    "ls [-1afhlnrSt] [path]             Display remote directory listing\n"
260	    "lumask umask                       Set local umask to 'umask'\n"
261	    "mkdir path                         Create remote directory\n"
262	    "progress                           Toggle display of progress meter\n"
263	    "put [-Ppr] local [remote]          Upload file\n"
264	    "pwd                                Display remote working directory\n"
265	    "quit                               Quit sftp\n"
266	    "rename oldpath newpath             Rename remote file\n"
267	    "rm path                            Delete remote file\n"
268	    "rmdir path                         Remove remote directory\n"
269	    "symlink oldpath newpath            Symlink remote file\n"
270	    "version                            Show SFTP version\n"
271	    "!command                           Execute 'command' in local shell\n"
272	    "!                                  Escape to local shell\n"
273	    "?                                  Synonym for help\n");
274}
275
276static void
277local_do_shell(const char *args)
278{
279	int status;
280	char *shell;
281	pid_t pid;
282
283	if (!*args)
284		args = NULL;
285
286	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
287		shell = _PATH_BSHELL;
288
289	if ((pid = fork()) == -1)
290		fatal("Couldn't fork: %s", strerror(errno));
291
292	if (pid == 0) {
293		/* XXX: child has pipe fds to ssh subproc open - issue? */
294		if (args) {
295			debug3("Executing %s -c \"%s\"", shell, args);
296			execl(shell, shell, "-c", args, (char *)NULL);
297		} else {
298			debug3("Executing %s", shell);
299			execl(shell, shell, (char *)NULL);
300		}
301		fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
302		    strerror(errno));
303		_exit(1);
304	}
305	while (waitpid(pid, &status, 0) == -1)
306		if (errno != EINTR)
307			fatal("Couldn't wait for child: %s", strerror(errno));
308	if (!WIFEXITED(status))
309		error("Shell exited abnormally");
310	else if (WEXITSTATUS(status))
311		error("Shell exited with status %d", WEXITSTATUS(status));
312}
313
314static void
315local_do_ls(const char *args)
316{
317	if (!args || !*args)
318		local_do_shell(_PATH_LS);
319	else {
320		int len = strlen(_PATH_LS " ") + strlen(args) + 1;
321		char *buf = xmalloc(len);
322
323		/* XXX: quoting - rip quoting code from ftp? */
324		snprintf(buf, len, _PATH_LS " %s", args);
325		local_do_shell(buf);
326		free(buf);
327	}
328}
329
330/* Strip one path (usually the pwd) from the start of another */
331static char *
332path_strip(char *path, char *strip)
333{
334	size_t len;
335
336	if (strip == NULL)
337		return (xstrdup(path));
338
339	len = strlen(strip);
340	if (strncmp(path, strip, len) == 0) {
341		if (strip[len - 1] != '/' && path[len] == '/')
342			len++;
343		return (xstrdup(path + len));
344	}
345
346	return (xstrdup(path));
347}
348
349static char *
350make_absolute(char *p, char *pwd)
351{
352	char *abs_str;
353
354	/* Derelativise */
355	if (p && p[0] != '/') {
356		abs_str = path_append(pwd, p);
357		free(p);
358		return(abs_str);
359	} else
360		return(p);
361}
362
363static int
364parse_getput_flags(const char *cmd, char **argv, int argc,
365    int *aflag, int *fflag, int *pflag, int *rflag)
366{
367	extern int opterr, optind, optopt, optreset;
368	int ch;
369
370	optind = optreset = 1;
371	opterr = 0;
372
373	*aflag = *fflag = *rflag = *pflag = 0;
374	while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
375		switch (ch) {
376		case 'a':
377			*aflag = 1;
378			break;
379		case 'f':
380			*fflag = 1;
381			break;
382		case 'p':
383		case 'P':
384			*pflag = 1;
385			break;
386		case 'r':
387		case 'R':
388			*rflag = 1;
389			break;
390		default:
391			error("%s: Invalid flag -%c", cmd, optopt);
392			return -1;
393		}
394	}
395
396	return optind;
397}
398
399static int
400parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
401{
402	extern int opterr, optind, optopt, optreset;
403	int ch;
404
405	optind = optreset = 1;
406	opterr = 0;
407
408	*sflag = 0;
409	while ((ch = getopt(argc, argv, "s")) != -1) {
410		switch (ch) {
411		case 's':
412			*sflag = 1;
413			break;
414		default:
415			error("%s: Invalid flag -%c", cmd, optopt);
416			return -1;
417		}
418	}
419
420	return optind;
421}
422
423static int
424parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
425{
426	extern int opterr, optind, optopt, optreset;
427	int ch;
428
429	optind = optreset = 1;
430	opterr = 0;
431
432	*lflag = 0;
433	while ((ch = getopt(argc, argv, "l")) != -1) {
434		switch (ch) {
435		case 'l':
436			*lflag = 1;
437			break;
438		default:
439			error("%s: Invalid flag -%c", cmd, optopt);
440			return -1;
441		}
442	}
443
444	return optind;
445}
446
447static int
448parse_ls_flags(char **argv, int argc, int *lflag)
449{
450	extern int opterr, optind, optopt, optreset;
451	int ch;
452
453	optind = optreset = 1;
454	opterr = 0;
455
456	*lflag = LS_NAME_SORT;
457	while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
458		switch (ch) {
459		case '1':
460			*lflag &= ~VIEW_FLAGS;
461			*lflag |= LS_SHORT_VIEW;
462			break;
463		case 'S':
464			*lflag &= ~SORT_FLAGS;
465			*lflag |= LS_SIZE_SORT;
466			break;
467		case 'a':
468			*lflag |= LS_SHOW_ALL;
469			break;
470		case 'f':
471			*lflag &= ~SORT_FLAGS;
472			break;
473		case 'h':
474			*lflag |= LS_SI_UNITS;
475			break;
476		case 'l':
477			*lflag &= ~LS_SHORT_VIEW;
478			*lflag |= LS_LONG_VIEW;
479			break;
480		case 'n':
481			*lflag &= ~LS_SHORT_VIEW;
482			*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
483			break;
484		case 'r':
485			*lflag |= LS_REVERSE_SORT;
486			break;
487		case 't':
488			*lflag &= ~SORT_FLAGS;
489			*lflag |= LS_TIME_SORT;
490			break;
491		default:
492			error("ls: Invalid flag -%c", optopt);
493			return -1;
494		}
495	}
496
497	return optind;
498}
499
500static int
501parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
502{
503	extern int opterr, optind, optopt, optreset;
504	int ch;
505
506	optind = optreset = 1;
507	opterr = 0;
508
509	*hflag = *iflag = 0;
510	while ((ch = getopt(argc, argv, "hi")) != -1) {
511		switch (ch) {
512		case 'h':
513			*hflag = 1;
514			break;
515		case 'i':
516			*iflag = 1;
517			break;
518		default:
519			error("%s: Invalid flag -%c", cmd, optopt);
520			return -1;
521		}
522	}
523
524	return optind;
525}
526
527static int
528parse_no_flags(const char *cmd, char **argv, int argc)
529{
530	extern int opterr, optind, optopt, optreset;
531	int ch;
532
533	optind = optreset = 1;
534	opterr = 0;
535
536	while ((ch = getopt(argc, argv, "")) != -1) {
537		switch (ch) {
538		default:
539			error("%s: Invalid flag -%c", cmd, optopt);
540			return -1;
541		}
542	}
543
544	return optind;
545}
546
547static int
548is_dir(char *path)
549{
550	struct stat sb;
551
552	/* XXX: report errors? */
553	if (stat(path, &sb) == -1)
554		return(0);
555
556	return(S_ISDIR(sb.st_mode));
557}
558
559static int
560remote_is_dir(struct sftp_conn *conn, char *path)
561{
562	Attrib *a;
563
564	/* XXX: report errors? */
565	if ((a = do_stat(conn, path, 1)) == NULL)
566		return(0);
567	if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
568		return(0);
569	return(S_ISDIR(a->perm));
570}
571
572/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
573static int
574pathname_is_dir(char *pathname)
575{
576	size_t l = strlen(pathname);
577
578	return l > 0 && pathname[l - 1] == '/';
579}
580
581static int
582process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
583    int pflag, int rflag, int resume, int fflag)
584{
585	char *abs_src = NULL;
586	char *abs_dst = NULL;
587	glob_t g;
588	char *filename, *tmp=NULL;
589	int i, err = 0;
590
591	abs_src = xstrdup(src);
592	abs_src = make_absolute(abs_src, pwd);
593	memset(&g, 0, sizeof(g));
594
595	debug3("Looking up %s", abs_src);
596	if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
597		error("File \"%s\" not found.", abs_src);
598		err = -1;
599		goto out;
600	}
601
602	/*
603	 * If multiple matches then dst must be a directory or
604	 * unspecified.
605	 */
606	if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
607		error("Multiple source paths, but destination "
608		    "\"%s\" is not a directory", dst);
609		err = -1;
610		goto out;
611	}
612
613	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
614		tmp = xstrdup(g.gl_pathv[i]);
615		if ((filename = basename(tmp)) == NULL) {
616			error("basename %s: %s", tmp, strerror(errno));
617			free(tmp);
618			err = -1;
619			goto out;
620		}
621
622		if (g.gl_matchc == 1 && dst) {
623			if (is_dir(dst)) {
624				abs_dst = path_append(dst, filename);
625			} else {
626				abs_dst = xstrdup(dst);
627			}
628		} else if (dst) {
629			abs_dst = path_append(dst, filename);
630		} else {
631			abs_dst = xstrdup(filename);
632		}
633		free(tmp);
634
635		resume |= global_aflag;
636		if (!quiet && resume)
637			printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
638		else if (!quiet && !resume)
639			printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
640		if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
641			if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
642			    pflag || global_pflag, 1, resume,
643			    fflag || global_fflag) == -1)
644				err = -1;
645		} else {
646			if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
647			    pflag || global_pflag, resume,
648			    fflag || global_fflag) == -1)
649				err = -1;
650		}
651		free(abs_dst);
652		abs_dst = NULL;
653	}
654
655out:
656	free(abs_src);
657	globfree(&g);
658	return(err);
659}
660
661static int
662process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
663    int pflag, int rflag, int fflag)
664{
665	char *tmp_dst = NULL;
666	char *abs_dst = NULL;
667	char *tmp = NULL, *filename = NULL;
668	glob_t g;
669	int err = 0;
670	int i, dst_is_dir = 1;
671	struct stat sb;
672
673	if (dst) {
674		tmp_dst = xstrdup(dst);
675		tmp_dst = make_absolute(tmp_dst, pwd);
676	}
677
678	memset(&g, 0, sizeof(g));
679	debug3("Looking up %s", src);
680	if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
681		error("File \"%s\" not found.", src);
682		err = -1;
683		goto out;
684	}
685
686	/* If we aren't fetching to pwd then stash this status for later */
687	if (tmp_dst != NULL)
688		dst_is_dir = remote_is_dir(conn, tmp_dst);
689
690	/* If multiple matches, dst may be directory or unspecified */
691	if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
692		error("Multiple paths match, but destination "
693		    "\"%s\" is not a directory", tmp_dst);
694		err = -1;
695		goto out;
696	}
697
698	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
699		if (stat(g.gl_pathv[i], &sb) == -1) {
700			err = -1;
701			error("stat %s: %s", g.gl_pathv[i], strerror(errno));
702			continue;
703		}
704
705		tmp = xstrdup(g.gl_pathv[i]);
706		if ((filename = basename(tmp)) == NULL) {
707			error("basename %s: %s", tmp, strerror(errno));
708			free(tmp);
709			err = -1;
710			goto out;
711		}
712
713		if (g.gl_matchc == 1 && tmp_dst) {
714			/* If directory specified, append filename */
715			if (dst_is_dir)
716				abs_dst = path_append(tmp_dst, filename);
717			else
718				abs_dst = xstrdup(tmp_dst);
719		} else if (tmp_dst) {
720			abs_dst = path_append(tmp_dst, filename);
721		} else {
722			abs_dst = make_absolute(xstrdup(filename), pwd);
723		}
724		free(tmp);
725
726		if (!quiet)
727			printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
728		if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
729			if (upload_dir(conn, g.gl_pathv[i], abs_dst,
730			    pflag || global_pflag, 1,
731			    fflag || global_fflag) == -1)
732				err = -1;
733		} else {
734			if (do_upload(conn, g.gl_pathv[i], abs_dst,
735			    pflag || global_pflag,
736			    fflag || global_fflag) == -1)
737				err = -1;
738		}
739	}
740
741out:
742	free(abs_dst);
743	free(tmp_dst);
744	globfree(&g);
745	return(err);
746}
747
748static int
749sdirent_comp(const void *aa, const void *bb)
750{
751	SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
752	SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
753	int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
754
755#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
756	if (sort_flag & LS_NAME_SORT)
757		return (rmul * strcmp(a->filename, b->filename));
758	else if (sort_flag & LS_TIME_SORT)
759		return (rmul * NCMP(a->a.mtime, b->a.mtime));
760	else if (sort_flag & LS_SIZE_SORT)
761		return (rmul * NCMP(a->a.size, b->a.size));
762
763	fatal("Unknown ls sort type");
764}
765
766/* sftp ls.1 replacement for directories */
767static int
768do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
769{
770	int n;
771	u_int c = 1, colspace = 0, columns = 1;
772	SFTP_DIRENT **d;
773
774	if ((n = do_readdir(conn, path, &d)) != 0)
775		return (n);
776
777	if (!(lflag & LS_SHORT_VIEW)) {
778		u_int m = 0, width = 80;
779		struct winsize ws;
780		char *tmp;
781
782		/* Count entries for sort and find longest filename */
783		for (n = 0; d[n] != NULL; n++) {
784			if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
785				m = MAX(m, strlen(d[n]->filename));
786		}
787
788		/* Add any subpath that also needs to be counted */
789		tmp = path_strip(path, strip_path);
790		m += strlen(tmp);
791		free(tmp);
792
793		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
794			width = ws.ws_col;
795
796		columns = width / (m + 2);
797		columns = MAX(columns, 1);
798		colspace = width / columns;
799		colspace = MIN(colspace, width);
800	}
801
802	if (lflag & SORT_FLAGS) {
803		for (n = 0; d[n] != NULL; n++)
804			;	/* count entries */
805		sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
806		qsort(d, n, sizeof(*d), sdirent_comp);
807	}
808
809	for (n = 0; d[n] != NULL && !interrupted; n++) {
810		char *tmp, *fname;
811
812		if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
813			continue;
814
815		tmp = path_append(path, d[n]->filename);
816		fname = path_strip(tmp, strip_path);
817		free(tmp);
818
819		if (lflag & LS_LONG_VIEW) {
820			if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
821				char *lname;
822				struct stat sb;
823
824				memset(&sb, 0, sizeof(sb));
825				attrib_to_stat(&d[n]->a, &sb);
826				lname = ls_file(fname, &sb, 1,
827				    (lflag & LS_SI_UNITS));
828				printf("%s\n", lname);
829				free(lname);
830			} else
831				printf("%s\n", d[n]->longname);
832		} else {
833			printf("%-*s", colspace, fname);
834			if (c >= columns) {
835				printf("\n");
836				c = 1;
837			} else
838				c++;
839		}
840
841		free(fname);
842	}
843
844	if (!(lflag & LS_LONG_VIEW) && (c != 1))
845		printf("\n");
846
847	free_sftp_dirents(d);
848	return (0);
849}
850
851/* sftp ls.1 replacement which handles path globs */
852static int
853do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
854    int lflag)
855{
856	char *fname, *lname;
857	glob_t g;
858	int err;
859	struct winsize ws;
860	u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
861
862	memset(&g, 0, sizeof(g));
863
864	if (remote_glob(conn, path,
865	    GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
866	    NULL, &g) ||
867	    (g.gl_pathc && !g.gl_matchc)) {
868		if (g.gl_pathc)
869			globfree(&g);
870		error("Can't ls: \"%s\" not found", path);
871		return -1;
872	}
873
874	if (interrupted)
875		goto out;
876
877	/*
878	 * If the glob returns a single match and it is a directory,
879	 * then just list its contents.
880	 */
881	if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
882	    S_ISDIR(g.gl_statv[0]->st_mode)) {
883		err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
884		globfree(&g);
885		return err;
886	}
887
888	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
889		width = ws.ws_col;
890
891	if (!(lflag & LS_SHORT_VIEW)) {
892		/* Count entries for sort and find longest filename */
893		for (i = 0; g.gl_pathv[i]; i++)
894			m = MAX(m, strlen(g.gl_pathv[i]));
895
896		columns = width / (m + 2);
897		columns = MAX(columns, 1);
898		colspace = width / columns;
899	}
900
901	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
902		fname = path_strip(g.gl_pathv[i], strip_path);
903		if (lflag & LS_LONG_VIEW) {
904			if (g.gl_statv[i] == NULL) {
905				error("no stat information for %s", fname);
906				continue;
907			}
908			lname = ls_file(fname, g.gl_statv[i], 1,
909			    (lflag & LS_SI_UNITS));
910			printf("%s\n", lname);
911			free(lname);
912		} else {
913			printf("%-*s", colspace, fname);
914			if (c >= columns) {
915				printf("\n");
916				c = 1;
917			} else
918				c++;
919		}
920		free(fname);
921	}
922
923	if (!(lflag & LS_LONG_VIEW) && (c != 1))
924		printf("\n");
925
926 out:
927	if (g.gl_pathc)
928		globfree(&g);
929
930	return 0;
931}
932
933static int
934do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
935{
936	struct sftp_statvfs st;
937	char s_used[FMT_SCALED_STRSIZE];
938	char s_avail[FMT_SCALED_STRSIZE];
939	char s_root[FMT_SCALED_STRSIZE];
940	char s_total[FMT_SCALED_STRSIZE];
941	unsigned long long ffree;
942
943	if (do_statvfs(conn, path, &st, 1) == -1)
944		return -1;
945	if (iflag) {
946		ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
947		printf("     Inodes        Used       Avail      "
948		    "(root)    %%Capacity\n");
949		printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
950		    (unsigned long long)st.f_files,
951		    (unsigned long long)(st.f_files - st.f_ffree),
952		    (unsigned long long)st.f_favail,
953		    (unsigned long long)st.f_ffree, ffree);
954	} else if (hflag) {
955		strlcpy(s_used, "error", sizeof(s_used));
956		strlcpy(s_avail, "error", sizeof(s_avail));
957		strlcpy(s_root, "error", sizeof(s_root));
958		strlcpy(s_total, "error", sizeof(s_total));
959		fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
960		fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
961		fmt_scaled(st.f_bfree * st.f_frsize, s_root);
962		fmt_scaled(st.f_blocks * st.f_frsize, s_total);
963		printf("    Size     Used    Avail   (root)    %%Capacity\n");
964		printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
965		    s_total, s_used, s_avail, s_root,
966		    (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
967		    st.f_blocks));
968	} else {
969		printf("        Size         Used        Avail       "
970		    "(root)    %%Capacity\n");
971		printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
972		    (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
973		    (unsigned long long)(st.f_frsize *
974		    (st.f_blocks - st.f_bfree) / 1024),
975		    (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
976		    (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
977		    (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
978		    st.f_blocks));
979	}
980	return 0;
981}
982
983/*
984 * Undo escaping of glob sequences in place. Used to undo extra escaping
985 * applied in makeargv() when the string is destined for a function that
986 * does not glob it.
987 */
988static void
989undo_glob_escape(char *s)
990{
991	size_t i, j;
992
993	for (i = j = 0;;) {
994		if (s[i] == '\0') {
995			s[j] = '\0';
996			return;
997		}
998		if (s[i] != '\\') {
999			s[j++] = s[i++];
1000			continue;
1001		}
1002		/* s[i] == '\\' */
1003		++i;
1004		switch (s[i]) {
1005		case '?':
1006		case '[':
1007		case '*':
1008		case '\\':
1009			s[j++] = s[i++];
1010			break;
1011		case '\0':
1012			s[j++] = '\\';
1013			s[j] = '\0';
1014			return;
1015		default:
1016			s[j++] = '\\';
1017			s[j++] = s[i++];
1018			break;
1019		}
1020	}
1021}
1022
1023/*
1024 * Split a string into an argument vector using sh(1)-style quoting,
1025 * comment and escaping rules, but with some tweaks to handle glob(3)
1026 * wildcards.
1027 * The "sloppy" flag allows for recovery from missing terminating quote, for
1028 * use in parsing incomplete commandlines during tab autocompletion.
1029 *
1030 * Returns NULL on error or a NULL-terminated array of arguments.
1031 *
1032 * If "lastquote" is not NULL, the quoting character used for the last
1033 * argument is placed in *lastquote ("\0", "'" or "\"").
1034 *
1035 * If "terminated" is not NULL, *terminated will be set to 1 when the
1036 * last argument's quote has been properly terminated or 0 otherwise.
1037 * This parameter is only of use if "sloppy" is set.
1038 */
1039#define MAXARGS 	128
1040#define MAXARGLEN	8192
1041static char **
1042makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1043    u_int *terminated)
1044{
1045	int argc, quot;
1046	size_t i, j;
1047	static char argvs[MAXARGLEN];
1048	static char *argv[MAXARGS + 1];
1049	enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1050
1051	*argcp = argc = 0;
1052	if (strlen(arg) > sizeof(argvs) - 1) {
1053 args_too_longs:
1054		error("string too long");
1055		return NULL;
1056	}
1057	if (terminated != NULL)
1058		*terminated = 1;
1059	if (lastquote != NULL)
1060		*lastquote = '\0';
1061	state = MA_START;
1062	i = j = 0;
1063	for (;;) {
1064		if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1065			error("Too many arguments.");
1066			return NULL;
1067		}
1068		if (isspace((unsigned char)arg[i])) {
1069			if (state == MA_UNQUOTED) {
1070				/* Terminate current argument */
1071				argvs[j++] = '\0';
1072				argc++;
1073				state = MA_START;
1074			} else if (state != MA_START)
1075				argvs[j++] = arg[i];
1076		} else if (arg[i] == '"' || arg[i] == '\'') {
1077			q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1078			if (state == MA_START) {
1079				argv[argc] = argvs + j;
1080				state = q;
1081				if (lastquote != NULL)
1082					*lastquote = arg[i];
1083			} else if (state == MA_UNQUOTED)
1084				state = q;
1085			else if (state == q)
1086				state = MA_UNQUOTED;
1087			else
1088				argvs[j++] = arg[i];
1089		} else if (arg[i] == '\\') {
1090			if (state == MA_SQUOTE || state == MA_DQUOTE) {
1091				quot = state == MA_SQUOTE ? '\'' : '"';
1092				/* Unescape quote we are in */
1093				/* XXX support \n and friends? */
1094				if (arg[i + 1] == quot) {
1095					i++;
1096					argvs[j++] = arg[i];
1097				} else if (arg[i + 1] == '?' ||
1098				    arg[i + 1] == '[' || arg[i + 1] == '*') {
1099					/*
1100					 * Special case for sftp: append
1101					 * double-escaped glob sequence -
1102					 * glob will undo one level of
1103					 * escaping. NB. string can grow here.
1104					 */
1105					if (j >= sizeof(argvs) - 5)
1106						goto args_too_longs;
1107					argvs[j++] = '\\';
1108					argvs[j++] = arg[i++];
1109					argvs[j++] = '\\';
1110					argvs[j++] = arg[i];
1111				} else {
1112					argvs[j++] = arg[i++];
1113					argvs[j++] = arg[i];
1114				}
1115			} else {
1116				if (state == MA_START) {
1117					argv[argc] = argvs + j;
1118					state = MA_UNQUOTED;
1119					if (lastquote != NULL)
1120						*lastquote = '\0';
1121				}
1122				if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1123				    arg[i + 1] == '*' || arg[i + 1] == '\\') {
1124					/*
1125					 * Special case for sftp: append
1126					 * escaped glob sequence -
1127					 * glob will undo one level of
1128					 * escaping.
1129					 */
1130					argvs[j++] = arg[i++];
1131					argvs[j++] = arg[i];
1132				} else {
1133					/* Unescape everything */
1134					/* XXX support \n and friends? */
1135					i++;
1136					argvs[j++] = arg[i];
1137				}
1138			}
1139		} else if (arg[i] == '#') {
1140			if (state == MA_SQUOTE || state == MA_DQUOTE)
1141				argvs[j++] = arg[i];
1142			else
1143				goto string_done;
1144		} else if (arg[i] == '\0') {
1145			if (state == MA_SQUOTE || state == MA_DQUOTE) {
1146				if (sloppy) {
1147					state = MA_UNQUOTED;
1148					if (terminated != NULL)
1149						*terminated = 0;
1150					goto string_done;
1151				}
1152				error("Unterminated quoted argument");
1153				return NULL;
1154			}
1155 string_done:
1156			if (state == MA_UNQUOTED) {
1157				argvs[j++] = '\0';
1158				argc++;
1159			}
1160			break;
1161		} else {
1162			if (state == MA_START) {
1163				argv[argc] = argvs + j;
1164				state = MA_UNQUOTED;
1165				if (lastquote != NULL)
1166					*lastquote = '\0';
1167			}
1168			if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1169			    (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1170				/*
1171				 * Special case for sftp: escape quoted
1172				 * glob(3) wildcards. NB. string can grow
1173				 * here.
1174				 */
1175				if (j >= sizeof(argvs) - 3)
1176					goto args_too_longs;
1177				argvs[j++] = '\\';
1178				argvs[j++] = arg[i];
1179			} else
1180				argvs[j++] = arg[i];
1181		}
1182		i++;
1183	}
1184	*argcp = argc;
1185	return argv;
1186}
1187
1188static int
1189parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag,
1190    int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag,
1191    unsigned long *n_arg, char **path1, char **path2)
1192{
1193	const char *cmd, *cp = *cpp;
1194	char *cp2, **argv;
1195	int base = 0;
1196	long l;
1197	int i, cmdnum, optidx, argc;
1198
1199	/* Skip leading whitespace */
1200	cp = cp + strspn(cp, WHITESPACE);
1201
1202	/* Check for leading '-' (disable error processing) */
1203	*ignore_errors = 0;
1204	if (*cp == '-') {
1205		*ignore_errors = 1;
1206		cp++;
1207		cp = cp + strspn(cp, WHITESPACE);
1208	}
1209
1210	/* Ignore blank lines and lines which begin with comment '#' char */
1211	if (*cp == '\0' || *cp == '#')
1212		return (0);
1213
1214	if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1215		return -1;
1216
1217	/* Figure out which command we have */
1218	for (i = 0; cmds[i].c != NULL; i++) {
1219		if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1220			break;
1221	}
1222	cmdnum = cmds[i].n;
1223	cmd = cmds[i].c;
1224
1225	/* Special case */
1226	if (*cp == '!') {
1227		cp++;
1228		cmdnum = I_SHELL;
1229	} else if (cmdnum == -1) {
1230		error("Invalid command.");
1231		return -1;
1232	}
1233
1234	/* Get arguments and parse flags */
1235	*aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1236	*rflag = *sflag = 0;
1237	*path1 = *path2 = NULL;
1238	optidx = 1;
1239	switch (cmdnum) {
1240	case I_GET:
1241	case I_REGET:
1242	case I_PUT:
1243		if ((optidx = parse_getput_flags(cmd, argv, argc,
1244		    aflag, fflag, pflag, rflag)) == -1)
1245			return -1;
1246		/* Get first pathname (mandatory) */
1247		if (argc - optidx < 1) {
1248			error("You must specify at least one path after a "
1249			    "%s command.", cmd);
1250			return -1;
1251		}
1252		*path1 = xstrdup(argv[optidx]);
1253		/* Get second pathname (optional) */
1254		if (argc - optidx > 1) {
1255			*path2 = xstrdup(argv[optidx + 1]);
1256			/* Destination is not globbed */
1257			undo_glob_escape(*path2);
1258		}
1259		if (*aflag && cmdnum == I_PUT) {
1260			/* XXX implement resume for uploads */
1261			error("Resume is not supported for uploads");
1262			return -1;
1263		}
1264		break;
1265	case I_LINK:
1266		if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1267			return -1;
1268		goto parse_two_paths;
1269	case I_RENAME:
1270		if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1271			return -1;
1272		goto parse_two_paths;
1273	case I_SYMLINK:
1274		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1275			return -1;
1276 parse_two_paths:
1277		if (argc - optidx < 2) {
1278			error("You must specify two paths after a %s "
1279			    "command.", cmd);
1280			return -1;
1281		}
1282		*path1 = xstrdup(argv[optidx]);
1283		*path2 = xstrdup(argv[optidx + 1]);
1284		/* Paths are not globbed */
1285		undo_glob_escape(*path1);
1286		undo_glob_escape(*path2);
1287		break;
1288	case I_RM:
1289	case I_MKDIR:
1290	case I_RMDIR:
1291	case I_CHDIR:
1292	case I_LCHDIR:
1293	case I_LMKDIR:
1294		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1295			return -1;
1296		/* Get pathname (mandatory) */
1297		if (argc - optidx < 1) {
1298			error("You must specify a path after a %s command.",
1299			    cmd);
1300			return -1;
1301		}
1302		*path1 = xstrdup(argv[optidx]);
1303		/* Only "rm" globs */
1304		if (cmdnum != I_RM)
1305			undo_glob_escape(*path1);
1306		break;
1307	case I_DF:
1308		if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1309		    iflag)) == -1)
1310			return -1;
1311		/* Default to current directory if no path specified */
1312		if (argc - optidx < 1)
1313			*path1 = NULL;
1314		else {
1315			*path1 = xstrdup(argv[optidx]);
1316			undo_glob_escape(*path1);
1317		}
1318		break;
1319	case I_LS:
1320		if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1321			return(-1);
1322		/* Path is optional */
1323		if (argc - optidx > 0)
1324			*path1 = xstrdup(argv[optidx]);
1325		break;
1326	case I_LLS:
1327		/* Skip ls command and following whitespace */
1328		cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1329	case I_SHELL:
1330		/* Uses the rest of the line */
1331		break;
1332	case I_LUMASK:
1333	case I_CHMOD:
1334		base = 8;
1335	case I_CHOWN:
1336	case I_CHGRP:
1337		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1338			return -1;
1339		/* Get numeric arg (mandatory) */
1340		if (argc - optidx < 1)
1341			goto need_num_arg;
1342		errno = 0;
1343		l = strtol(argv[optidx], &cp2, base);
1344		if (cp2 == argv[optidx] || *cp2 != '\0' ||
1345		    ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1346		    l < 0) {
1347 need_num_arg:
1348			error("You must supply a numeric argument "
1349			    "to the %s command.", cmd);
1350			return -1;
1351		}
1352		*n_arg = l;
1353		if (cmdnum == I_LUMASK)
1354			break;
1355		/* Get pathname (mandatory) */
1356		if (argc - optidx < 2) {
1357			error("You must specify a path after a %s command.",
1358			    cmd);
1359			return -1;
1360		}
1361		*path1 = xstrdup(argv[optidx + 1]);
1362		break;
1363	case I_QUIT:
1364	case I_PWD:
1365	case I_LPWD:
1366	case I_HELP:
1367	case I_VERSION:
1368	case I_PROGRESS:
1369		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1370			return -1;
1371		break;
1372	default:
1373		fatal("Command not implemented");
1374	}
1375
1376	*cpp = cp;
1377	return(cmdnum);
1378}
1379
1380static int
1381parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1382    int err_abort)
1383{
1384	char *path1, *path2, *tmp;
1385	int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1386	int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1387	int cmdnum, i;
1388	unsigned long n_arg = 0;
1389	Attrib a, *aa;
1390	char path_buf[MAXPATHLEN];
1391	int err = 0;
1392	glob_t g;
1393
1394	path1 = path2 = NULL;
1395	cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1396	    &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1397	if (ignore_errors != 0)
1398		err_abort = 0;
1399
1400	memset(&g, 0, sizeof(g));
1401
1402	/* Perform command */
1403	switch (cmdnum) {
1404	case 0:
1405		/* Blank line */
1406		break;
1407	case -1:
1408		/* Unrecognized command */
1409		err = -1;
1410		break;
1411	case I_REGET:
1412		aflag = 1;
1413		/* FALLTHROUGH */
1414	case I_GET:
1415		err = process_get(conn, path1, path2, *pwd, pflag,
1416		    rflag, aflag, fflag);
1417		break;
1418	case I_PUT:
1419		err = process_put(conn, path1, path2, *pwd, pflag,
1420		    rflag, fflag);
1421		break;
1422	case I_RENAME:
1423		path1 = make_absolute(path1, *pwd);
1424		path2 = make_absolute(path2, *pwd);
1425		err = do_rename(conn, path1, path2, lflag);
1426		break;
1427	case I_SYMLINK:
1428		sflag = 1;
1429	case I_LINK:
1430		if (!sflag)
1431			path1 = make_absolute(path1, *pwd);
1432		path2 = make_absolute(path2, *pwd);
1433		err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1434		break;
1435	case I_RM:
1436		path1 = make_absolute(path1, *pwd);
1437		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1438		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1439			if (!quiet)
1440				printf("Removing %s\n", g.gl_pathv[i]);
1441			err = do_rm(conn, g.gl_pathv[i]);
1442			if (err != 0 && err_abort)
1443				break;
1444		}
1445		break;
1446	case I_MKDIR:
1447		path1 = make_absolute(path1, *pwd);
1448		attrib_clear(&a);
1449		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1450		a.perm = 0777;
1451		err = do_mkdir(conn, path1, &a, 1);
1452		break;
1453	case I_RMDIR:
1454		path1 = make_absolute(path1, *pwd);
1455		err = do_rmdir(conn, path1);
1456		break;
1457	case I_CHDIR:
1458		path1 = make_absolute(path1, *pwd);
1459		if ((tmp = do_realpath(conn, path1)) == NULL) {
1460			err = 1;
1461			break;
1462		}
1463		if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1464			free(tmp);
1465			err = 1;
1466			break;
1467		}
1468		if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1469			error("Can't change directory: Can't check target");
1470			free(tmp);
1471			err = 1;
1472			break;
1473		}
1474		if (!S_ISDIR(aa->perm)) {
1475			error("Can't change directory: \"%s\" is not "
1476			    "a directory", tmp);
1477			free(tmp);
1478			err = 1;
1479			break;
1480		}
1481		free(*pwd);
1482		*pwd = tmp;
1483		break;
1484	case I_LS:
1485		if (!path1) {
1486			do_ls_dir(conn, *pwd, *pwd, lflag);
1487			break;
1488		}
1489
1490		/* Strip pwd off beginning of non-absolute paths */
1491		tmp = NULL;
1492		if (*path1 != '/')
1493			tmp = *pwd;
1494
1495		path1 = make_absolute(path1, *pwd);
1496		err = do_globbed_ls(conn, path1, tmp, lflag);
1497		break;
1498	case I_DF:
1499		/* Default to current directory if no path specified */
1500		if (path1 == NULL)
1501			path1 = xstrdup(*pwd);
1502		path1 = make_absolute(path1, *pwd);
1503		err = do_df(conn, path1, hflag, iflag);
1504		break;
1505	case I_LCHDIR:
1506		if (chdir(path1) == -1) {
1507			error("Couldn't change local directory to "
1508			    "\"%s\": %s", path1, strerror(errno));
1509			err = 1;
1510		}
1511		break;
1512	case I_LMKDIR:
1513		if (mkdir(path1, 0777) == -1) {
1514			error("Couldn't create local directory "
1515			    "\"%s\": %s", path1, strerror(errno));
1516			err = 1;
1517		}
1518		break;
1519	case I_LLS:
1520		local_do_ls(cmd);
1521		break;
1522	case I_SHELL:
1523		local_do_shell(cmd);
1524		break;
1525	case I_LUMASK:
1526		umask(n_arg);
1527		printf("Local umask: %03lo\n", n_arg);
1528		break;
1529	case I_CHMOD:
1530		path1 = make_absolute(path1, *pwd);
1531		attrib_clear(&a);
1532		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1533		a.perm = n_arg;
1534		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1535		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1536			if (!quiet)
1537				printf("Changing mode on %s\n", g.gl_pathv[i]);
1538			err = do_setstat(conn, g.gl_pathv[i], &a);
1539			if (err != 0 && err_abort)
1540				break;
1541		}
1542		break;
1543	case I_CHOWN:
1544	case I_CHGRP:
1545		path1 = make_absolute(path1, *pwd);
1546		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1547		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1548			if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1549				if (err_abort) {
1550					err = -1;
1551					break;
1552				} else
1553					continue;
1554			}
1555			if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1556				error("Can't get current ownership of "
1557				    "remote file \"%s\"", g.gl_pathv[i]);
1558				if (err_abort) {
1559					err = -1;
1560					break;
1561				} else
1562					continue;
1563			}
1564			aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1565			if (cmdnum == I_CHOWN) {
1566				if (!quiet)
1567					printf("Changing owner on %s\n",
1568					    g.gl_pathv[i]);
1569				aa->uid = n_arg;
1570			} else {
1571				if (!quiet)
1572					printf("Changing group on %s\n",
1573					    g.gl_pathv[i]);
1574				aa->gid = n_arg;
1575			}
1576			err = do_setstat(conn, g.gl_pathv[i], aa);
1577			if (err != 0 && err_abort)
1578				break;
1579		}
1580		break;
1581	case I_PWD:
1582		printf("Remote working directory: %s\n", *pwd);
1583		break;
1584	case I_LPWD:
1585		if (!getcwd(path_buf, sizeof(path_buf))) {
1586			error("Couldn't get local cwd: %s", strerror(errno));
1587			err = -1;
1588			break;
1589		}
1590		printf("Local working directory: %s\n", path_buf);
1591		break;
1592	case I_QUIT:
1593		/* Processed below */
1594		break;
1595	case I_HELP:
1596		help();
1597		break;
1598	case I_VERSION:
1599		printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1600		break;
1601	case I_PROGRESS:
1602		showprogress = !showprogress;
1603		if (showprogress)
1604			printf("Progress meter enabled\n");
1605		else
1606			printf("Progress meter disabled\n");
1607		break;
1608	default:
1609		fatal("%d is not implemented", cmdnum);
1610	}
1611
1612	if (g.gl_pathc)
1613		globfree(&g);
1614	free(path1);
1615	free(path2);
1616
1617	/* If an unignored error occurs in batch mode we should abort. */
1618	if (err_abort && err != 0)
1619		return (-1);
1620	else if (cmdnum == I_QUIT)
1621		return (1);
1622
1623	return (0);
1624}
1625
1626#ifdef USE_LIBEDIT
1627static char *
1628prompt(EditLine *el)
1629{
1630	return ("sftp> ");
1631}
1632
1633/* Display entries in 'list' after skipping the first 'len' chars */
1634static void
1635complete_display(char **list, u_int len)
1636{
1637	u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1638	struct winsize ws;
1639	char *tmp;
1640
1641	/* Count entries for sort and find longest */
1642	for (y = 0; list[y]; y++)
1643		m = MAX(m, strlen(list[y]));
1644
1645	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1646		width = ws.ws_col;
1647
1648	m = m > len ? m - len : 0;
1649	columns = width / (m + 2);
1650	columns = MAX(columns, 1);
1651	colspace = width / columns;
1652	colspace = MIN(colspace, width);
1653
1654	printf("\n");
1655	m = 1;
1656	for (y = 0; list[y]; y++) {
1657		llen = strlen(list[y]);
1658		tmp = llen > len ? list[y] + len : "";
1659		printf("%-*s", colspace, tmp);
1660		if (m >= columns) {
1661			printf("\n");
1662			m = 1;
1663		} else
1664			m++;
1665	}
1666	printf("\n");
1667}
1668
1669/*
1670 * Given a "list" of words that begin with a common prefix of "word",
1671 * attempt to find an autocompletion to extends "word" by the next
1672 * characters common to all entries in "list".
1673 */
1674static char *
1675complete_ambiguous(const char *word, char **list, size_t count)
1676{
1677	if (word == NULL)
1678		return NULL;
1679
1680	if (count > 0) {
1681		u_int y, matchlen = strlen(list[0]);
1682
1683		/* Find length of common stem */
1684		for (y = 1; list[y]; y++) {
1685			u_int x;
1686
1687			for (x = 0; x < matchlen; x++)
1688				if (list[0][x] != list[y][x])
1689					break;
1690
1691			matchlen = x;
1692		}
1693
1694		if (matchlen > strlen(word)) {
1695			char *tmp = xstrdup(list[0]);
1696
1697			tmp[matchlen] = '\0';
1698			return tmp;
1699		}
1700	}
1701
1702	return xstrdup(word);
1703}
1704
1705/* Autocomplete a sftp command */
1706static int
1707complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1708    int terminated)
1709{
1710	u_int y, count = 0, cmdlen, tmplen;
1711	char *tmp, **list, argterm[3];
1712	const LineInfo *lf;
1713
1714	list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1715
1716	/* No command specified: display all available commands */
1717	if (cmd == NULL) {
1718		for (y = 0; cmds[y].c; y++)
1719			list[count++] = xstrdup(cmds[y].c);
1720
1721		list[count] = NULL;
1722		complete_display(list, 0);
1723
1724		for (y = 0; list[y] != NULL; y++)
1725			free(list[y]);
1726		free(list);
1727		return count;
1728	}
1729
1730	/* Prepare subset of commands that start with "cmd" */
1731	cmdlen = strlen(cmd);
1732	for (y = 0; cmds[y].c; y++)  {
1733		if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1734			list[count++] = xstrdup(cmds[y].c);
1735	}
1736	list[count] = NULL;
1737
1738	if (count == 0) {
1739		free(list);
1740		return 0;
1741	}
1742
1743	/* Complete ambigious command */
1744	tmp = complete_ambiguous(cmd, list, count);
1745	if (count > 1)
1746		complete_display(list, 0);
1747
1748	for (y = 0; list[y]; y++)
1749		free(list[y]);
1750	free(list);
1751
1752	if (tmp != NULL) {
1753		tmplen = strlen(tmp);
1754		cmdlen = strlen(cmd);
1755		/* If cmd may be extended then do so */
1756		if (tmplen > cmdlen)
1757			if (el_insertstr(el, tmp + cmdlen) == -1)
1758				fatal("el_insertstr failed.");
1759		lf = el_line(el);
1760		/* Terminate argument cleanly */
1761		if (count == 1) {
1762			y = 0;
1763			if (!terminated)
1764				argterm[y++] = quote;
1765			if (lastarg || *(lf->cursor) != ' ')
1766				argterm[y++] = ' ';
1767			argterm[y] = '\0';
1768			if (y > 0 && el_insertstr(el, argterm) == -1)
1769				fatal("el_insertstr failed.");
1770		}
1771		free(tmp);
1772	}
1773
1774	return count;
1775}
1776
1777/*
1778 * Determine whether a particular sftp command's arguments (if any)
1779 * represent local or remote files.
1780 */
1781static int
1782complete_is_remote(char *cmd) {
1783	int i;
1784
1785	if (cmd == NULL)
1786		return -1;
1787
1788	for (i = 0; cmds[i].c; i++) {
1789		if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1790			return cmds[i].t;
1791	}
1792
1793	return -1;
1794}
1795
1796/* Autocomplete a filename "file" */
1797static int
1798complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1799    char *file, int remote, int lastarg, char quote, int terminated)
1800{
1801	glob_t g;
1802	char *tmp, *tmp2, ins[8];
1803	u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1804	int clen;
1805	const LineInfo *lf;
1806
1807	/* Glob from "file" location */
1808	if (file == NULL)
1809		tmp = xstrdup("*");
1810	else
1811		xasprintf(&tmp, "%s*", file);
1812
1813	/* Check if the path is absolute. */
1814	isabs = tmp[0] == '/';
1815
1816	memset(&g, 0, sizeof(g));
1817	if (remote != LOCAL) {
1818		tmp = make_absolute(tmp, remote_path);
1819		remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1820	} else
1821		glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1822
1823	/* Determine length of pwd so we can trim completion display */
1824	for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1825		/* Terminate counting on first unescaped glob metacharacter */
1826		if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1827			if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1828				hadglob = 1;
1829			break;
1830		}
1831		if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1832			tmplen++;
1833		if (tmp[tmplen] == '/')
1834			pwdlen = tmplen + 1;	/* track last seen '/' */
1835	}
1836	free(tmp);
1837
1838	if (g.gl_matchc == 0)
1839		goto out;
1840
1841	if (g.gl_matchc > 1)
1842		complete_display(g.gl_pathv, pwdlen);
1843
1844	tmp = NULL;
1845	/* Don't try to extend globs */
1846	if (file == NULL || hadglob)
1847		goto out;
1848
1849	tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1850	tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1851	free(tmp2);
1852
1853	if (tmp == NULL)
1854		goto out;
1855
1856	tmplen = strlen(tmp);
1857	filelen = strlen(file);
1858
1859	/* Count the number of escaped characters in the input string. */
1860	cesc = isesc = 0;
1861	for (i = 0; i < filelen; i++) {
1862		if (!isesc && file[i] == '\\' && i + 1 < filelen){
1863			isesc = 1;
1864			cesc++;
1865		} else
1866			isesc = 0;
1867	}
1868
1869	if (tmplen > (filelen - cesc)) {
1870		tmp2 = tmp + filelen - cesc;
1871		len = strlen(tmp2);
1872		/* quote argument on way out */
1873		for (i = 0; i < len; i += clen) {
1874			if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1875			    (size_t)clen > sizeof(ins) - 2)
1876				fatal("invalid multibyte character");
1877			ins[0] = '\\';
1878			memcpy(ins + 1, tmp2 + i, clen);
1879			ins[clen + 1] = '\0';
1880			switch (tmp2[i]) {
1881			case '\'':
1882			case '"':
1883			case '\\':
1884			case '\t':
1885			case '[':
1886			case ' ':
1887			case '#':
1888			case '*':
1889				if (quote == '\0' || tmp2[i] == quote) {
1890					if (el_insertstr(el, ins) == -1)
1891						fatal("el_insertstr "
1892						    "failed.");
1893					break;
1894				}
1895				/* FALLTHROUGH */
1896			default:
1897				if (el_insertstr(el, ins + 1) == -1)
1898					fatal("el_insertstr failed.");
1899				break;
1900			}
1901		}
1902	}
1903
1904	lf = el_line(el);
1905	if (g.gl_matchc == 1) {
1906		i = 0;
1907		if (!terminated)
1908			ins[i++] = quote;
1909		if (*(lf->cursor - 1) != '/' &&
1910		    (lastarg || *(lf->cursor) != ' '))
1911			ins[i++] = ' ';
1912		ins[i] = '\0';
1913		if (i > 0 && el_insertstr(el, ins) == -1)
1914			fatal("el_insertstr failed.");
1915	}
1916	free(tmp);
1917
1918 out:
1919	globfree(&g);
1920	return g.gl_matchc;
1921}
1922
1923/* tab-completion hook function, called via libedit */
1924static unsigned char
1925complete(EditLine *el, int ch)
1926{
1927	char **argv, *line, quote;
1928	int argc, carg;
1929	u_int cursor, len, terminated, ret = CC_ERROR;
1930	const LineInfo *lf;
1931	struct complete_ctx *complete_ctx;
1932
1933	lf = el_line(el);
1934	if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1935		fatal("%s: el_get failed", __func__);
1936
1937	/* Figure out which argument the cursor points to */
1938	cursor = lf->cursor - lf->buffer;
1939	line = (char *)xmalloc(cursor + 1);
1940	memcpy(line, lf->buffer, cursor);
1941	line[cursor] = '\0';
1942	argv = makeargv(line, &carg, 1, &quote, &terminated);
1943	free(line);
1944
1945	/* Get all the arguments on the line */
1946	len = lf->lastchar - lf->buffer;
1947	line = (char *)xmalloc(len + 1);
1948	memcpy(line, lf->buffer, len);
1949	line[len] = '\0';
1950	argv = makeargv(line, &argc, 1, NULL, NULL);
1951
1952	/* Ensure cursor is at EOL or a argument boundary */
1953	if (line[cursor] != ' ' && line[cursor] != '\0' &&
1954	    line[cursor] != '\n') {
1955		free(line);
1956		return ret;
1957	}
1958
1959	if (carg == 0) {
1960		/* Show all available commands */
1961		complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1962		ret = CC_REDISPLAY;
1963	} else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
1964		/* Handle the command parsing */
1965		if (complete_cmd_parse(el, argv[0], argc == carg,
1966		    quote, terminated) != 0)
1967			ret = CC_REDISPLAY;
1968	} else if (carg >= 1) {
1969		/* Handle file parsing */
1970		int remote = complete_is_remote(argv[0]);
1971		char *filematch = NULL;
1972
1973		if (carg > 1 && line[cursor-1] != ' ')
1974			filematch = argv[carg - 1];
1975
1976		if (remote != 0 &&
1977		    complete_match(el, complete_ctx->conn,
1978		    *complete_ctx->remote_pathp, filematch,
1979		    remote, carg == argc, quote, terminated) != 0)
1980			ret = CC_REDISPLAY;
1981	}
1982
1983	free(line);
1984	return ret;
1985}
1986#endif /* USE_LIBEDIT */
1987
1988int
1989interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1990{
1991	char *remote_path;
1992	char *dir = NULL;
1993	char cmd[2048];
1994	int err, interactive;
1995	EditLine *el = NULL;
1996#ifdef USE_LIBEDIT
1997	History *hl = NULL;
1998	HistEvent hev;
1999	extern char *__progname;
2000	struct complete_ctx complete_ctx;
2001
2002	if (!batchmode && isatty(STDIN_FILENO)) {
2003		if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2004			fatal("Couldn't initialise editline");
2005		if ((hl = history_init()) == NULL)
2006			fatal("Couldn't initialise editline history");
2007		history(hl, &hev, H_SETSIZE, 100);
2008		el_set(el, EL_HIST, history, hl);
2009
2010		el_set(el, EL_PROMPT, prompt);
2011		el_set(el, EL_EDITOR, "emacs");
2012		el_set(el, EL_TERMINAL, NULL);
2013		el_set(el, EL_SIGNAL, 1);
2014		el_source(el, NULL);
2015
2016		/* Tab Completion */
2017		el_set(el, EL_ADDFN, "ftp-complete",
2018		    "Context sensitive argument completion", complete);
2019		complete_ctx.conn = conn;
2020		complete_ctx.remote_pathp = &remote_path;
2021		el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2022		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2023		/* enable ctrl-left-arrow and ctrl-right-arrow */
2024		el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2025		el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2026		el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2027		el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2028		/* make ^w match ksh behaviour */
2029		el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2030	}
2031#endif /* USE_LIBEDIT */
2032
2033	remote_path = do_realpath(conn, ".");
2034	if (remote_path == NULL)
2035		fatal("Need cwd");
2036
2037	if (file1 != NULL) {
2038		dir = xstrdup(file1);
2039		dir = make_absolute(dir, remote_path);
2040
2041		if (remote_is_dir(conn, dir) && file2 == NULL) {
2042			if (!quiet)
2043				printf("Changing to: %s\n", dir);
2044			snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2045			if (parse_dispatch_command(conn, cmd,
2046			    &remote_path, 1) != 0) {
2047				free(dir);
2048				free(remote_path);
2049				free(conn);
2050				return (-1);
2051			}
2052		} else {
2053			/* XXX this is wrong wrt quoting */
2054			snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2055			    global_aflag ? " -a" : "", dir,
2056			    file2 == NULL ? "" : " ",
2057			    file2 == NULL ? "" : file2);
2058			err = parse_dispatch_command(conn, cmd,
2059			    &remote_path, 1);
2060			free(dir);
2061			free(remote_path);
2062			free(conn);
2063			return (err);
2064		}
2065		free(dir);
2066	}
2067
2068	setlinebuf(stdout);
2069	setlinebuf(infile);
2070
2071	interactive = !batchmode && isatty(STDIN_FILENO);
2072	err = 0;
2073	for (;;) {
2074		char *cp;
2075
2076		signal(SIGINT, SIG_IGN);
2077
2078		if (el == NULL) {
2079			if (interactive)
2080				printf("sftp> ");
2081			if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2082				if (interactive)
2083					printf("\n");
2084				break;
2085			}
2086			if (!interactive) { /* Echo command */
2087				printf("sftp> %s", cmd);
2088				if (strlen(cmd) > 0 &&
2089				    cmd[strlen(cmd) - 1] != '\n')
2090					printf("\n");
2091			}
2092		} else {
2093#ifdef USE_LIBEDIT
2094			const char *line;
2095			int count = 0;
2096
2097			if ((line = el_gets(el, &count)) == NULL ||
2098			    count <= 0) {
2099				printf("\n");
2100 				break;
2101			}
2102			history(hl, &hev, H_ENTER, line);
2103			if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2104				fprintf(stderr, "Error: input line too long\n");
2105				continue;
2106			}
2107#endif /* USE_LIBEDIT */
2108		}
2109
2110		cp = strrchr(cmd, '\n');
2111		if (cp)
2112			*cp = '\0';
2113
2114		/* Handle user interrupts gracefully during commands */
2115		interrupted = 0;
2116		signal(SIGINT, cmd_interrupt);
2117
2118		err = parse_dispatch_command(conn, cmd, &remote_path,
2119		    batchmode);
2120		if (err != 0)
2121			break;
2122	}
2123	free(remote_path);
2124	free(conn);
2125
2126#ifdef USE_LIBEDIT
2127	if (el != NULL)
2128		el_end(el);
2129#endif /* USE_LIBEDIT */
2130
2131	/* err == 1 signifies normal "quit" exit */
2132	return (err >= 0 ? 0 : -1);
2133}
2134
2135static void
2136connect_to_server(char *path, char **args, int *in, int *out)
2137{
2138	int c_in, c_out;
2139
2140#ifdef USE_PIPES
2141	int pin[2], pout[2];
2142
2143	if ((pipe(pin) == -1) || (pipe(pout) == -1))
2144		fatal("pipe: %s", strerror(errno));
2145	*in = pin[0];
2146	*out = pout[1];
2147	c_in = pout[0];
2148	c_out = pin[1];
2149#else /* USE_PIPES */
2150	int inout[2];
2151
2152	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2153		fatal("socketpair: %s", strerror(errno));
2154	*in = *out = inout[0];
2155	c_in = c_out = inout[1];
2156#endif /* USE_PIPES */
2157
2158	if ((sshpid = fork()) == -1)
2159		fatal("fork: %s", strerror(errno));
2160	else if (sshpid == 0) {
2161		if ((dup2(c_in, STDIN_FILENO) == -1) ||
2162		    (dup2(c_out, STDOUT_FILENO) == -1)) {
2163			fprintf(stderr, "dup2: %s\n", strerror(errno));
2164			_exit(1);
2165		}
2166		close(*in);
2167		close(*out);
2168		close(c_in);
2169		close(c_out);
2170
2171		/*
2172		 * The underlying ssh is in the same process group, so we must
2173		 * ignore SIGINT if we want to gracefully abort commands,
2174		 * otherwise the signal will make it to the ssh process and
2175		 * kill it too.  Contrawise, since sftp sends SIGTERMs to the
2176		 * underlying ssh, it must *not* ignore that signal.
2177		 */
2178		signal(SIGINT, SIG_IGN);
2179		signal(SIGTERM, SIG_DFL);
2180		execvp(path, args);
2181		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2182		_exit(1);
2183	}
2184
2185	signal(SIGTERM, killchild);
2186	signal(SIGINT, killchild);
2187	signal(SIGHUP, killchild);
2188	close(c_in);
2189	close(c_out);
2190}
2191
2192static void
2193usage(void)
2194{
2195	extern char *__progname;
2196
2197	fprintf(stderr,
2198	    "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2199	    "          [-D sftp_server_path] [-F ssh_config] "
2200	    "[-i identity_file] [-l limit]\n"
2201	    "          [-o ssh_option] [-P port] [-R num_requests] "
2202	    "[-S program]\n"
2203	    "          [-s subsystem | sftp_server] host\n"
2204	    "       %s [user@]host[:file ...]\n"
2205	    "       %s [user@]host[:dir[/]]\n"
2206	    "       %s -b batchfile [user@]host\n",
2207	    __progname, __progname, __progname, __progname);
2208	exit(1);
2209}
2210
2211int
2212main(int argc, char **argv)
2213{
2214	int in, out, ch, err;
2215	char *host = NULL, *userhost, *cp, *file2 = NULL;
2216	int debug_level = 0, sshver = 2;
2217	char *file1 = NULL, *sftp_server = NULL;
2218	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2219	const char *errstr;
2220	LogLevel ll = SYSLOG_LEVEL_INFO;
2221	arglist args;
2222	extern int optind;
2223	extern char *optarg;
2224	struct sftp_conn *conn;
2225	size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2226	size_t num_requests = DEFAULT_NUM_REQUESTS;
2227	long long limit_kbps = 0;
2228
2229	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2230	sanitise_stdfd();
2231	setlocale(LC_CTYPE, "");
2232
2233	__progname = ssh_get_progname(argv[0]);
2234	memset(&args, '\0', sizeof(args));
2235	args.list = NULL;
2236	addargs(&args, "%s", ssh_program);
2237	addargs(&args, "-oForwardX11 no");
2238	addargs(&args, "-oForwardAgent no");
2239	addargs(&args, "-oPermitLocalCommand no");
2240	addargs(&args, "-oClearAllForwardings yes");
2241
2242	ll = SYSLOG_LEVEL_INFO;
2243	infile = stdin;
2244
2245	while ((ch = getopt(argc, argv,
2246	    "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2247		switch (ch) {
2248		/* Passed through to ssh(1) */
2249		case '4':
2250		case '6':
2251		case 'C':
2252			addargs(&args, "-%c", ch);
2253			break;
2254		/* Passed through to ssh(1) with argument */
2255		case 'F':
2256		case 'c':
2257		case 'i':
2258		case 'o':
2259			addargs(&args, "-%c", ch);
2260			addargs(&args, "%s", optarg);
2261			break;
2262		case 'q':
2263			ll = SYSLOG_LEVEL_ERROR;
2264			quiet = 1;
2265			showprogress = 0;
2266			addargs(&args, "-%c", ch);
2267			break;
2268		case 'P':
2269			addargs(&args, "-oPort %s", optarg);
2270			break;
2271		case 'v':
2272			if (debug_level < 3) {
2273				addargs(&args, "-v");
2274				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2275			}
2276			debug_level++;
2277			break;
2278		case '1':
2279			sshver = 1;
2280			if (sftp_server == NULL)
2281				sftp_server = _PATH_SFTP_SERVER;
2282			break;
2283		case '2':
2284			sshver = 2;
2285			break;
2286		case 'a':
2287			global_aflag = 1;
2288			break;
2289		case 'B':
2290			copy_buffer_len = strtol(optarg, &cp, 10);
2291			if (copy_buffer_len == 0 || *cp != '\0')
2292				fatal("Invalid buffer size \"%s\"", optarg);
2293			break;
2294		case 'b':
2295			if (batchmode)
2296				fatal("Batch file already specified.");
2297
2298			/* Allow "-" as stdin */
2299			if (strcmp(optarg, "-") != 0 &&
2300			    (infile = fopen(optarg, "r")) == NULL)
2301				fatal("%s (%s).", strerror(errno), optarg);
2302			showprogress = 0;
2303			quiet = batchmode = 1;
2304			addargs(&args, "-obatchmode yes");
2305			break;
2306		case 'f':
2307			global_fflag = 1;
2308			break;
2309		case 'p':
2310			global_pflag = 1;
2311			break;
2312		case 'D':
2313			sftp_direct = optarg;
2314			break;
2315		case 'l':
2316			limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2317			    &errstr);
2318			if (errstr != NULL)
2319				usage();
2320			limit_kbps *= 1024; /* kbps */
2321			break;
2322		case 'r':
2323			global_rflag = 1;
2324			break;
2325		case 'R':
2326			num_requests = strtol(optarg, &cp, 10);
2327			if (num_requests == 0 || *cp != '\0')
2328				fatal("Invalid number of requests \"%s\"",
2329				    optarg);
2330			break;
2331		case 's':
2332			sftp_server = optarg;
2333			break;
2334		case 'S':
2335			ssh_program = optarg;
2336			replacearg(&args, 0, "%s", ssh_program);
2337			break;
2338		case 'h':
2339		default:
2340			usage();
2341		}
2342	}
2343
2344	if (!isatty(STDERR_FILENO))
2345		showprogress = 0;
2346
2347	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2348
2349	if (sftp_direct == NULL) {
2350		if (optind == argc || argc > (optind + 2))
2351			usage();
2352
2353		userhost = xstrdup(argv[optind]);
2354		file2 = argv[optind+1];
2355
2356		if ((host = strrchr(userhost, '@')) == NULL)
2357			host = userhost;
2358		else {
2359			*host++ = '\0';
2360			if (!userhost[0]) {
2361				fprintf(stderr, "Missing username\n");
2362				usage();
2363			}
2364			addargs(&args, "-l");
2365			addargs(&args, "%s", userhost);
2366		}
2367
2368		if ((cp = colon(host)) != NULL) {
2369			*cp++ = '\0';
2370			file1 = cp;
2371		}
2372
2373		host = cleanhostname(host);
2374		if (!*host) {
2375			fprintf(stderr, "Missing hostname\n");
2376			usage();
2377		}
2378
2379		addargs(&args, "-oProtocol %d", sshver);
2380
2381		/* no subsystem if the server-spec contains a '/' */
2382		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2383			addargs(&args, "-s");
2384
2385		addargs(&args, "--");
2386		addargs(&args, "%s", host);
2387		addargs(&args, "%s", (sftp_server != NULL ?
2388		    sftp_server : "sftp"));
2389
2390		connect_to_server(ssh_program, args.list, &in, &out);
2391	} else {
2392		args.list = NULL;
2393		addargs(&args, "sftp-server");
2394
2395		connect_to_server(sftp_direct, args.list, &in, &out);
2396	}
2397	freeargs(&args);
2398
2399	conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2400	if (conn == NULL)
2401		fatal("Couldn't initialise connection to server");
2402
2403	if (!quiet) {
2404		if (sftp_direct == NULL)
2405			fprintf(stderr, "Connected to %s.\n", host);
2406		else
2407			fprintf(stderr, "Attached to %s.\n", sftp_direct);
2408	}
2409
2410	err = interactive_loop(conn, file1, file2);
2411
2412#if !defined(USE_PIPES)
2413	shutdown(in, SHUT_RDWR);
2414	shutdown(out, SHUT_RDWR);
2415#endif
2416
2417	close(in);
2418	close(out);
2419	if (batchmode)
2420		fclose(infile);
2421
2422	while (waitpid(sshpid, NULL, 0) == -1)
2423		if (errno != EINTR)
2424			fatal("Couldn't wait for ssh process: %s",
2425			    strerror(errno));
2426
2427	exit(err == 0 ? 0 : 1);
2428}
2429