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