Raise WARNS in secure/ as high as possible and fix some resulting warnings.
[dragonfly.git] / crypto / openssh / sftp.c
1 /* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker 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 USE_LIBEDIT
39 #include <histedit.h>
40 #else
41 typedef void EditLine;
42 #endif
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdarg.h>
49
50 #ifdef HAVE_UTIL_H
51 # include <util.h>
52 #endif
53
54 #ifdef HAVE_LIBUTIL_H
55 # include <libutil.h>
56 #endif
57
58 #include "xmalloc.h"
59 #include "log.h"
60 #include "pathnames.h"
61 #include "misc.h"
62
63 #include "sftp.h"
64 #include "buffer.h"
65 #include "sftp-common.h"
66 #include "sftp-client.h"
67
68 /* File to read commands from */
69 FILE* infile;
70
71 /* Are we in batchfile mode? */
72 int batchmode = 0;
73
74 /* Size of buffer used when copying files */
75 size_t copy_buffer_len = 32768;
76
77 /* Number of concurrent outstanding requests */
78 size_t num_requests = 256;
79
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
82
83 /* This is set to 0 if the progressmeter is not desired. */
84 int showprogress = 1;
85
86 /* SIGINT received during command processing */
87 volatile sig_atomic_t interrupted = 0;
88
89 /* I wish qsort() took a separate ctx for the comparison function...*/
90 int sort_flag;
91
92 int remote_glob(struct sftp_conn *, const char *, int,
93     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
94
95 extern char *__progname;
96
97 /* Separators for interactive commands */
98 #define WHITESPACE " \t\r\n"
99
100 /* ls flags */
101 #define LS_LONG_VIEW    0x01    /* Full view ala ls -l */
102 #define LS_SHORT_VIEW   0x02    /* Single row view ala ls -1 */
103 #define LS_NUMERIC_VIEW 0x04    /* Long view with numeric uid/gid */
104 #define LS_NAME_SORT    0x08    /* Sort by name (default) */
105 #define LS_TIME_SORT    0x10    /* Sort by mtime */
106 #define LS_SIZE_SORT    0x20    /* Sort by file size */
107 #define LS_REVERSE_SORT 0x40    /* Reverse sort order */
108 #define LS_SHOW_ALL     0x80    /* Don't skip filenames starting with '.' */
109
110 #define VIEW_FLAGS      (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
111 #define SORT_FLAGS      (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
112
113 /* Commands for interactive mode */
114 #define I_CHDIR         1
115 #define I_CHGRP         2
116 #define I_CHMOD         3
117 #define I_CHOWN         4
118 #define I_DF            24
119 #define I_GET           5
120 #define I_HELP          6
121 #define I_LCHDIR        7
122 #define I_LLS           8
123 #define I_LMKDIR        9
124 #define I_LPWD          10
125 #define I_LS            11
126 #define I_LUMASK        12
127 #define I_MKDIR         13
128 #define I_PUT           14
129 #define I_PWD           15
130 #define I_QUIT          16
131 #define I_RENAME        17
132 #define I_RM            18
133 #define I_RMDIR         19
134 #define I_SHELL         20
135 #define I_SYMLINK       21
136 #define I_VERSION       22
137 #define I_PROGRESS      23
138
139 struct CMD {
140         const char *c;
141         const int n;
142 };
143
144 static const struct CMD cmds[] = {
145         { "bye",        I_QUIT },
146         { "cd",         I_CHDIR },
147         { "chdir",      I_CHDIR },
148         { "chgrp",      I_CHGRP },
149         { "chmod",      I_CHMOD },
150         { "chown",      I_CHOWN },
151         { "df",         I_DF },
152         { "dir",        I_LS },
153         { "exit",       I_QUIT },
154         { "get",        I_GET },
155         { "mget",       I_GET },
156         { "help",       I_HELP },
157         { "lcd",        I_LCHDIR },
158         { "lchdir",     I_LCHDIR },
159         { "lls",        I_LLS },
160         { "lmkdir",     I_LMKDIR },
161         { "ln",         I_SYMLINK },
162         { "lpwd",       I_LPWD },
163         { "ls",         I_LS },
164         { "lumask",     I_LUMASK },
165         { "mkdir",      I_MKDIR },
166         { "progress",   I_PROGRESS },
167         { "put",        I_PUT },
168         { "mput",       I_PUT },
169         { "pwd",        I_PWD },
170         { "quit",       I_QUIT },
171         { "rename",     I_RENAME },
172         { "rm",         I_RM },
173         { "rmdir",      I_RMDIR },
174         { "symlink",    I_SYMLINK },
175         { "version",    I_VERSION },
176         { "!",          I_SHELL },
177         { "?",          I_HELP },
178         { NULL,                 -1}
179 };
180
181 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
182
183 /* ARGSUSED */
184 static void
185 killchild(int signo)
186 {
187         if (sshpid > 1) {
188                 kill(sshpid, SIGTERM);
189                 waitpid(sshpid, NULL, 0);
190         }
191
192         _exit(1);
193 }
194
195 /* ARGSUSED */
196 static void
197 cmd_interrupt(int signo)
198 {
199         const char msg[] = "\rInterrupt  \n";
200         int olderrno = errno;
201
202         write(STDERR_FILENO, msg, sizeof(msg) - 1);
203         interrupted = 1;
204         errno = olderrno;
205 }
206
207 static void
208 help(void)
209 {
210         printf("Available commands:\n"
211             "bye                                Quit sftp\n"
212             "cd path                            Change remote directory to 'path'\n"
213             "chgrp grp path                     Change group of file 'path' to 'grp'\n"
214             "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
215             "chown own path                     Change owner of file 'path' to 'own'\n"
216             "df [-hi] [path]                    Display statistics for current directory or\n"
217             "                                   filesystem containing 'path'\n"
218             "exit                               Quit sftp\n"
219             "get [-P] remote-path [local-path]  Download file\n"
220             "help                               Display this help text\n"
221             "lcd path                           Change local directory to 'path'\n"
222             "lls [ls-options [path]]            Display local directory listing\n"
223             "lmkdir path                        Create local directory\n"
224             "ln oldpath newpath                 Symlink remote file\n"
225             "lpwd                               Print local working directory\n"
226             "ls [-1aflnrSt] [path]              Display remote directory listing\n"
227             "lumask umask                       Set local umask to 'umask'\n"
228             "mkdir path                         Create remote directory\n"
229             "progress                           Toggle display of progress meter\n"
230             "put [-P] local-path [remote-path]  Upload file\n"
231             "pwd                                Display remote working directory\n"
232             "quit                               Quit sftp\n"
233             "rename oldpath newpath             Rename remote file\n"
234             "rm path                            Delete remote file\n"
235             "rmdir path                         Remove remote directory\n"
236             "symlink oldpath newpath            Symlink remote file\n"
237             "version                            Show SFTP version\n"
238             "!command                           Execute 'command' in local shell\n"
239             "!                                  Escape to local shell\n"
240             "?                                  Synonym for help\n");
241 }
242
243 static void
244 local_do_shell(const char *args)
245 {
246         int status;
247         char *shell;
248         pid_t pid;
249
250         if (!*args)
251                 args = NULL;
252
253         if ((shell = getenv("SHELL")) == NULL)
254                 shell = _PATH_BSHELL;
255
256         if ((pid = fork()) == -1)
257                 fatal("Couldn't fork: %s", strerror(errno));
258
259         if (pid == 0) {
260                 /* XXX: child has pipe fds to ssh subproc open - issue? */
261                 if (args) {
262                         debug3("Executing %s -c \"%s\"", shell, args);
263                         execl(shell, shell, "-c", args, (char *)NULL);
264                 } else {
265                         debug3("Executing %s", shell);
266                         execl(shell, shell, (char *)NULL);
267                 }
268                 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
269                     strerror(errno));
270                 _exit(1);
271         }
272         while (waitpid(pid, &status, 0) == -1)
273                 if (errno != EINTR)
274                         fatal("Couldn't wait for child: %s", strerror(errno));
275         if (!WIFEXITED(status))
276                 error("Shell exited abnormally");
277         else if (WEXITSTATUS(status))
278                 error("Shell exited with status %d", WEXITSTATUS(status));
279 }
280
281 static void
282 local_do_ls(const char *args)
283 {
284         if (!args || !*args)
285                 local_do_shell(_PATH_LS);
286         else {
287                 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
288                 char *buf = xmalloc(len);
289
290                 /* XXX: quoting - rip quoting code from ftp? */
291                 snprintf(buf, len, _PATH_LS " %s", args);
292                 local_do_shell(buf);
293                 xfree(buf);
294         }
295 }
296
297 /* Strip one path (usually the pwd) from the start of another */
298 static char *
299 path_strip(char *path, char *strip)
300 {
301         size_t len;
302
303         if (strip == NULL)
304                 return (xstrdup(path));
305
306         len = strlen(strip);
307         if (strncmp(path, strip, len) == 0) {
308                 if (strip[len - 1] != '/' && path[len] == '/')
309                         len++;
310                 return (xstrdup(path + len));
311         }
312
313         return (xstrdup(path));
314 }
315
316 static char *
317 path_append(char *p1, char *p2)
318 {
319         char *ret;
320         size_t len = strlen(p1) + strlen(p2) + 2;
321
322         ret = xmalloc(len);
323         strlcpy(ret, p1, len);
324         if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
325                 strlcat(ret, "/", len);
326         strlcat(ret, p2, len);
327
328         return(ret);
329 }
330
331 static char *
332 make_absolute(char *p, char *pwd)
333 {
334         char *abs_str;
335
336         /* Derelativise */
337         if (p && p[0] != '/') {
338                 abs_str = path_append(pwd, p);
339                 xfree(p);
340                 return(abs_str);
341         } else
342                 return(p);
343 }
344
345 static int
346 infer_path(const char *p, char **ifp)
347 {
348         char *cp;
349
350         cp = strrchr(p, '/');
351         if (cp == NULL) {
352                 *ifp = xstrdup(p);
353                 return(0);
354         }
355
356         if (!cp[1]) {
357                 error("Invalid path");
358                 return(-1);
359         }
360
361         *ifp = xstrdup(cp + 1);
362         return(0);
363 }
364
365 static int
366 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
367 {
368         extern int opterr, optind, optopt, optreset;
369         int ch;
370
371         optind = optreset = 1;
372         opterr = 0;
373
374         *pflag = 0;
375         while ((ch = getopt(argc, argv, "Pp")) != -1) {
376                 switch (ch) {
377                 case 'p':
378                 case 'P':
379                         *pflag = 1;
380                         break;
381                 default:
382                         error("%s: Invalid flag -%c", cmd, optopt);
383                         return -1;
384                 }
385         }
386
387         return optind;
388 }
389
390 static int
391 parse_ls_flags(char **argv, int argc, int *lflag)
392 {
393         extern int opterr, optind, optopt, optreset;
394         int ch;
395
396         optind = optreset = 1;
397         opterr = 0;
398
399         *lflag = LS_NAME_SORT;
400         while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
401                 switch (ch) {
402                 case '1':
403                         *lflag &= ~VIEW_FLAGS;
404                         *lflag |= LS_SHORT_VIEW;
405                         break;
406                 case 'S':
407                         *lflag &= ~SORT_FLAGS;
408                         *lflag |= LS_SIZE_SORT;
409                         break;
410                 case 'a':
411                         *lflag |= LS_SHOW_ALL;
412                         break;
413                 case 'f':
414                         *lflag &= ~SORT_FLAGS;
415                         break;
416                 case 'l':
417                         *lflag &= ~VIEW_FLAGS;
418                         *lflag |= LS_LONG_VIEW;
419                         break;
420                 case 'n':
421                         *lflag &= ~VIEW_FLAGS;
422                         *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
423                         break;
424                 case 'r':
425                         *lflag |= LS_REVERSE_SORT;
426                         break;
427                 case 't':
428                         *lflag &= ~SORT_FLAGS;
429                         *lflag |= LS_TIME_SORT;
430                         break;
431                 default:
432                         error("ls: Invalid flag -%c", optopt);
433                         return -1;
434                 }
435         }
436
437         return optind;
438 }
439
440 static int
441 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
442 {
443         extern int opterr, optind, optopt, optreset;
444         int ch;
445
446         optind = optreset = 1;
447         opterr = 0;
448
449         *hflag = *iflag = 0;
450         while ((ch = getopt(argc, argv, "hi")) != -1) {
451                 switch (ch) {
452                 case 'h':
453                         *hflag = 1;
454                         break;
455                 case 'i':
456                         *iflag = 1;
457                         break;
458                 default:
459                         error("%s: Invalid flag -%c", cmd, optopt);
460                         return -1;
461                 }
462         }
463
464         return optind;
465 }
466
467 static int
468 is_dir(char *path)
469 {
470         struct stat sb;
471
472         /* XXX: report errors? */
473         if (stat(path, &sb) == -1)
474                 return(0);
475
476         return(S_ISDIR(sb.st_mode));
477 }
478
479 static int
480 remote_is_dir(struct sftp_conn *conn, char *path)
481 {
482         Attrib *a;
483
484         /* XXX: report errors? */
485         if ((a = do_stat(conn, path, 1)) == NULL)
486                 return(0);
487         if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
488                 return(0);
489         return(S_ISDIR(a->perm));
490 }
491
492 static int
493 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
494 {
495         char *abs_src = NULL;
496         char *abs_dst = NULL;
497         char *tmp;
498         glob_t g;
499         int err = 0;
500         int i;
501
502         abs_src = xstrdup(src);
503         abs_src = make_absolute(abs_src, pwd);
504
505         memset(&g, 0, sizeof(g));
506         debug3("Looking up %s", abs_src);
507         if (remote_glob(conn, abs_src, 0, NULL, &g)) {
508                 error("File \"%s\" not found.", abs_src);
509                 err = -1;
510                 goto out;
511         }
512
513         /* If multiple matches, dst must be a directory or unspecified */
514         if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
515                 error("Multiple files match, but \"%s\" is not a directory",
516                     dst);
517                 err = -1;
518                 goto out;
519         }
520
521         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
522                 if (infer_path(g.gl_pathv[i], &tmp)) {
523                         err = -1;
524                         goto out;
525                 }
526
527                 if (g.gl_matchc == 1 && dst) {
528                         /* If directory specified, append filename */
529                         xfree(tmp);
530                         if (is_dir(dst)) {
531                                 if (infer_path(g.gl_pathv[0], &tmp)) {
532                                         err = 1;
533                                         goto out;
534                                 }
535                                 abs_dst = path_append(dst, tmp);
536                                 xfree(tmp);
537                         } else
538                                 abs_dst = xstrdup(dst);
539                 } else if (dst) {
540                         abs_dst = path_append(dst, tmp);
541                         xfree(tmp);
542                 } else
543                         abs_dst = tmp;
544
545                 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
546                 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
547                         err = -1;
548                 xfree(abs_dst);
549                 abs_dst = NULL;
550         }
551
552 out:
553         xfree(abs_src);
554         globfree(&g);
555         return(err);
556 }
557
558 static int
559 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
560 {
561         char *tmp_dst = NULL;
562         char *abs_dst = NULL;
563         char *tmp;
564         glob_t g;
565         int err = 0;
566         int i;
567         struct stat sb;
568
569         if (dst) {
570                 tmp_dst = xstrdup(dst);
571                 tmp_dst = make_absolute(tmp_dst, pwd);
572         }
573
574         memset(&g, 0, sizeof(g));
575         debug3("Looking up %s", src);
576         if (glob(src, GLOB_NOCHECK, NULL, &g)) {
577                 error("File \"%s\" not found.", src);
578                 err = -1;
579                 goto out;
580         }
581
582         /* If multiple matches, dst may be directory or unspecified */
583         if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
584                 error("Multiple files match, but \"%s\" is not a directory",
585                     tmp_dst);
586                 err = -1;
587                 goto out;
588         }
589
590         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
591                 if (stat(g.gl_pathv[i], &sb) == -1) {
592                         err = -1;
593                         error("stat %s: %s", g.gl_pathv[i], strerror(errno));
594                         continue;
595                 }
596
597                 if (!S_ISREG(sb.st_mode)) {
598                         error("skipping non-regular file %s",
599                             g.gl_pathv[i]);
600                         continue;
601                 }
602                 if (infer_path(g.gl_pathv[i], &tmp)) {
603                         err = -1;
604                         goto out;
605                 }
606
607                 if (g.gl_matchc == 1 && tmp_dst) {
608                         /* If directory specified, append filename */
609                         if (remote_is_dir(conn, tmp_dst)) {
610                                 if (infer_path(g.gl_pathv[0], &tmp)) {
611                                         err = 1;
612                                         goto out;
613                                 }
614                                 abs_dst = path_append(tmp_dst, tmp);
615                                 xfree(tmp);
616                         } else
617                                 abs_dst = xstrdup(tmp_dst);
618
619                 } else if (tmp_dst) {
620                         abs_dst = path_append(tmp_dst, tmp);
621                         xfree(tmp);
622                 } else
623                         abs_dst = make_absolute(tmp, pwd);
624
625                 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
626                 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
627                         err = -1;
628         }
629
630 out:
631         if (abs_dst)
632                 xfree(abs_dst);
633         if (tmp_dst)
634                 xfree(tmp_dst);
635         globfree(&g);
636         return(err);
637 }
638
639 static int
640 sdirent_comp(const void *aa, const void *bb)
641 {
642         SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
643         SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
644         int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
645
646 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
647         if (sort_flag & LS_NAME_SORT)
648                 return (rmul * strcmp(a->filename, b->filename));
649         else if (sort_flag & LS_TIME_SORT)
650                 return (rmul * NCMP(a->a.mtime, b->a.mtime));
651         else if (sort_flag & LS_SIZE_SORT)
652                 return (rmul * NCMP(a->a.size, b->a.size));
653
654         fatal("Unknown ls sort type");
655 }
656
657 /* sftp ls.1 replacement for directories */
658 static int
659 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
660 {
661         int n;
662         u_int c = 1, colspace = 0, columns = 1;
663         SFTP_DIRENT **d;
664
665         if ((n = do_readdir(conn, path, &d)) != 0)
666                 return (n);
667
668         if (!(lflag & LS_SHORT_VIEW)) {
669                 u_int m = 0, width = 80;
670                 struct winsize ws;
671                 char *tmp;
672
673                 /* Count entries for sort and find longest filename */
674                 for (n = 0; d[n] != NULL; n++) {
675                         if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
676                                 m = MAX(m, strlen(d[n]->filename));
677                 }
678
679                 /* Add any subpath that also needs to be counted */
680                 tmp = path_strip(path, strip_path);
681                 m += strlen(tmp);
682                 xfree(tmp);
683
684                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
685                         width = ws.ws_col;
686
687                 columns = width / (m + 2);
688                 columns = MAX(columns, 1);
689                 colspace = width / columns;
690                 colspace = MIN(colspace, width);
691         }
692
693         if (lflag & SORT_FLAGS) {
694                 for (n = 0; d[n] != NULL; n++)
695                         ;       /* count entries */
696                 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
697                 qsort(d, n, sizeof(*d), sdirent_comp);
698         }
699
700         for (n = 0; d[n] != NULL && !interrupted; n++) {
701                 char *tmp, *fname;
702
703                 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
704                         continue;
705
706                 tmp = path_append(path, d[n]->filename);
707                 fname = path_strip(tmp, strip_path);
708                 xfree(tmp);
709
710                 if (lflag & LS_LONG_VIEW) {
711                         if (lflag & LS_NUMERIC_VIEW) {
712                                 char *lname;
713                                 struct stat sb;
714
715                                 memset(&sb, 0, sizeof(sb));
716                                 attrib_to_stat(&d[n]->a, &sb);
717                                 lname = ls_file(fname, &sb, 1);
718                                 printf("%s\n", lname);
719                                 xfree(lname);
720                         } else
721                                 printf("%s\n", d[n]->longname);
722                 } else {
723                         printf("%-*s", colspace, fname);
724                         if (c >= columns) {
725                                 printf("\n");
726                                 c = 1;
727                         } else
728                                 c++;
729                 }
730
731                 xfree(fname);
732         }
733
734         if (!(lflag & LS_LONG_VIEW) && (c != 1))
735                 printf("\n");
736
737         free_sftp_dirents(d);
738         return (0);
739 }
740
741 /* sftp ls.1 replacement which handles path globs */
742 static int
743 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
744     int lflag)
745 {
746         glob_t g;
747         u_int i, c = 1, colspace = 0, columns = 1;
748         Attrib *a = NULL;
749
750         memset(&g, 0, sizeof(g));
751
752         if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
753             NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
754                 if (g.gl_pathc)
755                         globfree(&g);
756                 error("Can't ls: \"%s\" not found", path);
757                 return (-1);
758         }
759
760         if (interrupted)
761                 goto out;
762
763         /*
764          * If the glob returns a single match and it is a directory,
765          * then just list its contents.
766          */
767         if (g.gl_matchc == 1) {
768                 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
769                         globfree(&g);
770                         return (-1);
771                 }
772                 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
773                     S_ISDIR(a->perm)) {
774                         int err;
775
776                         err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
777                         globfree(&g);
778                         return (err);
779                 }
780         }
781
782         if (!(lflag & LS_SHORT_VIEW)) {
783                 u_int m = 0, width = 80;
784                 struct winsize ws;
785
786                 /* Count entries for sort and find longest filename */
787                 for (i = 0; g.gl_pathv[i]; i++)
788                         m = MAX(m, strlen(g.gl_pathv[i]));
789
790                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
791                         width = ws.ws_col;
792
793                 columns = width / (m + 2);
794                 columns = MAX(columns, 1);
795                 colspace = width / columns;
796         }
797
798         for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
799                 char *fname;
800
801                 fname = path_strip(g.gl_pathv[i], strip_path);
802
803                 if (lflag & LS_LONG_VIEW) {
804                         char *lname;
805                         struct stat sb;
806
807                         /*
808                          * XXX: this is slow - 1 roundtrip per path
809                          * A solution to this is to fork glob() and
810                          * build a sftp specific version which keeps the
811                          * attribs (which currently get thrown away)
812                          * that the server returns as well as the filenames.
813                          */
814                         memset(&sb, 0, sizeof(sb));
815                         if (a == NULL)
816                                 a = do_lstat(conn, g.gl_pathv[i], 1);
817                         if (a != NULL)
818                                 attrib_to_stat(a, &sb);
819                         lname = ls_file(fname, &sb, 1);
820                         printf("%s\n", lname);
821                         xfree(lname);
822                 } else {
823                         printf("%-*s", colspace, fname);
824                         if (c >= columns) {
825                                 printf("\n");
826                                 c = 1;
827                         } else
828                                 c++;
829                 }
830                 xfree(fname);
831         }
832
833         if (!(lflag & LS_LONG_VIEW) && (c != 1))
834                 printf("\n");
835
836  out:
837         if (g.gl_pathc)
838                 globfree(&g);
839
840         return (0);
841 }
842
843 static int
844 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
845 {
846         struct sftp_statvfs st;
847         char s_used[FMT_SCALED_STRSIZE];
848         char s_avail[FMT_SCALED_STRSIZE];
849         char s_root[FMT_SCALED_STRSIZE];
850         char s_total[FMT_SCALED_STRSIZE];
851
852         if (do_statvfs(conn, path, &st, 1) == -1)
853                 return -1;
854         if (iflag) {
855                 printf("     Inodes        Used       Avail      "
856                     "(root)    %%Capacity\n");
857                 printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
858                     (unsigned long long)st.f_files,
859                     (unsigned long long)(st.f_files - st.f_ffree),
860                     (unsigned long long)st.f_favail,
861                     (unsigned long long)st.f_ffree,
862                     (unsigned long long)(100 * (st.f_files - st.f_ffree) /
863                     st.f_files));
864         } else if (hflag) {
865                 strlcpy(s_used, "error", sizeof(s_used));
866                 strlcpy(s_avail, "error", sizeof(s_avail));
867                 strlcpy(s_root, "error", sizeof(s_root));
868                 strlcpy(s_total, "error", sizeof(s_total));
869                 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
870                 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
871                 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
872                 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
873                 printf("    Size     Used    Avail   (root)    %%Capacity\n");
874                 printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
875                     s_total, s_used, s_avail, s_root,
876                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
877                     st.f_blocks));
878         } else {
879                 printf("        Size         Used        Avail       "
880                     "(root)    %%Capacity\n");
881                 printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
882                     (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
883                     (unsigned long long)(st.f_frsize *
884                     (st.f_blocks - st.f_bfree) / 1024),
885                     (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
886                     (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
887                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
888                     st.f_blocks));
889         }
890         return 0;
891 }
892
893 /*
894  * Undo escaping of glob sequences in place. Used to undo extra escaping
895  * applied in makeargv() when the string is destined for a function that
896  * does not glob it.
897  */
898 static void
899 undo_glob_escape(char *s)
900 {
901         size_t i, j;
902
903         for (i = j = 0;;) {
904                 if (s[i] == '\0') {
905                         s[j] = '\0';
906                         return;
907                 }
908                 if (s[i] != '\\') {
909                         s[j++] = s[i++];
910                         continue;
911                 }
912                 /* s[i] == '\\' */
913                 ++i;
914                 switch (s[i]) {
915                 case '?':
916                 case '[':
917                 case '*':
918                 case '\\':
919                         s[j++] = s[i++];
920                         break;
921                 case '\0':
922                         s[j++] = '\\';
923                         s[j] = '\0';
924                         return;
925                 default:
926                         s[j++] = '\\';
927                         s[j++] = s[i++];
928                         break;
929                 }
930         }
931 }
932
933 /*
934  * Split a string into an argument vector using sh(1)-style quoting,
935  * comment and escaping rules, but with some tweaks to handle glob(3)
936  * wildcards.
937  * Returns NULL on error or a NULL-terminated array of arguments.
938  */
939 #define MAXARGS         128
940 #define MAXARGLEN       8192
941 static char **
942 makeargv(const char *arg, int *argcp)
943 {
944         int argc, quot;
945         size_t i, j;
946         static char argvs[MAXARGLEN];
947         static char *argv[MAXARGS + 1];
948         enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
949
950         *argcp = argc = 0;
951         if (strlen(arg) > sizeof(argvs) - 1) {
952  args_too_longs:
953                 error("string too long");
954                 return NULL;
955         }
956         state = MA_START;
957         i = j = 0;
958         for (;;) {
959                 if (isspace(arg[i])) {
960                         if (state == MA_UNQUOTED) {
961                                 /* Terminate current argument */
962                                 argvs[j++] = '\0';
963                                 argc++;
964                                 state = MA_START;
965                         } else if (state != MA_START)
966                                 argvs[j++] = arg[i];
967                 } else if (arg[i] == '"' || arg[i] == '\'') {
968                         q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
969                         if (state == MA_START) {
970                                 argv[argc] = argvs + j;
971                                 state = q;
972                         } else if (state == MA_UNQUOTED) 
973                                 state = q;
974                         else if (state == q)
975                                 state = MA_UNQUOTED;
976                         else
977                                 argvs[j++] = arg[i];
978                 } else if (arg[i] == '\\') {
979                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
980                                 quot = state == MA_SQUOTE ? '\'' : '"';
981                                 /* Unescape quote we are in */
982                                 /* XXX support \n and friends? */
983                                 if (arg[i + 1] == quot) {
984                                         i++;
985                                         argvs[j++] = arg[i];
986                                 } else if (arg[i + 1] == '?' ||
987                                     arg[i + 1] == '[' || arg[i + 1] == '*') {
988                                         /*
989                                          * Special case for sftp: append
990                                          * double-escaped glob sequence -
991                                          * glob will undo one level of
992                                          * escaping. NB. string can grow here.
993                                          */
994                                         if (j >= sizeof(argvs) - 5)
995                                                 goto args_too_longs;
996                                         argvs[j++] = '\\';
997                                         argvs[j++] = arg[i++];
998                                         argvs[j++] = '\\';
999                                         argvs[j++] = arg[i];
1000                                 } else {
1001                                         argvs[j++] = arg[i++];
1002                                         argvs[j++] = arg[i];
1003                                 }
1004                         } else {
1005                                 if (state == MA_START) {
1006                                         argv[argc] = argvs + j;
1007                                         state = MA_UNQUOTED;
1008                                 }
1009                                 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1010                                     arg[i + 1] == '*' || arg[i + 1] == '\\') {
1011                                         /*
1012                                          * Special case for sftp: append
1013                                          * escaped glob sequence -
1014                                          * glob will undo one level of
1015                                          * escaping.
1016                                          */
1017                                         argvs[j++] = arg[i++];
1018                                         argvs[j++] = arg[i];
1019                                 } else {
1020                                         /* Unescape everything */
1021                                         /* XXX support \n and friends? */
1022                                         i++;
1023                                         argvs[j++] = arg[i];
1024                                 }
1025                         }
1026                 } else if (arg[i] == '#') {
1027                         if (state == MA_SQUOTE || state == MA_DQUOTE)
1028                                 argvs[j++] = arg[i];
1029                         else
1030                                 goto string_done;
1031                 } else if (arg[i] == '\0') {
1032                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
1033                                 error("Unterminated quoted argument");
1034                                 return NULL;
1035                         }
1036  string_done:
1037                         if (state == MA_UNQUOTED) {
1038                                 argvs[j++] = '\0';
1039                                 argc++;
1040                         }
1041                         break;
1042                 } else {
1043                         if (state == MA_START) {
1044                                 argv[argc] = argvs + j;
1045                                 state = MA_UNQUOTED;
1046                         }
1047                         if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1048                             (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1049                                 /*
1050                                  * Special case for sftp: escape quoted
1051                                  * glob(3) wildcards. NB. string can grow
1052                                  * here.
1053                                  */
1054                                 if (j >= sizeof(argvs) - 3)
1055                                         goto args_too_longs;
1056                                 argvs[j++] = '\\';
1057                                 argvs[j++] = arg[i];
1058                         } else
1059                                 argvs[j++] = arg[i];
1060                 }
1061                 i++;
1062         }
1063         *argcp = argc;
1064         return argv;
1065 }
1066
1067 static int
1068 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
1069     unsigned long *n_arg, char **path1, char **path2)
1070 {
1071         const char *cmd, *cp = *cpp;
1072         char *cp2, **argv;
1073         int base = 0;
1074         long l;
1075         int i, cmdnum, optidx, argc;
1076
1077         /* Skip leading whitespace */
1078         cp = cp + strspn(cp, WHITESPACE);
1079
1080         /* Ignore blank lines and lines which begin with comment '#' char */
1081         if (*cp == '\0' || *cp == '#')
1082                 return (0);
1083
1084         /* Check for leading '-' (disable error processing) */
1085         *iflag = 0;
1086         if (*cp == '-') {
1087                 *iflag = 1;
1088                 cp++;
1089         }
1090
1091         if ((argv = makeargv(cp, &argc)) == NULL)
1092                 return -1;
1093
1094         /* Figure out which command we have */
1095         for (i = 0; cmds[i].c != NULL; i++) {
1096                 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1097                         break;
1098         }
1099         cmdnum = cmds[i].n;
1100         cmd = cmds[i].c;
1101
1102         /* Special case */
1103         if (*cp == '!') {
1104                 cp++;
1105                 cmdnum = I_SHELL;
1106         } else if (cmdnum == -1) {
1107                 error("Invalid command.");
1108                 return -1;
1109         }
1110
1111         /* Get arguments and parse flags */
1112         *lflag = *pflag = *hflag = *n_arg = 0;
1113         *path1 = *path2 = NULL;
1114         optidx = 1;
1115         switch (cmdnum) {
1116         case I_GET:
1117         case I_PUT:
1118                 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
1119                         return -1;
1120                 /* Get first pathname (mandatory) */
1121                 if (argc - optidx < 1) {
1122                         error("You must specify at least one path after a "
1123                             "%s command.", cmd);
1124                         return -1;
1125                 }
1126                 *path1 = xstrdup(argv[optidx]);
1127                 /* Get second pathname (optional) */
1128                 if (argc - optidx > 1) {
1129                         *path2 = xstrdup(argv[optidx + 1]);
1130                         /* Destination is not globbed */
1131                         undo_glob_escape(*path2);
1132                 }
1133                 break;
1134         case I_RENAME:
1135         case I_SYMLINK:
1136                 if (argc - optidx < 2) {
1137                         error("You must specify two paths after a %s "
1138                             "command.", cmd);
1139                         return -1;
1140                 }
1141                 *path1 = xstrdup(argv[optidx]);
1142                 *path2 = xstrdup(argv[optidx + 1]);
1143                 /* Paths are not globbed */
1144                 undo_glob_escape(*path1);
1145                 undo_glob_escape(*path2);
1146                 break;
1147         case I_RM:
1148         case I_MKDIR:
1149         case I_RMDIR:
1150         case I_CHDIR:
1151         case I_LCHDIR:
1152         case I_LMKDIR:
1153                 /* Get pathname (mandatory) */
1154                 if (argc - optidx < 1) {
1155                         error("You must specify a path after a %s command.",
1156                             cmd);
1157                         return -1;
1158                 }
1159                 *path1 = xstrdup(argv[optidx]);
1160                 /* Only "rm" globs */
1161                 if (cmdnum != I_RM)
1162                         undo_glob_escape(*path1);
1163                 break;
1164         case I_DF:
1165                 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1166                     iflag)) == -1)
1167                         return -1;
1168                 /* Default to current directory if no path specified */
1169                 if (argc - optidx < 1)
1170                         *path1 = NULL;
1171                 else {
1172                         *path1 = xstrdup(argv[optidx]);
1173                         undo_glob_escape(*path1);
1174                 }
1175                 break;
1176         case I_LS:
1177                 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1178                         return(-1);
1179                 /* Path is optional */
1180                 if (argc - optidx > 0)
1181                         *path1 = xstrdup(argv[optidx]);
1182                 break;
1183         case I_LLS:
1184                 /* Skip ls command and following whitespace */
1185                 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1186         case I_SHELL:
1187                 /* Uses the rest of the line */
1188                 break;
1189         case I_LUMASK:
1190         case I_CHMOD:
1191                 base = 8;
1192         case I_CHOWN:
1193         case I_CHGRP:
1194                 /* Get numeric arg (mandatory) */
1195                 if (argc - optidx < 1)
1196                         goto need_num_arg;
1197                 errno = 0;
1198                 l = strtol(argv[optidx], &cp2, base);
1199                 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1200                     ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1201                     l < 0) {
1202  need_num_arg:
1203                         error("You must supply a numeric argument "
1204                             "to the %s command.", cmd);
1205                         return -1;
1206                 }
1207                 *n_arg = l;
1208                 if (cmdnum == I_LUMASK)
1209                         break;
1210                 /* Get pathname (mandatory) */
1211                 if (argc - optidx < 2) {
1212                         error("You must specify a path after a %s command.",
1213                             cmd);
1214                         return -1;
1215                 }
1216                 *path1 = xstrdup(argv[optidx + 1]);
1217                 break;
1218         case I_QUIT:
1219         case I_PWD:
1220         case I_LPWD:
1221         case I_HELP:
1222         case I_VERSION:
1223         case I_PROGRESS:
1224                 break;
1225         default:
1226                 fatal("Command not implemented");
1227         }
1228
1229         *cpp = cp;
1230         return(cmdnum);
1231 }
1232
1233 static int
1234 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1235     int err_abort)
1236 {
1237         char *path1, *path2, *tmp;
1238         int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1239         unsigned long n_arg = 0;
1240         Attrib a, *aa;
1241         char path_buf[MAXPATHLEN];
1242         int err = 0;
1243         glob_t g;
1244
1245         path1 = path2 = NULL;
1246         cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
1247             &path1, &path2);
1248
1249         if (iflag != 0)
1250                 err_abort = 0;
1251
1252         memset(&g, 0, sizeof(g));
1253
1254         /* Perform command */
1255         switch (cmdnum) {
1256         case 0:
1257                 /* Blank line */
1258                 break;
1259         case -1:
1260                 /* Unrecognized command */
1261                 err = -1;
1262                 break;
1263         case I_GET:
1264                 err = process_get(conn, path1, path2, *pwd, pflag);
1265                 break;
1266         case I_PUT:
1267                 err = process_put(conn, path1, path2, *pwd, pflag);
1268                 break;
1269         case I_RENAME:
1270                 path1 = make_absolute(path1, *pwd);
1271                 path2 = make_absolute(path2, *pwd);
1272                 err = do_rename(conn, path1, path2);
1273                 break;
1274         case I_SYMLINK:
1275                 path2 = make_absolute(path2, *pwd);
1276                 err = do_symlink(conn, path1, path2);
1277                 break;
1278         case I_RM:
1279                 path1 = make_absolute(path1, *pwd);
1280                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1281                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1282                         printf("Removing %s\n", g.gl_pathv[i]);
1283                         err = do_rm(conn, g.gl_pathv[i]);
1284                         if (err != 0 && err_abort)
1285                                 break;
1286                 }
1287                 break;
1288         case I_MKDIR:
1289                 path1 = make_absolute(path1, *pwd);
1290                 attrib_clear(&a);
1291                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1292                 a.perm = 0777;
1293                 err = do_mkdir(conn, path1, &a);
1294                 break;
1295         case I_RMDIR:
1296                 path1 = make_absolute(path1, *pwd);
1297                 err = do_rmdir(conn, path1);
1298                 break;
1299         case I_CHDIR:
1300                 path1 = make_absolute(path1, *pwd);
1301                 if ((tmp = do_realpath(conn, path1)) == NULL) {
1302                         err = 1;
1303                         break;
1304                 }
1305                 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1306                         xfree(tmp);
1307                         err = 1;
1308                         break;
1309                 }
1310                 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1311                         error("Can't change directory: Can't check target");
1312                         xfree(tmp);
1313                         err = 1;
1314                         break;
1315                 }
1316                 if (!S_ISDIR(aa->perm)) {
1317                         error("Can't change directory: \"%s\" is not "
1318                             "a directory", tmp);
1319                         xfree(tmp);
1320                         err = 1;
1321                         break;
1322                 }
1323                 xfree(*pwd);
1324                 *pwd = tmp;
1325                 break;
1326         case I_LS:
1327                 if (!path1) {
1328                         do_globbed_ls(conn, *pwd, *pwd, lflag);
1329                         break;
1330                 }
1331
1332                 /* Strip pwd off beginning of non-absolute paths */
1333                 tmp = NULL;
1334                 if (*path1 != '/')
1335                         tmp = *pwd;
1336
1337                 path1 = make_absolute(path1, *pwd);
1338                 err = do_globbed_ls(conn, path1, tmp, lflag);
1339                 break;
1340         case I_DF:
1341                 /* Default to current directory if no path specified */
1342                 if (path1 == NULL)
1343                         path1 = xstrdup(*pwd);
1344                 path1 = make_absolute(path1, *pwd);
1345                 err = do_df(conn, path1, hflag, iflag);
1346                 break;
1347         case I_LCHDIR:
1348                 if (chdir(path1) == -1) {
1349                         error("Couldn't change local directory to "
1350                             "\"%s\": %s", path1, strerror(errno));
1351                         err = 1;
1352                 }
1353                 break;
1354         case I_LMKDIR:
1355                 if (mkdir(path1, 0777) == -1) {
1356                         error("Couldn't create local directory "
1357                             "\"%s\": %s", path1, strerror(errno));
1358                         err = 1;
1359                 }
1360                 break;
1361         case I_LLS:
1362                 local_do_ls(cmd);
1363                 break;
1364         case I_SHELL:
1365                 local_do_shell(cmd);
1366                 break;
1367         case I_LUMASK:
1368                 umask(n_arg);
1369                 printf("Local umask: %03lo\n", n_arg);
1370                 break;
1371         case I_CHMOD:
1372                 path1 = make_absolute(path1, *pwd);
1373                 attrib_clear(&a);
1374                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1375                 a.perm = n_arg;
1376                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1377                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1378                         printf("Changing mode on %s\n", g.gl_pathv[i]);
1379                         err = do_setstat(conn, g.gl_pathv[i], &a);
1380                         if (err != 0 && err_abort)
1381                                 break;
1382                 }
1383                 break;
1384         case I_CHOWN:
1385         case I_CHGRP:
1386                 path1 = make_absolute(path1, *pwd);
1387                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1388                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1389                         if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1390                                 if (err_abort) {
1391                                         err = -1;
1392                                         break;
1393                                 } else
1394                                         continue;
1395                         }
1396                         if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1397                                 error("Can't get current ownership of "
1398                                     "remote file \"%s\"", g.gl_pathv[i]);
1399                                 if (err_abort) {
1400                                         err = -1;
1401                                         break;
1402                                 } else
1403                                         continue;
1404                         }
1405                         aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1406                         if (cmdnum == I_CHOWN) {
1407                                 printf("Changing owner on %s\n", g.gl_pathv[i]);
1408                                 aa->uid = n_arg;
1409                         } else {
1410                                 printf("Changing group on %s\n", g.gl_pathv[i]);
1411                                 aa->gid = n_arg;
1412                         }
1413                         err = do_setstat(conn, g.gl_pathv[i], aa);
1414                         if (err != 0 && err_abort)
1415                                 break;
1416                 }
1417                 break;
1418         case I_PWD:
1419                 printf("Remote working directory: %s\n", *pwd);
1420                 break;
1421         case I_LPWD:
1422                 if (!getcwd(path_buf, sizeof(path_buf))) {
1423                         error("Couldn't get local cwd: %s", strerror(errno));
1424                         err = -1;
1425                         break;
1426                 }
1427                 printf("Local working directory: %s\n", path_buf);
1428                 break;
1429         case I_QUIT:
1430                 /* Processed below */
1431                 break;
1432         case I_HELP:
1433                 help();
1434                 break;
1435         case I_VERSION:
1436                 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1437                 break;
1438         case I_PROGRESS:
1439                 showprogress = !showprogress;
1440                 if (showprogress)
1441                         printf("Progress meter enabled\n");
1442                 else
1443                         printf("Progress meter disabled\n");
1444                 break;
1445         default:
1446                 fatal("%d is not implemented", cmdnum);
1447         }
1448
1449         if (g.gl_pathc)
1450                 globfree(&g);
1451         if (path1)
1452                 xfree(path1);
1453         if (path2)
1454                 xfree(path2);
1455
1456         /* If an unignored error occurs in batch mode we should abort. */
1457         if (err_abort && err != 0)
1458                 return (-1);
1459         else if (cmdnum == I_QUIT)
1460                 return (1);
1461
1462         return (0);
1463 }
1464
1465 #ifdef USE_LIBEDIT
1466 static char *
1467 prompt(EditLine *el)
1468 {
1469         return ("sftp> ");
1470 }
1471 #endif
1472
1473 int
1474 interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1475 {
1476         char *pwd;
1477         char *dir = NULL;
1478         char cmd[2048];
1479         struct sftp_conn *conn;
1480         int err, interactive;
1481         EditLine *el = NULL;
1482 #ifdef USE_LIBEDIT
1483         History *hl = NULL;
1484         HistEvent hev;
1485         extern char *__progname;
1486
1487         if (!batchmode && isatty(STDIN_FILENO)) {
1488                 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1489                         fatal("Couldn't initialise editline");
1490                 if ((hl = history_init()) == NULL)
1491                         fatal("Couldn't initialise editline history");
1492                 history(hl, &hev, H_SETSIZE, 100);
1493                 el_set(el, EL_HIST, history, hl);
1494
1495                 el_set(el, EL_PROMPT, prompt);
1496                 el_set(el, EL_EDITOR, "emacs");
1497                 el_set(el, EL_TERMINAL, NULL);
1498                 el_set(el, EL_SIGNAL, 1);
1499                 el_source(el, NULL);
1500         }
1501 #endif /* USE_LIBEDIT */
1502
1503         conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1504         if (conn == NULL)
1505                 fatal("Couldn't initialise connection to server");
1506
1507         pwd = do_realpath(conn, ".");
1508         if (pwd == NULL)
1509                 fatal("Need cwd");
1510
1511         if (file1 != NULL) {
1512                 dir = xstrdup(file1);
1513                 dir = make_absolute(dir, pwd);
1514
1515                 if (remote_is_dir(conn, dir) && file2 == NULL) {
1516                         printf("Changing to: %s\n", dir);
1517                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1518                         if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1519                                 xfree(dir);
1520                                 xfree(pwd);
1521                                 xfree(conn);
1522                                 return (-1);
1523                         }
1524                 } else {
1525                         if (file2 == NULL)
1526                                 snprintf(cmd, sizeof cmd, "get %s", dir);
1527                         else
1528                                 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1529                                     file2);
1530
1531                         err = parse_dispatch_command(conn, cmd, &pwd, 1);
1532                         xfree(dir);
1533                         xfree(pwd);
1534                         xfree(conn);
1535                         return (err);
1536                 }
1537                 xfree(dir);
1538         }
1539
1540 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1541         setvbuf(stdout, NULL, _IOLBF, 0);
1542         setvbuf(infile, NULL, _IOLBF, 0);
1543 #else
1544         setlinebuf(stdout);
1545         setlinebuf(infile);
1546 #endif
1547
1548         interactive = !batchmode && isatty(STDIN_FILENO);
1549         err = 0;
1550         for (;;) {
1551                 char *cp;
1552
1553                 signal(SIGINT, SIG_IGN);
1554
1555                 if (el == NULL) {
1556                         if (interactive)
1557                                 printf("sftp> ");
1558                         if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1559                                 if (interactive)
1560                                         printf("\n");
1561                                 break;
1562                         }
1563                         if (!interactive) { /* Echo command */
1564                                 printf("sftp> %s", cmd);
1565                                 if (strlen(cmd) > 0 &&
1566                                     cmd[strlen(cmd) - 1] != '\n')
1567                                         printf("\n");
1568                         }
1569                 } else {
1570 #ifdef USE_LIBEDIT
1571                         const char *line;
1572                         int count = 0;
1573
1574                         if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1575                                 printf("\n");
1576                                 break;
1577                         }
1578                         history(hl, &hev, H_ENTER, line);
1579                         if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1580                                 fprintf(stderr, "Error: input line too long\n");
1581                                 continue;
1582                         }
1583 #endif /* USE_LIBEDIT */
1584                 }
1585
1586                 cp = strrchr(cmd, '\n');
1587                 if (cp)
1588                         *cp = '\0';
1589
1590                 /* Handle user interrupts gracefully during commands */
1591                 interrupted = 0;
1592                 signal(SIGINT, cmd_interrupt);
1593
1594                 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1595                 if (err != 0)
1596                         break;
1597         }
1598         xfree(pwd);
1599         xfree(conn);
1600
1601 #ifdef USE_LIBEDIT
1602         if (el != NULL)
1603                 el_end(el);
1604 #endif /* USE_LIBEDIT */
1605
1606         /* err == 1 signifies normal "quit" exit */
1607         return (err >= 0 ? 0 : -1);
1608 }
1609
1610 static void
1611 connect_to_server(char *path, char **args, int *in, int *out)
1612 {
1613         int c_in, c_out;
1614
1615 #ifdef USE_PIPES
1616         int pin[2], pout[2];
1617
1618         if ((pipe(pin) == -1) || (pipe(pout) == -1))
1619                 fatal("pipe: %s", strerror(errno));
1620         *in = pin[0];
1621         *out = pout[1];
1622         c_in = pout[0];
1623         c_out = pin[1];
1624 #else /* USE_PIPES */
1625         int inout[2];
1626
1627         if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1628                 fatal("socketpair: %s", strerror(errno));
1629         *in = *out = inout[0];
1630         c_in = c_out = inout[1];
1631 #endif /* USE_PIPES */
1632
1633         if ((sshpid = fork()) == -1)
1634                 fatal("fork: %s", strerror(errno));
1635         else if (sshpid == 0) {
1636                 if ((dup2(c_in, STDIN_FILENO) == -1) ||
1637                     (dup2(c_out, STDOUT_FILENO) == -1)) {
1638                         fprintf(stderr, "dup2: %s\n", strerror(errno));
1639                         _exit(1);
1640                 }
1641                 close(*in);
1642                 close(*out);
1643                 close(c_in);
1644                 close(c_out);
1645
1646                 /*
1647                  * The underlying ssh is in the same process group, so we must
1648                  * ignore SIGINT if we want to gracefully abort commands,
1649                  * otherwise the signal will make it to the ssh process and
1650                  * kill it too
1651                  */
1652                 signal(SIGINT, SIG_IGN);
1653                 execvp(path, args);
1654                 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1655                 _exit(1);
1656         }
1657
1658         signal(SIGTERM, killchild);
1659         signal(SIGINT, killchild);
1660         signal(SIGHUP, killchild);
1661         close(c_in);
1662         close(c_out);
1663 }
1664
1665 static void
1666 usage(void)
1667 {
1668         extern char *__progname;
1669
1670         fprintf(stderr,
1671             "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
1672             "            [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
1673             "            [-S program] [-s subsystem | sftp_server] host\n"
1674             "       %s [user@]host[:file ...]\n"
1675             "       %s [user@]host[:dir[/]]\n"
1676             "       %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
1677         exit(1);
1678 }
1679
1680 int
1681 main(int argc, char **argv)
1682 {
1683         int in, out, ch, err;
1684         char *host, *userhost, *cp, *file2 = NULL;
1685         int debug_level = 0, sshver = 2;
1686         char *file1 = NULL, *sftp_server = NULL;
1687         char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1688         LogLevel ll = SYSLOG_LEVEL_INFO;
1689         arglist args;
1690         extern int optind;
1691         extern char *optarg;
1692
1693         /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1694         sanitise_stdfd();
1695
1696         __progname = ssh_get_progname(argv[0]);
1697         memset(&args, '\0', sizeof(args));
1698         args.list = NULL;
1699         addargs(&args, "%s", ssh_program);
1700         addargs(&args, "-oForwardX11 no");
1701         addargs(&args, "-oForwardAgent no");
1702         addargs(&args, "-oPermitLocalCommand no");
1703         addargs(&args, "-oClearAllForwardings yes");
1704
1705         ll = SYSLOG_LEVEL_INFO;
1706         infile = stdin;
1707
1708         while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
1709                 switch (ch) {
1710                 case 'C':
1711                         addargs(&args, "-C");
1712                         break;
1713                 case 'v':
1714                         if (debug_level < 3) {
1715                                 addargs(&args, "-v");
1716                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1717                         }
1718                         debug_level++;
1719                         break;
1720                 case 'F':
1721                 case 'o':
1722                         addargs(&args, "-%c%s", ch, optarg);
1723                         break;
1724                 case '1':
1725                         sshver = 1;
1726                         if (sftp_server == NULL)
1727                                 sftp_server = _PATH_SFTP_SERVER;
1728                         break;
1729                 case 's':
1730                         sftp_server = optarg;
1731                         break;
1732                 case 'S':
1733                         ssh_program = optarg;
1734                         replacearg(&args, 0, "%s", ssh_program);
1735                         break;
1736                 case 'b':
1737                         if (batchmode)
1738                                 fatal("Batch file already specified.");
1739
1740                         /* Allow "-" as stdin */
1741                         if (strcmp(optarg, "-") != 0 &&
1742                             (infile = fopen(optarg, "r")) == NULL)
1743                                 fatal("%s (%s).", strerror(errno), optarg);
1744                         showprogress = 0;
1745                         batchmode = 1;
1746                         addargs(&args, "-obatchmode yes");
1747                         break;
1748                 case 'P':
1749                         sftp_direct = optarg;
1750                         break;
1751                 case 'B':
1752                         copy_buffer_len = strtol(optarg, &cp, 10);
1753                         if (copy_buffer_len == 0 || *cp != '\0')
1754                                 fatal("Invalid buffer size \"%s\"", optarg);
1755                         break;
1756                 case 'R':
1757                         num_requests = strtol(optarg, &cp, 10);
1758                         if (num_requests == 0 || *cp != '\0')
1759                                 fatal("Invalid number of requests \"%s\"",
1760                                     optarg);
1761                         break;
1762                 case 'h':
1763                 default:
1764                         usage();
1765                 }
1766         }
1767
1768         if (!isatty(STDERR_FILENO))
1769                 showprogress = 0;
1770
1771         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1772
1773         if (sftp_direct == NULL) {
1774                 if (optind == argc || argc > (optind + 2))
1775                         usage();
1776
1777                 userhost = xstrdup(argv[optind]);
1778                 file2 = argv[optind+1];
1779
1780                 if ((host = strrchr(userhost, '@')) == NULL)
1781                         host = userhost;
1782                 else {
1783                         *host++ = '\0';
1784                         if (!userhost[0]) {
1785                                 fprintf(stderr, "Missing username\n");
1786                                 usage();
1787                         }
1788                         addargs(&args, "-l%s", userhost);
1789                 }
1790
1791                 if ((cp = colon(host)) != NULL) {
1792                         *cp++ = '\0';
1793                         file1 = cp;
1794                 }
1795
1796                 host = cleanhostname(host);
1797                 if (!*host) {
1798                         fprintf(stderr, "Missing hostname\n");
1799                         usage();
1800                 }
1801
1802                 addargs(&args, "-oProtocol %d", sshver);
1803
1804                 /* no subsystem if the server-spec contains a '/' */
1805                 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1806                         addargs(&args, "-s");
1807
1808                 addargs(&args, "%s", host);
1809                 addargs(&args, "%s", (sftp_server != NULL ?
1810                     sftp_server : "sftp"));
1811
1812                 if (!batchmode)
1813                         fprintf(stderr, "Connecting to %s...\n", host);
1814                 connect_to_server(ssh_program, args.list, &in, &out);
1815         } else {
1816                 args.list = NULL;
1817                 addargs(&args, "sftp-server");
1818
1819                 if (!batchmode)
1820                         fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1821                 connect_to_server(sftp_direct, args.list, &in, &out);
1822         }
1823         freeargs(&args);
1824
1825         err = interactive_loop(in, out, file1, file2);
1826
1827 #if !defined(USE_PIPES)
1828         shutdown(in, SHUT_RDWR);
1829         shutdown(out, SHUT_RDWR);
1830 #endif
1831
1832         close(in);
1833         close(out);
1834         if (batchmode)
1835                 fclose(infile);
1836
1837         while (waitpid(sshpid, NULL, 0) == -1)
1838                 if (errno != EINTR)
1839                         fatal("Couldn't wait for ssh process: %s",
1840                             strerror(errno));
1841
1842         exit(err == 0 ? 0 : 1);
1843 }