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