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