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