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