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