nrelease - fix/improve livecd
[dragonfly.git] / crypto / openssh / sftp.c
blobc3c347e087e498a9e8d24fc2d63192e0060dfc2d
1 /* $OpenBSD: sftp.c,v 1.222 2022/09/19 10:46:00 djm Exp $ */
2 /*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
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.
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.
18 #include "includes.h"
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/socket.h>
26 #include <sys/wait.h>
27 #ifdef HAVE_SYS_STATVFS_H
28 #include <sys/statvfs.h>
29 #endif
31 #include <ctype.h>
32 #include <errno.h>
34 #ifdef HAVE_PATHS_H
35 # include <paths.h>
36 #endif
37 #ifdef HAVE_LIBGEN_H
38 #include <libgen.h>
39 #endif
40 #ifdef HAVE_LOCALE_H
41 # include <locale.h>
42 #endif
43 #ifdef USE_LIBEDIT
44 #include <histedit.h>
45 #else
46 typedef void EditLine;
47 #endif
48 #include <limits.h>
49 #include <signal.h>
50 #include <stdarg.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <unistd.h>
56 #ifdef HAVE_UTIL_H
57 # include <util.h>
58 #endif
60 #include "xmalloc.h"
61 #include "log.h"
62 #include "pathnames.h"
63 #include "misc.h"
64 #include "utf8.h"
66 #include "sftp.h"
67 #include "ssherr.h"
68 #include "sshbuf.h"
69 #include "sftp-common.h"
70 #include "sftp-client.h"
71 #include "sftp-usergroup.h"
73 /* File to read commands from */
74 FILE* infile;
76 /* Are we in batchfile mode? */
77 int batchmode = 0;
79 /* PID of ssh transport process */
80 static volatile pid_t sshpid = -1;
82 /* Suppress diagnostic messages */
83 int quiet = 0;
85 /* This is set to 0 if the progressmeter is not desired. */
86 int showprogress = 1;
88 /* When this option is set, we always recursively download/upload directories */
89 int global_rflag = 0;
91 /* When this option is set, we resume download or upload if possible */
92 int global_aflag = 0;
94 /* When this option is set, the file transfers will always preserve times */
95 int global_pflag = 0;
97 /* When this option is set, transfers will have fsync() called on each file */
98 int global_fflag = 0;
100 /* SIGINT received during command processing */
101 volatile sig_atomic_t interrupted = 0;
103 /* I wish qsort() took a separate ctx for the comparison function...*/
104 int sort_flag;
105 glob_t *sort_glob;
107 /* Context used for commandline completion */
108 struct complete_ctx {
109 struct sftp_conn *conn;
110 char **remote_pathp;
113 int remote_glob(struct sftp_conn *, const char *, int,
114 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
116 extern char *__progname;
118 /* Separators for interactive commands */
119 #define WHITESPACE " \t\r\n"
121 /* ls flags */
122 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
123 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
124 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
125 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
126 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
127 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
128 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
129 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
130 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
132 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
133 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
135 /* Commands for interactive mode */
136 enum sftp_command {
137 I_CHDIR = 1,
138 I_CHGRP,
139 I_CHMOD,
140 I_CHOWN,
141 I_COPY,
142 I_DF,
143 I_GET,
144 I_HELP,
145 I_LCHDIR,
146 I_LINK,
147 I_LLS,
148 I_LMKDIR,
149 I_LPWD,
150 I_LS,
151 I_LUMASK,
152 I_MKDIR,
153 I_PUT,
154 I_PWD,
155 I_QUIT,
156 I_REGET,
157 I_RENAME,
158 I_REPUT,
159 I_RM,
160 I_RMDIR,
161 I_SHELL,
162 I_SYMLINK,
163 I_VERSION,
164 I_PROGRESS,
167 struct CMD {
168 const char *c;
169 const int n;
170 const int t; /* Completion type for the first argument */
171 const int t2; /* completion type for the optional second argument */
174 /* Type of completion */
175 #define NOARGS 0
176 #define REMOTE 1
177 #define LOCAL 2
179 static const struct CMD cmds[] = {
180 { "bye", I_QUIT, NOARGS, NOARGS },
181 { "cd", I_CHDIR, REMOTE, NOARGS },
182 { "chdir", I_CHDIR, REMOTE, NOARGS },
183 { "chgrp", I_CHGRP, REMOTE, NOARGS },
184 { "chmod", I_CHMOD, REMOTE, NOARGS },
185 { "chown", I_CHOWN, REMOTE, NOARGS },
186 { "copy", I_COPY, REMOTE, LOCAL },
187 { "cp", I_COPY, REMOTE, LOCAL },
188 { "df", I_DF, REMOTE, NOARGS },
189 { "dir", I_LS, REMOTE, NOARGS },
190 { "exit", I_QUIT, NOARGS, NOARGS },
191 { "get", I_GET, REMOTE, LOCAL },
192 { "help", I_HELP, NOARGS, NOARGS },
193 { "lcd", I_LCHDIR, LOCAL, NOARGS },
194 { "lchdir", I_LCHDIR, LOCAL, NOARGS },
195 { "lls", I_LLS, LOCAL, NOARGS },
196 { "lmkdir", I_LMKDIR, LOCAL, NOARGS },
197 { "ln", I_LINK, REMOTE, REMOTE },
198 { "lpwd", I_LPWD, LOCAL, NOARGS },
199 { "ls", I_LS, REMOTE, NOARGS },
200 { "lumask", I_LUMASK, NOARGS, NOARGS },
201 { "mkdir", I_MKDIR, REMOTE, NOARGS },
202 { "mget", I_GET, REMOTE, LOCAL },
203 { "mput", I_PUT, LOCAL, REMOTE },
204 { "progress", I_PROGRESS, NOARGS, NOARGS },
205 { "put", I_PUT, LOCAL, REMOTE },
206 { "pwd", I_PWD, REMOTE, NOARGS },
207 { "quit", I_QUIT, NOARGS, NOARGS },
208 { "reget", I_REGET, REMOTE, LOCAL },
209 { "rename", I_RENAME, REMOTE, REMOTE },
210 { "reput", I_REPUT, LOCAL, REMOTE },
211 { "rm", I_RM, REMOTE, NOARGS },
212 { "rmdir", I_RMDIR, REMOTE, NOARGS },
213 { "symlink", I_SYMLINK, REMOTE, REMOTE },
214 { "version", I_VERSION, NOARGS, NOARGS },
215 { "!", I_SHELL, NOARGS, NOARGS },
216 { "?", I_HELP, NOARGS, NOARGS },
217 { NULL, -1, -1, -1 }
220 /* ARGSUSED */
221 static void
222 killchild(int signo)
224 pid_t pid;
226 pid = sshpid;
227 if (pid > 1) {
228 kill(pid, SIGTERM);
229 waitpid(pid, NULL, 0);
232 _exit(1);
235 /* ARGSUSED */
236 static void
237 suspchild(int signo)
239 if (sshpid > 1) {
240 kill(sshpid, signo);
241 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
242 continue;
244 kill(getpid(), SIGSTOP);
247 /* ARGSUSED */
248 static void
249 cmd_interrupt(int signo)
251 const char msg[] = "\rInterrupt \n";
252 int olderrno = errno;
254 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
255 interrupted = 1;
256 errno = olderrno;
259 /* ARGSUSED */
260 static void
261 read_interrupt(int signo)
263 interrupted = 1;
266 /*ARGSUSED*/
267 static void
268 sigchld_handler(int sig)
270 int save_errno = errno;
271 pid_t pid;
272 const char msg[] = "\rConnection closed. \n";
274 /* Report if ssh transport process dies. */
275 while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
276 continue;
277 if (pid == sshpid) {
278 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
279 sshpid = -1;
282 errno = save_errno;
285 static void
286 help(void)
288 printf("Available commands:\n"
289 "bye Quit sftp\n"
290 "cd path Change remote directory to 'path'\n"
291 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
292 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
293 "chown [-h] own path Change owner of file 'path' to 'own'\n"
294 "copy oldpath newpath Copy remote file\n"
295 "cp oldpath newpath Copy remote file\n"
296 "df [-hi] [path] Display statistics for current directory or\n"
297 " filesystem containing 'path'\n"
298 "exit Quit sftp\n"
299 "get [-afpR] remote [local] Download file\n"
300 "help Display this help text\n"
301 "lcd path Change local directory to 'path'\n"
302 "lls [ls-options [path]] Display local directory listing\n"
303 "lmkdir path Create local directory\n"
304 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
305 "lpwd Print local working directory\n"
306 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
307 "lumask umask Set local umask to 'umask'\n"
308 "mkdir path Create remote directory\n"
309 "progress Toggle display of progress meter\n"
310 "put [-afpR] local [remote] Upload file\n"
311 "pwd Display remote working directory\n"
312 "quit Quit sftp\n"
313 "reget [-fpR] remote [local] Resume download file\n"
314 "rename oldpath newpath Rename remote file\n"
315 "reput [-fpR] local [remote] Resume upload file\n"
316 "rm path Delete remote file\n"
317 "rmdir path Remove remote directory\n"
318 "symlink oldpath newpath Symlink remote file\n"
319 "version Show SFTP version\n"
320 "!command Execute 'command' in local shell\n"
321 "! Escape to local shell\n"
322 "? Synonym for help\n");
325 static void
326 local_do_shell(const char *args)
328 int status;
329 char *shell;
330 pid_t pid;
332 if (!*args)
333 args = NULL;
335 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
336 shell = _PATH_BSHELL;
338 if ((pid = fork()) == -1)
339 fatal("Couldn't fork: %s", strerror(errno));
341 if (pid == 0) {
342 /* XXX: child has pipe fds to ssh subproc open - issue? */
343 if (args) {
344 debug3("Executing %s -c \"%s\"", shell, args);
345 execl(shell, shell, "-c", args, (char *)NULL);
346 } else {
347 debug3("Executing %s", shell);
348 execl(shell, shell, (char *)NULL);
350 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
351 strerror(errno));
352 _exit(1);
354 while (waitpid(pid, &status, 0) == -1)
355 if (errno != EINTR)
356 fatal("Couldn't wait for child: %s", strerror(errno));
357 if (!WIFEXITED(status))
358 error("Shell exited abnormally");
359 else if (WEXITSTATUS(status))
360 error("Shell exited with status %d", WEXITSTATUS(status));
363 static void
364 local_do_ls(const char *args)
366 if (!args || !*args)
367 local_do_shell(_PATH_LS);
368 else {
369 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
370 char *buf = xmalloc(len);
372 /* XXX: quoting - rip quoting code from ftp? */
373 snprintf(buf, len, _PATH_LS " %s", args);
374 local_do_shell(buf);
375 free(buf);
379 /* Strip one path (usually the pwd) from the start of another */
380 static char *
381 path_strip(const char *path, const char *strip)
383 size_t len;
385 if (strip == NULL)
386 return (xstrdup(path));
388 len = strlen(strip);
389 if (strncmp(path, strip, len) == 0) {
390 if (strip[len - 1] != '/' && path[len] == '/')
391 len++;
392 return (xstrdup(path + len));
395 return (xstrdup(path));
398 static int
399 parse_getput_flags(const char *cmd, char **argv, int argc,
400 int *aflag, int *fflag, int *pflag, int *rflag)
402 extern int opterr, optind, optopt, optreset;
403 int ch;
405 optind = optreset = 1;
406 opterr = 0;
408 *aflag = *fflag = *rflag = *pflag = 0;
409 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
410 switch (ch) {
411 case 'a':
412 *aflag = 1;
413 break;
414 case 'f':
415 *fflag = 1;
416 break;
417 case 'p':
418 case 'P':
419 *pflag = 1;
420 break;
421 case 'r':
422 case 'R':
423 *rflag = 1;
424 break;
425 default:
426 error("%s: Invalid flag -%c", cmd, optopt);
427 return -1;
431 return optind;
434 static int
435 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
437 extern int opterr, optind, optopt, optreset;
438 int ch;
440 optind = optreset = 1;
441 opterr = 0;
443 *sflag = 0;
444 while ((ch = getopt(argc, argv, "s")) != -1) {
445 switch (ch) {
446 case 's':
447 *sflag = 1;
448 break;
449 default:
450 error("%s: Invalid flag -%c", cmd, optopt);
451 return -1;
455 return optind;
458 static int
459 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
461 extern int opterr, optind, optopt, optreset;
462 int ch;
464 optind = optreset = 1;
465 opterr = 0;
467 *lflag = 0;
468 while ((ch = getopt(argc, argv, "l")) != -1) {
469 switch (ch) {
470 case 'l':
471 *lflag = 1;
472 break;
473 default:
474 error("%s: Invalid flag -%c", cmd, optopt);
475 return -1;
479 return optind;
482 static int
483 parse_ls_flags(char **argv, int argc, int *lflag)
485 extern int opterr, optind, optopt, optreset;
486 int ch;
488 optind = optreset = 1;
489 opterr = 0;
491 *lflag = LS_NAME_SORT;
492 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
493 switch (ch) {
494 case '1':
495 *lflag &= ~VIEW_FLAGS;
496 *lflag |= LS_SHORT_VIEW;
497 break;
498 case 'S':
499 *lflag &= ~SORT_FLAGS;
500 *lflag |= LS_SIZE_SORT;
501 break;
502 case 'a':
503 *lflag |= LS_SHOW_ALL;
504 break;
505 case 'f':
506 *lflag &= ~SORT_FLAGS;
507 break;
508 case 'h':
509 *lflag |= LS_SI_UNITS;
510 break;
511 case 'l':
512 *lflag &= ~LS_SHORT_VIEW;
513 *lflag |= LS_LONG_VIEW;
514 break;
515 case 'n':
516 *lflag &= ~LS_SHORT_VIEW;
517 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
518 break;
519 case 'r':
520 *lflag |= LS_REVERSE_SORT;
521 break;
522 case 't':
523 *lflag &= ~SORT_FLAGS;
524 *lflag |= LS_TIME_SORT;
525 break;
526 default:
527 error("ls: Invalid flag -%c", optopt);
528 return -1;
532 return optind;
535 static int
536 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
538 extern int opterr, optind, optopt, optreset;
539 int ch;
541 optind = optreset = 1;
542 opterr = 0;
544 *hflag = *iflag = 0;
545 while ((ch = getopt(argc, argv, "hi")) != -1) {
546 switch (ch) {
547 case 'h':
548 *hflag = 1;
549 break;
550 case 'i':
551 *iflag = 1;
552 break;
553 default:
554 error("%s: Invalid flag -%c", cmd, optopt);
555 return -1;
559 return optind;
562 static int
563 parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
565 extern int opterr, optind, optopt, optreset;
566 int ch;
568 optind = optreset = 1;
569 opterr = 0;
571 *hflag = 0;
572 while ((ch = getopt(argc, argv, "h")) != -1) {
573 switch (ch) {
574 case 'h':
575 *hflag = 1;
576 break;
577 default:
578 error("%s: Invalid flag -%c", cmd, optopt);
579 return -1;
583 return optind;
586 static int
587 parse_no_flags(const char *cmd, char **argv, int argc)
589 extern int opterr, optind, optopt, optreset;
590 int ch;
592 optind = optreset = 1;
593 opterr = 0;
595 while ((ch = getopt(argc, argv, "")) != -1) {
596 switch (ch) {
597 default:
598 error("%s: Invalid flag -%c", cmd, optopt);
599 return -1;
603 return optind;
606 static char *
607 escape_glob(const char *s)
609 size_t i, o, len;
610 char *ret;
612 len = strlen(s);
613 ret = xcalloc(2, len + 1);
614 for (i = o = 0; i < len; i++) {
615 if (strchr("[]?*\\", s[i]) != NULL)
616 ret[o++] = '\\';
617 ret[o++] = s[i];
619 ret[o++] = '\0';
620 return ret;
623 static char *
624 make_absolute_pwd_glob(const char *p, const char *pwd)
626 char *ret, *escpwd;
628 escpwd = escape_glob(pwd);
629 if (p == NULL)
630 return escpwd;
631 ret = make_absolute(xstrdup(p), escpwd);
632 free(escpwd);
633 return ret;
636 static int
637 process_get(struct sftp_conn *conn, const char *src, const char *dst,
638 const char *pwd, int pflag, int rflag, int resume, int fflag)
640 char *filename, *abs_src = NULL, *abs_dst = NULL, *tmp = NULL;
641 glob_t g;
642 int i, r, err = 0;
644 abs_src = make_absolute_pwd_glob(src, pwd);
645 memset(&g, 0, sizeof(g));
647 debug3("Looking up %s", abs_src);
648 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
649 if (r == GLOB_NOSPACE) {
650 error("Too many matches for \"%s\".", abs_src);
651 } else {
652 error("File \"%s\" not found.", abs_src);
654 err = -1;
655 goto out;
659 * If multiple matches then dst must be a directory or
660 * unspecified.
662 if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) {
663 error("Multiple source paths, but destination "
664 "\"%s\" is not a directory", dst);
665 err = -1;
666 goto out;
669 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
670 tmp = xstrdup(g.gl_pathv[i]);
671 if ((filename = basename(tmp)) == NULL) {
672 error("basename %s: %s", tmp, strerror(errno));
673 free(tmp);
674 err = -1;
675 goto out;
678 if (g.gl_matchc == 1 && dst) {
679 if (local_is_dir(dst)) {
680 abs_dst = path_append(dst, filename);
681 } else {
682 abs_dst = xstrdup(dst);
684 } else if (dst) {
685 abs_dst = path_append(dst, filename);
686 } else {
687 abs_dst = xstrdup(filename);
689 free(tmp);
691 resume |= global_aflag;
692 if (!quiet && resume)
693 mprintf("Resuming %s to %s\n",
694 g.gl_pathv[i], abs_dst);
695 else if (!quiet && !resume)
696 mprintf("Fetching %s to %s\n",
697 g.gl_pathv[i], abs_dst);
698 /* XXX follow link flag */
699 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
700 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
701 pflag || global_pflag, 1, resume,
702 fflag || global_fflag, 0, 0) == -1)
703 err = -1;
704 } else {
705 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
706 pflag || global_pflag, resume,
707 fflag || global_fflag, 0) == -1)
708 err = -1;
710 free(abs_dst);
711 abs_dst = NULL;
714 out:
715 free(abs_src);
716 globfree(&g);
717 return(err);
720 static int
721 process_put(struct sftp_conn *conn, const char *src, const char *dst,
722 const char *pwd, int pflag, int rflag, int resume, int fflag)
724 char *tmp_dst = NULL;
725 char *abs_dst = NULL;
726 char *tmp = NULL, *filename = NULL;
727 glob_t g;
728 int err = 0;
729 int i, dst_is_dir = 1;
730 struct stat sb;
732 if (dst) {
733 tmp_dst = xstrdup(dst);
734 tmp_dst = make_absolute(tmp_dst, pwd);
737 memset(&g, 0, sizeof(g));
738 debug3("Looking up %s", src);
739 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
740 error("File \"%s\" not found.", src);
741 err = -1;
742 goto out;
745 /* If we aren't fetching to pwd then stash this status for later */
746 if (tmp_dst != NULL)
747 dst_is_dir = remote_is_dir(conn, tmp_dst);
749 /* If multiple matches, dst may be directory or unspecified */
750 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
751 error("Multiple paths match, but destination "
752 "\"%s\" is not a directory", tmp_dst);
753 err = -1;
754 goto out;
757 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
758 if (stat(g.gl_pathv[i], &sb) == -1) {
759 err = -1;
760 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
761 continue;
764 tmp = xstrdup(g.gl_pathv[i]);
765 if ((filename = basename(tmp)) == NULL) {
766 error("basename %s: %s", tmp, strerror(errno));
767 free(tmp);
768 err = -1;
769 goto out;
772 if (g.gl_matchc == 1 && tmp_dst) {
773 /* If directory specified, append filename */
774 if (dst_is_dir)
775 abs_dst = path_append(tmp_dst, filename);
776 else
777 abs_dst = xstrdup(tmp_dst);
778 } else if (tmp_dst) {
779 abs_dst = path_append(tmp_dst, filename);
780 } else {
781 abs_dst = make_absolute(xstrdup(filename), pwd);
783 free(tmp);
785 resume |= global_aflag;
786 if (!quiet && resume)
787 mprintf("Resuming upload of %s to %s\n",
788 g.gl_pathv[i], abs_dst);
789 else if (!quiet && !resume)
790 mprintf("Uploading %s to %s\n",
791 g.gl_pathv[i], abs_dst);
792 /* XXX follow_link_flag */
793 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
794 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
795 pflag || global_pflag, 1, resume,
796 fflag || global_fflag, 0, 0) == -1)
797 err = -1;
798 } else {
799 if (do_upload(conn, g.gl_pathv[i], abs_dst,
800 pflag || global_pflag, resume,
801 fflag || global_fflag, 0) == -1)
802 err = -1;
806 out:
807 free(abs_dst);
808 free(tmp_dst);
809 globfree(&g);
810 return(err);
813 static int
814 sdirent_comp(const void *aa, const void *bb)
816 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
817 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
818 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
820 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
821 if (sort_flag & LS_NAME_SORT)
822 return (rmul * strcmp(a->filename, b->filename));
823 else if (sort_flag & LS_TIME_SORT)
824 return (rmul * NCMP(a->a.mtime, b->a.mtime));
825 else if (sort_flag & LS_SIZE_SORT)
826 return (rmul * NCMP(a->a.size, b->a.size));
828 fatal("Unknown ls sort type");
831 /* sftp ls.1 replacement for directories */
832 static int
833 do_ls_dir(struct sftp_conn *conn, const char *path,
834 const char *strip_path, int lflag)
836 int n;
837 u_int c = 1, colspace = 0, columns = 1;
838 SFTP_DIRENT **d;
840 if ((n = do_readdir(conn, path, &d)) != 0)
841 return (n);
843 if (!(lflag & LS_SHORT_VIEW)) {
844 u_int m = 0, width = 80;
845 struct winsize ws;
846 char *tmp;
848 /* Count entries for sort and find longest filename */
849 for (n = 0; d[n] != NULL; n++) {
850 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
851 m = MAXIMUM(m, strlen(d[n]->filename));
854 /* Add any subpath that also needs to be counted */
855 tmp = path_strip(path, strip_path);
856 m += strlen(tmp);
857 free(tmp);
859 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
860 width = ws.ws_col;
862 columns = width / (m + 2);
863 columns = MAXIMUM(columns, 1);
864 colspace = width / columns;
865 colspace = MINIMUM(colspace, width);
868 if (lflag & SORT_FLAGS) {
869 for (n = 0; d[n] != NULL; n++)
870 ; /* count entries */
871 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
872 qsort(d, n, sizeof(*d), sdirent_comp);
875 get_remote_user_groups_from_dirents(conn, d);
876 for (n = 0; d[n] != NULL && !interrupted; n++) {
877 char *tmp, *fname;
879 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
880 continue;
882 tmp = path_append(path, d[n]->filename);
883 fname = path_strip(tmp, strip_path);
884 free(tmp);
886 if (lflag & LS_LONG_VIEW) {
887 if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 ||
888 can_get_users_groups_by_id(conn)) {
889 char *lname;
890 struct stat sb;
892 memset(&sb, 0, sizeof(sb));
893 attrib_to_stat(&d[n]->a, &sb);
894 lname = ls_file(fname, &sb, 1,
895 (lflag & LS_SI_UNITS),
896 ruser_name(sb.st_uid),
897 rgroup_name(sb.st_gid));
898 mprintf("%s\n", lname);
899 free(lname);
900 } else
901 mprintf("%s\n", d[n]->longname);
902 } else {
903 mprintf("%-*s", colspace, fname);
904 if (c >= columns) {
905 printf("\n");
906 c = 1;
907 } else
908 c++;
911 free(fname);
914 if (!(lflag & LS_LONG_VIEW) && (c != 1))
915 printf("\n");
917 free_sftp_dirents(d);
918 return (0);
921 static int
922 sglob_comp(const void *aa, const void *bb)
924 u_int a = *(const u_int *)aa;
925 u_int b = *(const u_int *)bb;
926 const char *ap = sort_glob->gl_pathv[a];
927 const char *bp = sort_glob->gl_pathv[b];
928 const struct stat *as = sort_glob->gl_statv[a];
929 const struct stat *bs = sort_glob->gl_statv[b];
930 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
932 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
933 if (sort_flag & LS_NAME_SORT)
934 return (rmul * strcmp(ap, bp));
935 else if (sort_flag & LS_TIME_SORT) {
936 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
937 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==))
938 return 0;
939 return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ?
940 rmul : -rmul;
941 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
942 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
943 #else
944 return rmul * 1;
945 #endif
946 } else if (sort_flag & LS_SIZE_SORT)
947 return (rmul * NCMP(as->st_size, bs->st_size));
949 fatal("Unknown ls sort type");
952 /* sftp ls.1 replacement which handles path globs */
953 static int
954 do_globbed_ls(struct sftp_conn *conn, const char *path,
955 const char *strip_path, int lflag)
957 char *fname, *lname;
958 glob_t g;
959 int err, r;
960 struct winsize ws;
961 u_int i, j, nentries, *indices = NULL, c = 1;
962 u_int colspace = 0, columns = 1, m = 0, width = 80;
964 memset(&g, 0, sizeof(g));
966 if ((r = remote_glob(conn, path,
967 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
968 NULL, &g)) != 0 ||
969 (g.gl_pathc && !g.gl_matchc)) {
970 if (g.gl_pathc)
971 globfree(&g);
972 if (r == GLOB_NOSPACE) {
973 error("Can't ls: Too many matches for \"%s\"", path);
974 } else {
975 error("Can't ls: \"%s\" not found", path);
977 return -1;
980 if (interrupted)
981 goto out;
984 * If the glob returns a single match and it is a directory,
985 * then just list its contents.
987 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
988 S_ISDIR(g.gl_statv[0]->st_mode)) {
989 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
990 globfree(&g);
991 return err;
994 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
995 width = ws.ws_col;
997 if (!(lflag & LS_SHORT_VIEW)) {
998 /* Count entries for sort and find longest filename */
999 for (i = 0; g.gl_pathv[i]; i++)
1000 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
1002 columns = width / (m + 2);
1003 columns = MAXIMUM(columns, 1);
1004 colspace = width / columns;
1008 * Sorting: rather than mess with the contents of glob_t, prepare
1009 * an array of indices into it and sort that. For the usual
1010 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1012 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1013 ; /* count entries */
1014 indices = calloc(nentries, sizeof(*indices));
1015 for (i = 0; i < nentries; i++)
1016 indices[i] = i;
1018 if (lflag & SORT_FLAGS) {
1019 sort_glob = &g;
1020 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1021 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1022 sort_glob = NULL;
1025 get_remote_user_groups_from_glob(conn, &g);
1026 for (j = 0; j < nentries && !interrupted; j++) {
1027 i = indices[j];
1028 fname = path_strip(g.gl_pathv[i], strip_path);
1029 if (lflag & LS_LONG_VIEW) {
1030 if (g.gl_statv[i] == NULL) {
1031 error("no stat information for %s", fname);
1032 continue;
1034 lname = ls_file(fname, g.gl_statv[i], 1,
1035 (lflag & LS_SI_UNITS),
1036 ruser_name(g.gl_statv[i]->st_uid),
1037 rgroup_name(g.gl_statv[i]->st_gid));
1038 mprintf("%s\n", lname);
1039 free(lname);
1040 } else {
1041 mprintf("%-*s", colspace, fname);
1042 if (c >= columns) {
1043 printf("\n");
1044 c = 1;
1045 } else
1046 c++;
1048 free(fname);
1051 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1052 printf("\n");
1054 out:
1055 if (g.gl_pathc)
1056 globfree(&g);
1057 free(indices);
1059 return 0;
1062 static int
1063 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1065 struct sftp_statvfs st;
1066 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1067 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1068 char s_icapacity[16], s_dcapacity[16];
1070 if (do_statvfs(conn, path, &st, 1) == -1)
1071 return -1;
1072 if (st.f_files == 0)
1073 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1074 else {
1075 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1076 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1077 st.f_files));
1079 if (st.f_blocks == 0)
1080 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1081 else {
1082 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1083 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1084 st.f_blocks));
1086 if (iflag) {
1087 printf(" Inodes Used Avail "
1088 "(root) %%Capacity\n");
1089 printf("%11llu %11llu %11llu %11llu %s\n",
1090 (unsigned long long)st.f_files,
1091 (unsigned long long)(st.f_files - st.f_ffree),
1092 (unsigned long long)st.f_favail,
1093 (unsigned long long)st.f_ffree, s_icapacity);
1094 } else if (hflag) {
1095 strlcpy(s_used, "error", sizeof(s_used));
1096 strlcpy(s_avail, "error", sizeof(s_avail));
1097 strlcpy(s_root, "error", sizeof(s_root));
1098 strlcpy(s_total, "error", sizeof(s_total));
1099 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1100 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1101 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1102 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1103 printf(" Size Used Avail (root) %%Capacity\n");
1104 printf("%7sB %7sB %7sB %7sB %s\n",
1105 s_total, s_used, s_avail, s_root, s_dcapacity);
1106 } else {
1107 printf(" Size Used Avail "
1108 "(root) %%Capacity\n");
1109 printf("%12llu %12llu %12llu %12llu %s\n",
1110 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1111 (unsigned long long)(st.f_frsize *
1112 (st.f_blocks - st.f_bfree) / 1024),
1113 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1114 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1115 s_dcapacity);
1117 return 0;
1121 * Undo escaping of glob sequences in place. Used to undo extra escaping
1122 * applied in makeargv() when the string is destined for a function that
1123 * does not glob it.
1125 static void
1126 undo_glob_escape(char *s)
1128 size_t i, j;
1130 for (i = j = 0;;) {
1131 if (s[i] == '\0') {
1132 s[j] = '\0';
1133 return;
1135 if (s[i] != '\\') {
1136 s[j++] = s[i++];
1137 continue;
1139 /* s[i] == '\\' */
1140 ++i;
1141 switch (s[i]) {
1142 case '?':
1143 case '[':
1144 case '*':
1145 case '\\':
1146 s[j++] = s[i++];
1147 break;
1148 case '\0':
1149 s[j++] = '\\';
1150 s[j] = '\0';
1151 return;
1152 default:
1153 s[j++] = '\\';
1154 s[j++] = s[i++];
1155 break;
1161 * Split a string into an argument vector using sh(1)-style quoting,
1162 * comment and escaping rules, but with some tweaks to handle glob(3)
1163 * wildcards.
1164 * The "sloppy" flag allows for recovery from missing terminating quote, for
1165 * use in parsing incomplete commandlines during tab autocompletion.
1167 * Returns NULL on error or a NULL-terminated array of arguments.
1169 * If "lastquote" is not NULL, the quoting character used for the last
1170 * argument is placed in *lastquote ("\0", "'" or "\"").
1172 * If "terminated" is not NULL, *terminated will be set to 1 when the
1173 * last argument's quote has been properly terminated or 0 otherwise.
1174 * This parameter is only of use if "sloppy" is set.
1176 #define MAXARGS 128
1177 #define MAXARGLEN 8192
1178 static char **
1179 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1180 u_int *terminated)
1182 int argc, quot;
1183 size_t i, j;
1184 static char argvs[MAXARGLEN];
1185 static char *argv[MAXARGS + 1];
1186 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1188 *argcp = argc = 0;
1189 if (strlen(arg) > sizeof(argvs) - 1) {
1190 args_too_longs:
1191 error("string too long");
1192 return NULL;
1194 if (terminated != NULL)
1195 *terminated = 1;
1196 if (lastquote != NULL)
1197 *lastquote = '\0';
1198 state = MA_START;
1199 i = j = 0;
1200 for (;;) {
1201 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1202 error("Too many arguments.");
1203 return NULL;
1205 if (isspace((unsigned char)arg[i])) {
1206 if (state == MA_UNQUOTED) {
1207 /* Terminate current argument */
1208 argvs[j++] = '\0';
1209 argc++;
1210 state = MA_START;
1211 } else if (state != MA_START)
1212 argvs[j++] = arg[i];
1213 } else if (arg[i] == '"' || arg[i] == '\'') {
1214 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1215 if (state == MA_START) {
1216 argv[argc] = argvs + j;
1217 state = q;
1218 if (lastquote != NULL)
1219 *lastquote = arg[i];
1220 } else if (state == MA_UNQUOTED)
1221 state = q;
1222 else if (state == q)
1223 state = MA_UNQUOTED;
1224 else
1225 argvs[j++] = arg[i];
1226 } else if (arg[i] == '\\') {
1227 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1228 quot = state == MA_SQUOTE ? '\'' : '"';
1229 /* Unescape quote we are in */
1230 /* XXX support \n and friends? */
1231 if (arg[i + 1] == quot) {
1232 i++;
1233 argvs[j++] = arg[i];
1234 } else if (arg[i + 1] == '?' ||
1235 arg[i + 1] == '[' || arg[i + 1] == '*') {
1237 * Special case for sftp: append
1238 * double-escaped glob sequence -
1239 * glob will undo one level of
1240 * escaping. NB. string can grow here.
1242 if (j >= sizeof(argvs) - 5)
1243 goto args_too_longs;
1244 argvs[j++] = '\\';
1245 argvs[j++] = arg[i++];
1246 argvs[j++] = '\\';
1247 argvs[j++] = arg[i];
1248 } else {
1249 argvs[j++] = arg[i++];
1250 argvs[j++] = arg[i];
1252 } else {
1253 if (state == MA_START) {
1254 argv[argc] = argvs + j;
1255 state = MA_UNQUOTED;
1256 if (lastquote != NULL)
1257 *lastquote = '\0';
1259 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1260 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1262 * Special case for sftp: append
1263 * escaped glob sequence -
1264 * glob will undo one level of
1265 * escaping.
1267 argvs[j++] = arg[i++];
1268 argvs[j++] = arg[i];
1269 } else {
1270 /* Unescape everything */
1271 /* XXX support \n and friends? */
1272 i++;
1273 argvs[j++] = arg[i];
1276 } else if (arg[i] == '#') {
1277 if (state == MA_SQUOTE || state == MA_DQUOTE)
1278 argvs[j++] = arg[i];
1279 else
1280 goto string_done;
1281 } else if (arg[i] == '\0') {
1282 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1283 if (sloppy) {
1284 state = MA_UNQUOTED;
1285 if (terminated != NULL)
1286 *terminated = 0;
1287 goto string_done;
1289 error("Unterminated quoted argument");
1290 return NULL;
1292 string_done:
1293 if (state == MA_UNQUOTED) {
1294 argvs[j++] = '\0';
1295 argc++;
1297 break;
1298 } else {
1299 if (state == MA_START) {
1300 argv[argc] = argvs + j;
1301 state = MA_UNQUOTED;
1302 if (lastquote != NULL)
1303 *lastquote = '\0';
1305 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1306 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1308 * Special case for sftp: escape quoted
1309 * glob(3) wildcards. NB. string can grow
1310 * here.
1312 if (j >= sizeof(argvs) - 3)
1313 goto args_too_longs;
1314 argvs[j++] = '\\';
1315 argvs[j++] = arg[i];
1316 } else
1317 argvs[j++] = arg[i];
1319 i++;
1321 *argcp = argc;
1322 return argv;
1325 static int
1326 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1327 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1328 int *rflag, int *sflag,
1329 unsigned long *n_arg, char **path1, char **path2)
1331 const char *cmd, *cp = *cpp;
1332 char *cp2, **argv;
1333 int base = 0;
1334 long long ll;
1335 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1337 /* Skip leading whitespace */
1338 cp = cp + strspn(cp, WHITESPACE);
1341 * Check for leading '-' (disable error processing) and '@' (suppress
1342 * command echo)
1344 *ignore_errors = 0;
1345 *disable_echo = 0;
1346 for (;*cp != '\0'; cp++) {
1347 if (*cp == '-') {
1348 *ignore_errors = 1;
1349 } else if (*cp == '@') {
1350 *disable_echo = 1;
1351 } else {
1352 /* all other characters terminate prefix processing */
1353 break;
1356 cp = cp + strspn(cp, WHITESPACE);
1358 /* Ignore blank lines and lines which begin with comment '#' char */
1359 if (*cp == '\0' || *cp == '#')
1360 return (0);
1362 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1363 return -1;
1365 /* Figure out which command we have */
1366 for (i = 0; cmds[i].c != NULL; i++) {
1367 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1368 break;
1370 cmdnum = cmds[i].n;
1371 cmd = cmds[i].c;
1373 /* Special case */
1374 if (*cp == '!') {
1375 cp++;
1376 cmdnum = I_SHELL;
1377 } else if (cmdnum == -1) {
1378 error("Invalid command.");
1379 return -1;
1382 /* Get arguments and parse flags */
1383 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1384 *rflag = *sflag = 0;
1385 *path1 = *path2 = NULL;
1386 optidx = 1;
1387 switch (cmdnum) {
1388 case I_GET:
1389 case I_REGET:
1390 case I_REPUT:
1391 case I_PUT:
1392 if ((optidx = parse_getput_flags(cmd, argv, argc,
1393 aflag, fflag, pflag, rflag)) == -1)
1394 return -1;
1395 /* Get first pathname (mandatory) */
1396 if (argc - optidx < 1) {
1397 error("You must specify at least one path after a "
1398 "%s command.", cmd);
1399 return -1;
1401 *path1 = xstrdup(argv[optidx]);
1402 /* Get second pathname (optional) */
1403 if (argc - optidx > 1) {
1404 *path2 = xstrdup(argv[optidx + 1]);
1405 /* Destination is not globbed */
1406 undo_glob_escape(*path2);
1408 break;
1409 case I_LINK:
1410 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1411 return -1;
1412 goto parse_two_paths;
1413 case I_COPY:
1414 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1415 return -1;
1416 goto parse_two_paths;
1417 case I_RENAME:
1418 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1419 return -1;
1420 goto parse_two_paths;
1421 case I_SYMLINK:
1422 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1423 return -1;
1424 parse_two_paths:
1425 if (argc - optidx < 2) {
1426 error("You must specify two paths after a %s "
1427 "command.", cmd);
1428 return -1;
1430 *path1 = xstrdup(argv[optidx]);
1431 *path2 = xstrdup(argv[optidx + 1]);
1432 /* Paths are not globbed */
1433 undo_glob_escape(*path1);
1434 undo_glob_escape(*path2);
1435 break;
1436 case I_RM:
1437 case I_MKDIR:
1438 case I_RMDIR:
1439 case I_LMKDIR:
1440 path1_mandatory = 1;
1441 /* FALLTHROUGH */
1442 case I_CHDIR:
1443 case I_LCHDIR:
1444 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1445 return -1;
1446 /* Get pathname (mandatory) */
1447 if (argc - optidx < 1) {
1448 if (!path1_mandatory)
1449 break; /* return a NULL path1 */
1450 error("You must specify a path after a %s command.",
1451 cmd);
1452 return -1;
1454 *path1 = xstrdup(argv[optidx]);
1455 /* Only "rm" globs */
1456 if (cmdnum != I_RM)
1457 undo_glob_escape(*path1);
1458 break;
1459 case I_DF:
1460 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1461 iflag)) == -1)
1462 return -1;
1463 /* Default to current directory if no path specified */
1464 if (argc - optidx < 1)
1465 *path1 = NULL;
1466 else {
1467 *path1 = xstrdup(argv[optidx]);
1468 undo_glob_escape(*path1);
1470 break;
1471 case I_LS:
1472 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1473 return(-1);
1474 /* Path is optional */
1475 if (argc - optidx > 0)
1476 *path1 = xstrdup(argv[optidx]);
1477 break;
1478 case I_LLS:
1479 /* Skip ls command and following whitespace */
1480 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1481 case I_SHELL:
1482 /* Uses the rest of the line */
1483 break;
1484 case I_LUMASK:
1485 case I_CHMOD:
1486 base = 8;
1487 /* FALLTHROUGH */
1488 case I_CHOWN:
1489 case I_CHGRP:
1490 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1491 return -1;
1492 /* Get numeric arg (mandatory) */
1493 if (argc - optidx < 1)
1494 goto need_num_arg;
1495 errno = 0;
1496 ll = strtoll(argv[optidx], &cp2, base);
1497 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1498 ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) ||
1499 ll < 0 || ll > UINT32_MAX) {
1500 need_num_arg:
1501 error("You must supply a numeric argument "
1502 "to the %s command.", cmd);
1503 return -1;
1505 *n_arg = ll;
1506 if (cmdnum == I_LUMASK)
1507 break;
1508 /* Get pathname (mandatory) */
1509 if (argc - optidx < 2) {
1510 error("You must specify a path after a %s command.",
1511 cmd);
1512 return -1;
1514 *path1 = xstrdup(argv[optidx + 1]);
1515 break;
1516 case I_QUIT:
1517 case I_PWD:
1518 case I_LPWD:
1519 case I_HELP:
1520 case I_VERSION:
1521 case I_PROGRESS:
1522 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1523 return -1;
1524 break;
1525 default:
1526 fatal("Command not implemented");
1529 *cpp = cp;
1530 return(cmdnum);
1533 static int
1534 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1535 const char *startdir, int err_abort, int echo_command)
1537 const char *ocmd = cmd;
1538 char *path1, *path2, *tmp;
1539 int ignore_errors = 0, disable_echo = 1;
1540 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1541 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1542 int cmdnum, i;
1543 unsigned long n_arg = 0;
1544 Attrib a, *aa;
1545 char path_buf[PATH_MAX];
1546 int err = 0;
1547 glob_t g;
1549 path1 = path2 = NULL;
1550 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1551 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1552 &path1, &path2);
1553 if (ignore_errors != 0)
1554 err_abort = 0;
1556 if (echo_command && !disable_echo)
1557 mprintf("sftp> %s\n", ocmd);
1559 memset(&g, 0, sizeof(g));
1561 /* Perform command */
1562 switch (cmdnum) {
1563 case 0:
1564 /* Blank line */
1565 break;
1566 case -1:
1567 /* Unrecognized command */
1568 err = -1;
1569 break;
1570 case I_REGET:
1571 aflag = 1;
1572 /* FALLTHROUGH */
1573 case I_GET:
1574 err = process_get(conn, path1, path2, *pwd, pflag,
1575 rflag, aflag, fflag);
1576 break;
1577 case I_REPUT:
1578 aflag = 1;
1579 /* FALLTHROUGH */
1580 case I_PUT:
1581 err = process_put(conn, path1, path2, *pwd, pflag,
1582 rflag, aflag, fflag);
1583 break;
1584 case I_COPY:
1585 path1 = make_absolute(path1, *pwd);
1586 path2 = make_absolute(path2, *pwd);
1587 err = do_copy(conn, path1, path2);
1588 break;
1589 case I_RENAME:
1590 path1 = make_absolute(path1, *pwd);
1591 path2 = make_absolute(path2, *pwd);
1592 err = do_rename(conn, path1, path2, lflag);
1593 break;
1594 case I_SYMLINK:
1595 sflag = 1;
1596 /* FALLTHROUGH */
1597 case I_LINK:
1598 if (!sflag)
1599 path1 = make_absolute(path1, *pwd);
1600 path2 = make_absolute(path2, *pwd);
1601 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1602 break;
1603 case I_RM:
1604 path1 = make_absolute_pwd_glob(path1, *pwd);
1605 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1606 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1607 if (!quiet)
1608 mprintf("Removing %s\n", g.gl_pathv[i]);
1609 err = do_rm(conn, g.gl_pathv[i]);
1610 if (err != 0 && err_abort)
1611 break;
1613 break;
1614 case I_MKDIR:
1615 path1 = make_absolute(path1, *pwd);
1616 attrib_clear(&a);
1617 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1618 a.perm = 0777;
1619 err = do_mkdir(conn, path1, &a, 1);
1620 break;
1621 case I_RMDIR:
1622 path1 = make_absolute(path1, *pwd);
1623 err = do_rmdir(conn, path1);
1624 break;
1625 case I_CHDIR:
1626 if (path1 == NULL || *path1 == '\0')
1627 path1 = xstrdup(startdir);
1628 path1 = make_absolute(path1, *pwd);
1629 if ((tmp = do_realpath(conn, path1)) == NULL) {
1630 err = 1;
1631 break;
1633 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1634 free(tmp);
1635 err = 1;
1636 break;
1638 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1639 error("Can't change directory: Can't check target");
1640 free(tmp);
1641 err = 1;
1642 break;
1644 if (!S_ISDIR(aa->perm)) {
1645 error("Can't change directory: \"%s\" is not "
1646 "a directory", tmp);
1647 free(tmp);
1648 err = 1;
1649 break;
1651 free(*pwd);
1652 *pwd = tmp;
1653 break;
1654 case I_LS:
1655 if (!path1) {
1656 do_ls_dir(conn, *pwd, *pwd, lflag);
1657 break;
1660 /* Strip pwd off beginning of non-absolute paths */
1661 tmp = NULL;
1662 if (!path_absolute(path1))
1663 tmp = *pwd;
1665 path1 = make_absolute_pwd_glob(path1, *pwd);
1666 err = do_globbed_ls(conn, path1, tmp, lflag);
1667 break;
1668 case I_DF:
1669 /* Default to current directory if no path specified */
1670 if (path1 == NULL)
1671 path1 = xstrdup(*pwd);
1672 path1 = make_absolute(path1, *pwd);
1673 err = do_df(conn, path1, hflag, iflag);
1674 break;
1675 case I_LCHDIR:
1676 if (path1 == NULL || *path1 == '\0')
1677 path1 = xstrdup("~");
1678 tmp = tilde_expand_filename(path1, getuid());
1679 free(path1);
1680 path1 = tmp;
1681 if (chdir(path1) == -1) {
1682 error("Couldn't change local directory to "
1683 "\"%s\": %s", path1, strerror(errno));
1684 err = 1;
1686 break;
1687 case I_LMKDIR:
1688 if (mkdir(path1, 0777) == -1) {
1689 error("Couldn't create local directory "
1690 "\"%s\": %s", path1, strerror(errno));
1691 err = 1;
1693 break;
1694 case I_LLS:
1695 local_do_ls(cmd);
1696 break;
1697 case I_SHELL:
1698 local_do_shell(cmd);
1699 break;
1700 case I_LUMASK:
1701 umask(n_arg);
1702 printf("Local umask: %03lo\n", n_arg);
1703 break;
1704 case I_CHMOD:
1705 path1 = make_absolute_pwd_glob(path1, *pwd);
1706 attrib_clear(&a);
1707 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1708 a.perm = n_arg;
1709 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1710 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1711 if (!quiet)
1712 mprintf("Changing mode on %s\n",
1713 g.gl_pathv[i]);
1714 err = (hflag ? do_lsetstat : do_setstat)(conn,
1715 g.gl_pathv[i], &a);
1716 if (err != 0 && err_abort)
1717 break;
1719 break;
1720 case I_CHOWN:
1721 case I_CHGRP:
1722 path1 = make_absolute_pwd_glob(path1, *pwd);
1723 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1724 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1725 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1726 g.gl_pathv[i], 0))) {
1727 if (err_abort) {
1728 err = -1;
1729 break;
1730 } else
1731 continue;
1733 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1734 error("Can't get current ownership of "
1735 "remote file \"%s\"", g.gl_pathv[i]);
1736 if (err_abort) {
1737 err = -1;
1738 break;
1739 } else
1740 continue;
1742 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1743 if (cmdnum == I_CHOWN) {
1744 if (!quiet)
1745 mprintf("Changing owner on %s\n",
1746 g.gl_pathv[i]);
1747 aa->uid = n_arg;
1748 } else {
1749 if (!quiet)
1750 mprintf("Changing group on %s\n",
1751 g.gl_pathv[i]);
1752 aa->gid = n_arg;
1754 err = (hflag ? do_lsetstat : do_setstat)(conn,
1755 g.gl_pathv[i], aa);
1756 if (err != 0 && err_abort)
1757 break;
1759 break;
1760 case I_PWD:
1761 mprintf("Remote working directory: %s\n", *pwd);
1762 break;
1763 case I_LPWD:
1764 if (!getcwd(path_buf, sizeof(path_buf))) {
1765 error("Couldn't get local cwd: %s", strerror(errno));
1766 err = -1;
1767 break;
1769 mprintf("Local working directory: %s\n", path_buf);
1770 break;
1771 case I_QUIT:
1772 /* Processed below */
1773 break;
1774 case I_HELP:
1775 help();
1776 break;
1777 case I_VERSION:
1778 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1779 break;
1780 case I_PROGRESS:
1781 showprogress = !showprogress;
1782 if (showprogress)
1783 printf("Progress meter enabled\n");
1784 else
1785 printf("Progress meter disabled\n");
1786 break;
1787 default:
1788 fatal("%d is not implemented", cmdnum);
1791 if (g.gl_pathc)
1792 globfree(&g);
1793 free(path1);
1794 free(path2);
1796 /* If an unignored error occurs in batch mode we should abort. */
1797 if (err_abort && err != 0)
1798 return (-1);
1799 else if (cmdnum == I_QUIT)
1800 return (1);
1802 return (0);
1805 #ifdef USE_LIBEDIT
1806 static char *
1807 prompt(EditLine *el)
1809 return ("sftp> ");
1812 /* Display entries in 'list' after skipping the first 'len' chars */
1813 static void
1814 complete_display(char **list, u_int len)
1816 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1817 struct winsize ws;
1818 char *tmp;
1820 /* Count entries for sort and find longest */
1821 for (y = 0; list[y]; y++)
1822 m = MAXIMUM(m, strlen(list[y]));
1824 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1825 width = ws.ws_col;
1827 m = m > len ? m - len : 0;
1828 columns = width / (m + 2);
1829 columns = MAXIMUM(columns, 1);
1830 colspace = width / columns;
1831 colspace = MINIMUM(colspace, width);
1833 printf("\n");
1834 m = 1;
1835 for (y = 0; list[y]; y++) {
1836 llen = strlen(list[y]);
1837 tmp = llen > len ? list[y] + len : "";
1838 mprintf("%-*s", colspace, tmp);
1839 if (m >= columns) {
1840 printf("\n");
1841 m = 1;
1842 } else
1843 m++;
1845 printf("\n");
1849 * Given a "list" of words that begin with a common prefix of "word",
1850 * attempt to find an autocompletion to extends "word" by the next
1851 * characters common to all entries in "list".
1853 static char *
1854 complete_ambiguous(const char *word, char **list, size_t count)
1856 if (word == NULL)
1857 return NULL;
1859 if (count > 0) {
1860 u_int y, matchlen = strlen(list[0]);
1862 /* Find length of common stem */
1863 for (y = 1; list[y]; y++) {
1864 u_int x;
1866 for (x = 0; x < matchlen; x++)
1867 if (list[0][x] != list[y][x])
1868 break;
1870 matchlen = x;
1873 if (matchlen > strlen(word)) {
1874 char *tmp = xstrdup(list[0]);
1876 tmp[matchlen] = '\0';
1877 return tmp;
1881 return xstrdup(word);
1884 /* Autocomplete a sftp command */
1885 static int
1886 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1887 int terminated)
1889 u_int y, count = 0, cmdlen, tmplen;
1890 char *tmp, **list, argterm[3];
1891 const LineInfo *lf;
1893 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1895 /* No command specified: display all available commands */
1896 if (cmd == NULL) {
1897 for (y = 0; cmds[y].c; y++)
1898 list[count++] = xstrdup(cmds[y].c);
1900 list[count] = NULL;
1901 complete_display(list, 0);
1903 for (y = 0; list[y] != NULL; y++)
1904 free(list[y]);
1905 free(list);
1906 return count;
1909 /* Prepare subset of commands that start with "cmd" */
1910 cmdlen = strlen(cmd);
1911 for (y = 0; cmds[y].c; y++) {
1912 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1913 list[count++] = xstrdup(cmds[y].c);
1915 list[count] = NULL;
1917 if (count == 0) {
1918 free(list);
1919 return 0;
1922 /* Complete ambiguous command */
1923 tmp = complete_ambiguous(cmd, list, count);
1924 if (count > 1)
1925 complete_display(list, 0);
1927 for (y = 0; list[y]; y++)
1928 free(list[y]);
1929 free(list);
1931 if (tmp != NULL) {
1932 tmplen = strlen(tmp);
1933 cmdlen = strlen(cmd);
1934 /* If cmd may be extended then do so */
1935 if (tmplen > cmdlen)
1936 if (el_insertstr(el, tmp + cmdlen) == -1)
1937 fatal("el_insertstr failed.");
1938 lf = el_line(el);
1939 /* Terminate argument cleanly */
1940 if (count == 1) {
1941 y = 0;
1942 if (!terminated)
1943 argterm[y++] = quote;
1944 if (lastarg || *(lf->cursor) != ' ')
1945 argterm[y++] = ' ';
1946 argterm[y] = '\0';
1947 if (y > 0 && el_insertstr(el, argterm) == -1)
1948 fatal("el_insertstr failed.");
1950 free(tmp);
1953 return count;
1957 * Determine whether a particular sftp command's arguments (if any) represent
1958 * local or remote files. The "cmdarg" argument specifies the actual argument
1959 * and accepts values 1 or 2.
1961 static int
1962 complete_is_remote(char *cmd, int cmdarg) {
1963 int i;
1965 if (cmd == NULL)
1966 return -1;
1968 for (i = 0; cmds[i].c; i++) {
1969 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
1970 if (cmdarg == 1)
1971 return cmds[i].t;
1972 else if (cmdarg == 2)
1973 return cmds[i].t2;
1974 break;
1978 return -1;
1981 /* Autocomplete a filename "file" */
1982 static int
1983 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1984 char *file, int remote, int lastarg, char quote, int terminated)
1986 glob_t g;
1987 char *tmp, *tmp2, ins[8];
1988 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1989 int clen;
1990 const LineInfo *lf;
1992 /* Glob from "file" location */
1993 if (file == NULL)
1994 tmp = xstrdup("*");
1995 else
1996 xasprintf(&tmp, "%s*", file);
1998 /* Check if the path is absolute. */
1999 isabs = path_absolute(tmp);
2001 memset(&g, 0, sizeof(g));
2002 if (remote != LOCAL) {
2003 tmp = make_absolute_pwd_glob(tmp, remote_path);
2004 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2005 } else
2006 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2008 /* Determine length of pwd so we can trim completion display */
2009 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
2010 /* Terminate counting on first unescaped glob metacharacter */
2011 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
2012 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
2013 hadglob = 1;
2014 break;
2016 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2017 tmplen++;
2018 if (tmp[tmplen] == '/')
2019 pwdlen = tmplen + 1; /* track last seen '/' */
2021 free(tmp);
2022 tmp = NULL;
2024 if (g.gl_matchc == 0)
2025 goto out;
2027 if (g.gl_matchc > 1)
2028 complete_display(g.gl_pathv, pwdlen);
2030 /* Don't try to extend globs */
2031 if (file == NULL || hadglob)
2032 goto out;
2034 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2035 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
2036 free(tmp2);
2038 if (tmp == NULL)
2039 goto out;
2041 tmplen = strlen(tmp);
2042 filelen = strlen(file);
2044 /* Count the number of escaped characters in the input string. */
2045 cesc = isesc = 0;
2046 for (i = 0; i < filelen; i++) {
2047 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2048 isesc = 1;
2049 cesc++;
2050 } else
2051 isesc = 0;
2054 if (tmplen > (filelen - cesc)) {
2055 tmp2 = tmp + filelen - cesc;
2056 len = strlen(tmp2);
2057 /* quote argument on way out */
2058 for (i = 0; i < len; i += clen) {
2059 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2060 (size_t)clen > sizeof(ins) - 2)
2061 fatal("invalid multibyte character");
2062 ins[0] = '\\';
2063 memcpy(ins + 1, tmp2 + i, clen);
2064 ins[clen + 1] = '\0';
2065 switch (tmp2[i]) {
2066 case '\'':
2067 case '"':
2068 case '\\':
2069 case '\t':
2070 case '[':
2071 case ' ':
2072 case '#':
2073 case '*':
2074 if (quote == '\0' || tmp2[i] == quote) {
2075 if (el_insertstr(el, ins) == -1)
2076 fatal("el_insertstr "
2077 "failed.");
2078 break;
2080 /* FALLTHROUGH */
2081 default:
2082 if (el_insertstr(el, ins + 1) == -1)
2083 fatal("el_insertstr failed.");
2084 break;
2089 lf = el_line(el);
2090 if (g.gl_matchc == 1) {
2091 i = 0;
2092 if (!terminated && quote != '\0')
2093 ins[i++] = quote;
2094 if (*(lf->cursor - 1) != '/' &&
2095 (lastarg || *(lf->cursor) != ' '))
2096 ins[i++] = ' ';
2097 ins[i] = '\0';
2098 if (i > 0 && el_insertstr(el, ins) == -1)
2099 fatal("el_insertstr failed.");
2101 free(tmp);
2103 out:
2104 globfree(&g);
2105 return g.gl_matchc;
2108 /* tab-completion hook function, called via libedit */
2109 static unsigned char
2110 complete(EditLine *el, int ch)
2112 char **argv, *line, quote;
2113 int argc, carg;
2114 u_int cursor, len, terminated, ret = CC_ERROR;
2115 const LineInfo *lf;
2116 struct complete_ctx *complete_ctx;
2118 lf = el_line(el);
2119 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2120 fatal_f("el_get failed");
2122 /* Figure out which argument the cursor points to */
2123 cursor = lf->cursor - lf->buffer;
2124 line = xmalloc(cursor + 1);
2125 memcpy(line, lf->buffer, cursor);
2126 line[cursor] = '\0';
2127 argv = makeargv(line, &carg, 1, &quote, &terminated);
2128 free(line);
2130 /* Get all the arguments on the line */
2131 len = lf->lastchar - lf->buffer;
2132 line = xmalloc(len + 1);
2133 memcpy(line, lf->buffer, len);
2134 line[len] = '\0';
2135 argv = makeargv(line, &argc, 1, NULL, NULL);
2137 /* Ensure cursor is at EOL or a argument boundary */
2138 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2139 line[cursor] != '\n') {
2140 free(line);
2141 return ret;
2144 if (carg == 0) {
2145 /* Show all available commands */
2146 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2147 ret = CC_REDISPLAY;
2148 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2149 /* Handle the command parsing */
2150 if (complete_cmd_parse(el, argv[0], argc == carg,
2151 quote, terminated) != 0)
2152 ret = CC_REDISPLAY;
2153 } else if (carg >= 1) {
2154 /* Handle file parsing */
2155 int remote = 0;
2156 int i = 0, cmdarg = 0;
2157 char *filematch = NULL;
2159 if (carg > 1 && line[cursor-1] != ' ')
2160 filematch = argv[carg - 1];
2162 for (i = 1; i < carg; i++) {
2163 /* Skip flags */
2164 if (argv[i][0] != '-')
2165 cmdarg++;
2169 * If previous argument is complete, then offer completion
2170 * on the next one.
2172 if (line[cursor - 1] == ' ')
2173 cmdarg++;
2175 remote = complete_is_remote(argv[0], cmdarg);
2177 if ((remote == REMOTE || remote == LOCAL) &&
2178 complete_match(el, complete_ctx->conn,
2179 *complete_ctx->remote_pathp, filematch,
2180 remote, carg == argc, quote, terminated) != 0)
2181 ret = CC_REDISPLAY;
2184 free(line);
2185 return ret;
2187 #endif /* USE_LIBEDIT */
2189 static int
2190 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2192 char *remote_path;
2193 char *dir = NULL, *startdir = NULL;
2194 char cmd[2048];
2195 int err, interactive;
2196 EditLine *el = NULL;
2197 #ifdef USE_LIBEDIT
2198 History *hl = NULL;
2199 HistEvent hev;
2200 extern char *__progname;
2201 struct complete_ctx complete_ctx;
2203 if (!batchmode && isatty(STDIN_FILENO)) {
2204 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2205 fatal("Couldn't initialise editline");
2206 if ((hl = history_init()) == NULL)
2207 fatal("Couldn't initialise editline history");
2208 history(hl, &hev, H_SETSIZE, 100);
2209 el_set(el, EL_HIST, history, hl);
2211 el_set(el, EL_PROMPT, prompt);
2212 el_set(el, EL_EDITOR, "emacs");
2213 el_set(el, EL_TERMINAL, NULL);
2214 el_set(el, EL_SIGNAL, 1);
2215 el_source(el, NULL);
2217 /* Tab Completion */
2218 el_set(el, EL_ADDFN, "ftp-complete",
2219 "Context sensitive argument completion", complete);
2220 complete_ctx.conn = conn;
2221 complete_ctx.remote_pathp = &remote_path;
2222 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2223 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2224 /* enable ctrl-left-arrow and ctrl-right-arrow */
2225 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2226 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
2227 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2228 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2229 /* make ^w match ksh behaviour */
2230 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2232 #endif /* USE_LIBEDIT */
2234 remote_path = do_realpath(conn, ".");
2235 if (remote_path == NULL)
2236 fatal("Need cwd");
2237 startdir = xstrdup(remote_path);
2239 if (file1 != NULL) {
2240 dir = xstrdup(file1);
2241 dir = make_absolute(dir, remote_path);
2243 if (remote_is_dir(conn, dir) && file2 == NULL) {
2244 if (!quiet)
2245 mprintf("Changing to: %s\n", dir);
2246 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2247 if (parse_dispatch_command(conn, cmd,
2248 &remote_path, startdir, 1, 0) != 0) {
2249 free(dir);
2250 free(startdir);
2251 free(remote_path);
2252 free(conn);
2253 return (-1);
2255 } else {
2256 /* XXX this is wrong wrt quoting */
2257 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2258 global_aflag ? " -a" : "", dir,
2259 file2 == NULL ? "" : " ",
2260 file2 == NULL ? "" : file2);
2261 err = parse_dispatch_command(conn, cmd,
2262 &remote_path, startdir, 1, 0);
2263 free(dir);
2264 free(startdir);
2265 free(remote_path);
2266 free(conn);
2267 return (err);
2269 free(dir);
2272 setvbuf(stdout, NULL, _IOLBF, 0);
2273 setvbuf(infile, NULL, _IOLBF, 0);
2275 interactive = !batchmode && isatty(STDIN_FILENO);
2276 err = 0;
2277 for (;;) {
2278 struct sigaction sa;
2280 interrupted = 0;
2281 memset(&sa, 0, sizeof(sa));
2282 sa.sa_handler = interactive ? read_interrupt : killchild;
2283 if (sigaction(SIGINT, &sa, NULL) == -1) {
2284 debug3("sigaction(%s): %s", strsignal(SIGINT),
2285 strerror(errno));
2286 break;
2288 if (el == NULL) {
2289 if (interactive)
2290 printf("sftp> ");
2291 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2292 if (interactive)
2293 printf("\n");
2294 if (interrupted)
2295 continue;
2296 break;
2298 } else {
2299 #ifdef USE_LIBEDIT
2300 const char *line;
2301 int count = 0;
2303 if ((line = el_gets(el, &count)) == NULL ||
2304 count <= 0) {
2305 printf("\n");
2306 if (interrupted)
2307 continue;
2308 break;
2310 history(hl, &hev, H_ENTER, line);
2311 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2312 fprintf(stderr, "Error: input line too long\n");
2313 continue;
2315 #endif /* USE_LIBEDIT */
2318 cmd[strcspn(cmd, "\n")] = '\0';
2320 /* Handle user interrupts gracefully during commands */
2321 interrupted = 0;
2322 ssh_signal(SIGINT, cmd_interrupt);
2324 err = parse_dispatch_command(conn, cmd, &remote_path,
2325 startdir, batchmode, !interactive && el == NULL);
2326 if (err != 0)
2327 break;
2329 ssh_signal(SIGCHLD, SIG_DFL);
2330 free(remote_path);
2331 free(startdir);
2332 free(conn);
2334 #ifdef USE_LIBEDIT
2335 if (el != NULL)
2336 el_end(el);
2337 #endif /* USE_LIBEDIT */
2339 /* err == 1 signifies normal "quit" exit */
2340 return (err >= 0 ? 0 : -1);
2343 static void
2344 connect_to_server(char *path, char **args, int *in, int *out)
2346 int c_in, c_out;
2347 #ifdef USE_PIPES
2348 int pin[2], pout[2];
2350 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2351 fatal("pipe: %s", strerror(errno));
2352 *in = pin[0];
2353 *out = pout[1];
2354 c_in = pout[0];
2355 c_out = pin[1];
2356 #else /* USE_PIPES */
2357 int inout[2];
2359 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2360 fatal("socketpair: %s", strerror(errno));
2361 *in = *out = inout[0];
2362 c_in = c_out = inout[1];
2363 #endif /* USE_PIPES */
2365 if ((sshpid = fork()) == -1)
2366 fatal("fork: %s", strerror(errno));
2367 else if (sshpid == 0) {
2368 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2369 (dup2(c_out, STDOUT_FILENO) == -1)) {
2370 fprintf(stderr, "dup2: %s\n", strerror(errno));
2371 _exit(1);
2373 close(*in);
2374 close(*out);
2375 close(c_in);
2376 close(c_out);
2379 * The underlying ssh is in the same process group, so we must
2380 * ignore SIGINT if we want to gracefully abort commands,
2381 * otherwise the signal will make it to the ssh process and
2382 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2383 * underlying ssh, it must *not* ignore that signal.
2385 ssh_signal(SIGINT, SIG_IGN);
2386 ssh_signal(SIGTERM, SIG_DFL);
2387 execvp(path, args);
2388 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2389 _exit(1);
2392 ssh_signal(SIGTERM, killchild);
2393 ssh_signal(SIGINT, killchild);
2394 ssh_signal(SIGHUP, killchild);
2395 ssh_signal(SIGTSTP, suspchild);
2396 ssh_signal(SIGTTIN, suspchild);
2397 ssh_signal(SIGTTOU, suspchild);
2398 ssh_signal(SIGCHLD, sigchld_handler);
2399 close(c_in);
2400 close(c_out);
2403 static void
2404 usage(void)
2406 extern char *__progname;
2408 fprintf(stderr,
2409 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2410 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2411 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2412 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2413 " destination\n",
2414 __progname);
2415 exit(1);
2419 main(int argc, char **argv)
2421 int r, in, out, ch, err, tmp, port = -1, noisy = 0;
2422 char *host = NULL, *user, *cp, **cpp, *file2 = NULL;
2423 int debug_level = 0;
2424 char *file1 = NULL, *sftp_server = NULL;
2425 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2426 const char *errstr;
2427 LogLevel ll = SYSLOG_LEVEL_INFO;
2428 arglist args;
2429 extern int optind;
2430 extern char *optarg;
2431 struct sftp_conn *conn;
2432 size_t copy_buffer_len = 0;
2433 size_t num_requests = 0;
2434 long long limit_kbps = 0;
2436 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2437 sanitise_stdfd();
2438 msetlocale();
2440 __progname = ssh_get_progname(argv[0]);
2441 memset(&args, '\0', sizeof(args));
2442 args.list = NULL;
2443 addargs(&args, "%s", ssh_program);
2444 addargs(&args, "-oForwardX11 no");
2445 addargs(&args, "-oPermitLocalCommand no");
2446 addargs(&args, "-oClearAllForwardings yes");
2448 ll = SYSLOG_LEVEL_INFO;
2449 infile = stdin;
2451 while ((ch = getopt(argc, argv,
2452 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) {
2453 switch (ch) {
2454 /* Passed through to ssh(1) */
2455 case 'A':
2456 case '4':
2457 case '6':
2458 case 'C':
2459 addargs(&args, "-%c", ch);
2460 break;
2461 /* Passed through to ssh(1) with argument */
2462 case 'F':
2463 case 'J':
2464 case 'c':
2465 case 'i':
2466 case 'o':
2467 addargs(&args, "-%c", ch);
2468 addargs(&args, "%s", optarg);
2469 break;
2470 case 'q':
2471 ll = SYSLOG_LEVEL_ERROR;
2472 quiet = 1;
2473 showprogress = 0;
2474 addargs(&args, "-%c", ch);
2475 break;
2476 case 'P':
2477 port = a2port(optarg);
2478 if (port <= 0)
2479 fatal("Bad port \"%s\"\n", optarg);
2480 break;
2481 case 'v':
2482 if (debug_level < 3) {
2483 addargs(&args, "-v");
2484 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2486 debug_level++;
2487 break;
2488 case '1':
2489 fatal("SSH protocol v.1 is no longer supported");
2490 break;
2491 case '2':
2492 /* accept silently */
2493 break;
2494 case 'a':
2495 global_aflag = 1;
2496 break;
2497 case 'B':
2498 copy_buffer_len = strtol(optarg, &cp, 10);
2499 if (copy_buffer_len == 0 || *cp != '\0')
2500 fatal("Invalid buffer size \"%s\"", optarg);
2501 break;
2502 case 'b':
2503 if (batchmode)
2504 fatal("Batch file already specified.");
2506 /* Allow "-" as stdin */
2507 if (strcmp(optarg, "-") != 0 &&
2508 (infile = fopen(optarg, "r")) == NULL)
2509 fatal("%s (%s).", strerror(errno), optarg);
2510 showprogress = 0;
2511 quiet = batchmode = 1;
2512 addargs(&args, "-obatchmode yes");
2513 break;
2514 case 'f':
2515 global_fflag = 1;
2516 break;
2517 case 'N':
2518 noisy = 1; /* Used to clear quiet mode after getopt */
2519 break;
2520 case 'p':
2521 global_pflag = 1;
2522 break;
2523 case 'D':
2524 sftp_direct = optarg;
2525 break;
2526 case 'l':
2527 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2528 &errstr);
2529 if (errstr != NULL)
2530 usage();
2531 limit_kbps *= 1024; /* kbps */
2532 break;
2533 case 'r':
2534 global_rflag = 1;
2535 break;
2536 case 'R':
2537 num_requests = strtol(optarg, &cp, 10);
2538 if (num_requests == 0 || *cp != '\0')
2539 fatal("Invalid number of requests \"%s\"",
2540 optarg);
2541 break;
2542 case 's':
2543 sftp_server = optarg;
2544 break;
2545 case 'S':
2546 ssh_program = optarg;
2547 replacearg(&args, 0, "%s", ssh_program);
2548 break;
2549 case 'h':
2550 default:
2551 usage();
2555 /* Do this last because we want the user to be able to override it */
2556 addargs(&args, "-oForwardAgent no");
2558 if (!isatty(STDERR_FILENO))
2559 showprogress = 0;
2561 if (noisy)
2562 quiet = 0;
2564 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2566 if (sftp_direct == NULL) {
2567 if (optind == argc || argc > (optind + 2))
2568 usage();
2569 argv += optind;
2571 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2572 case -1:
2573 usage();
2574 break;
2575 case 0:
2576 if (tmp != -1)
2577 port = tmp;
2578 break;
2579 default:
2580 /* Try with user, host and path. */
2581 if (parse_user_host_path(*argv, &user, &host,
2582 &file1) == 0)
2583 break;
2584 /* Try with user and host. */
2585 if (parse_user_host_port(*argv, &user, &host, NULL)
2586 == 0)
2587 break;
2588 /* Treat as a plain hostname. */
2589 host = xstrdup(*argv);
2590 host = cleanhostname(host);
2591 break;
2593 file2 = *(argv + 1);
2595 if (!*host) {
2596 fprintf(stderr, "Missing hostname\n");
2597 usage();
2600 if (port != -1)
2601 addargs(&args, "-oPort %d", port);
2602 if (user != NULL) {
2603 addargs(&args, "-l");
2604 addargs(&args, "%s", user);
2607 /* no subsystem if the server-spec contains a '/' */
2608 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2609 addargs(&args, "-s");
2611 addargs(&args, "--");
2612 addargs(&args, "%s", host);
2613 addargs(&args, "%s", (sftp_server != NULL ?
2614 sftp_server : "sftp"));
2616 connect_to_server(ssh_program, args.list, &in, &out);
2617 } else {
2618 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
2619 fatal_r(r, "Parse -D arguments");
2620 if (cpp[0] == 0)
2621 fatal("No sftp server specified via -D");
2622 connect_to_server(cpp[0], cpp, &in, &out);
2623 argv_free(cpp, tmp);
2625 freeargs(&args);
2627 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2628 if (conn == NULL)
2629 fatal("Couldn't initialise connection to server");
2631 if (!quiet) {
2632 if (sftp_direct == NULL)
2633 fprintf(stderr, "Connected to %s.\n", host);
2634 else
2635 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2638 err = interactive_loop(conn, file1, file2);
2640 #if !defined(USE_PIPES)
2641 shutdown(in, SHUT_RDWR);
2642 shutdown(out, SHUT_RDWR);
2643 #endif
2645 close(in);
2646 close(out);
2647 if (batchmode)
2648 fclose(infile);
2650 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2651 if (errno != EINTR)
2652 fatal("Couldn't wait for ssh process: %s",
2653 strerror(errno));
2655 exit(err == 0 ? 0 : 1);