home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / util / comm / news102.sit / NewsWatcher / source / ftplow.c < prev    next >
Text File  |  1991-04-03  |  15KB  |  598 lines

  1. /*----------------------------------------------------------
  2. #
  3. #    NewsWatcher    - Macintosh NNTP Client Application
  4. #
  5. #    Written by Steven Falkenburg
  6. #    ⌐1990 Apple Computer, Inc.
  7. #
  8. #-----------------------------------------------------------
  9. #
  10. #    ftplow.c
  11. #
  12. #    This file contains the low-level implementation of the
  13. #    file transfer protocol.  This protocol is implemented
  14. #    using the TCP protocol, from calls in TCPHi.c and
  15. #    TCPRoutines.c
  16. #
  17. #-----------------------------------------------------------*/
  18.  
  19. #include "compat.h"
  20. #include <string.h>
  21. #include <stdio.h>
  22.  
  23. #ifdef PROTOS
  24. #include <Types.h>
  25. #include <Memory.h>
  26. #include <OSUtils.h>
  27. #include <Packages.h>
  28. #include <Desk.h>
  29. #include <CursorCtl.h>
  30. #include <Strings.h>
  31. #include <Errors.h>
  32. #include <StdIO.h>
  33. #include <Lists.h>
  34. #endif
  35.  
  36. #include "MacTCPCommonTypes.h"
  37. #include "TCPPB.h"
  38. #include "TCPHi.h"
  39. #include "TCPRoutines.h"
  40. #include "nntp.h"
  41. #include "FTPLow.h"
  42. #include "netstuff.h"
  43. #include "miscstuff.h"
  44.  
  45. /* local protos */
  46.  
  47. OSErr SayHello(unsigned long connID);
  48. OSErr UserAuth(unsigned long connID,char *userID,char *password);
  49. OSErr SendCommand(unsigned long connID,char *command,Ptr *response,unsigned short *respLen);
  50. OSErr MakeDataStream(unsigned long *dataConnID,tcp_port lPort,TCPiopb **pBlock);
  51. OSErr GetDirectData(unsigned long dataConnID,Ptr *directory,TCPiopb *pBlock);
  52. OSErr SendFileData(unsigned long dataConnID,char *fileName,char *data,TransType tType,long size,TCPiopb *pBlock);
  53. OSErr RemoveDataStream(unsigned long dataConnID);
  54. OSErr SendPortComm(unsigned long dataConnID, short portNum);
  55. tcp_port GetPortNum(void);
  56. OSErr FTPTextMode(unsigned long connID);
  57. void FixCRLF(char *data, long *length);
  58. void AddCRLF(char **data, long *length);
  59.  
  60. #define    cSleepTime        20L
  61. #define    cWaitForOpen    120L
  62. #define    cRBufLen        32768L
  63. #define    cDataLen        32768
  64. #define    cRBufDataLen    65535L
  65. #define    cXferBlockSize    32768
  66. #define    cSendBlockSize    16384
  67. #define    cControlPort    21
  68. #define    cInvalidHost    -1
  69. #define    cConnErr        -2
  70. #define    cAccessErr        -3
  71. #define    cNotCompErr        -4
  72. #define    kConnectionTimeOut 25
  73.  
  74. ProcPtr gStatusProc;        /* status procedure for callbacks */
  75.  
  76.  
  77. /*    SayHello is called after a control-port connection has been established.
  78.     It checks the status of the connection.
  79. */
  80.  
  81. OSErr SayHello(unsigned long connID)
  82. {
  83.     OSErr err;
  84.     Ptr dataPtr;
  85.     unsigned short dataLen = cDataLen;
  86.     
  87.     dataPtr = MyNewPtr((unsigned long)cDataLen);
  88.     if (MyMemErr() != noErr)
  89.         return MyMemErr();
  90.     if ((err = RecvData(connID,dataPtr,&dataLen,false)) == noErr) {
  91.         err = (strncmp((char *)dataPtr,"220",3)==0) ? noErr : cConnErr;
  92.     }
  93.     MyDisposPtr(dataPtr);
  94.     return err;
  95. }
  96.  
  97.  
  98. /*    UserAuth is called once a control connection has been opened and
  99.     checked.  It sends the user's name and password across the network
  100.     for authentication.  If the user is sucessfully authenticated, a
  101.     code of noErr is returned.
  102. */
  103.  
  104. OSErr UserAuth(unsigned long connID,char *userID,char *password)
  105. {
  106.     OSErr err;
  107.     Ptr sendBuff;
  108.     Ptr respBuff;
  109.     unsigned short respLen;
  110.     
  111.     sendBuff = MyNewPtr((unsigned long) (10+(strlen(userID)>strlen(password) ? strlen(userID) : strlen(password))));
  112.     if (MyMemErr() != noErr)
  113.         return MyMemErr();
  114.     strcpy((char *)sendBuff,"USER ");
  115.     strcat((char *)sendBuff,userID);
  116.     strcat((char *)sendBuff,CRLF);
  117.     if ((err = SendCommand(connID,sendBuff,&respBuff,&respLen)) == noErr) {
  118.         if ((err = (strncmp((char *)respBuff,"331",3)==0) ? noErr : cAccessErr) == noErr) {
  119.             MyDisposPtr(respBuff);
  120.             strcpy((char *)sendBuff,"PASS ");
  121.             strcat((char *)sendBuff,password);
  122.             strcat((char *)sendBuff,CRLF);
  123.             if ((err = SendCommand(connID,sendBuff,&respBuff,&respLen)) == noErr) {
  124.                 err = (strncmp((char *)respBuff,"230",3)==0) ? noErr : cAccessErr;
  125.             }
  126.         }
  127.     }
  128.     MyDisposPtr(sendBuff);
  129.     MyDisposPtr(respBuff);
  130.     return err;
  131. }
  132.  
  133.  
  134. /*    SendCommand is used to send a command along a TCP connection and get a
  135.     response back.  It is useful in handshaking protocols such as the one
  136.     used to set up transfers for FTP.
  137. */
  138.  
  139. OSErr SendCommand(unsigned long connID,char *command,Ptr *response,unsigned short *respLen)
  140. {
  141.     OSErr err;
  142.     unsigned short respLeft,oneLen;
  143.     char *bufPtr;
  144.     
  145.     respLeft = (unsigned short) cDataLen - 1;
  146.     *respLen = 0;
  147.     bufPtr = *response = MyNewPtr((unsigned long)cDataLen);
  148.     *bufPtr = '\0';
  149.     if (MyMemErr() != noErr)
  150.         return MyMemErr();
  151.  
  152.     if ((err = SendData(connID,(Ptr)command,(unsigned short)strlen(command),false)) == noErr)
  153.         do {
  154.             oneLen = respLeft;
  155.             err = RecvData(connID,bufPtr,&oneLen,false);
  156.             if (err==noErr) {
  157.                 bufPtr += oneLen;
  158.                 *bufPtr = '\0';
  159.                 *respLen += oneLen;
  160.                 respLeft -= oneLen;
  161.             }
  162.         }
  163.         while (err==noErr && respLeft > 0 && *(bufPtr-1)!=LF);
  164.     
  165.     if (err == noErr)
  166.         (gStatusProc)(*response);
  167.     
  168.     return err;
  169. }
  170.  
  171.  
  172. /*    MakeDataStream is called when a data transfer is about to be started.
  173.     It sets up a secondary TCP communications channed between the client
  174.     and server for the transfer of data.
  175. */
  176.  
  177. OSErr MakeDataStream(unsigned long *dataConnID,tcp_port localPort,TCPiopb **pBlock)
  178. {
  179.     OSErr err;
  180.     Ptr recvBPtr;
  181.     unsigned long recvBLen = cRBufDataLen;
  182.     long remoteHost = 0;
  183.     short remotePort = 0;
  184.     
  185.     recvBPtr = MyNewPtr(recvBLen);
  186.     if (MyMemErr() != noErr)
  187.         return MyMemErr();
  188.     err = CreateStream(dataConnID,recvBLen);
  189.     if (err == noErr)
  190.         AsyncWaitForConnection(*dataConnID,kConnectionTimeOut,localPort,remoteHost,
  191.                                 remotePort,pBlock);
  192.     return err;
  193. }
  194.  
  195.  
  196. /*    GetDirectData is called to receive textual data, such as the response
  197.     from an 'ls' command.  The data received is passed back in an allocated
  198.     pointer.
  199. */
  200.  
  201. OSErr GetDirectData(unsigned long dataConnID,Ptr *directory,TCPiopb *pBlock)
  202. {
  203.     OSErr err;
  204.     unsigned short length = cXferBlockSize;
  205.     long remoteHost;
  206.     short remotePort;
  207.     Ptr transferBlock;
  208.     Size totalLength = 0;
  209.     OSErr err2;
  210.     Handle theHndl;
  211.     
  212.     while (pBlock->ioResult > 0)
  213.         GiveTime(cSleepTime);
  214.         
  215.     err = AsyncGetConnectionData(pBlock,&remoteHost,&remotePort);
  216.     
  217.     if (err == noErr) {
  218.         theHndl = MyNewHandle((unsigned long)cXferBlockSize);
  219.         if (MyMemErr() != noErr)
  220.             return MyMemErr();
  221.         HLock(theHndl);
  222.         *directory = transferBlock = *theHndl;
  223.         **directory = '\0';
  224.         do {
  225.             length = cXferBlockSize;
  226.             err = RecvData(dataConnID,transferBlock,&length,false);
  227.             if (err == noErr || err == connectionClosing) {
  228.                 totalLength += length;
  229.                 HUnlock(theHndl);
  230.                 MySetHandleSize(theHndl,totalLength+cXferBlockSize);
  231.                 HLock(theHndl);
  232.                 *directory = *theHndl;
  233.                 if ((err2 = MyMemErr()) != noErr) {
  234.                     MyDisposHandle(theHndl);
  235.                     return err2;
  236.                 }
  237.                 transferBlock += (unsigned long) length;
  238.             }
  239.             GiveTime(cSleepTime);
  240.         } while (err == noErr);
  241.         if (err == connectionClosing) {
  242.             err = noErr;
  243.             if (totalLength)
  244.                 FixCRLF(*directory,&totalLength);
  245.             *directory = MyNewPtr(totalLength);
  246.             if ((err = MyMemErr()) == noErr)
  247.                 BlockMove(*theHndl,*directory,totalLength);
  248.         }
  249.         HUnlock(theHndl);
  250.         MyDisposHandle(theHndl);
  251.     }
  252.     return err;
  253. }
  254.  
  255.  
  256. /*    SendFileData is called once a transfer has been initiated to send data
  257.     from a client to a server.  The data is sent out along the secondary
  258.     data channel which has been negotiated between the hosts.
  259. */
  260.  
  261. /* warning-- char *data is disposed by this routine!!!!!!!!! */
  262.  
  263. OSErr SendFileData(unsigned long dataConnID,char *fileName,char *data,
  264.                     TransType tType,long size,TCPiopb *pBlock)
  265. {
  266.     #pragma unused (fileName,tType)
  267.     
  268.     OSErr err;
  269.     long remoteHost;
  270.     short remotePort;
  271.     long index;
  272.     unsigned short sendLen;
  273.     
  274.     /* wait for connect to complete */
  275.     
  276.     while (pBlock->ioResult > 0)
  277.         GiveTime(cSleepTime);
  278.             
  279.     /* get completion code from data connection opening */
  280.     
  281.     err = AsyncGetConnectionData(pBlock,&remoteHost,&remotePort);
  282.     
  283.     if (err == noErr) { 
  284.         AddCRLF(&data,&size);
  285.         index = 0;
  286.         while (index < size && err==noErr) {
  287.             sendLen = (unsigned short)cSendBlockSize;
  288.             if ((index+(unsigned long)sendLen) > size)
  289.                 sendLen = (unsigned short)size-(unsigned short)index;
  290.             err = SendData(dataConnID,data+index,sendLen,false);
  291.             index += cSendBlockSize;
  292.         }
  293.         MyDisposPtr(data);
  294.     }
  295.     
  296.     return err;
  297. }
  298.  
  299.  
  300. /*    RemoveDataStream is called once a data transfer has been completed.
  301.     It shuts down the secondary data channel between the client and
  302.     server which was used solely for one data transfer.
  303. */
  304.  
  305. OSErr RemoveDataStream(unsigned long dataConnID)
  306. {
  307.     OSErr err;
  308.     
  309.     CloseConnection(dataConnID);
  310.     err = ReleaseStream(dataConnID);
  311.     return err;
  312. }
  313.  
  314.  
  315. /*    GetPortNum is a routine which picks a random port number for use in data
  316.     transfers.
  317. */
  318.  
  319. tcp_port GetPortNum(void)
  320. {
  321.     return (40000 + (((unsigned long) TickCount()) & 0x3fff)); /* get a random port */
  322. }
  323.  
  324.  
  325. /*    SendPortComm is called to send the 'PORT' command, telling the server which
  326.     port is to be used for the transfer of data between server and client.
  327. */
  328.  
  329. OSErr SendPortComm(unsigned long dataConnID, short portNum)
  330. {
  331.     OSErr err;
  332.     Ptr respBuff;
  333.     unsigned short respLen;
  334.     ip_addr myNetNum;
  335.     char commandStr[40];
  336.     
  337.     if ((err = GetMyIP(&myNetNum)) == noErr) {
  338.         sprintf(commandStr,"PORT %lu,%lu,%lu,%lu,%u,%u",
  339.             ( myNetNum >> 24),                    /* converts addr to dot notation */
  340.             ((myNetNum & 0x00FF0000) >> 16),    
  341.             ((myNetNum & 0x0000FF00) >> 8),
  342.             ( myNetNum & 0x000000FF),
  343.             ( portNum >> 8),
  344.             ( portNum & 0x00FF));
  345.         strcat(commandStr,CRLF);
  346.         err = SendCommand(dataConnID,commandStr,&respBuff,&respLen);
  347.         MyDisposPtr(respBuff);
  348.     }
  349.     return err;
  350. }
  351.  
  352.  
  353. /*    FTPTextMode is called to switch the mode of the data transfer to text mode.
  354. */
  355.  
  356. OSErr FTPTextMode(unsigned long connID)
  357. {
  358.     Ptr respBuff;
  359.     unsigned short respLen;
  360.     OSErr err;
  361.     char commandStr[256];
  362.     
  363.     strcpy(commandStr,"TYPE A");
  364.     strcat(commandStr,CRLF);
  365.     
  366.     if ((err = SendCommand(connID,commandStr,&respBuff,&respLen)) == noErr) {
  367.         err = (strncmp((char *)respBuff,"200",3)==0);
  368.     }
  369.     MyDisposPtr(respBuff);
  370.     return err;
  371. }
  372.  
  373.  
  374. /*    FixCRLF removes the LF from CRLF pairs received on the data connection.
  375. */
  376.  
  377. void FixCRLF(char *data, long *length)
  378. {
  379.     register char *source,*dest;
  380.     
  381.     if (*length > 0) {
  382.         source = dest = data;
  383.         while ((source - data) < (*length-1)) {
  384.             if (*source == LF)
  385.                 source++;
  386.             *dest++ = *source++;
  387.         }
  388.         if (*source != LF && (source - data) < *length)
  389.             *dest++ = *source++;
  390.         *length = dest - data;
  391.     }
  392. }
  393.  
  394.  
  395. /*    AddCRLF adds a LF to each CR for data to be sent to the remote host
  396.     in text (ascii) mode.
  397. */
  398.  
  399. void AddCRLF(char **data, long *length)
  400. {
  401.     register char *curOld,*curNew,*newData;
  402.     
  403.     newData = curNew = MyNewPtr((unsigned long)cSendBlockSize*2);
  404.     if (MyMemErr() != noErr)
  405.         return;
  406.         
  407.     curOld = *data;
  408.  
  409.     if (*curOld == LF)
  410.         curOld++;
  411.  
  412.     while ( (curOld-*data) < *length ) {
  413.         *curNew++ = *curOld++;
  414.         if (*(curOld-1) == CR && *curOld != LF)
  415.             *curNew++ = LF;
  416.     }
  417.  
  418.     *length = curNew-newData;
  419.     MyDisposPtr(*data);
  420.     *data = newData;
  421. }
  422.  
  423.  
  424. /*------------------------------------------------------------*/
  425.  
  426.  
  427. /*    FTPInit    is called to initialize the necessary drivers for FTP connections.
  428.     In addition, it registers a callback routine for status messages.
  429. */
  430.  
  431. OSErr FTPInit(ProcPtr statusproc)
  432. {
  433.     gStatusProc = statusproc;
  434.     return InitNetwork();
  435. }
  436.  
  437.  
  438. /*    FTPFinish is called when all FTP transactions have been completed.
  439.     In this implementation, the routine actually does nothing.
  440. */
  441.  
  442. OSErr FTPFinish(void)
  443. {
  444.     return noErr;
  445. }
  446.  
  447.  
  448. /*    FTPConnect sets up an FTP control connection between the local machine
  449.     and a remote FTP server.
  450. */
  451.  
  452. OSErr FTPConnect(unsigned long *connID,char *address,char *userID,char *password)
  453. {
  454.     ip_addr    connAddr;
  455.     tcp_port lPort = 0;
  456.     OSErr    err = cInvalidHost;
  457.             
  458.     if    (((err = ConvertStringToAddr(address,&connAddr)) == noErr) &&
  459.          ((err = CreateStream(connID,cRBufLen)) == noErr)) {
  460.             if (((err = OpenConnection(*connID,connAddr,cControlPort,kConnectionTimeOut)) == noErr) &&
  461.                 ((err = SayHello(*connID)) == noErr))
  462.                     err = UserAuth(*connID,userID,password);
  463.             if (err != noErr)
  464.                 FTPDisconnect(*connID);
  465.     }
  466.     return err;
  467. }
  468.  
  469.  
  470. /*    FTPDisconnect shuts down an FTP connection which was opened using FTPConnect.
  471. */
  472.  
  473. OSErr FTPDisconnect(unsigned long connID)
  474. {
  475.     OSErr err;
  476.     Ptr recvPtr;
  477.     unsigned short respLen;
  478.     char commandStr[256];
  479.     
  480.     strcpy(commandStr,"QUIT");
  481.     strcat(commandStr,CRLF);
  482.     SendCommand(connID,commandStr,&recvPtr,&respLen);
  483.     MyDisposPtr(recvPtr);
  484.     
  485.     CloseConnection(connID);
  486.     err = ReleaseStream(connID);
  487.     return err;
  488. }
  489.  
  490.  
  491. /*    FTPViewFile is called to get a text-mode file from a remote machine
  492.     and store the contents of the file in a pointer allocated within
  493.     the call.
  494. */
  495.  
  496. OSErr FTPViewFile(unsigned long connID,Ptr *file,char *fileName)
  497. {
  498.     OSErr    err = noErr;
  499.     unsigned long dataConnID;
  500.     Ptr respBuff,dataPtr;
  501.     unsigned short respLen;
  502.     short dataPort;
  503.     unsigned short dataLen = cDataLen;
  504.     char *command;
  505.     TCPiopb *pBlock;
  506.     char compStr[256];
  507.     
  508.     strcpy(compStr,CRLF);
  509.     strcat(compStr,"226");
  510.     
  511.     GiveTime(cSleepTime);
  512.     
  513.     command = MyNewPtr(256);
  514.     if (MyMemErr() != noErr)
  515.         return MyMemErr();
  516.     strcpy(command,"RETR ");
  517.     strcat(command,fileName);
  518.     strcat(command,CRLF);
  519.  
  520.     FTPTextMode(connID);
  521.     dataPort = GetPortNum();
  522.     StatusWindow("Opening Data Connection...",-1);
  523.     GiveTime(0);
  524.     MakeDataStream(&dataConnID,dataPort,&pBlock);
  525.     StatusWindow("Sending PORT Command...",-1);
  526.     GiveTime(0);
  527.     if ((err = SendPortComm(connID,dataPort)) == noErr) {
  528.         StatusWindow("Requesting File...",-1);
  529.         GiveTime(0);
  530.         if (((err = SendCommand(connID,command,&respBuff,&respLen)) == noErr) &&
  531.             ((err = (strncmp((char *)respBuff,"150",3)==0) ? noErr : cNotCompErr) == noErr) &&
  532.             (StatusWindow("Receiving File...",-1)) &&
  533.             ((err = GetDirectData(dataConnID,file,pBlock)) == noErr)) {
  534.                 if (!(strstr(respBuff,compStr))) {
  535.                     dataPtr = MyNewPtr((unsigned long)cDataLen);
  536.                     if (MyMemErr() == noErr) {
  537.                         err = RecvData(connID,dataPtr,&dataLen,false);
  538.                         MyDisposPtr(dataPtr);
  539.                     }
  540.                     else err = MyMemErr();
  541.                 }
  542.             }
  543.         MyDisposPtr(respBuff);
  544.     }
  545.     RemoveDataStream(dataConnID);
  546.     MyDisposPtr(command);
  547.     return err;
  548. }
  549.  
  550.  
  551. /*    FTPPutFile sends a buffer from the local machine to the remote host.
  552.     The transfer is completed in text mode.
  553. */
  554.  
  555. OSErr FTPPutFile(unsigned long connID,char *fileName,char *data,long size)
  556. {
  557.     OSErr err = noErr;
  558.     unsigned long dataConnID;
  559.     Ptr respBuff;
  560.     unsigned short respLen;
  561.     tcp_port dataPort;
  562.     char *getCommand;
  563.     unsigned short dataLen = cDataLen;
  564.     TCPiopb *pBlock;
  565.     
  566.     GiveTime(cSleepTime);
  567.     
  568.     FTPTextMode(connID);
  569.         
  570.     getCommand = MyNewPtr(256);
  571.     if (MyMemErr() != noErr)
  572.         return MyMemErr();
  573.         
  574.     strcpy(getCommand,"STOR ");
  575.     strcat(getCommand,fileName);
  576.     strcat(getCommand,CRLF);
  577.     
  578.     dataPort = GetPortNum();
  579.     StatusWindow("Opening Data Connection...",-1);
  580.     GiveTime(0);
  581.     MakeDataStream(&dataConnID,dataPort,&pBlock);
  582.     if ((err = SendPortComm(connID,dataPort)) == noErr) {
  583.         StatusWindow("Requesting Transfer...",-1);
  584.         GiveTime(0);
  585.         if (((err = SendCommand(connID,getCommand,&respBuff,&respLen)) == noErr) &&
  586.             ((err = (strncmp((char *)respBuff,"150",3)==0) ? noErr : cNotCompErr) == noErr)) {
  587.                 StatusWindow("Sending Data...",-1);
  588.                 GiveTime(0);
  589.                 err = SendFileData(dataConnID,fileName,data,text,size,pBlock);
  590.         }
  591.         MyDisposPtr(respBuff);
  592.     }
  593.     RemoveDataStream(dataConnID);
  594.     MyDisposPtr(getCommand);
  595.     SetCursor(&QDARROW);
  596.     return err;
  597. }
  598.