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