]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - crypto/openssh/sftp.c
Pull in OpenSSH 6.1 from head.
[FreeBSD/stable/9.git] / crypto / openssh / sftp.c
1 /* $OpenBSD: sftp.c,v 1.136 2012/06/22 14:36:33 dtucker 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 #ifdef HAVE_LIBUTIL_H
59 # include <libutil.h>
60 #endif
61
62 #include "xmalloc.h"
63 #include "log.h"
64 #include "pathnames.h"
65 #include "misc.h"
66
67 #include "sftp.h"
68 #include "buffer.h"
69 #include "sftp-common.h"
70 #include "sftp-client.h"
71
72 #define DEFAULT_COPY_BUFLEN     32768   /* Size of buffer for up/download */
73 #define DEFAULT_NUM_REQUESTS    256     /* # concurrent outstanding requests */
74
75 /* File to read commands from */
76 FILE* infile;
77
78 /* Are we in batchfile mode? */
79 int batchmode = 0;
80
81 /* PID of ssh transport process */
82 static pid_t sshpid = -1;
83
84 /* This is set to 0 if the progressmeter is not desired. */
85 int showprogress = 1;
86
87 /* When this option is set, we always recursively download/upload directories */
88 int global_rflag = 0;
89
90 /* When this option is set, the file transfers will always preserve times */
91 int global_pflag = 0;
92
93 /* SIGINT received during command processing */
94 volatile sig_atomic_t interrupted = 0;
95
96 /* I wish qsort() took a separate ctx for the comparison function...*/
97 int sort_flag;
98
99 /* Context used for commandline completion */
100 struct complete_ctx {
101         struct sftp_conn *conn;
102         char **remote_pathp;
103 };
104
105 int remote_glob(struct sftp_conn *, const char *, int,
106     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
107
108 extern char *__progname;
109
110 /* Separators for interactive commands */
111 #define WHITESPACE " \t\r\n"
112
113 /* ls flags */
114 #define LS_LONG_VIEW    0x0001  /* Full view ala ls -l */
115 #define LS_SHORT_VIEW   0x0002  /* Single row view ala ls -1 */
116 #define LS_NUMERIC_VIEW 0x0004  /* Long view with numeric uid/gid */
117 #define LS_NAME_SORT    0x0008  /* Sort by name (default) */
118 #define LS_TIME_SORT    0x0010  /* Sort by mtime */
119 #define LS_SIZE_SORT    0x0020  /* Sort by file size */
120 #define LS_REVERSE_SORT 0x0040  /* Reverse sort order */
121 #define LS_SHOW_ALL     0x0080  /* Don't skip filenames starting with '.' */
122 #define LS_SI_UNITS     0x0100  /* Display sizes as K, M, G, etc. */
123
124 #define VIEW_FLAGS      (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
125 #define SORT_FLAGS      (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
126
127 /* Commands for interactive mode */
128 #define I_CHDIR         1
129 #define I_CHGRP         2
130 #define I_CHMOD         3
131 #define I_CHOWN         4
132 #define I_DF            24
133 #define I_GET           5
134 #define I_HELP          6
135 #define I_LCHDIR        7
136 #define I_LINK          25
137 #define I_LLS           8
138 #define I_LMKDIR        9
139 #define I_LPWD          10
140 #define I_LS            11
141 #define I_LUMASK        12
142 #define I_MKDIR         13
143 #define I_PUT           14
144 #define I_PWD           15
145 #define I_QUIT          16
146 #define I_RENAME        17
147 #define I_RM            18
148 #define I_RMDIR         19
149 #define I_SHELL         20
150 #define I_SYMLINK       21
151 #define I_VERSION       22
152 #define I_PROGRESS      23
153
154 struct CMD {
155         const char *c;
156         const int n;
157         const int t;
158 };
159
160 /* Type of completion */
161 #define NOARGS  0
162 #define REMOTE  1
163 #define LOCAL   2
164
165 static const struct CMD cmds[] = {
166         { "bye",        I_QUIT,         NOARGS  },
167         { "cd",         I_CHDIR,        REMOTE  },
168         { "chdir",      I_CHDIR,        REMOTE  },
169         { "chgrp",      I_CHGRP,        REMOTE  },
170         { "chmod",      I_CHMOD,        REMOTE  },
171         { "chown",      I_CHOWN,        REMOTE  },
172         { "df",         I_DF,           REMOTE  },
173         { "dir",        I_LS,           REMOTE  },
174         { "exit",       I_QUIT,         NOARGS  },
175         { "get",        I_GET,          REMOTE  },
176         { "help",       I_HELP,         NOARGS  },
177         { "lcd",        I_LCHDIR,       LOCAL   },
178         { "lchdir",     I_LCHDIR,       LOCAL   },
179         { "lls",        I_LLS,          LOCAL   },
180         { "lmkdir",     I_LMKDIR,       LOCAL   },
181         { "ln",         I_LINK,         REMOTE  },
182         { "lpwd",       I_LPWD,         LOCAL   },
183         { "ls",         I_LS,           REMOTE  },
184         { "lumask",     I_LUMASK,       NOARGS  },
185         { "mkdir",      I_MKDIR,        REMOTE  },
186         { "mget",       I_GET,          REMOTE  },
187         { "mput",       I_PUT,          LOCAL   },
188         { "progress",   I_PROGRESS,     NOARGS  },
189         { "put",        I_PUT,          LOCAL   },
190         { "pwd",        I_PWD,          REMOTE  },
191         { "quit",       I_QUIT,         NOARGS  },
192         { "rename",     I_RENAME,       REMOTE  },
193         { "rm",         I_RM,           REMOTE  },
194         { "rmdir",      I_RMDIR,        REMOTE  },
195         { "symlink",    I_SYMLINK,      REMOTE  },
196         { "version",    I_VERSION,      NOARGS  },
197         { "!",          I_SHELL,        NOARGS  },
198         { "?",          I_HELP,         NOARGS  },
199         { NULL,         -1,             -1      }
200 };
201
202 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
203
204 /* ARGSUSED */
205 static void
206 killchild(int signo)
207 {
208         if (sshpid > 1) {
209                 kill(sshpid, SIGTERM);
210                 waitpid(sshpid, NULL, 0);
211         }
212
213         _exit(1);
214 }
215
216 /* ARGSUSED */
217 static void
218 cmd_interrupt(int signo)
219 {
220         const char msg[] = "\rInterrupt  \n";
221         int olderrno = errno;
222
223         write(STDERR_FILENO, msg, sizeof(msg) - 1);
224         interrupted = 1;
225         errno = olderrno;
226 }
227
228 static void
229 help(void)
230 {
231         printf("Available commands:\n"
232             "bye                                Quit sftp\n"
233             "cd path                            Change remote directory to 'path'\n"
234             "chgrp grp path                     Change group of file 'path' to 'grp'\n"
235             "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
236             "chown own path                     Change owner of file 'path' to 'own'\n"
237             "df [-hi] [path]                    Display statistics for current directory or\n"
238             "                                   filesystem containing 'path'\n"
239             "exit                               Quit sftp\n"
240             "get [-Ppr] remote [local]          Download file\n"
241             "help                               Display this help text\n"
242             "lcd path                           Change local directory to 'path'\n"
243             "lls [ls-options [path]]            Display local directory listing\n"
244             "lmkdir path                        Create local directory\n"
245             "ln [-s] oldpath newpath            Link remote file (-s for symlink)\n"
246             "lpwd                               Print local working directory\n"
247             "ls [-1afhlnrSt] [path]             Display remote directory listing\n"
248             "lumask umask                       Set local umask to 'umask'\n"
249             "mkdir path                         Create remote directory\n"
250             "progress                           Toggle display of progress meter\n"
251             "put [-Ppr] local [remote]          Upload file\n"
252             "pwd                                Display remote working directory\n"
253             "quit                               Quit sftp\n"
254             "rename oldpath newpath             Rename remote file\n"
255             "rm path                            Delete remote file\n"
256             "rmdir path                         Remove remote directory\n"
257             "symlink oldpath newpath            Symlink remote file\n"
258             "version                            Show SFTP version\n"
259             "!command                           Execute 'command' in local shell\n"
260             "!                                  Escape to local shell\n"
261             "?                                  Synonym for help\n");
262 }
263
264 static void
265 local_do_shell(const char *args)
266 {
267         int status;
268         char *shell;
269         pid_t pid;
270
271         if (!*args)
272                 args = NULL;
273
274         if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
275                 shell = _PATH_BSHELL;
276
277         if ((pid = fork()) == -1)
278                 fatal("Couldn't fork: %s", strerror(errno));
279
280         if (pid == 0) {
281                 /* XXX: child has pipe fds to ssh subproc open - issue? */
282                 if (args) {
283                         debug3("Executing %s -c \"%s\"", shell, args);
284                         execl(shell, shell, "-c", args, (char *)NULL);
285                 } else {
286                         debug3("Executing %s", shell);
287                         execl(shell, shell, (char *)NULL);
288                 }
289                 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
290                     strerror(errno));
291                 _exit(1);
292         }
293         while (waitpid(pid, &status, 0) == -1)
294                 if (errno != EINTR)
295                         fatal("Couldn't wait for child: %s", strerror(errno));
296         if (!WIFEXITED(status))
297                 error("Shell exited abnormally");
298         else if (WEXITSTATUS(status))
299                 error("Shell exited with status %d", WEXITSTATUS(status));
300 }
301
302 static void
303 local_do_ls(const char *args)
304 {
305         if (!args || !*args)
306                 local_do_shell(_PATH_LS);
307         else {
308                 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
309                 char *buf = xmalloc(len);
310
311                 /* XXX: quoting - rip quoting code from ftp? */
312                 snprintf(buf, len, _PATH_LS " %s", args);
313                 local_do_shell(buf);
314                 xfree(buf);
315         }
316 }
317
318 /* Strip one path (usually the pwd) from the start of another */
319 static char *
320 path_strip(char *path, char *strip)
321 {
322         size_t len;
323
324         if (strip == NULL)
325                 return (xstrdup(path));
326
327         len = strlen(strip);
328         if (strncmp(path, strip, len) == 0) {
329                 if (strip[len - 1] != '/' && path[len] == '/')
330                         len++;
331                 return (xstrdup(path + len));
332         }
333
334         return (xstrdup(path));
335 }
336
337 static char *
338 make_absolute(char *p, char *pwd)
339 {
340         char *abs_str;
341
342         /* Derelativise */
343         if (p && p[0] != '/') {
344                 abs_str = path_append(pwd, p);
345                 xfree(p);
346                 return(abs_str);
347         } else
348                 return(p);
349 }
350
351 static int
352 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
353     int *rflag)
354 {
355         extern int opterr, optind, optopt, optreset;
356         int ch;
357
358         optind = optreset = 1;
359         opterr = 0;
360
361         *rflag = *pflag = 0;
362         while ((ch = getopt(argc, argv, "PpRr")) != -1) {
363                 switch (ch) {
364                 case 'p':
365                 case 'P':
366                         *pflag = 1;
367                         break;
368                 case 'r':
369                 case 'R':
370                         *rflag = 1;
371                         break;
372                 default:
373                         error("%s: Invalid flag -%c", cmd, optopt);
374                         return -1;
375                 }
376         }
377
378         return optind;
379 }
380
381 static int
382 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
383 {
384         extern int opterr, optind, optopt, optreset;
385         int ch;
386
387         optind = optreset = 1;
388         opterr = 0;
389
390         *sflag = 0;
391         while ((ch = getopt(argc, argv, "s")) != -1) {
392                 switch (ch) {
393                 case 's':
394                         *sflag = 1;
395                         break;
396                 default:
397                         error("%s: Invalid flag -%c", cmd, optopt);
398                         return -1;
399                 }
400         }
401
402         return optind;
403 }
404
405 static int
406 parse_ls_flags(char **argv, int argc, int *lflag)
407 {
408         extern int opterr, optind, optopt, optreset;
409         int ch;
410
411         optind = optreset = 1;
412         opterr = 0;
413
414         *lflag = LS_NAME_SORT;
415         while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
416                 switch (ch) {
417                 case '1':
418                         *lflag &= ~VIEW_FLAGS;
419                         *lflag |= LS_SHORT_VIEW;
420                         break;
421                 case 'S':
422                         *lflag &= ~SORT_FLAGS;
423                         *lflag |= LS_SIZE_SORT;
424                         break;
425                 case 'a':
426                         *lflag |= LS_SHOW_ALL;
427                         break;
428                 case 'f':
429                         *lflag &= ~SORT_FLAGS;
430                         break;
431                 case 'h':
432                         *lflag |= LS_SI_UNITS;
433                         break;
434                 case 'l':
435                         *lflag &= ~LS_SHORT_VIEW;
436                         *lflag |= LS_LONG_VIEW;
437                         break;
438                 case 'n':
439                         *lflag &= ~LS_SHORT_VIEW;
440                         *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
441                         break;
442                 case 'r':
443                         *lflag |= LS_REVERSE_SORT;
444                         break;
445                 case 't':
446                         *lflag &= ~SORT_FLAGS;
447                         *lflag |= LS_TIME_SORT;
448                         break;
449                 default:
450                         error("ls: Invalid flag -%c", optopt);
451                         return -1;
452                 }
453         }
454
455         return optind;
456 }
457
458 static int
459 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
460 {
461         extern int opterr, optind, optopt, optreset;
462         int ch;
463
464         optind = optreset = 1;
465         opterr = 0;
466
467         *hflag = *iflag = 0;
468         while ((ch = getopt(argc, argv, "hi")) != -1) {
469                 switch (ch) {
470                 case 'h':
471                         *hflag = 1;
472                         break;
473                 case 'i':
474                         *iflag = 1;
475                         break;
476                 default:
477                         error("%s: Invalid flag -%c", cmd, optopt);
478                         return -1;
479                 }
480         }
481
482         return optind;
483 }
484
485 static int
486 is_dir(char *path)
487 {
488         struct stat sb;
489
490         /* XXX: report errors? */
491         if (stat(path, &sb) == -1)
492                 return(0);
493
494         return(S_ISDIR(sb.st_mode));
495 }
496
497 static int
498 remote_is_dir(struct sftp_conn *conn, char *path)
499 {
500         Attrib *a;
501
502         /* XXX: report errors? */
503         if ((a = do_stat(conn, path, 1)) == NULL)
504                 return(0);
505         if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
506                 return(0);
507         return(S_ISDIR(a->perm));
508 }
509
510 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
511 static int
512 pathname_is_dir(char *pathname)
513 {
514         size_t l = strlen(pathname);
515
516         return l > 0 && pathname[l - 1] == '/';
517 }
518
519 static int
520 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
521     int pflag, int rflag)
522 {
523         char *abs_src = NULL;
524         char *abs_dst = NULL;
525         glob_t g;
526         char *filename, *tmp=NULL;
527         int i, err = 0;
528
529         abs_src = xstrdup(src);
530         abs_src = make_absolute(abs_src, pwd);
531         memset(&g, 0, sizeof(g));
532
533         debug3("Looking up %s", abs_src);
534         if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
535                 error("File \"%s\" not found.", abs_src);
536                 err = -1;
537                 goto out;
538         }
539
540         /*
541          * If multiple matches then dst must be a directory or
542          * unspecified.
543          */
544         if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
545                 error("Multiple source paths, but destination "
546                     "\"%s\" is not a directory", dst);
547                 err = -1;
548                 goto out;
549         }
550
551         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
552                 tmp = xstrdup(g.gl_pathv[i]);
553                 if ((filename = basename(tmp)) == NULL) {
554                         error("basename %s: %s", tmp, strerror(errno));
555                         xfree(tmp);
556                         err = -1;
557                         goto out;
558                 }
559
560                 if (g.gl_matchc == 1 && dst) {
561                         if (is_dir(dst)) {
562                                 abs_dst = path_append(dst, filename);
563                         } else {
564                                 abs_dst = xstrdup(dst);
565                         }
566                 } else if (dst) {
567                         abs_dst = path_append(dst, filename);
568                 } else {
569                         abs_dst = xstrdup(filename);
570                 }
571                 xfree(tmp);
572
573                 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
574                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
575                         if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 
576                             pflag || global_pflag, 1) == -1)
577                                 err = -1;
578                 } else {
579                         if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
580                             pflag || global_pflag) == -1)
581                                 err = -1;
582                 }
583                 xfree(abs_dst);
584                 abs_dst = NULL;
585         }
586
587 out:
588         xfree(abs_src);
589         globfree(&g);
590         return(err);
591 }
592
593 static int
594 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
595     int pflag, int rflag)
596 {
597         char *tmp_dst = NULL;
598         char *abs_dst = NULL;
599         char *tmp = NULL, *filename = NULL;
600         glob_t g;
601         int err = 0;
602         int i, dst_is_dir = 1;
603         struct stat sb;
604
605         if (dst) {
606                 tmp_dst = xstrdup(dst);
607                 tmp_dst = make_absolute(tmp_dst, pwd);
608         }
609
610         memset(&g, 0, sizeof(g));
611         debug3("Looking up %s", src);
612         if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
613                 error("File \"%s\" not found.", src);
614                 err = -1;
615                 goto out;
616         }
617
618         /* If we aren't fetching to pwd then stash this status for later */
619         if (tmp_dst != NULL)
620                 dst_is_dir = remote_is_dir(conn, tmp_dst);
621
622         /* If multiple matches, dst may be directory or unspecified */
623         if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
624                 error("Multiple paths match, but destination "
625                     "\"%s\" is not a directory", tmp_dst);
626                 err = -1;
627                 goto out;
628         }
629
630         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
631                 if (stat(g.gl_pathv[i], &sb) == -1) {
632                         err = -1;
633                         error("stat %s: %s", g.gl_pathv[i], strerror(errno));
634                         continue;
635                 }
636                 
637                 tmp = xstrdup(g.gl_pathv[i]);
638                 if ((filename = basename(tmp)) == NULL) {
639                         error("basename %s: %s", tmp, strerror(errno));
640                         xfree(tmp);
641                         err = -1;
642                         goto out;
643                 }
644
645                 if (g.gl_matchc == 1 && tmp_dst) {
646                         /* If directory specified, append filename */
647                         if (dst_is_dir)
648                                 abs_dst = path_append(tmp_dst, filename);
649                         else
650                                 abs_dst = xstrdup(tmp_dst);
651                 } else if (tmp_dst) {
652                         abs_dst = path_append(tmp_dst, filename);
653                 } else {
654                         abs_dst = make_absolute(xstrdup(filename), pwd);
655                 }
656                 xfree(tmp);
657
658                 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
659                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
660                         if (upload_dir(conn, g.gl_pathv[i], abs_dst,
661                             pflag || global_pflag, 1) == -1)
662                                 err = -1;
663                 } else {
664                         if (do_upload(conn, g.gl_pathv[i], abs_dst,
665                             pflag || global_pflag) == -1)
666                                 err = -1;
667                 }
668         }
669
670 out:
671         if (abs_dst)
672                 xfree(abs_dst);
673         if (tmp_dst)
674                 xfree(tmp_dst);
675         globfree(&g);
676         return(err);
677 }
678
679 static int
680 sdirent_comp(const void *aa, const void *bb)
681 {
682         SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
683         SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
684         int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
685
686 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
687         if (sort_flag & LS_NAME_SORT)
688                 return (rmul * strcmp(a->filename, b->filename));
689         else if (sort_flag & LS_TIME_SORT)
690                 return (rmul * NCMP(a->a.mtime, b->a.mtime));
691         else if (sort_flag & LS_SIZE_SORT)
692                 return (rmul * NCMP(a->a.size, b->a.size));
693
694         fatal("Unknown ls sort type");
695 }
696
697 /* sftp ls.1 replacement for directories */
698 static int
699 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
700 {
701         int n;
702         u_int c = 1, colspace = 0, columns = 1;
703         SFTP_DIRENT **d;
704
705         if ((n = do_readdir(conn, path, &d)) != 0)
706                 return (n);
707
708         if (!(lflag & LS_SHORT_VIEW)) {
709                 u_int m = 0, width = 80;
710                 struct winsize ws;
711                 char *tmp;
712
713                 /* Count entries for sort and find longest filename */
714                 for (n = 0; d[n] != NULL; n++) {
715                         if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
716                                 m = MAX(m, strlen(d[n]->filename));
717                 }
718
719                 /* Add any subpath that also needs to be counted */
720                 tmp = path_strip(path, strip_path);
721                 m += strlen(tmp);
722                 xfree(tmp);
723
724                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
725                         width = ws.ws_col;
726
727                 columns = width / (m + 2);
728                 columns = MAX(columns, 1);
729                 colspace = width / columns;
730                 colspace = MIN(colspace, width);
731         }
732
733         if (lflag & SORT_FLAGS) {
734                 for (n = 0; d[n] != NULL; n++)
735                         ;       /* count entries */
736                 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
737                 qsort(d, n, sizeof(*d), sdirent_comp);
738         }
739
740         for (n = 0; d[n] != NULL && !interrupted; n++) {
741                 char *tmp, *fname;
742
743                 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
744                         continue;
745
746                 tmp = path_append(path, d[n]->filename);
747                 fname = path_strip(tmp, strip_path);
748                 xfree(tmp);
749
750                 if (lflag & LS_LONG_VIEW) {
751                         if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
752                                 char *lname;
753                                 struct stat sb;
754
755                                 memset(&sb, 0, sizeof(sb));
756                                 attrib_to_stat(&d[n]->a, &sb);
757                                 lname = ls_file(fname, &sb, 1,
758                                     (lflag & LS_SI_UNITS));
759                                 printf("%s\n", lname);
760                                 xfree(lname);
761                         } else
762                                 printf("%s\n", d[n]->longname);
763                 } else {
764                         printf("%-*s", colspace, fname);
765                         if (c >= columns) {
766                                 printf("\n");
767                                 c = 1;
768                         } else
769                                 c++;
770                 }
771
772                 xfree(fname);
773         }
774
775         if (!(lflag & LS_LONG_VIEW) && (c != 1))
776                 printf("\n");
777
778         free_sftp_dirents(d);
779         return (0);
780 }
781
782 /* sftp ls.1 replacement which handles path globs */
783 static int
784 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
785     int lflag)
786 {
787         char *fname, *lname;
788         glob_t g;
789         int err;
790         struct winsize ws;
791         u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
792
793         memset(&g, 0, sizeof(g));
794
795         if (remote_glob(conn, path,
796             GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
797             NULL, &g) ||
798             (g.gl_pathc && !g.gl_matchc)) {
799                 if (g.gl_pathc)
800                         globfree(&g);
801                 error("Can't ls: \"%s\" not found", path);
802                 return -1;
803         }
804
805         if (interrupted)
806                 goto out;
807
808         /*
809          * If the glob returns a single match and it is a directory,
810          * then just list its contents.
811          */
812         if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
813             S_ISDIR(g.gl_statv[0]->st_mode)) {
814                 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
815                 globfree(&g);
816                 return err;
817         }
818
819         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
820                 width = ws.ws_col;
821
822         if (!(lflag & LS_SHORT_VIEW)) {
823                 /* Count entries for sort and find longest filename */
824                 for (i = 0; g.gl_pathv[i]; i++)
825                         m = MAX(m, strlen(g.gl_pathv[i]));
826
827                 columns = width / (m + 2);
828                 columns = MAX(columns, 1);
829                 colspace = width / columns;
830         }
831
832         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
833                 fname = path_strip(g.gl_pathv[i], strip_path);
834                 if (lflag & LS_LONG_VIEW) {
835                         if (g.gl_statv[i] == NULL) {
836                                 error("no stat information for %s", fname);
837                                 continue;
838                         }
839                         lname = ls_file(fname, g.gl_statv[i], 1,
840                             (lflag & LS_SI_UNITS));
841                         printf("%s\n", lname);
842                         xfree(lname);
843                 } else {
844                         printf("%-*s", colspace, fname);
845                         if (c >= columns) {
846                                 printf("\n");
847                                 c = 1;
848                         } else
849                                 c++;
850                 }
851                 xfree(fname);
852         }
853
854         if (!(lflag & LS_LONG_VIEW) && (c != 1))
855                 printf("\n");
856
857  out:
858         if (g.gl_pathc)
859                 globfree(&g);
860
861         return 0;
862 }
863
864 static int
865 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
866 {
867         struct sftp_statvfs st;
868         char s_used[FMT_SCALED_STRSIZE];
869         char s_avail[FMT_SCALED_STRSIZE];
870         char s_root[FMT_SCALED_STRSIZE];
871         char s_total[FMT_SCALED_STRSIZE];
872         unsigned long long ffree;
873
874         if (do_statvfs(conn, path, &st, 1) == -1)
875                 return -1;
876         if (iflag) {
877                 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
878                 printf("     Inodes        Used       Avail      "
879                     "(root)    %%Capacity\n");
880                 printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
881                     (unsigned long long)st.f_files,
882                     (unsigned long long)(st.f_files - st.f_ffree),
883                     (unsigned long long)st.f_favail,
884                     (unsigned long long)st.f_ffree, ffree);
885         } else if (hflag) {
886                 strlcpy(s_used, "error", sizeof(s_used));
887                 strlcpy(s_avail, "error", sizeof(s_avail));
888                 strlcpy(s_root, "error", sizeof(s_root));
889                 strlcpy(s_total, "error", sizeof(s_total));
890                 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
891                 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
892                 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
893                 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
894                 printf("    Size     Used    Avail   (root)    %%Capacity\n");
895                 printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
896                     s_total, s_used, s_avail, s_root,
897                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
898                     st.f_blocks));
899         } else {
900                 printf("        Size         Used        Avail       "
901                     "(root)    %%Capacity\n");
902                 printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
903                     (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
904                     (unsigned long long)(st.f_frsize *
905                     (st.f_blocks - st.f_bfree) / 1024),
906                     (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
907                     (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
908                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
909                     st.f_blocks));
910         }
911         return 0;
912 }
913
914 /*
915  * Undo escaping of glob sequences in place. Used to undo extra escaping
916  * applied in makeargv() when the string is destined for a function that
917  * does not glob it.
918  */
919 static void
920 undo_glob_escape(char *s)
921 {
922         size_t i, j;
923
924         for (i = j = 0;;) {
925                 if (s[i] == '\0') {
926                         s[j] = '\0';
927                         return;
928                 }
929                 if (s[i] != '\\') {
930                         s[j++] = s[i++];
931                         continue;
932                 }
933                 /* s[i] == '\\' */
934                 ++i;
935                 switch (s[i]) {
936                 case '?':
937                 case '[':
938                 case '*':
939                 case '\\':
940                         s[j++] = s[i++];
941                         break;
942                 case '\0':
943                         s[j++] = '\\';
944                         s[j] = '\0';
945                         return;
946                 default:
947                         s[j++] = '\\';
948                         s[j++] = s[i++];
949                         break;
950                 }
951         }
952 }
953
954 /*
955  * Split a string into an argument vector using sh(1)-style quoting,
956  * comment and escaping rules, but with some tweaks to handle glob(3)
957  * wildcards.
958  * The "sloppy" flag allows for recovery from missing terminating quote, for
959  * use in parsing incomplete commandlines during tab autocompletion.
960  *
961  * Returns NULL on error or a NULL-terminated array of arguments.
962  *
963  * If "lastquote" is not NULL, the quoting character used for the last
964  * argument is placed in *lastquote ("\0", "'" or "\"").
965  * 
966  * If "terminated" is not NULL, *terminated will be set to 1 when the
967  * last argument's quote has been properly terminated or 0 otherwise.
968  * This parameter is only of use if "sloppy" is set.
969  */
970 #define MAXARGS         128
971 #define MAXARGLEN       8192
972 static char **
973 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
974     u_int *terminated)
975 {
976         int argc, quot;
977         size_t i, j;
978         static char argvs[MAXARGLEN];
979         static char *argv[MAXARGS + 1];
980         enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
981
982         *argcp = argc = 0;
983         if (strlen(arg) > sizeof(argvs) - 1) {
984  args_too_longs:
985                 error("string too long");
986                 return NULL;
987         }
988         if (terminated != NULL)
989                 *terminated = 1;
990         if (lastquote != NULL)
991                 *lastquote = '\0';
992         state = MA_START;
993         i = j = 0;
994         for (;;) {
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 (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;
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         memset(&g, 0, sizeof(g));
1709         if (remote != LOCAL) {
1710                 tmp = make_absolute(tmp, remote_path);
1711                 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1712         } else 
1713                 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1714         
1715         /* Determine length of pwd so we can trim completion display */
1716         for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1717                 /* Terminate counting on first unescaped glob metacharacter */
1718                 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1719                         if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1720                                 hadglob = 1;
1721                         break;
1722                 }
1723                 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1724                         tmplen++;
1725                 if (tmp[tmplen] == '/')
1726                         pwdlen = tmplen + 1;    /* track last seen '/' */
1727         }
1728         xfree(tmp);
1729
1730         if (g.gl_matchc == 0) 
1731                 goto out;
1732
1733         if (g.gl_matchc > 1)
1734                 complete_display(g.gl_pathv, pwdlen);
1735
1736         tmp = NULL;
1737         /* Don't try to extend globs */
1738         if (file == NULL || hadglob)
1739                 goto out;
1740
1741         tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1742         tmp = path_strip(tmp2, remote_path);
1743         xfree(tmp2);
1744
1745         if (tmp == NULL)
1746                 goto out;
1747
1748         tmplen = strlen(tmp);
1749         filelen = strlen(file);
1750
1751         if (tmplen > filelen)  {
1752                 tmp2 = tmp + filelen;
1753                 len = strlen(tmp2); 
1754                 /* quote argument on way out */
1755                 for (i = 0; i < len; i++) {
1756                         ins[0] = '\\';
1757                         ins[1] = tmp2[i];
1758                         ins[2] = '\0';
1759                         switch (tmp2[i]) {
1760                         case '\'':
1761                         case '"':
1762                         case '\\':
1763                         case '\t':
1764                         case '[':
1765                         case ' ':
1766                                 if (quote == '\0' || tmp2[i] == quote) {
1767                                         if (el_insertstr(el, ins) == -1)
1768                                                 fatal("el_insertstr "
1769                                                     "failed.");
1770                                         break;
1771                                 }
1772                                 /* FALLTHROUGH */
1773                         default:
1774                                 if (el_insertstr(el, ins + 1) == -1)
1775                                         fatal("el_insertstr failed.");
1776                                 break;
1777                         }
1778                 }
1779         }
1780
1781         lf = el_line(el);
1782         if (g.gl_matchc == 1) {
1783                 i = 0;
1784                 if (!terminated)
1785                         ins[i++] = quote;
1786                 if (*(lf->cursor - 1) != '/' &&
1787                     (lastarg || *(lf->cursor) != ' '))
1788                         ins[i++] = ' ';
1789                 ins[i] = '\0';
1790                 if (i > 0 && el_insertstr(el, ins) == -1)
1791                         fatal("el_insertstr failed.");
1792         }
1793         xfree(tmp);
1794
1795  out:
1796         globfree(&g);
1797         return g.gl_matchc;
1798 }
1799
1800 /* tab-completion hook function, called via libedit */
1801 static unsigned char
1802 complete(EditLine *el, int ch)
1803 {
1804         char **argv, *line, quote; 
1805         u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1806         const LineInfo *lf;
1807         struct complete_ctx *complete_ctx;
1808
1809         lf = el_line(el);
1810         if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1811                 fatal("%s: el_get failed", __func__);
1812
1813         /* Figure out which argument the cursor points to */
1814         cursor = lf->cursor - lf->buffer;
1815         line = (char *)xmalloc(cursor + 1);
1816         memcpy(line, lf->buffer, cursor);
1817         line[cursor] = '\0';
1818         argv = makeargv(line, &carg, 1, &quote, &terminated);
1819         xfree(line);
1820
1821         /* Get all the arguments on the line */
1822         len = lf->lastchar - lf->buffer;
1823         line = (char *)xmalloc(len + 1);
1824         memcpy(line, lf->buffer, len);
1825         line[len] = '\0';
1826         argv = makeargv(line, &argc, 1, NULL, NULL);
1827
1828         /* Ensure cursor is at EOL or a argument boundary */
1829         if (line[cursor] != ' ' && line[cursor] != '\0' &&
1830             line[cursor] != '\n') {
1831                 xfree(line);
1832                 return ret;
1833         }
1834
1835         if (carg == 0) {
1836                 /* Show all available commands */
1837                 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1838                 ret = CC_REDISPLAY;
1839         } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
1840                 /* Handle the command parsing */
1841                 if (complete_cmd_parse(el, argv[0], argc == carg,
1842                     quote, terminated) != 0) 
1843                         ret = CC_REDISPLAY;
1844         } else if (carg >= 1) {
1845                 /* Handle file parsing */
1846                 int remote = complete_is_remote(argv[0]);
1847                 char *filematch = NULL;
1848
1849                 if (carg > 1 && line[cursor-1] != ' ')
1850                         filematch = argv[carg - 1];
1851
1852                 if (remote != 0 &&
1853                     complete_match(el, complete_ctx->conn,
1854                     *complete_ctx->remote_pathp, filematch,
1855                     remote, carg == argc, quote, terminated) != 0) 
1856                         ret = CC_REDISPLAY;
1857         }
1858
1859         xfree(line);    
1860         return ret;
1861 }
1862 #endif /* USE_LIBEDIT */
1863
1864 int
1865 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1866 {
1867         char *remote_path;
1868         char *dir = NULL;
1869         char cmd[2048];
1870         int err, interactive;
1871         EditLine *el = NULL;
1872 #ifdef USE_LIBEDIT
1873         History *hl = NULL;
1874         HistEvent hev;
1875         extern char *__progname;
1876         struct complete_ctx complete_ctx;
1877
1878         if (!batchmode && isatty(STDIN_FILENO)) {
1879                 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1880                         fatal("Couldn't initialise editline");
1881                 if ((hl = history_init()) == NULL)
1882                         fatal("Couldn't initialise editline history");
1883                 history(hl, &hev, H_SETSIZE, 100);
1884                 el_set(el, EL_HIST, history, hl);
1885
1886                 el_set(el, EL_PROMPT, prompt);
1887                 el_set(el, EL_EDITOR, "emacs");
1888                 el_set(el, EL_TERMINAL, NULL);
1889                 el_set(el, EL_SIGNAL, 1);
1890                 el_source(el, NULL);
1891
1892                 /* Tab Completion */
1893                 el_set(el, EL_ADDFN, "ftp-complete", 
1894                     "Context sensitive argument completion", complete);
1895                 complete_ctx.conn = conn;
1896                 complete_ctx.remote_pathp = &remote_path;
1897                 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1898                 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1899         }
1900 #endif /* USE_LIBEDIT */
1901
1902         remote_path = do_realpath(conn, ".");
1903         if (remote_path == NULL)
1904                 fatal("Need cwd");
1905
1906         if (file1 != NULL) {
1907                 dir = xstrdup(file1);
1908                 dir = make_absolute(dir, remote_path);
1909
1910                 if (remote_is_dir(conn, dir) && file2 == NULL) {
1911                         printf("Changing to: %s\n", dir);
1912                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1913                         if (parse_dispatch_command(conn, cmd,
1914                             &remote_path, 1) != 0) {
1915                                 xfree(dir);
1916                                 xfree(remote_path);
1917                                 xfree(conn);
1918                                 return (-1);
1919                         }
1920                 } else {
1921                         if (file2 == NULL)
1922                                 snprintf(cmd, sizeof cmd, "get %s", dir);
1923                         else
1924                                 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1925                                     file2);
1926
1927                         err = parse_dispatch_command(conn, cmd,
1928                             &remote_path, 1);
1929                         xfree(dir);
1930                         xfree(remote_path);
1931                         xfree(conn);
1932                         return (err);
1933                 }
1934                 xfree(dir);
1935         }
1936
1937         setlinebuf(stdout);
1938         setlinebuf(infile);
1939
1940         interactive = !batchmode && isatty(STDIN_FILENO);
1941         err = 0;
1942         for (;;) {
1943                 char *cp;
1944
1945                 signal(SIGINT, SIG_IGN);
1946
1947                 if (el == NULL) {
1948                         if (interactive)
1949                                 printf("sftp> ");
1950                         if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1951                                 if (interactive)
1952                                         printf("\n");
1953                                 break;
1954                         }
1955                         if (!interactive) { /* Echo command */
1956                                 printf("sftp> %s", cmd);
1957                                 if (strlen(cmd) > 0 &&
1958                                     cmd[strlen(cmd) - 1] != '\n')
1959                                         printf("\n");
1960                         }
1961                 } else {
1962 #ifdef USE_LIBEDIT
1963                         const char *line;
1964                         int count = 0;
1965
1966                         if ((line = el_gets(el, &count)) == NULL ||
1967                             count <= 0) {
1968                                 printf("\n");
1969                                 break;
1970                         }
1971                         history(hl, &hev, H_ENTER, line);
1972                         if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1973                                 fprintf(stderr, "Error: input line too long\n");
1974                                 continue;
1975                         }
1976 #endif /* USE_LIBEDIT */
1977                 }
1978
1979                 cp = strrchr(cmd, '\n');
1980                 if (cp)
1981                         *cp = '\0';
1982
1983                 /* Handle user interrupts gracefully during commands */
1984                 interrupted = 0;
1985                 signal(SIGINT, cmd_interrupt);
1986
1987                 err = parse_dispatch_command(conn, cmd, &remote_path,
1988                     batchmode);
1989                 if (err != 0)
1990                         break;
1991         }
1992         xfree(remote_path);
1993         xfree(conn);
1994
1995 #ifdef USE_LIBEDIT
1996         if (el != NULL)
1997                 el_end(el);
1998 #endif /* USE_LIBEDIT */
1999
2000         /* err == 1 signifies normal "quit" exit */
2001         return (err >= 0 ? 0 : -1);
2002 }
2003
2004 static void
2005 connect_to_server(char *path, char **args, int *in, int *out)
2006 {
2007         int c_in, c_out;
2008
2009 #ifdef USE_PIPES
2010         int pin[2], pout[2];
2011
2012         if ((pipe(pin) == -1) || (pipe(pout) == -1))
2013                 fatal("pipe: %s", strerror(errno));
2014         *in = pin[0];
2015         *out = pout[1];
2016         c_in = pout[0];
2017         c_out = pin[1];
2018 #else /* USE_PIPES */
2019         int inout[2];
2020
2021         if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2022                 fatal("socketpair: %s", strerror(errno));
2023         *in = *out = inout[0];
2024         c_in = c_out = inout[1];
2025 #endif /* USE_PIPES */
2026
2027         if ((sshpid = fork()) == -1)
2028                 fatal("fork: %s", strerror(errno));
2029         else if (sshpid == 0) {
2030                 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2031                     (dup2(c_out, STDOUT_FILENO) == -1)) {
2032                         fprintf(stderr, "dup2: %s\n", strerror(errno));
2033                         _exit(1);
2034                 }
2035                 close(*in);
2036                 close(*out);
2037                 close(c_in);
2038                 close(c_out);
2039
2040                 /*
2041                  * The underlying ssh is in the same process group, so we must
2042                  * ignore SIGINT if we want to gracefully abort commands,
2043                  * otherwise the signal will make it to the ssh process and
2044                  * kill it too.  Contrawise, since sftp sends SIGTERMs to the
2045                  * underlying ssh, it must *not* ignore that signal.
2046                  */
2047                 signal(SIGINT, SIG_IGN);
2048                 signal(SIGTERM, SIG_DFL);
2049                 execvp(path, args);
2050                 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2051                 _exit(1);
2052         }
2053
2054         signal(SIGTERM, killchild);
2055         signal(SIGINT, killchild);
2056         signal(SIGHUP, killchild);
2057         close(c_in);
2058         close(c_out);
2059 }
2060
2061 static void
2062 usage(void)
2063 {
2064         extern char *__progname;
2065
2066         fprintf(stderr,
2067             "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2068             "          [-D sftp_server_path] [-F ssh_config] "
2069             "[-i identity_file] [-l limit]\n"
2070             "          [-o ssh_option] [-P port] [-R num_requests] "
2071             "[-S program]\n"
2072             "          [-s subsystem | sftp_server] host\n"
2073             "       %s [user@]host[:file ...]\n"
2074             "       %s [user@]host[:dir[/]]\n"
2075             "       %s -b batchfile [user@]host\n",
2076             __progname, __progname, __progname, __progname);
2077         exit(1);
2078 }
2079
2080 int
2081 main(int argc, char **argv)
2082 {
2083         int in, out, ch, err;
2084         char *host = NULL, *userhost, *cp, *file2 = NULL;
2085         int debug_level = 0, sshver = 2;
2086         char *file1 = NULL, *sftp_server = NULL;
2087         char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2088         const char *errstr;
2089         LogLevel ll = SYSLOG_LEVEL_INFO;
2090         arglist args;
2091         extern int optind;
2092         extern char *optarg;
2093         struct sftp_conn *conn;
2094         size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2095         size_t num_requests = DEFAULT_NUM_REQUESTS;
2096         long long limit_kbps = 0;
2097
2098         /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2099         sanitise_stdfd();
2100
2101         __progname = ssh_get_progname(argv[0]);
2102         memset(&args, '\0', sizeof(args));
2103         args.list = NULL;
2104         addargs(&args, "%s", ssh_program);
2105         addargs(&args, "-oForwardX11 no");
2106         addargs(&args, "-oForwardAgent no");
2107         addargs(&args, "-oPermitLocalCommand no");
2108         addargs(&args, "-oClearAllForwardings yes");
2109
2110         ll = SYSLOG_LEVEL_INFO;
2111         infile = stdin;
2112
2113         while ((ch = getopt(argc, argv,
2114             "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2115                 switch (ch) {
2116                 /* Passed through to ssh(1) */
2117                 case '4':
2118                 case '6':
2119                 case 'C':
2120                         addargs(&args, "-%c", ch);
2121                         break;
2122                 /* Passed through to ssh(1) with argument */
2123                 case 'F':
2124                 case 'c':
2125                 case 'i':
2126                 case 'o':
2127                         addargs(&args, "-%c", ch);
2128                         addargs(&args, "%s", optarg);
2129                         break;
2130                 case 'q':
2131                         showprogress = 0;
2132                         addargs(&args, "-%c", ch);
2133                         break;
2134                 case 'P':
2135                         addargs(&args, "-oPort %s", optarg);
2136                         break;
2137                 case 'v':
2138                         if (debug_level < 3) {
2139                                 addargs(&args, "-v");
2140                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2141                         }
2142                         debug_level++;
2143                         break;
2144                 case '1':
2145                         sshver = 1;
2146                         if (sftp_server == NULL)
2147                                 sftp_server = _PATH_SFTP_SERVER;
2148                         break;
2149                 case '2':
2150                         sshver = 2;
2151                         break;
2152                 case 'B':
2153                         copy_buffer_len = strtol(optarg, &cp, 10);
2154                         if (copy_buffer_len == 0 || *cp != '\0')
2155                                 fatal("Invalid buffer size \"%s\"", optarg);
2156                         break;
2157                 case 'b':
2158                         if (batchmode)
2159                                 fatal("Batch file already specified.");
2160
2161                         /* Allow "-" as stdin */
2162                         if (strcmp(optarg, "-") != 0 &&
2163                             (infile = fopen(optarg, "r")) == NULL)
2164                                 fatal("%s (%s).", strerror(errno), optarg);
2165                         showprogress = 0;
2166                         batchmode = 1;
2167                         addargs(&args, "-obatchmode yes");
2168                         break;
2169                 case 'p':
2170                         global_pflag = 1;
2171                         break;
2172                 case 'D':
2173                         sftp_direct = optarg;
2174                         break;
2175                 case 'l':
2176                         limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2177                             &errstr);
2178                         if (errstr != NULL)
2179                                 usage();
2180                         limit_kbps *= 1024; /* kbps */
2181                         break;
2182                 case 'r':
2183                         global_rflag = 1;
2184                         break;
2185                 case 'R':
2186                         num_requests = strtol(optarg, &cp, 10);
2187                         if (num_requests == 0 || *cp != '\0')
2188                                 fatal("Invalid number of requests \"%s\"",
2189                                     optarg);
2190                         break;
2191                 case 's':
2192                         sftp_server = optarg;
2193                         break;
2194                 case 'S':
2195                         ssh_program = optarg;
2196                         replacearg(&args, 0, "%s", ssh_program);
2197                         break;
2198                 case 'h':
2199                 default:
2200                         usage();
2201                 }
2202         }
2203
2204         if (!isatty(STDERR_FILENO))
2205                 showprogress = 0;
2206
2207         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2208
2209         if (sftp_direct == NULL) {
2210                 if (optind == argc || argc > (optind + 2))
2211                         usage();
2212
2213                 userhost = xstrdup(argv[optind]);
2214                 file2 = argv[optind+1];
2215
2216                 if ((host = strrchr(userhost, '@')) == NULL)
2217                         host = userhost;
2218                 else {
2219                         *host++ = '\0';
2220                         if (!userhost[0]) {
2221                                 fprintf(stderr, "Missing username\n");
2222                                 usage();
2223                         }
2224                         addargs(&args, "-l");
2225                         addargs(&args, "%s", userhost);
2226                 }
2227
2228                 if ((cp = colon(host)) != NULL) {
2229                         *cp++ = '\0';
2230                         file1 = cp;
2231                 }
2232
2233                 host = cleanhostname(host);
2234                 if (!*host) {
2235                         fprintf(stderr, "Missing hostname\n");
2236                         usage();
2237                 }
2238
2239                 addargs(&args, "-oProtocol %d", sshver);
2240
2241                 /* no subsystem if the server-spec contains a '/' */
2242                 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2243                         addargs(&args, "-s");
2244
2245                 addargs(&args, "--");
2246                 addargs(&args, "%s", host);
2247                 addargs(&args, "%s", (sftp_server != NULL ?
2248                     sftp_server : "sftp"));
2249
2250                 connect_to_server(ssh_program, args.list, &in, &out);
2251         } else {
2252                 args.list = NULL;
2253                 addargs(&args, "sftp-server");
2254
2255                 connect_to_server(sftp_direct, args.list, &in, &out);
2256         }
2257         freeargs(&args);
2258
2259         conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2260         if (conn == NULL)
2261                 fatal("Couldn't initialise connection to server");
2262
2263         if (!batchmode) {
2264                 if (sftp_direct == NULL)
2265                         fprintf(stderr, "Connected to %s.\n", host);
2266                 else
2267                         fprintf(stderr, "Attached to %s.\n", sftp_direct);
2268         }
2269
2270         err = interactive_loop(conn, file1, file2);
2271
2272 #if !defined(USE_PIPES)
2273         shutdown(in, SHUT_RDWR);
2274         shutdown(out, SHUT_RDWR);
2275 #endif
2276
2277         close(in);
2278         close(out);
2279         if (batchmode)
2280                 fclose(infile);
2281
2282         while (waitpid(sshpid, NULL, 0) == -1)
2283                 if (errno != EINTR)
2284                         fatal("Couldn't wait for ssh process: %s",
2285                             strerror(errno));
2286
2287         exit(err == 0 ? 0 : 1);
2288 }