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