1 #ifndef lint 2 static char sccsid[] = "@(#)cmds.c 4.5 (Berkeley) 03/29/83"; 3 #endif 4 5 /* 6 * FTP User Program -- Command Routines. 7 */ 8 #include <sys/param.h> 9 #include <sys/socket.h> 10 11 #include <signal.h> 12 #include <stdio.h> 13 #include <errno.h> 14 #include <netdb.h> 15 #include <stat.h> 16 17 #include "ftp.h" 18 #include "ftp_var.h" 19 20 extern char *globerr; 21 extern char **glob(); 22 extern char *home; 23 extern short gflag; 24 extern char *remglob(); 25 extern char *getenv(); 26 extern char *index(); 27 extern char *rindex(); 28 29 /* 30 * Connect to peer server and 31 * auto-login, if possible. 32 */ 33 setpeer(argc, argv) 34 int argc; 35 char *argv[]; 36 { 37 struct hostent *host, *hookup(); 38 int port; 39 40 if (connected) { 41 printf("Already connected to %s, use disconnect first.\n", 42 hostname); 43 return; 44 } 45 if (argc < 2) { 46 strcat(line, " "); 47 printf("(to) "); 48 gets(&line[strlen(line)]); 49 makeargv(); 50 argc = margc; 51 argv = margv; 52 } 53 if (argc > 3) { 54 printf("usage: %s host-name [port]\n", argv[0]); 55 return; 56 } 57 port = sp->s_port; 58 if (argc > 2) { 59 port = atoi(argv[2]); 60 if (port <= 0) { 61 printf("%s: bad port number-- %s\n", argv[1], argv[2]); 62 printf ("usage: %s host-name [port]\n", argv[0]); 63 return; 64 } 65 port = htons(port); 66 } 67 host = hookup(argv[1], port); 68 if (host) { 69 connected = 1; 70 if (autologin) 71 login(host); 72 } 73 } 74 75 struct types { 76 char *t_name; 77 char *t_mode; 78 int t_type; 79 char *t_arg; 80 } types[] = { 81 { "ascii", "A", TYPE_A, 0 }, 82 { "binary", "I", TYPE_I, 0 }, 83 { "image", "I", TYPE_I, 0 }, 84 { "ebcdic", "E", TYPE_E, 0 }, 85 { "tenex", "L", TYPE_L, bytename }, 86 0 87 }; 88 89 /* 90 * Set transfer type. 91 */ 92 settype(argc, argv) 93 char *argv[]; 94 { 95 register struct types *p; 96 int comret; 97 98 if (argc > 2) { 99 char *sep; 100 101 printf("usage: %s [", argv[0]); 102 sep = " "; 103 for (p = types; p->t_name; p++) { 104 printf("%s%s", sep, p->t_name); 105 if (*sep == ' ') 106 sep = " | "; 107 } 108 printf(" ]\n"); 109 return; 110 } 111 if (argc < 2) { 112 printf("Using %s mode to transfer files.\n", typename); 113 return; 114 } 115 for (p = types; p->t_name; p++) 116 if (strcmp(argv[1], p->t_name) == 0) 117 break; 118 if (p->t_name == 0) { 119 printf("%s: unknown mode\n", argv[1]); 120 return; 121 } 122 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) 123 comret = command ("TYPE %s %s", p->t_mode, p->t_arg); 124 else 125 comret = command("TYPE %s", p->t_mode); 126 if (comret == COMPLETE) { 127 strcpy(typename, p->t_name); 128 type = p->t_type; 129 } 130 } 131 132 /* 133 * Set binary transfer type. 134 */ 135 /*VARARGS*/ 136 setbinary() 137 { 138 139 call(settype, "type", "binary", 0); 140 } 141 142 /* 143 * Set ascii transfer type. 144 */ 145 /*VARARGS*/ 146 setascii() 147 { 148 149 call(settype, "type", "ascii", 0); 150 } 151 152 /* 153 * Set tenex transfer type. 154 */ 155 /*VARARGS*/ 156 settenex() 157 { 158 159 call(settype, "type", "tenex", 0); 160 } 161 162 /* 163 * Set ebcdic transfer type. 164 */ 165 /*VARARGS*/ 166 setebcdic() 167 { 168 169 call(settype, "type", "ebcdic", 0); 170 } 171 172 /* 173 * Set file transfer mode. 174 */ 175 setmode(argc, argv) 176 char *argv[]; 177 { 178 179 printf("We only support %s mode, sorry.\n", modename); 180 } 181 182 /* 183 * Set file transfer format. 184 */ 185 setform(argc, argv) 186 char *argv[]; 187 { 188 189 printf("We only support %s format, sorry.\n", formname); 190 } 191 192 /* 193 * Set file transfer structure. 194 */ 195 setstruct(argc, argv) 196 char *argv[]; 197 { 198 199 printf("We only support %s structure, sorry.\n", structname); 200 } 201 202 put(argc, argv) 203 int argc; 204 char *argv[]; 205 { 206 char *cmd; 207 208 if (argc == 2) 209 argc++, argv[2] = argv[1]; 210 if (argc < 2) { 211 strcat(line, " "); 212 printf("(local-file) "); 213 gets(&line[strlen(line)]); 214 makeargv(); 215 argc = margc; 216 argv = margv; 217 } 218 if (argc < 2) { 219 usage: 220 printf("%s local-file remote-file\n", argv[0]); 221 return; 222 } 223 if (argc < 3) { 224 strcat(line, " "); 225 printf("(remote-file) "); 226 gets(&line[strlen(line)]); 227 makeargv(); 228 argc = margc; 229 argv = margv; 230 } 231 if (argc < 3) 232 goto usage; 233 if (!globulize(&argv[1])) 234 return; 235 cmd = (argv[0][0] == 'a') ? "APPE" : "STOR"; 236 sendrequest(cmd, argv[1], argv[2]); 237 } 238 239 /* 240 * Send multiple files. 241 */ 242 mput(argc, argv) 243 char *argv[]; 244 { 245 char **cpp, **gargs = NULL; 246 247 if (argc < 2) { 248 strcat(line, " "); 249 printf("(local-files) "); 250 gets(&line[strlen(line)]); 251 makeargv(); 252 argc = margc; 253 argv = margv; 254 } 255 if (argc < 2) { 256 printf("%s local-files\n", argv[0]); 257 return; 258 } 259 cpp = argv + 1; 260 if (doglob) { 261 gargs = glob(cpp); 262 if (globerr != NULL) { 263 printf("%s\n", globerr); 264 if (gargs) 265 blkfree(gargs); 266 return; 267 } 268 } 269 if (gargs != NULL) 270 cpp = gargs; 271 for (; *cpp != NULL; cpp++) 272 if (confirm(argv[0], *cpp)) 273 sendrequest("STOR", *cpp, *cpp); 274 if (gargs != NULL) 275 blkfree(gargs); 276 } 277 278 /* 279 * Receive one file. 280 */ 281 get(argc, argv) 282 char *argv[]; 283 { 284 285 if (argc == 2) 286 argc++, argv[2] = argv[1]; 287 if (argc < 2) { 288 strcat(line, " "); 289 printf("(remote-file) "); 290 gets(&line[strlen(line)]); 291 makeargv(); 292 argc = margc; 293 argv = margv; 294 } 295 if (argc < 2) { 296 usage: 297 printf("%s remote-file [ local-file ]\n", argv[0]); 298 return; 299 } 300 if (argc < 3) { 301 strcat(line, " "); 302 printf("(local-file) "); 303 gets(&line[strlen(line)]); 304 makeargv(); 305 argc = margc; 306 argv = margv; 307 } 308 if (argc < 3) 309 goto usage; 310 if (!globulize(&argv[2])) 311 return; 312 recvrequest("RETR", argv[2], argv[1], "w"); 313 } 314 315 /* 316 * Get multiple files. 317 */ 318 mget(argc, argv) 319 char *argv[]; 320 { 321 char *cp; 322 323 if (argc < 2) { 324 strcat(line, " "); 325 printf("(remote-files) "); 326 gets(&line[strlen(line)]); 327 makeargv(); 328 argc = margc; 329 argv = margv; 330 } 331 if (argc < 2) { 332 printf("%s remote-files\n", argv[0]); 333 return; 334 } 335 while ((cp = remglob(argc, argv)) != NULL) 336 if (confirm(argv[0], cp)) 337 recvrequest("RETR", cp, cp, "w"); 338 } 339 340 char * 341 remglob(argc, argv) 342 char *argv[]; 343 { 344 char temp[16]; 345 static char buf[MAXPATHLEN]; 346 static FILE *ftemp = NULL; 347 static char **args; 348 int oldverbose; 349 char *cp, *mode; 350 351 if (!doglob) { 352 if (args == NULL) 353 args = argv; 354 if ((cp = *++args) == NULL) 355 args = NULL; 356 return (cp); 357 } 358 if (ftemp == NULL) { 359 strcpy(temp, "/tmp/ftpXXXXXX"); 360 mktemp(temp); 361 oldverbose = verbose, verbose = 0; 362 for (mode = "w"; *++argv != NULL; mode = "a") 363 recvrequest ("NLST", temp, *argv, mode); 364 verbose = oldverbose; 365 ftemp = fopen(temp, "r"); 366 unlink(temp); 367 if (ftemp == NULL) { 368 printf("can't find list of remote files, oops\n"); 369 return (NULL); 370 } 371 } 372 if (fgets(buf, sizeof (buf), ftemp) == NULL) { 373 fclose(ftemp), ftemp = NULL; 374 return (NULL); 375 } 376 if ((cp = index(buf, '\n')) != NULL) 377 *cp = '\0'; 378 return (buf); 379 } 380 381 char * 382 onoff(bool) 383 int bool; 384 { 385 386 return (bool ? "on" : "off"); 387 } 388 389 /* 390 * Show status. 391 */ 392 status(argc, argv) 393 char *argv[]; 394 { 395 396 if (connected) 397 printf("Connected to %s.\n", hostname); 398 else 399 printf("Not connected.\n"); 400 printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n", 401 modename, typename, formname, structname); 402 printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n", 403 onoff(verbose), onoff(bell), onoff(interactive), 404 onoff(doglob)); 405 printf("Hash mark printing: %s; Use of PORT cmds: %s\n", 406 onoff(hash), onoff(sendport)); 407 } 408 409 /* 410 * Set beep on cmd completed mode. 411 */ 412 /*VARARGS*/ 413 setbell() 414 { 415 416 bell = !bell; 417 printf("Bell mode %s.\n", onoff(bell)); 418 } 419 420 /* 421 * Turn on packet tracing. 422 */ 423 /*VARARGS*/ 424 settrace() 425 { 426 427 trace = !trace; 428 printf("Packet tracing %s.\n", onoff(trace)); 429 } 430 431 /* 432 * Toggle hash mark printing during transfers. 433 */ 434 /*VARARGS*/ 435 sethash() 436 { 437 438 hash = !hash; 439 printf("Hash mark printing %s", onoff(hash)); 440 if (hash) 441 printf(" (%d bytes/hash mark)", BUFSIZ); 442 printf(".\n"); 443 } 444 445 /* 446 * Turn on printing of server echo's. 447 */ 448 /*VARARGS*/ 449 setverbose() 450 { 451 452 verbose = !verbose; 453 printf("Verbose mode %s.\n", onoff(verbose)); 454 } 455 456 /* 457 * Toggle PORT cmd use before each data connection. 458 */ 459 /*VARARGS*/ 460 setport() 461 { 462 463 sendport = !sendport; 464 printf("Use of PORT cmds %s.\n", onoff(sendport)); 465 } 466 467 /* 468 * Turn on interactive prompting 469 * during mget, mput, and mdelete. 470 */ 471 /*VARARGS*/ 472 setprompt() 473 { 474 475 interactive = !interactive; 476 printf("Interactive mode %s.\n", onoff(interactive)); 477 } 478 479 /* 480 * Toggle metacharacter interpretation 481 * on local file names. 482 */ 483 /*VARARGS*/ 484 setglob() 485 { 486 487 doglob = !doglob; 488 printf("Globbing %s.\n", onoff(doglob)); 489 } 490 491 /* 492 * Set debugging mode on/off and/or 493 * set level of debugging. 494 */ 495 /*VARARGS*/ 496 setdebug(argc, argv) 497 char *argv[]; 498 { 499 int val; 500 501 if (argc > 1) { 502 val = atoi(argv[1]); 503 if (val < 0) { 504 printf("%s: bad debugging value.\n", argv[1]); 505 return; 506 } 507 } else 508 val = !debug; 509 debug = val; 510 if (debug) 511 options |= SO_DEBUG; 512 else 513 options &= ~SO_DEBUG; 514 printf("Debugging %s (debug=%d).\n", onoff(debug), debug); 515 } 516 517 /* 518 * Set current working directory 519 * on remote machine. 520 */ 521 cd(argc, argv) 522 char *argv[]; 523 { 524 525 if (argc < 2) { 526 strcat(line, " "); 527 printf("(remote-directory) "); 528 gets(&line[strlen(line)]); 529 makeargv(); 530 argc = margc; 531 argv = margv; 532 } 533 if (argc < 2) { 534 printf("%s remote-directory\n", argv[0]); 535 return; 536 } 537 (void) command("CWD %s", argv[1]); 538 } 539 540 /* 541 * Set current working directory 542 * on local machine. 543 */ 544 lcd(argc, argv) 545 char *argv[]; 546 { 547 char buf[MAXPATHLEN]; 548 549 if (argc < 2) 550 argc++, argv[1] = home; 551 if (argc != 2) { 552 printf("%s local-directory\n", argv[0]); 553 return; 554 } 555 if (!globulize(&argv[1])) 556 return; 557 if (chdir(argv[1]) < 0) { 558 perror(argv[1]); 559 return; 560 } 561 printf("Local directory now %s\n", getwd(buf)); 562 } 563 564 /* 565 * Delete a single file. 566 */ 567 delete(argc, argv) 568 char *argv[]; 569 { 570 571 if (argc < 2) { 572 strcat(line, " "); 573 printf("(remote-file) "); 574 gets(&line[strlen(line)]); 575 makeargv(); 576 argc = margc; 577 argv = margv; 578 } 579 if (argc < 2) { 580 printf("%s remote-file\n", argv[0]); 581 return; 582 } 583 (void) command("DELE %s", argv[1]); 584 } 585 586 /* 587 * Delete multiple files. 588 */ 589 mdelete(argc, argv) 590 char *argv[]; 591 { 592 char *cp; 593 594 if (argc < 2) { 595 strcat(line, " "); 596 printf("(remote-files) "); 597 gets(&line[strlen(line)]); 598 makeargv(); 599 argc = margc; 600 argv = margv; 601 } 602 if (argc < 2) { 603 printf("%s remote-files\n", argv[0]); 604 return; 605 } 606 while ((cp = remglob(argc, argv)) != NULL) 607 if (confirm(argv[0], cp)) 608 (void) command("DELE %s", cp); 609 } 610 611 /* 612 * Rename a remote file. 613 */ 614 renamefile(argc, argv) 615 char *argv[]; 616 { 617 618 if (argc < 2) { 619 strcat(line, " "); 620 printf("(from-name) "); 621 gets(&line[strlen(line)]); 622 makeargv(); 623 argc = margc; 624 argv = margv; 625 } 626 if (argc < 2) { 627 usage: 628 printf("%s from-name to-name\n", argv[0]); 629 return; 630 } 631 if (argc < 3) { 632 strcat(line, " "); 633 printf("(to-name) "); 634 gets(&line[strlen(line)]); 635 makeargv(); 636 argc = margc; 637 argv = margv; 638 } 639 if (argc < 3) 640 goto usage; 641 if (command("RNFR %s", argv[1]) == CONTINUE) 642 (void) command("RNTO %s", argv[2]); 643 } 644 645 /* 646 * Get a directory listing 647 * of remote files. 648 */ 649 ls(argc, argv) 650 char *argv[]; 651 { 652 char *cmd; 653 654 if (argc < 2) 655 argc++, argv[1] = NULL; 656 if (argc < 3) 657 argc++, argv[2] = "-"; 658 if (argc > 3) { 659 printf("usage: %s remote-directory local-file\n", argv[0]); 660 return; 661 } 662 cmd = argv[0][0] == 'l' ? "NLST" : "LIST"; 663 if (strcmp(argv[2], "-") && !globulize(&argv[2])) 664 return; 665 recvrequest(cmd, argv[2], argv[1], "w"); 666 } 667 668 /* 669 * Get a directory listing 670 * of multiple remote files. 671 */ 672 mls(argc, argv) 673 char *argv[]; 674 { 675 char *cmd, *mode; 676 int i, dest; 677 678 if (argc < 2) 679 argc++, argv[1] = NULL; 680 if (argc < 3) 681 argc++, argv[2] = "-"; 682 dest = argc - 1; 683 cmd = argv[0][1] == 'l' ? "NLST" : "LIST"; 684 if (strcmp(argv[dest], "-") != 0) 685 if (globulize(&argv[dest]) && confirm("local-file", argv[dest])) 686 return; 687 for (i = 1, mode = "w"; i < dest; i++, mode = "a") 688 recvrequest(cmd, argv[dest], argv[i], mode); 689 } 690 691 /* 692 * Do a shell escape 693 */ 694 shell(argc, argv) 695 char *argv[]; 696 { 697 int pid, status, (*old1)(), (*old2)(); 698 char shellnam[40], *shell, *namep; 699 char **cpp, **gargs; 700 701 old1 = signal (SIGINT, SIG_IGN); 702 old2 = signal (SIGQUIT, SIG_IGN); 703 if ((pid = fork()) == 0) { 704 for (pid = 3; pid < 20; pid++) 705 close(pid); 706 signal(SIGINT, SIG_DFL); 707 signal(SIGQUIT, SIG_DFL); 708 if (argc <= 1) { 709 shell = getenv("SHELL"); 710 if (shell == NULL) 711 shell = "/bin/sh"; 712 namep = rindex(shell,'/'); 713 if (namep == NULL) 714 namep = shell; 715 strcpy(shellnam,"-"); 716 strcat(shellnam, ++namep); 717 if (strcmp(namep, "sh") != 0) 718 shellnam[0] = '+'; 719 if (debug) { 720 printf ("%s\n", shell); 721 fflush (stdout); 722 } 723 execl(shell, shellnam, 0); 724 perror(shell); 725 exit(1); 726 } 727 cpp = &argv[1]; 728 if (argc > 2) { 729 if ((gargs = glob(cpp)) != NULL) 730 cpp = gargs; 731 if (globerr != NULL) { 732 printf("%s\n", globerr); 733 exit(1); 734 } 735 } 736 if (debug) { 737 register char **zip = cpp; 738 739 printf("%s", *zip); 740 while (*++zip != NULL) 741 printf(" %s", *zip); 742 printf("\n"); 743 fflush(stdout); 744 } 745 execvp(argv[1], cpp); 746 perror(argv[1]); 747 exit(1); 748 } 749 if (pid > 0) 750 while (wait(&status) != pid) 751 ; 752 signal(SIGINT, old1); 753 signal(SIGQUIT, old2); 754 if (pid == -1) 755 perror("Try again later"); 756 return (0); 757 } 758 759 /* 760 * Send new user information (re-login) 761 */ 762 user(argc, argv) 763 int argc; 764 char **argv; 765 { 766 char acct[80], *getpass(); 767 int n; 768 769 if (argc < 2) { 770 strcat(line, " "); 771 printf("(username) "); 772 gets(&line[strlen(line)]); 773 makeargv(); 774 argc = margc; 775 argv = margv; 776 } 777 if (argc > 4) { 778 printf("usage: %s username [password] [account]\n", argv[0]); 779 return (0); 780 } 781 n = command("USER %s", argv[1]); 782 if (n == CONTINUE) { 783 if (argc < 3 ) 784 argv[2] = getpass("Password: "), argc++; 785 n = command("PASS %s", argv[2]); 786 } 787 if (n == CONTINUE) { 788 if (argc < 4) { 789 printf("Account: "); (void) fflush(stdout); 790 (void) fgets(acct, sizeof(acct) - 1, stdin); 791 acct[strlen(acct) - 1] = '\0'; 792 argv[3] = acct; argc++; 793 } 794 n = command("ACCT %s", acct); 795 } 796 if (n != COMPLETE) { 797 fprintf(stderr, "Login failed.\n"); 798 return (0); 799 } 800 return (1); 801 } 802 803 /* 804 * Print working directory. 805 */ 806 /*VARARGS*/ 807 pwd() 808 { 809 810 (void) command("XPWD"); 811 } 812 813 /* 814 * Make a directory. 815 */ 816 makedir(argc, argv) 817 char *argv[]; 818 { 819 820 if (argc < 2) { 821 strcat(line, " "); 822 printf("(directory-name) "); 823 gets(&line[strlen(line)]); 824 makeargv(); 825 argc = margc; 826 argv = margv; 827 } 828 if (argc < 2) { 829 printf("%s directory-name\n", argv[0]); 830 return; 831 } 832 (void) command("XMKD %s", argv[1]); 833 } 834 835 /* 836 * Remove a directory. 837 */ 838 removedir(argc, argv) 839 char *argv[]; 840 { 841 842 if (argc < 2) { 843 strcat(line, " "); 844 printf("(directory-name) "); 845 gets(&line[strlen(line)]); 846 makeargv(); 847 argc = margc; 848 argv = margv; 849 } 850 if (argc < 2) { 851 printf("%s directory-name\n", argv[0]); 852 return; 853 } 854 (void) command("XRMD %s", argv[1]); 855 } 856 857 /* 858 * Send a line, verbatim, to the remote machine. 859 */ 860 quote(argc, argv) 861 char *argv[]; 862 { 863 int i; 864 char buf[BUFSIZ]; 865 866 if (argc < 2) { 867 strcat(line, " "); 868 printf("(command line to send) "); 869 gets(&line[strlen(line)]); 870 makeargv(); 871 argc = margc; 872 argv = margv; 873 } 874 if (argc < 2) { 875 printf("usage: %s line-to-send\n", argv[0]); 876 return; 877 } 878 strcpy(buf, argv[1]); 879 for (i = 2; i < argc; i++) { 880 strcat(buf, " "); 881 strcat(buf, argv[i]); 882 } 883 (void) command(buf); 884 } 885 886 /* 887 * Ask the other side for help. 888 */ 889 rmthelp(argc, argv) 890 char *argv[]; 891 { 892 int oldverbose = verbose; 893 894 verbose = 1; 895 (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]); 896 verbose = oldverbose; 897 } 898 899 /* 900 * Terminate session and exit. 901 */ 902 /*VARARGS*/ 903 quit() 904 { 905 906 disconnect(); 907 exit(0); 908 } 909 910 /* 911 * Terminate session, but don't exit. 912 */ 913 disconnect() 914 { 915 extern FILE *cout; 916 extern int data; 917 918 if (!connected) 919 return; 920 (void) command("QUIT"); 921 (void) fclose(cout); 922 cout = NULL; 923 connected = 0; 924 data = -1; 925 } 926 927 confirm(cmd, file) 928 char *cmd, *file; 929 { 930 char line[BUFSIZ]; 931 932 if (!interactive) 933 return (1); 934 printf("%s %s? ", cmd, file); 935 fflush(stdout); 936 gets(line); 937 return (*line != 'n' && *line != 'N'); 938 } 939 940 fatal(msg) 941 char *msg; 942 { 943 944 fprintf(stderr, "ftp: %s\n"); 945 exit(1); 946 } 947 948 /* 949 * Glob a local file name specification with 950 * the expectation of a single return value. 951 * Can't control multiple values being expanded 952 * from the expression, we return only the first. 953 */ 954 globulize(cpp) 955 char **cpp; 956 { 957 char **globbed; 958 959 if (!doglob) 960 return (1); 961 globbed = glob(*cpp); 962 if (globerr != NULL) { 963 printf("%s: %s\n", *cpp, globerr); 964 if (globbed) 965 blkfree(globbed); 966 return (0); 967 } 968 if (globbed) { 969 *cpp = *globbed++; 970 /* don't waste too much memory */ 971 if (*globbed) 972 blkfree(globbed); 973 } 974 return (1); 975 } 976