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