sadly, two typos on one line is not my best record
[openssh-git.git] / sftp.c
blob7b4a852353e986b8d845e56e8f1615082e908b47
1 /* $OpenBSD: sftp.c,v 1.129 2010/09/26 22:26:33 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 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 if (quote == '\0' || tmp2[i] == quote) {
1730 if (el_insertstr(el, ins) == -1)
1731 fatal("el_insertstr "
1732 "failed.");
1733 break;
1735 /* FALLTHROUGH */
1736 default:
1737 if (el_insertstr(el, ins + 1) == -1)
1738 fatal("el_insertstr failed.");
1739 break;
1744 lf = el_line(el);
1745 if (g.gl_matchc == 1) {
1746 i = 0;
1747 if (!terminated)
1748 ins[i++] = quote;
1749 if (*(lf->cursor - 1) != '/' &&
1750 (lastarg || *(lf->cursor) != ' '))
1751 ins[i++] = ' ';
1752 ins[i] = '\0';
1753 if (i > 0 && el_insertstr(el, ins) == -1)
1754 fatal("el_insertstr failed.");
1756 xfree(tmp);
1758 out:
1759 globfree(&g);
1760 return g.gl_matchc;
1763 /* tab-completion hook function, called via libedit */
1764 static unsigned char
1765 complete(EditLine *el, int ch)
1767 char **argv, *line, quote;
1768 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1769 const LineInfo *lf;
1770 struct complete_ctx *complete_ctx;
1772 lf = el_line(el);
1773 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1774 fatal("%s: el_get failed", __func__);
1776 /* Figure out which argument the cursor points to */
1777 cursor = lf->cursor - lf->buffer;
1778 line = (char *)xmalloc(cursor + 1);
1779 memcpy(line, lf->buffer, cursor);
1780 line[cursor] = '\0';
1781 argv = makeargv(line, &carg, 1, &quote, &terminated);
1782 xfree(line);
1784 /* Get all the arguments on the line */
1785 len = lf->lastchar - lf->buffer;
1786 line = (char *)xmalloc(len + 1);
1787 memcpy(line, lf->buffer, len);
1788 line[len] = '\0';
1789 argv = makeargv(line, &argc, 1, NULL, NULL);
1791 /* Ensure cursor is at EOL or a argument boundary */
1792 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1793 line[cursor] != '\n') {
1794 xfree(line);
1795 return ret;
1798 if (carg == 0) {
1799 /* Show all available commands */
1800 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1801 ret = CC_REDISPLAY;
1802 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1803 /* Handle the command parsing */
1804 if (complete_cmd_parse(el, argv[0], argc == carg,
1805 quote, terminated) != 0)
1806 ret = CC_REDISPLAY;
1807 } else if (carg >= 1) {
1808 /* Handle file parsing */
1809 int remote = complete_is_remote(argv[0]);
1810 char *filematch = NULL;
1812 if (carg > 1 && line[cursor-1] != ' ')
1813 filematch = argv[carg - 1];
1815 if (remote != 0 &&
1816 complete_match(el, complete_ctx->conn,
1817 *complete_ctx->remote_pathp, filematch,
1818 remote, carg == argc, quote, terminated) != 0)
1819 ret = CC_REDISPLAY;
1822 xfree(line);
1823 return ret;
1825 #endif /* USE_LIBEDIT */
1828 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1830 char *remote_path;
1831 char *dir = NULL;
1832 char cmd[2048];
1833 int err, interactive;
1834 EditLine *el = NULL;
1835 #ifdef USE_LIBEDIT
1836 History *hl = NULL;
1837 HistEvent hev;
1838 extern char *__progname;
1839 struct complete_ctx complete_ctx;
1841 if (!batchmode && isatty(STDIN_FILENO)) {
1842 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1843 fatal("Couldn't initialise editline");
1844 if ((hl = history_init()) == NULL)
1845 fatal("Couldn't initialise editline history");
1846 history(hl, &hev, H_SETSIZE, 100);
1847 el_set(el, EL_HIST, history, hl);
1849 el_set(el, EL_PROMPT, prompt);
1850 el_set(el, EL_EDITOR, "emacs");
1851 el_set(el, EL_TERMINAL, NULL);
1852 el_set(el, EL_SIGNAL, 1);
1853 el_source(el, NULL);
1855 /* Tab Completion */
1856 el_set(el, EL_ADDFN, "ftp-complete",
1857 "Context senstive argument completion", complete);
1858 complete_ctx.conn = conn;
1859 complete_ctx.remote_pathp = &remote_path;
1860 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1861 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1863 #endif /* USE_LIBEDIT */
1865 remote_path = do_realpath(conn, ".");
1866 if (remote_path == NULL)
1867 fatal("Need cwd");
1869 if (file1 != NULL) {
1870 dir = xstrdup(file1);
1871 dir = make_absolute(dir, remote_path);
1873 if (remote_is_dir(conn, dir) && file2 == NULL) {
1874 printf("Changing to: %s\n", dir);
1875 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1876 if (parse_dispatch_command(conn, cmd,
1877 &remote_path, 1) != 0) {
1878 xfree(dir);
1879 xfree(remote_path);
1880 xfree(conn);
1881 return (-1);
1883 } else {
1884 if (file2 == NULL)
1885 snprintf(cmd, sizeof cmd, "get %s", dir);
1886 else
1887 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1888 file2);
1890 err = parse_dispatch_command(conn, cmd,
1891 &remote_path, 1);
1892 xfree(dir);
1893 xfree(remote_path);
1894 xfree(conn);
1895 return (err);
1897 xfree(dir);
1900 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1901 setvbuf(stdout, NULL, _IOLBF, 0);
1902 setvbuf(infile, NULL, _IOLBF, 0);
1903 #else
1904 setlinebuf(stdout);
1905 setlinebuf(infile);
1906 #endif
1908 interactive = !batchmode && isatty(STDIN_FILENO);
1909 err = 0;
1910 for (;;) {
1911 char *cp;
1913 signal(SIGINT, SIG_IGN);
1915 if (el == NULL) {
1916 if (interactive)
1917 printf("sftp> ");
1918 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1919 if (interactive)
1920 printf("\n");
1921 break;
1923 if (!interactive) { /* Echo command */
1924 printf("sftp> %s", cmd);
1925 if (strlen(cmd) > 0 &&
1926 cmd[strlen(cmd) - 1] != '\n')
1927 printf("\n");
1929 } else {
1930 #ifdef USE_LIBEDIT
1931 const char *line;
1932 int count = 0;
1934 if ((line = el_gets(el, &count)) == NULL ||
1935 count <= 0) {
1936 printf("\n");
1937 break;
1939 history(hl, &hev, H_ENTER, line);
1940 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1941 fprintf(stderr, "Error: input line too long\n");
1942 continue;
1944 #endif /* USE_LIBEDIT */
1947 cp = strrchr(cmd, '\n');
1948 if (cp)
1949 *cp = '\0';
1951 /* Handle user interrupts gracefully during commands */
1952 interrupted = 0;
1953 signal(SIGINT, cmd_interrupt);
1955 err = parse_dispatch_command(conn, cmd, &remote_path,
1956 batchmode);
1957 if (err != 0)
1958 break;
1960 xfree(remote_path);
1961 xfree(conn);
1963 #ifdef USE_LIBEDIT
1964 if (el != NULL)
1965 el_end(el);
1966 #endif /* USE_LIBEDIT */
1968 /* err == 1 signifies normal "quit" exit */
1969 return (err >= 0 ? 0 : -1);
1972 static void
1973 connect_to_server(char *path, char **args, int *in, int *out)
1975 int c_in, c_out;
1977 #ifdef USE_PIPES
1978 int pin[2], pout[2];
1980 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1981 fatal("pipe: %s", strerror(errno));
1982 *in = pin[0];
1983 *out = pout[1];
1984 c_in = pout[0];
1985 c_out = pin[1];
1986 #else /* USE_PIPES */
1987 int inout[2];
1989 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1990 fatal("socketpair: %s", strerror(errno));
1991 *in = *out = inout[0];
1992 c_in = c_out = inout[1];
1993 #endif /* USE_PIPES */
1995 if ((sshpid = fork()) == -1)
1996 fatal("fork: %s", strerror(errno));
1997 else if (sshpid == 0) {
1998 if ((dup2(c_in, STDIN_FILENO) == -1) ||
1999 (dup2(c_out, STDOUT_FILENO) == -1)) {
2000 fprintf(stderr, "dup2: %s\n", strerror(errno));
2001 _exit(1);
2003 close(*in);
2004 close(*out);
2005 close(c_in);
2006 close(c_out);
2009 * The underlying ssh is in the same process group, so we must
2010 * ignore SIGINT if we want to gracefully abort commands,
2011 * otherwise the signal will make it to the ssh process and
2012 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2013 * underlying ssh, it must *not* ignore that signal.
2015 signal(SIGINT, SIG_IGN);
2016 signal(SIGTERM, SIG_DFL);
2017 execvp(path, args);
2018 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2019 _exit(1);
2022 signal(SIGTERM, killchild);
2023 signal(SIGINT, killchild);
2024 signal(SIGHUP, killchild);
2025 close(c_in);
2026 close(c_out);
2029 static void
2030 usage(void)
2032 extern char *__progname;
2034 fprintf(stderr,
2035 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2036 " [-D sftp_server_path] [-F ssh_config] "
2037 "[-i identity_file] [-l limit]\n"
2038 " [-o ssh_option] [-P port] [-R num_requests] "
2039 "[-S program]\n"
2040 " [-s subsystem | sftp_server] host\n"
2041 " %s [user@]host[:file ...]\n"
2042 " %s [user@]host[:dir[/]]\n"
2043 " %s -b batchfile [user@]host\n",
2044 __progname, __progname, __progname, __progname);
2045 exit(1);
2049 main(int argc, char **argv)
2051 int in, out, ch, err;
2052 char *host = NULL, *userhost, *cp, *file2 = NULL;
2053 int debug_level = 0, sshver = 2;
2054 char *file1 = NULL, *sftp_server = NULL;
2055 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2056 const char *errstr;
2057 LogLevel ll = SYSLOG_LEVEL_INFO;
2058 arglist args;
2059 extern int optind;
2060 extern char *optarg;
2061 struct sftp_conn *conn;
2062 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2063 size_t num_requests = DEFAULT_NUM_REQUESTS;
2064 long long limit_kbps = 0;
2066 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2067 sanitise_stdfd();
2069 __progname = ssh_get_progname(argv[0]);
2070 memset(&args, '\0', sizeof(args));
2071 args.list = NULL;
2072 addargs(&args, "%s", ssh_program);
2073 addargs(&args, "-oForwardX11 no");
2074 addargs(&args, "-oForwardAgent no");
2075 addargs(&args, "-oPermitLocalCommand no");
2076 addargs(&args, "-oClearAllForwardings yes");
2078 ll = SYSLOG_LEVEL_INFO;
2079 infile = stdin;
2081 while ((ch = getopt(argc, argv,
2082 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2083 switch (ch) {
2084 /* Passed through to ssh(1) */
2085 case '4':
2086 case '6':
2087 case 'C':
2088 addargs(&args, "-%c", ch);
2089 break;
2090 /* Passed through to ssh(1) with argument */
2091 case 'F':
2092 case 'c':
2093 case 'i':
2094 case 'o':
2095 addargs(&args, "-%c", ch);
2096 addargs(&args, "%s", optarg);
2097 break;
2098 case 'q':
2099 showprogress = 0;
2100 addargs(&args, "-%c", ch);
2101 break;
2102 case 'P':
2103 addargs(&args, "-oPort %s", optarg);
2104 break;
2105 case 'v':
2106 if (debug_level < 3) {
2107 addargs(&args, "-v");
2108 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2110 debug_level++;
2111 break;
2112 case '1':
2113 sshver = 1;
2114 if (sftp_server == NULL)
2115 sftp_server = _PATH_SFTP_SERVER;
2116 break;
2117 case '2':
2118 sshver = 2;
2119 break;
2120 case 'B':
2121 copy_buffer_len = strtol(optarg, &cp, 10);
2122 if (copy_buffer_len == 0 || *cp != '\0')
2123 fatal("Invalid buffer size \"%s\"", optarg);
2124 break;
2125 case 'b':
2126 if (batchmode)
2127 fatal("Batch file already specified.");
2129 /* Allow "-" as stdin */
2130 if (strcmp(optarg, "-") != 0 &&
2131 (infile = fopen(optarg, "r")) == NULL)
2132 fatal("%s (%s).", strerror(errno), optarg);
2133 showprogress = 0;
2134 batchmode = 1;
2135 addargs(&args, "-obatchmode yes");
2136 break;
2137 case 'p':
2138 global_pflag = 1;
2139 break;
2140 case 'D':
2141 sftp_direct = optarg;
2142 break;
2143 case 'l':
2144 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2145 &errstr);
2146 if (errstr != NULL)
2147 usage();
2148 limit_kbps *= 1024; /* kbps */
2149 break;
2150 case 'r':
2151 global_rflag = 1;
2152 break;
2153 case 'R':
2154 num_requests = strtol(optarg, &cp, 10);
2155 if (num_requests == 0 || *cp != '\0')
2156 fatal("Invalid number of requests \"%s\"",
2157 optarg);
2158 break;
2159 case 's':
2160 sftp_server = optarg;
2161 break;
2162 case 'S':
2163 ssh_program = optarg;
2164 replacearg(&args, 0, "%s", ssh_program);
2165 break;
2166 case 'h':
2167 default:
2168 usage();
2172 if (!isatty(STDERR_FILENO))
2173 showprogress = 0;
2175 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2177 if (sftp_direct == NULL) {
2178 if (optind == argc || argc > (optind + 2))
2179 usage();
2181 userhost = xstrdup(argv[optind]);
2182 file2 = argv[optind+1];
2184 if ((host = strrchr(userhost, '@')) == NULL)
2185 host = userhost;
2186 else {
2187 *host++ = '\0';
2188 if (!userhost[0]) {
2189 fprintf(stderr, "Missing username\n");
2190 usage();
2192 addargs(&args, "-l");
2193 addargs(&args, "%s", userhost);
2196 if ((cp = colon(host)) != NULL) {
2197 *cp++ = '\0';
2198 file1 = cp;
2201 host = cleanhostname(host);
2202 if (!*host) {
2203 fprintf(stderr, "Missing hostname\n");
2204 usage();
2207 addargs(&args, "-oProtocol %d", sshver);
2209 /* no subsystem if the server-spec contains a '/' */
2210 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2211 addargs(&args, "-s");
2213 addargs(&args, "--");
2214 addargs(&args, "%s", host);
2215 addargs(&args, "%s", (sftp_server != NULL ?
2216 sftp_server : "sftp"));
2218 connect_to_server(ssh_program, args.list, &in, &out);
2219 } else {
2220 args.list = NULL;
2221 addargs(&args, "sftp-server");
2223 connect_to_server(sftp_direct, args.list, &in, &out);
2225 freeargs(&args);
2227 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2228 if (conn == NULL)
2229 fatal("Couldn't initialise connection to server");
2231 if (!batchmode) {
2232 if (sftp_direct == NULL)
2233 fprintf(stderr, "Connected to %s.\n", host);
2234 else
2235 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2238 err = interactive_loop(conn, file1, file2);
2240 #if !defined(USE_PIPES)
2241 shutdown(in, SHUT_RDWR);
2242 shutdown(out, SHUT_RDWR);
2243 #endif
2245 close(in);
2246 close(out);
2247 if (batchmode)
2248 fclose(infile);
2250 while (waitpid(sshpid, NULL, 0) == -1)
2251 if (errno != EINTR)
2252 fatal("Couldn't wait for ssh process: %s",
2253 strerror(errno));
2255 exit(err == 0 ? 0 : 1);