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