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