home *** CD-ROM | disk | FTP | other *** search
/ ftp.ncftp.com / ftp.ncftp.com.zip / ftp.ncftp.com / ncftp / older_versions / ncftp-3.2.2-src.tar.bz2 / ncftp-3.2.2-src.tar / ncftp-3.2.2 / libncftp / io_get.c < prev    next >
C/C++ Source or Header  |  2008-07-15  |  23KB  |  827 lines

  1. /* io_get.c
  2.  *
  3.  * Copyright (c) 1996-2008 Mike Gleason, NcFTP Software.
  4.  * All rights reserved.
  5.  *
  6.  */
  7.  
  8. #include "syshdrs.h"
  9. #ifdef PRAGMA_HDRSTOP
  10. #    pragma hdrstop
  11. #endif
  12.  
  13. #define _CRT_SECURE_NO_WARNINGS 1
  14.  
  15. #if 0    /* For now, don't do this, which takes a shortcut. */
  16. #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
  17. #    define ASCII_TRANSLATION 0
  18. #endif
  19. #endif
  20.  
  21. #ifndef ASCII_TRANSLATION
  22. #    define ASCII_TRANSLATION 1
  23. #endif
  24.  
  25. #ifndef NO_SIGNALS
  26. #    define NO_SIGNALS 1
  27. #endif
  28.  
  29. #ifndef O_BINARY
  30.     /* Needed for platforms using different EOLN sequence (i.e. DOS) */
  31. #    ifdef _O_BINARY
  32. #        define O_BINARY _O_BINARY
  33. #    else
  34. #        define O_BINARY 0
  35. #    endif
  36. #endif
  37.  
  38. int
  39. FTPGetOneF(
  40.     const FTPCIPtr cip,
  41.     const char *const file,
  42.     const char *dstfile,
  43.     int xtype,
  44.     const int fdtouse,
  45.     longest_int expectedSize,
  46.     time_t mdtm,
  47.     const int resumeflag,
  48.     const int appendflag,
  49.     const int deleteflag,
  50.     const FTPConfirmResumeDownloadProc resumeProc)
  51. {
  52.     char *buf;
  53.     size_t bufSize;
  54.     int tmpResult;
  55.     volatile int result;
  56.     read_return_t nread;
  57.     write_return_t nwrote;
  58.     volatile int fd;
  59. #if ASCII_TRANSLATION
  60.     char *src, *srclim;
  61.     char *dst, *dstlim;
  62.     char outbuf[512];
  63.     int cr, crcr, add_another_eoln, crcr_offset;
  64. #endif
  65.     longest_int tstartPoint;
  66.     volatile longest_int startPoint = 0;
  67.     struct utimbuf ut;
  68.     struct Stat st;
  69. #if !defined(NO_SIGNALS)
  70.     volatile FTPSigProc osigpipe;
  71.     volatile FTPCIPtr vcip;
  72.     volatile int vfd, vfdtouse;
  73.     int sj;
  74. #endif    /* NO_SIGNALS */
  75.     volatile int created = 0;
  76.     int zaction = kConfirmResumeProcSaidBestGuess;
  77.     int statrc;
  78.     int noMdtmCheck;
  79.     int is_dev;
  80.     time_t now;
  81.  
  82.     if (cip->buf == NULL) {
  83.         FTPLogError(cip, kDoPerror, "Transfer buffer not allocated.\n");
  84.         cip->errNo = kErrNoBuf;
  85.         return (cip->errNo);
  86.     }
  87.  
  88.     result = kNoErr;
  89.     cip->usingTAR = 0;
  90.  
  91.     if (fdtouse < 0) {
  92.         /* Only ask for extended information
  93.          * if we have the name of the file
  94.          * and we didn't already have the
  95.          * info.
  96.          *
  97.          * Always ask for the modification time,
  98.          * because even if it was passed in it
  99.          * may not be accurate.  This is often
  100.          * the case when it came from an ls
  101.          * listing, in which the local time
  102.          * zone could be a factor.
  103.          *
  104.          */
  105.  
  106.         if ((file == NULL) || (file[0] == '\0') || (dstfile == NULL) || (dstfile[0] == '\0')) {
  107.             return (kErrBadParameter);
  108.         }
  109.  
  110.         AutomaticallyUseASCIIModeDependingOnExtension(cip, file, &xtype);
  111.  
  112.         if ((cip->progress != (FTPProgressMeterProc) 0) || (resumeflag == kResumeYes) || (resumeProc != kNoFTPConfirmResumeDownloadProc)) {
  113.             if (expectedSize == kSizeUnknown) {
  114.                 (void) FTPFileSizeAndModificationTime(cip, file, &expectedSize, xtype, &mdtm);
  115.             } else {
  116.                 (void) FTPFileModificationTime(cip, file, &mdtm);
  117.             }
  118.         }
  119.  
  120.         /* For Get, we can't recover very well if it turns out restart
  121.          * didn't work, so check beforehand.
  122.          */
  123.         if ((resumeflag == kResumeYes) || (resumeProc != kNoFTPConfirmResumeDownloadProc)) {
  124.             FTPCheckForRestartModeAvailability(cip); 
  125.         }
  126.  
  127.         if (appendflag == kAppendYes) {
  128.             zaction = kConfirmResumeProcSaidAppend;
  129.         } else if (cip->hasREST == kCommandNotAvailable) {
  130.             zaction = kConfirmResumeProcSaidOverwrite;
  131.         } else if (resumeflag == kResumeYes) {
  132.             zaction = kConfirmResumeProcSaidBestGuess;
  133.         } else {
  134.             zaction = kConfirmResumeProcSaidOverwrite;
  135.         }
  136.  
  137.         is_dev = 0;
  138.         statrc = Stat(dstfile, &st);
  139.         if (statrc == 0) {
  140.             if (resumeProc != NULL) {
  141.                 tstartPoint = startPoint;
  142.                 zaction = (*resumeProc)(
  143.                         cip,
  144.                         &dstfile,
  145.                         (longest_int) st.st_size,    /* displays local size, not the FTP CR+LF size for ASCII text */
  146.                         st.st_mtime,
  147.                         file,
  148.                         expectedSize,
  149.                         mdtm,
  150.                         &tstartPoint
  151.                 );
  152.                 startPoint = tstartPoint;
  153.             }
  154. #ifdef S_ISBLK
  155.             if (S_ISBLK(st.st_mode))
  156.                 is_dev = 1;
  157. #endif
  158. #ifdef S_ISCHR
  159.             if (S_ISCHR(st.st_mode))
  160.                 is_dev = 1;
  161. #endif
  162.  
  163.             if (zaction == kConfirmResumeProcSaidBestGuess) {
  164.                 if (expectedSize != kSizeUnknown) {
  165.                     /* We know the size of the remote file,
  166.                      * and we have a local file too.
  167.                      *
  168.                      * Try and decide if we need to get
  169.                      * the entire file, or just part of it.
  170.                      */
  171.  
  172.                     zaction = kConfirmResumeProcSaidResume;
  173.                     startPoint = (longest_int) st.st_size;
  174.                     if (xtype == kTypeAscii) {
  175.                         if ((dstfile == NULL) || (dstfile[0] == '\0') || ((startPoint = FTPLocalASCIIFileSize(dstfile, cip->buf, cip->bufSize)) < 0))
  176.                             zaction = kConfirmResumeProcSaidOverwrite;
  177.                     }
  178.  
  179.                     /* If the local file exists and has a recent
  180.                      * modification time (< 12 hours) and
  181.                      * the remote file's modtime is not recent,
  182.                      * then heuristically conclude that the
  183.                      * local modtime should not be trusted
  184.                      * (i.e. user killed the process before
  185.                      * the local modtime could be preserved).
  186.                      */
  187.                     noMdtmCheck = 0;
  188.                     if (mdtm != kModTimeUnknown) {
  189.                         time(&now);
  190.                         if ((st.st_mtime > now) || (((now - st.st_mtime) < 46200) && ((now - mdtm) >= 46200)))
  191.                             noMdtmCheck = 1;
  192.                     }
  193.  
  194.                     if ((mdtm == kModTimeUnknown) || (noMdtmCheck != 0)) {
  195.                         /* Can't use the timestamps as an aid. */
  196.                         if (startPoint == expectedSize) {
  197.                             /* Don't go to all the trouble of downloading nothing. */
  198.                             cip->errNo = kErrLocalSameAsRemote;
  199.                             if (deleteflag == kDeleteYes)
  200.                                 (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  201.                             return (cip->errNo);
  202.                         } else if (startPoint > expectedSize) {
  203.                             /* Panic;  odds are the file we have
  204.                              * was a different file altogether,
  205.                              * since it is larger than the
  206.                              * remote copy.  Re-do it all.
  207.                              */
  208.                             zaction = kConfirmResumeProcSaidOverwrite;
  209.                         } /* else resume at startPoint */
  210.                     } else if ((mdtm == st.st_mtime) || (mdtm == (st.st_mtime - 1)) || (mdtm == (st.st_mtime + 1))) {
  211.                         /* File has the same time.
  212.                          * Note: Windows' file timestamps can be off by one second!
  213.                          */
  214.                         if (startPoint == expectedSize) {
  215.                             /* Don't go to all the trouble of downloading nothing. */
  216.                             cip->errNo = kErrLocalSameAsRemote;
  217.                             if (deleteflag == kDeleteYes)
  218.                                 (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  219.                             return (cip->errNo);
  220.                         } else if (startPoint > expectedSize) {
  221.                             /* Panic;  odds are the file we have
  222.                              * was a different file altogether,
  223.                              * since it is larger than the
  224.                              * remote copy.  Re-do it all.
  225.                              */
  226.                             zaction = kConfirmResumeProcSaidOverwrite;
  227.                         } else {
  228.                             /* We have a file by the same time,
  229.                              * but smaller start point.  Leave
  230.                              * the startpoint as is since it
  231.                              * is most likely valid.
  232.                              */
  233.                         }
  234.                     } else if (mdtm < st.st_mtime) {
  235.                         /* Remote file is older than
  236.                          * local file.  Don't overwrite
  237.                          * our file.
  238.                          */
  239.                         cip->errNo = kErrLocalFileNewer;
  240.                         return (cip->errNo);
  241.                     } else /* if (mdtm > st.st_mtime) */ {
  242.                         /* File has a newer timestamp
  243.                          * altogether, assume the remote
  244.                          * file is an entirely new file
  245.                          * and replace ours with it.
  246.                          */
  247.                         zaction = kConfirmResumeProcSaidOverwrite;
  248.                     }
  249.                 } else {
  250.                     if ((cip->hasMDTM == kCommandAvailable) || (cip->hasSIZE != kCommandAvailable)) {
  251.                         /* Remote file does not seem to exist, but local already does.
  252.                          * Since we know we can determine the existence of remote files,
  253.                          * assume that doing a retrieve of this file would fail out with
  254.                          * an error and end up truncating the local file for no reason.
  255.                          */
  256.                         zaction = kConfirmResumeProcSaidSkip;
  257.                     } else {
  258.                         zaction = kConfirmResumeProcSaidOverwrite;
  259.                     }
  260.                 }
  261.             }
  262.         } else {
  263.             zaction = kConfirmResumeProcSaidOverwrite;
  264.         }
  265.  
  266.         if (zaction == kConfirmResumeProcSaidCancel) {
  267.             /* User wants to cancel this file and any
  268.              * remaining in batch.
  269.              */
  270.             cip->errNo = kErrUserCanceled;
  271.             return (cip->errNo);
  272.         } else if (zaction == kConfirmResumeProcSaidSkip) {
  273.             /* Nothing done, but not an error. */
  274.             if (deleteflag == kDeleteYes)
  275.                 (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  276.             return (kNoErr);
  277.         } else if (zaction == kConfirmResumeProcSaidResume) {
  278.             /* Resume; proc set the startPoint. */
  279.             if (startPoint == expectedSize) {
  280.                 /* Don't go to all the trouble of downloading nothing. */
  281.                 /* Nothing done, but not an error. */
  282.                 if (deleteflag == kDeleteYes)
  283.                     (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  284.                 return (kNoErr);
  285.             } else if (startPoint > expectedSize) {
  286.                 /* Cannot set start point past end of remote file */
  287.                 cip->errNo = result = kErrSetStartPoint;
  288.                 return (result);
  289.             }
  290.             fd = Open(dstfile, is_dev ? O_WRONLY|O_BINARY : O_WRONLY|O_APPEND|O_BINARY, 00666);
  291.         } else if (zaction == kConfirmResumeProcSaidAppend) {
  292.             /* leave startPoint at zero, we will append everything. */
  293.             startPoint = (longest_int) 0;
  294.             fd = Open(dstfile, is_dev ? O_WRONLY|O_BINARY|O_APPEND : O_WRONLY|O_CREAT|O_APPEND|O_BINARY, 00666);
  295.         } else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
  296.             created = 1;
  297.             startPoint = (longest_int) 0;
  298.             fd = Open(dstfile, is_dev ? O_WRONLY|O_BINARY : O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 00666);
  299.         }
  300.  
  301.         if (fd < 0) {
  302.             FTPLogError(cip, kDoPerror, "Cannot open local file %s for writing.\n", dstfile);
  303.             result = kErrOpenFailed;
  304.             cip->errNo = kErrOpenFailed;
  305.             return (result);
  306.         }
  307.  
  308.         if ((expectedSize == (longest_int) 0) && (startPoint <= (longest_int) 0) && (zaction != kConfirmResumeProcSaidOverwrite)) {
  309.             /* Don't go to all the trouble of downloading nothing. */
  310. #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
  311.             /* Note: Windows doesn't allow zero-size files. */
  312.             (void) write(fd, "\r\n", (write_size_t) 2);
  313. #endif
  314.             (void) close(fd);
  315.             if (mdtm != kModTimeUnknown) {
  316.                 cip->mdtm = mdtm;
  317.                 (void) time(&ut.actime);
  318.                 ut.modtime = mdtm;
  319.                 (void) utime(dstfile, &ut);
  320.             }
  321.             if (deleteflag == kDeleteYes)
  322.                 (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  323.             return (kNoErr);
  324.         }
  325.     } else {
  326.         fd = fdtouse;
  327.     }
  328.  
  329.     if ((cip->numDownloads == 0) && (cip->dataSocketRBufSize != 0)) {
  330.         /* If dataSocketSBufSize is non-zero, it means you
  331.          * want to explicitly try to set the size of the
  332.          * socket's I/O buffer.
  333.          *
  334.          * If it is zero, it means you want to just use the
  335.          * TCP stack's default value, which is typically
  336.          * between 8 and 64 kB.
  337.          *
  338.          * If you try to set the buffer larger than 64 kB,
  339.          * the TCP stack should try to use RFC 1323 to
  340.          * negotiate "TCP Large Windows" which may yield
  341.          * significant performance gains.
  342.          */
  343.         if (cip->hasSITE_RETRBUFSIZE == kCommandAvailable)
  344.             (void) FTPCmd(cip, "SITE RETRBUFSIZE %lu", (unsigned long) cip->dataSocketRBufSize);
  345.         else if (cip->hasSITE_RBUFSIZ == kCommandAvailable)
  346.             (void) FTPCmd(cip, "SITE RBUFSIZ %lu", (unsigned long) cip->dataSocketRBufSize);
  347.         else if (cip->hasSITE_RBUFSZ == kCommandAvailable)
  348.             (void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketRBufSize);
  349.         else if (cip->hasSITE_BUFSIZE == kCommandAvailable)
  350.             (void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
  351.     }
  352.  
  353. #ifdef NO_SIGNALS
  354. #else    /* NO_SIGNALS */
  355.     vcip = cip;
  356.     vfdtouse = fdtouse;
  357.     vfd = fd;
  358.     osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
  359.  
  360.     gGotBrokenData = 0;
  361.     gCanBrokenDataJmp = 0;
  362.  
  363. #ifdef HAVE_SIGSETJMP
  364.     sj = sigsetjmp(gBrokenDataJmp, 1);
  365. #else
  366.     sj = setjmp(gBrokenDataJmp);
  367. #endif    /* HAVE_SIGSETJMP */
  368.  
  369.     if (sj != 0) {
  370.         (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  371.         if (vfdtouse < 0) {
  372.             (void) close(vfd);
  373.         }
  374.         FTPShutdownHost(vcip);
  375.         vcip->errNo = kErrRemoteHostClosedConnection;
  376.         return(vcip->errNo);
  377.     }
  378.     gCanBrokenDataJmp = 1;
  379. #endif    /* NO_SIGNALS */
  380.  
  381.     tmpResult = FTPStartDataCmd(cip, kNetReading, xtype, startPoint, "RETR %s", file);
  382.  
  383.     if (tmpResult < 0) {
  384.         result = tmpResult;
  385.         if (result == kErrGeneric)
  386.             result = kErrRETRFailed;
  387.         cip->errNo = result;
  388.         if (fdtouse < 0) {
  389.             (void) close(fd);
  390.             if ((created != 0) && (appendflag == kAppendNo) && (cip->startPoint == 0))
  391.                 (void) unlink(dstfile);
  392.         }
  393. #if !defined(NO_SIGNALS)
  394.         (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  395. #endif    /* NO_SIGNALS */
  396.         return (result);
  397.     }
  398.  
  399.     if ((startPoint != 0) && (cip->startPoint == 0)) {
  400.         /* Remote could not or would not set the start offset
  401.          * to what we wanted.
  402.          *
  403.          * So now we have to undo our seek.
  404.          */
  405.         if (Lseek(fd, 0, SEEK_SET) != 0) {
  406.             cip->errNo = kErrLseekFailed;
  407.             if (fdtouse < 0) {
  408.                 (void) close(fd);
  409.             }
  410. #if !defined(NO_SIGNALS)
  411.             (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  412. #endif    /* NO_SIGNALS */
  413.             return (cip->errNo);
  414.         }
  415.         startPoint = 0;
  416.     }
  417.  
  418.     buf = cip->buf;
  419.     bufSize = cip->bufSize;
  420.  
  421.     FTPInitIOTimer(cip);
  422.     cip->mdtm = mdtm;
  423.     (void) time(&ut.actime);
  424.     ut.modtime = mdtm;
  425.     cip->expectedSize = expectedSize;
  426.     cip->lname = dstfile;    /* could be NULL */
  427.     cip->rname = file;
  428.     if (fdtouse >= 0)
  429.         cip->useProgressMeter = 0;
  430.     FTPStartIOTimer(cip);
  431.  
  432. #if ASCII_TRANSLATION
  433.     if (xtype == kTypeAscii) {
  434.         cr = crcr = crcr_offset = add_another_eoln = 0;
  435.         /* Ascii */
  436.         for (;;) {
  437.             if (! WaitForRemoteInput(cip)) {    /* could set cancelXfer */
  438.                 cip->errNo = result = kErrDataTimedOut;
  439.                 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
  440.                 break;
  441.             }
  442.             if (cip->cancelXfer > 0) {
  443.                 FTPAbortDataTransfer(cip);
  444.                 result = cip->errNo = kErrDataTransferAborted;
  445.                 break;
  446.             }
  447. #ifdef TESTING_ABOR
  448.             if (cip->bytesTransferred > 0) {
  449.                 cip->cancelXfer = 1;
  450.                 FTPAbortDataTransfer(cip);
  451.                 result = cip->errNo = kErrDataTransferAborted;
  452.                 break;
  453.             }
  454. #endif /* TESTING_ABOR */
  455. #ifdef NO_SIGNALS
  456.             nread = (read_return_t) SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
  457.             if (nread == kTimeoutErr) {
  458.                 cip->errNo = result = kErrDataTimedOut;
  459.                 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
  460.                 break;
  461.             } else if (nread < 0) {
  462.                 if (errno == EPIPE) {
  463.                     result = cip->errNo = kErrSocketReadFailed;
  464.                     errno = EPIPE;
  465.                     FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
  466.                 } else if (errno == EINTR) {
  467.                     continue;
  468.                 } else {
  469.                     FTPLogError(cip, kDoPerror, "Remote read failed.\n");
  470.                     result = kErrSocketReadFailed;
  471.                     cip->errNo = kErrSocketReadFailed;
  472.                 }
  473.                 break;
  474.             } else if (nread == 0) {
  475.                 break;
  476.             }
  477. #else
  478.             gCanBrokenDataJmp = 1;
  479.             if (cip->xferTimeout > 0)
  480.                 (void) alarm(cip->xferTimeout);
  481.             nread = read(cip->dataSocket, buf, (read_size_t) bufSize);
  482.             if (nread < 0) {
  483.                 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
  484.                     result = cip->errNo = kErrSocketReadFailed;
  485.                     errno = EPIPE;
  486.                     FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
  487.                     (void) shutdown(cip->dataSocket, 2);
  488.                 } else if (errno == EINTR) {
  489.                     continue;
  490.                 } else {
  491.                     result = cip->errNo = kErrSocketReadFailed;
  492.                     FTPLogError(cip, kDoPerror, "Remote read failed.\n");
  493.                     (void) shutdown(cip->dataSocket, 2);
  494.                 }
  495.                 break;
  496.             } else if (nread == 0) {
  497.                 break;
  498.             }
  499.  
  500.             gCanBrokenDataJmp = 0;
  501. #endif    /* NO_SIGNALS */
  502.  
  503.             src = buf;
  504.             srclim = src + nread;
  505.             dst = outbuf;
  506.             dstlim = dst + sizeof(outbuf);
  507.             while (src < srclim) {
  508.                 if (dst >= dstlim) {
  509.                     nwrote = write(fd, outbuf, (write_size_t) (dst - outbuf));
  510.                     if (nwrote == (write_return_t) (dst - outbuf)) {
  511.                         /* Success. */
  512.                         dst = outbuf;
  513.                     } else if (errno == EPIPE) {
  514.                         result = kErrWriteFailed;
  515.                         cip->errNo = kErrWriteFailed;
  516.                         errno = EPIPE;
  517.                         (void) shutdown(cip->dataSocket, 2);
  518.                         goto brk;
  519.                     } else {
  520.                         FTPLogError(cip, kDoPerror, "Local write failed.\n");
  521.                         result = kErrWriteFailed;
  522.                         cip->errNo = kErrWriteFailed;
  523.                         (void) shutdown(cip->dataSocket, 2);
  524.                         goto brk;
  525.                     }
  526.                 }
  527.  
  528.                 /*
  529.                  * The FTP protocol says we are to receive
  530.                  * CR+LF as the end-of-line (EOLN) sequence for
  531.                  * all files sent in ASCII mode.
  532.                  *
  533.                  * However, many servers only convert from
  534.                  * their native EOLN format to CR+LF.
  535.                  * For example, a Windows ISP being used
  536.                  * to host Mac OS 9 text files (whose EOLN
  537.                  * is just a CR) could violate protocol
  538.                  * and send the file with the raw CRs only.
  539.                  * It should be converting the file from CR
  540.                  * to CR+LF for us, but many do not, so we
  541.                  * try to convert raw CRs to our EOLN.
  542.                  *
  543.                  * We also look for raw LFs and convert them
  544.                  * if needed.
  545.                  */
  546.                 if (crcr != 0) {
  547.                     /* Leftover CR from previous buffer */
  548.                     crcr = 0;
  549.                     crcr_offset = 0;
  550.                     goto have_char_after_crcr;
  551.                 }
  552.                 if (cr != 0) {
  553.                     /* Leftover CR from previous buffer */
  554.                     cr = 0;
  555.                     goto have_char_after_cr;
  556.                 }
  557.  
  558.                 if (*src == '\n') {
  559.                     /* protocol violation: raw LF */
  560.                     src++;
  561.                     goto add_eoln;
  562.                 }
  563.                 if (*src == '\r') {
  564.                     src++;
  565.                     if (src < srclim) {
  566. have_char_after_cr:
  567.                         if (*src == '\n') {
  568.                             /* Normal case:
  569.                              * CR+LF eoln.
  570.                              */
  571.                             src++;
  572.                         } else if (*src == '\r') {
  573.                             /* Try to work-around
  574.                              * server bug which
  575.                              * sends CR+CR+LF
  576.                              * eolns.
  577.                              */
  578.                             crcr_offset = 1;
  579. have_char_after_crcr:
  580.                             if ((src + crcr_offset) < srclim) {
  581.                                 if (src[crcr_offset] == '\n') {
  582.                                     src += 1 + crcr_offset;
  583.                                 } else {
  584.                                     /* CR+CR+some other character ==
  585.                                      * Two blank lines for systems
  586.                                      * that use CR only as EOLN.
  587.                                      */
  588.                                     add_another_eoln++;
  589.                                     src += crcr_offset;
  590.                                 }
  591.                             } else {
  592.                                 crcr = 1;
  593.                                 src++;
  594.                                 continue;
  595.                             }
  596.                         }
  597.                         /* else {protocol violation: raw CR} */
  598.                     } else {
  599.                         cr = 1;
  600.                         continue;
  601.                     }
  602. add_eoln:
  603.                     if ((dst + 2) >= dstlim) {
  604.                         /* Write out buffer before
  605.                          * adding one or two chars.
  606.                          */
  607.                         nwrote = write(fd, outbuf, (write_size_t) (dst - outbuf));
  608.                         if (nwrote == (write_return_t) (dst - outbuf)) {
  609.                             /* Success. */
  610.                             dst = outbuf;
  611.                         } else if (errno == EPIPE) {
  612.                             result = kErrWriteFailed;
  613.                             cip->errNo = kErrWriteFailed;
  614.                             errno = EPIPE;
  615.                             (void) shutdown(cip->dataSocket, 2);
  616.                             goto brk;
  617.                         } else {
  618.                             FTPLogError(cip, kDoPerror, "Local write failed.\n");
  619.                             result = kErrWriteFailed;
  620.                             cip->errNo = kErrWriteFailed;
  621.                             (void) shutdown(cip->dataSocket, 2);
  622.                             goto brk;
  623.                         }
  624.                     }
  625.                     *dst++ = cip->textEOLN[0];
  626.                     if (cip->textEOLN[1] != '\0')
  627.                         *dst++ = cip->textEOLN[1];
  628.                     if (add_another_eoln != 0) {
  629.                         --add_another_eoln;
  630.                         goto add_eoln;
  631.                     }
  632.                     continue;
  633.                 }
  634.  
  635.                 *dst++ = *src++;
  636.             }
  637.             if (dst > outbuf) {
  638.                 nwrote = write(fd, outbuf, (write_size_t) (dst - outbuf));
  639.                 if (nwrote != (write_return_t) (dst - outbuf)) {
  640.                     if (errno == EPIPE) {
  641.                         result = kErrWriteFailed;
  642.                         cip->errNo = kErrWriteFailed;
  643.                         errno = EPIPE;
  644.                         (void) shutdown(cip->dataSocket, 2);
  645.                         goto brk;
  646.                     } else {
  647.                         FTPLogError(cip, kDoPerror, "Local write failed.\n");
  648.                         result = kErrWriteFailed;
  649.                         cip->errNo = kErrWriteFailed;
  650.                         (void) shutdown(cip->dataSocket, 2);
  651.                         goto brk;
  652.                     }
  653.                 }
  654.             }
  655.  
  656.             if (mdtm != kModTimeUnknown) {
  657.                 (void) utime(dstfile, &ut);
  658.             }
  659.             cip->bytesTransferred += (longest_int) nread;
  660.             FTPUpdateIOTimer(cip);
  661.         }
  662.         if (crcr != 0)
  663.             cr = 2;
  664.         while (--cr >= 0) {
  665.             /* Last block ended with a raw CR.
  666.              * Write out one last end-of-line.
  667.              */
  668.             nwrote = write(fd, cip->textEOLN, (write_size_t) strlen(cip->textEOLN));
  669.             if (nwrote == (write_return_t) strlen(cip->textEOLN)) {
  670.                 /* Success. */
  671.                 if (mdtm != kModTimeUnknown) {
  672.                     (void) utime(dstfile, &ut);
  673.                 }
  674.             } else if (errno == EPIPE) {
  675.                 result = kErrWriteFailed;
  676.                 cip->errNo = kErrWriteFailed;
  677.                 errno = EPIPE;
  678.                 (void) shutdown(cip->dataSocket, 2);
  679.                 goto brk;
  680.             } else {
  681.                 FTPLogError(cip, kDoPerror, "Local write failed.\n");
  682.                 result = kErrWriteFailed;
  683.                 cip->errNo = kErrWriteFailed;
  684.                 (void) shutdown(cip->dataSocket, 2);
  685.                 goto brk;
  686.             }
  687.         }
  688.     } else
  689. #endif    /* ASCII_TRANSLATION */
  690.     {
  691.         /* Binary */
  692.         for (;;) {
  693.             if (! WaitForRemoteInput(cip)) {    /* could set cancelXfer */
  694.                 cip->errNo = result = kErrDataTimedOut;
  695.                 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
  696.                 break;
  697.             }
  698.             if (cip->cancelXfer > 0) {
  699.                 FTPAbortDataTransfer(cip);
  700.                 result = cip->errNo = kErrDataTransferAborted;
  701.                 break;
  702.             }
  703. #ifdef TESTING_ABOR
  704.             if (cip->bytesTransferred > 0) {
  705.                 cip->cancelXfer = 1;
  706.                 FTPAbortDataTransfer(cip);
  707.                 result = cip->errNo = kErrDataTransferAborted;
  708.                 break;
  709.             }
  710. #endif /* TESTING_ABOR */
  711. #ifdef NO_SIGNALS
  712.             nread = (read_return_t) SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
  713.             if (nread == kTimeoutErr) {
  714.                 cip->errNo = result = kErrDataTimedOut;
  715.                 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
  716.                 break;
  717.             } else if (nread < 0) {
  718.                 if (errno == EPIPE) {
  719.                     result = cip->errNo = kErrSocketReadFailed;
  720.                     errno = EPIPE;
  721.                     FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
  722.                 } else if (errno == EINTR) {
  723.                     continue;
  724.                 } else {
  725.                     FTPLogError(cip, kDoPerror, "Remote read failed.\n");
  726.                     result = kErrSocketReadFailed;
  727.                     cip->errNo = kErrSocketReadFailed;
  728.                 }
  729.                 break;
  730.             } else if (nread == 0) {
  731.                 break;
  732.             }
  733. #else            
  734.             gCanBrokenDataJmp = 1;
  735.             if (cip->xferTimeout > 0)
  736.                 (void) alarm(cip->xferTimeout);
  737.             nread = read(cip->dataSocket, buf, (read_size_t) bufSize);
  738.             if (nread < 0) {
  739.                 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
  740.                     result = cip->errNo = kErrSocketReadFailed;
  741.                     errno = EPIPE;
  742.                     FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
  743.                 } else if (errno == EINTR) {
  744.                     continue;
  745.                 } else {
  746.                     result = cip->errNo = kErrSocketReadFailed;
  747.                     FTPLogError(cip, kDoPerror, "Remote read failed.\n");
  748.                 }
  749.                 (void) shutdown(cip->dataSocket, 2);
  750.                 break;
  751.             } else if (nread == 0) {
  752.                 break;
  753.             }
  754.             gCanBrokenDataJmp = 0;
  755. #endif    /* NO_SIGNALS */
  756.  
  757.             nwrote = write(fd, buf, (write_size_t) nread);
  758.             if (nwrote != nread) {
  759.                 if (errno == EPIPE) {
  760.                     result = kErrWriteFailed;
  761.                     cip->errNo = kErrWriteFailed;
  762.                     errno = EPIPE;
  763.                 } else {
  764.                     FTPLogError(cip, kDoPerror, "Local write failed.\n");
  765.                     result = kErrWriteFailed;
  766.                     cip->errNo = kErrWriteFailed;
  767.                 }
  768.                 (void) shutdown(cip->dataSocket, 2);
  769.                 break;
  770.             }
  771.  
  772.             /* Ugggh... do this after each write operation
  773.              * so it minimizes the chance of a user killing
  774.              * the process before we reset the timestamps.
  775.              */
  776.             if (mdtm != kModTimeUnknown) {
  777.                 (void) utime(dstfile, &ut);
  778.             }
  779.             cip->bytesTransferred += (longest_int) nread;
  780.             FTPUpdateIOTimer(cip);
  781.         }
  782.     }
  783.  
  784. #if ASCII_TRANSLATION
  785. brk:
  786. #endif
  787.  
  788. #if !defined(NO_SIGNALS)
  789.     if (cip->xferTimeout > 0)
  790.         (void) alarm(0);
  791.     gCanBrokenDataJmp = 0;
  792. #endif    /* NO_SIGNALS */
  793.  
  794.     if (fdtouse < 0) {
  795.         /* If they gave us a descriptor (fdtouse >= 0),
  796.          * leave it open, otherwise we opened it, so
  797.          * we need to close it.
  798.          */
  799.         (void) close(fd);
  800.         fd = -1;
  801.     }
  802.  
  803.     tmpResult = FTPEndDataCmd(cip, 1);
  804.     if ((tmpResult < 0) && (result == 0)) {
  805.         result = kErrRETRFailed;
  806.         cip->errNo = kErrRETRFailed;
  807.     }
  808.     FTPStopIOTimer(cip);
  809. #if !defined(NO_SIGNALS)
  810.     (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
  811. #endif    /* NO_SIGNALS */
  812.  
  813.     if (mdtm != kModTimeUnknown) {
  814.         (void) utime(dstfile, &ut);
  815.     }
  816.  
  817.     if (result == kNoErr) {
  818.         cip->numDownloads++;
  819.  
  820.         if (deleteflag == kDeleteYes) {
  821.             result = FTPDelete(cip, file, kRecursiveNo, kGlobNo);
  822.         }
  823.     }
  824.  
  825.     return (result);
  826. }    /* FTPGetOneF */
  827.