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