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