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