]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - crypto/openssh/sftp.c
Fix build breakage after r259448
[FreeBSD/stable/9.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                 if (!sflag)
1332                         path1 = make_absolute(path1, *pwd);
1333                 path2 = make_absolute(path2, *pwd);
1334                 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1335                 break;
1336         case I_RM:
1337                 path1 = make_absolute(path1, *pwd);
1338                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1339                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1340                         printf("Removing %s\n", g.gl_pathv[i]);
1341                         err = do_rm(conn, g.gl_pathv[i]);
1342                         if (err != 0 && err_abort)
1343                                 break;
1344                 }
1345                 break;
1346         case I_MKDIR:
1347                 path1 = make_absolute(path1, *pwd);
1348                 attrib_clear(&a);
1349                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1350                 a.perm = 0777;
1351                 err = do_mkdir(conn, path1, &a, 1);
1352                 break;
1353         case I_RMDIR:
1354                 path1 = make_absolute(path1, *pwd);
1355                 err = do_rmdir(conn, path1);
1356                 break;
1357         case I_CHDIR:
1358                 path1 = make_absolute(path1, *pwd);
1359                 if ((tmp = do_realpath(conn, path1)) == NULL) {
1360                         err = 1;
1361                         break;
1362                 }
1363                 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1364                         xfree(tmp);
1365                         err = 1;
1366                         break;
1367                 }
1368                 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1369                         error("Can't change directory: Can't check target");
1370                         xfree(tmp);
1371                         err = 1;
1372                         break;
1373                 }
1374                 if (!S_ISDIR(aa->perm)) {
1375                         error("Can't change directory: \"%s\" is not "
1376                             "a directory", tmp);
1377                         xfree(tmp);
1378                         err = 1;
1379                         break;
1380                 }
1381                 xfree(*pwd);
1382                 *pwd = tmp;
1383                 break;
1384         case I_LS:
1385                 if (!path1) {
1386                         do_ls_dir(conn, *pwd, *pwd, lflag);
1387                         break;
1388                 }
1389
1390                 /* Strip pwd off beginning of non-absolute paths */
1391                 tmp = NULL;
1392                 if (*path1 != '/')
1393                         tmp = *pwd;
1394
1395                 path1 = make_absolute(path1, *pwd);
1396                 err = do_globbed_ls(conn, path1, tmp, lflag);
1397                 break;
1398         case I_DF:
1399                 /* Default to current directory if no path specified */
1400                 if (path1 == NULL)
1401                         path1 = xstrdup(*pwd);
1402                 path1 = make_absolute(path1, *pwd);
1403                 err = do_df(conn, path1, hflag, iflag);
1404                 break;
1405         case I_LCHDIR:
1406                 if (chdir(path1) == -1) {
1407                         error("Couldn't change local directory to "
1408                             "\"%s\": %s", path1, strerror(errno));
1409                         err = 1;
1410                 }
1411                 break;
1412         case I_LMKDIR:
1413                 if (mkdir(path1, 0777) == -1) {
1414                         error("Couldn't create local directory "
1415                             "\"%s\": %s", path1, strerror(errno));
1416                         err = 1;
1417                 }
1418                 break;
1419         case I_LLS:
1420                 local_do_ls(cmd);
1421                 break;
1422         case I_SHELL:
1423                 local_do_shell(cmd);
1424                 break;
1425         case I_LUMASK:
1426                 umask(n_arg);
1427                 printf("Local umask: %03lo\n", n_arg);
1428                 break;
1429         case I_CHMOD:
1430                 path1 = make_absolute(path1, *pwd);
1431                 attrib_clear(&a);
1432                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1433                 a.perm = n_arg;
1434                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1435                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1436                         printf("Changing mode on %s\n", g.gl_pathv[i]);
1437                         err = do_setstat(conn, g.gl_pathv[i], &a);
1438                         if (err != 0 && err_abort)
1439                                 break;
1440                 }
1441                 break;
1442         case I_CHOWN:
1443         case I_CHGRP:
1444                 path1 = make_absolute(path1, *pwd);
1445                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1446                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1447                         if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1448                                 if (err_abort) {
1449                                         err = -1;
1450                                         break;
1451                                 } else
1452                                         continue;
1453                         }
1454                         if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1455                                 error("Can't get current ownership of "
1456                                     "remote file \"%s\"", g.gl_pathv[i]);
1457                                 if (err_abort) {
1458                                         err = -1;
1459                                         break;
1460                                 } else
1461                                         continue;
1462                         }
1463                         aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1464                         if (cmdnum == I_CHOWN) {
1465                                 printf("Changing owner on %s\n", g.gl_pathv[i]);
1466                                 aa->uid = n_arg;
1467                         } else {
1468                                 printf("Changing group on %s\n", g.gl_pathv[i]);
1469                                 aa->gid = n_arg;
1470                         }
1471                         err = do_setstat(conn, g.gl_pathv[i], aa);
1472                         if (err != 0 && err_abort)
1473                                 break;
1474                 }
1475                 break;
1476         case I_PWD:
1477                 printf("Remote working directory: %s\n", *pwd);
1478                 break;
1479         case I_LPWD:
1480                 if (!getcwd(path_buf, sizeof(path_buf))) {
1481                         error("Couldn't get local cwd: %s", strerror(errno));
1482                         err = -1;
1483                         break;
1484                 }
1485                 printf("Local working directory: %s\n", path_buf);
1486                 break;
1487         case I_QUIT:
1488                 /* Processed below */
1489                 break;
1490         case I_HELP:
1491                 help();
1492                 break;
1493         case I_VERSION:
1494                 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1495                 break;
1496         case I_PROGRESS:
1497                 showprogress = !showprogress;
1498                 if (showprogress)
1499                         printf("Progress meter enabled\n");
1500                 else
1501                         printf("Progress meter disabled\n");
1502                 break;
1503         default:
1504                 fatal("%d is not implemented", cmdnum);
1505         }
1506
1507         if (g.gl_pathc)
1508                 globfree(&g);
1509         if (path1)
1510                 xfree(path1);
1511         if (path2)
1512                 xfree(path2);
1513
1514         /* If an unignored error occurs in batch mode we should abort. */
1515         if (err_abort && err != 0)
1516                 return (-1);
1517         else if (cmdnum == I_QUIT)
1518                 return (1);
1519
1520         return (0);
1521 }
1522
1523 #ifdef USE_LIBEDIT
1524 static char *
1525 prompt(EditLine *el)
1526 {
1527         return ("sftp> ");
1528 }
1529
1530 /* Display entries in 'list' after skipping the first 'len' chars */
1531 static void
1532 complete_display(char **list, u_int len)
1533 {
1534         u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1535         struct winsize ws;
1536         char *tmp;
1537
1538         /* Count entries for sort and find longest */
1539         for (y = 0; list[y]; y++) 
1540                 m = MAX(m, strlen(list[y]));
1541
1542         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1543                 width = ws.ws_col;
1544
1545         m = m > len ? m - len : 0;
1546         columns = width / (m + 2);
1547         columns = MAX(columns, 1);
1548         colspace = width / columns;
1549         colspace = MIN(colspace, width);
1550
1551         printf("\n");
1552         m = 1;
1553         for (y = 0; list[y]; y++) {
1554                 llen = strlen(list[y]);
1555                 tmp = llen > len ? list[y] + len : "";
1556                 printf("%-*s", colspace, tmp);
1557                 if (m >= columns) {
1558                         printf("\n");
1559                         m = 1;
1560                 } else
1561                         m++;
1562         }
1563         printf("\n");
1564 }
1565
1566 /*
1567  * Given a "list" of words that begin with a common prefix of "word",
1568  * attempt to find an autocompletion to extends "word" by the next
1569  * characters common to all entries in "list".
1570  */
1571 static char *
1572 complete_ambiguous(const char *word, char **list, size_t count)
1573 {
1574         if (word == NULL)
1575                 return NULL;
1576
1577         if (count > 0) {
1578                 u_int y, matchlen = strlen(list[0]);
1579
1580                 /* Find length of common stem */
1581                 for (y = 1; list[y]; y++) {
1582                         u_int x;
1583
1584                         for (x = 0; x < matchlen; x++) 
1585                                 if (list[0][x] != list[y][x]) 
1586                                         break;
1587
1588                         matchlen = x;
1589                 }
1590
1591                 if (matchlen > strlen(word)) {
1592                         char *tmp = xstrdup(list[0]);
1593
1594                         tmp[matchlen] = '\0';
1595                         return tmp;
1596                 }
1597         } 
1598
1599         return xstrdup(word);
1600 }
1601
1602 /* Autocomplete a sftp command */
1603 static int
1604 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1605     int terminated)
1606 {
1607         u_int y, count = 0, cmdlen, tmplen;
1608         char *tmp, **list, argterm[3];
1609         const LineInfo *lf;
1610
1611         list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1612
1613         /* No command specified: display all available commands */
1614         if (cmd == NULL) {
1615                 for (y = 0; cmds[y].c; y++)
1616                         list[count++] = xstrdup(cmds[y].c);
1617                 
1618                 list[count] = NULL;
1619                 complete_display(list, 0);
1620
1621                 for (y = 0; list[y] != NULL; y++)  
1622                         xfree(list[y]); 
1623                 xfree(list);
1624                 return count;
1625         }
1626
1627         /* Prepare subset of commands that start with "cmd" */
1628         cmdlen = strlen(cmd);
1629         for (y = 0; cmds[y].c; y++)  {
1630                 if (!strncasecmp(cmd, cmds[y].c, cmdlen)) 
1631                         list[count++] = xstrdup(cmds[y].c);
1632         }
1633         list[count] = NULL;
1634
1635         if (count == 0) {
1636                 xfree(list);
1637                 return 0;
1638         }
1639
1640         /* Complete ambigious command */
1641         tmp = complete_ambiguous(cmd, list, count);
1642         if (count > 1)
1643                 complete_display(list, 0);
1644
1645         for (y = 0; list[y]; y++)  
1646                 xfree(list[y]); 
1647         xfree(list);
1648
1649         if (tmp != NULL) {
1650                 tmplen = strlen(tmp);
1651                 cmdlen = strlen(cmd);
1652                 /* If cmd may be extended then do so */
1653                 if (tmplen > cmdlen)
1654                         if (el_insertstr(el, tmp + cmdlen) == -1)
1655                                 fatal("el_insertstr failed.");
1656                 lf = el_line(el);
1657                 /* Terminate argument cleanly */
1658                 if (count == 1) {
1659                         y = 0;
1660                         if (!terminated)
1661                                 argterm[y++] = quote;
1662                         if (lastarg || *(lf->cursor) != ' ')
1663                                 argterm[y++] = ' ';
1664                         argterm[y] = '\0';
1665                         if (y > 0 && el_insertstr(el, argterm) == -1)
1666                                 fatal("el_insertstr failed.");
1667                 }
1668                 xfree(tmp);
1669         }
1670
1671         return count;
1672 }
1673
1674 /*
1675  * Determine whether a particular sftp command's arguments (if any)
1676  * represent local or remote files.
1677  */
1678 static int
1679 complete_is_remote(char *cmd) {
1680         int i;
1681
1682         if (cmd == NULL)
1683                 return -1;
1684
1685         for (i = 0; cmds[i].c; i++) {
1686                 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 
1687                         return cmds[i].t;
1688         }
1689
1690         return -1;
1691 }
1692
1693 /* Autocomplete a filename "file" */
1694 static int
1695 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1696     char *file, int remote, int lastarg, char quote, int terminated)
1697 {
1698         glob_t g;
1699         char *tmp, *tmp2, ins[3];
1700         u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1701         const LineInfo *lf;
1702         
1703         /* Glob from "file" location */
1704         if (file == NULL)
1705                 tmp = xstrdup("*");
1706         else
1707                 xasprintf(&tmp, "%s*", file);
1708
1709         /* Check if the path is absolute. */
1710         isabs = tmp[0] == '/';
1711
1712         memset(&g, 0, sizeof(g));
1713         if (remote != LOCAL) {
1714                 tmp = make_absolute(tmp, remote_path);
1715                 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1716         } else 
1717                 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1718         
1719         /* Determine length of pwd so we can trim completion display */
1720         for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1721                 /* Terminate counting on first unescaped glob metacharacter */
1722                 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1723                         if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1724                                 hadglob = 1;
1725                         break;
1726                 }
1727                 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1728                         tmplen++;
1729                 if (tmp[tmplen] == '/')
1730                         pwdlen = tmplen + 1;    /* track last seen '/' */
1731         }
1732         xfree(tmp);
1733
1734         if (g.gl_matchc == 0) 
1735                 goto out;
1736
1737         if (g.gl_matchc > 1)
1738                 complete_display(g.gl_pathv, pwdlen);
1739
1740         tmp = NULL;
1741         /* Don't try to extend globs */
1742         if (file == NULL || hadglob)
1743                 goto out;
1744
1745         tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1746         tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1747         xfree(tmp2);
1748
1749         if (tmp == NULL)
1750                 goto out;
1751
1752         tmplen = strlen(tmp);
1753         filelen = strlen(file);
1754
1755         /* Count the number of escaped characters in the input string. */
1756         cesc = isesc = 0;
1757         for (i = 0; i < filelen; i++) {
1758                 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1759                         isesc = 1;
1760                         cesc++;
1761                 } else
1762                         isesc = 0;
1763         }
1764
1765         if (tmplen > (filelen - cesc)) {
1766                 tmp2 = tmp + filelen - cesc;
1767                 len = strlen(tmp2); 
1768                 /* quote argument on way out */
1769                 for (i = 0; i < len; i++) {
1770                         ins[0] = '\\';
1771                         ins[1] = tmp2[i];
1772                         ins[2] = '\0';
1773                         switch (tmp2[i]) {
1774                         case '\'':
1775                         case '"':
1776                         case '\\':
1777                         case '\t':
1778                         case '[':
1779                         case ' ':
1780                         case '#':
1781                         case '*':
1782                                 if (quote == '\0' || tmp2[i] == quote) {
1783                                         if (el_insertstr(el, ins) == -1)
1784                                                 fatal("el_insertstr "
1785                                                     "failed.");
1786                                         break;
1787                                 }
1788                                 /* FALLTHROUGH */
1789                         default:
1790                                 if (el_insertstr(el, ins + 1) == -1)
1791                                         fatal("el_insertstr failed.");
1792                                 break;
1793                         }
1794                 }
1795         }
1796
1797         lf = el_line(el);
1798         if (g.gl_matchc == 1) {
1799                 i = 0;
1800                 if (!terminated)
1801                         ins[i++] = quote;
1802                 if (*(lf->cursor - 1) != '/' &&
1803                     (lastarg || *(lf->cursor) != ' '))
1804                         ins[i++] = ' ';
1805                 ins[i] = '\0';
1806                 if (i > 0 && el_insertstr(el, ins) == -1)
1807                         fatal("el_insertstr failed.");
1808         }
1809         xfree(tmp);
1810
1811  out:
1812         globfree(&g);
1813         return g.gl_matchc;
1814 }
1815
1816 /* tab-completion hook function, called via libedit */
1817 static unsigned char
1818 complete(EditLine *el, int ch)
1819 {
1820         char **argv, *line, quote; 
1821         u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1822         const LineInfo *lf;
1823         struct complete_ctx *complete_ctx;
1824
1825         lf = el_line(el);
1826         if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1827                 fatal("%s: el_get failed", __func__);
1828
1829         /* Figure out which argument the cursor points to */
1830         cursor = lf->cursor - lf->buffer;
1831         line = (char *)xmalloc(cursor + 1);
1832         memcpy(line, lf->buffer, cursor);
1833         line[cursor] = '\0';
1834         argv = makeargv(line, &carg, 1, &quote, &terminated);
1835         xfree(line);
1836
1837         /* Get all the arguments on the line */
1838         len = lf->lastchar - lf->buffer;
1839         line = (char *)xmalloc(len + 1);
1840         memcpy(line, lf->buffer, len);
1841         line[len] = '\0';
1842         argv = makeargv(line, &argc, 1, NULL, NULL);
1843
1844         /* Ensure cursor is at EOL or a argument boundary */
1845         if (line[cursor] != ' ' && line[cursor] != '\0' &&
1846             line[cursor] != '\n') {
1847                 xfree(line);
1848                 return ret;
1849         }
1850
1851         if (carg == 0) {
1852                 /* Show all available commands */
1853                 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1854                 ret = CC_REDISPLAY;
1855         } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
1856                 /* Handle the command parsing */
1857                 if (complete_cmd_parse(el, argv[0], argc == carg,
1858                     quote, terminated) != 0) 
1859                         ret = CC_REDISPLAY;
1860         } else if (carg >= 1) {
1861                 /* Handle file parsing */
1862                 int remote = complete_is_remote(argv[0]);
1863                 char *filematch = NULL;
1864
1865                 if (carg > 1 && line[cursor-1] != ' ')
1866                         filematch = argv[carg - 1];
1867
1868                 if (remote != 0 &&
1869                     complete_match(el, complete_ctx->conn,
1870                     *complete_ctx->remote_pathp, filematch,
1871                     remote, carg == argc, quote, terminated) != 0) 
1872                         ret = CC_REDISPLAY;
1873         }
1874
1875         xfree(line);    
1876         return ret;
1877 }
1878 #endif /* USE_LIBEDIT */
1879
1880 int
1881 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1882 {
1883         char *remote_path;
1884         char *dir = NULL;
1885         char cmd[2048];
1886         int err, interactive;
1887         EditLine *el = NULL;
1888 #ifdef USE_LIBEDIT
1889         History *hl = NULL;
1890         HistEvent hev;
1891         extern char *__progname;
1892         struct complete_ctx complete_ctx;
1893
1894         if (!batchmode && isatty(STDIN_FILENO)) {
1895                 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1896                         fatal("Couldn't initialise editline");
1897                 if ((hl = history_init()) == NULL)
1898                         fatal("Couldn't initialise editline history");
1899                 history(hl, &hev, H_SETSIZE, 100);
1900                 el_set(el, EL_HIST, history, hl);
1901
1902                 el_set(el, EL_PROMPT, prompt);
1903                 el_set(el, EL_EDITOR, "emacs");
1904                 el_set(el, EL_TERMINAL, NULL);
1905                 el_set(el, EL_SIGNAL, 1);
1906                 el_source(el, NULL);
1907
1908                 /* Tab Completion */
1909                 el_set(el, EL_ADDFN, "ftp-complete", 
1910                     "Context sensitive argument completion", complete);
1911                 complete_ctx.conn = conn;
1912                 complete_ctx.remote_pathp = &remote_path;
1913                 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1914                 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1915         }
1916 #endif /* USE_LIBEDIT */
1917
1918         remote_path = do_realpath(conn, ".");
1919         if (remote_path == NULL)
1920                 fatal("Need cwd");
1921
1922         if (file1 != NULL) {
1923                 dir = xstrdup(file1);
1924                 dir = make_absolute(dir, remote_path);
1925
1926                 if (remote_is_dir(conn, dir) && file2 == NULL) {
1927                         printf("Changing to: %s\n", dir);
1928                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1929                         if (parse_dispatch_command(conn, cmd,
1930                             &remote_path, 1) != 0) {
1931                                 xfree(dir);
1932                                 xfree(remote_path);
1933                                 xfree(conn);
1934                                 return (-1);
1935                         }
1936                 } else {
1937                         /* XXX this is wrong wrt quoting */
1938                         if (file2 == NULL)
1939                                 snprintf(cmd, sizeof cmd, "get %s", dir);
1940                         else
1941                                 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1942                                     file2);
1943
1944                         err = parse_dispatch_command(conn, cmd,
1945                             &remote_path, 1);
1946                         xfree(dir);
1947                         xfree(remote_path);
1948                         xfree(conn);
1949                         return (err);
1950                 }
1951                 xfree(dir);
1952         }
1953
1954         setlinebuf(stdout);
1955         setlinebuf(infile);
1956
1957         interactive = !batchmode && isatty(STDIN_FILENO);
1958         err = 0;
1959         for (;;) {
1960                 char *cp;
1961
1962                 signal(SIGINT, SIG_IGN);
1963
1964                 if (el == NULL) {
1965                         if (interactive)
1966                                 printf("sftp> ");
1967                         if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1968                                 if (interactive)
1969                                         printf("\n");
1970                                 break;
1971                         }
1972                         if (!interactive) { /* Echo command */
1973                                 printf("sftp> %s", cmd);
1974                                 if (strlen(cmd) > 0 &&
1975                                     cmd[strlen(cmd) - 1] != '\n')
1976                                         printf("\n");
1977                         }
1978                 } else {
1979 #ifdef USE_LIBEDIT
1980                         const char *line;
1981                         int count = 0;
1982
1983                         if ((line = el_gets(el, &count)) == NULL ||
1984                             count <= 0) {
1985                                 printf("\n");
1986                                 break;
1987                         }
1988                         history(hl, &hev, H_ENTER, line);
1989                         if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1990                                 fprintf(stderr, "Error: input line too long\n");
1991                                 continue;
1992                         }
1993 #endif /* USE_LIBEDIT */
1994                 }
1995
1996                 cp = strrchr(cmd, '\n');
1997                 if (cp)
1998                         *cp = '\0';
1999
2000                 /* Handle user interrupts gracefully during commands */
2001                 interrupted = 0;
2002                 signal(SIGINT, cmd_interrupt);
2003
2004                 err = parse_dispatch_command(conn, cmd, &remote_path,
2005                     batchmode);
2006                 if (err != 0)
2007                         break;
2008         }
2009         xfree(remote_path);
2010         xfree(conn);
2011
2012 #ifdef USE_LIBEDIT
2013         if (el != NULL)
2014                 el_end(el);
2015 #endif /* USE_LIBEDIT */
2016
2017         /* err == 1 signifies normal "quit" exit */
2018         return (err >= 0 ? 0 : -1);
2019 }
2020
2021 static void
2022 connect_to_server(char *path, char **args, int *in, int *out)
2023 {
2024         int c_in, c_out;
2025
2026 #ifdef USE_PIPES
2027         int pin[2], pout[2];
2028
2029         if ((pipe(pin) == -1) || (pipe(pout) == -1))
2030                 fatal("pipe: %s", strerror(errno));
2031         *in = pin[0];
2032         *out = pout[1];
2033         c_in = pout[0];
2034         c_out = pin[1];
2035 #else /* USE_PIPES */
2036         int inout[2];
2037
2038         if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2039                 fatal("socketpair: %s", strerror(errno));
2040         *in = *out = inout[0];
2041         c_in = c_out = inout[1];
2042 #endif /* USE_PIPES */
2043
2044         if ((sshpid = fork()) == -1)
2045                 fatal("fork: %s", strerror(errno));
2046         else if (sshpid == 0) {
2047                 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2048                     (dup2(c_out, STDOUT_FILENO) == -1)) {
2049                         fprintf(stderr, "dup2: %s\n", strerror(errno));
2050                         _exit(1);
2051                 }
2052                 close(*in);
2053                 close(*out);
2054                 close(c_in);
2055                 close(c_out);
2056
2057                 /*
2058                  * The underlying ssh is in the same process group, so we must
2059                  * ignore SIGINT if we want to gracefully abort commands,
2060                  * otherwise the signal will make it to the ssh process and
2061                  * kill it too.  Contrawise, since sftp sends SIGTERMs to the
2062                  * underlying ssh, it must *not* ignore that signal.
2063                  */
2064                 signal(SIGINT, SIG_IGN);
2065                 signal(SIGTERM, SIG_DFL);
2066                 execvp(path, args);
2067                 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2068                 _exit(1);
2069         }
2070
2071         signal(SIGTERM, killchild);
2072         signal(SIGINT, killchild);
2073         signal(SIGHUP, killchild);
2074         close(c_in);
2075         close(c_out);
2076 }
2077
2078 static void
2079 usage(void)
2080 {
2081         extern char *__progname;
2082
2083         fprintf(stderr,
2084             "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2085             "          [-D sftp_server_path] [-F ssh_config] "
2086             "[-i identity_file] [-l limit]\n"
2087             "          [-o ssh_option] [-P port] [-R num_requests] "
2088             "[-S program]\n"
2089             "          [-s subsystem | sftp_server] host\n"
2090             "       %s [user@]host[:file ...]\n"
2091             "       %s [user@]host[:dir[/]]\n"
2092             "       %s -b batchfile [user@]host\n",
2093             __progname, __progname, __progname, __progname);
2094         exit(1);
2095 }
2096
2097 int
2098 main(int argc, char **argv)
2099 {
2100         int in, out, ch, err;
2101         char *host = NULL, *userhost, *cp, *file2 = NULL;
2102         int debug_level = 0, sshver = 2;
2103         char *file1 = NULL, *sftp_server = NULL;
2104         char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2105         const char *errstr;
2106         LogLevel ll = SYSLOG_LEVEL_INFO;
2107         arglist args;
2108         extern int optind;
2109         extern char *optarg;
2110         struct sftp_conn *conn;
2111         size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2112         size_t num_requests = DEFAULT_NUM_REQUESTS;
2113         long long limit_kbps = 0;
2114
2115         /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2116         sanitise_stdfd();
2117
2118         __progname = ssh_get_progname(argv[0]);
2119         memset(&args, '\0', sizeof(args));
2120         args.list = NULL;
2121         addargs(&args, "%s", ssh_program);
2122         addargs(&args, "-oForwardX11 no");
2123         addargs(&args, "-oForwardAgent no");
2124         addargs(&args, "-oPermitLocalCommand no");
2125         addargs(&args, "-oClearAllForwardings yes");
2126
2127         ll = SYSLOG_LEVEL_INFO;
2128         infile = stdin;
2129
2130         while ((ch = getopt(argc, argv,
2131             "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2132                 switch (ch) {
2133                 /* Passed through to ssh(1) */
2134                 case '4':
2135                 case '6':
2136                 case 'C':
2137                         addargs(&args, "-%c", ch);
2138                         break;
2139                 /* Passed through to ssh(1) with argument */
2140                 case 'F':
2141                 case 'c':
2142                 case 'i':
2143                 case 'o':
2144                         addargs(&args, "-%c", ch);
2145                         addargs(&args, "%s", optarg);
2146                         break;
2147                 case 'q':
2148                         showprogress = 0;
2149                         addargs(&args, "-%c", ch);
2150                         break;
2151                 case 'P':
2152                         addargs(&args, "-oPort %s", optarg);
2153                         break;
2154                 case 'v':
2155                         if (debug_level < 3) {
2156                                 addargs(&args, "-v");
2157                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2158                         }
2159                         debug_level++;
2160                         break;
2161                 case '1':
2162                         sshver = 1;
2163                         if (sftp_server == NULL)
2164                                 sftp_server = _PATH_SFTP_SERVER;
2165                         break;
2166                 case '2':
2167                         sshver = 2;
2168                         break;
2169                 case 'B':
2170                         copy_buffer_len = strtol(optarg, &cp, 10);
2171                         if (copy_buffer_len == 0 || *cp != '\0')
2172                                 fatal("Invalid buffer size \"%s\"", optarg);
2173                         break;
2174                 case 'b':
2175                         if (batchmode)
2176                                 fatal("Batch file already specified.");
2177
2178                         /* Allow "-" as stdin */
2179                         if (strcmp(optarg, "-") != 0 &&
2180                             (infile = fopen(optarg, "r")) == NULL)
2181                                 fatal("%s (%s).", strerror(errno), optarg);
2182                         showprogress = 0;
2183                         batchmode = 1;
2184                         addargs(&args, "-obatchmode yes");
2185                         break;
2186                 case 'p':
2187                         global_pflag = 1;
2188                         break;
2189                 case 'D':
2190                         sftp_direct = optarg;
2191                         break;
2192                 case 'l':
2193                         limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2194                             &errstr);
2195                         if (errstr != NULL)
2196                                 usage();
2197                         limit_kbps *= 1024; /* kbps */
2198                         break;
2199                 case 'r':
2200                         global_rflag = 1;
2201                         break;
2202                 case 'R':
2203                         num_requests = strtol(optarg, &cp, 10);
2204                         if (num_requests == 0 || *cp != '\0')
2205                                 fatal("Invalid number of requests \"%s\"",
2206                                     optarg);
2207                         break;
2208                 case 's':
2209                         sftp_server = optarg;
2210                         break;
2211                 case 'S':
2212                         ssh_program = optarg;
2213                         replacearg(&args, 0, "%s", ssh_program);
2214                         break;
2215                 case 'h':
2216                 default:
2217                         usage();
2218                 }
2219         }
2220
2221         if (!isatty(STDERR_FILENO))
2222                 showprogress = 0;
2223
2224         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2225
2226         if (sftp_direct == NULL) {
2227                 if (optind == argc || argc > (optind + 2))
2228                         usage();
2229
2230                 userhost = xstrdup(argv[optind]);
2231                 file2 = argv[optind+1];
2232
2233                 if ((host = strrchr(userhost, '@')) == NULL)
2234                         host = userhost;
2235                 else {
2236                         *host++ = '\0';
2237                         if (!userhost[0]) {
2238                                 fprintf(stderr, "Missing username\n");
2239                                 usage();
2240                         }
2241                         addargs(&args, "-l");
2242                         addargs(&args, "%s", userhost);
2243                 }
2244
2245                 if ((cp = colon(host)) != NULL) {
2246                         *cp++ = '\0';
2247                         file1 = cp;
2248                 }
2249
2250                 host = cleanhostname(host);
2251                 if (!*host) {
2252                         fprintf(stderr, "Missing hostname\n");
2253                         usage();
2254                 }
2255
2256                 addargs(&args, "-oProtocol %d", sshver);
2257
2258                 /* no subsystem if the server-spec contains a '/' */
2259                 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2260                         addargs(&args, "-s");
2261
2262                 addargs(&args, "--");
2263                 addargs(&args, "%s", host);
2264                 addargs(&args, "%s", (sftp_server != NULL ?
2265                     sftp_server : "sftp"));
2266
2267                 connect_to_server(ssh_program, args.list, &in, &out);
2268         } else {
2269                 args.list = NULL;
2270                 addargs(&args, "sftp-server");
2271
2272                 connect_to_server(sftp_direct, args.list, &in, &out);
2273         }
2274         freeargs(&args);
2275
2276         conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2277         if (conn == NULL)
2278                 fatal("Couldn't initialise connection to server");
2279
2280         if (!batchmode) {
2281                 if (sftp_direct == NULL)
2282                         fprintf(stderr, "Connected to %s.\n", host);
2283                 else
2284                         fprintf(stderr, "Attached to %s.\n", sftp_direct);
2285         }
2286
2287         err = interactive_loop(conn, file1, file2);
2288
2289 #if !defined(USE_PIPES)
2290         shutdown(in, SHUT_RDWR);
2291         shutdown(out, SHUT_RDWR);
2292 #endif
2293
2294         close(in);
2295         close(out);
2296         if (batchmode)
2297                 fclose(infile);
2298
2299         while (waitpid(sshpid, NULL, 0) == -1)
2300                 if (errno != EINTR)
2301                         fatal("Couldn't wait for ssh process: %s",
2302                             strerror(errno));
2303
2304         exit(err == 0 ? 0 : 1);
2305 }