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