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_put.c < prev    next >
C/C++ Source or Header  |  2008-07-15  |  18KB  |  679 lines

  1. /* io_put.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. #ifndef O_BINARY
  16.     /* Needed for platforms using different EOLN sequence (i.e. DOS) */
  17. #    ifdef _O_BINARY
  18. #        define O_BINARY _O_BINARY
  19. #    else
  20. #        define O_BINARY 0
  21. #    endif
  22. #endif
  23.  
  24. static int
  25. FTPASCIILocalFileSeek(const int fd, const longest_int howMuchToSkip, char *const inbuf, const size_t bufsize)
  26. {
  27.     int c;
  28.     longest_int count = howMuchToSkip;
  29.     longest_int startpos, endpos, backskip;
  30.     const char *src = NULL, *srclim = NULL;
  31.     read_return_t nread;
  32.  
  33.     if (count == 0)
  34.         return (0);
  35.     if (count < 0)
  36.         return (-1);
  37.     startpos = (longest_int) Lseek(fd, 0, SEEK_CUR);
  38.     if (startpos == (longest_int) -1)
  39.         return (-1);
  40.  
  41.     while (count > 0) {
  42.         nread = read(fd, inbuf, (read_size_t) bufsize);
  43.         if (nread <= 0) {
  44.             count = -1;
  45.             break;
  46.         }
  47.         for (src = inbuf, srclim = inbuf + nread; src < srclim; ) {
  48.             c = (int) *src++;
  49.             if (c == '\r') {
  50.                 /* Oh dear... what's this doing in here? */
  51.                 count = -1;
  52.             } else if (c == '\n') {
  53.                 count -= 2;
  54.                 /* If count is now < 0, then the file
  55.                  * was corrupt and cannot be resumed.
  56.                  */
  57.             } else {
  58.                 --count;
  59.             }
  60.             if (count <= 0)
  61.                 break;
  62.         }
  63.     }
  64.     if (count < 0) {
  65.         endpos = (longest_int) Lseek(fd, startpos, SEEK_SET);
  66.         if (endpos != startpos)
  67.             return (-2);
  68.         return (-1);
  69.     }
  70.  
  71.     if (src != NULL) {
  72.         backskip = (longest_int) -(srclim - src);
  73.         if (backskip != 0) {
  74.             endpos = (longest_int) Lseek(fd, backskip,  SEEK_CUR);
  75.             if (endpos == (longest_int) -1)
  76.                 return (-2);
  77.         }
  78.     }
  79.  
  80.     return (0);
  81. }    /* FTPASCIILocalFileSeek */
  82.  
  83.  
  84.  
  85. static int
  86. FTPPutBlock(
  87.     const FTPCIPtr cip,
  88.     const char *cp,
  89.     write_size_t ntowrite
  90. )
  91. {
  92.     write_return_t nwrote;
  93.     int result = kNoErr;
  94.     
  95.     do {
  96.         if (! WaitForRemoteOutput(cip)) {    /* could set cancelXfer */
  97.             cip->errNo = result = kErrDataTimedOut;
  98.             FTPLogError(cip, kDontPerror, "Remote write timed out.\n");
  99.             return (result);
  100.         }
  101.         if (cip->cancelXfer > 0) {
  102.             FTPAbortDataTransfer(cip);
  103.             result = cip->errNo = kErrDataTransferAborted;
  104.             return (result);
  105.         }
  106.  
  107.         nwrote = (write_return_t) SWrite(cip->dataSocket, cp, (size_t) ntowrite, (int) cip->xferTimeout, kNoFirstSelect);
  108.         if (nwrote < 0) {
  109.             if (nwrote == kTimeoutErr) {
  110.                 cip->errNo = result = kErrDataTimedOut;
  111.                 FTPLogError(cip, kDontPerror, "Remote write timed out.\n");
  112.             } else if (errno == EPIPE) {
  113.                 cip->errNo = result = kErrSocketWriteFailed;
  114.                 errno = EPIPE;
  115.                 FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
  116.             } else if (errno == EINTR) {
  117.                 continue;
  118.             } else {
  119.                 cip->errNo = result = kErrSocketWriteFailed;
  120.                 FTPLogError(cip, kDoPerror, "Remote write failed.\n");
  121.             }
  122.             (void) shutdown(cip->dataSocket, 2);
  123.             return (result);
  124.         }
  125.         cp += nwrote;
  126.         ntowrite -= (write_size_t) nwrote;
  127.     } while (ntowrite != 0);
  128.     FTPUpdateIOTimer(cip);
  129.  
  130.     return (result);
  131. }    /* FTPPutBlock */
  132.  
  133.  
  134.  
  135.  
  136. int
  137. FTPPutOneF(
  138.     const FTPCIPtr cip,
  139.     const char *const file,
  140.     const char *volatile dstfile,
  141.     int xtype,
  142.     const int fdtouse,
  143.     const int appendflag,
  144.     const char *volatile tmppfx,
  145.     const char *volatile tmpsfx,
  146.     const int resumeflag,
  147.     const int deleteflag,
  148.     const FTPConfirmResumeUploadProc resumeProc)
  149. {
  150.     char *buf, *cp;
  151.     const char *cmd;
  152.     const char *odstfile;
  153.     const char *tdstfile;
  154.     size_t bufSize;
  155.     size_t l;
  156.     int tmpResult, result, pbrc;
  157.     read_return_t nread;
  158.     volatile int fd;
  159.     char dstfile2[512];
  160.     char *src, *srclim, *dst;
  161.     write_size_t ntowrite;
  162.     char inbuf[256], crlf[4];
  163.     int lastch_of_prev_block, lastch_of_cur_block;
  164.     int fstatrc, statrc;
  165.     longest_int startPoint = 0;
  166.     longest_int localsize;
  167.     struct Stat st;
  168.     time_t mdtm;
  169.     volatile int vzaction;
  170.     int zaction = kConfirmResumeProcSaidBestGuess;
  171.     int sameAsRemote;
  172.     int skiprc;
  173.     size_t skipbufsize;
  174.     longstring cmdStr;
  175.     
  176.     if (cip->buf == NULL) {
  177.         FTPLogError(cip, kDoPerror, "Transfer buffer not allocated.\n");
  178.         cip->errNo = kErrNoBuf;
  179.         return (cip->errNo);
  180.     }
  181.  
  182.     cip->usingTAR = 0;
  183.     if (fdtouse < 0) {
  184.         fd = Open(file, O_RDONLY|O_BINARY, 0);
  185.         if (fd < 0) {
  186.             FTPLogError(cip, kDoPerror, "Cannot open local file %s for reading.\n", file);
  187.             cip->errNo = kErrOpenFailed;
  188.             return (cip->errNo);
  189.         }
  190.     } else {
  191.         fd = fdtouse;
  192.     }
  193.  
  194.     fstatrc = Fstat(fd, &st);
  195.     if ((fstatrc == 0) && (S_ISDIR(st.st_mode))) {
  196.         if (fdtouse < 0) {
  197.             (void) close(fd);
  198.         }
  199.         FTPLogError(cip, kDontPerror, "%s is a directory.\n", (file != NULL) ? file : "that");
  200.         cip->errNo = kErrOpenFailed;
  201.         return (cip->errNo);
  202.     }
  203.  
  204.     localsize = (longest_int) st.st_size;
  205.     if ((xtype == kTypeAscii) && (file != NULL) && (file[0] != '\0')) {
  206.         localsize = FTPLocalASCIIFileSize(file, cip->buf, cip->bufSize);
  207.     }
  208.  
  209.     /* For Put, we can't recover very well if it turns out restart
  210.      * didn't work, so check beforehand.
  211.      */
  212.     if ((resumeflag == kResumeYes) || (resumeProc != kNoFTPConfirmResumeUploadProc)) {
  213.         FTPCheckForRestartModeAvailability(cip); 
  214.     }
  215.  
  216.     odstfile = dstfile;
  217.     if (((tmppfx != NULL) && (tmppfx[0] != '\0')) || ((tmpsfx != NULL) && (tmpsfx[0] != '\0'))) {
  218.         cp = strrchr(dstfile, '/');
  219.         if (cp == NULL)
  220.             cp = strrchr(dstfile, '\\');
  221.         if (cp == NULL) {
  222.             (void) STRNCPY(dstfile2, ((tmppfx == NULL) ? "" : tmppfx));
  223.             (void) STRNCAT(dstfile2, dstfile);
  224.             (void) STRNCAT(dstfile2, ((tmpsfx == NULL) ? "" : tmpsfx));
  225.         } else {
  226.             cp++;
  227.             l = (size_t) (cp - dstfile);
  228.             (void) STRNCPY(dstfile2, dstfile);
  229.             dstfile2[l] = '\0';    /* Nuke stuff after / */
  230.             (void) STRNCAT(dstfile2, ((tmppfx == NULL) ? "" : tmppfx));
  231.             (void) STRNCAT(dstfile2, cp);
  232.             (void) STRNCAT(dstfile2, ((tmpsfx == NULL) ? "" : tmpsfx));
  233.         }
  234.         dstfile = dstfile2;
  235.     }
  236.  
  237.     if (fdtouse < 0) {
  238.         AutomaticallyUseASCIIModeDependingOnExtension(cip, dstfile, &xtype);
  239.         mdtm = kModTimeUnknown;
  240.         if ((cip->progress != (FTPProgressMeterProc) 0) || (resumeflag == kResumeYes) || (resumeProc != kNoFTPConfirmResumeUploadProc)) {
  241.             (void) FTPFileSizeAndModificationTime(cip, dstfile, &startPoint, xtype, &mdtm);
  242.         }
  243.  
  244.         if (appendflag == kAppendYes) {
  245.             zaction = kConfirmResumeProcSaidAppend;
  246.         } else if (
  247.         /*        (cip->hasREST == kCommandNotAvailable) || We can now try to use APPE when REST is not availble. */
  248.         /*        (xtype != kTypeBinary) || We can now resume in ASCII too. */
  249.                 (fstatrc < 0)
  250.         ) {
  251.             zaction = kConfirmResumeProcSaidOverwrite;
  252.         } else if (resumeflag == kResumeYes) {
  253.             zaction = kConfirmResumeProcSaidBestGuess;
  254.         } else {
  255.             zaction = kConfirmResumeProcSaidOverwrite;
  256.         }
  257.  
  258.         statrc = -1;
  259.         if ((mdtm != kModTimeUnknown) || (startPoint != kSizeUnknown)) {
  260.             /* Then we know the file exists.  We will
  261.              * ask the user what to do, if possible, below.
  262.              */
  263.             statrc = 0;
  264.         } else if ((resumeProc != kNoFTPConfirmResumeUploadProc) && (cip->hasMDTM != kCommandAvailable) && (cip->hasSIZE != kCommandAvailable)) {
  265.             /* We already checked if the file had a filesize
  266.              * or timestamp above, but if the server indicated
  267.              * it did not support querying those directly,
  268.              * we now need to try to determine if the file
  269.              * exists in a few other ways.
  270.              */
  271.             statrc = FTPFileExists2(cip, dstfile, 0, 0, 0, 1, 1);
  272.         }
  273.  
  274.         sameAsRemote = 0;
  275.         if (
  276.             (resumeProc != kNoFTPConfirmResumeUploadProc) &&
  277.             (statrc == 0)
  278.         ) {
  279.             tdstfile = dstfile;
  280.             zaction = (*resumeProc)(cip, file, (longest_int) st.st_size, st.st_mtime, &tdstfile, startPoint, mdtm, &startPoint);
  281.             dstfile = tdstfile;
  282.         }
  283.  
  284.         if (zaction == kConfirmResumeProcSaidCancel) {
  285.             /* User wants to cancel this file and any
  286.              * remaining in batch.
  287.              */
  288.             cip->errNo = kErrUserCanceled;
  289.             return (cip->errNo);
  290.         }
  291.  
  292.         if (zaction == kConfirmResumeProcSaidBestGuess) {
  293.             if ((mdtm != kModTimeUnknown) && (st.st_mtime > (mdtm + 1))) {
  294.                 /* Local file is newer than remote,
  295.                  * overwrite the remote file instead
  296.                  * of trying to resume it.
  297.                  *
  298.                  * Note:  Add one second fudge factor
  299.                  * for Windows' file timestamps being
  300.                  * imprecise to one second.
  301.                  */
  302.                 zaction = kConfirmResumeProcSaidOverwrite; 
  303.             } else if (localsize == startPoint) {
  304.                 /* Already sent file, done. */
  305.                 zaction = kConfirmResumeProcSaidSkip; 
  306.                 sameAsRemote = 1;
  307.             } else if ((startPoint != kSizeUnknown) && (localsize > startPoint)) {
  308.                 zaction = kConfirmResumeProcSaidResume; 
  309.             } else {
  310.                 zaction = kConfirmResumeProcSaidOverwrite; 
  311.             }
  312.         }
  313.  
  314.         if (zaction == kConfirmResumeProcSaidSkip) {
  315.             /* Nothing done, but not an error. */
  316.             if (fdtouse < 0) {
  317.                 (void) close(fd);
  318.             }
  319.             if (deleteflag == kDeleteYes) {
  320.                 if (unlink(file) < 0) {
  321.                     cip->errNo = kErrLocalDeleteFailed;
  322.                     return (cip->errNo);
  323.                 }
  324.             }
  325.             if (sameAsRemote != 0) {
  326.                 cip->errNo = kErrRemoteSameAsLocal;
  327.                 return (cip->errNo);
  328.             }
  329.             return (kNoErr);
  330.         } else if (zaction == kConfirmResumeProcSaidResume) {
  331.             /* Resume; proc set the startPoint. */
  332.             if (localsize == startPoint) {
  333.                 /* Already sent file, done. */
  334.                 if (fdtouse < 0) {
  335.                     (void) close(fd);
  336.                 }
  337.  
  338.                 if (deleteflag == kDeleteYes) {
  339.                     if (unlink(file) < 0) {
  340.                         cip->errNo = kErrLocalDeleteFailed;
  341.                         return (cip->errNo);
  342.                     }
  343.                 }
  344.                 return (kNoErr);
  345.             } else if (xtype == kTypeAscii) {
  346.                 skipbufsize = cip->bufSize;
  347.                 if ((fdtouse >= 0) && (skipbufsize > 4096))
  348.                     skipbufsize = 4096;
  349.  
  350.                 skiprc = FTPASCIILocalFileSeek(fd, startPoint, cip->buf, skipbufsize);
  351.                 if (skiprc == -2) {
  352.                     cip->errNo = kErrAsciiSeekErr;
  353.                     return (cip->errNo);
  354.                 } else if (skiprc == -1) {
  355.                     /* Overwrite */
  356.                     cip->startPoint = startPoint = 0;
  357.                 } else {
  358.                     /* It worked */
  359.                     /* We could do: cip->startPoint = startPoint;
  360.                      * and let it do a REST with STOR,
  361.                      * but it is safer to use APPE only.
  362.                      */
  363.                     zaction = kConfirmResumeProcSaidAppend;
  364.                     cip->startPoint = startPoint = 0;
  365.                 }
  366.             } else if (Lseek(fd, startPoint, SEEK_SET) != -1) {
  367.                 cip->startPoint = startPoint;
  368.             }
  369.         } else if (zaction == kConfirmResumeProcSaidAppend) {
  370.             /* append: leave startPoint at zero, we will append everything. */
  371.             cip->startPoint = startPoint = 0;
  372.         } else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
  373.             /* overwrite: leave startPoint at zero */
  374.             cip->startPoint = startPoint = 0;
  375.         }
  376.     } else if (appendflag == kAppendYes) {
  377.         zaction = kConfirmResumeProcSaidAppend;
  378.     }
  379.  
  380.     FTPSetUploadSocketBufferSize(cip);
  381.  
  382.     vzaction = zaction;
  383.     if (vzaction == kConfirmResumeProcSaidAppend) {
  384.         cmd = "APPE ";
  385.         tmppfx = "";    /* Can't use that here. */
  386.         tmpsfx = "";
  387.     } else {
  388.         cmd = "STOR ";
  389.         if (tmppfx == NULL)
  390.             tmppfx = "";
  391.         if (tmpsfx == NULL)
  392.             tmpsfx = "";
  393.     }
  394.  
  395.     memcpy(cmdStr, cmd, 4 + 1 /* space */ + 1 /* nul byte */);
  396.     STRNCAT(cmdStr, dstfile);
  397.  
  398.     tmpResult = FTPStartDataCmd2(
  399.         cip,
  400.         kNetWriting,
  401.         xtype,
  402.         startPoint,
  403.         cmdStr,
  404.         sizeof(cmdStr),
  405.         "(not used)"
  406.     );
  407.  
  408.     if (tmpResult < 0) {
  409.         cip->errNo = tmpResult;
  410.         if (fdtouse < 0) {
  411.             (void) close(fd);
  412.         }
  413.         return (cip->errNo);
  414.     }
  415.  
  416.     if ((startPoint != 0) && (cip->startPoint == 0)) {
  417.         /* Remote could not or would not set the start offset
  418.          * to what we wanted.
  419.          *
  420.          * So now we have to undo our seek.
  421.          */
  422.         if (Lseek(fd, 0, SEEK_SET) != 0) {
  423.             cip->errNo = kErrLseekFailed;
  424.             if (fdtouse < 0) {
  425.                 (void) close(fd);
  426.             }
  427.             return (cip->errNo);
  428.         }
  429.         startPoint = 0;
  430.     }
  431.  
  432.     result = kNoErr;
  433.     buf = cip->buf;
  434.     bufSize = cip->bufSize;
  435.  
  436.     FTPInitIOTimer(cip);
  437.     if ((fstatrc == 0) && (S_ISREG(st.st_mode) != 0)) {
  438.         cip->expectedSize = st.st_size;
  439.         cip->mdtm = st.st_mtime;
  440.     }
  441.     cip->lname = file;    /* could be NULL */
  442.     cip->rname = odstfile;
  443.     if (fdtouse >= 0)
  444.         cip->useProgressMeter = 0;
  445.     FTPStartIOTimer(cip);
  446.  
  447.     /* Note: On Windows, we don't have to do anything special
  448.      * for ASCII mode, since Net ASCII's end-of-line sequence
  449.      * corresponds to the same thing used for DOS/Windows.
  450.      */
  451.  
  452.     if (xtype == kTypeAscii) {
  453.         /* ascii */
  454.  
  455.         if (cip->asciiTranslationMode == kAsciiTranslationModeNone) {
  456.             /* Warning: this is really just for testing FTP server software.
  457.              * Do not use this mode for your FTP client program!
  458.              */
  459.             for (;;) {
  460.                 nread = read(fd, inbuf, (read_size_t) sizeof(inbuf));
  461.                 if (nread < 0) {
  462.                     if (errno == EINTR) {
  463.                         continue;
  464.                     } else {
  465.                         result = kErrReadFailed;
  466.                         cip->errNo = kErrReadFailed;
  467.                         FTPLogError(cip, kDoPerror, "Local read failed.\n");
  468.                     }
  469.                     break;
  470.                 } else if (nread == 0) {
  471.                     break;
  472.                 }
  473.                 cip->bytesTransferred += (longest_int) nread;
  474.     
  475.                 ntowrite = (write_size_t) nread;
  476.                 cp = inbuf;
  477.     
  478.                 if ((pbrc = FTPPutBlock(cip, cp, ntowrite)) < 0) {
  479.                     result = pbrc;
  480.                     goto brk;
  481.                 }
  482.             }
  483.         } else if ((cip->asciiTranslationMode == kAsciiTranslationModeStripCRs) || (cip->asciiTranslationMode == kAsciiTranslationModeFixEOLNs)) {
  484.             /* TO-DO: Really add the fix mode, kAsciiTranslationModeFixEOLNs. */
  485.             lastch_of_prev_block = 0;
  486.             for (;;) {
  487.                 nread = read(fd, inbuf, (read_size_t) sizeof(inbuf));
  488.                 if (nread < 0) {
  489.                     if (errno == EINTR) {
  490.                         continue;
  491.                     } else {
  492.                         result = kErrReadFailed;
  493.                         cip->errNo = kErrReadFailed;
  494.                         FTPLogError(cip, kDoPerror, "Local read failed.\n");
  495.                     }
  496.                     break;
  497.                 } else if (nread == 0) {
  498.                     break;
  499.                 }
  500.                 cip->bytesTransferred += (longest_int) nread;
  501.                 
  502.                 src = inbuf;
  503.                 srclim = src + nread;
  504.                 lastch_of_cur_block = srclim[-1];
  505.                 if (lastch_of_cur_block == '\r') {
  506.                     srclim[-1] = '\0';
  507.                     srclim--;
  508.                     nread--;
  509.                     if (nread == 0) {
  510.                         lastch_of_prev_block = lastch_of_cur_block;
  511.                         break;
  512.                     }
  513.                 }
  514.                 dst = cip->buf;        /* must be 2x sizeof inbuf or more. */
  515.     
  516.                 if (*src == '\n') {
  517.                     src++;
  518.                     *dst++ = '\r';
  519.                     *dst++ = '\n';
  520.                 } else if (lastch_of_prev_block == '\r') {
  521.                     /* Raw CR at end of last block,
  522.                      * no LF at the start of this block.
  523.                      */
  524.                     *dst++ = '\r';
  525.                     *dst++ = '\n';
  526.                 }
  527.     
  528.                 /* Prepare the buffer, converting end-of-lines
  529.                  * to CR+LF format as required by protocol.
  530.                  */
  531.                 while (src < srclim) {
  532.                     if (*src == '\r') {
  533.                         if (src[1] == '\n') {
  534.                             /* CR+LF pair */
  535.                             *dst++ = *src++;
  536.                             *dst++ = *src++;
  537.                         } else {
  538.                             /* raw CR */
  539.                             *dst++ = *src++;
  540.                             *dst++ = '\n';
  541.                         }
  542.                     } else if (*src == '\n') {
  543.                         /* LF only; expected for UNIX text. */
  544.                         *dst++ = '\r';
  545.                         *dst++ = *src++;
  546.                     } else {
  547.                         *dst++ = *src++;
  548.                     }
  549.                 }
  550.                 lastch_of_prev_block = lastch_of_cur_block;
  551.     
  552.                 ntowrite = (write_size_t) (dst - cip->buf);
  553.                 cp = cip->buf;
  554.     
  555.                 if ((pbrc = FTPPutBlock(cip, cp, ntowrite)) < 0) {
  556.                     result = pbrc;
  557.                     goto brk;
  558.                 }
  559.             }
  560.     
  561.             if (lastch_of_prev_block == '\r') {
  562.                 /* Very rare, but if the file's last byte is a raw CR
  563.                  * we need to write out one more line since we
  564.                  * skipped it earlier.
  565.                  */
  566.                 crlf[0] = '\r';
  567.                 crlf[1] = '\n';
  568.                 crlf[2] = '\0';
  569.                 cp = crlf;
  570.                 ntowrite = 2;
  571.     
  572.                 if ((pbrc = FTPPutBlock(cip, cp, ntowrite)) < 0) {
  573.                     result = pbrc;
  574.                     goto brk;
  575.                 }
  576.             }
  577.         }
  578.     } else {
  579.         /* binary */
  580.         for (;;) {
  581.             cp = buf;
  582.             nread = read(fd, cp, (read_size_t) bufSize);
  583.             if (nread < 0) {
  584.                 if (errno == EINTR) {
  585.                     continue;
  586.                 } else {
  587.                     result = kErrReadFailed;
  588.                     cip->errNo = kErrReadFailed;
  589.                     FTPLogError(cip, kDoPerror, "Local read failed.\n");
  590.                 }
  591.                 break;
  592.             } else if (nread == 0) {
  593.                 break;
  594.             }
  595.             cip->bytesTransferred += (longest_int) nread;
  596.     
  597.             ntowrite = (write_size_t) nread;
  598.             if ((pbrc = FTPPutBlock(cip, cp, ntowrite)) < 0) {
  599.                 result = pbrc;
  600.                 goto brk;
  601.             }
  602.         }
  603.     }
  604. brk:
  605.  
  606.     /* This looks very bizarre, since
  607.      * we will be checking the socket
  608.      * for readability here!
  609.      *
  610.      * The reason for this is that we
  611.      * want to be able to timeout a
  612.      * small put.  So, we close the
  613.      * write end of the socket first,
  614.      * which tells the server we're
  615.      * done writing.  We then wait
  616.      * for the server to close down
  617.      * the whole socket (we know this
  618.      * when the socket is ready for
  619.      * reading an EOF), which tells
  620.      * us that the file was completed.
  621.      */
  622.     (void) shutdown(cip->dataSocket, 1);
  623.     (void) WaitForRemoteInput(cip);
  624.  
  625.     tmpResult = FTPEndDataCmd(cip, 1);
  626.     if ((tmpResult < 0) && (result == kNoErr)) {
  627.         cip->errNo = result = kErrSTORFailed;
  628.     }
  629.     FTPStopIOTimer(cip);
  630.  
  631.     if (fdtouse < 0) {
  632.         /* If they gave us a descriptor (fdtouse >= 0),
  633.          * leave it open, otherwise we opened it, so
  634.          * we need to dispose of it.
  635.          */
  636.         (void) Fstat(fd, &st);
  637.         (void) close(fd);
  638.         fd = -1;
  639.     }
  640.  
  641.     if (result == kNoErr) {
  642.         /* The store succeeded;  If we were
  643.          * uploading to a temporary file,
  644.          * move the new file to the new name.
  645.          */
  646.         cip->numUploads++;
  647.  
  648.         if ((tmppfx[0] != '\0') || (tmpsfx[0] != '\0')) {
  649.             if ((result = FTPRename(cip, dstfile, odstfile)) < 0) {
  650.                 /* May fail if file was already there,
  651.                  * so delete the old one so we can move
  652.                  * over it.
  653.                  */
  654.                 if (FTPDelete(cip, odstfile, kRecursiveNo, kGlobNo) == kNoErr) {
  655.                     result = FTPRename(cip, dstfile, odstfile);
  656.                     if (result < 0) {
  657.                         FTPLogError(cip, kDontPerror, "Could not rename %s to %s: %s.\n", dstfile, odstfile, FTPStrError(cip->errNo));
  658.                     }
  659.                 } else {
  660.                     FTPLogError(cip, kDontPerror, "Could not delete old %s, so could not rename %s to that: %s\n", odstfile, dstfile, FTPStrError(cip->errNo));
  661.                 }
  662.             }
  663.         }
  664.  
  665.         if (FTPUtime(cip, odstfile, st.st_atime, st.st_mtime, st.st_ctime) != kNoErr) {
  666.             if (cip->errNo != kErrUTIMENotAvailable)
  667.                 FTPLogError(cip, kDontPerror, "Could not preserve times for %s: %s.\n", odstfile, FTPStrError(cip->errNo));
  668.         }
  669.  
  670.         if ((result == kNoErr) && (deleteflag == kDeleteYes)) {
  671.             if (unlink(file) < 0) {
  672.                 result = cip->errNo = kErrLocalDeleteFailed;
  673.             }
  674.         }
  675.     }
  676.  
  677.     return (result);
  678. }    /* FTPPutOneF */
  679.