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