]> andersk Git - openssh.git/blob - sftp.c
- djm@cvs.openbsd.org 2010/01/30 02:54:53
[openssh.git] / sftp.c
1 /* $OpenBSD: sftp.c,v 1.123 2010/01/27 19:21:39 djm Exp $ */
2 /*
3  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4  *
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.
8  *
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.
16  */
17
18 #include "includes.h"
19
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
31
32 #include <ctype.h>
33 #include <errno.h>
34
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>
52
53 #ifdef HAVE_UTIL_H
54 # include <util.h>
55 #endif
56
57 #ifdef HAVE_LIBUTIL_H
58 # include <libutil.h>
59 #endif
60
61 #include "xmalloc.h"
62 #include "log.h"
63 #include "pathnames.h"
64 #include "misc.h"
65
66 #include "sftp.h"
67 #include "buffer.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
70
71 #define DEFAULT_COPY_BUFLEN     32768   /* Size of buffer for up/download */
72 #define DEFAULT_NUM_REQUESTS    64      /* # concurrent outstanding requests */
73
74 /* File to read commands from */
75 FILE* infile;
76
77 /* Are we in batchfile mode? */
78 int batchmode = 0;
79
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
82
83 /* This is set to 0 if the progressmeter is not desired. */
84 int showprogress = 1;
85
86 /* When this option is set, we always recursively download/upload directories */
87 int global_rflag = 0;
88
89 /* When this option is set, the file transfers will always preserve times */
90 int global_pflag = 0;
91
92 /* SIGINT received during command processing */
93 volatile sig_atomic_t interrupted = 0;
94
95 /* I wish qsort() took a separate ctx for the comparison function...*/
96 int sort_flag;
97
98 /* Context used for commandline completion */
99 struct complete_ctx {
100         struct sftp_conn *conn;
101         char **remote_pathp;
102 };
103
104 int remote_glob(struct sftp_conn *, const char *, int,
105     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
106
107 extern char *__progname;
108
109 /* Separators for interactive commands */
110 #define WHITESPACE " \t\r\n"
111
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. */
122
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)
125
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
151
152 struct CMD {
153         const char *c;
154         const int n;
155         const int t;
156 };
157
158 /* Type of completion */
159 #define NOARGS  0
160 #define REMOTE  1
161 #define LOCAL   2
162
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      }
196 };
197
198 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
199
200 /* ARGSUSED */
201 static void
202 killchild(int signo)
203 {
204         if (sshpid > 1) {
205                 kill(sshpid, SIGTERM);
206                 waitpid(sshpid, NULL, 0);
207         }
208
209         _exit(1);
210 }
211
212 /* ARGSUSED */
213 static void
214 cmd_interrupt(int signo)
215 {
216         const char msg[] = "\rInterrupt  \n";
217         int olderrno = errno;
218
219         write(STDERR_FILENO, msg, sizeof(msg) - 1);
220         interrupted = 1;
221         errno = olderrno;
222 }
223
224 static void
225 help(void)
226 {
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");
258 }
259
260 static void
261 local_do_shell(const char *args)
262 {
263         int status;
264         char *shell;
265         pid_t pid;
266
267         if (!*args)
268                 args = NULL;
269
270         if ((shell = getenv("SHELL")) == NULL)
271                 shell = _PATH_BSHELL;
272
273         if ((pid = fork()) == -1)
274                 fatal("Couldn't fork: %s", strerror(errno));
275
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);
284                 }
285                 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
286                     strerror(errno));
287                 _exit(1);
288         }
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));
296 }
297
298 static void
299 local_do_ls(const char *args)
300 {
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);
306
307                 /* XXX: quoting - rip quoting code from ftp? */
308                 snprintf(buf, len, _PATH_LS " %s", args);
309                 local_do_shell(buf);
310                 xfree(buf);
311         }
312 }
313
314 /* Strip one path (usually the pwd) from the start of another */
315 static char *
316 path_strip(char *path, char *strip)
317 {
318         size_t len;
319
320         if (strip == NULL)
321                 return (xstrdup(path));
322
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));
328         }
329
330         return (xstrdup(path));
331 }
332
333 static char *
334 make_absolute(char *p, char *pwd)
335 {
336         char *abs_str;
337
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);
345 }
346
347 static int
348 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
349     int *rflag)
350 {
351         extern int opterr, optind, optopt, optreset;
352         int ch;
353
354         optind = optreset = 1;
355         opterr = 0;
356
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;
371                 }
372         }
373
374         return optind;
375 }
376
377 static int
378 parse_ls_flags(char **argv, int argc, int *lflag)
379 {
380         extern int opterr, optind, optopt, optreset;
381         int ch;
382
383         optind = optreset = 1;
384         opterr = 0;
385
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;
424                 }
425         }
426
427         return optind;
428 }
429
430 static int
431 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
432 {
433         extern int opterr, optind, optopt, optreset;
434         int ch;
435
436         optind = optreset = 1;
437         opterr = 0;
438
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;
451                 }
452         }
453
454         return optind;
455 }
456
457 static int
458 is_dir(char *path)
459 {
460         struct stat sb;
461
462         /* XXX: report errors? */
463         if (stat(path, &sb) == -1)
464                 return(0);
465
466         return(S_ISDIR(sb.st_mode));
467 }
468
469 static int
470 remote_is_dir(struct sftp_conn *conn, char *path)
471 {
472         Attrib *a;
473
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));
480 }
481
482 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
483 static int
484 pathname_is_dir(char *pathname)
485 {
486         size_t l = strlen(pathname);
487
488         return l > 0 && pathname[l - 1] == '/';
489 }
490
491 static int
492 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
493     int pflag, int rflag)
494 {
495         char *abs_src = NULL;
496         char *abs_dst = NULL;
497         glob_t g;
498         char *filename, *tmp=NULL;
499         int i, err = 0;
500
501         abs_src = xstrdup(src);
502         abs_src = make_absolute(abs_src, pwd);
503         memset(&g, 0, sizeof(g));
504
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;
510         }
511
512         /*
513          * If multiple matches then dst must be a directory or
514          * unspecified.
515          */
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;
521         }
522
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;
530                 }
531
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);
537                         }
538                 } else if (dst) {
539                         abs_dst = path_append(dst, filename);
540                 } else {
541                         abs_dst = xstrdup(filename);
542                 }
543                 xfree(tmp);
544
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;
554                 }
555                 xfree(abs_dst);
556                 abs_dst = NULL;
557         }
558
559 out:
560         xfree(abs_src);
561         globfree(&g);
562         return(err);
563 }
564
565 static int
566 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
567     int pflag, int rflag)
568 {
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;
576
577         if (dst) {
578                 tmp_dst = xstrdup(dst);
579                 tmp_dst = make_absolute(tmp_dst, pwd);
580         }
581
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;
588         }
589
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);
593
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;
600         }
601
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;
607                 }
608                 
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;
615                 }
616
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);
627                 }
628                 xfree(tmp);
629
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;
639                 }
640         }
641
642 out:
643         if (abs_dst)
644                 xfree(abs_dst);
645         if (tmp_dst)
646                 xfree(tmp_dst);
647         globfree(&g);
648         return(err);
649 }
650
651 static int
652 sdirent_comp(const void *aa, const void *bb)
653 {
654         SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
655         SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
656         int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
657
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));
665
666         fatal("Unknown ls sort type");
667 }
668
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)
672 {
673         int n;
674         u_int c = 1, colspace = 0, columns = 1;
675         SFTP_DIRENT **d;
676
677         if ((n = do_readdir(conn, path, &d)) != 0)
678                 return (n);
679
680         if (!(lflag & LS_SHORT_VIEW)) {
681                 u_int m = 0, width = 80;
682                 struct winsize ws;
683                 char *tmp;
684
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));
689                 }
690
691                 /* Add any subpath that also needs to be counted */
692                 tmp = path_strip(path, strip_path);
693                 m += strlen(tmp);
694                 xfree(tmp);
695
696                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
697                         width = ws.ws_col;
698
699                 columns = width / (m + 2);
700                 columns = MAX(columns, 1);
701                 colspace = width / columns;
702                 colspace = MIN(colspace, width);
703         }
704
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);
710         }
711
712         for (n = 0; d[n] != NULL && !interrupted; n++) {
713                 char *tmp, *fname;
714
715                 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
716                         continue;
717
718                 tmp = path_append(path, d[n]->filename);
719                 fname = path_strip(tmp, strip_path);
720                 xfree(tmp);
721
722                 if (lflag & LS_LONG_VIEW) {
723                         if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
724                                 char *lname;
725                                 struct stat sb;
726
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++;
742                 }
743
744                 xfree(fname);
745         }
746
747         if (!(lflag & LS_LONG_VIEW) && (c != 1))
748                 printf("\n");
749
750         free_sftp_dirents(d);
751         return (0);
752 }
753
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)
758 {
759         glob_t g;
760         u_int i, c = 1, colspace = 0, columns = 1;
761         Attrib *a = NULL;
762
763         memset(&g, 0, sizeof(g));
764
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);
771         }
772
773         if (interrupted)
774                 goto out;
775
776         /*
777          * If the glob returns a single match and it is a directory,
778          * then just list its contents.
779          */
780         if (g.gl_matchc == 1) {
781                 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
782                         globfree(&g);
783                         return (-1);
784                 }
785                 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
786                     S_ISDIR(a->perm)) {
787                         int err;
788
789                         err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
790                         globfree(&g);
791                         return (err);
792                 }
793         }
794
795         if (!(lflag & LS_SHORT_VIEW)) {
796                 u_int m = 0, width = 80;
797                 struct winsize ws;
798
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]));
802
803                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
804                         width = ws.ws_col;
805
806                 columns = width / (m + 2);
807                 columns = MAX(columns, 1);
808                 colspace = width / columns;
809         }
810
811         for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
812                 char *fname;
813
814                 fname = path_strip(g.gl_pathv[i], strip_path);
815
816                 if (lflag & LS_LONG_VIEW) {
817                         char *lname;
818                         struct stat sb;
819
820                         /*
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.
826                          */
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++;
842                 }
843                 xfree(fname);
844         }
845
846         if (!(lflag & LS_LONG_VIEW) && (c != 1))
847                 printf("\n");
848
849  out:
850         if (g.gl_pathc)
851                 globfree(&g);
852
853         return (0);
854 }
855
856 static int
857 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
858 {
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;
865
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));
902         }
903         return 0;
904 }
905
906 /*
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.
910  */
911 static void
912 undo_glob_escape(char *s)
913 {
914         size_t i, j;
915
916         for (i = j = 0;;) {
917                 if (s[i] == '\0') {
918                         s[j] = '\0';
919                         return;
920                 }
921                 if (s[i] != '\\') {
922                         s[j++] = s[i++];
923                         continue;
924                 }
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;
942                 }
943         }
944 }
945
946 /*
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.
952  *
953  * Returns NULL on error or a NULL-terminated array of arguments.
954  *
955  * If "lastquote" is not NULL, the quoting character used for the last
956  * argument is placed in *lastquote ("\0", "'" or "\"").
957  * 
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.
961  */
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)
967 {
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;
973
974         *argcp = argc = 0;
975         if (strlen(arg) > sizeof(argvs) - 1) {
976  args_too_longs:
977                 error("string too long");
978                 return NULL;
979         }
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] == '*') {
1018                                         /*
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.
1023                                          */
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];
1033                                 }
1034                         } else {
1035                                 if (state == MA_START) {
1036                                         argv[argc] = argvs + j;
1037                                         state = MA_UNQUOTED;
1038                                         if (lastquote != NULL)
1039                                                 *lastquote = '\0';
1040                                 }
1041                                 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1042                                     arg[i + 1] == '*' || arg[i + 1] == '\\') {
1043                                         /*
1044                                          * Special case for sftp: append
1045                                          * escaped glob sequence -
1046                                          * glob will undo one level of
1047                                          * escaping.
1048                                          */
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];
1056                                 }
1057                         }
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;
1070                                 }
1071                                 error("Unterminated quoted argument");
1072                                 return NULL;
1073                         }
1074  string_done:
1075                         if (state == MA_UNQUOTED) {
1076                                 argvs[j++] = '\0';
1077                                 argc++;
1078                         }
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';
1086                         }
1087                         if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1088                             (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1089                                 /*
1090                                  * Special case for sftp: escape quoted
1091                                  * glob(3) wildcards. NB. string can grow
1092                                  * here.
1093                                  */
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];
1100                 }
1101                 i++;
1102         }
1103         *argcp = argc;
1104         return argv;
1105 }
1106
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)
1110 {
1111         const char *cmd, *cp = *cpp;
1112         char *cp2, **argv;
1113         int base = 0;
1114         long l;
1115         int i, cmdnum, optidx, argc;
1116
1117         /* Skip leading whitespace */
1118         cp = cp + strspn(cp, WHITESPACE);
1119
1120         /* Check for leading '-' (disable error processing) */
1121         *iflag = 0;
1122         if (*cp == '-') {
1123                 *iflag = 1;
1124                 cp++;
1125                 cp = cp + strspn(cp, WHITESPACE);
1126         }
1127
1128         /* Ignore blank lines and lines which begin with comment '#' char */
1129         if (*cp == '\0' || *cp == '#')
1130                 return (0);
1131
1132         if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1133                 return -1;
1134
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;
1139         }
1140         cmdnum = cmds[i].n;
1141         cmd = cmds[i].c;
1142
1143         /* Special case */
1144         if (*cp == '!') {
1145                 cp++;
1146                 cmdnum = I_SHELL;
1147         } else if (cmdnum == -1) {
1148                 error("Invalid command.");
1149                 return -1;
1150         }
1151
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;
1166                 }
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);
1173                 }
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;
1181                 }
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;
1199                 }
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);
1215                 }
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;
1247                 }
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;
1256                 }
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");
1268         }
1269
1270         *cpp = cp;
1271         return(cmdnum);
1272 }
1273
1274 static int
1275 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1276     int err_abort)
1277 {
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;
1285
1286         path1 = path2 = NULL;
1287         cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1288             &path1, &path2);
1289
1290         if (iflag != 0)
1291                 err_abort = 0;
1292
1293         memset(&g, 0, sizeof(g));
1294
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;
1327                 }
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;
1345                 }
1346                 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1347                         xfree(tmp);
1348                         err = 1;
1349                         break;
1350                 }
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;
1356                 }
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;
1363                 }
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;
1371                 }
1372
1373                 /* Strip pwd off beginning of non-absolute paths */
1374                 tmp = NULL;
1375                 if (*path1 != '/')
1376                         tmp = *pwd;
1377
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;
1393                 }
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;
1400                 }
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;
1423                 }
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;
1436                         }
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;
1445                         }
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;
1453                         }
1454                         err = do_setstat(conn, g.gl_pathv[i], aa);
1455                         if (err != 0 && err_abort)
1456                                 break;
1457                 }
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;
1467                 }
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);
1488         }
1489
1490         if (g.gl_pathc)
1491                 globfree(&g);
1492         if (path1)
1493                 xfree(path1);
1494         if (path2)
1495                 xfree(path2);
1496
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);
1502
1503         return (0);
1504 }
1505
1506 #ifdef USE_LIBEDIT
1507 static char *
1508 prompt(EditLine *el)
1509 {
1510         return ("sftp> ");
1511 }
1512
1513 /* Display entries in 'list' after skipping the first 'len' chars */
1514 static void
1515 complete_display(char **list, u_int len)
1516 {
1517         u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1518         struct winsize ws;
1519         char *tmp;
1520
1521         /* Count entries for sort and find longest */
1522         for (y = 0; list[y]; y++) 
1523                 m = MAX(m, strlen(list[y]));
1524
1525         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1526                 width = ws.ws_col;
1527
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);
1533
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++;
1545         }
1546         printf("\n");
1547 }
1548
1549 /*
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".
1553  */
1554 static char *
1555 complete_ambiguous(const char *word, char **list, size_t count)
1556 {
1557         if (word == NULL)
1558                 return NULL;
1559
1560         if (count > 0) {
1561                 u_int y, matchlen = strlen(list[0]);
1562
1563                 /* Find length of common stem */
1564                 for (y = 1; list[y]; y++) {
1565                         u_int x;
1566
1567                         for (x = 0; x < matchlen; x++) 
1568                                 if (list[0][x] != list[y][x]) 
1569                                         break;
1570
1571                         matchlen = x;
1572                 }
1573
1574                 if (matchlen > strlen(word)) {
1575                         char *tmp = xstrdup(list[0]);
1576
1577                         tmp[matchlen] = '\0';
1578                         return tmp;
1579                 }
1580         } 
1581
1582         return xstrdup(word);
1583 }
1584
1585 /* Autocomplete a sftp command */
1586 static int
1587 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1588     int terminated)
1589 {
1590         u_int y, count = 0, cmdlen, tmplen;
1591         char *tmp, **list, argterm[3];
1592         const LineInfo *lf;
1593
1594         list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1595
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);
1600                 
1601                 list[count] = NULL;
1602                 complete_display(list, 0);
1603
1604                 for (y = 0; list[y] != NULL; y++)  
1605                         xfree(list[y]); 
1606                 xfree(list);
1607                 return count;
1608         }
1609
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);
1615         }
1616         list[count] = NULL;
1617
1618         if (count == 0)
1619                 return 0;
1620
1621         /* Complete ambigious command */
1622         tmp = complete_ambiguous(cmd, list, count);
1623         if (count > 1)
1624                 complete_display(list, 0);
1625
1626         for (y = 0; list[y]; y++)  
1627                 xfree(list[y]); 
1628         xfree(list);
1629
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.");
1648                 }
1649                 xfree(tmp);
1650         }
1651
1652         return count;
1653 }
1654
1655 /*
1656  * Determine whether a particular sftp command's arguments (if any)
1657  * represent local or remote files.
1658  */
1659 static int
1660 complete_is_remote(char *cmd) {
1661         int i;
1662
1663         if (cmd == NULL)
1664                 return -1;
1665
1666         for (i = 0; cmds[i].c; i++) {
1667                 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 
1668                         return cmds[i].t;
1669         }
1670
1671         return -1;
1672 }
1673
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)
1678 {
1679         glob_t g;
1680         char *tmp, *tmp2, ins[3];
1681         u_int i, hadglob, pwdlen, len, tmplen, filelen;
1682         const LineInfo *lf;
1683         
1684         /* Glob from "file" location */
1685         if (file == NULL)
1686                 tmp = xstrdup("*");
1687         else
1688                 xasprintf(&tmp, "%s*", file);
1689
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);
1696         
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;
1704                 }
1705                 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1706                         tmplen++;
1707                 if (tmp[tmplen] == '/')
1708                         pwdlen = tmplen + 1;    /* track last seen '/' */
1709         }
1710         xfree(tmp);
1711
1712         if (g.gl_matchc == 0) 
1713                 goto out;
1714
1715         if (g.gl_matchc > 1)
1716                 complete_display(g.gl_pathv, pwdlen);
1717
1718         tmp = NULL;
1719         /* Don't try to extend globs */
1720         if (file == NULL || hadglob)
1721                 goto out;
1722
1723         tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1724         tmp = path_strip(tmp2, remote_path);
1725         xfree(tmp2);
1726
1727         if (tmp == NULL)
1728                 goto out;
1729
1730         tmplen = strlen(tmp);
1731         filelen = strlen(file);
1732
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;
1752                                 }
1753                                 /* FALLTHROUGH */
1754                         default:
1755                                 if (el_insertstr(el, ins + 1) == -1)
1756                                         fatal("el_insertstr failed.");
1757                                 break;
1758                         }
1759                 }
1760         }
1761
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.");
1773         }
1774         xfree(tmp);
1775
1776  out:
1777         globfree(&g);
1778         return g.gl_matchc;
1779 }
1780
1781 /* tab-completion hook function, called via libedit */
1782 static unsigned char
1783 complete(EditLine *el, int ch)
1784 {
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;
1789
1790         lf = el_line(el);
1791         if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1792                 fatal("%s: el_get failed", __func__);
1793
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);
1801
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);
1808
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;
1814         }
1815
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;
1829
1830                 if (carg > 1 && line[cursor-1] != ' ')
1831                         filematch = argv[carg - 1];
1832
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;
1838         }
1839
1840         xfree(line);    
1841         return ret;
1842 }
1843 #endif /* USE_LIBEDIT */
1844
1845 int
1846 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1847 {
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;
1858
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);
1866
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);
1872
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);
1880         }
1881 #endif /* USE_LIBEDIT */
1882
1883         remote_path = do_realpath(conn, ".");
1884         if (remote_path == NULL)
1885                 fatal("Need cwd");
1886
1887         if (file1 != NULL) {
1888                 dir = xstrdup(file1);
1889                 dir = make_absolute(dir, remote_path);
1890
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);
1900                         }
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);
1907
1908                         err = parse_dispatch_command(conn, cmd,
1909                             &remote_path, 1);
1910                         xfree(dir);
1911                         xfree(remote_path);
1912                         xfree(conn);
1913                         return (err);
1914                 }
1915                 xfree(dir);
1916         }
1917
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
1925
1926         interactive = !batchmode && isatty(STDIN_FILENO);
1927         err = 0;
1928         for (;;) {
1929                 char *cp;
1930
1931                 signal(SIGINT, SIG_IGN);
1932
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;
1940                         }
1941                         if (!interactive) { /* Echo command */
1942                                 printf("sftp> %s", cmd);
1943                                 if (strlen(cmd) > 0 &&
1944                                     cmd[strlen(cmd) - 1] != '\n')
1945                                         printf("\n");
1946                         }
1947                 } else {
1948 #ifdef USE_LIBEDIT
1949                         const char *line;
1950                         int count = 0;
1951
1952                         if ((line = el_gets(el, &count)) == NULL ||
1953                             count <= 0) {
1954                                 printf("\n");
1955                                 break;
1956                         }
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;
1961                         }
1962 #endif /* USE_LIBEDIT */
1963                 }
1964
1965                 cp = strrchr(cmd, '\n');
1966                 if (cp)
1967                         *cp = '\0';
1968
1969                 /* Handle user interrupts gracefully during commands */
1970                 interrupted = 0;
1971                 signal(SIGINT, cmd_interrupt);
1972
1973                 err = parse_dispatch_command(conn, cmd, &remote_path,
1974                     batchmode);
1975                 if (err != 0)
1976                         break;
1977         }
1978         xfree(remote_path);
1979         xfree(conn);
1980
1981 #ifdef USE_LIBEDIT
1982         if (el != NULL)
1983                 el_end(el);
1984 #endif /* USE_LIBEDIT */
1985
1986         /* err == 1 signifies normal "quit" exit */
1987         return (err >= 0 ? 0 : -1);
1988 }
1989
1990 static void
1991 connect_to_server(char *path, char **args, int *in, int *out)
1992 {
1993         int c_in, c_out;
1994
1995 #ifdef USE_PIPES
1996         int pin[2], pout[2];
1997
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];
2006
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 */
2012
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);
2020                 }
2021                 close(*in);
2022                 close(*out);
2023                 close(c_in);
2024                 close(c_out);
2025
2026                 /*
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.  Contrawise, since sftp sends SIGTERMs to the
2031                  * underlying ssh, it must *not* ignore that signal.
2032                  */
2033                 signal(SIGINT, SIG_IGN);
2034                 signal(SIGTERM, SIG_DFL);
2035                 execvp(path, args);
2036                 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2037                 _exit(1);
2038         }
2039
2040         signal(SIGTERM, killchild);
2041         signal(SIGINT, killchild);
2042         signal(SIGHUP, killchild);
2043         close(c_in);
2044         close(c_out);
2045 }
2046
2047 static void
2048 usage(void)
2049 {
2050         extern char *__progname;
2051
2052         fprintf(stderr,
2053             "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2054             "          [-D sftp_server_path] [-F ssh_config] "
2055             "[-i identity_file]\n"
2056             "          [-o ssh_option] [-P port] [-R num_requests] "
2057             "[-S program]\n"
2058             "          [-s subsystem | sftp_server] host\n"
2059             "       %s [user@]host[:file ...]\n"
2060             "       %s [user@]host[:dir[/]]\n"
2061             "       %s -b batchfile [user@]host\n",
2062             __progname, __progname, __progname, __progname);
2063         exit(1);
2064 }
2065
2066 int
2067 main(int argc, char **argv)
2068 {
2069         int in, out, ch, err;
2070         char *host = NULL, *userhost, *cp, *file2 = NULL;
2071         int debug_level = 0, sshver = 2;
2072         char *file1 = NULL, *sftp_server = NULL;
2073         char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2074         LogLevel ll = SYSLOG_LEVEL_INFO;
2075         arglist args;
2076         extern int optind;
2077         extern char *optarg;
2078         struct sftp_conn *conn;
2079         size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2080         size_t num_requests = DEFAULT_NUM_REQUESTS;
2081
2082         /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2083         sanitise_stdfd();
2084
2085         __progname = ssh_get_progname(argv[0]);
2086         memset(&args, '\0', sizeof(args));
2087         args.list = NULL;
2088         addargs(&args, "%s", ssh_program);
2089         addargs(&args, "-oForwardX11 no");
2090         addargs(&args, "-oForwardAgent no");
2091         addargs(&args, "-oPermitLocalCommand no");
2092         addargs(&args, "-oClearAllForwardings yes");
2093
2094         ll = SYSLOG_LEVEL_INFO;
2095         infile = stdin;
2096
2097         while ((ch = getopt(argc, argv,
2098             "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2099                 switch (ch) {
2100                 /* Passed through to ssh(1) */
2101                 case '4':
2102                 case '6':
2103                 case 'C':
2104                         addargs(&args, "-%c", ch);
2105                         break;
2106                 /* Passed through to ssh(1) with argument */
2107                 case 'F':
2108                 case 'c':
2109                 case 'i':
2110                 case 'o':
2111                         addargs(&args, "-%c", ch);
2112                         addargs(&args, "%s", optarg);
2113                         break;
2114                 case 'q':
2115                         showprogress = 0;
2116                         addargs(&args, "-%c", ch);
2117                         break;
2118                 case 'P':
2119                         addargs(&args, "-oPort %s", optarg);
2120                         break;
2121                 case 'v':
2122                         if (debug_level < 3) {
2123                                 addargs(&args, "-v");
2124                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2125                         }
2126                         debug_level++;
2127                         break;
2128                 case '1':
2129                         sshver = 1;
2130                         if (sftp_server == NULL)
2131                                 sftp_server = _PATH_SFTP_SERVER;
2132                         break;
2133                 case '2':
2134                         sshver = 2;
2135                         break;
2136                 case 'B':
2137                         copy_buffer_len = strtol(optarg, &cp, 10);
2138                         if (copy_buffer_len == 0 || *cp != '\0')
2139                                 fatal("Invalid buffer size \"%s\"", optarg);
2140                         break;
2141                 case 'b':
2142                         if (batchmode)
2143                                 fatal("Batch file already specified.");
2144
2145                         /* Allow "-" as stdin */
2146                         if (strcmp(optarg, "-") != 0 &&
2147                             (infile = fopen(optarg, "r")) == NULL)
2148                                 fatal("%s (%s).", strerror(errno), optarg);
2149                         showprogress = 0;
2150                         batchmode = 1;
2151                         addargs(&args, "-obatchmode yes");
2152                         break;
2153                 case 'p':
2154                         global_pflag = 1;
2155                         break;
2156                 case 'D':
2157                         sftp_direct = optarg;
2158                         break;
2159                 case 'r':
2160                         global_rflag = 1;
2161                         break;
2162                 case 'R':
2163                         num_requests = strtol(optarg, &cp, 10);
2164                         if (num_requests == 0 || *cp != '\0')
2165                                 fatal("Invalid number of requests \"%s\"",
2166                                     optarg);
2167                         break;
2168                 case 's':
2169                         sftp_server = optarg;
2170                         break;
2171                 case 'S':
2172                         ssh_program = optarg;
2173                         replacearg(&args, 0, "%s", ssh_program);
2174                         break;
2175                 case 'h':
2176                 default:
2177                         usage();
2178                 }
2179         }
2180
2181         if (!isatty(STDERR_FILENO))
2182                 showprogress = 0;
2183
2184         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2185
2186         if (sftp_direct == NULL) {
2187                 if (optind == argc || argc > (optind + 2))
2188                         usage();
2189
2190                 userhost = xstrdup(argv[optind]);
2191                 file2 = argv[optind+1];
2192
2193                 if ((host = strrchr(userhost, '@')) == NULL)
2194                         host = userhost;
2195                 else {
2196                         *host++ = '\0';
2197                         if (!userhost[0]) {
2198                                 fprintf(stderr, "Missing username\n");
2199                                 usage();
2200                         }
2201                         addargs(&args, "-l");
2202                         addargs(&args, "%s", userhost);
2203                 }
2204
2205                 if ((cp = colon(host)) != NULL) {
2206                         *cp++ = '\0';
2207                         file1 = cp;
2208                 }
2209
2210                 host = cleanhostname(host);
2211                 if (!*host) {
2212                         fprintf(stderr, "Missing hostname\n");
2213                         usage();
2214                 }
2215
2216                 addargs(&args, "-oProtocol %d", sshver);
2217
2218                 /* no subsystem if the server-spec contains a '/' */
2219                 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2220                         addargs(&args, "-s");
2221
2222                 addargs(&args, "--");
2223                 addargs(&args, "%s", host);
2224                 addargs(&args, "%s", (sftp_server != NULL ?
2225                     sftp_server : "sftp"));
2226
2227                 connect_to_server(ssh_program, args.list, &in, &out);
2228         } else {
2229                 args.list = NULL;
2230                 addargs(&args, "sftp-server");
2231
2232                 connect_to_server(sftp_direct, args.list, &in, &out);
2233         }
2234         freeargs(&args);
2235
2236         conn = do_init(in, out, copy_buffer_len, num_requests);
2237         if (conn == NULL)
2238                 fatal("Couldn't initialise connection to server");
2239
2240         if (!batchmode) {
2241                 if (sftp_direct == NULL)
2242                         fprintf(stderr, "Connected to %s.\n", host);
2243                 else
2244                         fprintf(stderr, "Attached to %s.\n", sftp_direct);
2245         }
2246
2247         err = interactive_loop(conn, file1, file2);
2248
2249 #if !defined(USE_PIPES)
2250         shutdown(in, SHUT_RDWR);
2251         shutdown(out, SHUT_RDWR);
2252 #endif
2253
2254         close(in);
2255         close(out);
2256         if (batchmode)
2257                 fclose(infile);
2258
2259         while (waitpid(sshpid, NULL, 0) == -1)
2260                 if (errno != EINTR)
2261                         fatal("Couldn't wait for ssh process: %s",
2262                             strerror(errno));
2263
2264         exit(err == 0 ? 0 : 1);
2265 }
This page took 0.863012 seconds and 5 git commands to generate.