home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / s920603.zip / FTPCLI.C < prev    next >
C/C++ Source or Header  |  1992-06-01  |  25KB  |  1,082 lines

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