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