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