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