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