1 /* 2 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include "includes.h" 18 19 RCSID("$OpenBSD: sftp.c,v 1.45 2004/03/03 09:31:20 djm Exp $"); 20 21 #include "buffer.h" 22 #include "xmalloc.h" 23 #include "log.h" 24 #include "pathnames.h" 25 #include "misc.h" 26 27 #include "sftp.h" 28 #include "sftp-common.h" 29 #include "sftp-client.h" 30 31 /* File to read commands from */ 32 FILE* infile; 33 34 /* Are we in batchfile mode? */ 35 int batchmode = 0; 36 37 /* Size of buffer used when copying files */ 38 size_t copy_buffer_len = 32768; 39 40 /* Number of concurrent outstanding requests */ 41 size_t num_requests = 16; 42 43 /* PID of ssh transport process */ 44 static pid_t sshpid = -1; 45 46 /* This is set to 0 if the progressmeter is not desired. */ 47 int showprogress = 1; 48 49 int remote_glob(struct sftp_conn *, const char *, int, 50 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 51 52 #ifdef HAVE___PROGNAME 53 extern char *__progname; 54 #else 55 char *__progname; 56 #endif 57 58 /* Separators for interactive commands */ 59 #define WHITESPACE " \t\r\n" 60 61 /* Define what type of ls view (0 - multi-column) */ 62 #define LONG_VIEW 1 /* Full view ala ls -l */ 63 #define SHORT_VIEW 2 /* Single row view ala ls -1 */ 64 65 /* Commands for interactive mode */ 66 #define I_CHDIR 1 67 #define I_CHGRP 2 68 #define I_CHMOD 3 69 #define I_CHOWN 4 70 #define I_GET 5 71 #define I_HELP 6 72 #define I_LCHDIR 7 73 #define I_LLS 8 74 #define I_LMKDIR 9 75 #define I_LPWD 10 76 #define I_LS 11 77 #define I_LUMASK 12 78 #define I_MKDIR 13 79 #define I_PUT 14 80 #define I_PWD 15 81 #define I_QUIT 16 82 #define I_RENAME 17 83 #define I_RM 18 84 #define I_RMDIR 19 85 #define I_SHELL 20 86 #define I_SYMLINK 21 87 #define I_VERSION 22 88 #define I_PROGRESS 23 89 90 struct CMD { 91 const char *c; 92 const int n; 93 }; 94 95 static const struct CMD cmds[] = { 96 { "bye", I_QUIT }, 97 { "cd", I_CHDIR }, 98 { "chdir", I_CHDIR }, 99 { "chgrp", I_CHGRP }, 100 { "chmod", I_CHMOD }, 101 { "chown", I_CHOWN }, 102 { "dir", I_LS }, 103 { "exit", I_QUIT }, 104 { "get", I_GET }, 105 { "mget", I_GET }, 106 { "help", I_HELP }, 107 { "lcd", I_LCHDIR }, 108 { "lchdir", I_LCHDIR }, 109 { "lls", I_LLS }, 110 { "lmkdir", I_LMKDIR }, 111 { "ln", I_SYMLINK }, 112 { "lpwd", I_LPWD }, 113 { "ls", I_LS }, 114 { "lumask", I_LUMASK }, 115 { "mkdir", I_MKDIR }, 116 { "progress", I_PROGRESS }, 117 { "put", I_PUT }, 118 { "mput", I_PUT }, 119 { "pwd", I_PWD }, 120 { "quit", I_QUIT }, 121 { "rename", I_RENAME }, 122 { "rm", I_RM }, 123 { "rmdir", I_RMDIR }, 124 { "symlink", I_SYMLINK }, 125 { "version", I_VERSION }, 126 { "!", I_SHELL }, 127 { "?", I_HELP }, 128 { NULL, -1} 129 }; 130 131 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 132 133 static void 134 help(void) 135 { 136 printf("Available commands:\n"); 137 printf("cd path Change remote directory to 'path'\n"); 138 printf("lcd path Change local directory to 'path'\n"); 139 printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 140 printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 141 printf("chown own path Change owner of file 'path' to 'own'\n"); 142 printf("help Display this help text\n"); 143 printf("get remote-path [local-path] Download file\n"); 144 printf("lls [ls-options [path]] Display local directory listing\n"); 145 printf("ln oldpath newpath Symlink remote file\n"); 146 printf("lmkdir path Create local directory\n"); 147 printf("lpwd Print local working directory\n"); 148 printf("ls [path] Display remote directory listing\n"); 149 printf("lumask umask Set local umask to 'umask'\n"); 150 printf("mkdir path Create remote directory\n"); 151 printf("progress Toggle display of progress meter\n"); 152 printf("put local-path [remote-path] Upload file\n"); 153 printf("pwd Display remote working directory\n"); 154 printf("exit Quit sftp\n"); 155 printf("quit Quit sftp\n"); 156 printf("rename oldpath newpath Rename remote file\n"); 157 printf("rmdir path Remove remote directory\n"); 158 printf("rm path Delete remote file\n"); 159 printf("symlink oldpath newpath Symlink remote file\n"); 160 printf("version Show SFTP version\n"); 161 printf("!command Execute 'command' in local shell\n"); 162 printf("! Escape to local shell\n"); 163 printf("? Synonym for help\n"); 164 } 165 166 static void 167 local_do_shell(const char *args) 168 { 169 int status; 170 char *shell; 171 pid_t pid; 172 173 if (!*args) 174 args = NULL; 175 176 if ((shell = getenv("SHELL")) == NULL) 177 shell = _PATH_BSHELL; 178 179 if ((pid = fork()) == -1) 180 fatal("Couldn't fork: %s", strerror(errno)); 181 182 if (pid == 0) { 183 /* XXX: child has pipe fds to ssh subproc open - issue? */ 184 if (args) { 185 debug3("Executing %s -c \"%s\"", shell, args); 186 execl(shell, shell, "-c", args, (char *)NULL); 187 } else { 188 debug3("Executing %s", shell); 189 execl(shell, shell, (char *)NULL); 190 } 191 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 192 strerror(errno)); 193 _exit(1); 194 } 195 while (waitpid(pid, &status, 0) == -1) 196 if (errno != EINTR) 197 fatal("Couldn't wait for child: %s", strerror(errno)); 198 if (!WIFEXITED(status)) 199 error("Shell exited abormally"); 200 else if (WEXITSTATUS(status)) 201 error("Shell exited with status %d", WEXITSTATUS(status)); 202 } 203 204 static void 205 local_do_ls(const char *args) 206 { 207 if (!args || !*args) 208 local_do_shell(_PATH_LS); 209 else { 210 int len = strlen(_PATH_LS " ") + strlen(args) + 1; 211 char *buf = xmalloc(len); 212 213 /* XXX: quoting - rip quoting code from ftp? */ 214 snprintf(buf, len, _PATH_LS " %s", args); 215 local_do_shell(buf); 216 xfree(buf); 217 } 218 } 219 220 /* Strip one path (usually the pwd) from the start of another */ 221 static char * 222 path_strip(char *path, char *strip) 223 { 224 size_t len; 225 226 if (strip == NULL) 227 return (xstrdup(path)); 228 229 len = strlen(strip); 230 if (strip != NULL && strncmp(path, strip, len) == 0) { 231 if (strip[len - 1] != '/' && path[len] == '/') 232 len++; 233 return (xstrdup(path + len)); 234 } 235 236 return (xstrdup(path)); 237 } 238 239 static char * 240 path_append(char *p1, char *p2) 241 { 242 char *ret; 243 int len = strlen(p1) + strlen(p2) + 2; 244 245 ret = xmalloc(len); 246 strlcpy(ret, p1, len); 247 if (p1[strlen(p1) - 1] != '/') 248 strlcat(ret, "/", len); 249 strlcat(ret, p2, len); 250 251 return(ret); 252 } 253 254 static char * 255 make_absolute(char *p, char *pwd) 256 { 257 char *abs; 258 259 /* Derelativise */ 260 if (p && p[0] != '/') { 261 abs = path_append(pwd, p); 262 xfree(p); 263 return(abs); 264 } else 265 return(p); 266 } 267 268 static int 269 infer_path(const char *p, char **ifp) 270 { 271 char *cp; 272 273 cp = strrchr(p, '/'); 274 if (cp == NULL) { 275 *ifp = xstrdup(p); 276 return(0); 277 } 278 279 if (!cp[1]) { 280 error("Invalid path"); 281 return(-1); 282 } 283 284 *ifp = xstrdup(cp + 1); 285 return(0); 286 } 287 288 static int 289 parse_getput_flags(const char **cpp, int *pflag) 290 { 291 const char *cp = *cpp; 292 293 /* Check for flags */ 294 if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { 295 switch (cp[1]) { 296 case 'p': 297 case 'P': 298 *pflag = 1; 299 break; 300 default: 301 error("Invalid flag -%c", cp[1]); 302 return(-1); 303 } 304 cp += 2; 305 *cpp = cp + strspn(cp, WHITESPACE); 306 } 307 308 return(0); 309 } 310 311 static int 312 parse_ls_flags(const char **cpp, int *lflag) 313 { 314 const char *cp = *cpp; 315 316 /* Check for flags */ 317 if (cp++[0] == '-') { 318 for(; strchr(WHITESPACE, *cp) == NULL; cp++) { 319 switch (*cp) { 320 case 'l': 321 *lflag = LONG_VIEW; 322 break; 323 case '1': 324 *lflag = SHORT_VIEW; 325 break; 326 default: 327 error("Invalid flag -%c", *cp); 328 return(-1); 329 } 330 } 331 *cpp = cp + strspn(cp, WHITESPACE); 332 } 333 334 return(0); 335 } 336 337 static int 338 get_pathname(const char **cpp, char **path) 339 { 340 const char *cp = *cpp, *end; 341 char quot; 342 int i, j; 343 344 cp += strspn(cp, WHITESPACE); 345 if (!*cp) { 346 *cpp = cp; 347 *path = NULL; 348 return (0); 349 } 350 351 *path = xmalloc(strlen(cp) + 1); 352 353 /* Check for quoted filenames */ 354 if (*cp == '\"' || *cp == '\'') { 355 quot = *cp++; 356 357 /* Search for terminating quote, unescape some chars */ 358 for (i = j = 0; i <= strlen(cp); i++) { 359 if (cp[i] == quot) { /* Found quote */ 360 i++; 361 (*path)[j] = '\0'; 362 break; 363 } 364 if (cp[i] == '\0') { /* End of string */ 365 error("Unterminated quote"); 366 goto fail; 367 } 368 if (cp[i] == '\\') { /* Escaped characters */ 369 i++; 370 if (cp[i] != '\'' && cp[i] != '\"' && 371 cp[i] != '\\') { 372 error("Bad escaped character '\%c'", 373 cp[i]); 374 goto fail; 375 } 376 } 377 (*path)[j++] = cp[i]; 378 } 379 380 if (j == 0) { 381 error("Empty quotes"); 382 goto fail; 383 } 384 *cpp = cp + i + strspn(cp + i, WHITESPACE); 385 } else { 386 /* Read to end of filename */ 387 end = strpbrk(cp, WHITESPACE); 388 if (end == NULL) 389 end = strchr(cp, '\0'); 390 *cpp = end + strspn(end, WHITESPACE); 391 392 memcpy(*path, cp, end - cp); 393 (*path)[end - cp] = '\0'; 394 } 395 return (0); 396 397 fail: 398 xfree(*path); 399 *path = NULL; 400 return (-1); 401 } 402 403 static int 404 is_dir(char *path) 405 { 406 struct stat sb; 407 408 /* XXX: report errors? */ 409 if (stat(path, &sb) == -1) 410 return(0); 411 412 return(sb.st_mode & S_IFDIR); 413 } 414 415 static int 416 is_reg(char *path) 417 { 418 struct stat sb; 419 420 if (stat(path, &sb) == -1) 421 fatal("stat %s: %s", path, strerror(errno)); 422 423 return(S_ISREG(sb.st_mode)); 424 } 425 426 static int 427 remote_is_dir(struct sftp_conn *conn, char *path) 428 { 429 Attrib *a; 430 431 /* XXX: report errors? */ 432 if ((a = do_stat(conn, path, 1)) == NULL) 433 return(0); 434 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 435 return(0); 436 return(a->perm & S_IFDIR); 437 } 438 439 static int 440 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 441 { 442 char *abs_src = NULL; 443 char *abs_dst = NULL; 444 char *tmp; 445 glob_t g; 446 int err = 0; 447 int i; 448 449 abs_src = xstrdup(src); 450 abs_src = make_absolute(abs_src, pwd); 451 452 memset(&g, 0, sizeof(g)); 453 debug3("Looking up %s", abs_src); 454 if (remote_glob(conn, abs_src, 0, NULL, &g)) { 455 error("File \"%s\" not found.", abs_src); 456 err = -1; 457 goto out; 458 } 459 460 /* If multiple matches, dst must be a directory or unspecified */ 461 if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 462 error("Multiple files match, but \"%s\" is not a directory", 463 dst); 464 err = -1; 465 goto out; 466 } 467 468 for (i = 0; g.gl_pathv[i]; i++) { 469 if (infer_path(g.gl_pathv[i], &tmp)) { 470 err = -1; 471 goto out; 472 } 473 474 if (g.gl_matchc == 1 && dst) { 475 /* If directory specified, append filename */ 476 if (is_dir(dst)) { 477 if (infer_path(g.gl_pathv[0], &tmp)) { 478 err = 1; 479 goto out; 480 } 481 abs_dst = path_append(dst, tmp); 482 xfree(tmp); 483 } else 484 abs_dst = xstrdup(dst); 485 } else if (dst) { 486 abs_dst = path_append(dst, tmp); 487 xfree(tmp); 488 } else 489 abs_dst = tmp; 490 491 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 492 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 493 err = -1; 494 xfree(abs_dst); 495 abs_dst = NULL; 496 } 497 498 out: 499 xfree(abs_src); 500 if (abs_dst) 501 xfree(abs_dst); 502 globfree(&g); 503 return(err); 504 } 505 506 static int 507 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 508 { 509 char *tmp_dst = NULL; 510 char *abs_dst = NULL; 511 char *tmp; 512 glob_t g; 513 int err = 0; 514 int i; 515 516 if (dst) { 517 tmp_dst = xstrdup(dst); 518 tmp_dst = make_absolute(tmp_dst, pwd); 519 } 520 521 memset(&g, 0, sizeof(g)); 522 debug3("Looking up %s", src); 523 if (glob(src, 0, NULL, &g)) { 524 error("File \"%s\" not found.", src); 525 err = -1; 526 goto out; 527 } 528 529 /* If multiple matches, dst may be directory or unspecified */ 530 if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 531 error("Multiple files match, but \"%s\" is not a directory", 532 tmp_dst); 533 err = -1; 534 goto out; 535 } 536 537 for (i = 0; g.gl_pathv[i]; i++) { 538 if (!is_reg(g.gl_pathv[i])) { 539 error("skipping non-regular file %s", 540 g.gl_pathv[i]); 541 continue; 542 } 543 if (infer_path(g.gl_pathv[i], &tmp)) { 544 err = -1; 545 goto out; 546 } 547 548 if (g.gl_matchc == 1 && tmp_dst) { 549 /* If directory specified, append filename */ 550 if (remote_is_dir(conn, tmp_dst)) { 551 if (infer_path(g.gl_pathv[0], &tmp)) { 552 err = 1; 553 goto out; 554 } 555 abs_dst = path_append(tmp_dst, tmp); 556 xfree(tmp); 557 } else 558 abs_dst = xstrdup(tmp_dst); 559 560 } else if (tmp_dst) { 561 abs_dst = path_append(tmp_dst, tmp); 562 xfree(tmp); 563 } else 564 abs_dst = make_absolute(tmp, pwd); 565 566 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 567 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 568 err = -1; 569 } 570 571 out: 572 if (abs_dst) 573 xfree(abs_dst); 574 if (tmp_dst) 575 xfree(tmp_dst); 576 globfree(&g); 577 return(err); 578 } 579 580 static int 581 sdirent_comp(const void *aa, const void *bb) 582 { 583 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 584 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 585 586 return (strcmp(a->filename, b->filename)); 587 } 588 589 /* sftp ls.1 replacement for directories */ 590 static int 591 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 592 { 593 int n, c = 1, colspace = 0, columns = 1; 594 SFTP_DIRENT **d; 595 596 if ((n = do_readdir(conn, path, &d)) != 0) 597 return (n); 598 599 if (!(lflag & SHORT_VIEW)) { 600 int m = 0, width = 80; 601 struct winsize ws; 602 char *tmp; 603 604 /* Count entries for sort and find longest filename */ 605 for (n = 0; d[n] != NULL; n++) 606 m = MAX(m, strlen(d[n]->filename)); 607 608 /* Add any subpath that also needs to be counted */ 609 tmp = path_strip(path, strip_path); 610 m += strlen(tmp); 611 xfree(tmp); 612 613 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 614 width = ws.ws_col; 615 616 columns = width / (m + 2); 617 columns = MAX(columns, 1); 618 colspace = width / columns; 619 colspace = MIN(colspace, width); 620 } 621 622 qsort(d, n, sizeof(*d), sdirent_comp); 623 624 for (n = 0; d[n] != NULL; n++) { 625 char *tmp, *fname; 626 627 tmp = path_append(path, d[n]->filename); 628 fname = path_strip(tmp, strip_path); 629 xfree(tmp); 630 631 if (lflag & LONG_VIEW) { 632 char *lname; 633 struct stat sb; 634 635 memset(&sb, 0, sizeof(sb)); 636 attrib_to_stat(&d[n]->a, &sb); 637 lname = ls_file(fname, &sb, 1); 638 printf("%s\n", lname); 639 xfree(lname); 640 } else { 641 printf("%-*s", colspace, fname); 642 if (c >= columns) { 643 printf("\n"); 644 c = 1; 645 } else 646 c++; 647 } 648 649 xfree(fname); 650 } 651 652 if (!(lflag & LONG_VIEW) && (c != 1)) 653 printf("\n"); 654 655 free_sftp_dirents(d); 656 return (0); 657 } 658 659 /* sftp ls.1 replacement which handles path globs */ 660 static int 661 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 662 int lflag) 663 { 664 glob_t g; 665 int i, c = 1, colspace = 0, columns = 1; 666 Attrib *a; 667 668 memset(&g, 0, sizeof(g)); 669 670 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 671 NULL, &g)) { 672 error("Can't ls: \"%s\" not found", path); 673 return (-1); 674 } 675 676 /* 677 * If the glob returns a single match, which is the same as the 678 * input glob, and it is a directory, then just list its contents 679 */ 680 if (g.gl_pathc == 1 && 681 strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) { 682 if ((a = do_lstat(conn, path, 1)) == NULL) { 683 globfree(&g); 684 return (-1); 685 } 686 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 687 S_ISDIR(a->perm)) { 688 globfree(&g); 689 return (do_ls_dir(conn, path, strip_path, lflag)); 690 } 691 } 692 693 if (!(lflag & SHORT_VIEW)) { 694 int m = 0, width = 80; 695 struct winsize ws; 696 697 /* Count entries for sort and find longest filename */ 698 for (i = 0; g.gl_pathv[i]; i++) 699 m = MAX(m, strlen(g.gl_pathv[i])); 700 701 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 702 width = ws.ws_col; 703 704 columns = width / (m + 2); 705 columns = MAX(columns, 1); 706 colspace = width / columns; 707 } 708 709 for (i = 0; g.gl_pathv[i]; i++) { 710 char *fname; 711 712 fname = path_strip(g.gl_pathv[i], strip_path); 713 714 if (lflag & LONG_VIEW) { 715 char *lname; 716 struct stat sb; 717 718 /* 719 * XXX: this is slow - 1 roundtrip per path 720 * A solution to this is to fork glob() and 721 * build a sftp specific version which keeps the 722 * attribs (which currently get thrown away) 723 * that the server returns as well as the filenames. 724 */ 725 memset(&sb, 0, sizeof(sb)); 726 a = do_lstat(conn, g.gl_pathv[i], 1); 727 if (a != NULL) 728 attrib_to_stat(a, &sb); 729 lname = ls_file(fname, &sb, 1); 730 printf("%s\n", lname); 731 xfree(lname); 732 } else { 733 printf("%-*s", colspace, fname); 734 if (c >= columns) { 735 printf("\n"); 736 c = 1; 737 } else 738 c++; 739 } 740 xfree(fname); 741 } 742 743 if (!(lflag & LONG_VIEW) && (c != 1)) 744 printf("\n"); 745 746 if (g.gl_pathc) 747 globfree(&g); 748 749 return (0); 750 } 751 752 static int 753 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, 754 unsigned long *n_arg, char **path1, char **path2) 755 { 756 const char *cmd, *cp = *cpp; 757 char *cp2; 758 int base = 0; 759 long l; 760 int i, cmdnum; 761 762 /* Skip leading whitespace */ 763 cp = cp + strspn(cp, WHITESPACE); 764 765 /* Ignore blank lines and lines which begin with comment '#' char */ 766 if (*cp == '\0' || *cp == '#') 767 return (0); 768 769 /* Check for leading '-' (disable error processing) */ 770 *iflag = 0; 771 if (*cp == '-') { 772 *iflag = 1; 773 cp++; 774 } 775 776 /* Figure out which command we have */ 777 for (i = 0; cmds[i].c; i++) { 778 int cmdlen = strlen(cmds[i].c); 779 780 /* Check for command followed by whitespace */ 781 if (!strncasecmp(cp, cmds[i].c, cmdlen) && 782 strchr(WHITESPACE, cp[cmdlen])) { 783 cp += cmdlen; 784 cp = cp + strspn(cp, WHITESPACE); 785 break; 786 } 787 } 788 cmdnum = cmds[i].n; 789 cmd = cmds[i].c; 790 791 /* Special case */ 792 if (*cp == '!') { 793 cp++; 794 cmdnum = I_SHELL; 795 } else if (cmdnum == -1) { 796 error("Invalid command."); 797 return (-1); 798 } 799 800 /* Get arguments and parse flags */ 801 *lflag = *pflag = *n_arg = 0; 802 *path1 = *path2 = NULL; 803 switch (cmdnum) { 804 case I_GET: 805 case I_PUT: 806 if (parse_getput_flags(&cp, pflag)) 807 return(-1); 808 /* Get first pathname (mandatory) */ 809 if (get_pathname(&cp, path1)) 810 return(-1); 811 if (*path1 == NULL) { 812 error("You must specify at least one path after a " 813 "%s command.", cmd); 814 return(-1); 815 } 816 /* Try to get second pathname (optional) */ 817 if (get_pathname(&cp, path2)) 818 return(-1); 819 break; 820 case I_RENAME: 821 case I_SYMLINK: 822 if (get_pathname(&cp, path1)) 823 return(-1); 824 if (get_pathname(&cp, path2)) 825 return(-1); 826 if (!*path1 || !*path2) { 827 error("You must specify two paths after a %s " 828 "command.", cmd); 829 return(-1); 830 } 831 break; 832 case I_RM: 833 case I_MKDIR: 834 case I_RMDIR: 835 case I_CHDIR: 836 case I_LCHDIR: 837 case I_LMKDIR: 838 /* Get pathname (mandatory) */ 839 if (get_pathname(&cp, path1)) 840 return(-1); 841 if (*path1 == NULL) { 842 error("You must specify a path after a %s command.", 843 cmd); 844 return(-1); 845 } 846 break; 847 case I_LS: 848 if (parse_ls_flags(&cp, lflag)) 849 return(-1); 850 /* Path is optional */ 851 if (get_pathname(&cp, path1)) 852 return(-1); 853 break; 854 case I_LLS: 855 case I_SHELL: 856 /* Uses the rest of the line */ 857 break; 858 case I_LUMASK: 859 base = 8; 860 case I_CHMOD: 861 base = 8; 862 case I_CHOWN: 863 case I_CHGRP: 864 /* Get numeric arg (mandatory) */ 865 l = strtol(cp, &cp2, base); 866 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 867 errno == ERANGE) || l < 0) { 868 error("You must supply a numeric argument " 869 "to the %s command.", cmd); 870 return(-1); 871 } 872 cp = cp2; 873 *n_arg = l; 874 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 875 break; 876 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { 877 error("You must supply a numeric argument " 878 "to the %s command.", cmd); 879 return(-1); 880 } 881 cp += strspn(cp, WHITESPACE); 882 883 /* Get pathname (mandatory) */ 884 if (get_pathname(&cp, path1)) 885 return(-1); 886 if (*path1 == NULL) { 887 error("You must specify a path after a %s command.", 888 cmd); 889 return(-1); 890 } 891 break; 892 case I_QUIT: 893 case I_PWD: 894 case I_LPWD: 895 case I_HELP: 896 case I_VERSION: 897 case I_PROGRESS: 898 break; 899 default: 900 fatal("Command not implemented"); 901 } 902 903 *cpp = cp; 904 return(cmdnum); 905 } 906 907 static int 908 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 909 int err_abort) 910 { 911 char *path1, *path2, *tmp; 912 int pflag, lflag, iflag, cmdnum, i; 913 unsigned long n_arg; 914 Attrib a, *aa; 915 char path_buf[MAXPATHLEN]; 916 int err = 0; 917 glob_t g; 918 919 path1 = path2 = NULL; 920 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 921 &path1, &path2); 922 923 if (iflag != 0) 924 err_abort = 0; 925 926 memset(&g, 0, sizeof(g)); 927 928 /* Perform command */ 929 switch (cmdnum) { 930 case 0: 931 /* Blank line */ 932 break; 933 case -1: 934 /* Unrecognized command */ 935 err = -1; 936 break; 937 case I_GET: 938 err = process_get(conn, path1, path2, *pwd, pflag); 939 break; 940 case I_PUT: 941 err = process_put(conn, path1, path2, *pwd, pflag); 942 break; 943 case I_RENAME: 944 path1 = make_absolute(path1, *pwd); 945 path2 = make_absolute(path2, *pwd); 946 err = do_rename(conn, path1, path2); 947 break; 948 case I_SYMLINK: 949 path2 = make_absolute(path2, *pwd); 950 err = do_symlink(conn, path1, path2); 951 break; 952 case I_RM: 953 path1 = make_absolute(path1, *pwd); 954 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 955 for (i = 0; g.gl_pathv[i]; i++) { 956 printf("Removing %s\n", g.gl_pathv[i]); 957 err = do_rm(conn, g.gl_pathv[i]); 958 if (err != 0 && err_abort) 959 break; 960 } 961 break; 962 case I_MKDIR: 963 path1 = make_absolute(path1, *pwd); 964 attrib_clear(&a); 965 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 966 a.perm = 0777; 967 err = do_mkdir(conn, path1, &a); 968 break; 969 case I_RMDIR: 970 path1 = make_absolute(path1, *pwd); 971 err = do_rmdir(conn, path1); 972 break; 973 case I_CHDIR: 974 path1 = make_absolute(path1, *pwd); 975 if ((tmp = do_realpath(conn, path1)) == NULL) { 976 err = 1; 977 break; 978 } 979 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 980 xfree(tmp); 981 err = 1; 982 break; 983 } 984 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 985 error("Can't change directory: Can't check target"); 986 xfree(tmp); 987 err = 1; 988 break; 989 } 990 if (!S_ISDIR(aa->perm)) { 991 error("Can't change directory: \"%s\" is not " 992 "a directory", tmp); 993 xfree(tmp); 994 err = 1; 995 break; 996 } 997 xfree(*pwd); 998 *pwd = tmp; 999 break; 1000 case I_LS: 1001 if (!path1) { 1002 do_globbed_ls(conn, *pwd, *pwd, lflag); 1003 break; 1004 } 1005 1006 /* Strip pwd off beginning of non-absolute paths */ 1007 tmp = NULL; 1008 if (*path1 != '/') 1009 tmp = *pwd; 1010 1011 path1 = make_absolute(path1, *pwd); 1012 err = do_globbed_ls(conn, path1, tmp, lflag); 1013 break; 1014 case I_LCHDIR: 1015 if (chdir(path1) == -1) { 1016 error("Couldn't change local directory to " 1017 "\"%s\": %s", path1, strerror(errno)); 1018 err = 1; 1019 } 1020 break; 1021 case I_LMKDIR: 1022 if (mkdir(path1, 0777) == -1) { 1023 error("Couldn't create local directory " 1024 "\"%s\": %s", path1, strerror(errno)); 1025 err = 1; 1026 } 1027 break; 1028 case I_LLS: 1029 local_do_ls(cmd); 1030 break; 1031 case I_SHELL: 1032 local_do_shell(cmd); 1033 break; 1034 case I_LUMASK: 1035 umask(n_arg); 1036 printf("Local umask: %03lo\n", n_arg); 1037 break; 1038 case I_CHMOD: 1039 path1 = make_absolute(path1, *pwd); 1040 attrib_clear(&a); 1041 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1042 a.perm = n_arg; 1043 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1044 for (i = 0; g.gl_pathv[i]; i++) { 1045 printf("Changing mode on %s\n", g.gl_pathv[i]); 1046 err = do_setstat(conn, g.gl_pathv[i], &a); 1047 if (err != 0 && err_abort) 1048 break; 1049 } 1050 break; 1051 case I_CHOWN: 1052 case I_CHGRP: 1053 path1 = make_absolute(path1, *pwd); 1054 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1055 for (i = 0; g.gl_pathv[i]; i++) { 1056 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1057 if (err != 0 && err_abort) 1058 break; 1059 else 1060 continue; 1061 } 1062 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1063 error("Can't get current ownership of " 1064 "remote file \"%s\"", g.gl_pathv[i]); 1065 if (err != 0 && err_abort) 1066 break; 1067 else 1068 continue; 1069 } 1070 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1071 if (cmdnum == I_CHOWN) { 1072 printf("Changing owner on %s\n", g.gl_pathv[i]); 1073 aa->uid = n_arg; 1074 } else { 1075 printf("Changing group on %s\n", g.gl_pathv[i]); 1076 aa->gid = n_arg; 1077 } 1078 err = do_setstat(conn, g.gl_pathv[i], aa); 1079 if (err != 0 && err_abort) 1080 break; 1081 } 1082 break; 1083 case I_PWD: 1084 printf("Remote working directory: %s\n", *pwd); 1085 break; 1086 case I_LPWD: 1087 if (!getcwd(path_buf, sizeof(path_buf))) { 1088 error("Couldn't get local cwd: %s", strerror(errno)); 1089 err = -1; 1090 break; 1091 } 1092 printf("Local working directory: %s\n", path_buf); 1093 break; 1094 case I_QUIT: 1095 /* Processed below */ 1096 break; 1097 case I_HELP: 1098 help(); 1099 break; 1100 case I_VERSION: 1101 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1102 break; 1103 case I_PROGRESS: 1104 showprogress = !showprogress; 1105 if (showprogress) 1106 printf("Progress meter enabled\n"); 1107 else 1108 printf("Progress meter disabled\n"); 1109 break; 1110 default: 1111 fatal("%d is not implemented", cmdnum); 1112 } 1113 1114 if (g.gl_pathc) 1115 globfree(&g); 1116 if (path1) 1117 xfree(path1); 1118 if (path2) 1119 xfree(path2); 1120 1121 /* If an unignored error occurs in batch mode we should abort. */ 1122 if (err_abort && err != 0) 1123 return (-1); 1124 else if (cmdnum == I_QUIT) 1125 return (1); 1126 1127 return (0); 1128 } 1129 1130 int 1131 interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1132 { 1133 char *pwd; 1134 char *dir = NULL; 1135 char cmd[2048]; 1136 struct sftp_conn *conn; 1137 int err; 1138 1139 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1140 if (conn == NULL) 1141 fatal("Couldn't initialise connection to server"); 1142 1143 pwd = do_realpath(conn, "."); 1144 if (pwd == NULL) 1145 fatal("Need cwd"); 1146 1147 if (file1 != NULL) { 1148 dir = xstrdup(file1); 1149 dir = make_absolute(dir, pwd); 1150 1151 if (remote_is_dir(conn, dir) && file2 == NULL) { 1152 printf("Changing to: %s\n", dir); 1153 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1154 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) 1155 return (-1); 1156 } else { 1157 if (file2 == NULL) 1158 snprintf(cmd, sizeof cmd, "get %s", dir); 1159 else 1160 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1161 file2); 1162 1163 err = parse_dispatch_command(conn, cmd, &pwd, 1); 1164 xfree(dir); 1165 xfree(pwd); 1166 return (err); 1167 } 1168 xfree(dir); 1169 } 1170 1171 #if HAVE_SETVBUF 1172 setvbuf(stdout, NULL, _IOLBF, 0); 1173 setvbuf(infile, NULL, _IOLBF, 0); 1174 #else 1175 setlinebuf(stdout); 1176 setlinebuf(infile); 1177 #endif 1178 1179 err = 0; 1180 for (;;) { 1181 char *cp; 1182 1183 printf("sftp> "); 1184 1185 /* XXX: use libedit */ 1186 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1187 printf("\n"); 1188 break; 1189 } 1190 1191 if (batchmode) /* Echo command */ 1192 printf("%s", cmd); 1193 1194 cp = strrchr(cmd, '\n'); 1195 if (cp) 1196 *cp = '\0'; 1197 1198 err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1199 if (err != 0) 1200 break; 1201 } 1202 xfree(pwd); 1203 1204 /* err == 1 signifies normal "quit" exit */ 1205 return (err >= 0 ? 0 : -1); 1206 } 1207 1208 static void 1209 killchild(int signo) 1210 { 1211 if (sshpid > 1) 1212 kill(sshpid, signo); 1213 1214 _exit(1); 1215 } 1216 1217 static void 1218 connect_to_server(char *path, char **args, int *in, int *out) 1219 { 1220 int c_in, c_out; 1221 1222 #ifdef USE_PIPES 1223 int pin[2], pout[2]; 1224 1225 if ((pipe(pin) == -1) || (pipe(pout) == -1)) 1226 fatal("pipe: %s", strerror(errno)); 1227 *in = pin[0]; 1228 *out = pout[1]; 1229 c_in = pout[0]; 1230 c_out = pin[1]; 1231 #else /* USE_PIPES */ 1232 int inout[2]; 1233 1234 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 1235 fatal("socketpair: %s", strerror(errno)); 1236 *in = *out = inout[0]; 1237 c_in = c_out = inout[1]; 1238 #endif /* USE_PIPES */ 1239 1240 if ((sshpid = fork()) == -1) 1241 fatal("fork: %s", strerror(errno)); 1242 else if (sshpid == 0) { 1243 if ((dup2(c_in, STDIN_FILENO) == -1) || 1244 (dup2(c_out, STDOUT_FILENO) == -1)) { 1245 fprintf(stderr, "dup2: %s\n", strerror(errno)); 1246 exit(1); 1247 } 1248 close(*in); 1249 close(*out); 1250 close(c_in); 1251 close(c_out); 1252 execv(path, args); 1253 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1254 exit(1); 1255 } 1256 1257 signal(SIGTERM, killchild); 1258 signal(SIGINT, killchild); 1259 signal(SIGHUP, killchild); 1260 close(c_in); 1261 close(c_out); 1262 } 1263 1264 static void 1265 usage(void) 1266 { 1267 extern char *__progname; 1268 1269 fprintf(stderr, 1270 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1271 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1272 " [-S program] [-s subsystem | sftp_server] host\n" 1273 " %s [[user@]host[:file [file]]]\n" 1274 " %s [[user@]host[:dir[/]]]\n" 1275 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 1276 exit(1); 1277 } 1278 1279 int 1280 main(int argc, char **argv) 1281 { 1282 int in, out, ch, err; 1283 char *host, *userhost, *cp, *file2; 1284 int debug_level = 0, sshver = 2; 1285 char *file1 = NULL, *sftp_server = NULL; 1286 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1287 LogLevel ll = SYSLOG_LEVEL_INFO; 1288 arglist args; 1289 extern int optind; 1290 extern char *optarg; 1291 1292 __progname = ssh_get_progname(argv[0]); 1293 args.list = NULL; 1294 addargs(&args, "ssh"); /* overwritten with ssh_program */ 1295 addargs(&args, "-oForwardX11 no"); 1296 addargs(&args, "-oForwardAgent no"); 1297 addargs(&args, "-oClearAllForwardings yes"); 1298 1299 ll = SYSLOG_LEVEL_INFO; 1300 infile = stdin; 1301 1302 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 1303 switch (ch) { 1304 case 'C': 1305 addargs(&args, "-C"); 1306 break; 1307 case 'v': 1308 if (debug_level < 3) { 1309 addargs(&args, "-v"); 1310 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1311 } 1312 debug_level++; 1313 break; 1314 case 'F': 1315 case 'o': 1316 addargs(&args, "-%c%s", ch, optarg); 1317 break; 1318 case '1': 1319 sshver = 1; 1320 if (sftp_server == NULL) 1321 sftp_server = _PATH_SFTP_SERVER; 1322 break; 1323 case 's': 1324 sftp_server = optarg; 1325 break; 1326 case 'S': 1327 ssh_program = optarg; 1328 break; 1329 case 'b': 1330 if (batchmode) 1331 fatal("Batch file already specified."); 1332 1333 /* Allow "-" as stdin */ 1334 if (strcmp(optarg, "-") != 0 && 1335 (infile = fopen(optarg, "r")) == NULL) 1336 fatal("%s (%s).", strerror(errno), optarg); 1337 showprogress = 0; 1338 batchmode = 1; 1339 break; 1340 case 'P': 1341 sftp_direct = optarg; 1342 break; 1343 case 'B': 1344 copy_buffer_len = strtol(optarg, &cp, 10); 1345 if (copy_buffer_len == 0 || *cp != '\0') 1346 fatal("Invalid buffer size \"%s\"", optarg); 1347 break; 1348 case 'R': 1349 num_requests = strtol(optarg, &cp, 10); 1350 if (num_requests == 0 || *cp != '\0') 1351 fatal("Invalid number of requests \"%s\"", 1352 optarg); 1353 break; 1354 case 'h': 1355 default: 1356 usage(); 1357 } 1358 } 1359 1360 if (!isatty(STDERR_FILENO)) 1361 showprogress = 0; 1362 1363 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1364 1365 if (sftp_direct == NULL) { 1366 if (optind == argc || argc > (optind + 2)) 1367 usage(); 1368 1369 userhost = xstrdup(argv[optind]); 1370 file2 = argv[optind+1]; 1371 1372 if ((host = strrchr(userhost, '@')) == NULL) 1373 host = userhost; 1374 else { 1375 *host++ = '\0'; 1376 if (!userhost[0]) { 1377 fprintf(stderr, "Missing username\n"); 1378 usage(); 1379 } 1380 addargs(&args, "-l%s",userhost); 1381 } 1382 1383 if ((cp = colon(host)) != NULL) { 1384 *cp++ = '\0'; 1385 file1 = cp; 1386 } 1387 1388 host = cleanhostname(host); 1389 if (!*host) { 1390 fprintf(stderr, "Missing hostname\n"); 1391 usage(); 1392 } 1393 1394 addargs(&args, "-oProtocol %d", sshver); 1395 1396 /* no subsystem if the server-spec contains a '/' */ 1397 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1398 addargs(&args, "-s"); 1399 1400 addargs(&args, "%s", host); 1401 addargs(&args, "%s", (sftp_server != NULL ? 1402 sftp_server : "sftp")); 1403 args.list[0] = ssh_program; 1404 1405 if (!batchmode) 1406 fprintf(stderr, "Connecting to %s...\n", host); 1407 connect_to_server(ssh_program, args.list, &in, &out); 1408 } else { 1409 args.list = NULL; 1410 addargs(&args, "sftp-server"); 1411 1412 if (!batchmode) 1413 fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1414 connect_to_server(sftp_direct, args.list, &in, &out); 1415 } 1416 1417 err = interactive_loop(in, out, file1, file2); 1418 1419 #if !defined(USE_PIPES) 1420 shutdown(in, SHUT_RDWR); 1421 shutdown(out, SHUT_RDWR); 1422 #endif 1423 1424 close(in); 1425 close(out); 1426 if (batchmode) 1427 fclose(infile); 1428 1429 while (waitpid(sshpid, NULL, 0) == -1) 1430 if (errno != EINTR) 1431 fatal("Couldn't wait for ssh process: %s", 1432 strerror(errno)); 1433 1434 exit(err == 0 ? 0 : 1); 1435 } 1436