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