- dtucker@cvs.openbsd.org 2010/01/09 05:17:00
[openssh-git.git] / sftp.c
blob9f5fa354dc43bd368f85de4de6bccf41264927ee
1 /* $OpenBSD: sftp.c,v 1.117 2010/01/08 21:50:49 dtucker 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 0x01 /* Full view ala ls -l */
114 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
115 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
116 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
117 #define LS_TIME_SORT 0x10 /* Sort by mtime */
118 #define LS_SIZE_SORT 0x20 /* Sort by file size */
119 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
120 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
122 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
123 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
125 /* Commands for interactive mode */
126 #define I_CHDIR 1
127 #define I_CHGRP 2
128 #define I_CHMOD 3
129 #define I_CHOWN 4
130 #define I_DF 24
131 #define I_GET 5
132 #define I_HELP 6
133 #define I_LCHDIR 7
134 #define I_LLS 8
135 #define I_LMKDIR 9
136 #define I_LPWD 10
137 #define I_LS 11
138 #define I_LUMASK 12
139 #define I_MKDIR 13
140 #define I_PUT 14
141 #define I_PWD 15
142 #define I_QUIT 16
143 #define I_RENAME 17
144 #define I_RM 18
145 #define I_RMDIR 19
146 #define I_SHELL 20
147 #define I_SYMLINK 21
148 #define I_VERSION 22
149 #define I_PROGRESS 23
151 struct CMD {
152 const char *c;
153 const int n;
154 const int t;
157 /* Type of completion */
158 #define NOARGS 0
159 #define REMOTE 1
160 #define LOCAL 2
162 static const struct CMD cmds[] = {
163 { "bye", I_QUIT, NOARGS },
164 { "cd", I_CHDIR, REMOTE },
165 { "chdir", I_CHDIR, REMOTE },
166 { "chgrp", I_CHGRP, REMOTE },
167 { "chmod", I_CHMOD, REMOTE },
168 { "chown", I_CHOWN, REMOTE },
169 { "df", I_DF, REMOTE },
170 { "dir", I_LS, REMOTE },
171 { "exit", I_QUIT, NOARGS },
172 { "get", I_GET, REMOTE },
173 { "help", I_HELP, NOARGS },
174 { "lcd", I_LCHDIR, LOCAL },
175 { "lchdir", I_LCHDIR, LOCAL },
176 { "lls", I_LLS, LOCAL },
177 { "lmkdir", I_LMKDIR, LOCAL },
178 { "ln", I_SYMLINK, REMOTE },
179 { "lpwd", I_LPWD, LOCAL },
180 { "ls", I_LS, REMOTE },
181 { "lumask", I_LUMASK, NOARGS },
182 { "mkdir", I_MKDIR, REMOTE },
183 { "progress", I_PROGRESS, NOARGS },
184 { "put", I_PUT, LOCAL },
185 { "pwd", I_PWD, REMOTE },
186 { "quit", I_QUIT, NOARGS },
187 { "rename", I_RENAME, REMOTE },
188 { "rm", I_RM, REMOTE },
189 { "rmdir", I_RMDIR, REMOTE },
190 { "symlink", I_SYMLINK, REMOTE },
191 { "version", I_VERSION, NOARGS },
192 { "!", I_SHELL, NOARGS },
193 { "?", I_HELP, NOARGS },
194 { NULL, -1, -1 }
197 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
199 /* ARGSUSED */
200 static void
201 killchild(int signo)
203 if (sshpid > 1) {
204 kill(sshpid, SIGTERM);
205 waitpid(sshpid, NULL, 0);
208 _exit(1);
211 /* ARGSUSED */
212 static void
213 cmd_interrupt(int signo)
215 const char msg[] = "\rInterrupt \n";
216 int olderrno = errno;
218 write(STDERR_FILENO, msg, sizeof(msg) - 1);
219 interrupted = 1;
220 errno = olderrno;
223 static void
224 help(void)
226 printf("Available commands:\n"
227 "bye Quit sftp\n"
228 "cd path Change remote directory to 'path'\n"
229 "chgrp grp path Change group of file 'path' to 'grp'\n"
230 "chmod mode path Change permissions of file 'path' to 'mode'\n"
231 "chown own path Change owner of file 'path' to 'own'\n"
232 "df [-hi] [path] Display statistics for current directory or\n"
233 " filesystem containing 'path'\n"
234 "exit Quit sftp\n"
235 "get [-Pr] remote-path [local-path] Download file\n"
236 "help Display this help text\n"
237 "lcd path Change local directory to 'path'\n"
238 "lls [ls-options [path]] Display local directory listing\n"
239 "lmkdir path Create local directory\n"
240 "ln oldpath newpath Symlink remote file\n"
241 "lpwd Print local working directory\n"
242 "ls [-1aflnrSt] [path] Display remote directory listing\n"
243 "lumask umask Set local umask to 'umask'\n"
244 "mkdir path Create remote directory\n"
245 "progress Toggle display of progress meter\n"
246 "put [-Pr] local-path [remote-path] Upload file\n"
247 "pwd Display remote working directory\n"
248 "quit Quit sftp\n"
249 "rename oldpath newpath Rename remote file\n"
250 "rm path Delete remote file\n"
251 "rmdir path Remove remote directory\n"
252 "symlink oldpath newpath Symlink remote file\n"
253 "version Show SFTP version\n"
254 "!command Execute 'command' in local shell\n"
255 "! Escape to local shell\n"
256 "? Synonym for help\n");
259 static void
260 local_do_shell(const char *args)
262 int status;
263 char *shell;
264 pid_t pid;
266 if (!*args)
267 args = NULL;
269 if ((shell = getenv("SHELL")) == NULL)
270 shell = _PATH_BSHELL;
272 if ((pid = fork()) == -1)
273 fatal("Couldn't fork: %s", strerror(errno));
275 if (pid == 0) {
276 /* XXX: child has pipe fds to ssh subproc open - issue? */
277 if (args) {
278 debug3("Executing %s -c \"%s\"", shell, args);
279 execl(shell, shell, "-c", args, (char *)NULL);
280 } else {
281 debug3("Executing %s", shell);
282 execl(shell, shell, (char *)NULL);
284 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
285 strerror(errno));
286 _exit(1);
288 while (waitpid(pid, &status, 0) == -1)
289 if (errno != EINTR)
290 fatal("Couldn't wait for child: %s", strerror(errno));
291 if (!WIFEXITED(status))
292 error("Shell exited abnormally");
293 else if (WEXITSTATUS(status))
294 error("Shell exited with status %d", WEXITSTATUS(status));
297 static void
298 local_do_ls(const char *args)
300 if (!args || !*args)
301 local_do_shell(_PATH_LS);
302 else {
303 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
304 char *buf = xmalloc(len);
306 /* XXX: quoting - rip quoting code from ftp? */
307 snprintf(buf, len, _PATH_LS " %s", args);
308 local_do_shell(buf);
309 xfree(buf);
313 /* Strip one path (usually the pwd) from the start of another */
314 static char *
315 path_strip(char *path, char *strip)
317 size_t len;
319 if (strip == NULL)
320 return (xstrdup(path));
322 len = strlen(strip);
323 if (strncmp(path, strip, len) == 0) {
324 if (strip[len - 1] != '/' && path[len] == '/')
325 len++;
326 return (xstrdup(path + len));
329 return (xstrdup(path));
332 static char *
333 make_absolute(char *p, char *pwd)
335 char *abs_str;
337 /* Derelativise */
338 if (p && p[0] != '/') {
339 abs_str = path_append(pwd, p);
340 xfree(p);
341 return(abs_str);
342 } else
343 return(p);
346 static int
347 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
348 int *rflag)
350 extern int opterr, optind, optopt, optreset;
351 int ch;
353 optind = optreset = 1;
354 opterr = 0;
356 *rflag = *pflag = 0;
357 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
358 switch (ch) {
359 case 'p':
360 case 'P':
361 *pflag = 1;
362 break;
363 case 'r':
364 case 'R':
365 *rflag = 1;
366 break;
367 default:
368 error("%s: Invalid flag -%c", cmd, optopt);
369 return -1;
373 return optind;
376 static int
377 parse_ls_flags(char **argv, int argc, int *lflag)
379 extern int opterr, optind, optopt, optreset;
380 int ch;
382 optind = optreset = 1;
383 opterr = 0;
385 *lflag = LS_NAME_SORT;
386 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
387 switch (ch) {
388 case '1':
389 *lflag &= ~VIEW_FLAGS;
390 *lflag |= LS_SHORT_VIEW;
391 break;
392 case 'S':
393 *lflag &= ~SORT_FLAGS;
394 *lflag |= LS_SIZE_SORT;
395 break;
396 case 'a':
397 *lflag |= LS_SHOW_ALL;
398 break;
399 case 'f':
400 *lflag &= ~SORT_FLAGS;
401 break;
402 case 'l':
403 *lflag &= ~VIEW_FLAGS;
404 *lflag |= LS_LONG_VIEW;
405 break;
406 case 'n':
407 *lflag &= ~VIEW_FLAGS;
408 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
409 break;
410 case 'r':
411 *lflag |= LS_REVERSE_SORT;
412 break;
413 case 't':
414 *lflag &= ~SORT_FLAGS;
415 *lflag |= LS_TIME_SORT;
416 break;
417 default:
418 error("ls: Invalid flag -%c", optopt);
419 return -1;
423 return optind;
426 static int
427 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
429 extern int opterr, optind, optopt, optreset;
430 int ch;
432 optind = optreset = 1;
433 opterr = 0;
435 *hflag = *iflag = 0;
436 while ((ch = getopt(argc, argv, "hi")) != -1) {
437 switch (ch) {
438 case 'h':
439 *hflag = 1;
440 break;
441 case 'i':
442 *iflag = 1;
443 break;
444 default:
445 error("%s: Invalid flag -%c", cmd, optopt);
446 return -1;
450 return optind;
453 static int
454 is_dir(char *path)
456 struct stat sb;
458 /* XXX: report errors? */
459 if (stat(path, &sb) == -1)
460 return(0);
462 return(S_ISDIR(sb.st_mode));
465 static int
466 remote_is_dir(struct sftp_conn *conn, char *path)
468 Attrib *a;
470 /* XXX: report errors? */
471 if ((a = do_stat(conn, path, 1)) == NULL)
472 return(0);
473 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
474 return(0);
475 return(S_ISDIR(a->perm));
478 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
479 static int
480 pathname_is_dir(char *pathname)
482 size_t l = strlen(pathname);
484 return l > 0 && pathname[l - 1] == '/';
487 static int
488 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
489 int pflag, int rflag)
491 char *abs_src = NULL;
492 char *abs_dst = NULL;
493 glob_t g;
494 char *filename, *tmp=NULL;
495 int i, err = 0;
497 abs_src = xstrdup(src);
498 abs_src = make_absolute(abs_src, pwd);
499 memset(&g, 0, sizeof(g));
501 debug3("Looking up %s", abs_src);
502 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
503 error("File \"%s\" not found.", abs_src);
504 err = -1;
505 goto out;
509 * If multiple matches then dst must be a directory or
510 * unspecified.
512 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
513 error("Multiple source paths, but destination "
514 "\"%s\" is not a directory", dst);
515 err = -1;
516 goto out;
519 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
520 tmp = xstrdup(g.gl_pathv[i]);
521 if ((filename = basename(tmp)) == NULL) {
522 error("basename %s: %s", tmp, strerror(errno));
523 xfree(tmp);
524 err = -1;
525 goto out;
528 if (g.gl_matchc == 1 && dst) {
529 if (is_dir(dst)) {
530 abs_dst = path_append(dst, filename);
531 } else {
532 abs_dst = xstrdup(dst);
534 } else if (dst) {
535 abs_dst = path_append(dst, filename);
536 } else {
537 abs_dst = xstrdup(filename);
539 xfree(tmp);
541 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
542 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
543 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
544 pflag || global_pflag, 1) == -1)
545 err = -1;
546 } else {
547 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
548 pflag || global_pflag) == -1)
549 err = -1;
551 xfree(abs_dst);
552 abs_dst = NULL;
555 out:
556 xfree(abs_src);
557 globfree(&g);
558 return(err);
561 static int
562 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
563 int pflag, int rflag)
565 char *tmp_dst = NULL;
566 char *abs_dst = NULL;
567 char *tmp = NULL, *filename = NULL;
568 glob_t g;
569 int err = 0;
570 int i, dst_is_dir = 1;
571 struct stat sb;
573 if (dst) {
574 tmp_dst = xstrdup(dst);
575 tmp_dst = make_absolute(tmp_dst, pwd);
578 memset(&g, 0, sizeof(g));
579 debug3("Looking up %s", src);
580 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
581 error("File \"%s\" not found.", src);
582 err = -1;
583 goto out;
586 /* If we aren't fetching to pwd then stash this status for later */
587 if (tmp_dst != NULL)
588 dst_is_dir = remote_is_dir(conn, tmp_dst);
590 /* If multiple matches, dst may be directory or unspecified */
591 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
592 error("Multiple paths match, but destination "
593 "\"%s\" is not a directory", tmp_dst);
594 err = -1;
595 goto out;
598 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
599 if (stat(g.gl_pathv[i], &sb) == -1) {
600 err = -1;
601 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
602 continue;
605 tmp = xstrdup(g.gl_pathv[i]);
606 if ((filename = basename(tmp)) == NULL) {
607 error("basename %s: %s", tmp, strerror(errno));
608 xfree(tmp);
609 err = -1;
610 goto out;
613 if (g.gl_matchc == 1 && tmp_dst) {
614 /* If directory specified, append filename */
615 if (dst_is_dir)
616 abs_dst = path_append(tmp_dst, filename);
617 else
618 abs_dst = xstrdup(tmp_dst);
619 } else if (tmp_dst) {
620 abs_dst = path_append(tmp_dst, filename);
621 } else {
622 abs_dst = make_absolute(xstrdup(filename), pwd);
624 xfree(tmp);
626 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
627 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
628 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
629 pflag || global_pflag, 1) == -1)
630 err = -1;
631 } else {
632 if (do_upload(conn, g.gl_pathv[i], abs_dst,
633 pflag || global_pflag) == -1)
634 err = -1;
638 out:
639 if (abs_dst)
640 xfree(abs_dst);
641 if (tmp_dst)
642 xfree(tmp_dst);
643 globfree(&g);
644 return(err);
647 static int
648 sdirent_comp(const void *aa, const void *bb)
650 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
651 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
652 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
654 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
655 if (sort_flag & LS_NAME_SORT)
656 return (rmul * strcmp(a->filename, b->filename));
657 else if (sort_flag & LS_TIME_SORT)
658 return (rmul * NCMP(a->a.mtime, b->a.mtime));
659 else if (sort_flag & LS_SIZE_SORT)
660 return (rmul * NCMP(a->a.size, b->a.size));
662 fatal("Unknown ls sort type");
665 /* sftp ls.1 replacement for directories */
666 static int
667 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
669 int n;
670 u_int c = 1, colspace = 0, columns = 1;
671 SFTP_DIRENT **d;
673 if ((n = do_readdir(conn, path, &d)) != 0)
674 return (n);
676 if (!(lflag & LS_SHORT_VIEW)) {
677 u_int m = 0, width = 80;
678 struct winsize ws;
679 char *tmp;
681 /* Count entries for sort and find longest filename */
682 for (n = 0; d[n] != NULL; n++) {
683 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
684 m = MAX(m, strlen(d[n]->filename));
687 /* Add any subpath that also needs to be counted */
688 tmp = path_strip(path, strip_path);
689 m += strlen(tmp);
690 xfree(tmp);
692 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
693 width = ws.ws_col;
695 columns = width / (m + 2);
696 columns = MAX(columns, 1);
697 colspace = width / columns;
698 colspace = MIN(colspace, width);
701 if (lflag & SORT_FLAGS) {
702 for (n = 0; d[n] != NULL; n++)
703 ; /* count entries */
704 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
705 qsort(d, n, sizeof(*d), sdirent_comp);
708 for (n = 0; d[n] != NULL && !interrupted; n++) {
709 char *tmp, *fname;
711 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
712 continue;
714 tmp = path_append(path, d[n]->filename);
715 fname = path_strip(tmp, strip_path);
716 xfree(tmp);
718 if (lflag & LS_LONG_VIEW) {
719 if (lflag & LS_NUMERIC_VIEW) {
720 char *lname;
721 struct stat sb;
723 memset(&sb, 0, sizeof(sb));
724 attrib_to_stat(&d[n]->a, &sb);
725 lname = ls_file(fname, &sb, 1);
726 printf("%s\n", lname);
727 xfree(lname);
728 } else
729 printf("%s\n", d[n]->longname);
730 } else {
731 printf("%-*s", colspace, fname);
732 if (c >= columns) {
733 printf("\n");
734 c = 1;
735 } else
736 c++;
739 xfree(fname);
742 if (!(lflag & LS_LONG_VIEW) && (c != 1))
743 printf("\n");
745 free_sftp_dirents(d);
746 return (0);
749 /* sftp ls.1 replacement which handles path globs */
750 static int
751 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
752 int lflag)
754 glob_t g;
755 u_int i, c = 1, colspace = 0, columns = 1;
756 Attrib *a = NULL;
758 memset(&g, 0, sizeof(g));
760 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
761 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
762 if (g.gl_pathc)
763 globfree(&g);
764 error("Can't ls: \"%s\" not found", path);
765 return (-1);
768 if (interrupted)
769 goto out;
772 * If the glob returns a single match and it is a directory,
773 * then just list its contents.
775 if (g.gl_matchc == 1) {
776 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
777 globfree(&g);
778 return (-1);
780 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
781 S_ISDIR(a->perm)) {
782 int err;
784 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
785 globfree(&g);
786 return (err);
790 if (!(lflag & LS_SHORT_VIEW)) {
791 u_int m = 0, width = 80;
792 struct winsize ws;
794 /* Count entries for sort and find longest filename */
795 for (i = 0; g.gl_pathv[i]; i++)
796 m = MAX(m, strlen(g.gl_pathv[i]));
798 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
799 width = ws.ws_col;
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 char *fname;
809 fname = path_strip(g.gl_pathv[i], strip_path);
811 if (lflag & LS_LONG_VIEW) {
812 char *lname;
813 struct stat sb;
816 * XXX: this is slow - 1 roundtrip per path
817 * A solution to this is to fork glob() and
818 * build a sftp specific version which keeps the
819 * attribs (which currently get thrown away)
820 * that the server returns as well as the filenames.
822 memset(&sb, 0, sizeof(sb));
823 if (a == NULL)
824 a = do_lstat(conn, g.gl_pathv[i], 1);
825 if (a != NULL)
826 attrib_to_stat(a, &sb);
827 lname = ls_file(fname, &sb, 1);
828 printf("%s\n", lname);
829 xfree(lname);
830 } else {
831 printf("%-*s", colspace, fname);
832 if (c >= columns) {
833 printf("\n");
834 c = 1;
835 } else
836 c++;
838 xfree(fname);
841 if (!(lflag & LS_LONG_VIEW) && (c != 1))
842 printf("\n");
844 out:
845 if (g.gl_pathc)
846 globfree(&g);
848 return (0);
851 static int
852 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
854 struct sftp_statvfs st;
855 char s_used[FMT_SCALED_STRSIZE];
856 char s_avail[FMT_SCALED_STRSIZE];
857 char s_root[FMT_SCALED_STRSIZE];
858 char s_total[FMT_SCALED_STRSIZE];
859 unsigned long long ffree;
861 if (do_statvfs(conn, path, &st, 1) == -1)
862 return -1;
863 if (iflag) {
864 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
865 printf(" Inodes Used Avail "
866 "(root) %%Capacity\n");
867 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
868 (unsigned long long)st.f_files,
869 (unsigned long long)(st.f_files - st.f_ffree),
870 (unsigned long long)st.f_favail,
871 (unsigned long long)st.f_ffree, ffree);
872 } else if (hflag) {
873 strlcpy(s_used, "error", sizeof(s_used));
874 strlcpy(s_avail, "error", sizeof(s_avail));
875 strlcpy(s_root, "error", sizeof(s_root));
876 strlcpy(s_total, "error", sizeof(s_total));
877 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
878 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
879 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
880 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
881 printf(" Size Used Avail (root) %%Capacity\n");
882 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
883 s_total, s_used, s_avail, s_root,
884 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
885 st.f_blocks));
886 } else {
887 printf(" Size Used Avail "
888 "(root) %%Capacity\n");
889 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
890 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
891 (unsigned long long)(st.f_frsize *
892 (st.f_blocks - st.f_bfree) / 1024),
893 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
894 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
895 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
896 st.f_blocks));
898 return 0;
902 * Undo escaping of glob sequences in place. Used to undo extra escaping
903 * applied in makeargv() when the string is destined for a function that
904 * does not glob it.
906 static void
907 undo_glob_escape(char *s)
909 size_t i, j;
911 for (i = j = 0;;) {
912 if (s[i] == '\0') {
913 s[j] = '\0';
914 return;
916 if (s[i] != '\\') {
917 s[j++] = s[i++];
918 continue;
920 /* s[i] == '\\' */
921 ++i;
922 switch (s[i]) {
923 case '?':
924 case '[':
925 case '*':
926 case '\\':
927 s[j++] = s[i++];
928 break;
929 case '\0':
930 s[j++] = '\\';
931 s[j] = '\0';
932 return;
933 default:
934 s[j++] = '\\';
935 s[j++] = s[i++];
936 break;
942 * Split a string into an argument vector using sh(1)-style quoting,
943 * comment and escaping rules, but with some tweaks to handle glob(3)
944 * wildcards.
945 * The "sloppy" flag allows for recovery from missing terminating quote, for
946 * use in parsing incomplete commandlines during tab autocompletion.
948 * Returns NULL on error or a NULL-terminated array of arguments.
950 * If "lastquote" is not NULL, the quoting character used for the last
951 * argument is placed in *lastquote ("\0", "'" or "\"").
953 * If "terminated" is not NULL, *terminated will be set to 1 when the
954 * last argument's quote has been properly terminated or 0 otherwise.
955 * This parameter is only of use if "sloppy" is set.
957 #define MAXARGS 128
958 #define MAXARGLEN 8192
959 static char **
960 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
961 u_int *terminated)
963 int argc, quot;
964 size_t i, j;
965 static char argvs[MAXARGLEN];
966 static char *argv[MAXARGS + 1];
967 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
969 *argcp = argc = 0;
970 if (strlen(arg) > sizeof(argvs) - 1) {
971 args_too_longs:
972 error("string too long");
973 return NULL;
975 if (terminated != NULL)
976 *terminated = 1;
977 if (lastquote != NULL)
978 *lastquote = '\0';
979 state = MA_START;
980 i = j = 0;
981 for (;;) {
982 if (isspace(arg[i])) {
983 if (state == MA_UNQUOTED) {
984 /* Terminate current argument */
985 argvs[j++] = '\0';
986 argc++;
987 state = MA_START;
988 } else if (state != MA_START)
989 argvs[j++] = arg[i];
990 } else if (arg[i] == '"' || arg[i] == '\'') {
991 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
992 if (state == MA_START) {
993 argv[argc] = argvs + j;
994 state = q;
995 if (lastquote != NULL)
996 *lastquote = arg[i];
997 } else if (state == MA_UNQUOTED)
998 state = q;
999 else if (state == q)
1000 state = MA_UNQUOTED;
1001 else
1002 argvs[j++] = arg[i];
1003 } else if (arg[i] == '\\') {
1004 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1005 quot = state == MA_SQUOTE ? '\'' : '"';
1006 /* Unescape quote we are in */
1007 /* XXX support \n and friends? */
1008 if (arg[i + 1] == quot) {
1009 i++;
1010 argvs[j++] = arg[i];
1011 } else if (arg[i + 1] == '?' ||
1012 arg[i + 1] == '[' || arg[i + 1] == '*') {
1014 * Special case for sftp: append
1015 * double-escaped glob sequence -
1016 * glob will undo one level of
1017 * escaping. NB. string can grow here.
1019 if (j >= sizeof(argvs) - 5)
1020 goto args_too_longs;
1021 argvs[j++] = '\\';
1022 argvs[j++] = arg[i++];
1023 argvs[j++] = '\\';
1024 argvs[j++] = arg[i];
1025 } else {
1026 argvs[j++] = arg[i++];
1027 argvs[j++] = arg[i];
1029 } else {
1030 if (state == MA_START) {
1031 argv[argc] = argvs + j;
1032 state = MA_UNQUOTED;
1033 if (lastquote != NULL)
1034 *lastquote = '\0';
1036 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1037 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1039 * Special case for sftp: append
1040 * escaped glob sequence -
1041 * glob will undo one level of
1042 * escaping.
1044 argvs[j++] = arg[i++];
1045 argvs[j++] = arg[i];
1046 } else {
1047 /* Unescape everything */
1048 /* XXX support \n and friends? */
1049 i++;
1050 argvs[j++] = arg[i];
1053 } else if (arg[i] == '#') {
1054 if (state == MA_SQUOTE || state == MA_DQUOTE)
1055 argvs[j++] = arg[i];
1056 else
1057 goto string_done;
1058 } else if (arg[i] == '\0') {
1059 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1060 if (sloppy) {
1061 state = MA_UNQUOTED;
1062 if (terminated != NULL)
1063 *terminated = 0;
1064 goto string_done;
1066 error("Unterminated quoted argument");
1067 return NULL;
1069 string_done:
1070 if (state == MA_UNQUOTED) {
1071 argvs[j++] = '\0';
1072 argc++;
1074 break;
1075 } else {
1076 if (state == MA_START) {
1077 argv[argc] = argvs + j;
1078 state = MA_UNQUOTED;
1079 if (lastquote != NULL)
1080 *lastquote = '\0';
1082 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1083 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1085 * Special case for sftp: escape quoted
1086 * glob(3) wildcards. NB. string can grow
1087 * here.
1089 if (j >= sizeof(argvs) - 3)
1090 goto args_too_longs;
1091 argvs[j++] = '\\';
1092 argvs[j++] = arg[i];
1093 } else
1094 argvs[j++] = arg[i];
1096 i++;
1098 *argcp = argc;
1099 return argv;
1102 static int
1103 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1104 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1106 const char *cmd, *cp = *cpp;
1107 char *cp2, **argv;
1108 int base = 0;
1109 long l;
1110 int i, cmdnum, optidx, argc;
1112 /* Skip leading whitespace */
1113 cp = cp + strspn(cp, WHITESPACE);
1115 /* Ignore blank lines and lines which begin with comment '#' char */
1116 if (*cp == '\0' || *cp == '#')
1117 return (0);
1119 /* Check for leading '-' (disable error processing) */
1120 *iflag = 0;
1121 if (*cp == '-') {
1122 *iflag = 1;
1123 cp++;
1126 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1127 return -1;
1129 /* Figure out which command we have */
1130 for (i = 0; cmds[i].c != NULL; i++) {
1131 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1132 break;
1134 cmdnum = cmds[i].n;
1135 cmd = cmds[i].c;
1137 /* Special case */
1138 if (*cp == '!') {
1139 cp++;
1140 cmdnum = I_SHELL;
1141 } else if (cmdnum == -1) {
1142 error("Invalid command.");
1143 return -1;
1146 /* Get arguments and parse flags */
1147 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1148 *path1 = *path2 = NULL;
1149 optidx = 1;
1150 switch (cmdnum) {
1151 case I_GET:
1152 case I_PUT:
1153 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1154 return -1;
1155 /* Get first pathname (mandatory) */
1156 if (argc - optidx < 1) {
1157 error("You must specify at least one path after a "
1158 "%s command.", cmd);
1159 return -1;
1161 *path1 = xstrdup(argv[optidx]);
1162 /* Get second pathname (optional) */
1163 if (argc - optidx > 1) {
1164 *path2 = xstrdup(argv[optidx + 1]);
1165 /* Destination is not globbed */
1166 undo_glob_escape(*path2);
1168 break;
1169 case I_RENAME:
1170 case I_SYMLINK:
1171 if (argc - optidx < 2) {
1172 error("You must specify two paths after a %s "
1173 "command.", cmd);
1174 return -1;
1176 *path1 = xstrdup(argv[optidx]);
1177 *path2 = xstrdup(argv[optidx + 1]);
1178 /* Paths are not globbed */
1179 undo_glob_escape(*path1);
1180 undo_glob_escape(*path2);
1181 break;
1182 case I_RM:
1183 case I_MKDIR:
1184 case I_RMDIR:
1185 case I_CHDIR:
1186 case I_LCHDIR:
1187 case I_LMKDIR:
1188 /* Get pathname (mandatory) */
1189 if (argc - optidx < 1) {
1190 error("You must specify a path after a %s command.",
1191 cmd);
1192 return -1;
1194 *path1 = xstrdup(argv[optidx]);
1195 /* Only "rm" globs */
1196 if (cmdnum != I_RM)
1197 undo_glob_escape(*path1);
1198 break;
1199 case I_DF:
1200 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1201 iflag)) == -1)
1202 return -1;
1203 /* Default to current directory if no path specified */
1204 if (argc - optidx < 1)
1205 *path1 = NULL;
1206 else {
1207 *path1 = xstrdup(argv[optidx]);
1208 undo_glob_escape(*path1);
1210 break;
1211 case I_LS:
1212 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1213 return(-1);
1214 /* Path is optional */
1215 if (argc - optidx > 0)
1216 *path1 = xstrdup(argv[optidx]);
1217 break;
1218 case I_LLS:
1219 /* Skip ls command and following whitespace */
1220 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1221 case I_SHELL:
1222 /* Uses the rest of the line */
1223 break;
1224 case I_LUMASK:
1225 case I_CHMOD:
1226 base = 8;
1227 case I_CHOWN:
1228 case I_CHGRP:
1229 /* Get numeric arg (mandatory) */
1230 if (argc - optidx < 1)
1231 goto need_num_arg;
1232 errno = 0;
1233 l = strtol(argv[optidx], &cp2, base);
1234 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1235 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1236 l < 0) {
1237 need_num_arg:
1238 error("You must supply a numeric argument "
1239 "to the %s command.", cmd);
1240 return -1;
1242 *n_arg = l;
1243 if (cmdnum == I_LUMASK)
1244 break;
1245 /* Get pathname (mandatory) */
1246 if (argc - optidx < 2) {
1247 error("You must specify a path after a %s command.",
1248 cmd);
1249 return -1;
1251 *path1 = xstrdup(argv[optidx + 1]);
1252 break;
1253 case I_QUIT:
1254 case I_PWD:
1255 case I_LPWD:
1256 case I_HELP:
1257 case I_VERSION:
1258 case I_PROGRESS:
1259 break;
1260 default:
1261 fatal("Command not implemented");
1264 *cpp = cp;
1265 return(cmdnum);
1268 static int
1269 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1270 int err_abort)
1272 char *path1, *path2, *tmp;
1273 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1274 unsigned long n_arg = 0;
1275 Attrib a, *aa;
1276 char path_buf[MAXPATHLEN];
1277 int err = 0;
1278 glob_t g;
1280 path1 = path2 = NULL;
1281 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1282 &path1, &path2);
1284 if (iflag != 0)
1285 err_abort = 0;
1287 memset(&g, 0, sizeof(g));
1289 /* Perform command */
1290 switch (cmdnum) {
1291 case 0:
1292 /* Blank line */
1293 break;
1294 case -1:
1295 /* Unrecognized command */
1296 err = -1;
1297 break;
1298 case I_GET:
1299 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1300 break;
1301 case I_PUT:
1302 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1303 break;
1304 case I_RENAME:
1305 path1 = make_absolute(path1, *pwd);
1306 path2 = make_absolute(path2, *pwd);
1307 err = do_rename(conn, path1, path2);
1308 break;
1309 case I_SYMLINK:
1310 path2 = make_absolute(path2, *pwd);
1311 err = do_symlink(conn, path1, path2);
1312 break;
1313 case I_RM:
1314 path1 = make_absolute(path1, *pwd);
1315 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1316 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1317 printf("Removing %s\n", g.gl_pathv[i]);
1318 err = do_rm(conn, g.gl_pathv[i]);
1319 if (err != 0 && err_abort)
1320 break;
1322 break;
1323 case I_MKDIR:
1324 path1 = make_absolute(path1, *pwd);
1325 attrib_clear(&a);
1326 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1327 a.perm = 0777;
1328 err = do_mkdir(conn, path1, &a, 1);
1329 break;
1330 case I_RMDIR:
1331 path1 = make_absolute(path1, *pwd);
1332 err = do_rmdir(conn, path1);
1333 break;
1334 case I_CHDIR:
1335 path1 = make_absolute(path1, *pwd);
1336 if ((tmp = do_realpath(conn, path1)) == NULL) {
1337 err = 1;
1338 break;
1340 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1341 xfree(tmp);
1342 err = 1;
1343 break;
1345 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1346 error("Can't change directory: Can't check target");
1347 xfree(tmp);
1348 err = 1;
1349 break;
1351 if (!S_ISDIR(aa->perm)) {
1352 error("Can't change directory: \"%s\" is not "
1353 "a directory", tmp);
1354 xfree(tmp);
1355 err = 1;
1356 break;
1358 xfree(*pwd);
1359 *pwd = tmp;
1360 break;
1361 case I_LS:
1362 if (!path1) {
1363 do_globbed_ls(conn, *pwd, *pwd, lflag);
1364 break;
1367 /* Strip pwd off beginning of non-absolute paths */
1368 tmp = NULL;
1369 if (*path1 != '/')
1370 tmp = *pwd;
1372 path1 = make_absolute(path1, *pwd);
1373 err = do_globbed_ls(conn, path1, tmp, lflag);
1374 break;
1375 case I_DF:
1376 /* Default to current directory if no path specified */
1377 if (path1 == NULL)
1378 path1 = xstrdup(*pwd);
1379 path1 = make_absolute(path1, *pwd);
1380 err = do_df(conn, path1, hflag, iflag);
1381 break;
1382 case I_LCHDIR:
1383 if (chdir(path1) == -1) {
1384 error("Couldn't change local directory to "
1385 "\"%s\": %s", path1, strerror(errno));
1386 err = 1;
1388 break;
1389 case I_LMKDIR:
1390 if (mkdir(path1, 0777) == -1) {
1391 error("Couldn't create local directory "
1392 "\"%s\": %s", path1, strerror(errno));
1393 err = 1;
1395 break;
1396 case I_LLS:
1397 local_do_ls(cmd);
1398 break;
1399 case I_SHELL:
1400 local_do_shell(cmd);
1401 break;
1402 case I_LUMASK:
1403 umask(n_arg);
1404 printf("Local umask: %03lo\n", n_arg);
1405 break;
1406 case I_CHMOD:
1407 path1 = make_absolute(path1, *pwd);
1408 attrib_clear(&a);
1409 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1410 a.perm = n_arg;
1411 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1412 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1413 printf("Changing mode on %s\n", g.gl_pathv[i]);
1414 err = do_setstat(conn, g.gl_pathv[i], &a);
1415 if (err != 0 && err_abort)
1416 break;
1418 break;
1419 case I_CHOWN:
1420 case I_CHGRP:
1421 path1 = make_absolute(path1, *pwd);
1422 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1423 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1424 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1425 if (err_abort) {
1426 err = -1;
1427 break;
1428 } else
1429 continue;
1431 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1432 error("Can't get current ownership of "
1433 "remote file \"%s\"", g.gl_pathv[i]);
1434 if (err_abort) {
1435 err = -1;
1436 break;
1437 } else
1438 continue;
1440 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1441 if (cmdnum == I_CHOWN) {
1442 printf("Changing owner on %s\n", g.gl_pathv[i]);
1443 aa->uid = n_arg;
1444 } else {
1445 printf("Changing group on %s\n", g.gl_pathv[i]);
1446 aa->gid = n_arg;
1448 err = do_setstat(conn, g.gl_pathv[i], aa);
1449 if (err != 0 && err_abort)
1450 break;
1452 break;
1453 case I_PWD:
1454 printf("Remote working directory: %s\n", *pwd);
1455 break;
1456 case I_LPWD:
1457 if (!getcwd(path_buf, sizeof(path_buf))) {
1458 error("Couldn't get local cwd: %s", strerror(errno));
1459 err = -1;
1460 break;
1462 printf("Local working directory: %s\n", path_buf);
1463 break;
1464 case I_QUIT:
1465 /* Processed below */
1466 break;
1467 case I_HELP:
1468 help();
1469 break;
1470 case I_VERSION:
1471 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1472 break;
1473 case I_PROGRESS:
1474 showprogress = !showprogress;
1475 if (showprogress)
1476 printf("Progress meter enabled\n");
1477 else
1478 printf("Progress meter disabled\n");
1479 break;
1480 default:
1481 fatal("%d is not implemented", cmdnum);
1484 if (g.gl_pathc)
1485 globfree(&g);
1486 if (path1)
1487 xfree(path1);
1488 if (path2)
1489 xfree(path2);
1491 /* If an unignored error occurs in batch mode we should abort. */
1492 if (err_abort && err != 0)
1493 return (-1);
1494 else if (cmdnum == I_QUIT)
1495 return (1);
1497 return (0);
1500 #ifdef USE_LIBEDIT
1501 static char *
1502 prompt(EditLine *el)
1504 return ("sftp> ");
1507 /* Display entries in 'list' after skipping the first 'len' chars */
1508 static void
1509 complete_display(char **list, u_int len)
1511 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1512 struct winsize ws;
1513 char *tmp;
1515 /* Count entries for sort and find longest */
1516 for (y = 0; list[y]; y++)
1517 m = MAX(m, strlen(list[y]));
1519 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1520 width = ws.ws_col;
1522 m = m > len ? m - len : 0;
1523 columns = width / (m + 2);
1524 columns = MAX(columns, 1);
1525 colspace = width / columns;
1526 colspace = MIN(colspace, width);
1528 printf("\n");
1529 m = 1;
1530 for (y = 0; list[y]; y++) {
1531 llen = strlen(list[y]);
1532 tmp = llen > len ? list[y] + len : "";
1533 printf("%-*s", colspace, tmp);
1534 if (m >= columns) {
1535 printf("\n");
1536 m = 1;
1537 } else
1538 m++;
1540 printf("\n");
1544 * Given a "list" of words that begin with a common prefix of "word",
1545 * attempt to find an autocompletion to extends "word" by the next
1546 * characters common to all entries in "list".
1548 static char *
1549 complete_ambiguous(const char *word, char **list, size_t count)
1551 if (word == NULL)
1552 return NULL;
1554 if (count > 0) {
1555 u_int y, matchlen = strlen(list[0]);
1557 /* Find length of common stem */
1558 for (y = 1; list[y]; y++) {
1559 u_int x;
1561 for (x = 0; x < matchlen; x++)
1562 if (list[0][x] != list[y][x])
1563 break;
1565 matchlen = x;
1568 if (matchlen > strlen(word)) {
1569 char *tmp = xstrdup(list[0]);
1571 tmp[matchlen] = '\0';
1572 return tmp;
1576 return xstrdup(word);
1579 /* Autocomplete a sftp command */
1580 static int
1581 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1582 int terminated)
1584 u_int y, count = 0, cmdlen, tmplen;
1585 char *tmp, **list, argterm[3];
1586 const LineInfo *lf;
1588 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1590 /* No command specified: display all available commands */
1591 if (cmd == NULL) {
1592 for (y = 0; cmds[y].c; y++)
1593 list[count++] = xstrdup(cmds[y].c);
1595 list[count] = NULL;
1596 complete_display(list, 0);
1598 for (y = 0; list[y] != NULL; y++)
1599 xfree(list[y]);
1600 xfree(list);
1601 return count;
1604 /* Prepare subset of commands that start with "cmd" */
1605 cmdlen = strlen(cmd);
1606 for (y = 0; cmds[y].c; y++) {
1607 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1608 list[count++] = xstrdup(cmds[y].c);
1610 list[count] = NULL;
1612 if (count == 0)
1613 return 0;
1615 /* Complete ambigious command */
1616 tmp = complete_ambiguous(cmd, list, count);
1617 if (count > 1)
1618 complete_display(list, 0);
1620 for (y = 0; list[y]; y++)
1621 xfree(list[y]);
1622 xfree(list);
1624 if (tmp != NULL) {
1625 tmplen = strlen(tmp);
1626 cmdlen = strlen(cmd);
1627 /* If cmd may be extended then do so */
1628 if (tmplen > cmdlen)
1629 if (el_insertstr(el, tmp + cmdlen) == -1)
1630 fatal("el_insertstr failed.");
1631 lf = el_line(el);
1632 /* Terminate argument cleanly */
1633 if (count == 1) {
1634 y = 0;
1635 if (!terminated)
1636 argterm[y++] = quote;
1637 if (lastarg || *(lf->cursor) != ' ')
1638 argterm[y++] = ' ';
1639 argterm[y] = '\0';
1640 if (y > 0 && el_insertstr(el, argterm) == -1)
1641 fatal("el_insertstr failed.");
1643 xfree(tmp);
1646 return count;
1650 * Determine whether a particular sftp command's arguments (if any)
1651 * represent local or remote files.
1653 static int
1654 complete_is_remote(char *cmd) {
1655 int i;
1657 if (cmd == NULL)
1658 return -1;
1660 for (i = 0; cmds[i].c; i++) {
1661 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1662 return cmds[i].t;
1665 return -1;
1668 /* Autocomplete a filename "file" */
1669 static int
1670 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1671 char *file, int remote, int lastarg, char quote, int terminated)
1673 glob_t g;
1674 char *tmp, *tmp2, ins[3];
1675 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1676 const LineInfo *lf;
1678 /* Glob from "file" location */
1679 if (file == NULL)
1680 tmp = xstrdup("*");
1681 else
1682 xasprintf(&tmp, "%s*", file);
1684 memset(&g, 0, sizeof(g));
1685 if (remote != LOCAL) {
1686 tmp = make_absolute(tmp, remote_path);
1687 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1688 } else
1689 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1691 /* Determine length of pwd so we can trim completion display */
1692 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1693 /* Terminate counting on first unescaped glob metacharacter */
1694 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1695 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1696 hadglob = 1;
1697 break;
1699 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1700 tmplen++;
1701 if (tmp[tmplen] == '/')
1702 pwdlen = tmplen + 1; /* track last seen '/' */
1704 xfree(tmp);
1706 if (g.gl_matchc == 0)
1707 goto out;
1709 if (g.gl_matchc > 1)
1710 complete_display(g.gl_pathv, pwdlen);
1712 tmp = NULL;
1713 /* Don't try to extend globs */
1714 if (file == NULL || hadglob)
1715 goto out;
1717 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1718 tmp = path_strip(tmp2, remote_path);
1719 xfree(tmp2);
1721 if (tmp == NULL)
1722 goto out;
1724 tmplen = strlen(tmp);
1725 filelen = strlen(file);
1727 if (tmplen > filelen) {
1728 tmp2 = tmp + filelen;
1729 len = strlen(tmp2);
1730 /* quote argument on way out */
1731 for (i = 0; i < len; i++) {
1732 ins[0] = '\\';
1733 ins[1] = tmp2[i];
1734 ins[2] = '\0';
1735 switch (tmp2[i]) {
1736 case '\'':
1737 case '"':
1738 case '\\':
1739 case '\t':
1740 case ' ':
1741 if (quote == '\0' || tmp2[i] == quote) {
1742 if (el_insertstr(el, ins) == -1)
1743 fatal("el_insertstr "
1744 "failed.");
1745 break;
1747 /* FALLTHROUGH */
1748 default:
1749 if (el_insertstr(el, ins + 1) == -1)
1750 fatal("el_insertstr failed.");
1751 break;
1756 lf = el_line(el);
1758 * XXX should we really extend here? the user may not be done if
1759 * the filename is a directory.
1761 if (g.gl_matchc == 1) {
1762 i = 0;
1763 if (!terminated)
1764 ins[i++] = quote;
1765 if (lastarg || *(lf->cursor) != ' ')
1766 ins[i++] = ' ';
1767 ins[i] = '\0';
1768 if (i > 0 && el_insertstr(el, ins) == -1)
1769 fatal("el_insertstr failed.");
1771 xfree(tmp);
1773 out:
1774 globfree(&g);
1775 return g.gl_matchc;
1778 /* tab-completion hook function, called via libedit */
1779 static unsigned char
1780 complete(EditLine *el, int ch)
1782 char **argv, *line, quote;
1783 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1784 const LineInfo *lf;
1785 struct complete_ctx *complete_ctx;
1787 lf = el_line(el);
1788 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1789 fatal("%s: el_get failed", __func__);
1791 /* Figure out which argument the cursor points to */
1792 cursor = lf->cursor - lf->buffer;
1793 line = (char *)xmalloc(cursor + 1);
1794 memcpy(line, lf->buffer, cursor);
1795 line[cursor] = '\0';
1796 argv = makeargv(line, &carg, 1, &quote, &terminated);
1797 xfree(line);
1799 /* Get all the arguments on the line */
1800 len = lf->lastchar - lf->buffer;
1801 line = (char *)xmalloc(len + 1);
1802 memcpy(line, lf->buffer, len);
1803 line[len] = '\0';
1804 argv = makeargv(line, &argc, 1, NULL, NULL);
1806 /* Ensure cursor is at EOL or a argument boundary */
1807 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1808 line[cursor] != '\n') {
1809 xfree(line);
1810 return ret;
1813 if (carg == 0) {
1814 /* Show all available commands */
1815 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1816 ret = CC_REDISPLAY;
1817 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1818 /* Handle the command parsing */
1819 if (complete_cmd_parse(el, argv[0], argc == carg,
1820 quote, terminated) != 0)
1821 ret = CC_REDISPLAY;
1822 } else if (carg >= 1) {
1823 /* Handle file parsing */
1824 int remote = complete_is_remote(argv[0]);
1825 char *filematch = NULL;
1827 if (carg > 1 && line[cursor-1] != ' ')
1828 filematch = argv[carg - 1];
1830 if (remote != 0 &&
1831 complete_match(el, complete_ctx->conn,
1832 *complete_ctx->remote_pathp, filematch,
1833 remote, carg == argc, quote, terminated) != 0)
1834 ret = CC_REDISPLAY;
1837 xfree(line);
1838 return ret;
1840 #endif /* USE_LIBEDIT */
1843 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1845 char *remote_path;
1846 char *dir = NULL;
1847 char cmd[2048];
1848 int err, interactive;
1849 EditLine *el = NULL;
1850 #ifdef USE_LIBEDIT
1851 History *hl = NULL;
1852 HistEvent hev;
1853 extern char *__progname;
1854 struct complete_ctx complete_ctx;
1856 if (!batchmode && isatty(STDIN_FILENO)) {
1857 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1858 fatal("Couldn't initialise editline");
1859 if ((hl = history_init()) == NULL)
1860 fatal("Couldn't initialise editline history");
1861 history(hl, &hev, H_SETSIZE, 100);
1862 el_set(el, EL_HIST, history, hl);
1864 el_set(el, EL_PROMPT, prompt);
1865 el_set(el, EL_EDITOR, "emacs");
1866 el_set(el, EL_TERMINAL, NULL);
1867 el_set(el, EL_SIGNAL, 1);
1868 el_source(el, NULL);
1870 /* Tab Completion */
1871 el_set(el, EL_ADDFN, "ftp-complete",
1872 "Context senstive argument completion", complete);
1873 complete_ctx.conn = conn;
1874 complete_ctx.remote_pathp = &remote_path;
1875 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1876 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1878 #endif /* USE_LIBEDIT */
1880 remote_path = do_realpath(conn, ".");
1881 if (remote_path == NULL)
1882 fatal("Need cwd");
1884 if (file1 != NULL) {
1885 dir = xstrdup(file1);
1886 dir = make_absolute(dir, remote_path);
1888 if (remote_is_dir(conn, dir) && file2 == NULL) {
1889 printf("Changing to: %s\n", dir);
1890 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1891 if (parse_dispatch_command(conn, cmd,
1892 &remote_path, 1) != 0) {
1893 xfree(dir);
1894 xfree(remote_path);
1895 xfree(conn);
1896 return (-1);
1898 } else {
1899 if (file2 == NULL)
1900 snprintf(cmd, sizeof cmd, "get %s", dir);
1901 else
1902 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1903 file2);
1905 err = parse_dispatch_command(conn, cmd,
1906 &remote_path, 1);
1907 xfree(dir);
1908 xfree(remote_path);
1909 xfree(conn);
1910 return (err);
1912 xfree(dir);
1915 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1916 setvbuf(stdout, NULL, _IOLBF, 0);
1917 setvbuf(infile, NULL, _IOLBF, 0);
1918 #else
1919 setlinebuf(stdout);
1920 setlinebuf(infile);
1921 #endif
1923 interactive = !batchmode && isatty(STDIN_FILENO);
1924 err = 0;
1925 for (;;) {
1926 char *cp;
1928 signal(SIGINT, SIG_IGN);
1930 if (el == NULL) {
1931 if (interactive)
1932 printf("sftp> ");
1933 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1934 if (interactive)
1935 printf("\n");
1936 break;
1938 if (!interactive) { /* Echo command */
1939 printf("sftp> %s", cmd);
1940 if (strlen(cmd) > 0 &&
1941 cmd[strlen(cmd) - 1] != '\n')
1942 printf("\n");
1944 } else {
1945 #ifdef USE_LIBEDIT
1946 const char *line;
1947 int count = 0;
1949 if ((line = el_gets(el, &count)) == NULL ||
1950 count <= 0) {
1951 printf("\n");
1952 break;
1954 history(hl, &hev, H_ENTER, line);
1955 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1956 fprintf(stderr, "Error: input line too long\n");
1957 continue;
1959 #endif /* USE_LIBEDIT */
1962 cp = strrchr(cmd, '\n');
1963 if (cp)
1964 *cp = '\0';
1966 /* Handle user interrupts gracefully during commands */
1967 interrupted = 0;
1968 signal(SIGINT, cmd_interrupt);
1970 err = parse_dispatch_command(conn, cmd, &remote_path,
1971 batchmode);
1972 if (err != 0)
1973 break;
1975 xfree(remote_path);
1976 xfree(conn);
1978 #ifdef USE_LIBEDIT
1979 if (el != NULL)
1980 el_end(el);
1981 #endif /* USE_LIBEDIT */
1983 /* err == 1 signifies normal "quit" exit */
1984 return (err >= 0 ? 0 : -1);
1987 static void
1988 connect_to_server(char *path, char **args, int *in, int *out)
1990 int c_in, c_out;
1992 #ifdef USE_PIPES
1993 int pin[2], pout[2];
1995 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1996 fatal("pipe: %s", strerror(errno));
1997 *in = pin[0];
1998 *out = pout[1];
1999 c_in = pout[0];
2000 c_out = pin[1];
2001 #else /* USE_PIPES */
2002 int inout[2];
2004 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2005 fatal("socketpair: %s", strerror(errno));
2006 *in = *out = inout[0];
2007 c_in = c_out = inout[1];
2008 #endif /* USE_PIPES */
2010 if ((sshpid = fork()) == -1)
2011 fatal("fork: %s", strerror(errno));
2012 else if (sshpid == 0) {
2013 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2014 (dup2(c_out, STDOUT_FILENO) == -1)) {
2015 fprintf(stderr, "dup2: %s\n", strerror(errno));
2016 _exit(1);
2018 close(*in);
2019 close(*out);
2020 close(c_in);
2021 close(c_out);
2024 * The underlying ssh is in the same process group, so we must
2025 * ignore SIGINT if we want to gracefully abort commands,
2026 * otherwise the signal will make it to the ssh process and
2027 * kill it too
2029 signal(SIGINT, SIG_IGN);
2030 execvp(path, args);
2031 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2032 _exit(1);
2035 signal(SIGTERM, killchild);
2036 signal(SIGINT, killchild);
2037 signal(SIGHUP, killchild);
2038 close(c_in);
2039 close(c_out);
2042 static void
2043 usage(void)
2045 extern char *__progname;
2047 fprintf(stderr,
2048 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2049 " [-D sftp_server_path] [-F ssh_config] "
2050 "[-i identity_file]\n"
2051 " [-o ssh_option] [-P port] [-R num_requests] "
2052 "[-S program]\n"
2053 " [-s subsystem | sftp_server] host\n"
2054 " %s [user@]host[:file ...]\n"
2055 " %s [user@]host[:dir[/]]\n"
2056 " %s -b batchfile [user@]host\n",
2057 __progname, __progname, __progname, __progname);
2058 exit(1);
2062 main(int argc, char **argv)
2064 int in, out, ch, err;
2065 char *host = NULL, *userhost, *cp, *file2 = NULL;
2066 int debug_level = 0, sshver = 2;
2067 char *file1 = NULL, *sftp_server = NULL;
2068 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2069 LogLevel ll = SYSLOG_LEVEL_INFO;
2070 arglist args;
2071 extern int optind;
2072 extern char *optarg;
2073 struct sftp_conn *conn;
2074 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2075 size_t num_requests = DEFAULT_NUM_REQUESTS;
2077 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2078 sanitise_stdfd();
2080 __progname = ssh_get_progname(argv[0]);
2081 memset(&args, '\0', sizeof(args));
2082 args.list = NULL;
2083 addargs(&args, "%s", ssh_program);
2084 addargs(&args, "-oForwardX11 no");
2085 addargs(&args, "-oForwardAgent no");
2086 addargs(&args, "-oPermitLocalCommand no");
2087 addargs(&args, "-oClearAllForwardings yes");
2089 ll = SYSLOG_LEVEL_INFO;
2090 infile = stdin;
2092 while ((ch = getopt(argc, argv,
2093 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2094 switch (ch) {
2095 /* Passed through to ssh(1) */
2096 case '4':
2097 case '6':
2098 case 'C':
2099 addargs(&args, "-%c", ch);
2100 break;
2101 /* Passed through to ssh(1) with argument */
2102 case 'F':
2103 case 'c':
2104 case 'i':
2105 case 'o':
2106 addargs(&args, "-%c", ch);
2107 addargs(&args, "%s", optarg);
2108 break;
2109 case 'q':
2110 showprogress = 0;
2111 addargs(&args, "-%c", ch);
2112 break;
2113 case 'P':
2114 addargs(&args, "-oPort %s", optarg);
2115 break;
2116 case 'v':
2117 if (debug_level < 3) {
2118 addargs(&args, "-v");
2119 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2121 debug_level++;
2122 break;
2123 case '1':
2124 sshver = 1;
2125 if (sftp_server == NULL)
2126 sftp_server = _PATH_SFTP_SERVER;
2127 break;
2128 case '2':
2129 sshver = 2;
2130 break;
2131 case 'B':
2132 copy_buffer_len = strtol(optarg, &cp, 10);
2133 if (copy_buffer_len == 0 || *cp != '\0')
2134 fatal("Invalid buffer size \"%s\"", optarg);
2135 break;
2136 case 'b':
2137 if (batchmode)
2138 fatal("Batch file already specified.");
2140 /* Allow "-" as stdin */
2141 if (strcmp(optarg, "-") != 0 &&
2142 (infile = fopen(optarg, "r")) == NULL)
2143 fatal("%s (%s).", strerror(errno), optarg);
2144 showprogress = 0;
2145 batchmode = 1;
2146 addargs(&args, "-obatchmode yes");
2147 break;
2148 case 'p':
2149 global_pflag = 1;
2150 break;
2151 case 'D':
2152 sftp_direct = optarg;
2153 break;
2154 case 'r':
2155 global_rflag = 1;
2156 break;
2157 case 'R':
2158 num_requests = strtol(optarg, &cp, 10);
2159 if (num_requests == 0 || *cp != '\0')
2160 fatal("Invalid number of requests \"%s\"",
2161 optarg);
2162 break;
2163 case 's':
2164 sftp_server = optarg;
2165 break;
2166 case 'S':
2167 ssh_program = optarg;
2168 replacearg(&args, 0, "%s", ssh_program);
2169 break;
2170 case 'h':
2171 default:
2172 usage();
2176 if (!isatty(STDERR_FILENO))
2177 showprogress = 0;
2179 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2181 if (sftp_direct == NULL) {
2182 if (optind == argc || argc > (optind + 2))
2183 usage();
2185 userhost = xstrdup(argv[optind]);
2186 file2 = argv[optind+1];
2188 if ((host = strrchr(userhost, '@')) == NULL)
2189 host = userhost;
2190 else {
2191 *host++ = '\0';
2192 if (!userhost[0]) {
2193 fprintf(stderr, "Missing username\n");
2194 usage();
2196 addargs(&args, "-l");
2197 addargs(&args, "%s", userhost);
2200 if ((cp = colon(host)) != NULL) {
2201 *cp++ = '\0';
2202 file1 = cp;
2205 host = cleanhostname(host);
2206 if (!*host) {
2207 fprintf(stderr, "Missing hostname\n");
2208 usage();
2211 addargs(&args, "-oProtocol %d", sshver);
2213 /* no subsystem if the server-spec contains a '/' */
2214 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2215 addargs(&args, "-s");
2217 addargs(&args, "--");
2218 addargs(&args, "%s", host);
2219 addargs(&args, "%s", (sftp_server != NULL ?
2220 sftp_server : "sftp"));
2222 connect_to_server(ssh_program, args.list, &in, &out);
2223 } else {
2224 args.list = NULL;
2225 addargs(&args, "sftp-server");
2227 connect_to_server(sftp_direct, args.list, &in, &out);
2229 freeargs(&args);
2231 conn = do_init(in, out, copy_buffer_len, num_requests);
2232 if (conn == NULL)
2233 fatal("Couldn't initialise connection to server");
2235 if (!batchmode) {
2236 if (sftp_direct == NULL)
2237 fprintf(stderr, "Connected to %s.\n", host);
2238 else
2239 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2242 err = interactive_loop(conn, file1, file2);
2244 #if !defined(USE_PIPES)
2245 shutdown(in, SHUT_RDWR);
2246 shutdown(out, SHUT_RDWR);
2247 #endif
2249 close(in);
2250 close(out);
2251 if (batchmode)
2252 fclose(infile);
2254 while (waitpid(sshpid, NULL, 0) == -1)
2255 if (errno != EINTR)
2256 fatal("Couldn't wait for ssh process: %s",
2257 strerror(errno));
2259 exit(err == 0 ? 0 : 1);