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