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