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