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