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