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