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