home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 3 / hamradioversion3.0examsandprograms1992.iso / packet / n17jsrc / ftpcli.c < prev    next >
C/C++ Source or Header  |  1991-08-22  |  27KB  |  1,141 lines

  1. /* Internet FTP client (interactive user)
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4.  /* Mods by G1EMM and PA0GRI */
  5. #include <stdio.h>
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "session.h"
  9. #include "cmdparse.h"
  10. #include "proc.h"
  11. #include "tty.h"
  12. #include "socket.h"
  13. #include "ftp.h"
  14. #include "ftpcli.h"
  15. #include "commands.h"
  16. #include "netuser.h"
  17. #include "dirutil.h"
  18.  
  19. #define    DIRBUF    256
  20.  
  21. static int doascii __ARGS((int argc,char *argv[],void *p));
  22. static int dobatch __ARGS((int argc,char *argv[],void *p));
  23. static int dobinary __ARGS((int argc,char *argv[],void *p));
  24. static int doftpcd __ARGS((int argc,char *argv[],void *p));
  25. static int doget __ARGS((int argc,char *argv[],void *p));
  26. static int dohash __ARGS((int argc,char *argv[],void *p));
  27. static int doverbose __ARGS((int argc,char *argv[],void *p));
  28. static int dolist __ARGS((int argc,char *argv[],void *p));
  29. static int dols __ARGS((int argc,char *argv[],void *p));
  30. static int domkdir __ARGS((int argc,char *argv[],void *p));
  31. static int domget __ARGS((int argc,char *argv[],void *p));
  32. static int domput __ARGS((int argc,char *argv[],void *p));
  33. static int doput __ARGS((int argc,char *argv[],void *p));
  34. static int doquit __ARGS((int argc,char *argv[],void *p));
  35. static int dormdir __ARGS((int argc,char *argv[],void *p));
  36. static int dotype __ARGS((int argc,char *argv[],void *p));
  37. static int getline __ARGS((struct session *sp,char *prompt,char *buf,int n));
  38. static int getresp __ARGS((struct ftpcli *ftp,int mincode));
  39. static long getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
  40.     char *localname));
  41. static long putsub __ARGS((struct ftpcli *ftp,char *remotename,char *localname));
  42. static void sendport __ARGS((int s,struct sockaddr_in *socket));
  43.  
  44. static char Notsess[] = "Not an FTP session!\n";
  45.  
  46. static int Ftp_type = ASCII_TYPE;
  47. static int Ftp_logbsize = 8;
  48.  
  49. static struct cmds Ftpcmds[] = {
  50.     "",        donothing,    0, 0, NULLCHAR,
  51.     "ascii",    doascii,    0, 0, NULLCHAR,
  52.     "batch",    dobatch,    0, 0, NULLCHAR,
  53.     "binary",    dobinary,    0, 0, NULLCHAR,
  54.     "cd",        doftpcd,    0, 2, "cd <directory>",
  55.     "dir",        dolist,        0, 0, NULLCHAR,
  56.     "list",        dolist,        0, 0, NULLCHAR,
  57.     "get",        doget,        0, 2, "get <remotefile> <localfile>",
  58.     "hash",        dohash,        0, 0, NULLCHAR,
  59.     "ls",        dols,        0, 0, NULLCHAR,
  60.     "mget",        domget,        0, 2, "mget <file> [<file> ...]",
  61.     "mkdir",    domkdir,    0, 2, "mkdir <directory>",
  62.     "mput",        domput,        0, 2, "mput <file> [<file> ...]",
  63.     "nlst",        dols,        0, 0, NULLCHAR,
  64.     "quit",        doquit,        0, 0, NULLCHAR,
  65.     "rmdir",    dormdir,    0, 2, "rmdir <directory>",
  66.     "put",        doput,        0, 2, "put <localfile> <remotefile>",
  67.     "type",        dotype,        0, 0, NULLCHAR,
  68.     "verbose",    doverbose,    0, 0, NULLCHAR,
  69.     NULLCHAR,    NULLFP,        0, 0, NULLCHAR,
  70. };
  71.  
  72. /* Handle top-level FTP command */
  73. int
  74. doftp(argc,argv,p)
  75. int argc;
  76. char *argv[];
  77. void *p;
  78. {
  79.     struct session *sp;
  80.     struct ftpcli ftp;
  81.     struct sockaddr_in fsocket;
  82.     int resp,vsave;
  83.     char *buf,*bufsav,*cp,*un;
  84.     char prmt[40];
  85.     int control;
  86.     char *ftpcli_login();
  87.  
  88.     /* Allocate a session control block */
  89.     if((sp = newsession(argv[1],FTP,0)) == NULLSESSION){
  90.         tprintf("Too many sessions\n");
  91.         return 1;
  92.     }
  93.     memset((char *)&ftp,0,sizeof(ftp));
  94.     ftp.control = ftp.data = -1;
  95.     ftp.verbose = V_NORMAL;
  96.     ftp.type = Ftp_type;
  97.     ftp.logbsize = Ftp_logbsize;
  98.  
  99.     sp->cb.ftp = &ftp;    /* Downward link */
  100.     ftp.session = sp;    /* Upward link */
  101.  
  102.     fsocket.sin_family = AF_INET;
  103.     if(argc < 3)
  104.         fsocket.sin_port = IPPORT_FTP;
  105.     else
  106.         fsocket.sin_port = atoi(argv[2]);
  107.  
  108.     tprintf("Resolving %s... ",sp->name);
  109.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  110.         tprintf(Badhost,sp->name);
  111.         keywait(NULLCHAR,1);
  112.         freesession(sp);
  113.         return 1;
  114.     }
  115.     /* Open the control connection */
  116.     if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
  117.         tprintf("Can't create socket\n");
  118.         keywait(NULLCHAR,1);
  119.         freesession(sp);
  120.         return 1;
  121.     }
  122.     sockmode(sp->s,SOCK_ASCII);
  123.     setflush(sp->s,-1);    /* Flush output only when we call getresp() */
  124.     tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  125.     if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
  126.         goto quit;
  127.     tprintf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
  128.         sp->name);
  129.  
  130.     /* Wait for greeting from server */
  131.     resp = getresp(&ftp,200);
  132.  
  133.     if(resp >= 400)
  134.         goto quit;
  135.     un = getenv("USER");
  136.         if(un != NULLCHAR)
  137.             sprintf(prmt,"Enter user name (%s): ",un);
  138.         else
  139.             sprintf(prmt,"Enter user name: ");
  140.     /* Now process responses and commands */
  141.     buf = mallocw(LINELEN);
  142.     while(resp != -1){
  143.         if(resp == 220){
  144.             /* Sign-on banner; prompt for and send USER command */
  145.             if((cp = ftpcli_login(&ftp, sp->name)) == NULLCHAR){
  146. #ifndef notdef
  147.                   getline(sp,prmt,buf,LINELEN);
  148. #else
  149.                   getline(sp,"Enter user name: ",buf,LINELEN);
  150. #endif
  151.                 /* Send the command only if the user response
  152.                  * was non-null
  153.                  */
  154.                 if(buf[0] != '\n'){
  155.                     usprintf(control,"USER %s",buf);
  156.                     resp = getresp(&ftp,200);
  157.                 } else {
  158.                     if(un != NULLCHAR){
  159.                         usprintf(control,"USER %s\n",un);
  160.                         resp = getresp(&ftp,200);
  161.                     } else {
  162.                         tprintf("No username sent\n");
  163.                         resp = 200;    /* dummy */
  164.                     }
  165.                 }
  166.             } else {
  167.                 usprintf(control,"USER %s\n",cp);
  168.                 free(cp);
  169.                 resp = getresp(&ftp,200);
  170.             }
  171.         } else if(resp == 331){
  172.             if(ftp.password == NULLCHAR){
  173.                 /* turn off echo */
  174.                 sp->ttystate.echo = 0;
  175.                 getline(sp,"Password: ",buf,LINELEN);
  176.                 tprintf("\n");
  177.                 /* Turn echo back on */
  178.                 sp->ttystate.echo = 1;
  179.                 /* Send the command only if the user response
  180.                  * was non-null
  181.                  */
  182.                 if(buf[0] != '\n'){
  183.                     usprintf(control,"PASS %s",buf);
  184.                     resp = getresp(&ftp,200);
  185.                 } else {
  186.                     tprintf("Password must be provided.\nLoggin failed.\n");
  187.                     resp = 200;    /* dummy */
  188.                 }
  189.             } else {
  190.                 usprintf(control,"PASS %s\n",ftp.password);
  191.                 resp = getresp(&ftp,200);
  192.                 free(ftp.password);
  193.                 ftp.password = NULLCHAR;    /* clean up */
  194.             }
  195.         } else {
  196.             /* Test the control channel first */
  197.             if(sockstate(control) == NULLCHAR)
  198.                 break;
  199.  
  200.             getline(sp,"ftp> ",buf,LINELEN);
  201.  
  202.         /* Copy because cmdparse modifies the original */
  203.             bufsav = strdup(buf);
  204.             if((resp = cmdparse(Ftpcmds,buf,&ftp)) != -1){
  205.                 /* Valid command, free buffer and get another */
  206.                 free(bufsav);
  207.             } else {
  208.                 /* Not a local cmd, send to remote server */
  209.                 usputs(control,bufsav);
  210.                 free(bufsav);
  211.  
  212.                 /* Enable display of server response */
  213.                 vsave = ftp.verbose;
  214.                 ftp.verbose = V_NORMAL;
  215.                 resp = getresp(&ftp,200);
  216.                 ftp.verbose = vsave;
  217.             }
  218.         }
  219.     }
  220.     free(buf);
  221. quit:    cp = sockerr(control);
  222.     tprintf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
  223.      cp != NULLCHAR ? cp : "EOF");
  224.  
  225.     if(ftp.fp != NULLFILE && ftp.fp != stdout)
  226.         fclose(ftp.fp);
  227.     if(ftp.data != -1)
  228.         close_s(ftp.data);
  229.     if(ftp.control != -1)
  230.         close_s(ftp.control);
  231.     keywait(NULLCHAR,1);
  232.     if(ftp.session != NULLSESSION)
  233.         freesession(ftp.session);
  234.     return 0;
  235. }
  236.  
  237. /* Control verbosity level */
  238. static int
  239. doverbose(argc,argv,p)
  240. int argc;
  241. char *argv[];
  242. void *p;
  243. {
  244.     register struct ftpcli *ftp;
  245.  
  246.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  247.         return -1;
  248.     return setshort(&ftp->verbose,"Verbose",argc,argv);
  249. }
  250. /* Enable/disable command batching */
  251. static int
  252. dobatch(argc,argv,p)
  253. int argc;
  254. char *argv[];
  255. void *p;
  256. {
  257.     register struct ftpcli *ftp;
  258.  
  259.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  260.         return -1;
  261.     return setbool(&ftp->batch,"Command batching",argc,argv);
  262. }
  263. /* Set verbosity to high (convenience command) */
  264. static int
  265. dohash(argc,argv,p)
  266. int argc;
  267. char *argv[];
  268. void *p;
  269. {
  270.     register struct ftpcli *ftp;
  271.  
  272.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  273.         return -1;
  274.     ftp->verbose = V_HASH;
  275.     return 0;
  276. }
  277.     
  278. /* Close session */
  279. static int
  280. doquit(argc,argv,p)
  281. int argc;
  282. char *argv[];
  283. void *p;
  284. {
  285.     register struct ftpcli *ftp;
  286.  
  287.     ftp = (struct ftpcli *)p;
  288.     if(ftp == NULLFTP)
  289.         return -1;
  290.     usprintf(ftp->control,"QUIT\n");
  291.     getresp(ftp,200);    /* Get the closing message */
  292.     getresp(ftp,200);    /* Wait for the server to close */
  293.     return -1;
  294. }
  295.  
  296. /* Translate 'cd' to 'cwd' for convenience */
  297. static int
  298. doftpcd(argc,argv,p)
  299. int argc;
  300. char *argv[];
  301. void *p;
  302. {
  303.     register struct ftpcli *ftp;
  304.  
  305.     ftp = (struct ftpcli *)p;
  306.     if(ftp == NULLFTP)
  307.         return -1;
  308.     usprintf(ftp->control,"CWD %s\n",argv[1]);
  309.     return getresp(ftp,200);
  310. }
  311. /* Translate 'mkdir' to 'xmkd' for convenience */
  312. static int
  313. domkdir(argc,argv,p)
  314. int argc;
  315. char *argv[];
  316. void *p;
  317. {
  318.     register struct ftpcli *ftp;
  319.  
  320.     ftp = (struct ftpcli *)p;
  321.     if(ftp == NULLFTP)
  322.         return -1;
  323.     usprintf(ftp->control,"XMKD %s\n",argv[1]);
  324.     return getresp(ftp,200);
  325. }
  326. /* Translate 'rmdir' to 'xrmd' for convenience */
  327. static int
  328. dormdir(argc,argv,p)
  329. int argc;
  330. char *argv[];
  331. void *p;
  332. {
  333.     register struct ftpcli *ftp;
  334.  
  335.     ftp = (struct ftpcli *)p;
  336.     if(ftp == NULLFTP)
  337.         return -1;
  338.     usprintf(ftp->control,"XRMD %s\n",argv[1]);
  339.     return getresp(ftp,200);
  340. }
  341. static int
  342. dobinary(argc,argv,p)
  343. int argc;
  344. char *argv[];
  345. void *p;
  346. {
  347.     char *args[2];
  348.  
  349.     args[1] = "I";
  350.     return dotype(2,args,p);
  351. }
  352. static int
  353. doascii(argc,argv,p)
  354. int argc;
  355. char *argv[];
  356. void *p;
  357. {
  358.     char *args[2];
  359.  
  360.     args[1] = "A";
  361.     return dotype(2,args,p);
  362. }
  363.  
  364. /* Handle "type" command from user */
  365. static int
  366. dotype(argc,argv,p)
  367. int argc;
  368. char *argv[];
  369. void *p;
  370. {
  371.     register struct ftpcli *ftp;
  372.  
  373.     ftp = (struct ftpcli *)p;
  374.     if(ftp == NULLFTP)
  375.         return -1;
  376.     if(argc < 2){
  377.         switch(ftp->type){
  378.         case IMAGE_TYPE:
  379.             tprintf("Image\n");
  380.             break;
  381.         case ASCII_TYPE:
  382.             tprintf("Ascii\n");
  383.             break;
  384.         case LOGICAL_TYPE:
  385.             tprintf("Logical bytesize %u\n",ftp->logbsize);
  386.             break;
  387.         }
  388.         return 0;
  389.     }
  390.     switch(*argv[1]){
  391.     case 'i':
  392.     case 'I':
  393.     case 'b':
  394.     case 'B':
  395.         ftp->type = IMAGE_TYPE;
  396.         break;
  397.     case 'a':
  398.     case 'A':
  399.         ftp->type = ASCII_TYPE;
  400.         break;
  401.     case 'L':
  402.     case 'l':
  403.         ftp->type = LOGICAL_TYPE;
  404.         ftp->logbsize = atoi(argv[2]);
  405.         break;
  406.     default:
  407.         tprintf("Invalid type %s\n",argv[1]);
  408.         return 1;
  409.     }
  410.     return 0;
  411. }
  412.  
  413. /* Handle "ftype" command */
  414. int
  415. doftype(argc,argv,p)
  416. int argc;
  417. char *argv[];
  418. void *p;
  419. {
  420.     if(argc < 2){
  421.         tprintf("Ftp initial TYPE is ");
  422.         switch(Ftp_type){
  423.         case IMAGE_TYPE:
  424.             tprintf("Image\n");
  425.             break;
  426.         case ASCII_TYPE:
  427.             tprintf("Ascii\n");
  428.             break;
  429.         case LOGICAL_TYPE:
  430.             tprintf("Logical bytesize %u\n",Ftp_logbsize);
  431.             break;
  432.         }
  433.         return 0;
  434.     }
  435.     switch(*argv[1]){
  436.     case 'i':
  437.     case 'I':
  438.     case 'b':
  439.     case 'B':
  440.         Ftp_type = IMAGE_TYPE;
  441.         break;
  442.     case 'a':
  443.     case 'A':
  444.         Ftp_type = ASCII_TYPE;
  445.         break;
  446.     case 'L':
  447.     case 'l':
  448.         Ftp_type = LOGICAL_TYPE;
  449.         Ftp_logbsize = atoi(argv[2]);
  450.         break;
  451.     default:
  452.         tprintf("Invalid type %s\n",argv[1]);
  453.         return 1;
  454.     }
  455.     return 0;
  456. }
  457.  
  458. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  459. static int
  460. doget(argc,argv,p)
  461. int argc;
  462. char *argv[];
  463. void *p;
  464. {
  465.     char *remotename,*localname;
  466.     register struct ftpcli *ftp;
  467.  
  468.     ftp = (struct ftpcli *)p;
  469.     if(ftp == NULLFTP){
  470.         tprintf(Notsess);
  471.         return 1;
  472.     }
  473.     remotename = argv[1];
  474.     if(argc < 3)
  475.         localname = remotename;
  476.     else
  477.         localname = argv[2];
  478.  
  479.     getsub(ftp,"RETR",remotename,localname);
  480.     return 0;
  481. }
  482. /* Get a collection of files */
  483. static int
  484. domget(argc,argv,p)
  485. int argc;
  486. char *argv[];
  487. void *p;
  488. {
  489.     register struct ftpcli *ftp;
  490.     FILE *files, *filel;
  491.     char tmpname[80];
  492.     char *buf, *local, *c;
  493.     int i, inlist;
  494.     long r;
  495.  
  496.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  497.         tprintf(Notsess);
  498.         return 1;
  499.     }
  500.     tmpnam(tmpname);
  501.     buf = mallocw(DIRBUF);
  502.     ftp->state = RECEIVING_STATE;
  503.     for(i=1;i<argc;i++){
  504.         if(argv[i][0] == '@'){
  505.             inlist = 1;
  506.             if((filel = fopen(&argv[i][1], "r")) == NULLFILE){
  507.                 tprintf("Can't open listfile: %s\n", &argv[i][1]);
  508.                 continue;
  509.             }
  510.             if((files = fopen(tmpname, "w")) == NULLFILE){
  511.                 tprintf("Can't open tempfile: %s\n", tmpname);
  512.                 continue;
  513.             }
  514.             while(fgets(buf,DIRBUF,filel) != NULLCHAR){
  515.                 fputs(buf,files);
  516.             }
  517.             fclose(files);
  518.             fclose(filel);
  519.             if((files = fopen(tmpname, "r")) == NULLFILE){
  520.                 tprintf("Can't open tempfile: %s\n", tmpname);
  521.                 continue;
  522.             }
  523.         } else {
  524.             inlist = 0;
  525.             r = getsub(ftp,"NLST",argv[i],tmpname);
  526.             if(ftp->abort)
  527.                 break;    /* Aborted */
  528.             if(r == -1 || (files = fopen(tmpname,"r")) == NULLFILE){
  529.                 tprintf("Can't NLST %s\n",argv[i]);
  530.                 unlink(tmpname);
  531.                 continue;
  532.             }
  533.         }
  534.         /* The tmp file now contains a list of the remote files, so
  535.          * go get 'em. Break out if the user signals an abort.
  536.          */
  537.         while(fgets(buf,DIRBUF,files) != NULLCHAR){
  538.             rip(buf);
  539.             local = strdup(buf);
  540. #ifdef    MSDOS
  541.             if(inlist){
  542.                 strrev(local);
  543.                 strtok(local, "\\/[]<>,?#~()&%");
  544.                 strrev(local);
  545.             }
  546.             if((c = strstr(local, ".")) != NULLCHAR) {
  547.                 c++;
  548.                 c = strtok(c, ".");    /* remove 2nd period if any*/
  549.             }
  550. #endif
  551.             getsub(ftp,"RETR",buf,local);
  552.             free(local);
  553.             if(ftp->abort){
  554.                 /* User abort */
  555.                 ftp->abort = 0;
  556.                 fclose(files);
  557.                 unlink(tmpname);
  558.                 free(buf);
  559.                 ftp->state = COMMAND_STATE;
  560.                 return 1;
  561.             }
  562.         }
  563.         fclose(files);
  564.         unlink(tmpname);
  565.     }
  566.     free(buf);
  567.     ftp->state = COMMAND_STATE;
  568.     ftp->abort = 0;
  569.     return 0;
  570. }
  571. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  572. static int
  573. dolist(argc,argv,p)
  574. int argc;
  575. char *argv[];
  576. void *p;
  577. {
  578.     char *remotename,*localname;
  579.     register struct ftpcli *ftp;
  580.  
  581.     ftp = (struct ftpcli *)p;
  582.     if(ftp == NULLFTP){
  583.         tprintf(Notsess);
  584.         return 1;
  585.     }
  586.     remotename = argv[1];
  587.     if(argc > 2)
  588.         localname = argv[2];
  589.     else
  590.         localname = NULLCHAR;
  591.  
  592.     getsub(ftp,"LIST",remotename,localname);
  593.     return 0;
  594. }
  595. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  596. static int
  597. dols(argc,argv,p)
  598. int argc;
  599. char *argv[];
  600. void *p;
  601. {
  602.     char *remotename,*localname;
  603.     register struct ftpcli *ftp;
  604.  
  605.     ftp = (struct ftpcli *)p;
  606.     if(ftp == NULLFTP){
  607.         tprintf(Notsess);
  608.         return 1;
  609.     }
  610.     remotename = argv[1];
  611.     if(argc > 2)
  612.         localname = argv[2];
  613.     else
  614.         localname = NULLCHAR;
  615.  
  616.     getsub(ftp,"NLST",remotename,localname);
  617.     return 0;
  618. }
  619. /* Common code to LIST/NLST/RETR and mget
  620.  * Returns number of bytes received if successful
  621.  * Returns -1 on error
  622.  */
  623. static long
  624. getsub(ftp,command,remotename,localname)
  625. register struct ftpcli *ftp;
  626. char *command,*remotename,*localname;
  627. {
  628.     unsigned long total;
  629.     FILE *fp;
  630.     int cnt,resp,i,control,savmode;
  631.     char *mode;
  632.     struct sockaddr_in lsocket;
  633.     struct sockaddr_in lcsocket;
  634.     int32 startclk,rate;
  635.     int vsave;
  636.     int typewait = 0;
  637.     int prevstate;
  638.  
  639.     if(ftp == NULLFTP)
  640.         return -1;
  641.     control = ftp->control;
  642.     savmode = ftp->type;
  643.  
  644.     switch(ftp->type){
  645.     case IMAGE_TYPE:
  646.     case LOGICAL_TYPE:
  647.         mode = WRITE_BINARY;
  648.         break;
  649.     case ASCII_TYPE:
  650.         mode = WRITE_TEXT;
  651.         break;
  652.     }
  653.     /* Open the file */
  654.     if(localname == NULLCHAR){
  655.         fp = NULLFILE;
  656.     } else if((fp = fopen(localname,mode)) == NULLFILE){
  657.         tprintf("Can't write %s: %s\n",localname,sys_errlist[errno]);
  658.         return -1;
  659.     }
  660.     /* Open the data connection */
  661.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  662.     listen(ftp->data,0);    /* Accept only one connection */
  663.     prevstate = ftp->state;
  664.     ftp->state = RECEIVING_STATE;
  665.  
  666.     /* Send TYPE message, if necessary */
  667.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
  668.         /* Directory listings are always in ASCII */
  669.         ftp->type = ASCII_TYPE;
  670.     }
  671.     if(ftp->typesent != ftp->type){
  672.         switch(ftp->type){
  673.         case ASCII_TYPE:
  674.             usprintf(control,"TYPE A\n");
  675.             break;
  676.         case IMAGE_TYPE:
  677.             usprintf(control,"TYPE I\n");
  678.             break;
  679.         case LOGICAL_TYPE:
  680.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  681.             break;
  682.         }
  683.         ftp->typesent = ftp->type;
  684.         if(!ftp->batch){
  685.             resp = getresp(ftp,200);
  686.             if(resp == -1 || resp > 299)
  687.                 goto failure;
  688.         } else
  689.             typewait = 1;
  690.     }
  691.     /* Send the PORT message. Use the IP address
  692.      * on the local end of our control connection.
  693.      */
  694.     i = SOCKSIZE;
  695.     getsockname(ftp->data,(char *)&lsocket,&i); /* Get port number */
  696.     i = SOCKSIZE;
  697.     getsockname(ftp->control,(char *)&lcsocket,&i);
  698.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  699.     sendport(control,&lsocket);
  700.     if(!ftp->batch){
  701.         /* Get response to PORT command */
  702.         resp = getresp(ftp,200);
  703.         if(resp == -1 || resp > 299)
  704.             goto failure;
  705.     }
  706.     /* Generate the command to start the transfer */
  707.     if(remotename != NULLCHAR)
  708.         usprintf(control,"%s %s\n",command,remotename);
  709.     else
  710.         usprintf(control,"%s\n",command);
  711.  
  712.     if(ftp->batch){
  713.         /* Get response to TYPE command, if sent */
  714.         if(typewait){
  715.             resp = getresp(ftp,200);
  716.             if(resp == -1 || resp > 299)
  717.                 goto failure;
  718.         }
  719.         /* Get response to PORT command */
  720.         resp = getresp(ftp,200);
  721.         if(resp == -1 || resp > 299)
  722.             goto failure;
  723.     }
  724.     /* Get the intermediate "150" response */
  725.     resp = getresp(ftp,100);
  726.     if(resp == -1 || resp >= 400)
  727.         goto failure;
  728.  
  729.     /* Wait for the server to open the data connection */
  730.     cnt = 0;
  731.     ftp->data = accept(ftp->data,NULLCHAR,&cnt);
  732.     startclk = msclock();
  733.  
  734.     /* If output is to the screen, temporarily disable hash marking */
  735.     vsave = ftp->verbose;
  736.     if(vsave >= V_HASH && fp == NULLFILE)
  737.         ftp->verbose = V_NORMAL;
  738.     total = recvfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  739.     /* Immediately close the data connection; some servers (e.g., TOPS-10)
  740.      * wait for the data connection to close completely before returning
  741.      * the completion message on the control channel
  742.      */
  743.     close_s(ftp->data);
  744.     ftp->data = -1;
  745.  
  746. #ifdef    CPM
  747.     if(fp != NULLFILE && ftp->type == ASCII_TYPE)
  748.         putc(CTLZ,fp);
  749. #endif
  750.     if(fp != NULLFILE && fp != stdout)
  751.         fclose(fp);
  752.     startclk = msclock() - startclk;
  753.     if(startclk != 0)
  754.         rate = (total*1000)/startclk;
  755.     else
  756.         rate = 0;
  757.  
  758.     if(remotename == NULLCHAR)
  759.         remotename = "";
  760.     if(total == -1){
  761.         tprintf("%s %s: Error/abort during data transfer\n",command,remotename);
  762.     } else if(ftp->verbose >= V_SHORT){
  763.         tprintf("%s %s: %lu bytes in %lu sec (%lu/sec)\n",
  764.          command,remotename, total,startclk/1000,rate);
  765.     }
  766.     /* Get the "Sent" message */
  767.     getresp(ftp,200);
  768.  
  769.     ftp->state = prevstate;
  770.     ftp->verbose = vsave;
  771.     ftp->type = savmode;
  772.     return total;
  773.  
  774. failure:
  775.     /* Error, quit */
  776.     if(fp != NULLFILE && fp != stdout)
  777.         fclose(fp);
  778.     close_s(ftp->data);
  779.     ftp->data = -1;
  780.     ftp->state = prevstate;
  781.     ftp->type = savmode;
  782.     return -1;
  783. }
  784. /* Send a file. Syntax: put <local name> [<remote name>] */
  785. static int
  786. doput(argc,argv,p)
  787. int argc;
  788. char *argv[];
  789. void *p;
  790. {
  791.     register struct ftpcli *ftp;
  792.     char *remotename,*localname;
  793.  
  794.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  795.         tprintf(Notsess);
  796.         return 1;
  797.     }
  798.     localname = argv[1];
  799.     if(argc < 3)
  800.         remotename = localname;
  801.     else
  802.         remotename = argv[2];
  803.  
  804.     putsub(ftp,remotename,localname);
  805.     return 0;
  806. }
  807. /* Put a collection of files */
  808. static int
  809. domput(argc,argv,p)
  810. int argc;
  811. char *argv[];
  812. void *p;
  813. {
  814.     register struct ftpcli *ftp;
  815.     FILE *files;
  816.     int i;
  817.     char tmpname[80];    
  818.     char *buf;
  819.  
  820.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  821.         tprintf(Notsess);
  822.         return 1;
  823.     }
  824.     tmpnam(tmpname);
  825.     if((files = fopen(tmpname,"w+")) == NULLFILE){
  826.         tprintf("Can't list local files\n");
  827.         unlink(tmpname);
  828.         return 1;
  829.     }
  830.     for(i=1;i<argc;i++)
  831.         getdir(argv[i],0,files);
  832.  
  833.     rewind(files);
  834.     buf = mallocw(DIRBUF);
  835.     ftp->state = SENDING_STATE;
  836.     while(fgets(buf,DIRBUF,files) != NULLCHAR){
  837.         rip(buf);
  838.         putsub(ftp,buf,buf);
  839.         if(ftp->abort)
  840.             break;        /* User abort */
  841.     }
  842.     fclose(files);
  843.     unlink(tmpname);
  844.     free(buf);
  845.     ftp->state = COMMAND_STATE;
  846.     ftp->abort = 0;
  847.     return 0;
  848. }
  849. /* Common code to put, mput.
  850.  * Returns number of bytes sent if successful
  851.  * Returns -1 on error
  852.  */
  853. static long
  854. putsub(ftp,remotename,localname)
  855. register struct ftpcli *ftp;
  856. char *remotename,*localname;
  857. {
  858.     char *mode;
  859.     int i,resp,control;
  860.     unsigned long total;
  861.     FILE *fp;
  862.     struct sockaddr_in lsocket,lcsocket;
  863.     int32 startclk,rate;
  864.     int typewait = 0;
  865.     int prevstate;
  866.  
  867.     control = ftp->control;
  868.     if(ftp->type == IMAGE_TYPE)
  869.         mode = READ_BINARY;
  870.     else
  871.         mode = READ_TEXT;
  872.  
  873.     /* Open the file */
  874.     if((fp = fopen(localname,mode)) == NULLFILE){
  875.         tprintf("Can't read %s: %s\n",localname,sys_errlist[errno]);
  876.         return -1;
  877.     }
  878.     if(ftp->type == ASCII_TYPE && isbinary(fp)){
  879.         tprintf("Warning: type is ASCII and %s appears to be binary\n",localname);
  880.     }
  881.     /* Open the data connection */
  882.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  883.     listen(ftp->data,0);
  884.     prevstate = ftp->state;
  885.     ftp->state = SENDING_STATE;
  886.  
  887.     /* Send TYPE message, if necessary */
  888.     if(ftp->typesent != ftp->type){
  889.         switch(ftp->type){
  890.         case ASCII_TYPE:
  891.             usprintf(control,"TYPE A\n");
  892.             break;
  893.         case IMAGE_TYPE:
  894.             usprintf(control,"TYPE I\n");
  895.             break;
  896.         case LOGICAL_TYPE:
  897.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  898.             break;
  899.         }
  900.         ftp->typesent = ftp->type;
  901.  
  902.         /* Get response to TYPE command */
  903.         if(!ftp->batch){
  904.             resp = getresp(ftp,200);
  905.             if(resp == -1 || resp > 299){
  906.                 goto failure;
  907.             }
  908.         } else
  909.             typewait = 1;
  910.     }
  911.     /* Send the PORT message. Use the IP address
  912.      * on the local end of our control connection.
  913.      */
  914.     i = SOCKSIZE;
  915.     getsockname(ftp->data,(char *)&lsocket,&i);
  916.     i = SOCKSIZE;
  917.     getsockname(ftp->control,(char *)&lcsocket,&i);
  918.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  919.     sendport(control,&lsocket);
  920.     if(!ftp->batch){
  921.         /* Get response to PORT command */
  922.         resp = getresp(ftp,200);
  923.         if(resp == -1 || resp > 299){
  924.             goto failure;
  925.         }
  926.     }
  927.     /* Generate the command to start the transfer */
  928.     usprintf(control,"STOR %s\n",remotename);
  929.  
  930.     if(ftp->batch){
  931.         /* Get response to TYPE command, if sent */
  932.         if(typewait){
  933.             resp = getresp(ftp,200);
  934.             if(resp == -1 || resp > 299){
  935.                 goto failure;
  936.             }
  937.         }
  938.         /* Get response to PORT command */
  939.         resp = getresp(ftp,200);
  940.         if(resp == -1 || resp > 299){
  941.             goto failure;
  942.         }
  943.     }
  944.     /* Get the intermediate "150" response */
  945.     resp = getresp(ftp,100);
  946.     if(resp == -1 || resp >= 400){
  947.         goto failure;
  948.     }
  949.  
  950.     /* Wait for the data connection to open. Otherwise the first
  951.      * block of data would go out with the SYN, and this may confuse
  952.      * some other TCPs
  953.      */
  954.     accept(ftp->data,NULLCHAR,(int *)NULL);
  955.  
  956.     startclk = msclock();
  957.  
  958.     total = sendfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  959.     close_s(ftp->data);
  960.     ftp->data = -1;
  961.     fclose(fp);
  962.  
  963.     startclk = msclock() - startclk;
  964.     if(startclk != 0)
  965.         rate = (total*1000)/startclk;
  966.     else
  967.         rate = 0;
  968.     if(total == -1){
  969.         tprintf("STOR %s: Error/abort during data transfer\n",remotename);
  970.     } else if(ftp->verbose >= V_SHORT){
  971.         tprintf("STOR %s: %lu bytes in %lu sec (%lu/sec)\n",
  972.          remotename,total,startclk/1000,rate);
  973.     }
  974.     getresp(ftp,200);
  975.     ftp->state = prevstate;
  976.     return total;
  977.  
  978. failure:
  979.     /* Error, quit */
  980.     fclose(fp);
  981.     close_s(ftp->data);
  982.     ftp->data = -1;
  983.     ftp->state = prevstate;
  984.     return -1;
  985. }
  986. /* Abort a GET or PUT operation in progress. Note: this will leave
  987.  * the partial file on the local or remote system
  988.  */
  989. int
  990. doabort(argc,argv,p)
  991. int argc;
  992. char *argv[];
  993. void *p;
  994. {
  995.     register struct session *sp;
  996.     register struct ftpcli *ftp;
  997.  
  998.     sp = (struct session *)p;
  999.     if(sp == NULLSESSION)
  1000.         return -1;
  1001.  
  1002.     /* Default is the current session, but it can be overridden with
  1003.      * an argument.
  1004.      */
  1005.     if(argc > 1)
  1006.         sp = sessptr(argv[1]);
  1007.  
  1008.     if(sp == NULLSESSION || sp->type != FTP){
  1009.         tprintf("Not an active FTP session\n");
  1010.         return 1;
  1011.     }
  1012.     ftp = sp->cb.ftp;
  1013.     switch(ftp->state){
  1014.     case COMMAND_STATE:
  1015.         tprintf("No active transfer\n");
  1016.         return 1;
  1017.     case SENDING_STATE:
  1018.         /* Send a premature EOF.
  1019.          * Unfortunately we can't just reset the connection
  1020.          * since the remote side might end up waiting forever
  1021.          * for us to send something.
  1022.          */
  1023.         shutdown(ftp->data,1);    /* Note fall-thru */
  1024.         ftp->abort = 1;
  1025.         break;
  1026.     case RECEIVING_STATE:
  1027.         /* Just blow away the receive socket */
  1028.         shutdown(ftp->data,2);    /* Note fall-thru */
  1029.         ftp->abort = 1;
  1030.         break;
  1031.     }
  1032.     return 0;
  1033. }
  1034. /* send PORT message */
  1035. static void
  1036. sendport(s,socket)
  1037. int s;
  1038. struct sockaddr_in *socket;
  1039. {
  1040.     /* Send PORT a,a,a,a,p,p message */
  1041.     usprintf(s,"PORT %u,%u,%u,%u,%u,%u\n",
  1042.         hibyte(hiword(socket->sin_addr.s_addr)),
  1043.         lobyte(hiword(socket->sin_addr.s_addr)),
  1044.         hibyte(loword(socket->sin_addr.s_addr)),
  1045.         lobyte(loword(socket->sin_addr.s_addr)),
  1046.         hibyte(socket->sin_port),
  1047.         lobyte(socket->sin_port));
  1048. }
  1049.  
  1050. /* Wait for, read and display response from FTP server. Return the result code.
  1051.  */
  1052. static int
  1053. getresp(ftp,mincode)
  1054. struct ftpcli *ftp;
  1055. int mincode;    /* Keep reading until at least this code comes back */
  1056. {
  1057.     register char *line;
  1058.     int rval;
  1059.  
  1060.     usflush(ftp->control);
  1061.     line = mallocw(LINELEN);
  1062.     for(;;){
  1063.         /* Get line */
  1064.         if(recvline(ftp->control,line,LINELEN) == -1){
  1065.             rval = -1;
  1066.             break;
  1067.         }
  1068.         rip(line);        /* Remove cr/lf */
  1069.         rval = atoi(line);
  1070.         if(rval >= 400 || ftp->verbose >= V_NORMAL)
  1071.             tprintf("%s\n",line);    /* Display to user */
  1072.  
  1073.         /* Messages with dashes are continued */
  1074.         if(line[3] != '-' && rval >= mincode)
  1075.             break;
  1076.     }
  1077.     free(line);
  1078.     return rval;
  1079. }
  1080.  
  1081. /* Issue a prompt and read a line from the user */
  1082. static int
  1083. getline(sp,prompt,buf,n)
  1084. struct session *sp;
  1085. char *prompt;
  1086. char *buf;
  1087. int n;
  1088. {
  1089.     /* If there's something already there, don't issue prompt */
  1090.     if(socklen(sp->input,0) == 0)
  1091.         tprintf(prompt);
  1092.  
  1093.     usflush(sp->output);
  1094.     return recvline(sp->input,buf,n);
  1095. }
  1096.  
  1097. /* Attempt to log in the user whose name is in ftp->username and password
  1098.  * in pass
  1099.  */
  1100. static char *
  1101. ftpcli_login(ftp,host)
  1102. struct ftpcli *ftp;
  1103. char *host; {
  1104.  
  1105.     char buf[80],*cp,*cp1;
  1106.     FILE *fp;
  1107.  
  1108.     extern char *Hostfile;    /* List of user names and permissions */
  1109.  
  1110.     if((fp = fopen(Hostfile,"r")) == NULLFILE){
  1111.         return NULLCHAR;
  1112.     }
  1113.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  1114.         buf[strlen(buf)-1] = '\0';    /* Nuke the newline */
  1115.         if(buf[0] == '#')
  1116.             continue;    /* Comment */
  1117.         if((cp = strchr(buf,' ')) == NULLCHAR)
  1118.             /* Bogus entry */
  1119.             continue;
  1120.         *cp++ = '\0';        /* Now points to user name */
  1121.         if(strcmp(host,buf) == 0)
  1122.             break;        /* Found host name */
  1123.     }
  1124.     if(feof(fp)){
  1125.         /* User name not found in file */
  1126.         fclose(fp);
  1127.         return NULLCHAR;
  1128.     }
  1129.     fclose(fp);
  1130.     /* Look for space after user field in file */
  1131.     if((cp1 = strchr(cp,' ')) == NULLCHAR)
  1132.         /* if not there then we'll prompt */
  1133.         ftp->password = NULLCHAR;
  1134.     else
  1135.         *cp1++ = '\0';        /* Now points to password */
  1136.         if(strcmp(cp,"*") == 0)
  1137.             cp1 = "anonymous";
  1138.         ftp->password = strdup(cp1);
  1139.     return strdup(cp);
  1140. }
  1141.