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