]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - crypto/openssh/sftp.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / crypto / openssh / sftp.c
1 /* $OpenBSD: sftp.c,v 1.148 2013/07/25 00:56:52 djm Exp $ */
2 /* $FreeBSD$ */
3 /*
4  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "includes.h"
20
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #ifdef HAVE_SYS_STAT_H
24 # include <sys/stat.h>
25 #endif
26 #include <sys/param.h>
27 #include <sys/socket.h>
28 #include <sys/wait.h>
29 #ifdef HAVE_SYS_STATVFS_H
30 #include <sys/statvfs.h>
31 #endif
32
33 #include <ctype.h>
34 #include <errno.h>
35
36 #ifdef HAVE_PATHS_H
37 # include <paths.h>
38 #endif
39 #ifdef HAVE_LIBGEN_H
40 #include <libgen.h>
41 #endif
42 #ifdef HAVE_LOCALE_H
43 # include <locale.h>
44 #endif
45 #ifdef USE_LIBEDIT
46 #include <histedit.h>
47 #else
48 typedef void EditLine;
49 #endif
50 #include <signal.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <stdarg.h>
56
57 #ifdef HAVE_UTIL_H
58 # include <util.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    256     /* # 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 /* Suppress diagnositic messages */
84 int quiet = 0;
85
86 /* This is set to 0 if the progressmeter is not desired. */
87 int showprogress = 1;
88
89 /* When this option is set, we always recursively download/upload directories */
90 int global_rflag = 0;
91
92 /* When this option is set, we resume download if possible */
93 int global_aflag = 0;
94
95 /* When this option is set, the file transfers will always preserve times */
96 int global_pflag = 0;
97
98 /* SIGINT received during command processing */
99 volatile sig_atomic_t interrupted = 0;
100
101 /* I wish qsort() took a separate ctx for the comparison function...*/
102 int sort_flag;
103
104 /* Context used for commandline completion */
105 struct complete_ctx {
106         struct sftp_conn *conn;
107         char **remote_pathp;
108 };
109
110 int remote_glob(struct sftp_conn *, const char *, int,
111     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
112
113 extern char *__progname;
114
115 /* Separators for interactive commands */
116 #define WHITESPACE " \t\r\n"
117
118 /* ls flags */
119 #define LS_LONG_VIEW    0x0001  /* Full view ala ls -l */
120 #define LS_SHORT_VIEW   0x0002  /* Single row view ala ls -1 */
121 #define LS_NUMERIC_VIEW 0x0004  /* Long view with numeric uid/gid */
122 #define LS_NAME_SORT    0x0008  /* Sort by name (default) */
123 #define LS_TIME_SORT    0x0010  /* Sort by mtime */
124 #define LS_SIZE_SORT    0x0020  /* Sort by file size */
125 #define LS_REVERSE_SORT 0x0040  /* Reverse sort order */
126 #define LS_SHOW_ALL     0x0080  /* Don't skip filenames starting with '.' */
127 #define LS_SI_UNITS     0x0100  /* Display sizes as K, M, G, etc. */
128
129 #define VIEW_FLAGS      (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
130 #define SORT_FLAGS      (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
131
132 /* Commands for interactive mode */
133 #define I_CHDIR         1
134 #define I_CHGRP         2
135 #define I_CHMOD         3
136 #define I_CHOWN         4
137 #define I_DF            24
138 #define I_GET           5
139 #define I_HELP          6
140 #define I_LCHDIR        7
141 #define I_LINK          25
142 #define I_LLS           8
143 #define I_LMKDIR        9
144 #define I_LPWD          10
145 #define I_LS            11
146 #define I_LUMASK        12
147 #define I_MKDIR         13
148 #define I_PUT           14
149 #define I_PWD           15
150 #define I_QUIT          16
151 #define I_RENAME        17
152 #define I_RM            18
153 #define I_RMDIR         19
154 #define I_SHELL         20
155 #define I_SYMLINK       21
156 #define I_VERSION       22
157 #define I_PROGRESS      23
158 #define I_REGET         26
159
160 struct CMD {
161         const char *c;
162         const int n;
163         const int t;
164 };
165
166 /* Type of completion */
167 #define NOARGS  0
168 #define REMOTE  1
169 #define LOCAL   2
170
171 static const struct CMD cmds[] = {
172         { "bye",        I_QUIT,         NOARGS  },
173         { "cd",         I_CHDIR,        REMOTE  },
174         { "chdir",      I_CHDIR,        REMOTE  },
175         { "chgrp",      I_CHGRP,        REMOTE  },
176         { "chmod",      I_CHMOD,        REMOTE  },
177         { "chown",      I_CHOWN,        REMOTE  },
178         { "df",         I_DF,           REMOTE  },
179         { "dir",        I_LS,           REMOTE  },
180         { "exit",       I_QUIT,         NOARGS  },
181         { "get",        I_GET,          REMOTE  },
182         { "help",       I_HELP,         NOARGS  },
183         { "lcd",        I_LCHDIR,       LOCAL   },
184         { "lchdir",     I_LCHDIR,       LOCAL   },
185         { "lls",        I_LLS,          LOCAL   },
186         { "lmkdir",     I_LMKDIR,       LOCAL   },
187         { "ln",         I_LINK,         REMOTE  },
188         { "lpwd",       I_LPWD,         LOCAL   },
189         { "ls",         I_LS,           REMOTE  },
190         { "lumask",     I_LUMASK,       NOARGS  },
191         { "mkdir",      I_MKDIR,        REMOTE  },
192         { "mget",       I_GET,          REMOTE  },
193         { "mput",       I_PUT,          LOCAL   },
194         { "progress",   I_PROGRESS,     NOARGS  },
195         { "put",        I_PUT,          LOCAL   },
196         { "pwd",        I_PWD,          REMOTE  },
197         { "quit",       I_QUIT,         NOARGS  },
198         { "reget",      I_REGET,        REMOTE  },
199         { "rename",     I_RENAME,       REMOTE  },
200         { "rm",         I_RM,           REMOTE  },
201         { "rmdir",      I_RMDIR,        REMOTE  },
202         { "symlink",    I_SYMLINK,      REMOTE  },
203         { "version",    I_VERSION,      NOARGS  },
204         { "!",          I_SHELL,        NOARGS  },
205         { "?",          I_HELP,         NOARGS  },
206         { NULL,         -1,             -1      }
207 };
208
209 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
210
211 /* ARGSUSED */
212 static void
213 killchild(int signo)
214 {
215         if (sshpid > 1) {
216                 kill(sshpid, SIGTERM);
217                 waitpid(sshpid, NULL, 0);
218         }
219
220         _exit(1);
221 }
222
223 /* ARGSUSED */
224 static void
225 cmd_interrupt(int signo)
226 {
227         const char msg[] = "\rInterrupt  \n";
228         int olderrno = errno;
229
230         (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
231         interrupted = 1;
232         errno = olderrno;
233 }
234
235 static void
236 help(void)
237 {
238         printf("Available commands:\n"
239             "bye                                Quit sftp\n"
240             "cd path                            Change remote directory to 'path'\n"
241             "chgrp grp path                     Change group of file 'path' to 'grp'\n"
242             "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
243             "chown own path                     Change owner of file 'path' to 'own'\n"
244             "df [-hi] [path]                    Display statistics for current directory or\n"
245             "                                   filesystem containing 'path'\n"
246             "exit                               Quit sftp\n"
247             "get [-Ppr] remote [local]          Download file\n"
248             "reget remote [local]               Resume download file\n"
249             "help                               Display this help text\n"
250             "lcd path                           Change local directory to 'path'\n"
251             "lls [ls-options [path]]            Display local directory listing\n"
252             "lmkdir path                        Create local directory\n"
253             "ln [-s] oldpath newpath            Link remote file (-s for symlink)\n"
254             "lpwd                               Print local working directory\n"
255             "ls [-1afhlnrSt] [path]             Display remote directory listing\n"
256             "lumask umask                       Set local umask to 'umask'\n"
257             "mkdir path                         Create remote directory\n"
258             "progress                           Toggle display of progress meter\n"
259             "put [-Ppr] local [remote]          Upload file\n"
260             "pwd                                Display remote working directory\n"
261             "quit                               Quit sftp\n"
262             "rename oldpath newpath             Rename remote file\n"
263             "rm path                            Delete remote file\n"
264             "rmdir path                         Remove remote directory\n"
265             "symlink oldpath newpath            Symlink remote file\n"
266             "version                            Show SFTP version\n"
267             "!command                           Execute 'command' in local shell\n"
268             "!                                  Escape to local shell\n"
269             "?                                  Synonym for help\n");
270 }
271
272 static void
273 local_do_shell(const char *args)
274 {
275         int status;
276         char *shell;
277         pid_t pid;
278
279         if (!*args)
280                 args = NULL;
281
282         if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
283                 shell = _PATH_BSHELL;
284
285         if ((pid = fork()) == -1)
286                 fatal("Couldn't fork: %s", strerror(errno));
287
288         if (pid == 0) {
289                 /* XXX: child has pipe fds to ssh subproc open - issue? */
290                 if (args) {
291                         debug3("Executing %s -c \"%s\"", shell, args);
292                         execl(shell, shell, "-c", args, (char *)NULL);
293                 } else {
294                         debug3("Executing %s", shell);
295                         execl(shell, shell, (char *)NULL);
296                 }
297                 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
298                     strerror(errno));
299                 _exit(1);
300         }
301         while (waitpid(pid, &status, 0) == -1)
302                 if (errno != EINTR)
303                         fatal("Couldn't wait for child: %s", strerror(errno));
304         if (!WIFEXITED(status))
305                 error("Shell exited abnormally");
306         else if (WEXITSTATUS(status))
307                 error("Shell exited with status %d", WEXITSTATUS(status));
308 }
309
310 static void
311 local_do_ls(const char *args)
312 {
313         if (!args || !*args)
314                 local_do_shell(_PATH_LS);
315         else {
316                 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
317                 char *buf = xmalloc(len);
318
319                 /* XXX: quoting - rip quoting code from ftp? */
320                 snprintf(buf, len, _PATH_LS " %s", args);
321                 local_do_shell(buf);
322                 free(buf);
323         }
324 }
325
326 /* Strip one path (usually the pwd) from the start of another */
327 static char *
328 path_strip(char *path, char *strip)
329 {
330         size_t len;
331
332         if (strip == NULL)
333                 return (xstrdup(path));
334
335         len = strlen(strip);
336         if (strncmp(path, strip, len) == 0) {
337                 if (strip[len - 1] != '/' && path[len] == '/')
338                         len++;
339                 return (xstrdup(path + len));
340         }
341
342         return (xstrdup(path));
343 }
344
345 static char *
346 make_absolute(char *p, char *pwd)
347 {
348         char *abs_str;
349
350         /* Derelativise */
351         if (p && p[0] != '/') {
352                 abs_str = path_append(pwd, p);
353                 free(p);
354                 return(abs_str);
355         } else
356                 return(p);
357 }
358
359 static int
360 parse_getput_flags(const char *cmd, char **argv, int argc,
361     int *aflag, int *pflag, int *rflag)
362 {
363         extern int opterr, optind, optopt, optreset;
364         int ch;
365
366         optind = optreset = 1;
367         opterr = 0;
368
369         *aflag = *rflag = *pflag = 0;
370         while ((ch = getopt(argc, argv, "aPpRr")) != -1) {
371                 switch (ch) {
372                 case 'a':
373                         *aflag = 1;
374                         break;
375                 case 'p':
376                 case 'P':
377                         *pflag = 1;
378                         break;
379                 case 'r':
380                 case 'R':
381                         *rflag = 1;
382                         break;
383                 default:
384                         error("%s: Invalid flag -%c", cmd, optopt);
385                         return -1;
386                 }
387         }
388
389         return optind;
390 }
391
392 static int
393 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
394 {
395         extern int opterr, optind, optopt, optreset;
396         int ch;
397
398         optind = optreset = 1;
399         opterr = 0;
400
401         *sflag = 0;
402         while ((ch = getopt(argc, argv, "s")) != -1) {
403                 switch (ch) {
404                 case 's':
405                         *sflag = 1;
406                         break;
407                 default:
408                         error("%s: Invalid flag -%c", cmd, optopt);
409                         return -1;
410                 }
411         }
412
413         return optind;
414 }
415
416 static int
417 parse_ls_flags(char **argv, int argc, int *lflag)
418 {
419         extern int opterr, optind, optopt, optreset;
420         int ch;
421
422         optind = optreset = 1;
423         opterr = 0;
424
425         *lflag = LS_NAME_SORT;
426         while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
427                 switch (ch) {
428                 case '1':
429                         *lflag &= ~VIEW_FLAGS;
430                         *lflag |= LS_SHORT_VIEW;
431                         break;
432                 case 'S':
433                         *lflag &= ~SORT_FLAGS;
434                         *lflag |= LS_SIZE_SORT;
435                         break;
436                 case 'a':
437                         *lflag |= LS_SHOW_ALL;
438                         break;
439                 case 'f':
440                         *lflag &= ~SORT_FLAGS;
441                         break;
442                 case 'h':
443                         *lflag |= LS_SI_UNITS;
444                         break;
445                 case 'l':
446                         *lflag &= ~LS_SHORT_VIEW;
447                         *lflag |= LS_LONG_VIEW;
448                         break;
449                 case 'n':
450                         *lflag &= ~LS_SHORT_VIEW;
451                         *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
452                         break;
453                 case 'r':
454                         *lflag |= LS_REVERSE_SORT;
455                         break;
456                 case 't':
457                         *lflag &= ~SORT_FLAGS;
458                         *lflag |= LS_TIME_SORT;
459                         break;
460                 default:
461                         error("ls: Invalid flag -%c", optopt);
462                         return -1;
463                 }
464         }
465
466         return optind;
467 }
468
469 static int
470 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
471 {
472         extern int opterr, optind, optopt, optreset;
473         int ch;
474
475         optind = optreset = 1;
476         opterr = 0;
477
478         *hflag = *iflag = 0;
479         while ((ch = getopt(argc, argv, "hi")) != -1) {
480                 switch (ch) {
481                 case 'h':
482                         *hflag = 1;
483                         break;
484                 case 'i':
485                         *iflag = 1;
486                         break;
487                 default:
488                         error("%s: Invalid flag -%c", cmd, optopt);
489                         return -1;
490                 }
491         }
492
493         return optind;
494 }
495
496 static int
497 is_dir(char *path)
498 {
499         struct stat sb;
500
501         /* XXX: report errors? */
502         if (stat(path, &sb) == -1)
503                 return(0);
504
505         return(S_ISDIR(sb.st_mode));
506 }
507
508 static int
509 remote_is_dir(struct sftp_conn *conn, char *path)
510 {
511         Attrib *a;
512
513         /* XXX: report errors? */
514         if ((a = do_stat(conn, path, 1)) == NULL)
515                 return(0);
516         if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
517                 return(0);
518         return(S_ISDIR(a->perm));
519 }
520
521 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
522 static int
523 pathname_is_dir(char *pathname)
524 {
525         size_t l = strlen(pathname);
526
527         return l > 0 && pathname[l - 1] == '/';
528 }
529
530 static int
531 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
532     int pflag, int rflag, int resume)
533 {
534         char *abs_src = NULL;
535         char *abs_dst = NULL;
536         glob_t g;
537         char *filename, *tmp=NULL;
538         int i, err = 0;
539
540         abs_src = xstrdup(src);
541         abs_src = make_absolute(abs_src, pwd);
542         memset(&g, 0, sizeof(g));
543
544         debug3("Looking up %s", abs_src);
545         if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
546                 error("File \"%s\" not found.", abs_src);
547                 err = -1;
548                 goto out;
549         }
550
551         /*
552          * If multiple matches then dst must be a directory or
553          * unspecified.
554          */
555         if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
556                 error("Multiple source paths, but destination "
557                     "\"%s\" is not a directory", dst);
558                 err = -1;
559                 goto out;
560         }
561
562         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
563                 tmp = xstrdup(g.gl_pathv[i]);
564                 if ((filename = basename(tmp)) == NULL) {
565                         error("basename %s: %s", tmp, strerror(errno));
566                         free(tmp);
567                         err = -1;
568                         goto out;
569                 }
570
571                 if (g.gl_matchc == 1 && dst) {
572                         if (is_dir(dst)) {
573                                 abs_dst = path_append(dst, filename);
574                         } else {
575                                 abs_dst = xstrdup(dst);
576                         }
577                 } else if (dst) {
578                         abs_dst = path_append(dst, filename);
579                 } else {
580                         abs_dst = xstrdup(filename);
581                 }
582                 free(tmp);
583
584                 resume |= global_aflag;
585                 if (!quiet && resume)
586                         printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
587                 else if (!quiet && !resume)
588                         printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
589                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
590                         if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
591                             pflag || global_pflag, 1, resume) == -1)
592                                 err = -1;
593                 } else {
594                         if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
595                             pflag || global_pflag, resume) == -1)
596                                 err = -1;
597                 }
598                 free(abs_dst);
599                 abs_dst = NULL;
600         }
601
602 out:
603         free(abs_src);
604         globfree(&g);
605         return(err);
606 }
607
608 static int
609 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
610     int pflag, int rflag)
611 {
612         char *tmp_dst = NULL;
613         char *abs_dst = NULL;
614         char *tmp = NULL, *filename = NULL;
615         glob_t g;
616         int err = 0;
617         int i, dst_is_dir = 1;
618         struct stat sb;
619
620         if (dst) {
621                 tmp_dst = xstrdup(dst);
622                 tmp_dst = make_absolute(tmp_dst, pwd);
623         }
624
625         memset(&g, 0, sizeof(g));
626         debug3("Looking up %s", src);
627         if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
628                 error("File \"%s\" not found.", src);
629                 err = -1;
630                 goto out;
631         }
632
633         /* If we aren't fetching to pwd then stash this status for later */
634         if (tmp_dst != NULL)
635                 dst_is_dir = remote_is_dir(conn, tmp_dst);
636
637         /* If multiple matches, dst may be directory or unspecified */
638         if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
639                 error("Multiple paths match, but destination "
640                     "\"%s\" is not a directory", tmp_dst);
641                 err = -1;
642                 goto out;
643         }
644
645         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
646                 if (stat(g.gl_pathv[i], &sb) == -1) {
647                         err = -1;
648                         error("stat %s: %s", g.gl_pathv[i], strerror(errno));
649                         continue;
650                 }
651                 
652                 tmp = xstrdup(g.gl_pathv[i]);
653                 if ((filename = basename(tmp)) == NULL) {
654                         error("basename %s: %s", tmp, strerror(errno));
655                         free(tmp);
656                         err = -1;
657                         goto out;
658                 }
659
660                 if (g.gl_matchc == 1 && tmp_dst) {
661                         /* If directory specified, append filename */
662                         if (dst_is_dir)
663                                 abs_dst = path_append(tmp_dst, filename);
664                         else
665                                 abs_dst = xstrdup(tmp_dst);
666                 } else if (tmp_dst) {
667                         abs_dst = path_append(tmp_dst, filename);
668                 } else {
669                         abs_dst = make_absolute(xstrdup(filename), pwd);
670                 }
671                 free(tmp);
672
673                 if (!quiet)
674                         printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
675                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
676                         if (upload_dir(conn, g.gl_pathv[i], abs_dst,
677                             pflag || global_pflag, 1) == -1)
678                                 err = -1;
679                 } else {
680                         if (do_upload(conn, g.gl_pathv[i], abs_dst,
681                             pflag || global_pflag) == -1)
682                                 err = -1;
683                 }
684         }
685
686 out:
687         free(abs_dst);
688         free(tmp_dst);
689         globfree(&g);
690         return(err);
691 }
692
693 static int
694 sdirent_comp(const void *aa, const void *bb)
695 {
696         SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
697         SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
698         int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
699
700 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
701         if (sort_flag & LS_NAME_SORT)
702                 return (rmul * strcmp(a->filename, b->filename));
703         else if (sort_flag & LS_TIME_SORT)
704                 return (rmul * NCMP(a->a.mtime, b->a.mtime));
705         else if (sort_flag & LS_SIZE_SORT)
706                 return (rmul * NCMP(a->a.size, b->a.size));
707
708         fatal("Unknown ls sort type");
709 }
710
711 /* sftp ls.1 replacement for directories */
712 static int
713 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
714 {
715         int n;
716         u_int c = 1, colspace = 0, columns = 1;
717         SFTP_DIRENT **d;
718
719         if ((n = do_readdir(conn, path, &d)) != 0)
720                 return (n);
721
722         if (!(lflag & LS_SHORT_VIEW)) {
723                 u_int m = 0, width = 80;
724                 struct winsize ws;
725                 char *tmp;
726
727                 /* Count entries for sort and find longest filename */
728                 for (n = 0; d[n] != NULL; n++) {
729                         if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
730                                 m = MAX(m, strlen(d[n]->filename));
731                 }
732
733                 /* Add any subpath that also needs to be counted */
734                 tmp = path_strip(path, strip_path);
735                 m += strlen(tmp);
736                 free(tmp);
737
738                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
739                         width = ws.ws_col;
740
741                 columns = width / (m + 2);
742                 columns = MAX(columns, 1);
743                 colspace = width / columns;
744                 colspace = MIN(colspace, width);
745         }
746
747         if (lflag & SORT_FLAGS) {
748                 for (n = 0; d[n] != NULL; n++)
749                         ;       /* count entries */
750                 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
751                 qsort(d, n, sizeof(*d), sdirent_comp);
752         }
753
754         for (n = 0; d[n] != NULL && !interrupted; n++) {
755                 char *tmp, *fname;
756
757                 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
758                         continue;
759
760                 tmp = path_append(path, d[n]->filename);
761                 fname = path_strip(tmp, strip_path);
762                 free(tmp);
763
764                 if (lflag & LS_LONG_VIEW) {
765                         if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
766                                 char *lname;
767                                 struct stat sb;
768
769                                 memset(&sb, 0, sizeof(sb));
770                                 attrib_to_stat(&d[n]->a, &sb);
771                                 lname = ls_file(fname, &sb, 1,
772                                     (lflag & LS_SI_UNITS));
773                                 printf("%s\n", lname);
774                                 free(lname);
775                         } else
776                                 printf("%s\n", d[n]->longname);
777                 } else {
778                         printf("%-*s", colspace, fname);
779                         if (c >= columns) {
780                                 printf("\n");
781                                 c = 1;
782                         } else
783                                 c++;
784                 }
785
786                 free(fname);
787         }
788
789         if (!(lflag & LS_LONG_VIEW) && (c != 1))
790                 printf("\n");
791
792         free_sftp_dirents(d);
793         return (0);
794 }
795
796 /* sftp ls.1 replacement which handles path globs */
797 static int
798 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
799     int lflag)
800 {
801         char *fname, *lname;
802         glob_t g;
803         int err;
804         struct winsize ws;
805         u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
806
807         memset(&g, 0, sizeof(g));
808
809         if (remote_glob(conn, path,
810             GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
811             NULL, &g) ||
812             (g.gl_pathc && !g.gl_matchc)) {
813                 if (g.gl_pathc)
814                         globfree(&g);
815                 error("Can't ls: \"%s\" not found", path);
816                 return -1;
817         }
818
819         if (interrupted)
820                 goto out;
821
822         /*
823          * If the glob returns a single match and it is a directory,
824          * then just list its contents.
825          */
826         if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
827             S_ISDIR(g.gl_statv[0]->st_mode)) {
828                 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
829                 globfree(&g);
830                 return err;
831         }
832
833         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
834                 width = ws.ws_col;
835
836         if (!(lflag & LS_SHORT_VIEW)) {
837                 /* Count entries for sort and find longest filename */
838                 for (i = 0; g.gl_pathv[i]; i++)
839                         m = MAX(m, strlen(g.gl_pathv[i]));
840
841                 columns = width / (m + 2);
842                 columns = MAX(columns, 1);
843                 colspace = width / columns;
844         }
845
846         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
847                 fname = path_strip(g.gl_pathv[i], strip_path);
848                 if (lflag & LS_LONG_VIEW) {
849                         if (g.gl_statv[i] == NULL) {
850                                 error("no stat information for %s", fname);
851                                 continue;
852                         }
853                         lname = ls_file(fname, g.gl_statv[i], 1,
854                             (lflag & LS_SI_UNITS));
855                         printf("%s\n", lname);
856                         free(lname);
857                 } else {
858                         printf("%-*s", colspace, fname);
859                         if (c >= columns) {
860                                 printf("\n");
861                                 c = 1;
862                         } else
863                                 c++;
864                 }
865                 free(fname);
866         }
867
868         if (!(lflag & LS_LONG_VIEW) && (c != 1))
869                 printf("\n");
870
871  out:
872         if (g.gl_pathc)
873                 globfree(&g);
874
875         return 0;
876 }
877
878 static int
879 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
880 {
881         struct sftp_statvfs st;
882         char s_used[FMT_SCALED_STRSIZE];
883         char s_avail[FMT_SCALED_STRSIZE];
884         char s_root[FMT_SCALED_STRSIZE];
885         char s_total[FMT_SCALED_STRSIZE];
886         unsigned long long ffree;
887
888         if (do_statvfs(conn, path, &st, 1) == -1)
889                 return -1;
890         if (iflag) {
891                 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
892                 printf("     Inodes        Used       Avail      "
893                     "(root)    %%Capacity\n");
894                 printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
895                     (unsigned long long)st.f_files,
896                     (unsigned long long)(st.f_files - st.f_ffree),
897                     (unsigned long long)st.f_favail,
898                     (unsigned long long)st.f_ffree, ffree);
899         } else if (hflag) {
900                 strlcpy(s_used, "error", sizeof(s_used));
901                 strlcpy(s_avail, "error", sizeof(s_avail));
902                 strlcpy(s_root, "error", sizeof(s_root));
903                 strlcpy(s_total, "error", sizeof(s_total));
904                 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
905                 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
906                 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
907                 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
908                 printf("    Size     Used    Avail   (root)    %%Capacity\n");
909                 printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
910                     s_total, s_used, s_avail, s_root,
911                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
912                     st.f_blocks));
913         } else {
914                 printf("        Size         Used        Avail       "
915                     "(root)    %%Capacity\n");
916                 printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
917                     (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
918                     (unsigned long long)(st.f_frsize *
919                     (st.f_blocks - st.f_bfree) / 1024),
920                     (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
921                     (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
922                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
923                     st.f_blocks));
924         }
925         return 0;
926 }
927
928 /*
929  * Undo escaping of glob sequences in place. Used to undo extra escaping
930  * applied in makeargv() when the string is destined for a function that
931  * does not glob it.
932  */
933 static void
934 undo_glob_escape(char *s)
935 {
936         size_t i, j;
937
938         for (i = j = 0;;) {
939                 if (s[i] == '\0') {
940                         s[j] = '\0';
941                         return;
942                 }
943                 if (s[i] != '\\') {
944                         s[j++] = s[i++];
945                         continue;
946                 }
947                 /* s[i] == '\\' */
948                 ++i;
949                 switch (s[i]) {
950                 case '?':
951                 case '[':
952                 case '*':
953                 case '\\':
954                         s[j++] = s[i++];
955                         break;
956                 case '\0':
957                         s[j++] = '\\';
958                         s[j] = '\0';
959                         return;
960                 default:
961                         s[j++] = '\\';
962                         s[j++] = s[i++];
963                         break;
964                 }
965         }
966 }
967
968 /*
969  * Split a string into an argument vector using sh(1)-style quoting,
970  * comment and escaping rules, but with some tweaks to handle glob(3)
971  * wildcards.
972  * The "sloppy" flag allows for recovery from missing terminating quote, for
973  * use in parsing incomplete commandlines during tab autocompletion.
974  *
975  * Returns NULL on error or a NULL-terminated array of arguments.
976  *
977  * If "lastquote" is not NULL, the quoting character used for the last
978  * argument is placed in *lastquote ("\0", "'" or "\"").
979  * 
980  * If "terminated" is not NULL, *terminated will be set to 1 when the
981  * last argument's quote has been properly terminated or 0 otherwise.
982  * This parameter is only of use if "sloppy" is set.
983  */
984 #define MAXARGS         128
985 #define MAXARGLEN       8192
986 static char **
987 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
988     u_int *terminated)
989 {
990         int argc, quot;
991         size_t i, j;
992         static char argvs[MAXARGLEN];
993         static char *argv[MAXARGS + 1];
994         enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
995
996         *argcp = argc = 0;
997         if (strlen(arg) > sizeof(argvs) - 1) {
998  args_too_longs:
999                 error("string too long");
1000                 return NULL;
1001         }
1002         if (terminated != NULL)
1003                 *terminated = 1;
1004         if (lastquote != NULL)
1005                 *lastquote = '\0';
1006         state = MA_START;
1007         i = j = 0;
1008         for (;;) {
1009                 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1010                         error("Too many arguments.");
1011                         return NULL;
1012                 }
1013                 if (isspace(arg[i])) {
1014                         if (state == MA_UNQUOTED) {
1015                                 /* Terminate current argument */
1016                                 argvs[j++] = '\0';
1017                                 argc++;
1018                                 state = MA_START;
1019                         } else if (state != MA_START)
1020                                 argvs[j++] = arg[i];
1021                 } else if (arg[i] == '"' || arg[i] == '\'') {
1022                         q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1023                         if (state == MA_START) {
1024                                 argv[argc] = argvs + j;
1025                                 state = q;
1026                                 if (lastquote != NULL)
1027                                         *lastquote = arg[i];
1028                         } else if (state == MA_UNQUOTED) 
1029                                 state = q;
1030                         else if (state == q)
1031                                 state = MA_UNQUOTED;
1032                         else
1033                                 argvs[j++] = arg[i];
1034                 } else if (arg[i] == '\\') {
1035                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
1036                                 quot = state == MA_SQUOTE ? '\'' : '"';
1037                                 /* Unescape quote we are in */
1038                                 /* XXX support \n and friends? */
1039                                 if (arg[i + 1] == quot) {
1040                                         i++;
1041                                         argvs[j++] = arg[i];
1042                                 } else if (arg[i + 1] == '?' ||
1043                                     arg[i + 1] == '[' || arg[i + 1] == '*') {
1044                                         /*
1045                                          * Special case for sftp: append
1046                                          * double-escaped glob sequence -
1047                                          * glob will undo one level of
1048                                          * escaping. NB. string can grow here.
1049                                          */
1050                                         if (j >= sizeof(argvs) - 5)
1051                                                 goto args_too_longs;
1052                                         argvs[j++] = '\\';
1053                                         argvs[j++] = arg[i++];
1054                                         argvs[j++] = '\\';
1055                                         argvs[j++] = arg[i];
1056                                 } else {
1057                                         argvs[j++] = arg[i++];
1058                                         argvs[j++] = arg[i];
1059                                 }
1060                         } else {
1061                                 if (state == MA_START) {
1062                                         argv[argc] = argvs + j;
1063                                         state = MA_UNQUOTED;
1064                                         if (lastquote != NULL)
1065                                                 *lastquote = '\0';
1066                                 }
1067                                 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1068                                     arg[i + 1] == '*' || arg[i + 1] == '\\') {
1069                                         /*
1070                                          * Special case for sftp: append
1071                                          * escaped glob sequence -
1072                                          * glob will undo one level of
1073                                          * escaping.
1074                                          */
1075                                         argvs[j++] = arg[i++];
1076                                         argvs[j++] = arg[i];
1077                                 } else {
1078                                         /* Unescape everything */
1079                                         /* XXX support \n and friends? */
1080                                         i++;
1081                                         argvs[j++] = arg[i];
1082                                 }
1083                         }
1084                 } else if (arg[i] == '#') {
1085                         if (state == MA_SQUOTE || state == MA_DQUOTE)
1086                                 argvs[j++] = arg[i];
1087                         else
1088                                 goto string_done;
1089                 } else if (arg[i] == '\0') {
1090                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
1091                                 if (sloppy) {
1092                                         state = MA_UNQUOTED;
1093                                         if (terminated != NULL)
1094                                                 *terminated = 0;
1095                                         goto string_done;
1096                                 }
1097                                 error("Unterminated quoted argument");
1098                                 return NULL;
1099                         }
1100  string_done:
1101                         if (state == MA_UNQUOTED) {
1102                                 argvs[j++] = '\0';
1103                                 argc++;
1104                         }
1105                         break;
1106                 } else {
1107                         if (state == MA_START) {
1108                                 argv[argc] = argvs + j;
1109                                 state = MA_UNQUOTED;
1110                                 if (lastquote != NULL)
1111                                         *lastquote = '\0';
1112                         }
1113                         if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1114                             (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1115                                 /*
1116                                  * Special case for sftp: escape quoted
1117                                  * glob(3) wildcards. NB. string can grow
1118                                  * here.
1119                                  */
1120                                 if (j >= sizeof(argvs) - 3)
1121                                         goto args_too_longs;
1122                                 argvs[j++] = '\\';
1123                                 argvs[j++] = arg[i];
1124                         } else
1125                                 argvs[j++] = arg[i];
1126                 }
1127                 i++;
1128         }
1129         *argcp = argc;
1130         return argv;
1131 }
1132
1133 static int
1134 parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag,
1135     int *pflag, int *rflag, int *sflag, unsigned long *n_arg,
1136     char **path1, char **path2)
1137 {
1138         const char *cmd, *cp = *cpp;
1139         char *cp2, **argv;
1140         int base = 0;
1141         long l;
1142         int i, cmdnum, optidx, argc;
1143
1144         /* Skip leading whitespace */
1145         cp = cp + strspn(cp, WHITESPACE);
1146
1147         /* Check for leading '-' (disable error processing) */
1148         *iflag = 0;
1149         if (*cp == '-') {
1150                 *iflag = 1;
1151                 cp++;
1152                 cp = cp + strspn(cp, WHITESPACE);
1153         }
1154
1155         /* Ignore blank lines and lines which begin with comment '#' char */
1156         if (*cp == '\0' || *cp == '#')
1157                 return (0);
1158
1159         if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1160                 return -1;
1161
1162         /* Figure out which command we have */
1163         for (i = 0; cmds[i].c != NULL; i++) {
1164                 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1165                         break;
1166         }
1167         cmdnum = cmds[i].n;
1168         cmd = cmds[i].c;
1169
1170         /* Special case */
1171         if (*cp == '!') {
1172                 cp++;
1173                 cmdnum = I_SHELL;
1174         } else if (cmdnum == -1) {
1175                 error("Invalid command.");
1176                 return -1;
1177         }
1178
1179         /* Get arguments and parse flags */
1180         *aflag = *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1181         *path1 = *path2 = NULL;
1182         optidx = 1;
1183         switch (cmdnum) {
1184         case I_GET:
1185         case I_REGET:
1186         case I_PUT:
1187                 if ((optidx = parse_getput_flags(cmd, argv, argc,
1188                     aflag, pflag, rflag)) == -1)
1189                         return -1;
1190                 /* Get first pathname (mandatory) */
1191                 if (argc - optidx < 1) {
1192                         error("You must specify at least one path after a "
1193                             "%s command.", cmd);
1194                         return -1;
1195                 }
1196                 *path1 = xstrdup(argv[optidx]);
1197                 /* Get second pathname (optional) */
1198                 if (argc - optidx > 1) {
1199                         *path2 = xstrdup(argv[optidx + 1]);
1200                         /* Destination is not globbed */
1201                         undo_glob_escape(*path2);
1202                 }
1203                 if (*aflag && cmdnum == I_PUT) {
1204                         /* XXX implement resume for uploads */
1205                         error("Resume is not supported for uploads");
1206                         return -1;
1207                 }
1208                 break;
1209         case I_LINK:
1210                 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1211                         return -1;
1212         case I_SYMLINK:
1213         case I_RENAME:
1214                 if (argc - optidx < 2) {
1215                         error("You must specify two paths after a %s "
1216                             "command.", cmd);
1217                         return -1;
1218                 }
1219                 *path1 = xstrdup(argv[optidx]);
1220                 *path2 = xstrdup(argv[optidx + 1]);
1221                 /* Paths are not globbed */
1222                 undo_glob_escape(*path1);
1223                 undo_glob_escape(*path2);
1224                 break;
1225         case I_RM:
1226         case I_MKDIR:
1227         case I_RMDIR:
1228         case I_CHDIR:
1229         case I_LCHDIR:
1230         case I_LMKDIR:
1231                 /* Get pathname (mandatory) */
1232                 if (argc - optidx < 1) {
1233                         error("You must specify a path after a %s command.",
1234                             cmd);
1235                         return -1;
1236                 }
1237                 *path1 = xstrdup(argv[optidx]);
1238                 /* Only "rm" globs */
1239                 if (cmdnum != I_RM)
1240                         undo_glob_escape(*path1);
1241                 break;
1242         case I_DF:
1243                 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1244                     iflag)) == -1)
1245                         return -1;
1246                 /* Default to current directory if no path specified */
1247                 if (argc - optidx < 1)
1248                         *path1 = NULL;
1249                 else {
1250                         *path1 = xstrdup(argv[optidx]);
1251                         undo_glob_escape(*path1);
1252                 }
1253                 break;
1254         case I_LS:
1255                 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1256                         return(-1);
1257                 /* Path is optional */
1258                 if (argc - optidx > 0)
1259                         *path1 = xstrdup(argv[optidx]);
1260                 break;
1261         case I_LLS:
1262                 /* Skip ls command and following whitespace */
1263                 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1264         case I_SHELL:
1265                 /* Uses the rest of the line */
1266                 break;
1267         case I_LUMASK:
1268         case I_CHMOD:
1269                 base = 8;
1270         case I_CHOWN:
1271         case I_CHGRP:
1272                 /* Get numeric arg (mandatory) */
1273                 if (argc - optidx < 1)
1274                         goto need_num_arg;
1275                 errno = 0;
1276                 l = strtol(argv[optidx], &cp2, base);
1277                 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1278                     ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1279                     l < 0) {
1280  need_num_arg:
1281                         error("You must supply a numeric argument "
1282                             "to the %s command.", cmd);
1283                         return -1;
1284                 }
1285                 *n_arg = l;
1286                 if (cmdnum == I_LUMASK)
1287                         break;
1288                 /* Get pathname (mandatory) */
1289                 if (argc - optidx < 2) {
1290                         error("You must specify a path after a %s command.",
1291                             cmd);
1292                         return -1;
1293                 }
1294                 *path1 = xstrdup(argv[optidx + 1]);
1295                 break;
1296         case I_QUIT:
1297         case I_PWD:
1298         case I_LPWD:
1299         case I_HELP:
1300         case I_VERSION:
1301         case I_PROGRESS:
1302                 break;
1303         default:
1304                 fatal("Command not implemented");
1305         }
1306
1307         *cpp = cp;
1308         return(cmdnum);
1309 }
1310
1311 static int
1312 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1313     int err_abort)
1314 {
1315         char *path1, *path2, *tmp;
1316         int aflag = 0, hflag = 0, iflag = 0, lflag = 0, pflag = 0;
1317         int rflag = 0, sflag = 0;
1318         int cmdnum, i;
1319         unsigned long n_arg = 0;
1320         Attrib a, *aa;
1321         char path_buf[MAXPATHLEN];
1322         int err = 0;
1323         glob_t g;
1324
1325         path1 = path2 = NULL;
1326         cmdnum = parse_args(&cmd, &aflag, &hflag, &iflag, &lflag, &pflag,
1327             &rflag, &sflag, &n_arg, &path1, &path2);
1328         if (iflag != 0)
1329                 err_abort = 0;
1330
1331         memset(&g, 0, sizeof(g));
1332
1333         /* Perform command */
1334         switch (cmdnum) {
1335         case 0:
1336                 /* Blank line */
1337                 break;
1338         case -1:
1339                 /* Unrecognized command */
1340                 err = -1;
1341                 break;
1342         case I_REGET:
1343                 aflag = 1;
1344                 /* FALLTHROUGH */
1345         case I_GET:
1346                 err = process_get(conn, path1, path2, *pwd, pflag,
1347                     rflag, aflag);
1348                 break;
1349         case I_PUT:
1350                 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1351                 break;
1352         case I_RENAME:
1353                 path1 = make_absolute(path1, *pwd);
1354                 path2 = make_absolute(path2, *pwd);
1355                 err = do_rename(conn, path1, path2);
1356                 break;
1357         case I_SYMLINK:
1358                 sflag = 1;
1359         case I_LINK:
1360                 path1 = make_absolute(path1, *pwd);
1361                 path2 = make_absolute(path2, *pwd);
1362                 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1363                 break;
1364         case I_RM:
1365                 path1 = make_absolute(path1, *pwd);
1366                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1367                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1368                         if (!quiet)
1369                                 printf("Removing %s\n", g.gl_pathv[i]);
1370                         err = do_rm(conn, g.gl_pathv[i]);
1371                         if (err != 0 && err_abort)
1372                                 break;
1373                 }
1374                 break;
1375         case I_MKDIR:
1376                 path1 = make_absolute(path1, *pwd);
1377                 attrib_clear(&a);
1378                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1379                 a.perm = 0777;
1380                 err = do_mkdir(conn, path1, &a, 1);
1381                 break;
1382         case I_RMDIR:
1383                 path1 = make_absolute(path1, *pwd);
1384                 err = do_rmdir(conn, path1);
1385                 break;
1386         case I_CHDIR:
1387                 path1 = make_absolute(path1, *pwd);
1388                 if ((tmp = do_realpath(conn, path1)) == NULL) {
1389                         err = 1;
1390                         break;
1391                 }
1392                 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1393                         free(tmp);
1394                         err = 1;
1395                         break;
1396                 }
1397                 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1398                         error("Can't change directory: Can't check target");
1399                         free(tmp);
1400                         err = 1;
1401                         break;
1402                 }
1403                 if (!S_ISDIR(aa->perm)) {
1404                         error("Can't change directory: \"%s\" is not "
1405                             "a directory", tmp);
1406                         free(tmp);
1407                         err = 1;
1408                         break;
1409                 }
1410                 free(*pwd);
1411                 *pwd = tmp;
1412                 break;
1413         case I_LS:
1414                 if (!path1) {
1415                         do_ls_dir(conn, *pwd, *pwd, lflag);
1416                         break;
1417                 }
1418
1419                 /* Strip pwd off beginning of non-absolute paths */
1420                 tmp = NULL;
1421                 if (*path1 != '/')
1422                         tmp = *pwd;
1423
1424                 path1 = make_absolute(path1, *pwd);
1425                 err = do_globbed_ls(conn, path1, tmp, lflag);
1426                 break;
1427         case I_DF:
1428                 /* Default to current directory if no path specified */
1429                 if (path1 == NULL)
1430                         path1 = xstrdup(*pwd);
1431                 path1 = make_absolute(path1, *pwd);
1432                 err = do_df(conn, path1, hflag, iflag);
1433                 break;
1434         case I_LCHDIR:
1435                 if (chdir(path1) == -1) {
1436                         error("Couldn't change local directory to "
1437                             "\"%s\": %s", path1, strerror(errno));
1438                         err = 1;
1439                 }
1440                 break;
1441         case I_LMKDIR:
1442                 if (mkdir(path1, 0777) == -1) {
1443                         error("Couldn't create local directory "
1444                             "\"%s\": %s", path1, strerror(errno));
1445                         err = 1;
1446                 }
1447                 break;
1448         case I_LLS:
1449                 local_do_ls(cmd);
1450                 break;
1451         case I_SHELL:
1452                 local_do_shell(cmd);
1453                 break;
1454         case I_LUMASK:
1455                 umask(n_arg);
1456                 printf("Local umask: %03lo\n", n_arg);
1457                 break;
1458         case I_CHMOD:
1459                 path1 = make_absolute(path1, *pwd);
1460                 attrib_clear(&a);
1461                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1462                 a.perm = n_arg;
1463                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1464                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1465                         if (!quiet)
1466                                 printf("Changing mode on %s\n", g.gl_pathv[i]);
1467                         err = do_setstat(conn, g.gl_pathv[i], &a);
1468                         if (err != 0 && err_abort)
1469                                 break;
1470                 }
1471                 break;
1472         case I_CHOWN:
1473         case I_CHGRP:
1474                 path1 = make_absolute(path1, *pwd);
1475                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1476                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1477                         if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1478                                 if (err_abort) {
1479                                         err = -1;
1480                                         break;
1481                                 } else
1482                                         continue;
1483                         }
1484                         if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1485                                 error("Can't get current ownership of "
1486                                     "remote file \"%s\"", g.gl_pathv[i]);
1487                                 if (err_abort) {
1488                                         err = -1;
1489                                         break;
1490                                 } else
1491                                         continue;
1492                         }
1493                         aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1494                         if (cmdnum == I_CHOWN) {
1495                                 if (!quiet)
1496                                         printf("Changing owner on %s\n",
1497                                             g.gl_pathv[i]);
1498                                 aa->uid = n_arg;
1499                         } else {
1500                                 if (!quiet)
1501                                         printf("Changing group on %s\n",
1502                                             g.gl_pathv[i]);
1503                                 aa->gid = n_arg;
1504                         }
1505                         err = do_setstat(conn, g.gl_pathv[i], aa);
1506                         if (err != 0 && err_abort)
1507                                 break;
1508                 }
1509                 break;
1510         case I_PWD:
1511                 printf("Remote working directory: %s\n", *pwd);
1512                 break;
1513         case I_LPWD:
1514                 if (!getcwd(path_buf, sizeof(path_buf))) {
1515                         error("Couldn't get local cwd: %s", strerror(errno));
1516                         err = -1;
1517                         break;
1518                 }
1519                 printf("Local working directory: %s\n", path_buf);
1520                 break;
1521         case I_QUIT:
1522                 /* Processed below */
1523                 break;
1524         case I_HELP:
1525                 help();
1526                 break;
1527         case I_VERSION:
1528                 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1529                 break;
1530         case I_PROGRESS:
1531                 showprogress = !showprogress;
1532                 if (showprogress)
1533                         printf("Progress meter enabled\n");
1534                 else
1535                         printf("Progress meter disabled\n");
1536                 break;
1537         default:
1538                 fatal("%d is not implemented", cmdnum);
1539         }
1540
1541         if (g.gl_pathc)
1542                 globfree(&g);
1543         free(path1);
1544         free(path2);
1545
1546         /* If an unignored error occurs in batch mode we should abort. */
1547         if (err_abort && err != 0)
1548                 return (-1);
1549         else if (cmdnum == I_QUIT)
1550                 return (1);
1551
1552         return (0);
1553 }
1554
1555 #ifdef USE_LIBEDIT
1556 static char *
1557 prompt(EditLine *el)
1558 {
1559         return ("sftp> ");
1560 }
1561
1562 /* Display entries in 'list' after skipping the first 'len' chars */
1563 static void
1564 complete_display(char **list, u_int len)
1565 {
1566         u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1567         struct winsize ws;
1568         char *tmp;
1569
1570         /* Count entries for sort and find longest */
1571         for (y = 0; list[y]; y++) 
1572                 m = MAX(m, strlen(list[y]));
1573
1574         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1575                 width = ws.ws_col;
1576
1577         m = m > len ? m - len : 0;
1578         columns = width / (m + 2);
1579         columns = MAX(columns, 1);
1580         colspace = width / columns;
1581         colspace = MIN(colspace, width);
1582
1583         printf("\n");
1584         m = 1;
1585         for (y = 0; list[y]; y++) {
1586                 llen = strlen(list[y]);
1587                 tmp = llen > len ? list[y] + len : "";
1588                 printf("%-*s", colspace, tmp);
1589                 if (m >= columns) {
1590                         printf("\n");
1591                         m = 1;
1592                 } else
1593                         m++;
1594         }
1595         printf("\n");
1596 }
1597
1598 /*
1599  * Given a "list" of words that begin with a common prefix of "word",
1600  * attempt to find an autocompletion to extends "word" by the next
1601  * characters common to all entries in "list".
1602  */
1603 static char *
1604 complete_ambiguous(const char *word, char **list, size_t count)
1605 {
1606         if (word == NULL)
1607                 return NULL;
1608
1609         if (count > 0) {
1610                 u_int y, matchlen = strlen(list[0]);
1611
1612                 /* Find length of common stem */
1613                 for (y = 1; list[y]; y++) {
1614                         u_int x;
1615
1616                         for (x = 0; x < matchlen; x++) 
1617                                 if (list[0][x] != list[y][x]) 
1618                                         break;
1619
1620                         matchlen = x;
1621                 }
1622
1623                 if (matchlen > strlen(word)) {
1624                         char *tmp = xstrdup(list[0]);
1625
1626                         tmp[matchlen] = '\0';
1627                         return tmp;
1628                 }
1629         } 
1630
1631         return xstrdup(word);
1632 }
1633
1634 /* Autocomplete a sftp command */
1635 static int
1636 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1637     int terminated)
1638 {
1639         u_int y, count = 0, cmdlen, tmplen;
1640         char *tmp, **list, argterm[3];
1641         const LineInfo *lf;
1642
1643         list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1644
1645         /* No command specified: display all available commands */
1646         if (cmd == NULL) {
1647                 for (y = 0; cmds[y].c; y++)
1648                         list[count++] = xstrdup(cmds[y].c);
1649                 
1650                 list[count] = NULL;
1651                 complete_display(list, 0);
1652
1653                 for (y = 0; list[y] != NULL; y++)  
1654                         free(list[y]);  
1655                 free(list);
1656                 return count;
1657         }
1658
1659         /* Prepare subset of commands that start with "cmd" */
1660         cmdlen = strlen(cmd);
1661         for (y = 0; cmds[y].c; y++)  {
1662                 if (!strncasecmp(cmd, cmds[y].c, cmdlen)) 
1663                         list[count++] = xstrdup(cmds[y].c);
1664         }
1665         list[count] = NULL;
1666
1667         if (count == 0) {
1668                 free(list);
1669                 return 0;
1670         }
1671
1672         /* Complete ambigious command */
1673         tmp = complete_ambiguous(cmd, list, count);
1674         if (count > 1)
1675                 complete_display(list, 0);
1676
1677         for (y = 0; list[y]; y++)  
1678                 free(list[y]);  
1679         free(list);
1680
1681         if (tmp != NULL) {
1682                 tmplen = strlen(tmp);
1683                 cmdlen = strlen(cmd);
1684                 /* If cmd may be extended then do so */
1685                 if (tmplen > cmdlen)
1686                         if (el_insertstr(el, tmp + cmdlen) == -1)
1687                                 fatal("el_insertstr failed.");
1688                 lf = el_line(el);
1689                 /* Terminate argument cleanly */
1690                 if (count == 1) {
1691                         y = 0;
1692                         if (!terminated)
1693                                 argterm[y++] = quote;
1694                         if (lastarg || *(lf->cursor) != ' ')
1695                                 argterm[y++] = ' ';
1696                         argterm[y] = '\0';
1697                         if (y > 0 && el_insertstr(el, argterm) == -1)
1698                                 fatal("el_insertstr failed.");
1699                 }
1700                 free(tmp);
1701         }
1702
1703         return count;
1704 }
1705
1706 /*
1707  * Determine whether a particular sftp command's arguments (if any)
1708  * represent local or remote files.
1709  */
1710 static int
1711 complete_is_remote(char *cmd) {
1712         int i;
1713
1714         if (cmd == NULL)
1715                 return -1;
1716
1717         for (i = 0; cmds[i].c; i++) {
1718                 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 
1719                         return cmds[i].t;
1720         }
1721
1722         return -1;
1723 }
1724
1725 /* Autocomplete a filename "file" */
1726 static int
1727 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1728     char *file, int remote, int lastarg, char quote, int terminated)
1729 {
1730         glob_t g;
1731         char *tmp, *tmp2, ins[8];
1732         u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1733         int clen;
1734         const LineInfo *lf;
1735         
1736         /* Glob from "file" location */
1737         if (file == NULL)
1738                 tmp = xstrdup("*");
1739         else
1740                 xasprintf(&tmp, "%s*", file);
1741
1742         /* Check if the path is absolute. */
1743         isabs = tmp[0] == '/';
1744
1745         memset(&g, 0, sizeof(g));
1746         if (remote != LOCAL) {
1747                 tmp = make_absolute(tmp, remote_path);
1748                 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1749         } else 
1750                 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1751         
1752         /* Determine length of pwd so we can trim completion display */
1753         for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1754                 /* Terminate counting on first unescaped glob metacharacter */
1755                 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1756                         if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1757                                 hadglob = 1;
1758                         break;
1759                 }
1760                 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1761                         tmplen++;
1762                 if (tmp[tmplen] == '/')
1763                         pwdlen = tmplen + 1;    /* track last seen '/' */
1764         }
1765         free(tmp);
1766
1767         if (g.gl_matchc == 0) 
1768                 goto out;
1769
1770         if (g.gl_matchc > 1)
1771                 complete_display(g.gl_pathv, pwdlen);
1772
1773         tmp = NULL;
1774         /* Don't try to extend globs */
1775         if (file == NULL || hadglob)
1776                 goto out;
1777
1778         tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1779         tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1780         free(tmp2);
1781
1782         if (tmp == NULL)
1783                 goto out;
1784
1785         tmplen = strlen(tmp);
1786         filelen = strlen(file);
1787
1788         /* Count the number of escaped characters in the input string. */
1789         cesc = isesc = 0;
1790         for (i = 0; i < filelen; i++) {
1791                 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1792                         isesc = 1;
1793                         cesc++;
1794                 } else
1795                         isesc = 0;
1796         }
1797
1798         if (tmplen > (filelen - cesc)) {
1799                 tmp2 = tmp + filelen - cesc;
1800                 len = strlen(tmp2); 
1801                 /* quote argument on way out */
1802                 for (i = 0; i < len; i += clen) {
1803                         if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1804                             (size_t)clen > sizeof(ins) - 2)
1805                                 fatal("invalid multibyte character");
1806                         ins[0] = '\\';
1807                         memcpy(ins + 1, tmp2 + i, clen);
1808                         ins[clen + 1] = '\0';
1809                         switch (tmp2[i]) {
1810                         case '\'':
1811                         case '"':
1812                         case '\\':
1813                         case '\t':
1814                         case '[':
1815                         case ' ':
1816                         case '#':
1817                         case '*':
1818                                 if (quote == '\0' || tmp2[i] == quote) {
1819                                         if (el_insertstr(el, ins) == -1)
1820                                                 fatal("el_insertstr "
1821                                                     "failed.");
1822                                         break;
1823                                 }
1824                                 /* FALLTHROUGH */
1825                         default:
1826                                 if (el_insertstr(el, ins + 1) == -1)
1827                                         fatal("el_insertstr failed.");
1828                                 break;
1829                         }
1830                 }
1831         }
1832
1833         lf = el_line(el);
1834         if (g.gl_matchc == 1) {
1835                 i = 0;
1836                 if (!terminated)
1837                         ins[i++] = quote;
1838                 if (*(lf->cursor - 1) != '/' &&
1839                     (lastarg || *(lf->cursor) != ' '))
1840                         ins[i++] = ' ';
1841                 ins[i] = '\0';
1842                 if (i > 0 && el_insertstr(el, ins) == -1)
1843                         fatal("el_insertstr failed.");
1844         }
1845         free(tmp);
1846
1847  out:
1848         globfree(&g);
1849         return g.gl_matchc;
1850 }
1851
1852 /* tab-completion hook function, called via libedit */
1853 static unsigned char
1854 complete(EditLine *el, int ch)
1855 {
1856         char **argv, *line, quote; 
1857         int argc, carg;
1858         u_int cursor, len, terminated, ret = CC_ERROR;
1859         const LineInfo *lf;
1860         struct complete_ctx *complete_ctx;
1861
1862         lf = el_line(el);
1863         if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1864                 fatal("%s: el_get failed", __func__);
1865
1866         /* Figure out which argument the cursor points to */
1867         cursor = lf->cursor - lf->buffer;
1868         line = (char *)xmalloc(cursor + 1);
1869         memcpy(line, lf->buffer, cursor);
1870         line[cursor] = '\0';
1871         argv = makeargv(line, &carg, 1, &quote, &terminated);
1872         free(line);
1873
1874         /* Get all the arguments on the line */
1875         len = lf->lastchar - lf->buffer;
1876         line = (char *)xmalloc(len + 1);
1877         memcpy(line, lf->buffer, len);
1878         line[len] = '\0';
1879         argv = makeargv(line, &argc, 1, NULL, NULL);
1880
1881         /* Ensure cursor is at EOL or a argument boundary */
1882         if (line[cursor] != ' ' && line[cursor] != '\0' &&
1883             line[cursor] != '\n') {
1884                 free(line);
1885                 return ret;
1886         }
1887
1888         if (carg == 0) {
1889                 /* Show all available commands */
1890                 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1891                 ret = CC_REDISPLAY;
1892         } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
1893                 /* Handle the command parsing */
1894                 if (complete_cmd_parse(el, argv[0], argc == carg,
1895                     quote, terminated) != 0) 
1896                         ret = CC_REDISPLAY;
1897         } else if (carg >= 1) {
1898                 /* Handle file parsing */
1899                 int remote = complete_is_remote(argv[0]);
1900                 char *filematch = NULL;
1901
1902                 if (carg > 1 && line[cursor-1] != ' ')
1903                         filematch = argv[carg - 1];
1904
1905                 if (remote != 0 &&
1906                     complete_match(el, complete_ctx->conn,
1907                     *complete_ctx->remote_pathp, filematch,
1908                     remote, carg == argc, quote, terminated) != 0) 
1909                         ret = CC_REDISPLAY;
1910         }
1911
1912         free(line);     
1913         return ret;
1914 }
1915 #endif /* USE_LIBEDIT */
1916
1917 int
1918 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1919 {
1920         char *remote_path;
1921         char *dir = NULL;
1922         char cmd[2048];
1923         int err, interactive;
1924         EditLine *el = NULL;
1925 #ifdef USE_LIBEDIT
1926         History *hl = NULL;
1927         HistEvent hev;
1928         extern char *__progname;
1929         struct complete_ctx complete_ctx;
1930
1931         if (!batchmode && isatty(STDIN_FILENO)) {
1932                 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1933                         fatal("Couldn't initialise editline");
1934                 if ((hl = history_init()) == NULL)
1935                         fatal("Couldn't initialise editline history");
1936                 history(hl, &hev, H_SETSIZE, 100);
1937                 el_set(el, EL_HIST, history, hl);
1938
1939                 el_set(el, EL_PROMPT, prompt);
1940                 el_set(el, EL_EDITOR, "emacs");
1941                 el_set(el, EL_TERMINAL, NULL);
1942                 el_set(el, EL_SIGNAL, 1);
1943                 el_source(el, NULL);
1944
1945                 /* Tab Completion */
1946                 el_set(el, EL_ADDFN, "ftp-complete", 
1947                     "Context sensitive argument completion", complete);
1948                 complete_ctx.conn = conn;
1949                 complete_ctx.remote_pathp = &remote_path;
1950                 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1951                 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1952         }
1953 #endif /* USE_LIBEDIT */
1954
1955         remote_path = do_realpath(conn, ".");
1956         if (remote_path == NULL)
1957                 fatal("Need cwd");
1958
1959         if (file1 != NULL) {
1960                 dir = xstrdup(file1);
1961                 dir = make_absolute(dir, remote_path);
1962
1963                 if (remote_is_dir(conn, dir) && file2 == NULL) {
1964                         if (!quiet)
1965                                 printf("Changing to: %s\n", dir);
1966                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1967                         if (parse_dispatch_command(conn, cmd,
1968                             &remote_path, 1) != 0) {
1969                                 free(dir);
1970                                 free(remote_path);
1971                                 free(conn);
1972                                 return (-1);
1973                         }
1974                 } else {
1975                         /* XXX this is wrong wrt quoting */
1976                         snprintf(cmd, sizeof cmd, "get%s %s%s%s",
1977                             global_aflag ? " -a" : "", dir,
1978                             file2 == NULL ? "" : " ",
1979                             file2 == NULL ? "" : file2);
1980                         err = parse_dispatch_command(conn, cmd,
1981                             &remote_path, 1);
1982                         free(dir);
1983                         free(remote_path);
1984                         free(conn);
1985                         return (err);
1986                 }
1987                 free(dir);
1988         }
1989
1990         setlinebuf(stdout);
1991         setlinebuf(infile);
1992
1993         interactive = !batchmode && isatty(STDIN_FILENO);
1994         err = 0;
1995         for (;;) {
1996                 char *cp;
1997
1998                 signal(SIGINT, SIG_IGN);
1999
2000                 if (el == NULL) {
2001                         if (interactive)
2002                                 printf("sftp> ");
2003                         if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2004                                 if (interactive)
2005                                         printf("\n");
2006                                 break;
2007                         }
2008                         if (!interactive) { /* Echo command */
2009                                 printf("sftp> %s", cmd);
2010                                 if (strlen(cmd) > 0 &&
2011                                     cmd[strlen(cmd) - 1] != '\n')
2012                                         printf("\n");
2013                         }
2014                 } else {
2015 #ifdef USE_LIBEDIT
2016                         const char *line;
2017                         int count = 0;
2018
2019                         if ((line = el_gets(el, &count)) == NULL ||
2020                             count <= 0) {
2021                                 printf("\n");
2022                                 break;
2023                         }
2024                         history(hl, &hev, H_ENTER, line);
2025                         if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2026                                 fprintf(stderr, "Error: input line too long\n");
2027                                 continue;
2028                         }
2029 #endif /* USE_LIBEDIT */
2030                 }
2031
2032                 cp = strrchr(cmd, '\n');
2033                 if (cp)
2034                         *cp = '\0';
2035
2036                 /* Handle user interrupts gracefully during commands */
2037                 interrupted = 0;
2038                 signal(SIGINT, cmd_interrupt);
2039
2040                 err = parse_dispatch_command(conn, cmd, &remote_path,
2041                     batchmode);
2042                 if (err != 0)
2043                         break;
2044         }
2045         free(remote_path);
2046         free(conn);
2047
2048 #ifdef USE_LIBEDIT
2049         if (el != NULL)
2050                 el_end(el);
2051 #endif /* USE_LIBEDIT */
2052
2053         /* err == 1 signifies normal "quit" exit */
2054         return (err >= 0 ? 0 : -1);
2055 }
2056
2057 static void
2058 connect_to_server(char *path, char **args, int *in, int *out)
2059 {
2060         int c_in, c_out;
2061
2062 #ifdef USE_PIPES
2063         int pin[2], pout[2];
2064
2065         if ((pipe(pin) == -1) || (pipe(pout) == -1))
2066                 fatal("pipe: %s", strerror(errno));
2067         *in = pin[0];
2068         *out = pout[1];
2069         c_in = pout[0];
2070         c_out = pin[1];
2071 #else /* USE_PIPES */
2072         int inout[2];
2073
2074         if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2075                 fatal("socketpair: %s", strerror(errno));
2076         *in = *out = inout[0];
2077         c_in = c_out = inout[1];
2078 #endif /* USE_PIPES */
2079
2080         if ((sshpid = fork()) == -1)
2081                 fatal("fork: %s", strerror(errno));
2082         else if (sshpid == 0) {
2083                 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2084                     (dup2(c_out, STDOUT_FILENO) == -1)) {
2085                         fprintf(stderr, "dup2: %s\n", strerror(errno));
2086                         _exit(1);
2087                 }
2088                 close(*in);
2089                 close(*out);
2090                 close(c_in);
2091                 close(c_out);
2092
2093                 /*
2094                  * The underlying ssh is in the same process group, so we must
2095                  * ignore SIGINT if we want to gracefully abort commands,
2096                  * otherwise the signal will make it to the ssh process and
2097                  * kill it too.  Contrawise, since sftp sends SIGTERMs to the
2098                  * underlying ssh, it must *not* ignore that signal.
2099                  */
2100                 signal(SIGINT, SIG_IGN);
2101                 signal(SIGTERM, SIG_DFL);
2102                 execvp(path, args);
2103                 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2104                 _exit(1);
2105         }
2106
2107         signal(SIGTERM, killchild);
2108         signal(SIGINT, killchild);
2109         signal(SIGHUP, killchild);
2110         close(c_in);
2111         close(c_out);
2112 }
2113
2114 static void
2115 usage(void)
2116 {
2117         extern char *__progname;
2118
2119         fprintf(stderr,
2120             "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2121             "          [-D sftp_server_path] [-F ssh_config] "
2122             "[-i identity_file] [-l limit]\n"
2123             "          [-o ssh_option] [-P port] [-R num_requests] "
2124             "[-S program]\n"
2125             "          [-s subsystem | sftp_server] host\n"
2126             "       %s [user@]host[:file ...]\n"
2127             "       %s [user@]host[:dir[/]]\n"
2128             "       %s -b batchfile [user@]host\n",
2129             __progname, __progname, __progname, __progname);
2130         exit(1);
2131 }
2132
2133 int
2134 main(int argc, char **argv)
2135 {
2136         int in, out, ch, err;
2137         char *host = NULL, *userhost, *cp, *file2 = NULL;
2138         int debug_level = 0, sshver = 2;
2139         char *file1 = NULL, *sftp_server = NULL;
2140         char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2141         const char *errstr;
2142         LogLevel ll = SYSLOG_LEVEL_INFO;
2143         arglist args;
2144         extern int optind;
2145         extern char *optarg;
2146         struct sftp_conn *conn;
2147         size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2148         size_t num_requests = DEFAULT_NUM_REQUESTS;
2149         long long limit_kbps = 0;
2150
2151         /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2152         sanitise_stdfd();
2153         setlocale(LC_CTYPE, "");
2154
2155         __progname = ssh_get_progname(argv[0]);
2156         memset(&args, '\0', sizeof(args));
2157         args.list = NULL;
2158         addargs(&args, "%s", ssh_program);
2159         addargs(&args, "-oForwardX11 no");
2160         addargs(&args, "-oForwardAgent no");
2161         addargs(&args, "-oPermitLocalCommand no");
2162         addargs(&args, "-oClearAllForwardings yes");
2163
2164         ll = SYSLOG_LEVEL_INFO;
2165         infile = stdin;
2166
2167         while ((ch = getopt(argc, argv,
2168             "1246ahpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2169                 switch (ch) {
2170                 /* Passed through to ssh(1) */
2171                 case '4':
2172                 case '6':
2173                 case 'C':
2174                         addargs(&args, "-%c", ch);
2175                         break;
2176                 /* Passed through to ssh(1) with argument */
2177                 case 'F':
2178                 case 'c':
2179                 case 'i':
2180                 case 'o':
2181                         addargs(&args, "-%c", ch);
2182                         addargs(&args, "%s", optarg);
2183                         break;
2184                 case 'q':
2185                         ll = SYSLOG_LEVEL_ERROR;
2186                         quiet = 1;
2187                         showprogress = 0;
2188                         addargs(&args, "-%c", ch);
2189                         break;
2190                 case 'P':
2191                         addargs(&args, "-oPort %s", optarg);
2192                         break;
2193                 case 'v':
2194                         if (debug_level < 3) {
2195                                 addargs(&args, "-v");
2196                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2197                         }
2198                         debug_level++;
2199                         break;
2200                 case '1':
2201                         sshver = 1;
2202                         if (sftp_server == NULL)
2203                                 sftp_server = _PATH_SFTP_SERVER;
2204                         break;
2205                 case '2':
2206                         sshver = 2;
2207                         break;
2208                 case 'a':
2209                         global_aflag = 1;
2210                         break;
2211                 case 'B':
2212                         copy_buffer_len = strtol(optarg, &cp, 10);
2213                         if (copy_buffer_len == 0 || *cp != '\0')
2214                                 fatal("Invalid buffer size \"%s\"", optarg);
2215                         break;
2216                 case 'b':
2217                         if (batchmode)
2218                                 fatal("Batch file already specified.");
2219
2220                         /* Allow "-" as stdin */
2221                         if (strcmp(optarg, "-") != 0 &&
2222                             (infile = fopen(optarg, "r")) == NULL)
2223                                 fatal("%s (%s).", strerror(errno), optarg);
2224                         showprogress = 0;
2225                         quiet = batchmode = 1;
2226                         addargs(&args, "-obatchmode yes");
2227                         break;
2228                 case 'p':
2229                         global_pflag = 1;
2230                         break;
2231                 case 'D':
2232                         sftp_direct = optarg;
2233                         break;
2234                 case 'l':
2235                         limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2236                             &errstr);
2237                         if (errstr != NULL)
2238                                 usage();
2239                         limit_kbps *= 1024; /* kbps */
2240                         break;
2241                 case 'r':
2242                         global_rflag = 1;
2243                         break;
2244                 case 'R':
2245                         num_requests = strtol(optarg, &cp, 10);
2246                         if (num_requests == 0 || *cp != '\0')
2247                                 fatal("Invalid number of requests \"%s\"",
2248                                     optarg);
2249                         break;
2250                 case 's':
2251                         sftp_server = optarg;
2252                         break;
2253                 case 'S':
2254                         ssh_program = optarg;
2255                         replacearg(&args, 0, "%s", ssh_program);
2256                         break;
2257                 case 'h':
2258                 default:
2259                         usage();
2260                 }
2261         }
2262
2263         if (!isatty(STDERR_FILENO))
2264                 showprogress = 0;
2265
2266         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2267
2268         if (sftp_direct == NULL) {
2269                 if (optind == argc || argc > (optind + 2))
2270                         usage();
2271
2272                 userhost = xstrdup(argv[optind]);
2273                 file2 = argv[optind+1];
2274
2275                 if ((host = strrchr(userhost, '@')) == NULL)
2276                         host = userhost;
2277                 else {
2278                         *host++ = '\0';
2279                         if (!userhost[0]) {
2280                                 fprintf(stderr, "Missing username\n");
2281                                 usage();
2282                         }
2283                         addargs(&args, "-l");
2284                         addargs(&args, "%s", userhost);
2285                 }
2286
2287                 if ((cp = colon(host)) != NULL) {
2288                         *cp++ = '\0';
2289                         file1 = cp;
2290                 }
2291
2292                 host = cleanhostname(host);
2293                 if (!*host) {
2294                         fprintf(stderr, "Missing hostname\n");
2295                         usage();
2296                 }
2297
2298                 addargs(&args, "-oProtocol %d", sshver);
2299
2300                 /* no subsystem if the server-spec contains a '/' */
2301                 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2302                         addargs(&args, "-s");
2303
2304                 addargs(&args, "--");
2305                 addargs(&args, "%s", host);
2306                 addargs(&args, "%s", (sftp_server != NULL ?
2307                     sftp_server : "sftp"));
2308
2309                 connect_to_server(ssh_program, args.list, &in, &out);
2310         } else {
2311                 args.list = NULL;
2312                 addargs(&args, "sftp-server");
2313
2314                 connect_to_server(sftp_direct, args.list, &in, &out);
2315         }
2316         freeargs(&args);
2317
2318         conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2319         if (conn == NULL)
2320                 fatal("Couldn't initialise connection to server");
2321
2322         if (!quiet) {
2323                 if (sftp_direct == NULL)
2324                         fprintf(stderr, "Connected to %s.\n", host);
2325                 else
2326                         fprintf(stderr, "Attached to %s.\n", sftp_direct);
2327         }
2328
2329         err = interactive_loop(conn, file1, file2);
2330
2331 #if !defined(USE_PIPES)
2332         shutdown(in, SHUT_RDWR);
2333         shutdown(out, SHUT_RDWR);
2334 #endif
2335
2336         close(in);
2337         close(out);
2338         if (batchmode)
2339                 fclose(infile);
2340
2341         while (waitpid(sshpid, NULL, 0) == -1)
2342                 if (errno != EINTR)
2343                         fatal("Couldn't wait for ssh process: %s",
2344                             strerror(errno));
2345
2346         exit(err == 0 ? 0 : 1);
2347 }