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