home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Utilities / DropFTP / ftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-03  |  20.3 KB  |  841 lines  |  [TEXT/KAHL]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     ftp.c
  4.  
  5.     This handle all transactions with FTP servers.
  6.     
  7.     Portions copyright © 1990, Apple Computer.
  8.     Portions copyright © 1993, Northwestern University.
  9.  
  10. ----------------------------------------------------------------------------*/
  11.  
  12. #include <string.h>
  13. #include <stdio.h>
  14.  
  15. #include "MacTCPCommonTypes.h"
  16. #include "TCPPB.h"
  17.  
  18. #include "glob.h"
  19. //#include "dlgutil.h"
  20. #include "ftp.h"
  21. #include "tcp.h"
  22. #include "util.h"
  23. #include "DLOG129.h"
  24.  
  25. Boolean     gCancel = false;            /* flag set when user cancels an action */
  26. short         gMemError;
  27. Handle        gLifeBoat;                    /* lifeboat memory -- de-allocated when 
  28.                                            memory gets low */
  29. Boolean        gSinking = true;            /* flag set after lifeboat has been 
  30.                                            jettisoned */
  31. Boolean     gOutOfMemory = false;        /* flag set when out of memory - and luck */
  32. static char    gStatusMsg[256];
  33.  
  34. static unsigned long gControlConnectionId;
  35. static unsigned long gDataConnectionId;
  36. static Ptr gBuffer;
  37. static Boolean gAlreadyGot226;
  38.  
  39.  
  40.  
  41. //----------------------------------------------------------------
  42.     void UnexpectedErrorMessage(short s)
  43. //----------------------------------------------------------------
  44. {
  45.     Alert(143,nil);
  46. }
  47.  
  48. //----------------------------------------------------------------
  49.     void ErrorMessage(char *s)
  50. //----------------------------------------------------------------
  51. {
  52.     c2pstr(s);
  53.     ParamText((StringPtr)s,"\p","\p","\p");
  54.     Alert(130,nil);
  55. }
  56.  
  57. //----------------------------------------------------------------
  58.     void MailOrFTPServerErrorMessage(char *s)
  59. //----------------------------------------------------------------
  60. {
  61.     Alert(139,nil);
  62. }    
  63.     
  64. /*    MyIOCheck can be called to display the result of a routine returning
  65.     an OSErr.  If the value in err is zero, the routine simply terminates.
  66. */
  67. /*    IsAppWindow returns true if the window belongs to the application
  68. */
  69.  
  70. //----------------------------------------------------------------
  71.     Boolean IsAppWindow (WindowPtr wind)
  72. //----------------------------------------------------------------
  73. {
  74.     short        windowKind;
  75.     
  76.     if (wind == nil)
  77.         return false;
  78.     else {
  79.         windowKind = ((WindowPeek)wind)->windowKind;
  80.         return windowKind >= userKind;
  81.     }
  82. }
  83.  
  84. //----------------------------------------------------------------
  85.     Boolean IsStatusWindow (WindowPtr wind)
  86. //----------------------------------------------------------------
  87. {
  88.     TWindow **info;
  89.  
  90.     if (!IsAppWindow(wind)) return false;
  91.     info = (TWindow**)GetWRefCon(wind);
  92.     return (**info).kind==kStatus;
  93. }
  94. /*    StatusWindow displays a movable-modal status window indicating
  95.     the current state of the program.
  96. */
  97.  
  98. //----------------------------------------------------------------
  99.     Boolean StatusWindow (char *text)
  100. //----------------------------------------------------------------
  101. {
  102.     WindowPtr statusWind;
  103.     Rect bounds = {0,0,75,420};
  104.     Rect buttonBounds = {42,347,62,407};
  105.     Rect msgBounds = {13, 13, 40, 420};
  106.     TWindow **info;
  107.     
  108.     if (!IsStatusWindow(statusWind = FrontWindow())) {
  109.             OffsetRect(&bounds,100,100);
  110.         info = (TWindow**) MyNewHandle(sizeof(TWindow));
  111.         if (MyMemErr() != noErr)
  112.             return false;
  113.         strcpy(gStatusMsg,text);
  114.         (**info).kind = kStatus;
  115.         statusWind = NewWindow(nil, &bounds, "\pStatus", true,
  116.             movableDBoxProc, (WindowPtr)-1,
  117.             false, (unsigned long)info);
  118.         SetPort(statusWind);
  119.         NewControl(statusWind, &buttonBounds, "\pCancel", true, 0, 0, 0, 
  120.             pushButProc, 0);
  121.         ValidRect(&statusWind->portRect);
  122.         SpinCursor(0);
  123.     } else {
  124.         SetPort(statusWind);
  125.         strcpy(gStatusMsg,text);
  126.         EraseRect(&msgBounds);
  127.     }
  128.     TextFont(systemFont);
  129.     TextSize(12);
  130.     MoveTo(13,29);
  131.     DrawText(gStatusMsg,0,strlen(gStatusMsg));
  132.     return true;
  133. }
  134.  
  135.  
  136. /*    UpdateStatus is called in response to update events for the status
  137.     window.  This routine will redraw sections of the window as necessary.
  138. */
  139.  
  140. //----------------------------------------------------------------
  141.     void UpdateStatus (void)
  142. //----------------------------------------------------------------
  143. {
  144.     GrafPtr savePort;
  145.     WindowPtr statusWind;
  146.     
  147.     statusWind = FrontWindow();
  148.     if (!IsStatusWindow(statusWind)) return;
  149.     GetPort(&savePort);
  150.     SetPort(statusWind);
  151.     EraseRect(&statusWind->portRect);
  152.     DrawControls(statusWind);
  153.     TextFont(systemFont);
  154.     TextSize(12);
  155.     MoveTo(13,29);
  156.     DrawText(gStatusMsg,0,strlen(gStatusMsg));
  157.     SetPort(savePort);
  158. }
  159.  
  160.  
  161. /*    CloseStatusWindow is called when the status window should be removed.
  162. */
  163.  
  164. //----------------------------------------------------------------
  165.     void CloseStatusWindow (void)
  166. //----------------------------------------------------------------
  167. {
  168.     WindowPtr statusWind;
  169.     GrafPtr savePort;
  170.     TWindow **info;
  171.     
  172.     if (IsStatusWindow(statusWind = FrontWindow())) {
  173.         //SetPt(&gPrefs.statusWindowLocn,0,0);
  174.         GetPort(&savePort);
  175.         SetPort(statusWind);
  176.         //LocalToGlobal(&gPrefs.statusWindowLocn);
  177.         info = (TWindow**)GetWRefCon(statusWind);
  178.         SetPort(savePort);
  179.         MyDisposHandle((Handle)info );
  180.         DisposeWindow(statusWind);
  181.     }
  182. }
  183.  
  184. //----------------------------------------------------------------
  185.     OSErr MyIOCheck (OSErr err)
  186. //----------------------------------------------------------------
  187. {
  188.     if (err != noErr) {
  189.         UnexpectedErrorMessage(err);
  190.     }
  191.     return err;
  192. }
  193.  
  194.  
  195. /*    LowMemory is called when the program runs out of useable memory.
  196.     If this is the first time this has happened, the program de-allocates
  197.     lifeboat memory which was allocated when the program was launched.
  198.     Otherwise, the user had better quit.
  199. */
  200.  
  201. //----------------------------------------------------------------
  202.     static Boolean LowMemory (void)
  203. //----------------------------------------------------------------
  204. {
  205.     Boolean result;
  206.     
  207.     if (MyMemErr() != memFullErr) {
  208.         MyIOCheck(MyMemErr());
  209.         return false;
  210.     }
  211.         
  212.     if (gSinking) {
  213.         result = false;
  214.         gOutOfMemory = true;
  215.         ErrorMessage("You have run out of memory");
  216.     }
  217.     else {
  218.         HUnlock(gLifeBoat);
  219.         DisposHandle(gLifeBoat);
  220.         gSinking = true;
  221.         result = true;
  222.         ErrorMessage("Memory is getting low.  Some operations may fail.");
  223.     }
  224.     return result;
  225. }
  226.  
  227.  
  228. /*    This is a wrapper for the NewPtr routine which automatically checks
  229.     the result of the call and takes appropriate action.
  230. */
  231.  
  232. //----------------------------------------------------------------
  233.     Ptr MyNewPtr (Size byteCount)
  234. //----------------------------------------------------------------
  235. {
  236.     Ptr thePtr;
  237.     
  238.     thePtr = NewPtrClear(byteCount);
  239.     if ((gMemError = MemError()) != noErr) {
  240.         if (LowMemory())
  241.             thePtr = MyNewPtr(byteCount);
  242.         else
  243.             thePtr = nil;
  244.     }
  245.     return thePtr;
  246. }
  247.  
  248.  
  249. /*    This is a wrapper for the NewHandle routine which automatically checks
  250.     the result of the call and takes appropriate action.
  251. */
  252.  
  253. //----------------------------------------------------------------
  254.     Handle MyNewHandle (Size byteCount)
  255. //----------------------------------------------------------------
  256. {
  257.     Handle theHndl;
  258.     
  259.     theHndl = NewHandleClear(byteCount);
  260.     if ((gMemError = MemError()) != noErr) {
  261.         if (LowMemory())
  262.             theHndl = MyNewHandle(byteCount);
  263.         else
  264.             theHndl = nil;
  265.     }
  266.     return theHndl;
  267. }
  268.  
  269. /*    This is a wrapper for the SetHandleSize routine which automatically checks
  270.     the result of the call and takes appropriate action.
  271. */
  272.  
  273. //----------------------------------------------------------------
  274.     void MySetHandleSize (Handle h, Size newSize)
  275. //----------------------------------------------------------------
  276. {
  277.     long oldSize;
  278.  
  279.     oldSize = GetHandleSize(h);
  280.     SetHandleSize(h,newSize);
  281.     if ((gMemError = MemError()) != noErr) {
  282.         if (LowMemory())
  283.             MySetHandleSize(h,newSize);
  284.     } else if (oldSize < newSize) {
  285.         memset(*h+oldSize, 0, newSize-oldSize);
  286.     }
  287. }
  288.  
  289.  
  290. /*    This is a wrapper for the HandToHand routine which automatically checks
  291.     the result of the call and takes appropriate action.
  292. */
  293.  
  294. //----------------------------------------------------------------
  295.     OSErr MyHandToHand (Handle *theHndl)
  296. //----------------------------------------------------------------
  297. {
  298.     Handle oldHndl;
  299.     OSErr result;
  300.     
  301.     oldHndl = *theHndl;
  302.     result = gMemError = HandToHand(theHndl);
  303.     if (result != noErr) {
  304.         *theHndl = oldHndl;
  305.         if (LowMemory())
  306.             result = MyHandToHand(theHndl);
  307.     }
  308.     return result;
  309. }
  310.  
  311.  
  312. /*    This is a wrapper for the DisposPtr routine which automatically checks
  313.     the result of the call and takes appropriate action.
  314.     NT: extra errorchecking for stability incorporated
  315. */
  316.  
  317. //----------------------------------------------------------------
  318.     OSErr MyDisposPtr (Ptr thePtr)
  319. //----------------------------------------------------------------
  320. {
  321.     if(thePtr != nil)
  322.     {
  323.         DisposPtr(thePtr);
  324.         gMemError = MemError();
  325.     }
  326.     else
  327.     {
  328.         gMemError = noErr;
  329.     }
  330.     return MyIOCheck(gMemError);
  331. }
  332.  
  333.  
  334. /*    This is a wrapper for the DisposHandle routine which automatically checks
  335.     the result of the call and takes appropriate action.
  336.     NT: extra errorchecking for stability incorporated
  337. */
  338.  
  339. //----------------------------------------------------------------
  340.     OSErr MyDisposHandle (Handle theHndl)
  341. //----------------------------------------------------------------
  342. {
  343.     if(theHndl != nil)
  344.     {
  345.         DisposHandle(theHndl);
  346.         gMemError = MemError();
  347.     }
  348.     else
  349.     {
  350.         gMemError = noErr;
  351.     }
  352.     return MyIOCheck(gMemError);
  353. }
  354.  
  355.  
  356. /*    This is a wrapper for the MemError routine which automatically checks
  357.     the result of the call and takes appropriate action.
  358. */
  359.  
  360. OSErr MyMemErr (void)
  361. {
  362.     return gMemError;
  363. }
  364.  
  365. /* Abort aborts a transaction with an FTP server. */
  366.  
  367. static void Abort (void)
  368. {
  369.     if (gControlConnectionId != 0) {
  370.         AbortConnection(gControlConnectionId);
  371.         ReleaseStream(gControlConnectionId);
  372.     }
  373.     if (gDataConnectionId != 0) {
  374.         AbortConnection(gDataConnectionId);
  375.         ReleaseStream(gDataConnectionId);
  376.     }
  377.     MyDisposPtr(gBuffer);
  378. }
  379. /*    The InitCursorCtl, SpinCursor, strcasecmp and strncasecmp functions 
  380.     are missing in Think, so we include them here    */
  381.  
  382. Handle gAcur = nil;
  383.  
  384. pascal void InitCursorCtl (Handle id)
  385. {
  386.     short NoFrames, i, CursId;
  387.     CursHandle    TheCursHndl;
  388.  
  389.     if (id == nil) {
  390.         gAcur = (Handle) GetResource('acur',0);
  391.         if(gAcur == nil) return;
  392.         HLock(gAcur);
  393.         NoFrames = ((short *)(*gAcur))[0];
  394.         for(i = 0; i < NoFrames; i++) {
  395.             CursId = ((short *)(*gAcur))[(2*i)+2];
  396.             TheCursHndl = GetCursor(CursId);
  397.             ((CursHandle *)(*gAcur))[i+1] = TheCursHndl;
  398.             HLock((Handle)TheCursHndl);
  399.         }
  400.     } else {
  401.         gAcur = id;
  402.         HLock(gAcur);
  403.         NoFrames = ((short *)(*gAcur))[0];
  404.         for(i = 0; i < NoFrames; i++) {
  405.             CursId = ((short *)(*gAcur))[(2*i)+2];
  406.             TheCursHndl = GetCursor(CursId);
  407.             ((CursHandle *)(*gAcur))[i+1] = TheCursHndl;
  408.             HLock((Handle)TheCursHndl);
  409.         }
  410.     }
  411.     ((short *)(*gAcur))[1] = 0;
  412.     DetachResource(gAcur);
  413. }
  414.  
  415.  
  416. pascal void SpinCursor (short num)
  417. {
  418.     short    NoFrames, CurrentFrame, CurrentCounter;
  419.     Cursor    CurrentCursor;
  420.  
  421.     if (gAcur == nil) InitCursorCtl(nil);
  422.     NoFrames = ((short *)(*gAcur))[0];
  423.     CurrentCounter = ((((short *)(*gAcur))[1] + num) % (NoFrames * 32));
  424.     ((short *)(*gAcur))[1] = CurrentCounter;
  425.     CurrentFrame = CurrentCounter / 32;
  426.     CurrentCursor = **(((CursHandle *)(*gAcur))[CurrentFrame+1]);
  427.     SetCursor(&CurrentCursor);
  428. }
  429.  
  430. /*    GiveTime is called whenever the application is waiting for a slow
  431.     process to complete.  The routine calls SpinCursor and WaitNextEvent
  432.     to give time to currently running background applications.
  433. */
  434. void DrawWindow(WindowPtr w);
  435. void DrawWindow(WindowPtr w)
  436. {
  437.     extern char host[256];
  438.     extern char user[256];    
  439.     SetPort(w);
  440.     
  441.     MoveTo(5,20);
  442.     DrawString("\pHost: ");
  443.     DrawText(host,0,strlen(host));
  444.     MoveTo(5,40);
  445.     DrawString("\pUserName: ");
  446.     DrawText(user,0,strlen(user));
  447. }
  448.  
  449. void HandleUpdate (WindowPtr wind)
  450. {
  451.     GrafPtr savePort;
  452.     
  453.     if (((WindowPeek)wind)->windowKind < 0 || 
  454.         ((WindowPeek)wind)->windowKind == dialogKind) return;
  455.     GetPort(&savePort);
  456.     SetPort(wind);
  457.     BeginUpdate(wind);
  458.     if (!IsStatusWindow(wind)) {
  459.         EraseRect(&wind->portRect);
  460.         DrawWindow(wind);
  461.     } else {
  462.         UpdateStatus();
  463.     }
  464.     EndUpdate(wind);
  465.     SetPort(savePort);
  466. }
  467.  
  468. Boolean GiveTime (void)
  469. {
  470.     EventRecord ev;
  471.     Boolean gotEvt;
  472.     static long nextTime = 0;
  473.     char keyPressed, charPressed;
  474.     WindowPtr statusWind;
  475.     GrafPtr savePort;
  476.     ControlHandle cancel;
  477.     long myticks;
  478.     short part;
  479.     WindowPtr theWindow;
  480.  
  481.     HiliteMenu(0);
  482.     //SetMenusTo(false, 0, 0, 0, 0, 0, 0);
  483.     ShowCursor();
  484.  
  485.     if (TickCount() >= nextTime) {
  486.             
  487.         SpinCursor(16);
  488.  
  489.         gotEvt = WaitNextEvent(everyEvent,&ev,0,nil);
  490.         
  491.         if ( gotEvt )
  492.             switch (ev.what) {
  493.                 case mouseDown:
  494.                     part = FindWindow(ev.where, &theWindow);
  495.                     if (part == inMenuBar) {
  496.                         MenuSelect(ev.where);
  497.                     } else if (IsStatusWindow(FrontWindow())) {
  498.                         //HandleMouseDown(&ev);
  499.                     }
  500.                     break;
  501.                 case activateEvt:
  502.                     //HandleActivate((WindowPtr)ev.message,((ev.modifiers & activeFlag) != 0)); 
  503.                     break;
  504.                 case updateEvt:
  505.                     HandleUpdate((WindowPtr)(ev.message));
  506.                     break;
  507.                 case app4Evt:
  508.                     //HandleSuspendResume(ev.message);
  509.                     break;
  510.                 case keyDown:
  511.                 case autoKey:
  512.                     FlushEvents(keyDownMask+keyUpMask+autoKeyMask,0);
  513.                     charPressed = ev.message & charCodeMask;
  514.                     keyPressed = (ev.message & keyCodeMask) >> 8;
  515.                     if (keyPressed == escapeKeyCode || 
  516.                         (ev.modifiers & cmdKey) != 0 && charPressed == '.') {
  517.                         statusWind = FrontWindow();
  518.                         if (IsStatusWindow(statusWind)) {
  519.                             cancel = ((WindowPeek)statusWind)->controlList;
  520.                             GetPort(&savePort);
  521.                             SetPort(statusWind);
  522.                             HiliteControl(cancel,1);
  523.                             Delay(8,&myticks);
  524.                             HiliteControl(cancel,0);
  525.                             SetPort(savePort);
  526.                         }
  527.                         gCancel = true;
  528.                     }
  529.                     break;
  530.             }
  531.         nextTime = TickCount() + 5;
  532.     }
  533.  
  534.     return !gCancel;
  535. }
  536.  
  537. /* Init initializes a transaction with an FTP server. */
  538.  
  539. static Boolean Init (char *host, char *user, char *pswd,
  540.     char *file, char *cmd)
  541. {
  542.     CStr255 sendData[4];
  543.     unsigned long addr;
  544.     unsigned long myAddr;
  545.     unsigned short length;
  546.     OSErr err;
  547.     TCPiopb *pBlock;
  548.     unsigned short localPort;
  549.     Ptr p,pEnd;
  550.     char *serverCommand = nil;
  551.     
  552.     gControlConnectionId = gDataConnectionId = 0;
  553.     gBuffer = nil;
  554.     pBlock = nil;
  555.     
  556.     /* Get host address. */
  557.  
  558.     if (IPNameToAddr(host, &addr) != noErr) {
  559.         if (err != -1) ErrorMessage("Could not get host address.");
  560.         goto exit3;
  561.     }
  562.     
  563.     /* Allocate data buffer. */
  564.     
  565.     gBuffer = MyNewPtr(kBufLen);
  566.     if ((err = MyMemErr()) != noErr) goto exit1;
  567.     
  568.     /* Create and open control stream connecton. */
  569.     
  570.     if ((err = CreateStream(&gControlConnectionId,kBufLen)) != noErr) goto exit1;
  571.     if ((err = OpenConnection(gControlConnectionId,addr,kFTPPort,20)) != noErr) {
  572.         if (err != -1) ErrorMessage("Could not open connection to host.");
  573.         goto exit3;
  574.     }
  575.     length = kBufLen;
  576.     if ((err = RecvData(gControlConnectionId,gBuffer,&length,true)) != noErr) goto exit1;
  577.     if (length < 3 || *gBuffer != '2') goto exit2;
  578.     
  579.     /* Send USER command. */
  580.     
  581.     strcpy(sendData[0],"USER ");
  582.     strcpy(sendData[1],user);
  583.     strcpy(sendData[2],CRLF);
  584.     if ((err = SendMultiData(gControlConnectionId,sendData,3)) != noErr) goto exit1;
  585.     length = kBufLen;
  586.     if ((err = RecvData(gControlConnectionId,gBuffer,&length,true)) != noErr) goto exit1;
  587.     if (length < 3 || *gBuffer != '3') goto exit4;
  588.     
  589.     /* Send PASS command. */
  590.     
  591.     strcpy(sendData[0],"PASS ");
  592.     strcpy(sendData[1],pswd);
  593.     strcpy(sendData[2],CRLF);
  594.     if ((err = SendMultiData(gControlConnectionId,sendData,3)) != noErr) goto exit1;
  595.     length = kBufLen;
  596.     if ((err = RecvData(gControlConnectionId,gBuffer,&length,true)) != noErr) goto exit1;
  597.     if (length < 3 || *gBuffer != '2') goto exit4;
  598.     
  599.     /* Create and open data stream connection. */
  600.     
  601.     if ((err = CreateStream(&gDataConnectionId,kBufLen)) != noErr) goto exit1;
  602.     if ((err = AsyncWaitForConnection(gDataConnectionId,0,0,0,0,&pBlock)) !=
  603.         noErr) goto exit1;
  604.         
  605.     /* Wait for MacTCP to assign port number. */
  606.         
  607.     while ((localPort = pBlock->csParam.open.localPort) == 0 && GiveTime());
  608.     if (gCancel) goto exit3;
  609.  
  610.     /* Send PORT command. */
  611.     
  612.     if ((err = GetMyIPAddr(&myAddr)) != noErr) goto exit1;
  613.     sprintf(sendData[0],"PORT %hu,%hu,%hu,%hu,%hu,%hu",
  614.         (unsigned short)((myAddr>>24)&0xff), 
  615.         (unsigned short)((myAddr>>16)&0xff), 
  616.         (unsigned short)((myAddr>>8)&0xff), 
  617.         (unsigned short)(myAddr&0xff),
  618.         (unsigned short)((localPort>>8)&0xff), 
  619.         (unsigned short)(localPort&0xff));
  620.     strcpy(sendData[1],CRLF);
  621.     serverCommand = "PORT";
  622.     if ((err = SendMultiData(gControlConnectionId,sendData,2)) != noErr) goto exit1;
  623.     length = kBufLen;
  624.     if ((err = RecvData(gControlConnectionId,gBuffer,&length,true)) != noErr) goto exit1;
  625.     if (length < 3 || *gBuffer != '2') goto exit2;
  626.     
  627.     /* Send RETR or STOR command. */
  628.     
  629.     strcpy(sendData[0],cmd);
  630.     strcpy(sendData[1]," ");
  631.     strcpy(sendData[2],file);
  632.     strcpy(sendData[3],CRLF);
  633.     serverCommand = cmd;
  634.     if ((err = SendMultiData(gControlConnectionId,sendData,4)) != noErr) goto exit1;
  635.     length = kBufLen;
  636.     if ((err = RecvData(gControlConnectionId,gBuffer,&length,true)) != noErr) goto exit1;
  637.     if (length < 3 || *gBuffer != '1') goto exit2;
  638.     
  639.     /* If the timing is just right, it is possible that the final 226 "data transfer
  640.        complete" message arrived as part of the buffer just received. We must check 
  641.        for this. */
  642.        
  643.     p = gBuffer;
  644.     pEnd = gBuffer+length-3;
  645.     gAlreadyGot226 = false;
  646.     while (p < pEnd ) {
  647.         if (*p == CR && *(p+1) == LF) {
  648.             p += 2;
  649.             length = pEnd - p;
  650.             if (length < 3 || *p != '2') {
  651.                 MailOrFTPServerErrorMessage(p);
  652.                 goto exit3;
  653.             }
  654.             gAlreadyGot226 = true;
  655.             break;
  656.         }
  657.         p++;
  658.     }
  659.     
  660.     /* Wait for server to open its end of the data stream connection. */
  661.     
  662.     while (pBlock->ioResult > 0 && GiveTime());
  663.     err = gCancel ? -1 : pBlock->ioResult;
  664.     if (err != noErr) goto exit1;
  665.     MyDisposPtr((Ptr)pBlock);
  666.     
  667.     return true;
  668.     
  669. exit1:
  670.  
  671.     UnexpectedErrorMessage(err);
  672.     goto exit3;
  673.     
  674. exit2:
  675.  
  676.     MailOrFTPServerErrorMessage(gBuffer);
  677.     
  678. exit3:
  679.  
  680.     Abort();
  681.     MyDisposPtr((Ptr)pBlock);
  682.     return false;
  683.     
  684. exit4:
  685.  
  686.     ErrorMessage("Invalid username or password.");
  687.     goto exit3;
  688. }
  689.  
  690.  
  691. /* Term terminates a transaction with an FTP server. */
  692.  
  693. static Boolean Term (char *cmd, Boolean get)
  694. {
  695.     CStr255 commStr;
  696.     unsigned short length;
  697.     OSErr err;
  698.     char *serverCommand;
  699.     
  700.     /* Close data stream. */
  701.     
  702.     if ((err = CloseConnection(gDataConnectionId, get)) != noErr) goto exit1;
  703.     if ((err = ReleaseStream(gDataConnectionId)) != noErr) goto exit1;
  704.     
  705.     /* Check for final 226 "data transfer complete" reply to RETR or STOR command. */
  706.     
  707.     serverCommand = cmd;
  708.     if (!gAlreadyGot226) {
  709.         length = kBufLen;
  710.         if ((err = RecvData(gControlConnectionId,gBuffer,&length,true)) != noErr) goto exit1;
  711.         if (length < 3 || *gBuffer != '2') goto exit2;
  712.     }
  713.     
  714.     /* Send QUIT command on control stream. */
  715.     
  716.     strcpy(commStr,"QUIT");
  717.     strcat(commStr,CRLF);
  718.     serverCommand = "QUIT";
  719.     if ((err = SendData(gControlConnectionId,commStr,6)) != noErr) goto exit1;
  720.     length = kBufLen;
  721.     if ((err = RecvData(gControlConnectionId,gBuffer,&length,true)) != noErr) goto exit1;
  722.     if (length < 3 || *gBuffer != '2') goto exit2;
  723.     
  724.     /* Close control stream. */
  725.  
  726.     if ((err = CloseConnection(gControlConnectionId, true)) != noErr) goto exit1;
  727.     if ((err = ReleaseStream(gControlConnectionId)) != noErr) goto exit1;
  728.  
  729.     /* Dispose the buffer. */
  730.     
  731.     MyDisposPtr(gBuffer);
  732.     return true;
  733.     
  734. exit1:
  735.  
  736.     UnexpectedErrorMessage(err);
  737.     goto exit3;
  738.     
  739. exit2:
  740.  
  741.     MailOrFTPServerErrorMessage(gBuffer);
  742.     
  743. exit3:
  744.  
  745.     Abort();
  746.     return false;
  747. }
  748.  
  749.  
  750. /* FTPGetFile gets a file from a host. */    
  751.     
  752. Boolean FTPGetFile (char *host, char *user, char *pswd, char *file, Handle data)
  753. {
  754.     unsigned short length;
  755.     long size, allocated;
  756.     OSErr err;
  757.     Ptr p,pEnd,q;
  758.  
  759.     if (!Init(host,user,pswd,file,"RETR")) return false;
  760.     
  761.     MySetHandleSize(data, kBufLen);
  762.     size = 0;
  763.     allocated = kBufLen;
  764.     
  765.     while (true) {
  766.         length = kBufLen;
  767.         if ((err = RecvData(gDataConnectionId,gBuffer,&length,true)) != noErr) break;
  768.         if (size + length > allocated) {
  769.             allocated += kBufLen;
  770.             MySetHandleSize(data, allocated);
  771.         }
  772.         BlockMove(gBuffer, *data + size, length);
  773.         size += length;
  774.     }
  775.     if (err != connectionClosing && err != connectionTerminated) goto exit1;
  776.     
  777.     p = q = *data;;
  778.     pEnd = *data + size;
  779.     while (p < pEnd) {
  780.         if (*p == CR && *(p+1) == LF) {
  781.             *q++ = CR;
  782.             p += 2;
  783.         } else {
  784.             *q++ = *p++;
  785.         }
  786.     }
  787.     size = q - *data;
  788.     MySetHandleSize(data, size + 1);
  789.     *(*data+size) = 0;
  790.  
  791.     return Term("RETR", true);
  792.     
  793. exit1:
  794.  
  795.     UnexpectedErrorMessage(err);
  796.     Abort();
  797.     return false;
  798. }
  799.  
  800.  
  801. /* FTPPutFile sends a file to a host. */    
  802.     
  803. Boolean FTPPutFile (char *host, char *user, char *pswd, char *file, Ptr data, long size)
  804. {
  805.     OSErr err;
  806.     Ptr p,pEnd,q,qEnd;
  807.  
  808.     if (host[0] == 0 ||user[0] == 0 || pswd[0] == 0 ) {
  809.         if (DoPrefs(0)==0)
  810.             return false;
  811.     }
  812.     if (!Init(host,user,pswd,file,"STOR")) return false;
  813.     
  814.     p = data;
  815.     pEnd = data + size;
  816.     while (p < pEnd) {
  817.         q = gBuffer;
  818.         qEnd = q + kBufLen - 1;
  819.         while (p < pEnd && q < qEnd) {
  820.             if (*p == CR) {
  821.                 *q++ = CR;
  822.                 *q++ = LF;
  823.                 p++;
  824.             } else {
  825.                 *q++ = *p++;
  826.             }
  827.         }
  828.         if (q > gBuffer) {
  829.             if ((err = SendData(gDataConnectionId,gBuffer,q-gBuffer)) != noErr) goto exit1;
  830.         }
  831.     }
  832.     
  833.     return Term("STOR", false);
  834.     
  835. exit1:
  836.  
  837.     UnexpectedErrorMessage(err);
  838.     Abort();
  839.     return false;
  840. }
  841.