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