summaryrefslogtreecommitdiff
path: root/sftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp.c')
-rw-r--r--sftp.c457
1 files changed, 296 insertions, 161 deletions
diff --git a/sftp.c b/sftp.c
index da7fbab3..ad1f8c84 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp.c,v 1.134 2011/11/16 12:24:28 oga Exp $ */
+/* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
@@ -38,6 +38,9 @@
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
#ifdef USE_LIBEDIT
#include <histedit.h>
#else
@@ -54,10 +57,6 @@ typedef void EditLine;
# include <util.h>
#endif
-#ifdef HAVE_LIBUTIL_H
-# include <libutil.h>
-#endif
-
#include "xmalloc.h"
#include "log.h"
#include "pathnames.h"
@@ -80,15 +79,24 @@ int batchmode = 0;
/* PID of ssh transport process */
static pid_t sshpid = -1;
+/* Suppress diagnositic messages */
+int quiet = 0;
+
/* This is set to 0 if the progressmeter is not desired. */
int showprogress = 1;
/* When this option is set, we always recursively download/upload directories */
int global_rflag = 0;
+/* When this option is set, we resume download if possible */
+int global_aflag = 0;
+
/* When this option is set, the file transfers will always preserve times */
int global_pflag = 0;
+/* When this option is set, transfers will have fsync() called on each file */
+int global_fflag = 0;
+
/* SIGINT received during command processing */
volatile sig_atomic_t interrupted = 0;
@@ -124,31 +132,34 @@ extern char *__progname;
#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
/* Commands for interactive mode */
-#define I_CHDIR 1
-#define I_CHGRP 2
-#define I_CHMOD 3
-#define I_CHOWN 4
-#define I_DF 24
-#define I_GET 5
-#define I_HELP 6
-#define I_LCHDIR 7
-#define I_LINK 25
-#define I_LLS 8
-#define I_LMKDIR 9
-#define I_LPWD 10
-#define I_LS 11
-#define I_LUMASK 12
-#define I_MKDIR 13
-#define I_PUT 14
-#define I_PWD 15
-#define I_QUIT 16
-#define I_RENAME 17
-#define I_RM 18
-#define I_RMDIR 19
-#define I_SHELL 20
-#define I_SYMLINK 21
-#define I_VERSION 22
-#define I_PROGRESS 23
+enum sftp_command {
+ I_CHDIR = 1,
+ I_CHGRP,
+ I_CHMOD,
+ I_CHOWN,
+ I_DF,
+ I_GET,
+ I_HELP,
+ I_LCHDIR,
+ I_LINK,
+ I_LLS,
+ I_LMKDIR,
+ I_LPWD,
+ I_LS,
+ I_LUMASK,
+ I_MKDIR,
+ I_PUT,
+ I_PWD,
+ I_QUIT,
+ I_RENAME,
+ I_RM,
+ I_RMDIR,
+ I_SHELL,
+ I_SYMLINK,
+ I_VERSION,
+ I_PROGRESS,
+ I_REGET,
+};
struct CMD {
const char *c;
@@ -188,6 +199,7 @@ static const struct CMD cmds[] = {
{ "put", I_PUT, LOCAL },
{ "pwd", I_PWD, REMOTE },
{ "quit", I_QUIT, NOARGS },
+ { "reget", I_REGET, REMOTE },
{ "rename", I_RENAME, REMOTE },
{ "rm", I_RM, REMOTE },
{ "rmdir", I_RMDIR, REMOTE },
@@ -219,7 +231,7 @@ cmd_interrupt(int signo)
const char msg[] = "\rInterrupt \n";
int olderrno = errno;
- write(STDERR_FILENO, msg, sizeof(msg) - 1);
+ (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
interrupted = 1;
errno = olderrno;
}
@@ -237,6 +249,7 @@ help(void)
" filesystem containing 'path'\n"
"exit Quit sftp\n"
"get [-Ppr] remote [local] Download file\n"
+ "reget remote [local] Resume download file\n"
"help Display this help text\n"
"lcd path Change local directory to 'path'\n"
"lls [ls-options [path]] Display local directory listing\n"
@@ -310,7 +323,7 @@ local_do_ls(const char *args)
/* XXX: quoting - rip quoting code from ftp? */
snprintf(buf, len, _PATH_LS " %s", args);
local_do_shell(buf);
- xfree(buf);
+ free(buf);
}
}
@@ -341,15 +354,15 @@ make_absolute(char *p, char *pwd)
/* Derelativise */
if (p && p[0] != '/') {
abs_str = path_append(pwd, p);
- xfree(p);
+ free(p);
return(abs_str);
} else
return(p);
}
static int
-parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
- int *rflag)
+parse_getput_flags(const char *cmd, char **argv, int argc,
+ int *aflag, int *fflag, int *pflag, int *rflag)
{
extern int opterr, optind, optopt, optreset;
int ch;
@@ -357,9 +370,15 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
optind = optreset = 1;
opterr = 0;
- *rflag = *pflag = 0;
- while ((ch = getopt(argc, argv, "PpRr")) != -1) {
+ *aflag = *fflag = *rflag = *pflag = 0;
+ while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
switch (ch) {
+ case 'a':
+ *aflag = 1;
+ break;
+ case 'f':
+ *fflag = 1;
+ break;
case 'p':
case 'P':
*pflag = 1;
@@ -402,6 +421,30 @@ parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
}
static int
+parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
+{
+ extern int opterr, optind, optopt, optreset;
+ int ch;
+
+ optind = optreset = 1;
+ opterr = 0;
+
+ *lflag = 0;
+ while ((ch = getopt(argc, argv, "l")) != -1) {
+ switch (ch) {
+ case 'l':
+ *lflag = 1;
+ break;
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
+static int
parse_ls_flags(char **argv, int argc, int *lflag)
{
extern int opterr, optind, optopt, optreset;
@@ -482,6 +525,26 @@ parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
}
static int
+parse_no_flags(const char *cmd, char **argv, int argc)
+{
+ extern int opterr, optind, optopt, optreset;
+ int ch;
+
+ optind = optreset = 1;
+ opterr = 0;
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch (ch) {
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
+static int
is_dir(char *path)
{
struct stat sb;
@@ -517,7 +580,7 @@ pathname_is_dir(char *pathname)
static int
process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
- int pflag, int rflag)
+ int pflag, int rflag, int resume, int fflag)
{
char *abs_src = NULL;
char *abs_dst = NULL;
@@ -551,7 +614,7 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
tmp = xstrdup(g.gl_pathv[i]);
if ((filename = basename(tmp)) == NULL) {
error("basename %s: %s", tmp, strerror(errno));
- xfree(tmp);
+ free(tmp);
err = -1;
goto out;
}
@@ -567,31 +630,37 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
} else {
abs_dst = xstrdup(filename);
}
- xfree(tmp);
+ free(tmp);
- printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
+ resume |= global_aflag;
+ if (!quiet && resume)
+ printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
+ else if (!quiet && !resume)
+ printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
- if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
- pflag || global_pflag, 1) == -1)
+ if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
+ pflag || global_pflag, 1, resume,
+ fflag || global_fflag) == -1)
err = -1;
} else {
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
- pflag || global_pflag) == -1)
+ pflag || global_pflag, resume,
+ fflag || global_fflag) == -1)
err = -1;
}
- xfree(abs_dst);
+ free(abs_dst);
abs_dst = NULL;
}
out:
- xfree(abs_src);
+ free(abs_src);
globfree(&g);
return(err);
}
static int
process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
- int pflag, int rflag)
+ int pflag, int rflag, int fflag)
{
char *tmp_dst = NULL;
char *abs_dst = NULL;
@@ -632,11 +701,11 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
error("stat %s: %s", g.gl_pathv[i], strerror(errno));
continue;
}
-
+
tmp = xstrdup(g.gl_pathv[i]);
if ((filename = basename(tmp)) == NULL) {
error("basename %s: %s", tmp, strerror(errno));
- xfree(tmp);
+ free(tmp);
err = -1;
goto out;
}
@@ -652,25 +721,26 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
} else {
abs_dst = make_absolute(xstrdup(filename), pwd);
}
- xfree(tmp);
+ free(tmp);
- printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
+ if (!quiet)
+ printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
if (upload_dir(conn, g.gl_pathv[i], abs_dst,
- pflag || global_pflag, 1) == -1)
+ pflag || global_pflag, 1,
+ fflag || global_fflag) == -1)
err = -1;
} else {
if (do_upload(conn, g.gl_pathv[i], abs_dst,
- pflag || global_pflag) == -1)
+ pflag || global_pflag,
+ fflag || global_fflag) == -1)
err = -1;
}
}
out:
- if (abs_dst)
- xfree(abs_dst);
- if (tmp_dst)
- xfree(tmp_dst);
+ free(abs_dst);
+ free(tmp_dst);
globfree(&g);
return(err);
}
@@ -718,7 +788,7 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
/* Add any subpath that also needs to be counted */
tmp = path_strip(path, strip_path);
m += strlen(tmp);
- xfree(tmp);
+ free(tmp);
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
width = ws.ws_col;
@@ -744,7 +814,7 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
tmp = path_append(path, d[n]->filename);
fname = path_strip(tmp, strip_path);
- xfree(tmp);
+ free(tmp);
if (lflag & LS_LONG_VIEW) {
if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
@@ -756,7 +826,7 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
lname = ls_file(fname, &sb, 1,
(lflag & LS_SI_UNITS));
printf("%s\n", lname);
- xfree(lname);
+ free(lname);
} else
printf("%s\n", d[n]->longname);
} else {
@@ -768,7 +838,7 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
c++;
}
- xfree(fname);
+ free(fname);
}
if (!(lflag & LS_LONG_VIEW) && (c != 1))
@@ -783,7 +853,6 @@ static int
do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
int lflag)
{
- Attrib *a = NULL;
char *fname, *lname;
glob_t g;
int err;
@@ -829,7 +898,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
colspace = width / columns;
}
- for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
+ for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
fname = path_strip(g.gl_pathv[i], strip_path);
if (lflag & LS_LONG_VIEW) {
if (g.gl_statv[i] == NULL) {
@@ -839,7 +908,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
lname = ls_file(fname, g.gl_statv[i], 1,
(lflag & LS_SI_UNITS));
printf("%s\n", lname);
- xfree(lname);
+ free(lname);
} else {
printf("%-*s", colspace, fname);
if (c >= columns) {
@@ -848,7 +917,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
} else
c++;
}
- xfree(fname);
+ free(fname);
}
if (!(lflag & LS_LONG_VIEW) && (c != 1))
@@ -962,7 +1031,7 @@ undo_glob_escape(char *s)
*
* If "lastquote" is not NULL, the quoting character used for the last
* argument is placed in *lastquote ("\0", "'" or "\"").
- *
+ *
* If "terminated" is not NULL, *terminated will be set to 1 when the
* last argument's quote has been properly terminated or 0 otherwise.
* This parameter is only of use if "sloppy" is set.
@@ -992,7 +1061,11 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
state = MA_START;
i = j = 0;
for (;;) {
- if (isspace(arg[i])) {
+ if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
+ error("Too many arguments.");
+ return NULL;
+ }
+ if (isspace((unsigned char)arg[i])) {
if (state == MA_UNQUOTED) {
/* Terminate current argument */
argvs[j++] = '\0';
@@ -1007,7 +1080,7 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
state = q;
if (lastquote != NULL)
*lastquote = arg[i];
- } else if (state == MA_UNQUOTED)
+ } else if (state == MA_UNQUOTED)
state = q;
else if (state == q)
state = MA_UNQUOTED;
@@ -1113,8 +1186,9 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
}
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)
+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)
{
const char *cmd, *cp = *cpp;
char *cp2, **argv;
@@ -1126,9 +1200,9 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
cp = cp + strspn(cp, WHITESPACE);
/* Check for leading '-' (disable error processing) */
- *iflag = 0;
+ *ignore_errors = 0;
if (*cp == '-') {
- *iflag = 1;
+ *ignore_errors = 1;
cp++;
cp = cp + strspn(cp, WHITESPACE);
}
@@ -1142,7 +1216,7 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
/* Figure out which command we have */
for (i = 0; cmds[i].c != NULL; i++) {
- if (strcasecmp(cmds[i].c, argv[0]) == 0)
+ if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
break;
}
cmdnum = cmds[i].n;
@@ -1158,14 +1232,16 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
}
/* Get arguments and parse flags */
- *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
+ *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
+ *rflag = *sflag = 0;
*path1 = *path2 = NULL;
optidx = 1;
switch (cmdnum) {
case I_GET:
+ case I_REGET:
case I_PUT:
if ((optidx = parse_getput_flags(cmd, argv, argc,
- pflag, rflag)) == -1)
+ aflag, fflag, pflag, rflag)) == -1)
return -1;
/* Get first pathname (mandatory) */
if (argc - optidx < 1) {
@@ -1180,12 +1256,24 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
/* Destination is not globbed */
undo_glob_escape(*path2);
}
+ if (*aflag && cmdnum == I_PUT) {
+ /* XXX implement resume for uploads */
+ error("Resume is not supported for uploads");
+ return -1;
+ }
break;
case I_LINK:
if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
return -1;
- case I_SYMLINK:
+ goto parse_two_paths;
case I_RENAME:
+ if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
+ return -1;
+ goto parse_two_paths;
+ case I_SYMLINK:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
+ parse_two_paths:
if (argc - optidx < 2) {
error("You must specify two paths after a %s "
"command.", cmd);
@@ -1203,6 +1291,8 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
case I_CHDIR:
case I_LCHDIR:
case I_LMKDIR:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
/* Get pathname (mandatory) */
if (argc - optidx < 1) {
error("You must specify a path after a %s command.",
@@ -1244,6 +1334,8 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
base = 8;
case I_CHOWN:
case I_CHGRP:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
/* Get numeric arg (mandatory) */
if (argc - optidx < 1)
goto need_num_arg;
@@ -1274,6 +1366,8 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
case I_HELP:
case I_VERSION:
case I_PROGRESS:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
break;
default:
fatal("Command not implemented");
@@ -1288,7 +1382,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
int err_abort)
{
char *path1, *path2, *tmp;
- int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
+ int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
+ int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
int cmdnum, i;
unsigned long n_arg = 0;
Attrib a, *aa;
@@ -1297,10 +1392,9 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
glob_t g;
path1 = path2 = NULL;
- cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
- &sflag, &n_arg, &path1, &path2);
-
- if (iflag != 0)
+ cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
+ &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
+ if (ignore_errors != 0)
err_abort = 0;
memset(&g, 0, sizeof(g));
@@ -1314,21 +1408,27 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
/* Unrecognized command */
err = -1;
break;
+ case I_REGET:
+ aflag = 1;
+ /* FALLTHROUGH */
case I_GET:
- err = process_get(conn, path1, path2, *pwd, pflag, rflag);
+ err = process_get(conn, path1, path2, *pwd, pflag,
+ rflag, aflag, fflag);
break;
case I_PUT:
- err = process_put(conn, path1, path2, *pwd, pflag, rflag);
+ err = process_put(conn, path1, path2, *pwd, pflag,
+ rflag, fflag);
break;
case I_RENAME:
path1 = make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd);
- err = do_rename(conn, path1, path2);
+ err = do_rename(conn, path1, path2, lflag);
break;
case I_SYMLINK:
sflag = 1;
case I_LINK:
- path1 = make_absolute(path1, *pwd);
+ if (!sflag)
+ path1 = make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd);
err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
break;
@@ -1336,7 +1436,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
path1 = make_absolute(path1, *pwd);
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- printf("Removing %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Removing %s\n", g.gl_pathv[i]);
err = do_rm(conn, g.gl_pathv[i]);
if (err != 0 && err_abort)
break;
@@ -1360,24 +1461,24 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
break;
}
if ((aa = do_stat(conn, tmp, 0)) == NULL) {
- xfree(tmp);
+ free(tmp);
err = 1;
break;
}
if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
error("Can't change directory: Can't check target");
- xfree(tmp);
+ free(tmp);
err = 1;
break;
}
if (!S_ISDIR(aa->perm)) {
error("Can't change directory: \"%s\" is not "
"a directory", tmp);
- xfree(tmp);
+ free(tmp);
err = 1;
break;
}
- xfree(*pwd);
+ free(*pwd);
*pwd = tmp;
break;
case I_LS:
@@ -1432,7 +1533,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
a.perm = n_arg;
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- printf("Changing mode on %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Changing mode on %s\n", g.gl_pathv[i]);
err = do_setstat(conn, g.gl_pathv[i], &a);
if (err != 0 && err_abort)
break;
@@ -1461,10 +1563,14 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
}
aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
if (cmdnum == I_CHOWN) {
- printf("Changing owner on %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Changing owner on %s\n",
+ g.gl_pathv[i]);
aa->uid = n_arg;
} else {
- printf("Changing group on %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Changing group on %s\n",
+ g.gl_pathv[i]);
aa->gid = n_arg;
}
err = do_setstat(conn, g.gl_pathv[i], aa);
@@ -1505,10 +1611,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
if (g.gl_pathc)
globfree(&g);
- if (path1)
- xfree(path1);
- if (path2)
- xfree(path2);
+ free(path1);
+ free(path2);
/* If an unignored error occurs in batch mode we should abort. */
if (err_abort && err != 0)
@@ -1535,7 +1639,7 @@ complete_display(char **list, u_int len)
char *tmp;
/* Count entries for sort and find longest */
- for (y = 0; list[y]; y++)
+ for (y = 0; list[y]; y++)
m = MAX(m, strlen(list[y]));
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
@@ -1580,8 +1684,8 @@ complete_ambiguous(const char *word, char **list, size_t count)
for (y = 1; list[y]; y++) {
u_int x;
- for (x = 0; x < matchlen; x++)
- if (list[0][x] != list[y][x])
+ for (x = 0; x < matchlen; x++)
+ if (list[0][x] != list[y][x])
break;
matchlen = x;
@@ -1593,7 +1697,7 @@ complete_ambiguous(const char *word, char **list, size_t count)
tmp[matchlen] = '\0';
return tmp;
}
- }
+ }
return xstrdup(word);
}
@@ -1613,26 +1717,26 @@ complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
if (cmd == NULL) {
for (y = 0; cmds[y].c; y++)
list[count++] = xstrdup(cmds[y].c);
-
+
list[count] = NULL;
complete_display(list, 0);
- for (y = 0; list[y] != NULL; y++)
- xfree(list[y]);
- xfree(list);
+ for (y = 0; list[y] != NULL; y++)
+ free(list[y]);
+ free(list);
return count;
}
/* Prepare subset of commands that start with "cmd" */
cmdlen = strlen(cmd);
for (y = 0; cmds[y].c; y++) {
- if (!strncasecmp(cmd, cmds[y].c, cmdlen))
+ if (!strncasecmp(cmd, cmds[y].c, cmdlen))
list[count++] = xstrdup(cmds[y].c);
}
list[count] = NULL;
if (count == 0) {
- xfree(list);
+ free(list);
return 0;
}
@@ -1641,9 +1745,9 @@ complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
if (count > 1)
complete_display(list, 0);
- for (y = 0; list[y]; y++)
- xfree(list[y]);
- xfree(list);
+ for (y = 0; list[y]; y++)
+ free(list[y]);
+ free(list);
if (tmp != NULL) {
tmplen = strlen(tmp);
@@ -1664,7 +1768,7 @@ complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
if (y > 0 && el_insertstr(el, argterm) == -1)
fatal("el_insertstr failed.");
}
- xfree(tmp);
+ free(tmp);
}
return count;
@@ -1682,7 +1786,7 @@ complete_is_remote(char *cmd) {
return -1;
for (i = 0; cmds[i].c; i++) {
- if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
+ if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
return cmds[i].t;
}
@@ -1695,23 +1799,27 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
char *file, int remote, int lastarg, char quote, int terminated)
{
glob_t g;
- char *tmp, *tmp2, ins[3];
- u_int i, hadglob, pwdlen, len, tmplen, filelen;
+ char *tmp, *tmp2, ins[8];
+ u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
+ int clen;
const LineInfo *lf;
-
+
/* Glob from "file" location */
if (file == NULL)
tmp = xstrdup("*");
else
xasprintf(&tmp, "%s*", file);
+ /* Check if the path is absolute. */
+ isabs = tmp[0] == '/';
+
memset(&g, 0, sizeof(g));
if (remote != LOCAL) {
tmp = make_absolute(tmp, remote_path);
remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
- } else
+ } else
glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
-
+
/* Determine length of pwd so we can trim completion display */
for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
/* Terminate counting on first unescaped glob metacharacter */
@@ -1725,9 +1833,9 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
if (tmp[tmplen] == '/')
pwdlen = tmplen + 1; /* track last seen '/' */
}
- xfree(tmp);
+ free(tmp);
- if (g.gl_matchc == 0)
+ if (g.gl_matchc == 0)
goto out;
if (g.gl_matchc > 1)
@@ -1739,8 +1847,8 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
goto out;
tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
- tmp = path_strip(tmp2, remote_path);
- xfree(tmp2);
+ tmp = path_strip(tmp2, isabs ? NULL : remote_path);
+ free(tmp2);
if (tmp == NULL)
goto out;
@@ -1748,14 +1856,27 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
tmplen = strlen(tmp);
filelen = strlen(file);
- if (tmplen > filelen) {
- tmp2 = tmp + filelen;
- len = strlen(tmp2);
+ /* Count the number of escaped characters in the input string. */
+ cesc = isesc = 0;
+ for (i = 0; i < filelen; i++) {
+ if (!isesc && file[i] == '\\' && i + 1 < filelen){
+ isesc = 1;
+ cesc++;
+ } else
+ isesc = 0;
+ }
+
+ if (tmplen > (filelen - cesc)) {
+ tmp2 = tmp + filelen - cesc;
+ len = strlen(tmp2);
/* quote argument on way out */
- for (i = 0; i < len; i++) {
+ for (i = 0; i < len; i += clen) {
+ if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
+ (size_t)clen > sizeof(ins) - 2)
+ fatal("invalid multibyte character");
ins[0] = '\\';
- ins[1] = tmp2[i];
- ins[2] = '\0';
+ memcpy(ins + 1, tmp2 + i, clen);
+ ins[clen + 1] = '\0';
switch (tmp2[i]) {
case '\'':
case '"':
@@ -1763,6 +1884,8 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
case '\t':
case '[':
case ' ':
+ case '#':
+ case '*':
if (quote == '\0' || tmp2[i] == quote) {
if (el_insertstr(el, ins) == -1)
fatal("el_insertstr "
@@ -1790,7 +1913,7 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
if (i > 0 && el_insertstr(el, ins) == -1)
fatal("el_insertstr failed.");
}
- xfree(tmp);
+ free(tmp);
out:
globfree(&g);
@@ -1801,8 +1924,9 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
static unsigned char
complete(EditLine *el, int ch)
{
- char **argv, *line, quote;
- u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
+ char **argv, *line, quote;
+ int argc, carg;
+ u_int cursor, len, terminated, ret = CC_ERROR;
const LineInfo *lf;
struct complete_ctx *complete_ctx;
@@ -1816,7 +1940,7 @@ complete(EditLine *el, int ch)
memcpy(line, lf->buffer, cursor);
line[cursor] = '\0';
argv = makeargv(line, &carg, 1, &quote, &terminated);
- xfree(line);
+ free(line);
/* Get all the arguments on the line */
len = lf->lastchar - lf->buffer;
@@ -1828,7 +1952,7 @@ complete(EditLine *el, int ch)
/* Ensure cursor is at EOL or a argument boundary */
if (line[cursor] != ' ' && line[cursor] != '\0' &&
line[cursor] != '\n') {
- xfree(line);
+ free(line);
return ret;
}
@@ -1839,7 +1963,7 @@ complete(EditLine *el, int ch)
} else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
/* Handle the command parsing */
if (complete_cmd_parse(el, argv[0], argc == carg,
- quote, terminated) != 0)
+ quote, terminated) != 0)
ret = CC_REDISPLAY;
} else if (carg >= 1) {
/* Handle file parsing */
@@ -1852,11 +1976,11 @@ complete(EditLine *el, int ch)
if (remote != 0 &&
complete_match(el, complete_ctx->conn,
*complete_ctx->remote_pathp, filematch,
- remote, carg == argc, quote, terminated) != 0)
+ remote, carg == argc, quote, terminated) != 0)
ret = CC_REDISPLAY;
}
- xfree(line);
+ free(line);
return ret;
}
#endif /* USE_LIBEDIT */
@@ -1890,12 +2014,19 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
el_source(el, NULL);
/* Tab Completion */
- el_set(el, EL_ADDFN, "ftp-complete",
+ el_set(el, EL_ADDFN, "ftp-complete",
"Context sensitive argument completion", complete);
complete_ctx.conn = conn;
complete_ctx.remote_pathp = &remote_path;
el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
+ /* enable ctrl-left-arrow and ctrl-right-arrow */
+ el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
+ el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
+ el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
+ el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
+ /* make ^w match ksh behaviour */
+ el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
}
#endif /* USE_LIBEDIT */
@@ -1908,39 +2039,34 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
dir = make_absolute(dir, remote_path);
if (remote_is_dir(conn, dir) && file2 == NULL) {
- printf("Changing to: %s\n", dir);
+ if (!quiet)
+ printf("Changing to: %s\n", dir);
snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
if (parse_dispatch_command(conn, cmd,
&remote_path, 1) != 0) {
- xfree(dir);
- xfree(remote_path);
- xfree(conn);
+ free(dir);
+ free(remote_path);
+ free(conn);
return (-1);
}
} else {
- if (file2 == NULL)
- snprintf(cmd, sizeof cmd, "get %s", dir);
- else
- snprintf(cmd, sizeof cmd, "get %s %s", dir,
- file2);
-
+ /* XXX this is wrong wrt quoting */
+ snprintf(cmd, sizeof cmd, "get%s %s%s%s",
+ global_aflag ? " -a" : "", dir,
+ file2 == NULL ? "" : " ",
+ file2 == NULL ? "" : file2);
err = parse_dispatch_command(conn, cmd,
&remote_path, 1);
- xfree(dir);
- xfree(remote_path);
- xfree(conn);
+ free(dir);
+ free(remote_path);
+ free(conn);
return (err);
}
- xfree(dir);
+ free(dir);
}
-#if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
- setvbuf(stdout, NULL, _IOLBF, 0);
- setvbuf(infile, NULL, _IOLBF, 0);
-#else
setlinebuf(stdout);
setlinebuf(infile);
-#endif
interactive = !batchmode && isatty(STDIN_FILENO);
err = 0;
@@ -1994,8 +2120,8 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
if (err != 0)
break;
}
- xfree(remote_path);
- xfree(conn);
+ free(remote_path);
+ free(conn);
#ifdef USE_LIBEDIT
if (el != NULL)
@@ -2069,7 +2195,7 @@ usage(void)
extern char *__progname;
fprintf(stderr,
- "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
+ "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
" [-D sftp_server_path] [-F ssh_config] "
"[-i identity_file] [-l limit]\n"
" [-o ssh_option] [-P port] [-R num_requests] "
@@ -2102,6 +2228,7 @@ main(int argc, char **argv)
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
+ setlocale(LC_CTYPE, "");
__progname = ssh_get_progname(argv[0]);
memset(&args, '\0', sizeof(args));
@@ -2116,7 +2243,7 @@ main(int argc, char **argv)
infile = stdin;
while ((ch = getopt(argc, argv,
- "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
+ "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
switch (ch) {
/* Passed through to ssh(1) */
case '4':
@@ -2133,6 +2260,8 @@ main(int argc, char **argv)
addargs(&args, "%s", optarg);
break;
case 'q':
+ ll = SYSLOG_LEVEL_ERROR;
+ quiet = 1;
showprogress = 0;
addargs(&args, "-%c", ch);
break;
@@ -2154,6 +2283,9 @@ main(int argc, char **argv)
case '2':
sshver = 2;
break;
+ case 'a':
+ global_aflag = 1;
+ break;
case 'B':
copy_buffer_len = strtol(optarg, &cp, 10);
if (copy_buffer_len == 0 || *cp != '\0')
@@ -2168,9 +2300,12 @@ main(int argc, char **argv)
(infile = fopen(optarg, "r")) == NULL)
fatal("%s (%s).", strerror(errno), optarg);
showprogress = 0;
- batchmode = 1;
+ quiet = batchmode = 1;
addargs(&args, "-obatchmode yes");
break;
+ case 'f':
+ global_fflag = 1;
+ break;
case 'p':
global_pflag = 1;
break;
@@ -2265,7 +2400,7 @@ main(int argc, char **argv)
if (conn == NULL)
fatal("Couldn't initialise connection to server");
- if (!batchmode) {
+ if (!quiet) {
if (sftp_direct == NULL)
fprintf(stderr, "Connected to %s.\n", host);
else