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