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