1 /* $OpenBSD: sftp.c,v 1.236 2023/09/10 23:12:32 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 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 2278 if (interactive) 2279 printf("\n"); 2280 if (interrupted) 2281 continue; 2282 break; 2283 } 2284 } else { 2285 if ((line = el_gets(el, &count)) == NULL || 2286 count <= 0) { 2287 printf("\n"); 2288 if (interrupted) 2289 continue; 2290 break; 2291 } 2292 history(hl, &hev, H_ENTER, line); 2293 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 2294 fprintf(stderr, "Error: input line too long\n"); 2295 continue; 2296 } 2297 } 2298 2299 cmd[strcspn(cmd, "\n")] = '\0'; 2300 2301 /* Handle user interrupts gracefully during commands */ 2302 interrupted = 0; 2303 ssh_signal(SIGINT, cmd_interrupt); 2304 2305 err = parse_dispatch_command(conn, cmd, &remote_path, 2306 startdir, batchmode, !interactive && el == NULL); 2307 if (err != 0) 2308 break; 2309 } 2310 ssh_signal(SIGCHLD, SIG_DFL); 2311 free(remote_path); 2312 free(startdir); 2313 free(conn); 2314 2315 if (el != NULL) 2316 el_end(el); 2317 2318 /* err == 1 signifies normal "quit" exit */ 2319 return (err >= 0 ? 0 : -1); 2320 } 2321 2322 static void 2323 connect_to_server(char *path, char **args, int *in, int *out) 2324 { 2325 int c_in, c_out, inout[2]; 2326 2327 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 2328 fatal("socketpair: %s", strerror(errno)); 2329 *in = *out = inout[0]; 2330 c_in = c_out = inout[1]; 2331 2332 if ((sshpid = fork()) == -1) 2333 fatal("fork: %s", strerror(errno)); 2334 else if (sshpid == 0) { 2335 if ((dup2(c_in, STDIN_FILENO) == -1) || 2336 (dup2(c_out, STDOUT_FILENO) == -1)) { 2337 fprintf(stderr, "dup2: %s\n", strerror(errno)); 2338 _exit(1); 2339 } 2340 close(*in); 2341 close(*out); 2342 close(c_in); 2343 close(c_out); 2344 2345 /* 2346 * The underlying ssh is in the same process group, so we must 2347 * ignore SIGINT if we want to gracefully abort commands, 2348 * otherwise the signal will make it to the ssh process and 2349 * kill it too. Contrawise, since sftp sends SIGTERMs to the 2350 * underlying ssh, it must *not* ignore that signal. 2351 */ 2352 ssh_signal(SIGINT, SIG_IGN); 2353 ssh_signal(SIGTERM, SIG_DFL); 2354 execvp(path, args); 2355 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 2356 _exit(1); 2357 } 2358 2359 ssh_signal(SIGTERM, killchild); 2360 ssh_signal(SIGINT, killchild); 2361 ssh_signal(SIGHUP, killchild); 2362 ssh_signal(SIGTSTP, suspchild); 2363 ssh_signal(SIGTTIN, suspchild); 2364 ssh_signal(SIGTTOU, suspchild); 2365 ssh_signal(SIGCHLD, sigchld_handler); 2366 close(c_in); 2367 close(c_out); 2368 } 2369 2370 static void 2371 usage(void) 2372 { 2373 extern char *__progname; 2374 2375 fprintf(stderr, 2376 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" 2377 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n" 2378 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n" 2379 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n" 2380 " [-X sftp_option] destination\n", 2381 __progname); 2382 exit(1); 2383 } 2384 2385 int 2386 main(int argc, char **argv) 2387 { 2388 int r, in, out, ch, err, tmp, port = -1, noisy = 0; 2389 char *host = NULL, *user, *cp, **cpp, *file2 = NULL; 2390 int debug_level = 0; 2391 char *file1 = NULL, *sftp_server = NULL; 2392 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 2393 const char *errstr; 2394 LogLevel ll = SYSLOG_LEVEL_INFO; 2395 arglist args; 2396 extern int optind; 2397 extern char *optarg; 2398 struct sftp_conn *conn; 2399 size_t copy_buffer_len = 0; 2400 size_t num_requests = 0; 2401 long long llv, limit_kbps = 0; 2402 2403 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 2404 sanitise_stdfd(); 2405 setlocale(LC_CTYPE, ""); 2406 2407 memset(&args, '\0', sizeof(args)); 2408 args.list = NULL; 2409 addargs(&args, "%s", ssh_program); 2410 addargs(&args, "-oForwardX11 no"); 2411 addargs(&args, "-oPermitLocalCommand no"); 2412 addargs(&args, "-oClearAllForwardings yes"); 2413 2414 ll = SYSLOG_LEVEL_INFO; 2415 infile = stdin; 2416 2417 while ((ch = getopt(argc, argv, 2418 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) { 2419 switch (ch) { 2420 /* Passed through to ssh(1) */ 2421 case 'A': 2422 case '4': 2423 case '6': 2424 case 'C': 2425 addargs(&args, "-%c", ch); 2426 break; 2427 /* Passed through to ssh(1) with argument */ 2428 case 'F': 2429 case 'J': 2430 case 'c': 2431 case 'i': 2432 case 'o': 2433 addargs(&args, "-%c", ch); 2434 addargs(&args, "%s", optarg); 2435 break; 2436 case 'q': 2437 ll = SYSLOG_LEVEL_ERROR; 2438 quiet = 1; 2439 showprogress = 0; 2440 addargs(&args, "-%c", ch); 2441 break; 2442 case 'P': 2443 port = a2port(optarg); 2444 if (port <= 0) 2445 fatal("Bad port \"%s\"\n", optarg); 2446 break; 2447 case 'v': 2448 if (debug_level < 3) { 2449 addargs(&args, "-v"); 2450 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 2451 } 2452 debug_level++; 2453 break; 2454 case '1': 2455 fatal("SSH protocol v.1 is no longer supported"); 2456 break; 2457 case '2': 2458 /* accept silently */ 2459 break; 2460 case 'a': 2461 global_aflag = 1; 2462 break; 2463 case 'B': 2464 copy_buffer_len = strtol(optarg, &cp, 10); 2465 if (copy_buffer_len == 0 || *cp != '\0') 2466 fatal("Invalid buffer size \"%s\"", optarg); 2467 break; 2468 case 'b': 2469 if (batchmode) 2470 fatal("Batch file already specified."); 2471 2472 /* Allow "-" as stdin */ 2473 if (strcmp(optarg, "-") != 0 && 2474 (infile = fopen(optarg, "r")) == NULL) 2475 fatal("%s (%s).", strerror(errno), optarg); 2476 showprogress = 0; 2477 quiet = batchmode = 1; 2478 addargs(&args, "-obatchmode yes"); 2479 break; 2480 case 'f': 2481 global_fflag = 1; 2482 break; 2483 case 'N': 2484 noisy = 1; /* Used to clear quiet mode after getopt */ 2485 break; 2486 case 'p': 2487 global_pflag = 1; 2488 break; 2489 case 'D': 2490 sftp_direct = optarg; 2491 break; 2492 case 'l': 2493 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 2494 &errstr); 2495 if (errstr != NULL) 2496 usage(); 2497 limit_kbps *= 1024; /* kbps */ 2498 break; 2499 case 'r': 2500 global_rflag = 1; 2501 break; 2502 case 'R': 2503 num_requests = strtol(optarg, &cp, 10); 2504 if (num_requests == 0 || *cp != '\0') 2505 fatal("Invalid number of requests \"%s\"", 2506 optarg); 2507 break; 2508 case 's': 2509 sftp_server = optarg; 2510 break; 2511 case 'S': 2512 ssh_program = optarg; 2513 replacearg(&args, 0, "%s", ssh_program); 2514 break; 2515 case 'X': 2516 /* Please keep in sync with ssh.c -X */ 2517 if (strncmp(optarg, "buffer=", 7) == 0) { 2518 r = scan_scaled(optarg + 7, &llv); 2519 if (r == 0 && (llv <= 0 || llv > 256 * 1024)) { 2520 r = -1; 2521 errno = EINVAL; 2522 } 2523 if (r == -1) { 2524 fatal("Invalid buffer size \"%s\": %s", 2525 optarg + 7, strerror(errno)); 2526 } 2527 copy_buffer_len = (size_t)llv; 2528 } else if (strncmp(optarg, "nrequests=", 10) == 0) { 2529 llv = strtonum(optarg + 10, 1, 256 * 1024, 2530 &errstr); 2531 if (errstr != NULL) { 2532 fatal("Invalid number of requests " 2533 "\"%s\": %s", optarg + 10, errstr); 2534 } 2535 num_requests = (size_t)llv; 2536 } else { 2537 fatal("Invalid -X option"); 2538 } 2539 break; 2540 case 'h': 2541 default: 2542 usage(); 2543 } 2544 } 2545 2546 /* Do this last because we want the user to be able to override it */ 2547 addargs(&args, "-oForwardAgent no"); 2548 2549 if (!isatty(STDERR_FILENO)) 2550 showprogress = 0; 2551 2552 if (noisy) 2553 quiet = 0; 2554 2555 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 2556 2557 if (sftp_direct == NULL) { 2558 if (optind == argc || argc > (optind + 2)) 2559 usage(); 2560 argv += optind; 2561 2562 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) { 2563 case -1: 2564 usage(); 2565 break; 2566 case 0: 2567 if (tmp != -1) 2568 port = tmp; 2569 break; 2570 default: 2571 /* Try with user, host and path. */ 2572 if (parse_user_host_path(*argv, &user, &host, 2573 &file1) == 0) 2574 break; 2575 /* Try with user and host. */ 2576 if (parse_user_host_port(*argv, &user, &host, NULL) 2577 == 0) 2578 break; 2579 /* Treat as a plain hostname. */ 2580 host = xstrdup(*argv); 2581 host = cleanhostname(host); 2582 break; 2583 } 2584 file2 = *(argv + 1); 2585 2586 if (!*host) { 2587 fprintf(stderr, "Missing hostname\n"); 2588 usage(); 2589 } 2590 2591 if (port != -1) 2592 addargs(&args, "-oPort %d", port); 2593 if (user != NULL) { 2594 addargs(&args, "-l"); 2595 addargs(&args, "%s", user); 2596 } 2597 2598 /* no subsystem if the server-spec contains a '/' */ 2599 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 2600 addargs(&args, "-s"); 2601 2602 addargs(&args, "--"); 2603 addargs(&args, "%s", host); 2604 addargs(&args, "%s", (sftp_server != NULL ? 2605 sftp_server : "sftp")); 2606 2607 connect_to_server(ssh_program, args.list, &in, &out); 2608 } else { 2609 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0) 2610 fatal_r(r, "Parse -D arguments"); 2611 if (cpp[0] == 0) 2612 fatal("No sftp server specified via -D"); 2613 connect_to_server(cpp[0], cpp, &in, &out); 2614 argv_free(cpp, tmp); 2615 } 2616 freeargs(&args); 2617 2618 conn = sftp_init(in, out, copy_buffer_len, num_requests, limit_kbps); 2619 if (conn == NULL) 2620 fatal("Couldn't initialise connection to server"); 2621 2622 if (!quiet) { 2623 if (sftp_direct == NULL) 2624 fprintf(stderr, "Connected to %s.\n", host); 2625 else 2626 fprintf(stderr, "Attached to %s.\n", sftp_direct); 2627 } 2628 2629 err = interactive_loop(conn, file1, file2); 2630 2631 close(in); 2632 close(out); 2633 if (batchmode) 2634 fclose(infile); 2635 2636 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1) 2637 if (errno != EINTR) 2638 fatal("Couldn't wait for ssh process: %s", 2639 strerror(errno)); 2640 2641 exit(err == 0 ? 0 : 1); 2642 } 2643