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