home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR3 / KA9Q212.ZIP / FTPCLI.C < prev    next >
C/C++ Source or Header  |  1993-07-16  |  26KB  |  1,066 lines

  1. /* Internet FTP client (interactive user)
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4.  
  5. /* Modified by paul@wolf.demon.co.uk to support a local 'view' command */
  6.  
  7. /****************************************************************************
  8. *    $Id: ftpcli.c 1.5 93/07/16 11:44:49 ROOT_DOS Exp $
  9. *    13 Sep 92    1.4        GT    Log "cd", "get" and "mget" commands.            *
  10. *    08 May 93    1.5        GT    Fix warnings.                                    *
  11. ****************************************************************************/
  12.  
  13. #include <stdio.h>
  14. #include "global.h"
  15. #include "files.h"
  16. #include "mbuf.h"
  17. #include "session.h"
  18. #include "cmdparse.h"
  19. #include "proc.h"
  20. #include "tty.h"
  21. #include "socket.h"
  22. #include "ftp.h"
  23. #include "ftpcli.h"
  24. #include "commands.h"
  25. #include "netuser.h"
  26. #include "dirutil.h"
  27. #include "help.h"
  28.  
  29. #define    DIRBUF    256
  30.  
  31. static int doascii __ARGS((int argc,char *argv[],void *p));
  32. static int dobatch __ARGS((int argc,char *argv[],void *p));
  33. static int dobinary __ARGS((int argc,char *argv[],void *p));
  34. static int doftpcd __ARGS((int argc,char *argv[],void *p));
  35. static int doget __ARGS((int argc,char *argv[],void *p));
  36. static int dohash __ARGS((int argc,char *argv[],void *p));
  37. static int doftphelp __ARGS((int argc,char *argv[],void *p));
  38. static int doverbose __ARGS((int argc,char *argv[],void *p));
  39. static int dolist __ARGS((int argc,char *argv[],void *p));
  40. static int dols __ARGS((int argc,char *argv[],void *p));
  41. static int domkdir __ARGS((int argc,char *argv[],void *p));
  42. static int domget __ARGS((int argc,char *argv[],void *p));
  43. static int domput __ARGS((int argc,char *argv[],void *p));
  44. static int dopager __ARGS((int argc,char *argv[],void *p));
  45. static int doput __ARGS((int argc,char *argv[],void *p));
  46. static int doquit __ARGS((int argc,char *argv[],void *p));
  47. static int dormdir __ARGS((int argc,char *argv[],void *p));
  48. static int dotype __ARGS((int argc,char *argv[],void *p));
  49. static int doview __ARGS((int argc,char *argv[],void *p));
  50. static int getline __ARGS((struct session *sp,char *prompt,char *buf,int n));
  51. static int getresp __ARGS((struct ftpcli *ftp,int mincode));
  52. static long getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
  53.     char *localname));
  54. static long putsub __ARGS((struct ftpcli *ftp,char *remotename,char *localname));
  55. static void sendport __ARGS((int s,struct sockaddr_in *socket));
  56.  
  57. static char Notsess[] = "Not an FTP session!\n";
  58.  
  59. static struct cmds Ftpcmds[] = {
  60.     { "",        donothing,    0, 0, NULLCHAR },
  61.     { "ascii",    doascii,    0, 0, NULLCHAR },
  62.     { "batch",    dobatch,    0, 0, NULLCHAR },
  63.     { "binary",    dobinary,    0, 0, NULLCHAR },
  64.     { "cd",        doftpcd,    0, 2, "cd <directory>" },
  65.     { "dir",        dolist,        0, 0, NULLCHAR },
  66.     { "list",        dolist,        0, 0, NULLCHAR },
  67.     { "get",        doget,        0, 2, "get <remotefile> <localfile>" },
  68.     { "hash",        dohash,        0, 0, NULLCHAR },
  69.     { "help",     doftphelp,  0, 0, NULLCHAR },
  70.     { "ls",        dols,        0, 0, NULLCHAR },
  71.     { "mget",        domget,        0, 2, "mget <file> [<file> ...]" },
  72.     { "mkdir",    domkdir,    0, 2, "mkdir <directory>" },
  73.     { "mput",        domput,        0, 2, "mput <file> [<file> ...]" },
  74.     { "nlst",        dols,        0, 0, NULLCHAR },
  75.     { "pager",    dopager,    0, 0, NULLCHAR },
  76.     { "quit",        doquit,        0, 0, NULLCHAR },
  77.     { "rmdir",    dormdir,    0, 2, "rmdir <directory>" },
  78.     { "put",        doput,        0, 2, "put <localfile> <remotefile>" },
  79.     { "type",        dotype,        0, 0, NULLCHAR },
  80.     { "verbose",    doverbose,    0, 0, NULLCHAR },
  81.     { "view",     doview,     0, 2, "view <remotefile>" },
  82.     { NULLCHAR,    NULLFP,        0, 0, NULLCHAR },
  83. };
  84.  
  85. /* Handle top-level FTP command */
  86. int
  87. doftp(argc,argv,p)
  88. int argc;
  89. char *argv[];
  90. void *p;
  91. {
  92.     struct session *sp;
  93.     struct ftpcli ftp;
  94.     struct sockaddr_in fsocket;
  95.     int resp,vsave;
  96.     char *buf,*bufsav,*cp;
  97.     int control;
  98.  
  99.     /* Allocate a session control block */
  100.     if((sp = newsession(argv[1],FTP)) == NULLSESSION){
  101.         tprintf("Too many sessions\n");
  102.         return 1;
  103.     }
  104.     memset((char *)&ftp,0,sizeof(ftp));
  105.     ftp.control = ftp.data = -1;
  106.     ftp.verbose = V_NORMAL;
  107.  
  108.     sp->cb.ftp = &ftp;    /* Downward link */
  109.     ftp.session = sp;    /* Upward link */
  110.  
  111.     fsocket.sin_family = AF_INET;
  112.     if(argc < 3)
  113.         fsocket.sin_port = IPPORT_FTP;
  114.     else
  115.         fsocket.sin_port = atoi(argv[2]);
  116.  
  117.     tprintf("Resolving %s... ",sp->name);
  118.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  119.         tprintf(Badhost,sp->name);
  120.         keywait(NULLCHAR,1);
  121.         freesession(sp);
  122.         return 1;
  123.     }
  124.     /* Open the control connection */
  125.     if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
  126.         tprintf("Can't create socket\n");
  127.         keywait(NULLCHAR,1);
  128.         freesession(sp);
  129.         return 1;
  130.     }
  131.     sockmode(sp->s,SOCK_ASCII);
  132.     setflush(sp->s,-1);    /* Flush output only when we call getresp() */
  133.     tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  134.     if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
  135.         goto quit;
  136.     tprintf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
  137.         sp->name);
  138.  
  139.     /* Wait for greeting from server */
  140.     resp = getresp(&ftp,200);
  141.  
  142.     if(resp >= 400)
  143.         goto quit;
  144.     /* Now process responses and commands */
  145.     buf = mallocw(LINELEN);
  146.     while(resp != -1){
  147.         if(resp == 220){
  148.             /* Sign-on banner; prompt for and send USER command */
  149.             getline(sp,"Enter user name: ",buf,LINELEN);
  150.             /* Send the command only if the user response
  151.              * was non-null
  152.              */
  153.             if(buf[0] != '\n'){
  154.                 usprintf(control,"USER %s",buf);
  155.                 resp = getresp(&ftp,200);
  156.             } else
  157.                 resp = 200;    /* dummy */
  158.         } else if(resp == 331){
  159.             /* turn off echo */
  160.             sp->ttystate.echo = 0;
  161.             getline(sp,"Password: ",buf,LINELEN);
  162.             tprintf("\n");
  163.             /* Turn echo back on */
  164.             sp->ttystate.echo = 1;
  165.             /* Send the command only if the user response
  166.              * was non-null
  167.              */
  168.             if(buf[0] != '\n'){
  169.                 usprintf(control,"PASS %s",buf);
  170.                 resp = getresp(&ftp,200);
  171.             } else
  172.                 resp = 200;    /* dummy */
  173.         } else {
  174.             /* Test the control channel first */
  175.             if(sockstate(control) == NULLCHAR)
  176.                 break;
  177.  
  178.             getline(sp,"ftp> ",buf,LINELEN);
  179.  
  180.             /* Copy because cmdparse modifies the original */
  181.             bufsav = strdup(buf);
  182.             if((resp = cmdparse(Ftpcmds,buf,&ftp)) != -1){
  183.                 /* Valid command, free buffer and get another */
  184.                 free(bufsav);
  185.             } else {
  186.                 /* Not a local cmd, send to remote server */
  187.                 usputs(control,bufsav);
  188.                 free(bufsav);
  189.  
  190.                 /* Enable display of server response */
  191.                 vsave = ftp.verbose;
  192.                 ftp.verbose = V_NORMAL;
  193.                 resp = getresp(&ftp,200);
  194.                 ftp.verbose = vsave;
  195.             }
  196.         }
  197.     }
  198.     free(buf);
  199. quit:    cp = sockerr(control);
  200.     tprintf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
  201.      cp != NULLCHAR ? cp : "EOF");
  202.  
  203.     if(ftp.fp != NULLFILE && ftp.fp != stdout)
  204.         fclose(ftp.fp);
  205.     if(ftp.data != -1)
  206.         close_s(ftp.data);
  207.     if(ftp.control != -1)
  208.         close_s(ftp.control);
  209.     keywait(NULLCHAR,1);
  210.     if(ftp.session != NULLSESSION)
  211.         freesession(ftp.session);
  212.     return 0;
  213. }
  214.  
  215. /* Control verbosity level */
  216. static int
  217. doverbose(argc,argv,p)
  218. int argc;
  219. char *argv[];
  220. void *p;
  221. {
  222.     register struct ftpcli *ftp;
  223.  
  224.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  225.         return -1;
  226.     return setshort(&ftp->verbose,"Verbose",argc,argv);
  227. }
  228. /* Enable/disable command batching */
  229. static int
  230. dobatch(argc,argv,p)
  231. int argc;
  232. char *argv[];
  233. void *p;
  234. {
  235.     register struct ftpcli *ftp;
  236.  
  237.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  238.         return -1;
  239.     return setbool(&ftp->batch,"Command batching",argc,argv);
  240. }
  241. /* Set verbosity to high (convenience command) */
  242. static int
  243. dohash(argc,argv,p)
  244. int argc;
  245. char *argv[];
  246. void *p;
  247. {
  248.     register struct ftpcli *ftp;
  249.  
  250.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  251.         return -1;
  252.     ftp->verbose = V_HASH;
  253.     return 0;
  254. }
  255.  
  256. /* Simple help display - displays all commands and parameters */
  257. static int
  258. doftphelp(argc,argv,p)
  259. int argc;
  260. char *argv[];
  261. void *p;
  262. {
  263.     char buf[256];
  264.     sprintf(buf,"%s/ftp.hlp",Helpdir);
  265.     helproutine(argc,argv,Ftpcmds,buf);
  266.     return 0;
  267. }
  268.  
  269. /* Close session */
  270. static int
  271. doquit(argc,argv,p)
  272. int argc;
  273. char *argv[];
  274. void *p;
  275. {
  276.     register struct ftpcli *ftp;
  277.  
  278.     ftp = (struct ftpcli *)p;
  279.     if(ftp == NULLFTP)
  280.         return -1;
  281.     usprintf(ftp->control,"QUIT\n");
  282.     getresp(ftp,200);    /* Get the closing message */
  283.     getresp(ftp,200);    /* Wait for the server to close */
  284.     return -1;
  285. }
  286.  
  287. /* Translate 'cd' to 'cwd' for convenience */
  288. static int
  289. doftpcd(argc,argv,p)
  290. int argc;
  291. char *argv[];
  292. void *p;
  293. {
  294.     register struct ftpcli *ftp;
  295.  
  296.     ftp = (struct ftpcli *)p;
  297.     if(ftp == NULLFTP)
  298.         return -1;
  299.     usprintf(ftp->control,"CWD %s\n",argv[1]);
  300.     log (ftp->control, "FTP %s: CWD %s", ftp->session->name, argv[1]);
  301.     return getresp(ftp,200);
  302. }
  303. /* Translate 'mkdir' to 'xmkd' for convenience */
  304. static int
  305. domkdir(argc,argv,p)
  306. int argc;
  307. char *argv[];
  308. void *p;
  309. {
  310.     register struct ftpcli *ftp;
  311.  
  312.     ftp = (struct ftpcli *)p;
  313.     if(ftp == NULLFTP)
  314.         return -1;
  315.     usprintf(ftp->control,"XMKD %s\n",argv[1]);
  316.     return getresp(ftp,200);
  317. }
  318. /* Translate 'rmdir' to 'xrmd' for convenience */
  319. static int
  320. dormdir(argc,argv,p)
  321. int argc;
  322. char *argv[];
  323. void *p;
  324. {
  325.     register struct ftpcli *ftp;
  326.  
  327.     ftp = (struct ftpcli *)p;
  328.     if(ftp == NULLFTP)
  329.         return -1;
  330.     usprintf(ftp->control,"XRMD %s\n",argv[1]);
  331.     return getresp(ftp,200);
  332. }
  333. static int
  334. dobinary(argc,argv,p)
  335. int argc;
  336. char *argv[];
  337. void *p;
  338. {
  339.     char *args[2];
  340.  
  341.     args[1] = "I";
  342.     return dotype(2,args,p);
  343. }
  344. static int
  345. doascii(argc,argv,p)
  346. int argc;
  347. char *argv[];
  348. void *p;
  349. {
  350.     char *args[2];
  351.  
  352.     args[1] = "A";
  353.     return dotype(2,args,p);
  354. }
  355.  
  356. /* Handle "type" command from user */
  357. static int
  358. dotype(argc,argv,p)
  359. int argc;
  360. char *argv[];
  361. void *p;
  362. {
  363.     register struct ftpcli *ftp;
  364.  
  365.     ftp = (struct ftpcli *)p;
  366.     if(ftp == NULLFTP)
  367.         return -1;
  368.     if(argc < 2){
  369.         switch(ftp->type){
  370.         case IMAGE_TYPE:
  371.             tprintf("Image\n");
  372.             break;
  373.         case ASCII_TYPE:
  374.             tprintf("Ascii\n");
  375.             break;
  376.         case LOGICAL_TYPE:
  377.             tprintf("Logical bytesize %u\n",ftp->logbsize);
  378.             break;
  379.         }
  380.         return 0;
  381.     }
  382.     switch(*argv[1]){
  383.     case 'i':
  384.     case 'I':
  385.     case 'b':
  386.     case 'B':
  387.         ftp->type = IMAGE_TYPE;
  388.         break;
  389.     case 'a':
  390.     case 'A':
  391.         ftp->type = ASCII_TYPE;
  392.         break;
  393.     case 'L':
  394.     case 'l':
  395.         ftp->type = LOGICAL_TYPE;
  396.         ftp->logbsize = atoi(argv[2]);
  397.         break;
  398.     default:
  399.         tprintf("Invalid type %s\n",argv[1]);
  400.         return 1;
  401.     }
  402.     return 0;
  403. }
  404.  
  405. /* Handle "pager" command from user */
  406. static int
  407. dopager(argc,argv,p)
  408. int argc;
  409. char *argv[];
  410. void *p;
  411. {
  412.     register struct ftpcli *ftp;
  413.     struct session *s;
  414.  
  415.     ftp=(struct ftpcli *)p;
  416.     s=ftp->session;
  417.  
  418.     if (ftp==NULLFTP){
  419.         tprintf(Notsess);
  420.         return 1;
  421.     }
  422.  
  423.     return setbool(&s->flowmode,"Pager",argc,argv);
  424. }
  425.  
  426. /* Start view transfer. Syntax: view <remote name>  */
  427. static int
  428. doview(argc,argv,p)
  429. int argc;
  430. char *argv[];
  431. void *p;
  432. {
  433.     char *remotename;
  434.     register struct ftpcli *ftp;
  435.  
  436.     ftp = (struct ftpcli *)p;
  437.     if(ftp == NULLFTP){
  438.         tprintf(Notsess);
  439.         return 1;
  440.     }
  441.     remotename = argv[1];
  442.  
  443.     getsub(ftp,"RETR",remotename,NULLCHAR);
  444.     return 0;
  445. }
  446.  
  447. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  448. static int
  449. doget(argc,argv,p)
  450. int argc;
  451. char *argv[];
  452. void *p;
  453. {
  454.     char *remotename,*localname;
  455.     register struct ftpcli *ftp;
  456.  
  457.     ftp = (struct ftpcli *)p;
  458.     if(ftp == NULLFTP){
  459.         tprintf(Notsess);
  460.         return 1;
  461.     }
  462.     remotename = argv[1];
  463.     if(argc < 3)
  464.         localname = remotename;
  465.     else
  466.         localname = argv[2];
  467.  
  468.     getsub(ftp,"RETR",remotename,localname);
  469.     return 0;
  470. }
  471. /* Get a collection of files */
  472. static int
  473. domget(argc,argv,p)
  474. int argc;
  475. char *argv[];
  476. void *p;
  477. {
  478.     register struct ftpcli *ftp;
  479.     FILE *files;
  480.     char tmpname[L_tmpnam+1];
  481.     char *buf;
  482.     int i;
  483.     long r;
  484.  
  485.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  486.         tprintf(Notsess);
  487.         return 1;
  488.     }
  489.     tmpnam(tmpname);
  490.     buf = mallocw(DIRBUF);
  491.     ftp->state = RECEIVING_STATE;
  492.     for(i=1;i<argc;i++){
  493.         r = getsub(ftp,"NLST",argv[i],tmpname);
  494.         if(ftp->abort)
  495.             break;    /* Aborted */
  496.         if(r == -1 || (files = fopen(tmpname,"r")) == NULLFILE){
  497.             tprintf("Can't NLST %s\n",argv[i]);
  498.             unlink(tmpname);
  499.             continue;
  500.         }
  501.         /* The tmp file now contains a list of the remote files, so
  502.          * go get 'em. Break out if the user signals an abort.
  503.          */
  504.         while(fgets(buf,DIRBUF,files) != NULLCHAR){
  505.             rip(buf);
  506.             getsub(ftp,"RETR",buf,buf);
  507.             if(ftp->abort){
  508.                 /* User abort */
  509.                 ftp->abort = 0;
  510.                 fclose(files);
  511.                 unlink(tmpname);
  512.                 free(buf);
  513.                 ftp->state = COMMAND_STATE;
  514.                 return 1;
  515.             }
  516.         }
  517.         fclose(files);
  518.         unlink(tmpname);
  519.     }
  520.     free(buf);
  521.     ftp->state = COMMAND_STATE;
  522.     ftp->abort = 0;
  523.     return 0;
  524. }
  525. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  526. static int
  527. dolist(argc,argv,p)
  528. int argc;
  529. char *argv[];
  530. void *p;
  531. {
  532.     char *remotename,*localname;
  533.     register struct ftpcli *ftp;
  534.  
  535.     ftp = (struct ftpcli *)p;
  536.     if(ftp == NULLFTP){
  537.         tprintf(Notsess);
  538.         return 1;
  539.     }
  540.     remotename = argv[1];
  541.     if(argc > 2)
  542.         localname = argv[2];
  543.     else
  544.         localname = NULLCHAR;
  545.  
  546.     getsub(ftp,"LIST",remotename,localname);
  547.     return 0;
  548. }
  549. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  550. static int
  551. dols(argc,argv,p)
  552. int argc;
  553. char *argv[];
  554. void *p;
  555. {
  556.     char *remotename,*localname;
  557.     register struct ftpcli *ftp;
  558.  
  559.     ftp = (struct ftpcli *)p;
  560.     if(ftp == NULLFTP){
  561.         tprintf(Notsess);
  562.         return 1;
  563.     }
  564.     remotename = argv[1];
  565.     if(argc > 2)
  566.         localname = argv[2];
  567.     else
  568.         localname = NULLCHAR;
  569.  
  570.     getsub(ftp,"NLST",remotename,localname);
  571.     return 0;
  572. }
  573. /* Common code to LIST/NLST/RETR and mget
  574.  * Returns number of bytes received if successful
  575.  * Returns -1 on error
  576.  */
  577. static long
  578. getsub(ftp,command,remotename,localname)
  579. register struct ftpcli *ftp;
  580. char *command,*remotename,*localname;
  581. {
  582.     unsigned long total;
  583.     FILE *fp;
  584.     int cnt,resp,i,control,savmode;
  585.     char *mode;
  586.     struct sockaddr_in lsocket;
  587.     struct sockaddr_in lcsocket;
  588.     int32 startclk,rate;
  589.     int vsave;
  590.     int typewait = 0;
  591.     int prevstate;
  592.  
  593.     if(ftp == NULLFTP)
  594.         return -1;
  595.     control = ftp->control;
  596.     savmode = ftp->type;
  597.  
  598.     switch(ftp->type){
  599.     case IMAGE_TYPE:
  600.     case LOGICAL_TYPE:
  601.         mode = WRITE_BINARY;
  602.         break;
  603.     case ASCII_TYPE:
  604.         mode = WRITE_TEXT;
  605.         break;
  606.     }
  607.     /* Open the file */
  608.     if(localname == NULLCHAR){
  609.         fp = NULLFILE;
  610.     } else if((fp = fopen(localname,mode)) == NULLFILE){
  611.         tprintf("Can't write %s: %s\n",localname,sys_errlist[errno]);
  612.         return -1;
  613.     }
  614.     /* Open the data connection */
  615.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  616.     listen(ftp->data,0);    /* Accept only one connection */
  617.     prevstate = ftp->state;
  618.     ftp->state = RECEIVING_STATE;
  619.  
  620.     /* Send TYPE message, if necessary */
  621.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0
  622.             || (fp==NULLFILE) )   {
  623.         /* Directory listings are always in ASCII */
  624.         ftp->type = ASCII_TYPE;
  625.     }
  626.     if(ftp->typesent != ftp->type){
  627.         switch(ftp->type){
  628.         case ASCII_TYPE:
  629.             usprintf(control,"TYPE A\n");
  630.             break;
  631.         case IMAGE_TYPE:
  632.             usprintf(control,"TYPE I\n");
  633.             break;
  634.         case LOGICAL_TYPE:
  635.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  636.             break;
  637.         }
  638.         ftp->typesent = ftp->type;
  639.         if(!ftp->batch){
  640.             resp = getresp(ftp,200);
  641.             if(resp == -1 || resp > 299)
  642.                 goto failure;
  643.         } else
  644.             typewait = 1;
  645.     }
  646.     /* Send the PORT message. Use the IP address
  647.      * on the local end of our control connection.
  648.      */
  649.     i = SOCKSIZE;
  650.     getsockname(ftp->data,(char *)&lsocket,&i); /* Get port number */
  651.     i = SOCKSIZE;
  652.     getsockname(ftp->control,(char *)&lcsocket,&i);
  653.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  654.     sendport(control,&lsocket);
  655.     if(!ftp->batch){
  656.         /* Get response to PORT command */
  657.         resp = getresp(ftp,200);
  658.         if(resp == -1 || resp > 299)
  659.             goto failure;
  660.     }
  661.     /* Generate the command to start the transfer */
  662.     if(remotename != NULLCHAR)
  663.         usprintf(control,"%s %s\n",command,remotename);
  664.     else
  665.         usprintf(control,"%s\n",command);
  666.  
  667.     if(ftp->batch){
  668.         /* Get response to TYPE command, if sent */
  669.         if(typewait){
  670.             resp = getresp(ftp,200);
  671.             if(resp == -1 || resp > 299)
  672.                 goto failure;
  673.         }
  674.         /* Get response to PORT command */
  675.         resp = getresp(ftp,200);
  676.         if(resp == -1 || resp > 299)
  677.             goto failure;
  678.     }
  679.     /* Get the intermediate "150" response */
  680.     resp = getresp(ftp,100);
  681.     if(resp == -1 || resp >= 400)
  682.         goto failure;
  683.  
  684.     /* Wait for the server to open the data connection */
  685.     cnt = 0;
  686.     ftp->data = accept(ftp->data,NULLCHAR,&cnt);
  687.     startclk = msclock();
  688.  
  689.     /* If output is to the screen, temporarily disable hash marking */
  690.     vsave = ftp->verbose;
  691.     if(vsave >= V_HASH && fp == NULLFILE)
  692.         ftp->verbose = V_NORMAL;
  693.     total = recvfile(fp,ftp->data,ftp->type,ftp->verbose >= V_HASH);
  694.     /* Immediately close the data connection; some servers (e.g., TOPS-10)
  695.      * wait for the data connection to close completely before returning
  696.      * the completion message on the control channel
  697.      */
  698.     close_s(ftp->data);
  699.     ftp->data = -1;
  700.  
  701. #ifdef    CPM
  702.     if(fp != NULLFILE && ftp->type == ASCII_TYPE)
  703.         putc(CTLZ,fp);
  704. #endif
  705.     if(fp != NULLFILE && fp != stdout)
  706.         fclose(fp);
  707.     if(remotename == NULLCHAR)
  708.         remotename = "";
  709.     if(total == -1){
  710.         tprintf("%s %s: Error/abort during data transfer\n",command,remotename);
  711.     } else if(ftp->verbose >= V_SHORT){
  712.         startclk = msclock() - startclk;
  713.         rate = 0;
  714.         if(startclk != 0){    /* Avoid divide-by-zero */
  715.             if(total < 4294967L) {
  716.                 rate = (total*1000)/startclk;
  717.             } else {    /* Avoid overflow */
  718.                 rate = total/(startclk/1000);
  719.             }
  720.         }
  721.         tprintf("%s %s: %lu bytes in %lu sec (%lu/sec)\n",
  722.          command,remotename, total,startclk/1000,rate);
  723.     }
  724.     /* Get the "Sent" message */
  725.     getresp(ftp,200);
  726.  
  727.     ftp->state = prevstate;
  728.     ftp->verbose = vsave;
  729.     ftp->type = savmode;
  730.     log (ftp->control, "FTP %s: %s remote: %s local: %s",
  731.          ftp->session->name, command, remotename, localname);
  732.     return total;
  733.  
  734. failure:
  735.     /* Error, quit */
  736.     if(fp != NULLFILE && fp != stdout)
  737.         fclose(fp);
  738.     close_s(ftp->data);
  739.     ftp->data = -1;
  740.     ftp->state = prevstate;
  741.     ftp->type = savmode;
  742.     log (ftp->control, "FTP failed %s: %s remote %s local %s\n",
  743.          ftp->session->name, command, remotename, localname);
  744.     return -1;
  745. }
  746. /* Send a file. Syntax: put <local name> [<remote name>] */
  747. static int
  748. doput(argc,argv,p)
  749. int argc;
  750. char *argv[];
  751. void *p;
  752. {
  753.     register struct ftpcli *ftp;
  754.     char *remotename,*localname;
  755.  
  756.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  757.         tprintf(Notsess);
  758.         return 1;
  759.     }
  760.     localname = argv[1];
  761.     if(argc < 3)
  762.         remotename = localname;
  763.     else
  764.         remotename = argv[2];
  765.  
  766.     putsub(ftp,remotename,localname);
  767.     return 0;
  768. }
  769. /* Put a collection of files */
  770. static int
  771. domput(argc,argv,p)
  772. int argc;
  773. char *argv[];
  774. void *p;
  775. {
  776.     register struct ftpcli *ftp;
  777.     FILE *files;
  778.     int i;
  779.     char tmpname[L_tmpnam+1];    
  780.     char *buf;
  781.  
  782.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  783.         tprintf(Notsess);
  784.         return 1;
  785.     }
  786.     tmpnam(tmpname);
  787.     if((files = fopen(tmpname,"w+")) == NULLFILE){
  788.         tprintf("Can't list local files\n");
  789.         unlink(tmpname);
  790.         return 1;
  791.     }
  792.     for(i=1;i<argc;i++)
  793.         getdir(argv[i],0,files);
  794.  
  795.     rewind(files);
  796.     buf = mallocw(DIRBUF);
  797.     ftp->state = SENDING_STATE;
  798.     while(fgets(buf,DIRBUF,files) != NULLCHAR){
  799.         rip(buf);
  800.         putsub(ftp,buf,buf);
  801.         if(ftp->abort)
  802.             break;        /* User abort */
  803.     }
  804.     fclose(files);
  805.     unlink(tmpname);
  806.     free(buf);
  807.     ftp->state = COMMAND_STATE;
  808.     ftp->abort = 0;
  809.     return 0;
  810. }
  811. /* Common code to put, mput.
  812.  * Returns number of bytes sent if successful
  813.  * Returns -1 on error
  814.  */
  815. static long
  816. putsub(ftp,remotename,localname)
  817. register struct ftpcli *ftp;
  818. char *remotename,*localname;
  819. {
  820.     char *mode;
  821.     int i,resp,control;
  822.     unsigned long total;
  823.     FILE *fp;
  824.     struct sockaddr_in lsocket,lcsocket;
  825.     int32 startclk,rate;
  826.     int typewait = 0;
  827.     int prevstate;
  828.  
  829.     control = ftp->control;
  830.     if(ftp->type == IMAGE_TYPE)
  831.         mode = READ_BINARY;
  832.     else
  833.         mode = READ_TEXT;
  834.  
  835.     /* Open the file */
  836.     if((fp = fopen(localname,mode)) == NULLFILE){
  837.         tprintf("Can't read %s: %s\n",localname,sys_errlist[errno]);
  838.         return -1;
  839.     }
  840.     if(ftp->type == ASCII_TYPE && isbinary(fp)){
  841.         tprintf("Warning: type is ASCII and %s appears to be binary\n",localname);
  842.     }
  843.     /* Open the data connection */
  844.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  845.     listen(ftp->data,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.             usprintf(control,"TYPE A\n");
  854.             break;
  855.         case IMAGE_TYPE:
  856.             usprintf(control,"TYPE I\n");
  857.             break;
  858.         case LOGICAL_TYPE:
  859.             usprintf(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(ftp->data,(char *)&lsocket,&i);
  878.     i = SOCKSIZE;
  879.     getsockname(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.     usprintf(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(ftp->data,NULLCHAR,(int *)NULL);
  917.  
  918.     startclk = msclock();
  919.  
  920.     total = sendfile(fp,ftp->data,ftp->type,ftp->verbose >= V_HASH);
  921.     close_s(ftp->data);
  922.     ftp->data = -1;
  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.         tprintf("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.         tprintf("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.     close_s(ftp->data);
  952.     ftp->data = -1;
  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.         tprintf("Not an active FTP session\n");
  980.         return 1;
  981.     }
  982.     ftp = sp->cb.ftp;
  983.     switch(ftp->state){
  984.     case COMMAND_STATE:
  985.         tprintf("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(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(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(s,socket)
  1007. int s;
  1008. struct sockaddr_in *socket;
  1009. {
  1010.     /* Send PORT a,a,a,a,p,p message */
  1011.     usprintf(s,"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.     usflush(ftp->control);
  1031.     line = mallocw(LINELEN);
  1032.     for(;;){
  1033.         /* Get line */
  1034.         if(recvline(ftp->control,line,LINELEN) == -1){
  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.             tprintf("%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.     /* If there's something already there, don't issue prompt */
  1060.     if(socklen(sp->input,0) == 0)
  1061.         tprintf(prompt);
  1062.  
  1063.     usflush(sp->output);
  1064.     return recvline(sp->input,buf,n);
  1065. }
  1066.