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