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