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