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