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