- (dtucker) platform.c session.c] Move the USE_LIBIAF fragment into
[openssh-git.git] / sftp.c
blobd605505eab59b97ae15cfe2eccb2f41aa68a06a6
1 /* $OpenBSD: sftp.c,v 1.131 2010/10/23 22:06:12 sthen Exp $ */
2 /*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
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.
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.
18 #include "includes.h"
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
32 #include <ctype.h>
33 #include <errno.h>
35 #ifdef HAVE_PATHS_H
36 # include <paths.h>
37 #endif
38 #ifdef HAVE_LIBGEN_H
39 #include <libgen.h>
40 #endif
41 #ifdef USE_LIBEDIT
42 #include <histedit.h>
43 #else
44 typedef void EditLine;
45 #endif
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <stdarg.h>
53 #ifdef HAVE_UTIL_H
54 # include <util.h>
55 #endif
57 #ifdef HAVE_LIBUTIL_H
58 # include <libutil.h>
59 #endif
61 #include "xmalloc.h"
62 #include "log.h"
63 #include "pathnames.h"
64 #include "misc.h"
66 #include "sftp.h"
67 #include "buffer.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
71 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
72 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
74 /* File to read commands from */
75 FILE* infile;
77 /* Are we in batchfile mode? */
78 int batchmode = 0;
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
83 /* This is set to 0 if the progressmeter is not desired. */
84 int showprogress = 1;
86 /* When this option is set, we always recursively download/upload directories */
87 int global_rflag = 0;
89 /* When this option is set, the file transfers will always preserve times */
90 int global_pflag = 0;
92 /* SIGINT received during command processing */
93 volatile sig_atomic_t interrupted = 0;
95 /* I wish qsort() took a separate ctx for the comparison function...*/
96 int sort_flag;
98 /* Context used for commandline completion */
99 struct complete_ctx {
100 struct sftp_conn *conn;
101 char **remote_pathp;
104 int remote_glob(struct sftp_conn *, const char *, int,
105 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
107 extern char *__progname;
109 /* Separators for interactive commands */
110 #define WHITESPACE " \t\r\n"
112 /* ls flags */
113 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
114 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
115 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
116 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
117 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
118 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
119 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
120 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
121 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
123 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
124 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
126 /* Commands for interactive mode */
127 #define I_CHDIR 1
128 #define I_CHGRP 2
129 #define I_CHMOD 3
130 #define I_CHOWN 4
131 #define I_DF 24
132 #define I_GET 5
133 #define I_HELP 6
134 #define I_LCHDIR 7
135 #define I_LLS 8
136 #define I_LMKDIR 9
137 #define I_LPWD 10
138 #define I_LS 11
139 #define I_LUMASK 12
140 #define I_MKDIR 13
141 #define I_PUT 14
142 #define I_PWD 15
143 #define I_QUIT 16
144 #define I_RENAME 17
145 #define I_RM 18
146 #define I_RMDIR 19
147 #define I_SHELL 20
148 #define I_SYMLINK 21
149 #define I_VERSION 22
150 #define I_PROGRESS 23
152 struct CMD {
153 const char *c;
154 const int n;
155 const int t;
158 /* Type of completion */
159 #define NOARGS 0
160 #define REMOTE 1
161 #define LOCAL 2
163 static const struct CMD cmds[] = {
164 { "bye", I_QUIT, NOARGS },
165 { "cd", I_CHDIR, REMOTE },
166 { "chdir", I_CHDIR, REMOTE },
167 { "chgrp", I_CHGRP, REMOTE },
168 { "chmod", I_CHMOD, REMOTE },
169 { "chown", I_CHOWN, REMOTE },
170 { "df", I_DF, REMOTE },
171 { "dir", I_LS, REMOTE },
172 { "exit", I_QUIT, NOARGS },
173 { "get", I_GET, REMOTE },
174 { "help", I_HELP, NOARGS },
175 { "lcd", I_LCHDIR, LOCAL },
176 { "lchdir", I_LCHDIR, LOCAL },
177 { "lls", I_LLS, LOCAL },
178 { "lmkdir", I_LMKDIR, LOCAL },
179 { "ln", I_SYMLINK, REMOTE },
180 { "lpwd", I_LPWD, LOCAL },
181 { "ls", I_LS, REMOTE },
182 { "lumask", I_LUMASK, NOARGS },
183 { "mkdir", I_MKDIR, REMOTE },
184 { "mget", I_GET, REMOTE },
185 { "mput", I_PUT, LOCAL },
186 { "progress", I_PROGRESS, NOARGS },
187 { "put", I_PUT, LOCAL },
188 { "pwd", I_PWD, REMOTE },
189 { "quit", I_QUIT, NOARGS },
190 { "rename", I_RENAME, REMOTE },
191 { "rm", I_RM, REMOTE },
192 { "rmdir", I_RMDIR, REMOTE },
193 { "symlink", I_SYMLINK, REMOTE },
194 { "version", I_VERSION, NOARGS },
195 { "!", I_SHELL, NOARGS },
196 { "?", I_HELP, NOARGS },
197 { NULL, -1, -1 }
200 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
202 /* ARGSUSED */
203 static void
204 killchild(int signo)
206 if (sshpid > 1) {
207 kill(sshpid, SIGTERM);
208 waitpid(sshpid, NULL, 0);
211 _exit(1);
214 /* ARGSUSED */
215 static void
216 cmd_interrupt(int signo)
218 const char msg[] = "\rInterrupt \n";
219 int olderrno = errno;
221 write(STDERR_FILENO, msg, sizeof(msg) - 1);
222 interrupted = 1;
223 errno = olderrno;
226 static void
227 help(void)
229 printf("Available commands:\n"
230 "bye Quit sftp\n"
231 "cd path Change remote directory to 'path'\n"
232 "chgrp grp path Change group of file 'path' to 'grp'\n"
233 "chmod mode path Change permissions of file 'path' to 'mode'\n"
234 "chown own path Change owner of file 'path' to 'own'\n"
235 "df [-hi] [path] Display statistics for current directory or\n"
236 " filesystem containing 'path'\n"
237 "exit Quit sftp\n"
238 "get [-Ppr] remote [local] Download file\n"
239 "help Display this help text\n"
240 "lcd path Change local directory to 'path'\n"
241 "lls [ls-options [path]] Display local directory listing\n"
242 "lmkdir path Create local directory\n"
243 "ln oldpath newpath Symlink remote file\n"
244 "lpwd Print local working directory\n"
245 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
246 "lumask umask Set local umask to 'umask'\n"
247 "mkdir path Create remote directory\n"
248 "progress Toggle display of progress meter\n"
249 "put [-Ppr] local [remote] Upload file\n"
250 "pwd Display remote working directory\n"
251 "quit Quit sftp\n"
252 "rename oldpath newpath Rename remote file\n"
253 "rm path Delete remote file\n"
254 "rmdir path Remove remote directory\n"
255 "symlink oldpath newpath Symlink remote file\n"
256 "version Show SFTP version\n"
257 "!command Execute 'command' in local shell\n"
258 "! Escape to local shell\n"
259 "? Synonym for help\n");
262 static void
263 local_do_shell(const char *args)
265 int status;
266 char *shell;
267 pid_t pid;
269 if (!*args)
270 args = NULL;
272 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
273 shell = _PATH_BSHELL;
275 if ((pid = fork()) == -1)
276 fatal("Couldn't fork: %s", strerror(errno));
278 if (pid == 0) {
279 /* XXX: child has pipe fds to ssh subproc open - issue? */
280 if (args) {
281 debug3("Executing %s -c \"%s\"", shell, args);
282 execl(shell, shell, "-c", args, (char *)NULL);
283 } else {
284 debug3("Executing %s", shell);
285 execl(shell, shell, (char *)NULL);
287 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
288 strerror(errno));
289 _exit(1);
291 while (waitpid(pid, &status, 0) == -1)
292 if (errno != EINTR)
293 fatal("Couldn't wait for child: %s", strerror(errno));
294 if (!WIFEXITED(status))
295 error("Shell exited abnormally");
296 else if (WEXITSTATUS(status))
297 error("Shell exited with status %d", WEXITSTATUS(status));
300 static void
301 local_do_ls(const char *args)
303 if (!args || !*args)
304 local_do_shell(_PATH_LS);
305 else {
306 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
307 char *buf = xmalloc(len);
309 /* XXX: quoting - rip quoting code from ftp? */
310 snprintf(buf, len, _PATH_LS " %s", args);
311 local_do_shell(buf);
312 xfree(buf);
316 /* Strip one path (usually the pwd) from the start of another */
317 static char *
318 path_strip(char *path, char *strip)
320 size_t len;
322 if (strip == NULL)
323 return (xstrdup(path));
325 len = strlen(strip);
326 if (strncmp(path, strip, len) == 0) {
327 if (strip[len - 1] != '/' && path[len] == '/')
328 len++;
329 return (xstrdup(path + len));
332 return (xstrdup(path));
335 static char *
336 make_absolute(char *p, char *pwd)
338 char *abs_str;
340 /* Derelativise */
341 if (p && p[0] != '/') {
342 abs_str = path_append(pwd, p);
343 xfree(p);
344 return(abs_str);
345 } else
346 return(p);
349 static int
350 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
351 int *rflag)
353 extern int opterr, optind, optopt, optreset;
354 int ch;
356 optind = optreset = 1;
357 opterr = 0;
359 *rflag = *pflag = 0;
360 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
361 switch (ch) {
362 case 'p':
363 case 'P':
364 *pflag = 1;
365 break;
366 case 'r':
367 case 'R':
368 *rflag = 1;
369 break;
370 default:
371 error("%s: Invalid flag -%c", cmd, optopt);
372 return -1;
376 return optind;
379 static int
380 parse_ls_flags(char **argv, int argc, int *lflag)
382 extern int opterr, optind, optopt, optreset;
383 int ch;
385 optind = optreset = 1;
386 opterr = 0;
388 *lflag = LS_NAME_SORT;
389 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
390 switch (ch) {
391 case '1':
392 *lflag &= ~VIEW_FLAGS;
393 *lflag |= LS_SHORT_VIEW;
394 break;
395 case 'S':
396 *lflag &= ~SORT_FLAGS;
397 *lflag |= LS_SIZE_SORT;
398 break;
399 case 'a':
400 *lflag |= LS_SHOW_ALL;
401 break;
402 case 'f':
403 *lflag &= ~SORT_FLAGS;
404 break;
405 case 'h':
406 *lflag |= LS_SI_UNITS;
407 break;
408 case 'l':
409 *lflag &= ~LS_SHORT_VIEW;
410 *lflag |= LS_LONG_VIEW;
411 break;
412 case 'n':
413 *lflag &= ~LS_SHORT_VIEW;
414 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
415 break;
416 case 'r':
417 *lflag |= LS_REVERSE_SORT;
418 break;
419 case 't':
420 *lflag &= ~SORT_FLAGS;
421 *lflag |= LS_TIME_SORT;
422 break;
423 default:
424 error("ls: Invalid flag -%c", optopt);
425 return -1;
429 return optind;
432 static int
433 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
435 extern int opterr, optind, optopt, optreset;
436 int ch;
438 optind = optreset = 1;
439 opterr = 0;
441 *hflag = *iflag = 0;
442 while ((ch = getopt(argc, argv, "hi")) != -1) {
443 switch (ch) {
444 case 'h':
445 *hflag = 1;
446 break;
447 case 'i':
448 *iflag = 1;
449 break;
450 default:
451 error("%s: Invalid flag -%c", cmd, optopt);
452 return -1;
456 return optind;
459 static int
460 is_dir(char *path)
462 struct stat sb;
464 /* XXX: report errors? */
465 if (stat(path, &sb) == -1)
466 return(0);
468 return(S_ISDIR(sb.st_mode));
471 static int
472 remote_is_dir(struct sftp_conn *conn, char *path)
474 Attrib *a;
476 /* XXX: report errors? */
477 if ((a = do_stat(conn, path, 1)) == NULL)
478 return(0);
479 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
480 return(0);
481 return(S_ISDIR(a->perm));
484 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
485 static int
486 pathname_is_dir(char *pathname)
488 size_t l = strlen(pathname);
490 return l > 0 && pathname[l - 1] == '/';
493 static int
494 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
495 int pflag, int rflag)
497 char *abs_src = NULL;
498 char *abs_dst = NULL;
499 glob_t g;
500 char *filename, *tmp=NULL;
501 int i, err = 0;
503 abs_src = xstrdup(src);
504 abs_src = make_absolute(abs_src, pwd);
505 memset(&g, 0, sizeof(g));
507 debug3("Looking up %s", abs_src);
508 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
509 error("File \"%s\" not found.", abs_src);
510 err = -1;
511 goto out;
515 * If multiple matches then dst must be a directory or
516 * unspecified.
518 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
519 error("Multiple source paths, but destination "
520 "\"%s\" is not a directory", dst);
521 err = -1;
522 goto out;
525 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
526 tmp = xstrdup(g.gl_pathv[i]);
527 if ((filename = basename(tmp)) == NULL) {
528 error("basename %s: %s", tmp, strerror(errno));
529 xfree(tmp);
530 err = -1;
531 goto out;
534 if (g.gl_matchc == 1 && dst) {
535 if (is_dir(dst)) {
536 abs_dst = path_append(dst, filename);
537 } else {
538 abs_dst = xstrdup(dst);
540 } else if (dst) {
541 abs_dst = path_append(dst, filename);
542 } else {
543 abs_dst = xstrdup(filename);
545 xfree(tmp);
547 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
548 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
549 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
550 pflag || global_pflag, 1) == -1)
551 err = -1;
552 } else {
553 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
554 pflag || global_pflag) == -1)
555 err = -1;
557 xfree(abs_dst);
558 abs_dst = NULL;
561 out:
562 xfree(abs_src);
563 globfree(&g);
564 return(err);
567 static int
568 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
569 int pflag, int rflag)
571 char *tmp_dst = NULL;
572 char *abs_dst = NULL;
573 char *tmp = NULL, *filename = NULL;
574 glob_t g;
575 int err = 0;
576 int i, dst_is_dir = 1;
577 struct stat sb;
579 if (dst) {
580 tmp_dst = xstrdup(dst);
581 tmp_dst = make_absolute(tmp_dst, pwd);
584 memset(&g, 0, sizeof(g));
585 debug3("Looking up %s", src);
586 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
587 error("File \"%s\" not found.", src);
588 err = -1;
589 goto out;
592 /* If we aren't fetching to pwd then stash this status for later */
593 if (tmp_dst != NULL)
594 dst_is_dir = remote_is_dir(conn, tmp_dst);
596 /* If multiple matches, dst may be directory or unspecified */
597 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
598 error("Multiple paths match, but destination "
599 "\"%s\" is not a directory", tmp_dst);
600 err = -1;
601 goto out;
604 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
605 if (stat(g.gl_pathv[i], &sb) == -1) {
606 err = -1;
607 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
608 continue;
611 tmp = xstrdup(g.gl_pathv[i]);
612 if ((filename = basename(tmp)) == NULL) {
613 error("basename %s: %s", tmp, strerror(errno));
614 xfree(tmp);
615 err = -1;
616 goto out;
619 if (g.gl_matchc == 1 && tmp_dst) {
620 /* If directory specified, append filename */
621 if (dst_is_dir)
622 abs_dst = path_append(tmp_dst, filename);
623 else
624 abs_dst = xstrdup(tmp_dst);
625 } else if (tmp_dst) {
626 abs_dst = path_append(tmp_dst, filename);
627 } else {
628 abs_dst = make_absolute(xstrdup(filename), pwd);
630 xfree(tmp);
632 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
633 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
634 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
635 pflag || global_pflag, 1) == -1)
636 err = -1;
637 } else {
638 if (do_upload(conn, g.gl_pathv[i], abs_dst,
639 pflag || global_pflag) == -1)
640 err = -1;
644 out:
645 if (abs_dst)
646 xfree(abs_dst);
647 if (tmp_dst)
648 xfree(tmp_dst);
649 globfree(&g);
650 return(err);
653 static int
654 sdirent_comp(const void *aa, const void *bb)
656 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
657 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
658 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
660 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
661 if (sort_flag & LS_NAME_SORT)
662 return (rmul * strcmp(a->filename, b->filename));
663 else if (sort_flag & LS_TIME_SORT)
664 return (rmul * NCMP(a->a.mtime, b->a.mtime));
665 else if (sort_flag & LS_SIZE_SORT)
666 return (rmul * NCMP(a->a.size, b->a.size));
668 fatal("Unknown ls sort type");
671 /* sftp ls.1 replacement for directories */
672 static int
673 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
675 int n;
676 u_int c = 1, colspace = 0, columns = 1;
677 SFTP_DIRENT **d;
679 if ((n = do_readdir(conn, path, &d)) != 0)
680 return (n);
682 if (!(lflag & LS_SHORT_VIEW)) {
683 u_int m = 0, width = 80;
684 struct winsize ws;
685 char *tmp;
687 /* Count entries for sort and find longest filename */
688 for (n = 0; d[n] != NULL; n++) {
689 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
690 m = MAX(m, strlen(d[n]->filename));
693 /* Add any subpath that also needs to be counted */
694 tmp = path_strip(path, strip_path);
695 m += strlen(tmp);
696 xfree(tmp);
698 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
699 width = ws.ws_col;
701 columns = width / (m + 2);
702 columns = MAX(columns, 1);
703 colspace = width / columns;
704 colspace = MIN(colspace, width);
707 if (lflag & SORT_FLAGS) {
708 for (n = 0; d[n] != NULL; n++)
709 ; /* count entries */
710 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
711 qsort(d, n, sizeof(*d), sdirent_comp);
714 for (n = 0; d[n] != NULL && !interrupted; n++) {
715 char *tmp, *fname;
717 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
718 continue;
720 tmp = path_append(path, d[n]->filename);
721 fname = path_strip(tmp, strip_path);
722 xfree(tmp);
724 if (lflag & LS_LONG_VIEW) {
725 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
726 char *lname;
727 struct stat sb;
729 memset(&sb, 0, sizeof(sb));
730 attrib_to_stat(&d[n]->a, &sb);
731 lname = ls_file(fname, &sb, 1,
732 (lflag & LS_SI_UNITS));
733 printf("%s\n", lname);
734 xfree(lname);
735 } else
736 printf("%s\n", d[n]->longname);
737 } else {
738 printf("%-*s", colspace, fname);
739 if (c >= columns) {
740 printf("\n");
741 c = 1;
742 } else
743 c++;
746 xfree(fname);
749 if (!(lflag & LS_LONG_VIEW) && (c != 1))
750 printf("\n");
752 free_sftp_dirents(d);
753 return (0);
756 /* sftp ls.1 replacement which handles path globs */
757 static int
758 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
759 int lflag)
761 Attrib *a = NULL;
762 char *fname, *lname;
763 glob_t g;
764 int err;
765 struct winsize ws;
766 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
768 memset(&g, 0, sizeof(g));
770 if (remote_glob(conn, path,
771 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT, NULL, &g) ||
772 (g.gl_pathc && !g.gl_matchc)) {
773 if (g.gl_pathc)
774 globfree(&g);
775 error("Can't ls: \"%s\" not found", path);
776 return -1;
779 if (interrupted)
780 goto out;
783 * If the glob returns a single match and it is a directory,
784 * then just list its contents.
786 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
787 S_ISDIR(g.gl_statv[0]->st_mode)) {
788 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
789 globfree(&g);
790 return err;
793 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
794 width = ws.ws_col;
796 if (!(lflag & LS_SHORT_VIEW)) {
797 /* Count entries for sort and find longest filename */
798 for (i = 0; g.gl_pathv[i]; i++)
799 m = MAX(m, strlen(g.gl_pathv[i]));
801 columns = width / (m + 2);
802 columns = MAX(columns, 1);
803 colspace = width / columns;
806 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
807 fname = path_strip(g.gl_pathv[i], strip_path);
808 if (lflag & LS_LONG_VIEW) {
809 if (g.gl_statv[i] == NULL) {
810 error("no stat information for %s", fname);
811 continue;
813 lname = ls_file(fname, g.gl_statv[i], 1,
814 (lflag & LS_SI_UNITS));
815 printf("%s\n", lname);
816 xfree(lname);
817 } else {
818 printf("%-*s", colspace, fname);
819 if (c >= columns) {
820 printf("\n");
821 c = 1;
822 } else
823 c++;
825 xfree(fname);
828 if (!(lflag & LS_LONG_VIEW) && (c != 1))
829 printf("\n");
831 out:
832 if (g.gl_pathc)
833 globfree(&g);
835 return 0;
838 static int
839 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
841 struct sftp_statvfs st;
842 char s_used[FMT_SCALED_STRSIZE];
843 char s_avail[FMT_SCALED_STRSIZE];
844 char s_root[FMT_SCALED_STRSIZE];
845 char s_total[FMT_SCALED_STRSIZE];
846 unsigned long long ffree;
848 if (do_statvfs(conn, path, &st, 1) == -1)
849 return -1;
850 if (iflag) {
851 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
852 printf(" Inodes Used Avail "
853 "(root) %%Capacity\n");
854 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
855 (unsigned long long)st.f_files,
856 (unsigned long long)(st.f_files - st.f_ffree),
857 (unsigned long long)st.f_favail,
858 (unsigned long long)st.f_ffree, ffree);
859 } else if (hflag) {
860 strlcpy(s_used, "error", sizeof(s_used));
861 strlcpy(s_avail, "error", sizeof(s_avail));
862 strlcpy(s_root, "error", sizeof(s_root));
863 strlcpy(s_total, "error", sizeof(s_total));
864 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
865 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
866 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
867 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
868 printf(" Size Used Avail (root) %%Capacity\n");
869 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
870 s_total, s_used, s_avail, s_root,
871 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
872 st.f_blocks));
873 } else {
874 printf(" Size Used Avail "
875 "(root) %%Capacity\n");
876 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
877 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
878 (unsigned long long)(st.f_frsize *
879 (st.f_blocks - st.f_bfree) / 1024),
880 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
881 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
882 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
883 st.f_blocks));
885 return 0;
889 * Undo escaping of glob sequences in place. Used to undo extra escaping
890 * applied in makeargv() when the string is destined for a function that
891 * does not glob it.
893 static void
894 undo_glob_escape(char *s)
896 size_t i, j;
898 for (i = j = 0;;) {
899 if (s[i] == '\0') {
900 s[j] = '\0';
901 return;
903 if (s[i] != '\\') {
904 s[j++] = s[i++];
905 continue;
907 /* s[i] == '\\' */
908 ++i;
909 switch (s[i]) {
910 case '?':
911 case '[':
912 case '*':
913 case '\\':
914 s[j++] = s[i++];
915 break;
916 case '\0':
917 s[j++] = '\\';
918 s[j] = '\0';
919 return;
920 default:
921 s[j++] = '\\';
922 s[j++] = s[i++];
923 break;
929 * Split a string into an argument vector using sh(1)-style quoting,
930 * comment and escaping rules, but with some tweaks to handle glob(3)
931 * wildcards.
932 * The "sloppy" flag allows for recovery from missing terminating quote, for
933 * use in parsing incomplete commandlines during tab autocompletion.
935 * Returns NULL on error or a NULL-terminated array of arguments.
937 * If "lastquote" is not NULL, the quoting character used for the last
938 * argument is placed in *lastquote ("\0", "'" or "\"").
940 * If "terminated" is not NULL, *terminated will be set to 1 when the
941 * last argument's quote has been properly terminated or 0 otherwise.
942 * This parameter is only of use if "sloppy" is set.
944 #define MAXARGS 128
945 #define MAXARGLEN 8192
946 static char **
947 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
948 u_int *terminated)
950 int argc, quot;
951 size_t i, j;
952 static char argvs[MAXARGLEN];
953 static char *argv[MAXARGS + 1];
954 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
956 *argcp = argc = 0;
957 if (strlen(arg) > sizeof(argvs) - 1) {
958 args_too_longs:
959 error("string too long");
960 return NULL;
962 if (terminated != NULL)
963 *terminated = 1;
964 if (lastquote != NULL)
965 *lastquote = '\0';
966 state = MA_START;
967 i = j = 0;
968 for (;;) {
969 if (isspace(arg[i])) {
970 if (state == MA_UNQUOTED) {
971 /* Terminate current argument */
972 argvs[j++] = '\0';
973 argc++;
974 state = MA_START;
975 } else if (state != MA_START)
976 argvs[j++] = arg[i];
977 } else if (arg[i] == '"' || arg[i] == '\'') {
978 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
979 if (state == MA_START) {
980 argv[argc] = argvs + j;
981 state = q;
982 if (lastquote != NULL)
983 *lastquote = arg[i];
984 } else if (state == MA_UNQUOTED)
985 state = q;
986 else if (state == q)
987 state = MA_UNQUOTED;
988 else
989 argvs[j++] = arg[i];
990 } else if (arg[i] == '\\') {
991 if (state == MA_SQUOTE || state == MA_DQUOTE) {
992 quot = state == MA_SQUOTE ? '\'' : '"';
993 /* Unescape quote we are in */
994 /* XXX support \n and friends? */
995 if (arg[i + 1] == quot) {
996 i++;
997 argvs[j++] = arg[i];
998 } else if (arg[i + 1] == '?' ||
999 arg[i + 1] == '[' || arg[i + 1] == '*') {
1001 * Special case for sftp: append
1002 * double-escaped glob sequence -
1003 * glob will undo one level of
1004 * escaping. NB. string can grow here.
1006 if (j >= sizeof(argvs) - 5)
1007 goto args_too_longs;
1008 argvs[j++] = '\\';
1009 argvs[j++] = arg[i++];
1010 argvs[j++] = '\\';
1011 argvs[j++] = arg[i];
1012 } else {
1013 argvs[j++] = arg[i++];
1014 argvs[j++] = arg[i];
1016 } else {
1017 if (state == MA_START) {
1018 argv[argc] = argvs + j;
1019 state = MA_UNQUOTED;
1020 if (lastquote != NULL)
1021 *lastquote = '\0';
1023 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1024 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1026 * Special case for sftp: append
1027 * escaped glob sequence -
1028 * glob will undo one level of
1029 * escaping.
1031 argvs[j++] = arg[i++];
1032 argvs[j++] = arg[i];
1033 } else {
1034 /* Unescape everything */
1035 /* XXX support \n and friends? */
1036 i++;
1037 argvs[j++] = arg[i];
1040 } else if (arg[i] == '#') {
1041 if (state == MA_SQUOTE || state == MA_DQUOTE)
1042 argvs[j++] = arg[i];
1043 else
1044 goto string_done;
1045 } else if (arg[i] == '\0') {
1046 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1047 if (sloppy) {
1048 state = MA_UNQUOTED;
1049 if (terminated != NULL)
1050 *terminated = 0;
1051 goto string_done;
1053 error("Unterminated quoted argument");
1054 return NULL;
1056 string_done:
1057 if (state == MA_UNQUOTED) {
1058 argvs[j++] = '\0';
1059 argc++;
1061 break;
1062 } else {
1063 if (state == MA_START) {
1064 argv[argc] = argvs + j;
1065 state = MA_UNQUOTED;
1066 if (lastquote != NULL)
1067 *lastquote = '\0';
1069 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1070 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1072 * Special case for sftp: escape quoted
1073 * glob(3) wildcards. NB. string can grow
1074 * here.
1076 if (j >= sizeof(argvs) - 3)
1077 goto args_too_longs;
1078 argvs[j++] = '\\';
1079 argvs[j++] = arg[i];
1080 } else
1081 argvs[j++] = arg[i];
1083 i++;
1085 *argcp = argc;
1086 return argv;
1089 static int
1090 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1091 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1093 const char *cmd, *cp = *cpp;
1094 char *cp2, **argv;
1095 int base = 0;
1096 long l;
1097 int i, cmdnum, optidx, argc;
1099 /* Skip leading whitespace */
1100 cp = cp + strspn(cp, WHITESPACE);
1102 /* Check for leading '-' (disable error processing) */
1103 *iflag = 0;
1104 if (*cp == '-') {
1105 *iflag = 1;
1106 cp++;
1107 cp = cp + strspn(cp, WHITESPACE);
1110 /* Ignore blank lines and lines which begin with comment '#' char */
1111 if (*cp == '\0' || *cp == '#')
1112 return (0);
1114 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1115 return -1;
1117 /* Figure out which command we have */
1118 for (i = 0; cmds[i].c != NULL; i++) {
1119 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1120 break;
1122 cmdnum = cmds[i].n;
1123 cmd = cmds[i].c;
1125 /* Special case */
1126 if (*cp == '!') {
1127 cp++;
1128 cmdnum = I_SHELL;
1129 } else if (cmdnum == -1) {
1130 error("Invalid command.");
1131 return -1;
1134 /* Get arguments and parse flags */
1135 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1136 *path1 = *path2 = NULL;
1137 optidx = 1;
1138 switch (cmdnum) {
1139 case I_GET:
1140 case I_PUT:
1141 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1142 return -1;
1143 /* Get first pathname (mandatory) */
1144 if (argc - optidx < 1) {
1145 error("You must specify at least one path after a "
1146 "%s command.", cmd);
1147 return -1;
1149 *path1 = xstrdup(argv[optidx]);
1150 /* Get second pathname (optional) */
1151 if (argc - optidx > 1) {
1152 *path2 = xstrdup(argv[optidx + 1]);
1153 /* Destination is not globbed */
1154 undo_glob_escape(*path2);
1156 break;
1157 case I_RENAME:
1158 case I_SYMLINK:
1159 if (argc - optidx < 2) {
1160 error("You must specify two paths after a %s "
1161 "command.", cmd);
1162 return -1;
1164 *path1 = xstrdup(argv[optidx]);
1165 *path2 = xstrdup(argv[optidx + 1]);
1166 /* Paths are not globbed */
1167 undo_glob_escape(*path1);
1168 undo_glob_escape(*path2);
1169 break;
1170 case I_RM:
1171 case I_MKDIR:
1172 case I_RMDIR:
1173 case I_CHDIR:
1174 case I_LCHDIR:
1175 case I_LMKDIR:
1176 /* Get pathname (mandatory) */
1177 if (argc - optidx < 1) {
1178 error("You must specify a path after a %s command.",
1179 cmd);
1180 return -1;
1182 *path1 = xstrdup(argv[optidx]);
1183 /* Only "rm" globs */
1184 if (cmdnum != I_RM)
1185 undo_glob_escape(*path1);
1186 break;
1187 case I_DF:
1188 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1189 iflag)) == -1)
1190 return -1;
1191 /* Default to current directory if no path specified */
1192 if (argc - optidx < 1)
1193 *path1 = NULL;
1194 else {
1195 *path1 = xstrdup(argv[optidx]);
1196 undo_glob_escape(*path1);
1198 break;
1199 case I_LS:
1200 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1201 return(-1);
1202 /* Path is optional */
1203 if (argc - optidx > 0)
1204 *path1 = xstrdup(argv[optidx]);
1205 break;
1206 case I_LLS:
1207 /* Skip ls command and following whitespace */
1208 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1209 case I_SHELL:
1210 /* Uses the rest of the line */
1211 break;
1212 case I_LUMASK:
1213 case I_CHMOD:
1214 base = 8;
1215 case I_CHOWN:
1216 case I_CHGRP:
1217 /* Get numeric arg (mandatory) */
1218 if (argc - optidx < 1)
1219 goto need_num_arg;
1220 errno = 0;
1221 l = strtol(argv[optidx], &cp2, base);
1222 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1223 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1224 l < 0) {
1225 need_num_arg:
1226 error("You must supply a numeric argument "
1227 "to the %s command.", cmd);
1228 return -1;
1230 *n_arg = l;
1231 if (cmdnum == I_LUMASK)
1232 break;
1233 /* Get pathname (mandatory) */
1234 if (argc - optidx < 2) {
1235 error("You must specify a path after a %s command.",
1236 cmd);
1237 return -1;
1239 *path1 = xstrdup(argv[optidx + 1]);
1240 break;
1241 case I_QUIT:
1242 case I_PWD:
1243 case I_LPWD:
1244 case I_HELP:
1245 case I_VERSION:
1246 case I_PROGRESS:
1247 break;
1248 default:
1249 fatal("Command not implemented");
1252 *cpp = cp;
1253 return(cmdnum);
1256 static int
1257 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1258 int err_abort)
1260 char *path1, *path2, *tmp;
1261 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1262 unsigned long n_arg = 0;
1263 Attrib a, *aa;
1264 char path_buf[MAXPATHLEN];
1265 int err = 0;
1266 glob_t g;
1268 path1 = path2 = NULL;
1269 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1270 &path1, &path2);
1272 if (iflag != 0)
1273 err_abort = 0;
1275 memset(&g, 0, sizeof(g));
1277 /* Perform command */
1278 switch (cmdnum) {
1279 case 0:
1280 /* Blank line */
1281 break;
1282 case -1:
1283 /* Unrecognized command */
1284 err = -1;
1285 break;
1286 case I_GET:
1287 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1288 break;
1289 case I_PUT:
1290 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1291 break;
1292 case I_RENAME:
1293 path1 = make_absolute(path1, *pwd);
1294 path2 = make_absolute(path2, *pwd);
1295 err = do_rename(conn, path1, path2);
1296 break;
1297 case I_SYMLINK:
1298 path2 = make_absolute(path2, *pwd);
1299 err = do_symlink(conn, path1, path2);
1300 break;
1301 case I_RM:
1302 path1 = make_absolute(path1, *pwd);
1303 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1304 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1305 printf("Removing %s\n", g.gl_pathv[i]);
1306 err = do_rm(conn, g.gl_pathv[i]);
1307 if (err != 0 && err_abort)
1308 break;
1310 break;
1311 case I_MKDIR:
1312 path1 = make_absolute(path1, *pwd);
1313 attrib_clear(&a);
1314 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1315 a.perm = 0777;
1316 err = do_mkdir(conn, path1, &a, 1);
1317 break;
1318 case I_RMDIR:
1319 path1 = make_absolute(path1, *pwd);
1320 err = do_rmdir(conn, path1);
1321 break;
1322 case I_CHDIR:
1323 path1 = make_absolute(path1, *pwd);
1324 if ((tmp = do_realpath(conn, path1)) == NULL) {
1325 err = 1;
1326 break;
1328 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1329 xfree(tmp);
1330 err = 1;
1331 break;
1333 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1334 error("Can't change directory: Can't check target");
1335 xfree(tmp);
1336 err = 1;
1337 break;
1339 if (!S_ISDIR(aa->perm)) {
1340 error("Can't change directory: \"%s\" is not "
1341 "a directory", tmp);
1342 xfree(tmp);
1343 err = 1;
1344 break;
1346 xfree(*pwd);
1347 *pwd = tmp;
1348 break;
1349 case I_LS:
1350 if (!path1) {
1351 do_ls_dir(conn, *pwd, *pwd, lflag);
1352 break;
1355 /* Strip pwd off beginning of non-absolute paths */
1356 tmp = NULL;
1357 if (*path1 != '/')
1358 tmp = *pwd;
1360 path1 = make_absolute(path1, *pwd);
1361 err = do_globbed_ls(conn, path1, tmp, lflag);
1362 break;
1363 case I_DF:
1364 /* Default to current directory if no path specified */
1365 if (path1 == NULL)
1366 path1 = xstrdup(*pwd);
1367 path1 = make_absolute(path1, *pwd);
1368 err = do_df(conn, path1, hflag, iflag);
1369 break;
1370 case I_LCHDIR:
1371 if (chdir(path1) == -1) {
1372 error("Couldn't change local directory to "
1373 "\"%s\": %s", path1, strerror(errno));
1374 err = 1;
1376 break;
1377 case I_LMKDIR:
1378 if (mkdir(path1, 0777) == -1) {
1379 error("Couldn't create local directory "
1380 "\"%s\": %s", path1, strerror(errno));
1381 err = 1;
1383 break;
1384 case I_LLS:
1385 local_do_ls(cmd);
1386 break;
1387 case I_SHELL:
1388 local_do_shell(cmd);
1389 break;
1390 case I_LUMASK:
1391 umask(n_arg);
1392 printf("Local umask: %03lo\n", n_arg);
1393 break;
1394 case I_CHMOD:
1395 path1 = make_absolute(path1, *pwd);
1396 attrib_clear(&a);
1397 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1398 a.perm = n_arg;
1399 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1400 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1401 printf("Changing mode on %s\n", g.gl_pathv[i]);
1402 err = do_setstat(conn, g.gl_pathv[i], &a);
1403 if (err != 0 && err_abort)
1404 break;
1406 break;
1407 case I_CHOWN:
1408 case I_CHGRP:
1409 path1 = make_absolute(path1, *pwd);
1410 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1411 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1412 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1413 if (err_abort) {
1414 err = -1;
1415 break;
1416 } else
1417 continue;
1419 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1420 error("Can't get current ownership of "
1421 "remote file \"%s\"", g.gl_pathv[i]);
1422 if (err_abort) {
1423 err = -1;
1424 break;
1425 } else
1426 continue;
1428 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1429 if (cmdnum == I_CHOWN) {
1430 printf("Changing owner on %s\n", g.gl_pathv[i]);
1431 aa->uid = n_arg;
1432 } else {
1433 printf("Changing group on %s\n", g.gl_pathv[i]);
1434 aa->gid = n_arg;
1436 err = do_setstat(conn, g.gl_pathv[i], aa);
1437 if (err != 0 && err_abort)
1438 break;
1440 break;
1441 case I_PWD:
1442 printf("Remote working directory: %s\n", *pwd);
1443 break;
1444 case I_LPWD:
1445 if (!getcwd(path_buf, sizeof(path_buf))) {
1446 error("Couldn't get local cwd: %s", strerror(errno));
1447 err = -1;
1448 break;
1450 printf("Local working directory: %s\n", path_buf);
1451 break;
1452 case I_QUIT:
1453 /* Processed below */
1454 break;
1455 case I_HELP:
1456 help();
1457 break;
1458 case I_VERSION:
1459 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1460 break;
1461 case I_PROGRESS:
1462 showprogress = !showprogress;
1463 if (showprogress)
1464 printf("Progress meter enabled\n");
1465 else
1466 printf("Progress meter disabled\n");
1467 break;
1468 default:
1469 fatal("%d is not implemented", cmdnum);
1472 if (g.gl_pathc)
1473 globfree(&g);
1474 if (path1)
1475 xfree(path1);
1476 if (path2)
1477 xfree(path2);
1479 /* If an unignored error occurs in batch mode we should abort. */
1480 if (err_abort && err != 0)
1481 return (-1);
1482 else if (cmdnum == I_QUIT)
1483 return (1);
1485 return (0);
1488 #ifdef USE_LIBEDIT
1489 static char *
1490 prompt(EditLine *el)
1492 return ("sftp> ");
1495 /* Display entries in 'list' after skipping the first 'len' chars */
1496 static void
1497 complete_display(char **list, u_int len)
1499 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1500 struct winsize ws;
1501 char *tmp;
1503 /* Count entries for sort and find longest */
1504 for (y = 0; list[y]; y++)
1505 m = MAX(m, strlen(list[y]));
1507 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1508 width = ws.ws_col;
1510 m = m > len ? m - len : 0;
1511 columns = width / (m + 2);
1512 columns = MAX(columns, 1);
1513 colspace = width / columns;
1514 colspace = MIN(colspace, width);
1516 printf("\n");
1517 m = 1;
1518 for (y = 0; list[y]; y++) {
1519 llen = strlen(list[y]);
1520 tmp = llen > len ? list[y] + len : "";
1521 printf("%-*s", colspace, tmp);
1522 if (m >= columns) {
1523 printf("\n");
1524 m = 1;
1525 } else
1526 m++;
1528 printf("\n");
1532 * Given a "list" of words that begin with a common prefix of "word",
1533 * attempt to find an autocompletion to extends "word" by the next
1534 * characters common to all entries in "list".
1536 static char *
1537 complete_ambiguous(const char *word, char **list, size_t count)
1539 if (word == NULL)
1540 return NULL;
1542 if (count > 0) {
1543 u_int y, matchlen = strlen(list[0]);
1545 /* Find length of common stem */
1546 for (y = 1; list[y]; y++) {
1547 u_int x;
1549 for (x = 0; x < matchlen; x++)
1550 if (list[0][x] != list[y][x])
1551 break;
1553 matchlen = x;
1556 if (matchlen > strlen(word)) {
1557 char *tmp = xstrdup(list[0]);
1559 tmp[matchlen] = '\0';
1560 return tmp;
1564 return xstrdup(word);
1567 /* Autocomplete a sftp command */
1568 static int
1569 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1570 int terminated)
1572 u_int y, count = 0, cmdlen, tmplen;
1573 char *tmp, **list, argterm[3];
1574 const LineInfo *lf;
1576 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1578 /* No command specified: display all available commands */
1579 if (cmd == NULL) {
1580 for (y = 0; cmds[y].c; y++)
1581 list[count++] = xstrdup(cmds[y].c);
1583 list[count] = NULL;
1584 complete_display(list, 0);
1586 for (y = 0; list[y] != NULL; y++)
1587 xfree(list[y]);
1588 xfree(list);
1589 return count;
1592 /* Prepare subset of commands that start with "cmd" */
1593 cmdlen = strlen(cmd);
1594 for (y = 0; cmds[y].c; y++) {
1595 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1596 list[count++] = xstrdup(cmds[y].c);
1598 list[count] = NULL;
1600 if (count == 0)
1601 return 0;
1603 /* Complete ambigious command */
1604 tmp = complete_ambiguous(cmd, list, count);
1605 if (count > 1)
1606 complete_display(list, 0);
1608 for (y = 0; list[y]; y++)
1609 xfree(list[y]);
1610 xfree(list);
1612 if (tmp != NULL) {
1613 tmplen = strlen(tmp);
1614 cmdlen = strlen(cmd);
1615 /* If cmd may be extended then do so */
1616 if (tmplen > cmdlen)
1617 if (el_insertstr(el, tmp + cmdlen) == -1)
1618 fatal("el_insertstr failed.");
1619 lf = el_line(el);
1620 /* Terminate argument cleanly */
1621 if (count == 1) {
1622 y = 0;
1623 if (!terminated)
1624 argterm[y++] = quote;
1625 if (lastarg || *(lf->cursor) != ' ')
1626 argterm[y++] = ' ';
1627 argterm[y] = '\0';
1628 if (y > 0 && el_insertstr(el, argterm) == -1)
1629 fatal("el_insertstr failed.");
1631 xfree(tmp);
1634 return count;
1638 * Determine whether a particular sftp command's arguments (if any)
1639 * represent local or remote files.
1641 static int
1642 complete_is_remote(char *cmd) {
1643 int i;
1645 if (cmd == NULL)
1646 return -1;
1648 for (i = 0; cmds[i].c; i++) {
1649 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1650 return cmds[i].t;
1653 return -1;
1656 /* Autocomplete a filename "file" */
1657 static int
1658 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1659 char *file, int remote, int lastarg, char quote, int terminated)
1661 glob_t g;
1662 char *tmp, *tmp2, ins[3];
1663 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1664 const LineInfo *lf;
1666 /* Glob from "file" location */
1667 if (file == NULL)
1668 tmp = xstrdup("*");
1669 else
1670 xasprintf(&tmp, "%s*", file);
1672 memset(&g, 0, sizeof(g));
1673 if (remote != LOCAL) {
1674 tmp = make_absolute(tmp, remote_path);
1675 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1676 } else
1677 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1679 /* Determine length of pwd so we can trim completion display */
1680 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1681 /* Terminate counting on first unescaped glob metacharacter */
1682 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1683 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1684 hadglob = 1;
1685 break;
1687 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1688 tmplen++;
1689 if (tmp[tmplen] == '/')
1690 pwdlen = tmplen + 1; /* track last seen '/' */
1692 xfree(tmp);
1694 if (g.gl_matchc == 0)
1695 goto out;
1697 if (g.gl_matchc > 1)
1698 complete_display(g.gl_pathv, pwdlen);
1700 tmp = NULL;
1701 /* Don't try to extend globs */
1702 if (file == NULL || hadglob)
1703 goto out;
1705 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1706 tmp = path_strip(tmp2, remote_path);
1707 xfree(tmp2);
1709 if (tmp == NULL)
1710 goto out;
1712 tmplen = strlen(tmp);
1713 filelen = strlen(file);
1715 if (tmplen > filelen) {
1716 tmp2 = tmp + filelen;
1717 len = strlen(tmp2);
1718 /* quote argument on way out */
1719 for (i = 0; i < len; i++) {
1720 ins[0] = '\\';
1721 ins[1] = tmp2[i];
1722 ins[2] = '\0';
1723 switch (tmp2[i]) {
1724 case '\'':
1725 case '"':
1726 case '\\':
1727 case '\t':
1728 case '[':
1729 case ' ':
1730 if (quote == '\0' || tmp2[i] == quote) {
1731 if (el_insertstr(el, ins) == -1)
1732 fatal("el_insertstr "
1733 "failed.");
1734 break;
1736 /* FALLTHROUGH */
1737 default:
1738 if (el_insertstr(el, ins + 1) == -1)
1739 fatal("el_insertstr failed.");
1740 break;
1745 lf = el_line(el);
1746 if (g.gl_matchc == 1) {
1747 i = 0;
1748 if (!terminated)
1749 ins[i++] = quote;
1750 if (*(lf->cursor - 1) != '/' &&
1751 (lastarg || *(lf->cursor) != ' '))
1752 ins[i++] = ' ';
1753 ins[i] = '\0';
1754 if (i > 0 && el_insertstr(el, ins) == -1)
1755 fatal("el_insertstr failed.");
1757 xfree(tmp);
1759 out:
1760 globfree(&g);
1761 return g.gl_matchc;
1764 /* tab-completion hook function, called via libedit */
1765 static unsigned char
1766 complete(EditLine *el, int ch)
1768 char **argv, *line, quote;
1769 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1770 const LineInfo *lf;
1771 struct complete_ctx *complete_ctx;
1773 lf = el_line(el);
1774 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1775 fatal("%s: el_get failed", __func__);
1777 /* Figure out which argument the cursor points to */
1778 cursor = lf->cursor - lf->buffer;
1779 line = (char *)xmalloc(cursor + 1);
1780 memcpy(line, lf->buffer, cursor);
1781 line[cursor] = '\0';
1782 argv = makeargv(line, &carg, 1, &quote, &terminated);
1783 xfree(line);
1785 /* Get all the arguments on the line */
1786 len = lf->lastchar - lf->buffer;
1787 line = (char *)xmalloc(len + 1);
1788 memcpy(line, lf->buffer, len);
1789 line[len] = '\0';
1790 argv = makeargv(line, &argc, 1, NULL, NULL);
1792 /* Ensure cursor is at EOL or a argument boundary */
1793 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1794 line[cursor] != '\n') {
1795 xfree(line);
1796 return ret;
1799 if (carg == 0) {
1800 /* Show all available commands */
1801 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1802 ret = CC_REDISPLAY;
1803 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1804 /* Handle the command parsing */
1805 if (complete_cmd_parse(el, argv[0], argc == carg,
1806 quote, terminated) != 0)
1807 ret = CC_REDISPLAY;
1808 } else if (carg >= 1) {
1809 /* Handle file parsing */
1810 int remote = complete_is_remote(argv[0]);
1811 char *filematch = NULL;
1813 if (carg > 1 && line[cursor-1] != ' ')
1814 filematch = argv[carg - 1];
1816 if (remote != 0 &&
1817 complete_match(el, complete_ctx->conn,
1818 *complete_ctx->remote_pathp, filematch,
1819 remote, carg == argc, quote, terminated) != 0)
1820 ret = CC_REDISPLAY;
1823 xfree(line);
1824 return ret;
1826 #endif /* USE_LIBEDIT */
1829 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1831 char *remote_path;
1832 char *dir = NULL;
1833 char cmd[2048];
1834 int err, interactive;
1835 EditLine *el = NULL;
1836 #ifdef USE_LIBEDIT
1837 History *hl = NULL;
1838 HistEvent hev;
1839 extern char *__progname;
1840 struct complete_ctx complete_ctx;
1842 if (!batchmode && isatty(STDIN_FILENO)) {
1843 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1844 fatal("Couldn't initialise editline");
1845 if ((hl = history_init()) == NULL)
1846 fatal("Couldn't initialise editline history");
1847 history(hl, &hev, H_SETSIZE, 100);
1848 el_set(el, EL_HIST, history, hl);
1850 el_set(el, EL_PROMPT, prompt);
1851 el_set(el, EL_EDITOR, "emacs");
1852 el_set(el, EL_TERMINAL, NULL);
1853 el_set(el, EL_SIGNAL, 1);
1854 el_source(el, NULL);
1856 /* Tab Completion */
1857 el_set(el, EL_ADDFN, "ftp-complete",
1858 "Context sensitive argument completion", complete);
1859 complete_ctx.conn = conn;
1860 complete_ctx.remote_pathp = &remote_path;
1861 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1862 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1864 #endif /* USE_LIBEDIT */
1866 remote_path = do_realpath(conn, ".");
1867 if (remote_path == NULL)
1868 fatal("Need cwd");
1870 if (file1 != NULL) {
1871 dir = xstrdup(file1);
1872 dir = make_absolute(dir, remote_path);
1874 if (remote_is_dir(conn, dir) && file2 == NULL) {
1875 printf("Changing to: %s\n", dir);
1876 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1877 if (parse_dispatch_command(conn, cmd,
1878 &remote_path, 1) != 0) {
1879 xfree(dir);
1880 xfree(remote_path);
1881 xfree(conn);
1882 return (-1);
1884 } else {
1885 if (file2 == NULL)
1886 snprintf(cmd, sizeof cmd, "get %s", dir);
1887 else
1888 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1889 file2);
1891 err = parse_dispatch_command(conn, cmd,
1892 &remote_path, 1);
1893 xfree(dir);
1894 xfree(remote_path);
1895 xfree(conn);
1896 return (err);
1898 xfree(dir);
1901 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1902 setvbuf(stdout, NULL, _IOLBF, 0);
1903 setvbuf(infile, NULL, _IOLBF, 0);
1904 #else
1905 setlinebuf(stdout);
1906 setlinebuf(infile);
1907 #endif
1909 interactive = !batchmode && isatty(STDIN_FILENO);
1910 err = 0;
1911 for (;;) {
1912 char *cp;
1914 signal(SIGINT, SIG_IGN);
1916 if (el == NULL) {
1917 if (interactive)
1918 printf("sftp> ");
1919 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1920 if (interactive)
1921 printf("\n");
1922 break;
1924 if (!interactive) { /* Echo command */
1925 printf("sftp> %s", cmd);
1926 if (strlen(cmd) > 0 &&
1927 cmd[strlen(cmd) - 1] != '\n')
1928 printf("\n");
1930 } else {
1931 #ifdef USE_LIBEDIT
1932 const char *line;
1933 int count = 0;
1935 if ((line = el_gets(el, &count)) == NULL ||
1936 count <= 0) {
1937 printf("\n");
1938 break;
1940 history(hl, &hev, H_ENTER, line);
1941 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1942 fprintf(stderr, "Error: input line too long\n");
1943 continue;
1945 #endif /* USE_LIBEDIT */
1948 cp = strrchr(cmd, '\n');
1949 if (cp)
1950 *cp = '\0';
1952 /* Handle user interrupts gracefully during commands */
1953 interrupted = 0;
1954 signal(SIGINT, cmd_interrupt);
1956 err = parse_dispatch_command(conn, cmd, &remote_path,
1957 batchmode);
1958 if (err != 0)
1959 break;
1961 xfree(remote_path);
1962 xfree(conn);
1964 #ifdef USE_LIBEDIT
1965 if (el != NULL)
1966 el_end(el);
1967 #endif /* USE_LIBEDIT */
1969 /* err == 1 signifies normal "quit" exit */
1970 return (err >= 0 ? 0 : -1);
1973 static void
1974 connect_to_server(char *path, char **args, int *in, int *out)
1976 int c_in, c_out;
1978 #ifdef USE_PIPES
1979 int pin[2], pout[2];
1981 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1982 fatal("pipe: %s", strerror(errno));
1983 *in = pin[0];
1984 *out = pout[1];
1985 c_in = pout[0];
1986 c_out = pin[1];
1987 #else /* USE_PIPES */
1988 int inout[2];
1990 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1991 fatal("socketpair: %s", strerror(errno));
1992 *in = *out = inout[0];
1993 c_in = c_out = inout[1];
1994 #endif /* USE_PIPES */
1996 if ((sshpid = fork()) == -1)
1997 fatal("fork: %s", strerror(errno));
1998 else if (sshpid == 0) {
1999 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2000 (dup2(c_out, STDOUT_FILENO) == -1)) {
2001 fprintf(stderr, "dup2: %s\n", strerror(errno));
2002 _exit(1);
2004 close(*in);
2005 close(*out);
2006 close(c_in);
2007 close(c_out);
2010 * The underlying ssh is in the same process group, so we must
2011 * ignore SIGINT if we want to gracefully abort commands,
2012 * otherwise the signal will make it to the ssh process and
2013 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2014 * underlying ssh, it must *not* ignore that signal.
2016 signal(SIGINT, SIG_IGN);
2017 signal(SIGTERM, SIG_DFL);
2018 execvp(path, args);
2019 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2020 _exit(1);
2023 signal(SIGTERM, killchild);
2024 signal(SIGINT, killchild);
2025 signal(SIGHUP, killchild);
2026 close(c_in);
2027 close(c_out);
2030 static void
2031 usage(void)
2033 extern char *__progname;
2035 fprintf(stderr,
2036 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2037 " [-D sftp_server_path] [-F ssh_config] "
2038 "[-i identity_file] [-l limit]\n"
2039 " [-o ssh_option] [-P port] [-R num_requests] "
2040 "[-S program]\n"
2041 " [-s subsystem | sftp_server] host\n"
2042 " %s [user@]host[:file ...]\n"
2043 " %s [user@]host[:dir[/]]\n"
2044 " %s -b batchfile [user@]host\n",
2045 __progname, __progname, __progname, __progname);
2046 exit(1);
2050 main(int argc, char **argv)
2052 int in, out, ch, err;
2053 char *host = NULL, *userhost, *cp, *file2 = NULL;
2054 int debug_level = 0, sshver = 2;
2055 char *file1 = NULL, *sftp_server = NULL;
2056 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2057 const char *errstr;
2058 LogLevel ll = SYSLOG_LEVEL_INFO;
2059 arglist args;
2060 extern int optind;
2061 extern char *optarg;
2062 struct sftp_conn *conn;
2063 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2064 size_t num_requests = DEFAULT_NUM_REQUESTS;
2065 long long limit_kbps = 0;
2067 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2068 sanitise_stdfd();
2070 __progname = ssh_get_progname(argv[0]);
2071 memset(&args, '\0', sizeof(args));
2072 args.list = NULL;
2073 addargs(&args, "%s", ssh_program);
2074 addargs(&args, "-oForwardX11 no");
2075 addargs(&args, "-oForwardAgent no");
2076 addargs(&args, "-oPermitLocalCommand no");
2077 addargs(&args, "-oClearAllForwardings yes");
2079 ll = SYSLOG_LEVEL_INFO;
2080 infile = stdin;
2082 while ((ch = getopt(argc, argv,
2083 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2084 switch (ch) {
2085 /* Passed through to ssh(1) */
2086 case '4':
2087 case '6':
2088 case 'C':
2089 addargs(&args, "-%c", ch);
2090 break;
2091 /* Passed through to ssh(1) with argument */
2092 case 'F':
2093 case 'c':
2094 case 'i':
2095 case 'o':
2096 addargs(&args, "-%c", ch);
2097 addargs(&args, "%s", optarg);
2098 break;
2099 case 'q':
2100 showprogress = 0;
2101 addargs(&args, "-%c", ch);
2102 break;
2103 case 'P':
2104 addargs(&args, "-oPort %s", optarg);
2105 break;
2106 case 'v':
2107 if (debug_level < 3) {
2108 addargs(&args, "-v");
2109 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2111 debug_level++;
2112 break;
2113 case '1':
2114 sshver = 1;
2115 if (sftp_server == NULL)
2116 sftp_server = _PATH_SFTP_SERVER;
2117 break;
2118 case '2':
2119 sshver = 2;
2120 break;
2121 case 'B':
2122 copy_buffer_len = strtol(optarg, &cp, 10);
2123 if (copy_buffer_len == 0 || *cp != '\0')
2124 fatal("Invalid buffer size \"%s\"", optarg);
2125 break;
2126 case 'b':
2127 if (batchmode)
2128 fatal("Batch file already specified.");
2130 /* Allow "-" as stdin */
2131 if (strcmp(optarg, "-") != 0 &&
2132 (infile = fopen(optarg, "r")) == NULL)
2133 fatal("%s (%s).", strerror(errno), optarg);
2134 showprogress = 0;
2135 batchmode = 1;
2136 addargs(&args, "-obatchmode yes");
2137 break;
2138 case 'p':
2139 global_pflag = 1;
2140 break;
2141 case 'D':
2142 sftp_direct = optarg;
2143 break;
2144 case 'l':
2145 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2146 &errstr);
2147 if (errstr != NULL)
2148 usage();
2149 limit_kbps *= 1024; /* kbps */
2150 break;
2151 case 'r':
2152 global_rflag = 1;
2153 break;
2154 case 'R':
2155 num_requests = strtol(optarg, &cp, 10);
2156 if (num_requests == 0 || *cp != '\0')
2157 fatal("Invalid number of requests \"%s\"",
2158 optarg);
2159 break;
2160 case 's':
2161 sftp_server = optarg;
2162 break;
2163 case 'S':
2164 ssh_program = optarg;
2165 replacearg(&args, 0, "%s", ssh_program);
2166 break;
2167 case 'h':
2168 default:
2169 usage();
2173 if (!isatty(STDERR_FILENO))
2174 showprogress = 0;
2176 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2178 if (sftp_direct == NULL) {
2179 if (optind == argc || argc > (optind + 2))
2180 usage();
2182 userhost = xstrdup(argv[optind]);
2183 file2 = argv[optind+1];
2185 if ((host = strrchr(userhost, '@')) == NULL)
2186 host = userhost;
2187 else {
2188 *host++ = '\0';
2189 if (!userhost[0]) {
2190 fprintf(stderr, "Missing username\n");
2191 usage();
2193 addargs(&args, "-l");
2194 addargs(&args, "%s", userhost);
2197 if ((cp = colon(host)) != NULL) {
2198 *cp++ = '\0';
2199 file1 = cp;
2202 host = cleanhostname(host);
2203 if (!*host) {
2204 fprintf(stderr, "Missing hostname\n");
2205 usage();
2208 addargs(&args, "-oProtocol %d", sshver);
2210 /* no subsystem if the server-spec contains a '/' */
2211 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2212 addargs(&args, "-s");
2214 addargs(&args, "--");
2215 addargs(&args, "%s", host);
2216 addargs(&args, "%s", (sftp_server != NULL ?
2217 sftp_server : "sftp"));
2219 connect_to_server(ssh_program, args.list, &in, &out);
2220 } else {
2221 args.list = NULL;
2222 addargs(&args, "sftp-server");
2224 connect_to_server(sftp_direct, args.list, &in, &out);
2226 freeargs(&args);
2228 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2229 if (conn == NULL)
2230 fatal("Couldn't initialise connection to server");
2232 if (!batchmode) {
2233 if (sftp_direct == NULL)
2234 fprintf(stderr, "Connected to %s.\n", host);
2235 else
2236 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2239 err = interactive_loop(conn, file1, file2);
2241 #if !defined(USE_PIPES)
2242 shutdown(in, SHUT_RDWR);
2243 shutdown(out, SHUT_RDWR);
2244 #endif
2246 close(in);
2247 close(out);
2248 if (batchmode)
2249 fclose(infile);
2251 while (waitpid(sshpid, NULL, 0) == -1)
2252 if (errno != EINTR)
2253 fatal("Couldn't wait for ssh process: %s",
2254 strerror(errno));
2256 exit(err == 0 ? 0 : 1);