- jmc@cvs.openbsd.org 2010/01/13 12:48:34
[openssh-git.git] / sftp.c
blobac6ac9fed3a2c766f20ff1412165f1d96c1cbe7f
1 /* $OpenBSD: sftp.c,v 1.121 2010/01/13 12:48:34 jmc 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 { "progress", I_PROGRESS, NOARGS },
185 { "put", I_PUT, LOCAL },
186 { "pwd", I_PWD, REMOTE },
187 { "quit", I_QUIT, NOARGS },
188 { "rename", I_RENAME, REMOTE },
189 { "rm", I_RM, REMOTE },
190 { "rmdir", I_RMDIR, REMOTE },
191 { "symlink", I_SYMLINK, REMOTE },
192 { "version", I_VERSION, NOARGS },
193 { "!", I_SHELL, NOARGS },
194 { "?", I_HELP, NOARGS },
195 { NULL, -1, -1 }
198 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
200 /* ARGSUSED */
201 static void
202 killchild(int signo)
204 if (sshpid > 1) {
205 kill(sshpid, SIGTERM);
206 waitpid(sshpid, NULL, 0);
209 _exit(1);
212 /* ARGSUSED */
213 static void
214 cmd_interrupt(int signo)
216 const char msg[] = "\rInterrupt \n";
217 int olderrno = errno;
219 write(STDERR_FILENO, msg, sizeof(msg) - 1);
220 interrupted = 1;
221 errno = olderrno;
224 static void
225 help(void)
227 printf("Available commands:\n"
228 "bye Quit sftp\n"
229 "cd path Change remote directory to 'path'\n"
230 "chgrp grp path Change group of file 'path' to 'grp'\n"
231 "chmod mode path Change permissions of file 'path' to 'mode'\n"
232 "chown own path Change owner of file 'path' to 'own'\n"
233 "df [-hi] [path] Display statistics for current directory or\n"
234 " filesystem containing 'path'\n"
235 "exit Quit sftp\n"
236 "get [-Ppr] remote [local] Download file\n"
237 "help Display this help text\n"
238 "lcd path Change local directory to 'path'\n"
239 "lls [ls-options [path]] Display local directory listing\n"
240 "lmkdir path Create local directory\n"
241 "ln oldpath newpath Symlink remote file\n"
242 "lpwd Print local working directory\n"
243 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
244 "lumask umask Set local umask to 'umask'\n"
245 "mkdir path Create remote directory\n"
246 "progress Toggle display of progress meter\n"
247 "put [-Ppr] local [remote] Upload file\n"
248 "pwd Display remote working directory\n"
249 "quit Quit sftp\n"
250 "rename oldpath newpath Rename remote file\n"
251 "rm path Delete remote file\n"
252 "rmdir path Remove remote directory\n"
253 "symlink oldpath newpath Symlink remote file\n"
254 "version Show SFTP version\n"
255 "!command Execute 'command' in local shell\n"
256 "! Escape to local shell\n"
257 "? Synonym for help\n");
260 static void
261 local_do_shell(const char *args)
263 int status;
264 char *shell;
265 pid_t pid;
267 if (!*args)
268 args = NULL;
270 if ((shell = getenv("SHELL")) == NULL)
271 shell = _PATH_BSHELL;
273 if ((pid = fork()) == -1)
274 fatal("Couldn't fork: %s", strerror(errno));
276 if (pid == 0) {
277 /* XXX: child has pipe fds to ssh subproc open - issue? */
278 if (args) {
279 debug3("Executing %s -c \"%s\"", shell, args);
280 execl(shell, shell, "-c", args, (char *)NULL);
281 } else {
282 debug3("Executing %s", shell);
283 execl(shell, shell, (char *)NULL);
285 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
286 strerror(errno));
287 _exit(1);
289 while (waitpid(pid, &status, 0) == -1)
290 if (errno != EINTR)
291 fatal("Couldn't wait for child: %s", strerror(errno));
292 if (!WIFEXITED(status))
293 error("Shell exited abnormally");
294 else if (WEXITSTATUS(status))
295 error("Shell exited with status %d", WEXITSTATUS(status));
298 static void
299 local_do_ls(const char *args)
301 if (!args || !*args)
302 local_do_shell(_PATH_LS);
303 else {
304 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
305 char *buf = xmalloc(len);
307 /* XXX: quoting - rip quoting code from ftp? */
308 snprintf(buf, len, _PATH_LS " %s", args);
309 local_do_shell(buf);
310 xfree(buf);
314 /* Strip one path (usually the pwd) from the start of another */
315 static char *
316 path_strip(char *path, char *strip)
318 size_t len;
320 if (strip == NULL)
321 return (xstrdup(path));
323 len = strlen(strip);
324 if (strncmp(path, strip, len) == 0) {
325 if (strip[len - 1] != '/' && path[len] == '/')
326 len++;
327 return (xstrdup(path + len));
330 return (xstrdup(path));
333 static char *
334 make_absolute(char *p, char *pwd)
336 char *abs_str;
338 /* Derelativise */
339 if (p && p[0] != '/') {
340 abs_str = path_append(pwd, p);
341 xfree(p);
342 return(abs_str);
343 } else
344 return(p);
347 static int
348 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
349 int *rflag)
351 extern int opterr, optind, optopt, optreset;
352 int ch;
354 optind = optreset = 1;
355 opterr = 0;
357 *rflag = *pflag = 0;
358 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
359 switch (ch) {
360 case 'p':
361 case 'P':
362 *pflag = 1;
363 break;
364 case 'r':
365 case 'R':
366 *rflag = 1;
367 break;
368 default:
369 error("%s: Invalid flag -%c", cmd, optopt);
370 return -1;
374 return optind;
377 static int
378 parse_ls_flags(char **argv, int argc, int *lflag)
380 extern int opterr, optind, optopt, optreset;
381 int ch;
383 optind = optreset = 1;
384 opterr = 0;
386 *lflag = LS_NAME_SORT;
387 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
388 switch (ch) {
389 case '1':
390 *lflag &= ~VIEW_FLAGS;
391 *lflag |= LS_SHORT_VIEW;
392 break;
393 case 'S':
394 *lflag &= ~SORT_FLAGS;
395 *lflag |= LS_SIZE_SORT;
396 break;
397 case 'a':
398 *lflag |= LS_SHOW_ALL;
399 break;
400 case 'f':
401 *lflag &= ~SORT_FLAGS;
402 break;
403 case 'h':
404 *lflag |= LS_SI_UNITS;
405 break;
406 case 'l':
407 *lflag &= ~LS_SHORT_VIEW;
408 *lflag |= LS_LONG_VIEW;
409 break;
410 case 'n':
411 *lflag &= ~LS_SHORT_VIEW;
412 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
413 break;
414 case 'r':
415 *lflag |= LS_REVERSE_SORT;
416 break;
417 case 't':
418 *lflag &= ~SORT_FLAGS;
419 *lflag |= LS_TIME_SORT;
420 break;
421 default:
422 error("ls: Invalid flag -%c", optopt);
423 return -1;
427 return optind;
430 static int
431 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
433 extern int opterr, optind, optopt, optreset;
434 int ch;
436 optind = optreset = 1;
437 opterr = 0;
439 *hflag = *iflag = 0;
440 while ((ch = getopt(argc, argv, "hi")) != -1) {
441 switch (ch) {
442 case 'h':
443 *hflag = 1;
444 break;
445 case 'i':
446 *iflag = 1;
447 break;
448 default:
449 error("%s: Invalid flag -%c", cmd, optopt);
450 return -1;
454 return optind;
457 static int
458 is_dir(char *path)
460 struct stat sb;
462 /* XXX: report errors? */
463 if (stat(path, &sb) == -1)
464 return(0);
466 return(S_ISDIR(sb.st_mode));
469 static int
470 remote_is_dir(struct sftp_conn *conn, char *path)
472 Attrib *a;
474 /* XXX: report errors? */
475 if ((a = do_stat(conn, path, 1)) == NULL)
476 return(0);
477 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
478 return(0);
479 return(S_ISDIR(a->perm));
482 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
483 static int
484 pathname_is_dir(char *pathname)
486 size_t l = strlen(pathname);
488 return l > 0 && pathname[l - 1] == '/';
491 static int
492 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
493 int pflag, int rflag)
495 char *abs_src = NULL;
496 char *abs_dst = NULL;
497 glob_t g;
498 char *filename, *tmp=NULL;
499 int i, err = 0;
501 abs_src = xstrdup(src);
502 abs_src = make_absolute(abs_src, pwd);
503 memset(&g, 0, sizeof(g));
505 debug3("Looking up %s", abs_src);
506 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
507 error("File \"%s\" not found.", abs_src);
508 err = -1;
509 goto out;
513 * If multiple matches then dst must be a directory or
514 * unspecified.
516 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
517 error("Multiple source paths, but destination "
518 "\"%s\" is not a directory", dst);
519 err = -1;
520 goto out;
523 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
524 tmp = xstrdup(g.gl_pathv[i]);
525 if ((filename = basename(tmp)) == NULL) {
526 error("basename %s: %s", tmp, strerror(errno));
527 xfree(tmp);
528 err = -1;
529 goto out;
532 if (g.gl_matchc == 1 && dst) {
533 if (is_dir(dst)) {
534 abs_dst = path_append(dst, filename);
535 } else {
536 abs_dst = xstrdup(dst);
538 } else if (dst) {
539 abs_dst = path_append(dst, filename);
540 } else {
541 abs_dst = xstrdup(filename);
543 xfree(tmp);
545 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
546 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
547 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
548 pflag || global_pflag, 1) == -1)
549 err = -1;
550 } else {
551 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
552 pflag || global_pflag) == -1)
553 err = -1;
555 xfree(abs_dst);
556 abs_dst = NULL;
559 out:
560 xfree(abs_src);
561 globfree(&g);
562 return(err);
565 static int
566 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
567 int pflag, int rflag)
569 char *tmp_dst = NULL;
570 char *abs_dst = NULL;
571 char *tmp = NULL, *filename = NULL;
572 glob_t g;
573 int err = 0;
574 int i, dst_is_dir = 1;
575 struct stat sb;
577 if (dst) {
578 tmp_dst = xstrdup(dst);
579 tmp_dst = make_absolute(tmp_dst, pwd);
582 memset(&g, 0, sizeof(g));
583 debug3("Looking up %s", src);
584 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
585 error("File \"%s\" not found.", src);
586 err = -1;
587 goto out;
590 /* If we aren't fetching to pwd then stash this status for later */
591 if (tmp_dst != NULL)
592 dst_is_dir = remote_is_dir(conn, tmp_dst);
594 /* If multiple matches, dst may be directory or unspecified */
595 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
596 error("Multiple paths match, but destination "
597 "\"%s\" is not a directory", tmp_dst);
598 err = -1;
599 goto out;
602 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
603 if (stat(g.gl_pathv[i], &sb) == -1) {
604 err = -1;
605 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
606 continue;
609 tmp = xstrdup(g.gl_pathv[i]);
610 if ((filename = basename(tmp)) == NULL) {
611 error("basename %s: %s", tmp, strerror(errno));
612 xfree(tmp);
613 err = -1;
614 goto out;
617 if (g.gl_matchc == 1 && tmp_dst) {
618 /* If directory specified, append filename */
619 if (dst_is_dir)
620 abs_dst = path_append(tmp_dst, filename);
621 else
622 abs_dst = xstrdup(tmp_dst);
623 } else if (tmp_dst) {
624 abs_dst = path_append(tmp_dst, filename);
625 } else {
626 abs_dst = make_absolute(xstrdup(filename), pwd);
628 xfree(tmp);
630 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
631 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
632 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
633 pflag || global_pflag, 1) == -1)
634 err = -1;
635 } else {
636 if (do_upload(conn, g.gl_pathv[i], abs_dst,
637 pflag || global_pflag) == -1)
638 err = -1;
642 out:
643 if (abs_dst)
644 xfree(abs_dst);
645 if (tmp_dst)
646 xfree(tmp_dst);
647 globfree(&g);
648 return(err);
651 static int
652 sdirent_comp(const void *aa, const void *bb)
654 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
655 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
656 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
658 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
659 if (sort_flag & LS_NAME_SORT)
660 return (rmul * strcmp(a->filename, b->filename));
661 else if (sort_flag & LS_TIME_SORT)
662 return (rmul * NCMP(a->a.mtime, b->a.mtime));
663 else if (sort_flag & LS_SIZE_SORT)
664 return (rmul * NCMP(a->a.size, b->a.size));
666 fatal("Unknown ls sort type");
669 /* sftp ls.1 replacement for directories */
670 static int
671 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
673 int n;
674 u_int c = 1, colspace = 0, columns = 1;
675 SFTP_DIRENT **d;
677 if ((n = do_readdir(conn, path, &d)) != 0)
678 return (n);
680 if (!(lflag & LS_SHORT_VIEW)) {
681 u_int m = 0, width = 80;
682 struct winsize ws;
683 char *tmp;
685 /* Count entries for sort and find longest filename */
686 for (n = 0; d[n] != NULL; n++) {
687 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
688 m = MAX(m, strlen(d[n]->filename));
691 /* Add any subpath that also needs to be counted */
692 tmp = path_strip(path, strip_path);
693 m += strlen(tmp);
694 xfree(tmp);
696 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
697 width = ws.ws_col;
699 columns = width / (m + 2);
700 columns = MAX(columns, 1);
701 colspace = width / columns;
702 colspace = MIN(colspace, width);
705 if (lflag & SORT_FLAGS) {
706 for (n = 0; d[n] != NULL; n++)
707 ; /* count entries */
708 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
709 qsort(d, n, sizeof(*d), sdirent_comp);
712 for (n = 0; d[n] != NULL && !interrupted; n++) {
713 char *tmp, *fname;
715 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
716 continue;
718 tmp = path_append(path, d[n]->filename);
719 fname = path_strip(tmp, strip_path);
720 xfree(tmp);
722 if (lflag & LS_LONG_VIEW) {
723 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
724 char *lname;
725 struct stat sb;
727 memset(&sb, 0, sizeof(sb));
728 attrib_to_stat(&d[n]->a, &sb);
729 lname = ls_file(fname, &sb, 1,
730 (lflag & LS_SI_UNITS));
731 printf("%s\n", lname);
732 xfree(lname);
733 } else
734 printf("%s\n", d[n]->longname);
735 } else {
736 printf("%-*s", colspace, fname);
737 if (c >= columns) {
738 printf("\n");
739 c = 1;
740 } else
741 c++;
744 xfree(fname);
747 if (!(lflag & LS_LONG_VIEW) && (c != 1))
748 printf("\n");
750 free_sftp_dirents(d);
751 return (0);
754 /* sftp ls.1 replacement which handles path globs */
755 static int
756 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
757 int lflag)
759 glob_t g;
760 u_int i, c = 1, colspace = 0, columns = 1;
761 Attrib *a = NULL;
763 memset(&g, 0, sizeof(g));
765 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
766 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
767 if (g.gl_pathc)
768 globfree(&g);
769 error("Can't ls: \"%s\" not found", path);
770 return (-1);
773 if (interrupted)
774 goto out;
777 * If the glob returns a single match and it is a directory,
778 * then just list its contents.
780 if (g.gl_matchc == 1) {
781 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
782 globfree(&g);
783 return (-1);
785 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
786 S_ISDIR(a->perm)) {
787 int err;
789 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
790 globfree(&g);
791 return (err);
795 if (!(lflag & LS_SHORT_VIEW)) {
796 u_int m = 0, width = 80;
797 struct winsize ws;
799 /* Count entries for sort and find longest filename */
800 for (i = 0; g.gl_pathv[i]; i++)
801 m = MAX(m, strlen(g.gl_pathv[i]));
803 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
804 width = ws.ws_col;
806 columns = width / (m + 2);
807 columns = MAX(columns, 1);
808 colspace = width / columns;
811 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
812 char *fname;
814 fname = path_strip(g.gl_pathv[i], strip_path);
816 if (lflag & LS_LONG_VIEW) {
817 char *lname;
818 struct stat sb;
821 * XXX: this is slow - 1 roundtrip per path
822 * A solution to this is to fork glob() and
823 * build a sftp specific version which keeps the
824 * attribs (which currently get thrown away)
825 * that the server returns as well as the filenames.
827 memset(&sb, 0, sizeof(sb));
828 if (a == NULL)
829 a = do_lstat(conn, g.gl_pathv[i], 1);
830 if (a != NULL)
831 attrib_to_stat(a, &sb);
832 lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS));
833 printf("%s\n", lname);
834 xfree(lname);
835 } else {
836 printf("%-*s", colspace, fname);
837 if (c >= columns) {
838 printf("\n");
839 c = 1;
840 } else
841 c++;
843 xfree(fname);
846 if (!(lflag & LS_LONG_VIEW) && (c != 1))
847 printf("\n");
849 out:
850 if (g.gl_pathc)
851 globfree(&g);
853 return (0);
856 static int
857 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
859 struct sftp_statvfs st;
860 char s_used[FMT_SCALED_STRSIZE];
861 char s_avail[FMT_SCALED_STRSIZE];
862 char s_root[FMT_SCALED_STRSIZE];
863 char s_total[FMT_SCALED_STRSIZE];
864 unsigned long long ffree;
866 if (do_statvfs(conn, path, &st, 1) == -1)
867 return -1;
868 if (iflag) {
869 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
870 printf(" Inodes Used Avail "
871 "(root) %%Capacity\n");
872 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
873 (unsigned long long)st.f_files,
874 (unsigned long long)(st.f_files - st.f_ffree),
875 (unsigned long long)st.f_favail,
876 (unsigned long long)st.f_ffree, ffree);
877 } else if (hflag) {
878 strlcpy(s_used, "error", sizeof(s_used));
879 strlcpy(s_avail, "error", sizeof(s_avail));
880 strlcpy(s_root, "error", sizeof(s_root));
881 strlcpy(s_total, "error", sizeof(s_total));
882 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
883 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
884 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
885 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
886 printf(" Size Used Avail (root) %%Capacity\n");
887 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
888 s_total, s_used, s_avail, s_root,
889 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
890 st.f_blocks));
891 } else {
892 printf(" Size Used Avail "
893 "(root) %%Capacity\n");
894 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
895 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
896 (unsigned long long)(st.f_frsize *
897 (st.f_blocks - st.f_bfree) / 1024),
898 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
899 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
900 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
901 st.f_blocks));
903 return 0;
907 * Undo escaping of glob sequences in place. Used to undo extra escaping
908 * applied in makeargv() when the string is destined for a function that
909 * does not glob it.
911 static void
912 undo_glob_escape(char *s)
914 size_t i, j;
916 for (i = j = 0;;) {
917 if (s[i] == '\0') {
918 s[j] = '\0';
919 return;
921 if (s[i] != '\\') {
922 s[j++] = s[i++];
923 continue;
925 /* s[i] == '\\' */
926 ++i;
927 switch (s[i]) {
928 case '?':
929 case '[':
930 case '*':
931 case '\\':
932 s[j++] = s[i++];
933 break;
934 case '\0':
935 s[j++] = '\\';
936 s[j] = '\0';
937 return;
938 default:
939 s[j++] = '\\';
940 s[j++] = s[i++];
941 break;
947 * Split a string into an argument vector using sh(1)-style quoting,
948 * comment and escaping rules, but with some tweaks to handle glob(3)
949 * wildcards.
950 * The "sloppy" flag allows for recovery from missing terminating quote, for
951 * use in parsing incomplete commandlines during tab autocompletion.
953 * Returns NULL on error or a NULL-terminated array of arguments.
955 * If "lastquote" is not NULL, the quoting character used for the last
956 * argument is placed in *lastquote ("\0", "'" or "\"").
958 * If "terminated" is not NULL, *terminated will be set to 1 when the
959 * last argument's quote has been properly terminated or 0 otherwise.
960 * This parameter is only of use if "sloppy" is set.
962 #define MAXARGS 128
963 #define MAXARGLEN 8192
964 static char **
965 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
966 u_int *terminated)
968 int argc, quot;
969 size_t i, j;
970 static char argvs[MAXARGLEN];
971 static char *argv[MAXARGS + 1];
972 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
974 *argcp = argc = 0;
975 if (strlen(arg) > sizeof(argvs) - 1) {
976 args_too_longs:
977 error("string too long");
978 return NULL;
980 if (terminated != NULL)
981 *terminated = 1;
982 if (lastquote != NULL)
983 *lastquote = '\0';
984 state = MA_START;
985 i = j = 0;
986 for (;;) {
987 if (isspace(arg[i])) {
988 if (state == MA_UNQUOTED) {
989 /* Terminate current argument */
990 argvs[j++] = '\0';
991 argc++;
992 state = MA_START;
993 } else if (state != MA_START)
994 argvs[j++] = arg[i];
995 } else if (arg[i] == '"' || arg[i] == '\'') {
996 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
997 if (state == MA_START) {
998 argv[argc] = argvs + j;
999 state = q;
1000 if (lastquote != NULL)
1001 *lastquote = arg[i];
1002 } else if (state == MA_UNQUOTED)
1003 state = q;
1004 else if (state == q)
1005 state = MA_UNQUOTED;
1006 else
1007 argvs[j++] = arg[i];
1008 } else if (arg[i] == '\\') {
1009 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1010 quot = state == MA_SQUOTE ? '\'' : '"';
1011 /* Unescape quote we are in */
1012 /* XXX support \n and friends? */
1013 if (arg[i + 1] == quot) {
1014 i++;
1015 argvs[j++] = arg[i];
1016 } else if (arg[i + 1] == '?' ||
1017 arg[i + 1] == '[' || arg[i + 1] == '*') {
1019 * Special case for sftp: append
1020 * double-escaped glob sequence -
1021 * glob will undo one level of
1022 * escaping. NB. string can grow here.
1024 if (j >= sizeof(argvs) - 5)
1025 goto args_too_longs;
1026 argvs[j++] = '\\';
1027 argvs[j++] = arg[i++];
1028 argvs[j++] = '\\';
1029 argvs[j++] = arg[i];
1030 } else {
1031 argvs[j++] = arg[i++];
1032 argvs[j++] = arg[i];
1034 } else {
1035 if (state == MA_START) {
1036 argv[argc] = argvs + j;
1037 state = MA_UNQUOTED;
1038 if (lastquote != NULL)
1039 *lastquote = '\0';
1041 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1042 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1044 * Special case for sftp: append
1045 * escaped glob sequence -
1046 * glob will undo one level of
1047 * escaping.
1049 argvs[j++] = arg[i++];
1050 argvs[j++] = arg[i];
1051 } else {
1052 /* Unescape everything */
1053 /* XXX support \n and friends? */
1054 i++;
1055 argvs[j++] = arg[i];
1058 } else if (arg[i] == '#') {
1059 if (state == MA_SQUOTE || state == MA_DQUOTE)
1060 argvs[j++] = arg[i];
1061 else
1062 goto string_done;
1063 } else if (arg[i] == '\0') {
1064 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1065 if (sloppy) {
1066 state = MA_UNQUOTED;
1067 if (terminated != NULL)
1068 *terminated = 0;
1069 goto string_done;
1071 error("Unterminated quoted argument");
1072 return NULL;
1074 string_done:
1075 if (state == MA_UNQUOTED) {
1076 argvs[j++] = '\0';
1077 argc++;
1079 break;
1080 } else {
1081 if (state == MA_START) {
1082 argv[argc] = argvs + j;
1083 state = MA_UNQUOTED;
1084 if (lastquote != NULL)
1085 *lastquote = '\0';
1087 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1088 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1090 * Special case for sftp: escape quoted
1091 * glob(3) wildcards. NB. string can grow
1092 * here.
1094 if (j >= sizeof(argvs) - 3)
1095 goto args_too_longs;
1096 argvs[j++] = '\\';
1097 argvs[j++] = arg[i];
1098 } else
1099 argvs[j++] = arg[i];
1101 i++;
1103 *argcp = argc;
1104 return argv;
1107 static int
1108 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1109 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1111 const char *cmd, *cp = *cpp;
1112 char *cp2, **argv;
1113 int base = 0;
1114 long l;
1115 int i, cmdnum, optidx, argc;
1117 /* Skip leading whitespace */
1118 cp = cp + strspn(cp, WHITESPACE);
1120 /* Check for leading '-' (disable error processing) */
1121 *iflag = 0;
1122 if (*cp == '-') {
1123 *iflag = 1;
1124 cp++;
1125 cp = cp + strspn(cp, WHITESPACE);
1128 /* Ignore blank lines and lines which begin with comment '#' char */
1129 if (*cp == '\0' || *cp == '#')
1130 return (0);
1132 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1133 return -1;
1135 /* Figure out which command we have */
1136 for (i = 0; cmds[i].c != NULL; i++) {
1137 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1138 break;
1140 cmdnum = cmds[i].n;
1141 cmd = cmds[i].c;
1143 /* Special case */
1144 if (*cp == '!') {
1145 cp++;
1146 cmdnum = I_SHELL;
1147 } else if (cmdnum == -1) {
1148 error("Invalid command.");
1149 return -1;
1152 /* Get arguments and parse flags */
1153 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1154 *path1 = *path2 = NULL;
1155 optidx = 1;
1156 switch (cmdnum) {
1157 case I_GET:
1158 case I_PUT:
1159 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1160 return -1;
1161 /* Get first pathname (mandatory) */
1162 if (argc - optidx < 1) {
1163 error("You must specify at least one path after a "
1164 "%s command.", cmd);
1165 return -1;
1167 *path1 = xstrdup(argv[optidx]);
1168 /* Get second pathname (optional) */
1169 if (argc - optidx > 1) {
1170 *path2 = xstrdup(argv[optidx + 1]);
1171 /* Destination is not globbed */
1172 undo_glob_escape(*path2);
1174 break;
1175 case I_RENAME:
1176 case I_SYMLINK:
1177 if (argc - optidx < 2) {
1178 error("You must specify two paths after a %s "
1179 "command.", cmd);
1180 return -1;
1182 *path1 = xstrdup(argv[optidx]);
1183 *path2 = xstrdup(argv[optidx + 1]);
1184 /* Paths are not globbed */
1185 undo_glob_escape(*path1);
1186 undo_glob_escape(*path2);
1187 break;
1188 case I_RM:
1189 case I_MKDIR:
1190 case I_RMDIR:
1191 case I_CHDIR:
1192 case I_LCHDIR:
1193 case I_LMKDIR:
1194 /* Get pathname (mandatory) */
1195 if (argc - optidx < 1) {
1196 error("You must specify a path after a %s command.",
1197 cmd);
1198 return -1;
1200 *path1 = xstrdup(argv[optidx]);
1201 /* Only "rm" globs */
1202 if (cmdnum != I_RM)
1203 undo_glob_escape(*path1);
1204 break;
1205 case I_DF:
1206 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1207 iflag)) == -1)
1208 return -1;
1209 /* Default to current directory if no path specified */
1210 if (argc - optidx < 1)
1211 *path1 = NULL;
1212 else {
1213 *path1 = xstrdup(argv[optidx]);
1214 undo_glob_escape(*path1);
1216 break;
1217 case I_LS:
1218 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1219 return(-1);
1220 /* Path is optional */
1221 if (argc - optidx > 0)
1222 *path1 = xstrdup(argv[optidx]);
1223 break;
1224 case I_LLS:
1225 /* Skip ls command and following whitespace */
1226 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1227 case I_SHELL:
1228 /* Uses the rest of the line */
1229 break;
1230 case I_LUMASK:
1231 case I_CHMOD:
1232 base = 8;
1233 case I_CHOWN:
1234 case I_CHGRP:
1235 /* Get numeric arg (mandatory) */
1236 if (argc - optidx < 1)
1237 goto need_num_arg;
1238 errno = 0;
1239 l = strtol(argv[optidx], &cp2, base);
1240 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1241 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1242 l < 0) {
1243 need_num_arg:
1244 error("You must supply a numeric argument "
1245 "to the %s command.", cmd);
1246 return -1;
1248 *n_arg = l;
1249 if (cmdnum == I_LUMASK)
1250 break;
1251 /* Get pathname (mandatory) */
1252 if (argc - optidx < 2) {
1253 error("You must specify a path after a %s command.",
1254 cmd);
1255 return -1;
1257 *path1 = xstrdup(argv[optidx + 1]);
1258 break;
1259 case I_QUIT:
1260 case I_PWD:
1261 case I_LPWD:
1262 case I_HELP:
1263 case I_VERSION:
1264 case I_PROGRESS:
1265 break;
1266 default:
1267 fatal("Command not implemented");
1270 *cpp = cp;
1271 return(cmdnum);
1274 static int
1275 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1276 int err_abort)
1278 char *path1, *path2, *tmp;
1279 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1280 unsigned long n_arg = 0;
1281 Attrib a, *aa;
1282 char path_buf[MAXPATHLEN];
1283 int err = 0;
1284 glob_t g;
1286 path1 = path2 = NULL;
1287 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1288 &path1, &path2);
1290 if (iflag != 0)
1291 err_abort = 0;
1293 memset(&g, 0, sizeof(g));
1295 /* Perform command */
1296 switch (cmdnum) {
1297 case 0:
1298 /* Blank line */
1299 break;
1300 case -1:
1301 /* Unrecognized command */
1302 err = -1;
1303 break;
1304 case I_GET:
1305 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1306 break;
1307 case I_PUT:
1308 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1309 break;
1310 case I_RENAME:
1311 path1 = make_absolute(path1, *pwd);
1312 path2 = make_absolute(path2, *pwd);
1313 err = do_rename(conn, path1, path2);
1314 break;
1315 case I_SYMLINK:
1316 path2 = make_absolute(path2, *pwd);
1317 err = do_symlink(conn, path1, path2);
1318 break;
1319 case I_RM:
1320 path1 = make_absolute(path1, *pwd);
1321 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1322 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1323 printf("Removing %s\n", g.gl_pathv[i]);
1324 err = do_rm(conn, g.gl_pathv[i]);
1325 if (err != 0 && err_abort)
1326 break;
1328 break;
1329 case I_MKDIR:
1330 path1 = make_absolute(path1, *pwd);
1331 attrib_clear(&a);
1332 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1333 a.perm = 0777;
1334 err = do_mkdir(conn, path1, &a, 1);
1335 break;
1336 case I_RMDIR:
1337 path1 = make_absolute(path1, *pwd);
1338 err = do_rmdir(conn, path1);
1339 break;
1340 case I_CHDIR:
1341 path1 = make_absolute(path1, *pwd);
1342 if ((tmp = do_realpath(conn, path1)) == NULL) {
1343 err = 1;
1344 break;
1346 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1347 xfree(tmp);
1348 err = 1;
1349 break;
1351 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1352 error("Can't change directory: Can't check target");
1353 xfree(tmp);
1354 err = 1;
1355 break;
1357 if (!S_ISDIR(aa->perm)) {
1358 error("Can't change directory: \"%s\" is not "
1359 "a directory", tmp);
1360 xfree(tmp);
1361 err = 1;
1362 break;
1364 xfree(*pwd);
1365 *pwd = tmp;
1366 break;
1367 case I_LS:
1368 if (!path1) {
1369 do_globbed_ls(conn, *pwd, *pwd, lflag);
1370 break;
1373 /* Strip pwd off beginning of non-absolute paths */
1374 tmp = NULL;
1375 if (*path1 != '/')
1376 tmp = *pwd;
1378 path1 = make_absolute(path1, *pwd);
1379 err = do_globbed_ls(conn, path1, tmp, lflag);
1380 break;
1381 case I_DF:
1382 /* Default to current directory if no path specified */
1383 if (path1 == NULL)
1384 path1 = xstrdup(*pwd);
1385 path1 = make_absolute(path1, *pwd);
1386 err = do_df(conn, path1, hflag, iflag);
1387 break;
1388 case I_LCHDIR:
1389 if (chdir(path1) == -1) {
1390 error("Couldn't change local directory to "
1391 "\"%s\": %s", path1, strerror(errno));
1392 err = 1;
1394 break;
1395 case I_LMKDIR:
1396 if (mkdir(path1, 0777) == -1) {
1397 error("Couldn't create local directory "
1398 "\"%s\": %s", path1, strerror(errno));
1399 err = 1;
1401 break;
1402 case I_LLS:
1403 local_do_ls(cmd);
1404 break;
1405 case I_SHELL:
1406 local_do_shell(cmd);
1407 break;
1408 case I_LUMASK:
1409 umask(n_arg);
1410 printf("Local umask: %03lo\n", n_arg);
1411 break;
1412 case I_CHMOD:
1413 path1 = make_absolute(path1, *pwd);
1414 attrib_clear(&a);
1415 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1416 a.perm = n_arg;
1417 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1418 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1419 printf("Changing mode on %s\n", g.gl_pathv[i]);
1420 err = do_setstat(conn, g.gl_pathv[i], &a);
1421 if (err != 0 && err_abort)
1422 break;
1424 break;
1425 case I_CHOWN:
1426 case I_CHGRP:
1427 path1 = make_absolute(path1, *pwd);
1428 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1429 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1430 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1431 if (err_abort) {
1432 err = -1;
1433 break;
1434 } else
1435 continue;
1437 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1438 error("Can't get current ownership of "
1439 "remote file \"%s\"", g.gl_pathv[i]);
1440 if (err_abort) {
1441 err = -1;
1442 break;
1443 } else
1444 continue;
1446 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1447 if (cmdnum == I_CHOWN) {
1448 printf("Changing owner on %s\n", g.gl_pathv[i]);
1449 aa->uid = n_arg;
1450 } else {
1451 printf("Changing group on %s\n", g.gl_pathv[i]);
1452 aa->gid = n_arg;
1454 err = do_setstat(conn, g.gl_pathv[i], aa);
1455 if (err != 0 && err_abort)
1456 break;
1458 break;
1459 case I_PWD:
1460 printf("Remote working directory: %s\n", *pwd);
1461 break;
1462 case I_LPWD:
1463 if (!getcwd(path_buf, sizeof(path_buf))) {
1464 error("Couldn't get local cwd: %s", strerror(errno));
1465 err = -1;
1466 break;
1468 printf("Local working directory: %s\n", path_buf);
1469 break;
1470 case I_QUIT:
1471 /* Processed below */
1472 break;
1473 case I_HELP:
1474 help();
1475 break;
1476 case I_VERSION:
1477 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1478 break;
1479 case I_PROGRESS:
1480 showprogress = !showprogress;
1481 if (showprogress)
1482 printf("Progress meter enabled\n");
1483 else
1484 printf("Progress meter disabled\n");
1485 break;
1486 default:
1487 fatal("%d is not implemented", cmdnum);
1490 if (g.gl_pathc)
1491 globfree(&g);
1492 if (path1)
1493 xfree(path1);
1494 if (path2)
1495 xfree(path2);
1497 /* If an unignored error occurs in batch mode we should abort. */
1498 if (err_abort && err != 0)
1499 return (-1);
1500 else if (cmdnum == I_QUIT)
1501 return (1);
1503 return (0);
1506 #ifdef USE_LIBEDIT
1507 static char *
1508 prompt(EditLine *el)
1510 return ("sftp> ");
1513 /* Display entries in 'list' after skipping the first 'len' chars */
1514 static void
1515 complete_display(char **list, u_int len)
1517 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1518 struct winsize ws;
1519 char *tmp;
1521 /* Count entries for sort and find longest */
1522 for (y = 0; list[y]; y++)
1523 m = MAX(m, strlen(list[y]));
1525 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1526 width = ws.ws_col;
1528 m = m > len ? m - len : 0;
1529 columns = width / (m + 2);
1530 columns = MAX(columns, 1);
1531 colspace = width / columns;
1532 colspace = MIN(colspace, width);
1534 printf("\n");
1535 m = 1;
1536 for (y = 0; list[y]; y++) {
1537 llen = strlen(list[y]);
1538 tmp = llen > len ? list[y] + len : "";
1539 printf("%-*s", colspace, tmp);
1540 if (m >= columns) {
1541 printf("\n");
1542 m = 1;
1543 } else
1544 m++;
1546 printf("\n");
1550 * Given a "list" of words that begin with a common prefix of "word",
1551 * attempt to find an autocompletion to extends "word" by the next
1552 * characters common to all entries in "list".
1554 static char *
1555 complete_ambiguous(const char *word, char **list, size_t count)
1557 if (word == NULL)
1558 return NULL;
1560 if (count > 0) {
1561 u_int y, matchlen = strlen(list[0]);
1563 /* Find length of common stem */
1564 for (y = 1; list[y]; y++) {
1565 u_int x;
1567 for (x = 0; x < matchlen; x++)
1568 if (list[0][x] != list[y][x])
1569 break;
1571 matchlen = x;
1574 if (matchlen > strlen(word)) {
1575 char *tmp = xstrdup(list[0]);
1577 tmp[matchlen] = '\0';
1578 return tmp;
1582 return xstrdup(word);
1585 /* Autocomplete a sftp command */
1586 static int
1587 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1588 int terminated)
1590 u_int y, count = 0, cmdlen, tmplen;
1591 char *tmp, **list, argterm[3];
1592 const LineInfo *lf;
1594 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1596 /* No command specified: display all available commands */
1597 if (cmd == NULL) {
1598 for (y = 0; cmds[y].c; y++)
1599 list[count++] = xstrdup(cmds[y].c);
1601 list[count] = NULL;
1602 complete_display(list, 0);
1604 for (y = 0; list[y] != NULL; y++)
1605 xfree(list[y]);
1606 xfree(list);
1607 return count;
1610 /* Prepare subset of commands that start with "cmd" */
1611 cmdlen = strlen(cmd);
1612 for (y = 0; cmds[y].c; y++) {
1613 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1614 list[count++] = xstrdup(cmds[y].c);
1616 list[count] = NULL;
1618 if (count == 0)
1619 return 0;
1621 /* Complete ambigious command */
1622 tmp = complete_ambiguous(cmd, list, count);
1623 if (count > 1)
1624 complete_display(list, 0);
1626 for (y = 0; list[y]; y++)
1627 xfree(list[y]);
1628 xfree(list);
1630 if (tmp != NULL) {
1631 tmplen = strlen(tmp);
1632 cmdlen = strlen(cmd);
1633 /* If cmd may be extended then do so */
1634 if (tmplen > cmdlen)
1635 if (el_insertstr(el, tmp + cmdlen) == -1)
1636 fatal("el_insertstr failed.");
1637 lf = el_line(el);
1638 /* Terminate argument cleanly */
1639 if (count == 1) {
1640 y = 0;
1641 if (!terminated)
1642 argterm[y++] = quote;
1643 if (lastarg || *(lf->cursor) != ' ')
1644 argterm[y++] = ' ';
1645 argterm[y] = '\0';
1646 if (y > 0 && el_insertstr(el, argterm) == -1)
1647 fatal("el_insertstr failed.");
1649 xfree(tmp);
1652 return count;
1656 * Determine whether a particular sftp command's arguments (if any)
1657 * represent local or remote files.
1659 static int
1660 complete_is_remote(char *cmd) {
1661 int i;
1663 if (cmd == NULL)
1664 return -1;
1666 for (i = 0; cmds[i].c; i++) {
1667 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1668 return cmds[i].t;
1671 return -1;
1674 /* Autocomplete a filename "file" */
1675 static int
1676 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1677 char *file, int remote, int lastarg, char quote, int terminated)
1679 glob_t g;
1680 char *tmp, *tmp2, ins[3];
1681 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1682 const LineInfo *lf;
1684 /* Glob from "file" location */
1685 if (file == NULL)
1686 tmp = xstrdup("*");
1687 else
1688 xasprintf(&tmp, "%s*", file);
1690 memset(&g, 0, sizeof(g));
1691 if (remote != LOCAL) {
1692 tmp = make_absolute(tmp, remote_path);
1693 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1694 } else
1695 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1697 /* Determine length of pwd so we can trim completion display */
1698 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1699 /* Terminate counting on first unescaped glob metacharacter */
1700 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1701 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1702 hadglob = 1;
1703 break;
1705 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1706 tmplen++;
1707 if (tmp[tmplen] == '/')
1708 pwdlen = tmplen + 1; /* track last seen '/' */
1710 xfree(tmp);
1712 if (g.gl_matchc == 0)
1713 goto out;
1715 if (g.gl_matchc > 1)
1716 complete_display(g.gl_pathv, pwdlen);
1718 tmp = NULL;
1719 /* Don't try to extend globs */
1720 if (file == NULL || hadglob)
1721 goto out;
1723 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1724 tmp = path_strip(tmp2, remote_path);
1725 xfree(tmp2);
1727 if (tmp == NULL)
1728 goto out;
1730 tmplen = strlen(tmp);
1731 filelen = strlen(file);
1733 if (tmplen > filelen) {
1734 tmp2 = tmp + filelen;
1735 len = strlen(tmp2);
1736 /* quote argument on way out */
1737 for (i = 0; i < len; i++) {
1738 ins[0] = '\\';
1739 ins[1] = tmp2[i];
1740 ins[2] = '\0';
1741 switch (tmp2[i]) {
1742 case '\'':
1743 case '"':
1744 case '\\':
1745 case '\t':
1746 case ' ':
1747 if (quote == '\0' || tmp2[i] == quote) {
1748 if (el_insertstr(el, ins) == -1)
1749 fatal("el_insertstr "
1750 "failed.");
1751 break;
1753 /* FALLTHROUGH */
1754 default:
1755 if (el_insertstr(el, ins + 1) == -1)
1756 fatal("el_insertstr failed.");
1757 break;
1762 lf = el_line(el);
1763 if (g.gl_matchc == 1) {
1764 i = 0;
1765 if (!terminated)
1766 ins[i++] = quote;
1767 if (*(lf->cursor - 1) != '/' &&
1768 (lastarg || *(lf->cursor) != ' '))
1769 ins[i++] = ' ';
1770 ins[i] = '\0';
1771 if (i > 0 && el_insertstr(el, ins) == -1)
1772 fatal("el_insertstr failed.");
1774 xfree(tmp);
1776 out:
1777 globfree(&g);
1778 return g.gl_matchc;
1781 /* tab-completion hook function, called via libedit */
1782 static unsigned char
1783 complete(EditLine *el, int ch)
1785 char **argv, *line, quote;
1786 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1787 const LineInfo *lf;
1788 struct complete_ctx *complete_ctx;
1790 lf = el_line(el);
1791 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1792 fatal("%s: el_get failed", __func__);
1794 /* Figure out which argument the cursor points to */
1795 cursor = lf->cursor - lf->buffer;
1796 line = (char *)xmalloc(cursor + 1);
1797 memcpy(line, lf->buffer, cursor);
1798 line[cursor] = '\0';
1799 argv = makeargv(line, &carg, 1, &quote, &terminated);
1800 xfree(line);
1802 /* Get all the arguments on the line */
1803 len = lf->lastchar - lf->buffer;
1804 line = (char *)xmalloc(len + 1);
1805 memcpy(line, lf->buffer, len);
1806 line[len] = '\0';
1807 argv = makeargv(line, &argc, 1, NULL, NULL);
1809 /* Ensure cursor is at EOL or a argument boundary */
1810 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1811 line[cursor] != '\n') {
1812 xfree(line);
1813 return ret;
1816 if (carg == 0) {
1817 /* Show all available commands */
1818 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1819 ret = CC_REDISPLAY;
1820 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1821 /* Handle the command parsing */
1822 if (complete_cmd_parse(el, argv[0], argc == carg,
1823 quote, terminated) != 0)
1824 ret = CC_REDISPLAY;
1825 } else if (carg >= 1) {
1826 /* Handle file parsing */
1827 int remote = complete_is_remote(argv[0]);
1828 char *filematch = NULL;
1830 if (carg > 1 && line[cursor-1] != ' ')
1831 filematch = argv[carg - 1];
1833 if (remote != 0 &&
1834 complete_match(el, complete_ctx->conn,
1835 *complete_ctx->remote_pathp, filematch,
1836 remote, carg == argc, quote, terminated) != 0)
1837 ret = CC_REDISPLAY;
1840 xfree(line);
1841 return ret;
1843 #endif /* USE_LIBEDIT */
1846 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1848 char *remote_path;
1849 char *dir = NULL;
1850 char cmd[2048];
1851 int err, interactive;
1852 EditLine *el = NULL;
1853 #ifdef USE_LIBEDIT
1854 History *hl = NULL;
1855 HistEvent hev;
1856 extern char *__progname;
1857 struct complete_ctx complete_ctx;
1859 if (!batchmode && isatty(STDIN_FILENO)) {
1860 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1861 fatal("Couldn't initialise editline");
1862 if ((hl = history_init()) == NULL)
1863 fatal("Couldn't initialise editline history");
1864 history(hl, &hev, H_SETSIZE, 100);
1865 el_set(el, EL_HIST, history, hl);
1867 el_set(el, EL_PROMPT, prompt);
1868 el_set(el, EL_EDITOR, "emacs");
1869 el_set(el, EL_TERMINAL, NULL);
1870 el_set(el, EL_SIGNAL, 1);
1871 el_source(el, NULL);
1873 /* Tab Completion */
1874 el_set(el, EL_ADDFN, "ftp-complete",
1875 "Context senstive argument completion", complete);
1876 complete_ctx.conn = conn;
1877 complete_ctx.remote_pathp = &remote_path;
1878 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1879 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1881 #endif /* USE_LIBEDIT */
1883 remote_path = do_realpath(conn, ".");
1884 if (remote_path == NULL)
1885 fatal("Need cwd");
1887 if (file1 != NULL) {
1888 dir = xstrdup(file1);
1889 dir = make_absolute(dir, remote_path);
1891 if (remote_is_dir(conn, dir) && file2 == NULL) {
1892 printf("Changing to: %s\n", dir);
1893 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1894 if (parse_dispatch_command(conn, cmd,
1895 &remote_path, 1) != 0) {
1896 xfree(dir);
1897 xfree(remote_path);
1898 xfree(conn);
1899 return (-1);
1901 } else {
1902 if (file2 == NULL)
1903 snprintf(cmd, sizeof cmd, "get %s", dir);
1904 else
1905 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1906 file2);
1908 err = parse_dispatch_command(conn, cmd,
1909 &remote_path, 1);
1910 xfree(dir);
1911 xfree(remote_path);
1912 xfree(conn);
1913 return (err);
1915 xfree(dir);
1918 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1919 setvbuf(stdout, NULL, _IOLBF, 0);
1920 setvbuf(infile, NULL, _IOLBF, 0);
1921 #else
1922 setlinebuf(stdout);
1923 setlinebuf(infile);
1924 #endif
1926 interactive = !batchmode && isatty(STDIN_FILENO);
1927 err = 0;
1928 for (;;) {
1929 char *cp;
1931 signal(SIGINT, SIG_IGN);
1933 if (el == NULL) {
1934 if (interactive)
1935 printf("sftp> ");
1936 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1937 if (interactive)
1938 printf("\n");
1939 break;
1941 if (!interactive) { /* Echo command */
1942 printf("sftp> %s", cmd);
1943 if (strlen(cmd) > 0 &&
1944 cmd[strlen(cmd) - 1] != '\n')
1945 printf("\n");
1947 } else {
1948 #ifdef USE_LIBEDIT
1949 const char *line;
1950 int count = 0;
1952 if ((line = el_gets(el, &count)) == NULL ||
1953 count <= 0) {
1954 printf("\n");
1955 break;
1957 history(hl, &hev, H_ENTER, line);
1958 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1959 fprintf(stderr, "Error: input line too long\n");
1960 continue;
1962 #endif /* USE_LIBEDIT */
1965 cp = strrchr(cmd, '\n');
1966 if (cp)
1967 *cp = '\0';
1969 /* Handle user interrupts gracefully during commands */
1970 interrupted = 0;
1971 signal(SIGINT, cmd_interrupt);
1973 err = parse_dispatch_command(conn, cmd, &remote_path,
1974 batchmode);
1975 if (err != 0)
1976 break;
1978 xfree(remote_path);
1979 xfree(conn);
1981 #ifdef USE_LIBEDIT
1982 if (el != NULL)
1983 el_end(el);
1984 #endif /* USE_LIBEDIT */
1986 /* err == 1 signifies normal "quit" exit */
1987 return (err >= 0 ? 0 : -1);
1990 static void
1991 connect_to_server(char *path, char **args, int *in, int *out)
1993 int c_in, c_out;
1995 #ifdef USE_PIPES
1996 int pin[2], pout[2];
1998 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1999 fatal("pipe: %s", strerror(errno));
2000 *in = pin[0];
2001 *out = pout[1];
2002 c_in = pout[0];
2003 c_out = pin[1];
2004 #else /* USE_PIPES */
2005 int inout[2];
2007 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2008 fatal("socketpair: %s", strerror(errno));
2009 *in = *out = inout[0];
2010 c_in = c_out = inout[1];
2011 #endif /* USE_PIPES */
2013 if ((sshpid = fork()) == -1)
2014 fatal("fork: %s", strerror(errno));
2015 else if (sshpid == 0) {
2016 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2017 (dup2(c_out, STDOUT_FILENO) == -1)) {
2018 fprintf(stderr, "dup2: %s\n", strerror(errno));
2019 _exit(1);
2021 close(*in);
2022 close(*out);
2023 close(c_in);
2024 close(c_out);
2027 * The underlying ssh is in the same process group, so we must
2028 * ignore SIGINT if we want to gracefully abort commands,
2029 * otherwise the signal will make it to the ssh process and
2030 * kill it too
2032 signal(SIGINT, SIG_IGN);
2033 execvp(path, args);
2034 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2035 _exit(1);
2038 signal(SIGTERM, killchild);
2039 signal(SIGINT, killchild);
2040 signal(SIGHUP, killchild);
2041 close(c_in);
2042 close(c_out);
2045 static void
2046 usage(void)
2048 extern char *__progname;
2050 fprintf(stderr,
2051 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2052 " [-D sftp_server_path] [-F ssh_config] "
2053 "[-i identity_file]\n"
2054 " [-o ssh_option] [-P port] [-R num_requests] "
2055 "[-S program]\n"
2056 " [-s subsystem | sftp_server] host\n"
2057 " %s [user@]host[:file ...]\n"
2058 " %s [user@]host[:dir[/]]\n"
2059 " %s -b batchfile [user@]host\n",
2060 __progname, __progname, __progname, __progname);
2061 exit(1);
2065 main(int argc, char **argv)
2067 int in, out, ch, err;
2068 char *host = NULL, *userhost, *cp, *file2 = NULL;
2069 int debug_level = 0, sshver = 2;
2070 char *file1 = NULL, *sftp_server = NULL;
2071 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2072 LogLevel ll = SYSLOG_LEVEL_INFO;
2073 arglist args;
2074 extern int optind;
2075 extern char *optarg;
2076 struct sftp_conn *conn;
2077 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2078 size_t num_requests = DEFAULT_NUM_REQUESTS;
2080 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2081 sanitise_stdfd();
2083 __progname = ssh_get_progname(argv[0]);
2084 memset(&args, '\0', sizeof(args));
2085 args.list = NULL;
2086 addargs(&args, "%s", ssh_program);
2087 addargs(&args, "-oForwardX11 no");
2088 addargs(&args, "-oForwardAgent no");
2089 addargs(&args, "-oPermitLocalCommand no");
2090 addargs(&args, "-oClearAllForwardings yes");
2092 ll = SYSLOG_LEVEL_INFO;
2093 infile = stdin;
2095 while ((ch = getopt(argc, argv,
2096 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2097 switch (ch) {
2098 /* Passed through to ssh(1) */
2099 case '4':
2100 case '6':
2101 case 'C':
2102 addargs(&args, "-%c", ch);
2103 break;
2104 /* Passed through to ssh(1) with argument */
2105 case 'F':
2106 case 'c':
2107 case 'i':
2108 case 'o':
2109 addargs(&args, "-%c", ch);
2110 addargs(&args, "%s", optarg);
2111 break;
2112 case 'q':
2113 showprogress = 0;
2114 addargs(&args, "-%c", ch);
2115 break;
2116 case 'P':
2117 addargs(&args, "-oPort %s", optarg);
2118 break;
2119 case 'v':
2120 if (debug_level < 3) {
2121 addargs(&args, "-v");
2122 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2124 debug_level++;
2125 break;
2126 case '1':
2127 sshver = 1;
2128 if (sftp_server == NULL)
2129 sftp_server = _PATH_SFTP_SERVER;
2130 break;
2131 case '2':
2132 sshver = 2;
2133 break;
2134 case 'B':
2135 copy_buffer_len = strtol(optarg, &cp, 10);
2136 if (copy_buffer_len == 0 || *cp != '\0')
2137 fatal("Invalid buffer size \"%s\"", optarg);
2138 break;
2139 case 'b':
2140 if (batchmode)
2141 fatal("Batch file already specified.");
2143 /* Allow "-" as stdin */
2144 if (strcmp(optarg, "-") != 0 &&
2145 (infile = fopen(optarg, "r")) == NULL)
2146 fatal("%s (%s).", strerror(errno), optarg);
2147 showprogress = 0;
2148 batchmode = 1;
2149 addargs(&args, "-obatchmode yes");
2150 break;
2151 case 'p':
2152 global_pflag = 1;
2153 break;
2154 case 'D':
2155 sftp_direct = optarg;
2156 break;
2157 case 'r':
2158 global_rflag = 1;
2159 break;
2160 case 'R':
2161 num_requests = strtol(optarg, &cp, 10);
2162 if (num_requests == 0 || *cp != '\0')
2163 fatal("Invalid number of requests \"%s\"",
2164 optarg);
2165 break;
2166 case 's':
2167 sftp_server = optarg;
2168 break;
2169 case 'S':
2170 ssh_program = optarg;
2171 replacearg(&args, 0, "%s", ssh_program);
2172 break;
2173 case 'h':
2174 default:
2175 usage();
2179 if (!isatty(STDERR_FILENO))
2180 showprogress = 0;
2182 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2184 if (sftp_direct == NULL) {
2185 if (optind == argc || argc > (optind + 2))
2186 usage();
2188 userhost = xstrdup(argv[optind]);
2189 file2 = argv[optind+1];
2191 if ((host = strrchr(userhost, '@')) == NULL)
2192 host = userhost;
2193 else {
2194 *host++ = '\0';
2195 if (!userhost[0]) {
2196 fprintf(stderr, "Missing username\n");
2197 usage();
2199 addargs(&args, "-l");
2200 addargs(&args, "%s", userhost);
2203 if ((cp = colon(host)) != NULL) {
2204 *cp++ = '\0';
2205 file1 = cp;
2208 host = cleanhostname(host);
2209 if (!*host) {
2210 fprintf(stderr, "Missing hostname\n");
2211 usage();
2214 addargs(&args, "-oProtocol %d", sshver);
2216 /* no subsystem if the server-spec contains a '/' */
2217 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2218 addargs(&args, "-s");
2220 addargs(&args, "--");
2221 addargs(&args, "%s", host);
2222 addargs(&args, "%s", (sftp_server != NULL ?
2223 sftp_server : "sftp"));
2225 connect_to_server(ssh_program, args.list, &in, &out);
2226 } else {
2227 args.list = NULL;
2228 addargs(&args, "sftp-server");
2230 connect_to_server(sftp_direct, args.list, &in, &out);
2232 freeargs(&args);
2234 conn = do_init(in, out, copy_buffer_len, num_requests);
2235 if (conn == NULL)
2236 fatal("Couldn't initialise connection to server");
2238 if (!batchmode) {
2239 if (sftp_direct == NULL)
2240 fprintf(stderr, "Connected to %s.\n", host);
2241 else
2242 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2245 err = interactive_loop(conn, file1, file2);
2247 #if !defined(USE_PIPES)
2248 shutdown(in, SHUT_RDWR);
2249 shutdown(out, SHUT_RDWR);
2250 #endif
2252 close(in);
2253 close(out);
2254 if (batchmode)
2255 fclose(infile);
2257 while (waitpid(sshpid, NULL, 0) == -1)
2258 if (errno != EINTR)
2259 fatal("Couldn't wait for ssh process: %s",
2260 strerror(errno));
2262 exit(err == 0 ? 0 : 1);