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