- (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact
[openssh-git.git] / sftp.c
blob229f12987e50287dc8dfd880706e89c72c15c06b
1 /* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm 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)
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 glob_t g;
762 u_int i, c = 1, colspace = 0, columns = 1;
763 Attrib *a = NULL;
765 memset(&g, 0, sizeof(g));
767 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
768 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
769 if (g.gl_pathc)
770 globfree(&g);
771 error("Can't ls: \"%s\" not found", path);
772 return (-1);
775 if (interrupted)
776 goto out;
779 * If the glob returns a single match and it is a directory,
780 * then just list its contents.
782 if (g.gl_matchc == 1) {
783 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
784 globfree(&g);
785 return (-1);
787 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
788 S_ISDIR(a->perm)) {
789 int err;
791 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
792 globfree(&g);
793 return (err);
797 if (!(lflag & LS_SHORT_VIEW)) {
798 u_int m = 0, width = 80;
799 struct winsize ws;
801 /* Count entries for sort and find longest filename */
802 for (i = 0; g.gl_pathv[i]; i++)
803 m = MAX(m, strlen(g.gl_pathv[i]));
805 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
806 width = ws.ws_col;
808 columns = width / (m + 2);
809 columns = MAX(columns, 1);
810 colspace = width / columns;
813 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
814 char *fname;
816 fname = path_strip(g.gl_pathv[i], strip_path);
818 if (lflag & LS_LONG_VIEW) {
819 char *lname;
820 struct stat sb;
823 * XXX: this is slow - 1 roundtrip per path
824 * A solution to this is to fork glob() and
825 * build a sftp specific version which keeps the
826 * attribs (which currently get thrown away)
827 * that the server returns as well as the filenames.
829 memset(&sb, 0, sizeof(sb));
830 if (a == NULL)
831 a = do_lstat(conn, g.gl_pathv[i], 1);
832 if (a != NULL)
833 attrib_to_stat(a, &sb);
834 lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS));
835 printf("%s\n", lname);
836 xfree(lname);
837 } else {
838 printf("%-*s", colspace, fname);
839 if (c >= columns) {
840 printf("\n");
841 c = 1;
842 } else
843 c++;
845 xfree(fname);
848 if (!(lflag & LS_LONG_VIEW) && (c != 1))
849 printf("\n");
851 out:
852 if (g.gl_pathc)
853 globfree(&g);
855 return (0);
858 static int
859 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
861 struct sftp_statvfs st;
862 char s_used[FMT_SCALED_STRSIZE];
863 char s_avail[FMT_SCALED_STRSIZE];
864 char s_root[FMT_SCALED_STRSIZE];
865 char s_total[FMT_SCALED_STRSIZE];
866 unsigned long long ffree;
868 if (do_statvfs(conn, path, &st, 1) == -1)
869 return -1;
870 if (iflag) {
871 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
872 printf(" Inodes Used Avail "
873 "(root) %%Capacity\n");
874 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
875 (unsigned long long)st.f_files,
876 (unsigned long long)(st.f_files - st.f_ffree),
877 (unsigned long long)st.f_favail,
878 (unsigned long long)st.f_ffree, ffree);
879 } else if (hflag) {
880 strlcpy(s_used, "error", sizeof(s_used));
881 strlcpy(s_avail, "error", sizeof(s_avail));
882 strlcpy(s_root, "error", sizeof(s_root));
883 strlcpy(s_total, "error", sizeof(s_total));
884 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
885 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
886 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
887 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
888 printf(" Size Used Avail (root) %%Capacity\n");
889 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
890 s_total, s_used, s_avail, s_root,
891 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
892 st.f_blocks));
893 } else {
894 printf(" Size Used Avail "
895 "(root) %%Capacity\n");
896 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
897 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
898 (unsigned long long)(st.f_frsize *
899 (st.f_blocks - st.f_bfree) / 1024),
900 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
901 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
902 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
903 st.f_blocks));
905 return 0;
909 * Undo escaping of glob sequences in place. Used to undo extra escaping
910 * applied in makeargv() when the string is destined for a function that
911 * does not glob it.
913 static void
914 undo_glob_escape(char *s)
916 size_t i, j;
918 for (i = j = 0;;) {
919 if (s[i] == '\0') {
920 s[j] = '\0';
921 return;
923 if (s[i] != '\\') {
924 s[j++] = s[i++];
925 continue;
927 /* s[i] == '\\' */
928 ++i;
929 switch (s[i]) {
930 case '?':
931 case '[':
932 case '*':
933 case '\\':
934 s[j++] = s[i++];
935 break;
936 case '\0':
937 s[j++] = '\\';
938 s[j] = '\0';
939 return;
940 default:
941 s[j++] = '\\';
942 s[j++] = s[i++];
943 break;
949 * Split a string into an argument vector using sh(1)-style quoting,
950 * comment and escaping rules, but with some tweaks to handle glob(3)
951 * wildcards.
952 * The "sloppy" flag allows for recovery from missing terminating quote, for
953 * use in parsing incomplete commandlines during tab autocompletion.
955 * Returns NULL on error or a NULL-terminated array of arguments.
957 * If "lastquote" is not NULL, the quoting character used for the last
958 * argument is placed in *lastquote ("\0", "'" or "\"").
960 * If "terminated" is not NULL, *terminated will be set to 1 when the
961 * last argument's quote has been properly terminated or 0 otherwise.
962 * This parameter is only of use if "sloppy" is set.
964 #define MAXARGS 128
965 #define MAXARGLEN 8192
966 static char **
967 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
968 u_int *terminated)
970 int argc, quot;
971 size_t i, j;
972 static char argvs[MAXARGLEN];
973 static char *argv[MAXARGS + 1];
974 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
976 *argcp = argc = 0;
977 if (strlen(arg) > sizeof(argvs) - 1) {
978 args_too_longs:
979 error("string too long");
980 return NULL;
982 if (terminated != NULL)
983 *terminated = 1;
984 if (lastquote != NULL)
985 *lastquote = '\0';
986 state = MA_START;
987 i = j = 0;
988 for (;;) {
989 if (isspace(arg[i])) {
990 if (state == MA_UNQUOTED) {
991 /* Terminate current argument */
992 argvs[j++] = '\0';
993 argc++;
994 state = MA_START;
995 } else if (state != MA_START)
996 argvs[j++] = arg[i];
997 } else if (arg[i] == '"' || arg[i] == '\'') {
998 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
999 if (state == MA_START) {
1000 argv[argc] = argvs + j;
1001 state = q;
1002 if (lastquote != NULL)
1003 *lastquote = arg[i];
1004 } else if (state == MA_UNQUOTED)
1005 state = q;
1006 else if (state == q)
1007 state = MA_UNQUOTED;
1008 else
1009 argvs[j++] = arg[i];
1010 } else if (arg[i] == '\\') {
1011 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1012 quot = state == MA_SQUOTE ? '\'' : '"';
1013 /* Unescape quote we are in */
1014 /* XXX support \n and friends? */
1015 if (arg[i + 1] == quot) {
1016 i++;
1017 argvs[j++] = arg[i];
1018 } else if (arg[i + 1] == '?' ||
1019 arg[i + 1] == '[' || arg[i + 1] == '*') {
1021 * Special case for sftp: append
1022 * double-escaped glob sequence -
1023 * glob will undo one level of
1024 * escaping. NB. string can grow here.
1026 if (j >= sizeof(argvs) - 5)
1027 goto args_too_longs;
1028 argvs[j++] = '\\';
1029 argvs[j++] = arg[i++];
1030 argvs[j++] = '\\';
1031 argvs[j++] = arg[i];
1032 } else {
1033 argvs[j++] = arg[i++];
1034 argvs[j++] = arg[i];
1036 } else {
1037 if (state == MA_START) {
1038 argv[argc] = argvs + j;
1039 state = MA_UNQUOTED;
1040 if (lastquote != NULL)
1041 *lastquote = '\0';
1043 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1044 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1046 * Special case for sftp: append
1047 * escaped glob sequence -
1048 * glob will undo one level of
1049 * escaping.
1051 argvs[j++] = arg[i++];
1052 argvs[j++] = arg[i];
1053 } else {
1054 /* Unescape everything */
1055 /* XXX support \n and friends? */
1056 i++;
1057 argvs[j++] = arg[i];
1060 } else if (arg[i] == '#') {
1061 if (state == MA_SQUOTE || state == MA_DQUOTE)
1062 argvs[j++] = arg[i];
1063 else
1064 goto string_done;
1065 } else if (arg[i] == '\0') {
1066 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1067 if (sloppy) {
1068 state = MA_UNQUOTED;
1069 if (terminated != NULL)
1070 *terminated = 0;
1071 goto string_done;
1073 error("Unterminated quoted argument");
1074 return NULL;
1076 string_done:
1077 if (state == MA_UNQUOTED) {
1078 argvs[j++] = '\0';
1079 argc++;
1081 break;
1082 } else {
1083 if (state == MA_START) {
1084 argv[argc] = argvs + j;
1085 state = MA_UNQUOTED;
1086 if (lastquote != NULL)
1087 *lastquote = '\0';
1089 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1090 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1092 * Special case for sftp: escape quoted
1093 * glob(3) wildcards. NB. string can grow
1094 * here.
1096 if (j >= sizeof(argvs) - 3)
1097 goto args_too_longs;
1098 argvs[j++] = '\\';
1099 argvs[j++] = arg[i];
1100 } else
1101 argvs[j++] = arg[i];
1103 i++;
1105 *argcp = argc;
1106 return argv;
1109 static int
1110 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1111 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1113 const char *cmd, *cp = *cpp;
1114 char *cp2, **argv;
1115 int base = 0;
1116 long l;
1117 int i, cmdnum, optidx, argc;
1119 /* Skip leading whitespace */
1120 cp = cp + strspn(cp, WHITESPACE);
1122 /* Check for leading '-' (disable error processing) */
1123 *iflag = 0;
1124 if (*cp == '-') {
1125 *iflag = 1;
1126 cp++;
1127 cp = cp + strspn(cp, WHITESPACE);
1130 /* Ignore blank lines and lines which begin with comment '#' char */
1131 if (*cp == '\0' || *cp == '#')
1132 return (0);
1134 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1135 return -1;
1137 /* Figure out which command we have */
1138 for (i = 0; cmds[i].c != NULL; i++) {
1139 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1140 break;
1142 cmdnum = cmds[i].n;
1143 cmd = cmds[i].c;
1145 /* Special case */
1146 if (*cp == '!') {
1147 cp++;
1148 cmdnum = I_SHELL;
1149 } else if (cmdnum == -1) {
1150 error("Invalid command.");
1151 return -1;
1154 /* Get arguments and parse flags */
1155 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1156 *path1 = *path2 = NULL;
1157 optidx = 1;
1158 switch (cmdnum) {
1159 case I_GET:
1160 case I_PUT:
1161 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1162 return -1;
1163 /* Get first pathname (mandatory) */
1164 if (argc - optidx < 1) {
1165 error("You must specify at least one path after a "
1166 "%s command.", cmd);
1167 return -1;
1169 *path1 = xstrdup(argv[optidx]);
1170 /* Get second pathname (optional) */
1171 if (argc - optidx > 1) {
1172 *path2 = xstrdup(argv[optidx + 1]);
1173 /* Destination is not globbed */
1174 undo_glob_escape(*path2);
1176 break;
1177 case I_RENAME:
1178 case I_SYMLINK:
1179 if (argc - optidx < 2) {
1180 error("You must specify two paths after a %s "
1181 "command.", cmd);
1182 return -1;
1184 *path1 = xstrdup(argv[optidx]);
1185 *path2 = xstrdup(argv[optidx + 1]);
1186 /* Paths are not globbed */
1187 undo_glob_escape(*path1);
1188 undo_glob_escape(*path2);
1189 break;
1190 case I_RM:
1191 case I_MKDIR:
1192 case I_RMDIR:
1193 case I_CHDIR:
1194 case I_LCHDIR:
1195 case I_LMKDIR:
1196 /* Get pathname (mandatory) */
1197 if (argc - optidx < 1) {
1198 error("You must specify a path after a %s command.",
1199 cmd);
1200 return -1;
1202 *path1 = xstrdup(argv[optidx]);
1203 /* Only "rm" globs */
1204 if (cmdnum != I_RM)
1205 undo_glob_escape(*path1);
1206 break;
1207 case I_DF:
1208 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1209 iflag)) == -1)
1210 return -1;
1211 /* Default to current directory if no path specified */
1212 if (argc - optidx < 1)
1213 *path1 = NULL;
1214 else {
1215 *path1 = xstrdup(argv[optidx]);
1216 undo_glob_escape(*path1);
1218 break;
1219 case I_LS:
1220 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1221 return(-1);
1222 /* Path is optional */
1223 if (argc - optidx > 0)
1224 *path1 = xstrdup(argv[optidx]);
1225 break;
1226 case I_LLS:
1227 /* Skip ls command and following whitespace */
1228 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1229 case I_SHELL:
1230 /* Uses the rest of the line */
1231 break;
1232 case I_LUMASK:
1233 case I_CHMOD:
1234 base = 8;
1235 case I_CHOWN:
1236 case I_CHGRP:
1237 /* Get numeric arg (mandatory) */
1238 if (argc - optidx < 1)
1239 goto need_num_arg;
1240 errno = 0;
1241 l = strtol(argv[optidx], &cp2, base);
1242 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1243 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1244 l < 0) {
1245 need_num_arg:
1246 error("You must supply a numeric argument "
1247 "to the %s command.", cmd);
1248 return -1;
1250 *n_arg = l;
1251 if (cmdnum == I_LUMASK)
1252 break;
1253 /* Get pathname (mandatory) */
1254 if (argc - optidx < 2) {
1255 error("You must specify a path after a %s command.",
1256 cmd);
1257 return -1;
1259 *path1 = xstrdup(argv[optidx + 1]);
1260 break;
1261 case I_QUIT:
1262 case I_PWD:
1263 case I_LPWD:
1264 case I_HELP:
1265 case I_VERSION:
1266 case I_PROGRESS:
1267 break;
1268 default:
1269 fatal("Command not implemented");
1272 *cpp = cp;
1273 return(cmdnum);
1276 static int
1277 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1278 int err_abort)
1280 char *path1, *path2, *tmp;
1281 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1282 unsigned long n_arg = 0;
1283 Attrib a, *aa;
1284 char path_buf[MAXPATHLEN];
1285 int err = 0;
1286 glob_t g;
1288 path1 = path2 = NULL;
1289 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1290 &path1, &path2);
1292 if (iflag != 0)
1293 err_abort = 0;
1295 memset(&g, 0, sizeof(g));
1297 /* Perform command */
1298 switch (cmdnum) {
1299 case 0:
1300 /* Blank line */
1301 break;
1302 case -1:
1303 /* Unrecognized command */
1304 err = -1;
1305 break;
1306 case I_GET:
1307 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1308 break;
1309 case I_PUT:
1310 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1311 break;
1312 case I_RENAME:
1313 path1 = make_absolute(path1, *pwd);
1314 path2 = make_absolute(path2, *pwd);
1315 err = do_rename(conn, path1, path2);
1316 break;
1317 case I_SYMLINK:
1318 path2 = make_absolute(path2, *pwd);
1319 err = do_symlink(conn, path1, path2);
1320 break;
1321 case I_RM:
1322 path1 = make_absolute(path1, *pwd);
1323 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1324 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1325 printf("Removing %s\n", g.gl_pathv[i]);
1326 err = do_rm(conn, g.gl_pathv[i]);
1327 if (err != 0 && err_abort)
1328 break;
1330 break;
1331 case I_MKDIR:
1332 path1 = make_absolute(path1, *pwd);
1333 attrib_clear(&a);
1334 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1335 a.perm = 0777;
1336 err = do_mkdir(conn, path1, &a, 1);
1337 break;
1338 case I_RMDIR:
1339 path1 = make_absolute(path1, *pwd);
1340 err = do_rmdir(conn, path1);
1341 break;
1342 case I_CHDIR:
1343 path1 = make_absolute(path1, *pwd);
1344 if ((tmp = do_realpath(conn, path1)) == NULL) {
1345 err = 1;
1346 break;
1348 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1349 xfree(tmp);
1350 err = 1;
1351 break;
1353 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1354 error("Can't change directory: Can't check target");
1355 xfree(tmp);
1356 err = 1;
1357 break;
1359 if (!S_ISDIR(aa->perm)) {
1360 error("Can't change directory: \"%s\" is not "
1361 "a directory", tmp);
1362 xfree(tmp);
1363 err = 1;
1364 break;
1366 xfree(*pwd);
1367 *pwd = tmp;
1368 break;
1369 case I_LS:
1370 if (!path1) {
1371 do_ls_dir(conn, *pwd, *pwd, lflag);
1372 break;
1375 /* Strip pwd off beginning of non-absolute paths */
1376 tmp = NULL;
1377 if (*path1 != '/')
1378 tmp = *pwd;
1380 path1 = make_absolute(path1, *pwd);
1381 err = do_globbed_ls(conn, path1, tmp, lflag);
1382 break;
1383 case I_DF:
1384 /* Default to current directory if no path specified */
1385 if (path1 == NULL)
1386 path1 = xstrdup(*pwd);
1387 path1 = make_absolute(path1, *pwd);
1388 err = do_df(conn, path1, hflag, iflag);
1389 break;
1390 case I_LCHDIR:
1391 if (chdir(path1) == -1) {
1392 error("Couldn't change local directory to "
1393 "\"%s\": %s", path1, strerror(errno));
1394 err = 1;
1396 break;
1397 case I_LMKDIR:
1398 if (mkdir(path1, 0777) == -1) {
1399 error("Couldn't create local directory "
1400 "\"%s\": %s", path1, strerror(errno));
1401 err = 1;
1403 break;
1404 case I_LLS:
1405 local_do_ls(cmd);
1406 break;
1407 case I_SHELL:
1408 local_do_shell(cmd);
1409 break;
1410 case I_LUMASK:
1411 umask(n_arg);
1412 printf("Local umask: %03lo\n", n_arg);
1413 break;
1414 case I_CHMOD:
1415 path1 = make_absolute(path1, *pwd);
1416 attrib_clear(&a);
1417 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1418 a.perm = n_arg;
1419 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1420 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1421 printf("Changing mode on %s\n", g.gl_pathv[i]);
1422 err = do_setstat(conn, g.gl_pathv[i], &a);
1423 if (err != 0 && err_abort)
1424 break;
1426 break;
1427 case I_CHOWN:
1428 case I_CHGRP:
1429 path1 = make_absolute(path1, *pwd);
1430 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1431 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1432 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1433 if (err_abort) {
1434 err = -1;
1435 break;
1436 } else
1437 continue;
1439 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1440 error("Can't get current ownership of "
1441 "remote file \"%s\"", g.gl_pathv[i]);
1442 if (err_abort) {
1443 err = -1;
1444 break;
1445 } else
1446 continue;
1448 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1449 if (cmdnum == I_CHOWN) {
1450 printf("Changing owner on %s\n", g.gl_pathv[i]);
1451 aa->uid = n_arg;
1452 } else {
1453 printf("Changing group on %s\n", g.gl_pathv[i]);
1454 aa->gid = n_arg;
1456 err = do_setstat(conn, g.gl_pathv[i], aa);
1457 if (err != 0 && err_abort)
1458 break;
1460 break;
1461 case I_PWD:
1462 printf("Remote working directory: %s\n", *pwd);
1463 break;
1464 case I_LPWD:
1465 if (!getcwd(path_buf, sizeof(path_buf))) {
1466 error("Couldn't get local cwd: %s", strerror(errno));
1467 err = -1;
1468 break;
1470 printf("Local working directory: %s\n", path_buf);
1471 break;
1472 case I_QUIT:
1473 /* Processed below */
1474 break;
1475 case I_HELP:
1476 help();
1477 break;
1478 case I_VERSION:
1479 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1480 break;
1481 case I_PROGRESS:
1482 showprogress = !showprogress;
1483 if (showprogress)
1484 printf("Progress meter enabled\n");
1485 else
1486 printf("Progress meter disabled\n");
1487 break;
1488 default:
1489 fatal("%d is not implemented", cmdnum);
1492 if (g.gl_pathc)
1493 globfree(&g);
1494 if (path1)
1495 xfree(path1);
1496 if (path2)
1497 xfree(path2);
1499 /* If an unignored error occurs in batch mode we should abort. */
1500 if (err_abort && err != 0)
1501 return (-1);
1502 else if (cmdnum == I_QUIT)
1503 return (1);
1505 return (0);
1508 #ifdef USE_LIBEDIT
1509 static char *
1510 prompt(EditLine *el)
1512 return ("sftp> ");
1515 /* Display entries in 'list' after skipping the first 'len' chars */
1516 static void
1517 complete_display(char **list, u_int len)
1519 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1520 struct winsize ws;
1521 char *tmp;
1523 /* Count entries for sort and find longest */
1524 for (y = 0; list[y]; y++)
1525 m = MAX(m, strlen(list[y]));
1527 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1528 width = ws.ws_col;
1530 m = m > len ? m - len : 0;
1531 columns = width / (m + 2);
1532 columns = MAX(columns, 1);
1533 colspace = width / columns;
1534 colspace = MIN(colspace, width);
1536 printf("\n");
1537 m = 1;
1538 for (y = 0; list[y]; y++) {
1539 llen = strlen(list[y]);
1540 tmp = llen > len ? list[y] + len : "";
1541 printf("%-*s", colspace, tmp);
1542 if (m >= columns) {
1543 printf("\n");
1544 m = 1;
1545 } else
1546 m++;
1548 printf("\n");
1552 * Given a "list" of words that begin with a common prefix of "word",
1553 * attempt to find an autocompletion to extends "word" by the next
1554 * characters common to all entries in "list".
1556 static char *
1557 complete_ambiguous(const char *word, char **list, size_t count)
1559 if (word == NULL)
1560 return NULL;
1562 if (count > 0) {
1563 u_int y, matchlen = strlen(list[0]);
1565 /* Find length of common stem */
1566 for (y = 1; list[y]; y++) {
1567 u_int x;
1569 for (x = 0; x < matchlen; x++)
1570 if (list[0][x] != list[y][x])
1571 break;
1573 matchlen = x;
1576 if (matchlen > strlen(word)) {
1577 char *tmp = xstrdup(list[0]);
1579 tmp[matchlen] = '\0';
1580 return tmp;
1584 return xstrdup(word);
1587 /* Autocomplete a sftp command */
1588 static int
1589 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1590 int terminated)
1592 u_int y, count = 0, cmdlen, tmplen;
1593 char *tmp, **list, argterm[3];
1594 const LineInfo *lf;
1596 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1598 /* No command specified: display all available commands */
1599 if (cmd == NULL) {
1600 for (y = 0; cmds[y].c; y++)
1601 list[count++] = xstrdup(cmds[y].c);
1603 list[count] = NULL;
1604 complete_display(list, 0);
1606 for (y = 0; list[y] != NULL; y++)
1607 xfree(list[y]);
1608 xfree(list);
1609 return count;
1612 /* Prepare subset of commands that start with "cmd" */
1613 cmdlen = strlen(cmd);
1614 for (y = 0; cmds[y].c; y++) {
1615 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1616 list[count++] = xstrdup(cmds[y].c);
1618 list[count] = NULL;
1620 if (count == 0)
1621 return 0;
1623 /* Complete ambigious command */
1624 tmp = complete_ambiguous(cmd, list, count);
1625 if (count > 1)
1626 complete_display(list, 0);
1628 for (y = 0; list[y]; y++)
1629 xfree(list[y]);
1630 xfree(list);
1632 if (tmp != NULL) {
1633 tmplen = strlen(tmp);
1634 cmdlen = strlen(cmd);
1635 /* If cmd may be extended then do so */
1636 if (tmplen > cmdlen)
1637 if (el_insertstr(el, tmp + cmdlen) == -1)
1638 fatal("el_insertstr failed.");
1639 lf = el_line(el);
1640 /* Terminate argument cleanly */
1641 if (count == 1) {
1642 y = 0;
1643 if (!terminated)
1644 argterm[y++] = quote;
1645 if (lastarg || *(lf->cursor) != ' ')
1646 argterm[y++] = ' ';
1647 argterm[y] = '\0';
1648 if (y > 0 && el_insertstr(el, argterm) == -1)
1649 fatal("el_insertstr failed.");
1651 xfree(tmp);
1654 return count;
1658 * Determine whether a particular sftp command's arguments (if any)
1659 * represent local or remote files.
1661 static int
1662 complete_is_remote(char *cmd) {
1663 int i;
1665 if (cmd == NULL)
1666 return -1;
1668 for (i = 0; cmds[i].c; i++) {
1669 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1670 return cmds[i].t;
1673 return -1;
1676 /* Autocomplete a filename "file" */
1677 static int
1678 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1679 char *file, int remote, int lastarg, char quote, int terminated)
1681 glob_t g;
1682 char *tmp, *tmp2, ins[3];
1683 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1684 const LineInfo *lf;
1686 /* Glob from "file" location */
1687 if (file == NULL)
1688 tmp = xstrdup("*");
1689 else
1690 xasprintf(&tmp, "%s*", file);
1692 memset(&g, 0, sizeof(g));
1693 if (remote != LOCAL) {
1694 tmp = make_absolute(tmp, remote_path);
1695 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1696 } else
1697 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1699 /* Determine length of pwd so we can trim completion display */
1700 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1701 /* Terminate counting on first unescaped glob metacharacter */
1702 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1703 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1704 hadglob = 1;
1705 break;
1707 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1708 tmplen++;
1709 if (tmp[tmplen] == '/')
1710 pwdlen = tmplen + 1; /* track last seen '/' */
1712 xfree(tmp);
1714 if (g.gl_matchc == 0)
1715 goto out;
1717 if (g.gl_matchc > 1)
1718 complete_display(g.gl_pathv, pwdlen);
1720 tmp = NULL;
1721 /* Don't try to extend globs */
1722 if (file == NULL || hadglob)
1723 goto out;
1725 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1726 tmp = path_strip(tmp2, remote_path);
1727 xfree(tmp2);
1729 if (tmp == NULL)
1730 goto out;
1732 tmplen = strlen(tmp);
1733 filelen = strlen(file);
1735 if (tmplen > filelen) {
1736 tmp2 = tmp + filelen;
1737 len = strlen(tmp2);
1738 /* quote argument on way out */
1739 for (i = 0; i < len; i++) {
1740 ins[0] = '\\';
1741 ins[1] = tmp2[i];
1742 ins[2] = '\0';
1743 switch (tmp2[i]) {
1744 case '\'':
1745 case '"':
1746 case '\\':
1747 case '\t':
1748 case ' ':
1749 if (quote == '\0' || tmp2[i] == quote) {
1750 if (el_insertstr(el, ins) == -1)
1751 fatal("el_insertstr "
1752 "failed.");
1753 break;
1755 /* FALLTHROUGH */
1756 default:
1757 if (el_insertstr(el, ins + 1) == -1)
1758 fatal("el_insertstr failed.");
1759 break;
1764 lf = el_line(el);
1765 if (g.gl_matchc == 1) {
1766 i = 0;
1767 if (!terminated)
1768 ins[i++] = quote;
1769 if (*(lf->cursor - 1) != '/' &&
1770 (lastarg || *(lf->cursor) != ' '))
1771 ins[i++] = ' ';
1772 ins[i] = '\0';
1773 if (i > 0 && el_insertstr(el, ins) == -1)
1774 fatal("el_insertstr failed.");
1776 xfree(tmp);
1778 out:
1779 globfree(&g);
1780 return g.gl_matchc;
1783 /* tab-completion hook function, called via libedit */
1784 static unsigned char
1785 complete(EditLine *el, int ch)
1787 char **argv, *line, quote;
1788 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1789 const LineInfo *lf;
1790 struct complete_ctx *complete_ctx;
1792 lf = el_line(el);
1793 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1794 fatal("%s: el_get failed", __func__);
1796 /* Figure out which argument the cursor points to */
1797 cursor = lf->cursor - lf->buffer;
1798 line = (char *)xmalloc(cursor + 1);
1799 memcpy(line, lf->buffer, cursor);
1800 line[cursor] = '\0';
1801 argv = makeargv(line, &carg, 1, &quote, &terminated);
1802 xfree(line);
1804 /* Get all the arguments on the line */
1805 len = lf->lastchar - lf->buffer;
1806 line = (char *)xmalloc(len + 1);
1807 memcpy(line, lf->buffer, len);
1808 line[len] = '\0';
1809 argv = makeargv(line, &argc, 1, NULL, NULL);
1811 /* Ensure cursor is at EOL or a argument boundary */
1812 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1813 line[cursor] != '\n') {
1814 xfree(line);
1815 return ret;
1818 if (carg == 0) {
1819 /* Show all available commands */
1820 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1821 ret = CC_REDISPLAY;
1822 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1823 /* Handle the command parsing */
1824 if (complete_cmd_parse(el, argv[0], argc == carg,
1825 quote, terminated) != 0)
1826 ret = CC_REDISPLAY;
1827 } else if (carg >= 1) {
1828 /* Handle file parsing */
1829 int remote = complete_is_remote(argv[0]);
1830 char *filematch = NULL;
1832 if (carg > 1 && line[cursor-1] != ' ')
1833 filematch = argv[carg - 1];
1835 if (remote != 0 &&
1836 complete_match(el, complete_ctx->conn,
1837 *complete_ctx->remote_pathp, filematch,
1838 remote, carg == argc, quote, terminated) != 0)
1839 ret = CC_REDISPLAY;
1842 xfree(line);
1843 return ret;
1845 #endif /* USE_LIBEDIT */
1848 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1850 char *remote_path;
1851 char *dir = NULL;
1852 char cmd[2048];
1853 int err, interactive;
1854 EditLine *el = NULL;
1855 #ifdef USE_LIBEDIT
1856 History *hl = NULL;
1857 HistEvent hev;
1858 extern char *__progname;
1859 struct complete_ctx complete_ctx;
1861 if (!batchmode && isatty(STDIN_FILENO)) {
1862 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1863 fatal("Couldn't initialise editline");
1864 if ((hl = history_init()) == NULL)
1865 fatal("Couldn't initialise editline history");
1866 history(hl, &hev, H_SETSIZE, 100);
1867 el_set(el, EL_HIST, history, hl);
1869 el_set(el, EL_PROMPT, prompt);
1870 el_set(el, EL_EDITOR, "emacs");
1871 el_set(el, EL_TERMINAL, NULL);
1872 el_set(el, EL_SIGNAL, 1);
1873 el_source(el, NULL);
1875 /* Tab Completion */
1876 el_set(el, EL_ADDFN, "ftp-complete",
1877 "Context senstive argument completion", complete);
1878 complete_ctx.conn = conn;
1879 complete_ctx.remote_pathp = &remote_path;
1880 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1881 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1883 #endif /* USE_LIBEDIT */
1885 remote_path = do_realpath(conn, ".");
1886 if (remote_path == NULL)
1887 fatal("Need cwd");
1889 if (file1 != NULL) {
1890 dir = xstrdup(file1);
1891 dir = make_absolute(dir, remote_path);
1893 if (remote_is_dir(conn, dir) && file2 == NULL) {
1894 printf("Changing to: %s\n", dir);
1895 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1896 if (parse_dispatch_command(conn, cmd,
1897 &remote_path, 1) != 0) {
1898 xfree(dir);
1899 xfree(remote_path);
1900 xfree(conn);
1901 return (-1);
1903 } else {
1904 if (file2 == NULL)
1905 snprintf(cmd, sizeof cmd, "get %s", dir);
1906 else
1907 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1908 file2);
1910 err = parse_dispatch_command(conn, cmd,
1911 &remote_path, 1);
1912 xfree(dir);
1913 xfree(remote_path);
1914 xfree(conn);
1915 return (err);
1917 xfree(dir);
1920 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1921 setvbuf(stdout, NULL, _IOLBF, 0);
1922 setvbuf(infile, NULL, _IOLBF, 0);
1923 #else
1924 setlinebuf(stdout);
1925 setlinebuf(infile);
1926 #endif
1928 interactive = !batchmode && isatty(STDIN_FILENO);
1929 err = 0;
1930 for (;;) {
1931 char *cp;
1933 signal(SIGINT, SIG_IGN);
1935 if (el == NULL) {
1936 if (interactive)
1937 printf("sftp> ");
1938 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1939 if (interactive)
1940 printf("\n");
1941 break;
1943 if (!interactive) { /* Echo command */
1944 printf("sftp> %s", cmd);
1945 if (strlen(cmd) > 0 &&
1946 cmd[strlen(cmd) - 1] != '\n')
1947 printf("\n");
1949 } else {
1950 #ifdef USE_LIBEDIT
1951 const char *line;
1952 int count = 0;
1954 if ((line = el_gets(el, &count)) == NULL ||
1955 count <= 0) {
1956 printf("\n");
1957 break;
1959 history(hl, &hev, H_ENTER, line);
1960 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1961 fprintf(stderr, "Error: input line too long\n");
1962 continue;
1964 #endif /* USE_LIBEDIT */
1967 cp = strrchr(cmd, '\n');
1968 if (cp)
1969 *cp = '\0';
1971 /* Handle user interrupts gracefully during commands */
1972 interrupted = 0;
1973 signal(SIGINT, cmd_interrupt);
1975 err = parse_dispatch_command(conn, cmd, &remote_path,
1976 batchmode);
1977 if (err != 0)
1978 break;
1980 xfree(remote_path);
1981 xfree(conn);
1983 #ifdef USE_LIBEDIT
1984 if (el != NULL)
1985 el_end(el);
1986 #endif /* USE_LIBEDIT */
1988 /* err == 1 signifies normal "quit" exit */
1989 return (err >= 0 ? 0 : -1);
1992 static void
1993 connect_to_server(char *path, char **args, int *in, int *out)
1995 int c_in, c_out;
1997 #ifdef USE_PIPES
1998 int pin[2], pout[2];
2000 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2001 fatal("pipe: %s", strerror(errno));
2002 *in = pin[0];
2003 *out = pout[1];
2004 c_in = pout[0];
2005 c_out = pin[1];
2006 #else /* USE_PIPES */
2007 int inout[2];
2009 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2010 fatal("socketpair: %s", strerror(errno));
2011 *in = *out = inout[0];
2012 c_in = c_out = inout[1];
2013 #endif /* USE_PIPES */
2015 if ((sshpid = fork()) == -1)
2016 fatal("fork: %s", strerror(errno));
2017 else if (sshpid == 0) {
2018 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2019 (dup2(c_out, STDOUT_FILENO) == -1)) {
2020 fprintf(stderr, "dup2: %s\n", strerror(errno));
2021 _exit(1);
2023 close(*in);
2024 close(*out);
2025 close(c_in);
2026 close(c_out);
2029 * The underlying ssh is in the same process group, so we must
2030 * ignore SIGINT if we want to gracefully abort commands,
2031 * otherwise the signal will make it to the ssh process and
2032 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2033 * underlying ssh, it must *not* ignore that signal.
2035 signal(SIGINT, SIG_IGN);
2036 signal(SIGTERM, SIG_DFL);
2037 execvp(path, args);
2038 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2039 _exit(1);
2042 signal(SIGTERM, killchild);
2043 signal(SIGINT, killchild);
2044 signal(SIGHUP, killchild);
2045 close(c_in);
2046 close(c_out);
2049 static void
2050 usage(void)
2052 extern char *__progname;
2054 fprintf(stderr,
2055 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2056 " [-D sftp_server_path] [-F ssh_config] "
2057 "[-i identity_file]\n"
2058 " [-o ssh_option] [-P port] [-R num_requests] "
2059 "[-S program]\n"
2060 " [-s subsystem | sftp_server] host\n"
2061 " %s [user@]host[:file ...]\n"
2062 " %s [user@]host[:dir[/]]\n"
2063 " %s -b batchfile [user@]host\n",
2064 __progname, __progname, __progname, __progname);
2065 exit(1);
2069 main(int argc, char **argv)
2071 int in, out, ch, err;
2072 char *host = NULL, *userhost, *cp, *file2 = NULL;
2073 int debug_level = 0, sshver = 2;
2074 char *file1 = NULL, *sftp_server = NULL;
2075 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2076 LogLevel ll = SYSLOG_LEVEL_INFO;
2077 arglist args;
2078 extern int optind;
2079 extern char *optarg;
2080 struct sftp_conn *conn;
2081 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2082 size_t num_requests = DEFAULT_NUM_REQUESTS;
2084 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2085 sanitise_stdfd();
2087 __progname = ssh_get_progname(argv[0]);
2088 memset(&args, '\0', sizeof(args));
2089 args.list = NULL;
2090 addargs(&args, "%s", ssh_program);
2091 addargs(&args, "-oForwardX11 no");
2092 addargs(&args, "-oForwardAgent no");
2093 addargs(&args, "-oPermitLocalCommand no");
2094 addargs(&args, "-oClearAllForwardings yes");
2096 ll = SYSLOG_LEVEL_INFO;
2097 infile = stdin;
2099 while ((ch = getopt(argc, argv,
2100 "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2101 switch (ch) {
2102 /* Passed through to ssh(1) */
2103 case '4':
2104 case '6':
2105 case 'C':
2106 addargs(&args, "-%c", ch);
2107 break;
2108 /* Passed through to ssh(1) with argument */
2109 case 'F':
2110 case 'c':
2111 case 'i':
2112 case 'o':
2113 addargs(&args, "-%c", ch);
2114 addargs(&args, "%s", optarg);
2115 break;
2116 case 'q':
2117 showprogress = 0;
2118 addargs(&args, "-%c", ch);
2119 break;
2120 case 'P':
2121 addargs(&args, "-oPort %s", optarg);
2122 break;
2123 case 'v':
2124 if (debug_level < 3) {
2125 addargs(&args, "-v");
2126 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2128 debug_level++;
2129 break;
2130 case '1':
2131 sshver = 1;
2132 if (sftp_server == NULL)
2133 sftp_server = _PATH_SFTP_SERVER;
2134 break;
2135 case '2':
2136 sshver = 2;
2137 break;
2138 case 'B':
2139 copy_buffer_len = strtol(optarg, &cp, 10);
2140 if (copy_buffer_len == 0 || *cp != '\0')
2141 fatal("Invalid buffer size \"%s\"", optarg);
2142 break;
2143 case 'b':
2144 if (batchmode)
2145 fatal("Batch file already specified.");
2147 /* Allow "-" as stdin */
2148 if (strcmp(optarg, "-") != 0 &&
2149 (infile = fopen(optarg, "r")) == NULL)
2150 fatal("%s (%s).", strerror(errno), optarg);
2151 showprogress = 0;
2152 batchmode = 1;
2153 addargs(&args, "-obatchmode yes");
2154 break;
2155 case 'p':
2156 global_pflag = 1;
2157 break;
2158 case 'D':
2159 sftp_direct = optarg;
2160 break;
2161 case 'r':
2162 global_rflag = 1;
2163 break;
2164 case 'R':
2165 num_requests = strtol(optarg, &cp, 10);
2166 if (num_requests == 0 || *cp != '\0')
2167 fatal("Invalid number of requests \"%s\"",
2168 optarg);
2169 break;
2170 case 's':
2171 sftp_server = optarg;
2172 break;
2173 case 'S':
2174 ssh_program = optarg;
2175 replacearg(&args, 0, "%s", ssh_program);
2176 break;
2177 case 'h':
2178 default:
2179 usage();
2183 if (!isatty(STDERR_FILENO))
2184 showprogress = 0;
2186 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2188 if (sftp_direct == NULL) {
2189 if (optind == argc || argc > (optind + 2))
2190 usage();
2192 userhost = xstrdup(argv[optind]);
2193 file2 = argv[optind+1];
2195 if ((host = strrchr(userhost, '@')) == NULL)
2196 host = userhost;
2197 else {
2198 *host++ = '\0';
2199 if (!userhost[0]) {
2200 fprintf(stderr, "Missing username\n");
2201 usage();
2203 addargs(&args, "-l");
2204 addargs(&args, "%s", userhost);
2207 if ((cp = colon(host)) != NULL) {
2208 *cp++ = '\0';
2209 file1 = cp;
2212 host = cleanhostname(host);
2213 if (!*host) {
2214 fprintf(stderr, "Missing hostname\n");
2215 usage();
2218 addargs(&args, "-oProtocol %d", sshver);
2220 /* no subsystem if the server-spec contains a '/' */
2221 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2222 addargs(&args, "-s");
2224 addargs(&args, "--");
2225 addargs(&args, "%s", host);
2226 addargs(&args, "%s", (sftp_server != NULL ?
2227 sftp_server : "sftp"));
2229 connect_to_server(ssh_program, args.list, &in, &out);
2230 } else {
2231 args.list = NULL;
2232 addargs(&args, "sftp-server");
2234 connect_to_server(sftp_direct, args.list, &in, &out);
2236 freeargs(&args);
2238 conn = do_init(in, out, copy_buffer_len, num_requests);
2239 if (conn == NULL)
2240 fatal("Couldn't initialise connection to server");
2242 if (!batchmode) {
2243 if (sftp_direct == NULL)
2244 fprintf(stderr, "Connected to %s.\n", host);
2245 else
2246 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2249 err = interactive_loop(conn, file1, file2);
2251 #if !defined(USE_PIPES)
2252 shutdown(in, SHUT_RDWR);
2253 shutdown(out, SHUT_RDWR);
2254 #endif
2256 close(in);
2257 close(out);
2258 if (batchmode)
2259 fclose(infile);
2261 while (waitpid(sshpid, NULL, 0) == -1)
2262 if (errno != EINTR)
2263 fatal("Couldn't wait for ssh process: %s",
2264 strerror(errno));
2266 exit(err == 0 ? 0 : 1);