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