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