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