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