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