home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff225.lzh / AmigaTCP / src / ftpcli.c < prev    next >
C/C++ Source or Header  |  1989-06-24  |  15KB  |  686 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "machdep.h"
  4. #include "mbuf.h"
  5. #include "netuser.h"
  6. #include "icmp.h"
  7. #include "timer.h"
  8. #include "tcp.h"
  9. #include "ftp.h"
  10. #include "session.h"
  11. #include "cmdparse.h"
  12. #include "dos.h"
  13.  
  14. extern struct session *current;
  15. extern char nospace[];
  16. #define MAXFILENAME    32
  17. char multiarg[NARG][MAXFILENAME];
  18. int multiargc = 0, curmulti = 0;
  19. struct FileInfoBlock info;
  20. int dodfind = 1;
  21. int docd(),dodir(),doget(),dols(),doput(),dotype(),doabort(), domput();
  22.  
  23. struct cmds ftpabort[] = {
  24.     "abort",     doabort, 0, NULLCHAR, NULLCHAR,
  25.     NULLCHAR,    NULLFP,  0, "Only valid command is \"abort\"", NULLCHAR,
  26. };
  27.  
  28. struct cmds ftpcmds[] = {
  29.     "cd",    docd,   2,    "cd <directory>", "Could not change directory",
  30.     "dir",    dodir,  0,    NULLCHAR,         "Could not complete dir",
  31.     "list",    dodir,  0,    NULLCHAR,         "Could not complete dir",
  32.     "get",    doget,  0,    NULLCHAR,         "Could not complete get",
  33.     "ls",    dols,   0,    NULLCHAR,         "Could not complete ls",
  34.     "mput", domput,    0,    NULLCHAR,     "Could not complete mput",
  35.     "nlst",    dols,   0,    NULLCHAR,         "Could not complete ls",
  36.     "put",    doput,  0,    NULLCHAR,         "Could not complete put",
  37.     "type",    dotype, 0,    NULLCHAR,         "Could not complete type",
  38.     NULLCHAR,    NULLFP, 0, NULLCHAR, NULLCHAR,
  39. };
  40.  
  41. /* Handle top-level FTP command */
  42. doftp(argc,argv)
  43. int argc;
  44. char *argv[];
  45. {
  46.     int32 aton();
  47.     int ftpparse();
  48.     char *inet_ntoa();
  49.     void r_ctl(),s_ctl();
  50.     struct session *s,*newsession();
  51.     struct ftp *ftp,*ftp_create();
  52.     struct tcb *tcb;
  53.     struct socket lsocket,fsocket;
  54.  
  55.     lsocket.address = ip_addr;
  56.     lsocket.port = lport++;
  57.     fsocket.address = aton(argv[1]);
  58.     if(fsocket.address == ip_addr){
  59.         printf("FTPing to yourself not supported\r\n");
  60.         return 1;
  61.     }
  62.     if(argc < 3)
  63.         fsocket.port = FTP_PORT;
  64.     else
  65.         fsocket.port = atoi(argv[2]);
  66.  
  67.     /* Allocate a session control block */
  68.     if((s = newsession()) == NULLSESSION){
  69.         printf("Too many sessions\r\n");
  70.         return 1;
  71.     }
  72.     current = s;
  73.     s->type = FTP;
  74.     s->parse = ftpparse;
  75.  
  76.     /* Allocate an FTP control block */
  77.     if((ftp = ftp_create(0)) == NULLFTP){
  78.         s->type = FREE;
  79.         printf(nospace);
  80.         return 1;
  81.     }
  82.     ftp->state = COMMAND_STATE;
  83.     s->cb.ftp = ftp;    /* Downward link */
  84.     ftp->session = s;    /* Upward link */
  85.  
  86.     /* Now open the control connection */
  87.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  88.         0,r_ctl,NULLVFP,s_ctl,0,(int *)ftp);
  89.     if(tcb == NULLTCB || tcb->state == CLOSED){
  90.         /* This is actually a bit dirty here. About the only time the
  91.          * state will ever be closed here is if we tried to connect to
  92.          * ourselves and got RST'ed.  If this is true then the close
  93.          * upcall will already have freed the TCB and the FTP block,
  94.          * so we're looking at the TCB after it's been freed.
  95.          */
  96.         s->type = FREE;
  97.         return 0;
  98.     }
  99.     ftp->control = tcb;
  100.     go();
  101.     return 0;
  102. }
  103. /* Parse user FTP commands */
  104. int
  105. ftpparse(line,len)
  106. char *line;
  107. int16 len;
  108. {
  109.     struct mbuf *bp;
  110.  
  111.     if(current->cb.ftp->state != COMMAND_STATE){
  112.         /* The only command allowed in data transfer state is ABORT */
  113.         if(cmdparse(ftpabort,line) == -1){
  114.             printf("Transfer in progress; only ABORT is acceptable\r\n");
  115.         }
  116.         fflush(stdout);
  117.         return;
  118.     }
  119.  
  120.     /* Save it now because cmdparse modifies the original */
  121.     bp = qdata(line,len);
  122.  
  123.     if(cmdparse(ftpcmds,line) == -1){
  124.         /* Send it direct */
  125.         if(bp != NULLBUF)
  126.             send_tcp(current->cb.ftp->control,bp);
  127.         else
  128.             printf(nospace);
  129.     } else {
  130.         free_p(bp);
  131.     }
  132.     fflush(stdout);
  133. }
  134. /* Translate 'cd' to 'cwd' for convenience */
  135. static
  136. int
  137. docd(argc,argv)
  138. int argc;
  139. char *argv[];
  140. {
  141.     register struct ftp *ftp;
  142.  
  143.     ftp = current->cb.ftp;
  144.     return sndmsg(ftp,"CWD %s\r\n",argv[1]);
  145. }
  146.  
  147.  
  148. /* Handle "type" command from user */
  149. static
  150. int
  151. dotype(argc,argv)
  152. int argc;
  153. char *argv[];
  154. {
  155.     register struct ftp *ftp;
  156.  
  157.     ftp = current->cb.ftp;
  158.     if(argc < 2){
  159.         switch(ftp->type){
  160.         case IMAGE_TYPE:
  161.             printf("Image\r\n");
  162.             break;
  163.         case ASCII_TYPE:
  164.             printf("Ascii\r\n");
  165.             break;
  166.         }
  167.         return 0;
  168.     }
  169.     switch(*argv[1]){
  170.     case 'i':
  171.     case 'b':
  172.         ftp->type = IMAGE_TYPE;
  173.         break;
  174.     case 'a':
  175.         ftp->type = ASCII_TYPE;
  176.         break;
  177.     default:
  178.         printf("Invalid type %s\r\n",argv[1]);
  179.         return 1;
  180.     }
  181.     /* Send a TYPE message */
  182.     return sndmsg(ftp,"TYPE %s\r\n",(ftp->type == ASCII_TYPE) ? "A" : "I");
  183. }
  184. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  185. static
  186. doget(argc,argv)
  187. int argc;
  188. char *argv[];
  189. {
  190.     void r_ftpd(),s_ftp();
  191.     char *index(),*remotename,*localname;
  192.     register struct ftp *ftp;
  193.  
  194.     ftp = current->cb.ftp;
  195.     if(ftp == NULLFTP){
  196.         printf("Not an FTP session!\r\n");
  197.         return 1;
  198.     }
  199.     if(argc < 2){
  200.         printf("File?\r\n");
  201.         return 1;
  202.     }
  203.     remotename = argv[1];
  204.     if(argc < 3)
  205.         localname = remotename;
  206.     else
  207.         localname = argv[2];
  208.  
  209.     if(ftp->fp != NULLFILE)
  210.         fclose(ftp->fp);
  211.  
  212.     if((ftp->fp = fopen(localname,"w")) == NULLFILE){
  213.         printf("Cannot write %s\r\n",localname);
  214.         return 1;
  215.     }
  216.     ftp->state = RECEIVING_STATE;
  217.     ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
  218.  
  219.     /* Generate the command to start the transfer */
  220.     return sndmsg(ftp,"RETR %s\r\n",remotename);
  221. }
  222. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  223. static
  224. dodir(argc,argv)
  225. int argc;
  226. char *argv[];
  227. {
  228.     void r_ftpd(),s_ftp();
  229.     char *index(),*localname;
  230.     register struct ftp *ftp;
  231.  
  232.     ftp = current->cb.ftp;
  233.     if(ftp == NULLFTP){
  234.         printf("Not an FTP session!\r\n");
  235.         return 1;
  236.     }
  237.     if(argc < 3)
  238. #ifdef    CPM
  239.         localname = "con:";
  240. #endif
  241. #ifdef    MSDOS
  242.         localname = "con";
  243. #endif
  244. #ifdef    UNIX
  245.         localname = "/dev/tty";
  246. #endif
  247. #ifdef  XENIX
  248.                 localname = "/dev/tty";
  249. #endif
  250. #ifdef  AMIGA
  251.         localname = "*";
  252. #endif  AMIGA
  253.     else
  254.         localname = argv[2];
  255.  
  256.     if(ftp->fp != NULLFILE)
  257.         fclose(ftp->fp);
  258.  
  259.     if((ftp->fp = fopen(localname,"w")) == NULLFILE){
  260.         printf("Cannot write %s\r\n",localname);
  261.         return 1;
  262.     }
  263.     ftp->state = RECEIVING_STATE;
  264.     ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
  265.     /* Generate the command to start the transfer
  266.      * It's done this way to avoid confusing the 4.2 FTP server
  267.      * if there's no argument
  268.      */
  269.     if(argc > 1)
  270.         return sndmsg(ftp,"LIST %s\r\n",argv[1]);
  271.     else
  272.         return sndmsg(ftp,"LIST\r\n","");
  273. }
  274. /* Abbreviated (name only) list of remote directory.
  275.  * Syntax: ls <remote directory/file> [<local name>]
  276.  */
  277. static
  278. dols(argc,argv)
  279. int argc;
  280. char *argv[];
  281. {
  282.     void r_ftpd(),s_ftp();
  283.     char *index(),*localname;
  284.     register struct ftp *ftp;
  285.  
  286.     ftp = current->cb.ftp;
  287.     if(ftp == NULLFTP){
  288.         printf("Not an FTP session!\r\n");
  289.         return 1;
  290.     }
  291.     if(argc < 3)
  292. #ifdef    CPM
  293.         localname = "con:";
  294. #endif
  295. #ifdef    MSDOS
  296.         localname = "con";
  297. #endif
  298. #ifdef    UNIX
  299.         localname = "/dev/tty";
  300. #endif
  301. #ifdef    XENIX
  302.         localname = "/dev/tty";
  303. #endif
  304. #ifdef  AMIGA
  305.         localname = "*";
  306. #endif  AMIGA
  307.     else
  308.         localname = argv[2];
  309.  
  310.     if(ftp->fp != NULLFILE)
  311.         fclose(ftp->fp);
  312.  
  313.     if((ftp->fp = fopen(localname,"w")) == NULLFILE){
  314.         printf("Cannot write %s\r\n",localname);
  315.         return 1;
  316.     }
  317.     ftp->state = RECEIVING_STATE;
  318.     ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
  319.     /* Generate the command to start the transfer */
  320.     if(argc > 1)
  321.         return sndmsg(ftp,"NLST %s\r\n",argv[1]);
  322.     else
  323.         return sndmsg(ftp,"NLST\r\n","");
  324. }
  325. doone(localname, remotename)
  326. char *localname, *remotename;
  327. {
  328.     void t_ftpd(),s_ftp();
  329.     struct ftp *ftp;
  330.  
  331.     if((ftp = current->cb.ftp) == NULLFTP){
  332.         printf("Not an FTP session!\r\n");
  333.         return 1;
  334.     }
  335.     if(ftp->fp != NULLFILE)
  336.         fclose(ftp->fp);
  337.  
  338.     if((ftp->fp = fopen(localname,"r")) == NULLFILE){
  339.         printf("Cannot read %s\r\n",localname);
  340.         return 1;
  341.     }
  342.     ftp->state = SENDING_STATE;
  343.     ftpsetup(ftp,NULLVFP,t_ftpd,s_ftp);
  344.  
  345.     /* Generate the command to start the transfer */
  346.     return sndmsg(ftp,"STOR %s\r\n",remotename);
  347. }
  348. /* Start transmit. Syntax: put <local name> [<remote name>] */
  349. static
  350. doput(argc,argv)
  351. int argc;
  352. char *argv[];
  353. {
  354.  
  355.     char *remotename,*localname;
  356.  
  357.     if(argc < 2){
  358.         printf("File?\r\n");
  359.         return 1;
  360.     }
  361.     localname = argv[1];
  362.     if(argc < 3)
  363.         remotename = localname;
  364.     else
  365.         remotename = argv[2];
  366.     doone(localname, remotename);
  367. }
  368.  
  369. donextmulti()
  370. {
  371.  
  372.   int error;
  373.     struct ftp *ftp;
  374.  
  375.     if((ftp = current->cb.ftp) == NULLFTP){
  376.         printf("Not an FTP session!\r\n");
  377.         return 1;
  378.     }
  379. again:
  380.   if (multiargc > 0)
  381.     {
  382.       if (dodfind)
  383.     {
  384.       error = dfind(&info, multiarg[curmulti], 0);
  385.       if (error < 0)
  386.             printf("No files matching %s\n", multiarg[curmulti]);
  387.       dodfind = 0;
  388.     }
  389.        else
  390.         error = dnext(&info);
  391.       if (error == 0)
  392.         doone(info.fib_FileName, info.fib_FileName);
  393.        else
  394.         {
  395.           curmulti++;
  396.           multiargc--;
  397.           dodfind = 1;
  398.       goto again;
  399.         }
  400.     }
  401.   if (multiargc == 0)
  402.     {
  403.       printf("Multi put is done\n");
  404.       ftp->state = COMMAND_STATE;
  405.     }
  406. }
  407. /* Start transmit. Syntax: put <local name> [<remote name>] */
  408. static
  409. domput(argc,argv)
  410. int argc;
  411. char *argv[];
  412. {
  413.     int i;
  414.     char *remotename,*localname;
  415.     struct ftp *ftp;
  416.  
  417.     if((ftp = current->cb.ftp) == NULLFTP){
  418.         printf("Not an FTP session!\r\n");
  419.         return 1;
  420.     }
  421.     if(argc < 2){
  422.         printf("File list?\r\n");
  423.         return 1;
  424.     }
  425.     for(i = 0; i < argc - 1; i++)
  426.         strncpy(multiarg[i], argv[i+1], MAXFILENAME);
  427.     multiargc = argc - 1;
  428.     for(i = 0; i < multiargc; i++)
  429.         printf("copy %s out\n", multiarg[i]);
  430.     curmulti = 0;
  431.     dodfind = 1;
  432.     donextmulti();
  433. }
  434. /* Abort a GET or PUT operation in progress. Note: this will leave
  435.  * the partial file on the local or remote system
  436.  */
  437. doabort(argc,argv)
  438. int argc;
  439. char *argv[];
  440. {
  441.     register struct ftp *ftp;
  442.  
  443.     ftp = current->cb.ftp;
  444.  
  445.     /* Close the local file */
  446.     fclose(ftp->fp);
  447.     ftp->fp = NULLFILE;
  448.  
  449.     switch(ftp->state){
  450.     case SENDING_STATE:
  451.         /* Send a premature EOF.
  452.          * Unfortunately we can't just reset the connection
  453.          * since the remote side might end up waiting forever
  454.          * for us to send something.
  455.          */
  456.         close_tcp(ftp->data);
  457.         multiargc = 0;
  458.         printf("Put aborted\r\n");
  459.         break;
  460.     case RECEIVING_STATE:
  461.         /* Just exterminate the data channel TCB; this will
  462.          * generate a RST on the next data packet which will
  463.          * abort the sender
  464.          */
  465.         del_tcp(ftp->data);
  466.         ftp->data = NULLTCB;
  467.         printf("Get aborted\r\n");
  468.         break;
  469.     }
  470.     ftp->state = COMMAND_STATE;
  471.     fflush(stdout);
  472. }
  473. /* create data port, and send PORT message */
  474. static
  475. ftpsetup(ftp,recv,send,state)
  476. struct ftp *ftp;
  477. void (*send)();
  478. void (*recv)();
  479. void (*state)();
  480. {
  481.     struct socket lsocket,fsocket;
  482.     struct mbuf *bp;
  483.  
  484.     lsocket.address = ip_addr;
  485.     lsocket.port = lport++;
  486.     fsocket.address = ftp->control->conn.remote.address;
  487.     fsocket.port = FTPD_PORT;
  488.  
  489.     /* Compose and send PORT a,a,a,a,p,p message */
  490.  
  491.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  492.         printf(nospace);
  493.         return;
  494.     }
  495.     /* I know, this looks gross, but it works! */
  496.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  497.         hibyte(hiword(lsocket.address)),
  498.         lobyte(hiword(lsocket.address)),
  499.         hibyte(loword(lsocket.address)),
  500.         lobyte(loword(lsocket.address)),
  501.         hibyte(lsocket.port),
  502.         lobyte(lsocket.port));
  503.     bp->cnt = strlen(bp->data);
  504.     send_tcp(ftp->control,bp);
  505.  
  506.     /* Post a listen on the data connection */
  507.     ftp->data = open_tcp(&lsocket,&fsocket,TCP_PASSIVE,0,
  508.         recv,send,state,0,(int *)ftp);
  509.  
  510. }
  511. /* FTP control channel receiver upcall routine */
  512. void
  513. r_ctl(tcb,cnt)
  514. register struct tcb *tcb;
  515. int16 cnt;
  516. {
  517.     struct mbuf *bp;
  518.     struct ftp *ftp;
  519.  
  520.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  521.         /* Unknown connection; kill it */
  522.         close_tcp(tcb);
  523.         return;
  524.     }
  525.     /* Hold output if we're not the current session */
  526.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
  527.         return;
  528.  
  529.     if(recv_tcp(tcb,&bp,cnt) > 0){
  530.         while(bp != NULLBUF){
  531. #ifndef    AMIGA
  532.             fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
  533. #else
  534.             register unsigned len = bp->cnt;
  535.             register char *dp = bp->data;
  536.  
  537.             while (len--)
  538.                 putchar(*dp++);
  539. #endif
  540.             bp = free_mbuf(bp);
  541.         }
  542.         fflush(stdout);
  543.     }
  544. }
  545.  
  546. /* Control channel state change upcall routine */
  547. static
  548. void
  549. s_ctl(tcb,old,new)
  550. register struct tcb *tcb;
  551. char old,new;
  552. {
  553.     void ftp_delete();
  554.     struct ftp *ftp;
  555.     char notify = 0;
  556.     extern char *tcpstates[];
  557.     extern char *reasons[];
  558.     extern char *unreach[];
  559.     extern char *exceed[];
  560.  
  561.     /* Can't add a check for unknown connection here, it would loop
  562.      * on a close upcall! We're just careful later on.
  563.      */
  564.     ftp = (struct ftp *)tcb->user;
  565.  
  566.     if(current != NULLSESSION && current->cb.ftp == ftp)
  567.         notify = 1;
  568.  
  569.     switch(new){
  570.     case CLOSE_WAIT:
  571.         if(notify)
  572.             printf("%s\r\n",tcpstates[new]);
  573.         close_tcp(tcb);
  574.         break;
  575.     case CLOSED:    /* heh heh */
  576.         if(notify){
  577.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  578.             if(tcb->reason == NETWORK){
  579.                 switch(tcb->type){
  580.                 case DEST_UNREACH:
  581.                     printf(": %s unreachable",unreach[tcb->code]);
  582.                     break;
  583.                 case TIME_EXCEED:
  584.                     printf(": %s time exceeded",exceed[tcb->code]);
  585.                     break;
  586.                 }
  587.             }
  588.             printf(")\r\n");
  589.             cmdmode();
  590.         }
  591.         del_tcp(tcb);
  592.         if(ftp != NULLFTP)
  593.             ftp_delete(ftp);
  594.         break;
  595.     default:
  596.         if(notify)
  597.             printf("%s\r\n",tcpstates[new]);
  598.         break;
  599.     }
  600.     if(notify)
  601.         fflush(stdout);
  602. }
  603. /* FTP client data channel connection state change upcall handler */
  604. static
  605. void
  606. s_ftp(tcb,old,new)
  607. struct tcb *tcb;
  608. char old,new;
  609. {
  610.     struct ftp *ftp;
  611.  
  612.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  613.         /* Unknown connection, kill it */
  614.         close_tcp(tcb);
  615.         return;
  616.     }
  617.     switch(new){
  618.     case FINWAIT2:
  619.     case TIME_WAIT:
  620.         if(ftp != NULLFTP && ftp->state == SENDING_STATE){
  621.             /* We've received an ack of our FIN, so
  622.              * return to command mode
  623.              */
  624.             if (multiargc <= 0)
  625.                 {
  626.                     printf("Multiput done\r\n");
  627.                     ftp->state = COMMAND_STATE;
  628.                 }
  629.             if(current != NULLSESSION && current->cb.ftp == ftp){
  630.                 printf("Put complete, %lu bytes sent\r\n",
  631.                     tcb->snd.una - tcb->iss - 2);
  632.                 fflush(stdout);
  633.             }
  634.         }
  635.         break;        
  636.     case CLOSE_WAIT:
  637.         close_tcp(tcb);
  638.         if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){
  639.             /* End of file received on incoming file */
  640. #ifdef    CPM
  641.             if(ftp->type == ASCII_TYPE)
  642.                 putc(CTLZ,ftp->fp);
  643. #endif
  644.             fclose(ftp->fp);
  645.             ftp->fp = NULLFILE;
  646.             ftp->state = COMMAND_STATE;
  647.             if(current != NULLSESSION && current->cb.ftp == ftp){
  648.                 printf("Get complete, %lu bytes received\r\n",
  649.                     tcb->rcv.nxt - tcb->irs - 2);
  650.                 fflush(stdout);
  651.             }
  652.         }
  653.         break;
  654.     case CLOSED:
  655.         if(ftp != NULLFTP)
  656.             ftp->data = NULLTCB;
  657.         del_tcp(tcb);
  658.         if (multiargc > 0)
  659.           donextmulti();
  660.         break;
  661.     }
  662.  
  663. }
  664. /* Send a message on the control channel */
  665. /*VARARGS*/
  666. static
  667. int
  668. sndmsg(ftp,fmt,arg)
  669. struct ftp *ftp;
  670. char *fmt;
  671. char *arg;
  672. {
  673.     struct mbuf *bp;
  674.     int16 len;
  675.  
  676.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  677.     if((bp = alloc_mbuf(len)) == NULLBUF){
  678.         printf(nospace);
  679.         return 1;
  680.     }
  681.     sprintf(bp->data,fmt,arg);
  682.     bp->cnt = strlen(bp->data);
  683.     send_tcp(ftp->control,bp);
  684.     return 0;
  685. }
  686.