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