void FTPStopIOTimer(const FTPCIPtr cip) { cip->nextProgressUpdate = 0; /* force last update */ FTPUpdateIOTimer(cip); if (cip->progress != (FTPProgressMeterProc) 0) (*cip->progress)(cip, kPrEndMsg); } /* FTPStopIOTimer */
int FTPPutFileFromMemory( const FTPCIPtr cip, const char *volatile dstfile, const char *volatile src, const size_t srcLen, const int appendflag) { const char *cp; const char *cmd; int tmpResult, result; read_return_t nread; write_return_t nwrote; size_t bufSize; const char *srcLim; const char *volatile srcp; #if !defined(NO_SIGNALS) int sj; volatile FTPSigProc osigpipe; volatile FTPCIPtr vcip; #endif /* NO_SIGNALS */ if (cip->buf == NULL) { FTPLogError(cip, kDoPerror, "Transfer buffer not allocated.\n"); cip->errNo = kErrNoBuf; return (cip->errNo); } cip->usingTAR = 0; /* For Put, we can't recover very well if it turns out restart * didn't work, so check beforehand. */ FTPCheckForRestartModeAvailability(cip); FTPSetUploadSocketBufferSize(cip); #ifdef NO_SIGNALS #else /* NO_SIGNALS */ vcip = cip; osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData); gGotBrokenData = 0; gCanBrokenDataJmp = 0; #ifdef HAVE_SIGSETJMP sj = sigsetjmp(gBrokenDataJmp, 1); #else sj = setjmp(gBrokenDataJmp); #endif /* HAVE_SIGSETJMP */ if (sj != 0) { (void) signal(SIGPIPE, (FTPSigProc) osigpipe); FTPShutdownHost(vcip); vcip->errNo = kErrRemoteHostClosedConnection; return(vcip->errNo); } gCanBrokenDataJmp = 1; #endif /* NO_SIGNALS */ cmd = appendflag == kAppendYes ? "APPE" : "STOR"; tmpResult = FTPStartDataCmd( cip, kNetWriting, kTypeBinary, (longest_int) 0, "%s %s", cmd, dstfile ); if (tmpResult < 0) { cip->errNo = tmpResult; #if !defined(NO_SIGNALS) (void) signal(SIGPIPE, (FTPSigProc) osigpipe); #endif /* NO_SIGNALS */ return (cip->errNo); } result = kNoErr; bufSize = cip->bufSize; FTPInitIOTimer(cip); cip->expectedSize = (longest_int) srcLen; cip->lname = NULL; /* could be NULL */ cip->rname = dstfile; srcp = src; srcLim = src + srcLen; FTPStartIOTimer(cip); { /* binary */ for (;;) { #if !defined(NO_SIGNALS) gCanBrokenDataJmp = 0; #endif /* NO_SIGNALS */ nread = (read_return_t) bufSize; if ((size_t) (srcLim - srcp) < bufSize) { nread = (read_return_t) (srcLim - srcp); if (nread == 0) { result = kNoErr; break; } } cip->bytesTransferred += (longest_int) nread; cp = srcp; srcp += nread; #if !defined(NO_SIGNALS) gCanBrokenDataJmp = 1; if (cip->xferTimeout > 0) (void) alarm(cip->xferTimeout); #endif /* NO_SIGNALS */ do { if (! WaitForRemoteOutput(cip)) { /* could set cancelXfer */ cip->errNo = result = kErrDataTimedOut; FTPLogError(cip, kDontPerror, "Remote write timed out.\n"); goto brk; } if (cip->cancelXfer > 0) { FTPAbortDataTransfer(cip); result = cip->errNo = kErrDataTransferAborted; goto brk; } #ifdef NO_SIGNALS nwrote = (write_return_t) SWrite(cip->dataSocket, cp, (size_t) nread, (int) cip->xferTimeout, kNoFirstSelect); if (nwrote < 0) { if (nwrote == kTimeoutErr) { cip->errNo = result = kErrDataTimedOut; FTPLogError(cip, kDontPerror, "Remote write timed out.\n"); } else if (errno == EPIPE) { cip->errNo = result = kErrSocketWriteFailed; errno = EPIPE; FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n"); } else if (errno == EINTR) { continue; } else { cip->errNo = result = kErrSocketWriteFailed; FTPLogError(cip, kDoPerror, "Remote write failed.\n"); } (void) shutdown(cip->dataSocket, 2); goto brk; } #else /* NO_SIGNALS */ nwrote = write(cip->dataSocket, cp, (write_size_t) nread); if (nwrote < 0) { if ((gGotBrokenData != 0) || (errno == EPIPE)) { cip->errNo = result = kErrSocketWriteFailed; errno = EPIPE; FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n"); } else if (errno == EINTR) { continue; } else { cip->errNo = result = kErrSocketWriteFailed; FTPLogError(cip, kDoPerror, "Remote write failed.\n"); } (void) shutdown(cip->dataSocket, 2); goto brk; } #endif /* NO_SIGNALS */ cp += nwrote; nread -= nwrote; } while (nread > 0); FTPUpdateIOTimer(cip); } } brk: /* This looks very bizarre, since * we will be checking the socket * for readability here! * * The reason for this is that we * want to be able to timeout a * small put. So, we close the * write end of the socket first, * which tells the server we're * done writing. We then wait * for the server to close down * the whole socket (we know this * when the socket is ready for * reading an EOF), which tells * us that the file was completed. */ (void) shutdown(cip->dataSocket, 1); (void) WaitForRemoteInput(cip); #if !defined(NO_SIGNALS) gCanBrokenDataJmp = 0; if (cip->xferTimeout > 0) (void) alarm(0); #endif /* NO_SIGNALS */ tmpResult = FTPEndDataCmd(cip, 1); if ((tmpResult < 0) && (result == kNoErr)) { cip->errNo = result = kErrSTORFailed; } FTPStopIOTimer(cip); if (result == kNoErr) { /* The store succeeded; If we were * uploading to a temporary file, * move the new file to the new name. */ cip->numUploads++; } #if !defined(NO_SIGNALS) (void) signal(SIGPIPE, (FTPSigProc) osigpipe); #endif /* NO_SIGNALS */ return (result); } /* FTPPutFileFromMemory */
/* The purpose of this is to provide updates for the progress meters * during lags. Return zero if the operation timed-out. */ int WaitForRemoteOutput(const FTPCIPtr cip) { fd_set ss, ss2; struct timeval tv; int result; int fd; int wsecs; int xferTimeout; int ocancelXfer; xferTimeout = cip->xferTimeout; if (xferTimeout < 1) return (1); fd = cip->dataSocket; if (fd < 0) return (1); if (cip->dataTimedOut > 0) { cip->dataTimedOut++; return (0); /* already timed-out */ } ocancelXfer = cip->cancelXfer; wsecs = 0; cip->stalled = 0; while ((xferTimeout <= 0) || (wsecs < xferTimeout)) { if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) { /* leave cip->stalled -- could have been stalled and then canceled. */ return (1); } MY_FD_ZERO(&ss); #if defined(__DECC) || defined(__DECCXX) #pragma message save #pragma message disable trunclongint #endif MY_FD_SET(fd, &ss); #if defined(__DECC) || defined(__DECCXX) #pragma message restore #endif ss2 = ss; tv.tv_sec = 1; tv.tv_usec = 0; result = select(fd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &ss2, &tv); if (result >= 1) { /* ready */ cip->stalled = 0; return (1); } else if (result < 0) { if (errno != EINTR) { cip->stalled = 0; return (1); /* Ready to read error */ } } else { wsecs++; cip->stalled = wsecs; } FTPUpdateIOTimer(cip); } #if !defined(NO_SIGNALS) /* Shouldn't get here -- alarm() should have * went off by now. */ (void) kill(getpid(), SIGALRM); #endif /* NO_SIGNALS */ cip->dataTimedOut++; return (0); /* timed-out */ } /* WaitForRemoteOutput */
int FTPGetFileToMemory( const FTPCIPtr cip, const char *const file, char *memBuf, const size_t maxNumberOfBytesToWriteToMemBuf, size_t *const numberOfBytesWrittenToMemBuf, const longest_int startPoint, const int deleteflag ) { int tmpResult; volatile int result; int atEOF; longest_int expectedSize; size_t ntoread; read_return_t nread; size_t numberOfBytesLeftInMemBuf; #if !defined(NO_SIGNALS) volatile FTPSigProc osigpipe; volatile FTPCIPtr vcip; int sj; #endif /* NO_SIGNALS */ result = kNoErr; atEOF = 1; cip->usingTAR = 0; numberOfBytesLeftInMemBuf = maxNumberOfBytesToWriteToMemBuf; if (numberOfBytesWrittenToMemBuf != NULL) *numberOfBytesWrittenToMemBuf = 0; if ((file == NULL) || (file[0] == '\0') || (memBuf == NULL) || (maxNumberOfBytesToWriteToMemBuf == 0)) { return (kErrBadParameter); } FTPCheckForRestartModeAvailability(cip); if ((startPoint != 0) && (cip->hasREST == kCommandNotAvailable)) { cip->errNo = kErrRESTNotAvailable; return (cip->errNo); } (void) FTPFileSize(cip, file, &expectedSize, kTypeBinary); if ((expectedSize != (longest_int) 0) && (startPoint > expectedSize)) { /* Don't go to all the trouble of downloading nothing. */ if (deleteflag == kDeleteYes) (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo); return (kNoErr); } if ((cip->numDownloads == 0) && (cip->dataSocketRBufSize != 0)) { /* If dataSocketSBufSize is non-zero, it means you * want to explicitly try to set the size of the * socket's I/O buffer. * * If it is zero, it means you want to just use the * TCP stack's default value, which is typically * between 8 and 64 kB. * * If you try to set the buffer larger than 64 kB, * the TCP stack should try to use RFC 1323 to * negotiate "TCP Large Windows" which may yield * significant performance gains. */ if (cip->hasSITE_RETRBUFSIZE == kCommandAvailable) (void) FTPCmd(cip, "SITE RETRBUFSIZE %lu", (unsigned long) cip->dataSocketRBufSize); else if (cip->hasSITE_RBUFSIZ == kCommandAvailable) (void) FTPCmd(cip, "SITE RBUFSIZ %lu", (unsigned long) cip->dataSocketRBufSize); else if (cip->hasSITE_RBUFSZ == kCommandAvailable) (void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketRBufSize); else if (cip->hasSITE_BUFSIZE == kCommandAvailable) (void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize); } #ifdef NO_SIGNALS #else /* NO_SIGNALS */ vcip = cip; osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData); gGotBrokenData = 0; gCanBrokenDataJmp = 0; #ifdef HAVE_SIGSETJMP sj = sigsetjmp(gBrokenDataJmp, 1); #else sj = setjmp(gBrokenDataJmp); #endif /* HAVE_SIGSETJMP */ if (sj != 0) { (void) signal(SIGPIPE, (FTPSigProc) osigpipe); FTPShutdownHost(vcip); vcip->errNo = kErrRemoteHostClosedConnection; return(vcip->errNo); } gCanBrokenDataJmp = 1; #endif /* NO_SIGNALS */ tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, startPoint, "RETR %s", file); if (tmpResult < 0) { result = tmpResult; if (result == kErrGeneric) result = kErrRETRFailed; cip->errNo = result; #if !defined(NO_SIGNALS) (void) signal(SIGPIPE, (FTPSigProc) osigpipe); #endif /* NO_SIGNALS */ return (result); } if ((startPoint != 0) && (cip->startPoint == 0)) { /* Remote could not or would not set the start offset * to what we wanted. */ cip->errNo = kErrSetStartPoint; #if !defined(NO_SIGNALS) (void) signal(SIGPIPE, (FTPSigProc) osigpipe); #endif /* NO_SIGNALS */ return (cip->errNo); } FTPInitIOTimer(cip); cip->expectedSize = expectedSize; cip->lname = NULL; /* could be NULL */ cip->rname = file; FTPStartIOTimer(cip); /* Binary */ for (;;) { if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */ cip->errNo = result = kErrDataTimedOut; FTPLogError(cip, kDontPerror, "Remote read timed out.\n"); break; } if (cip->cancelXfer > 0) { FTPAbortDataTransfer(cip); result = cip->errNo = kErrDataTransferAborted; break; } ntoread = numberOfBytesLeftInMemBuf; if (ntoread > cip->bufSize) ntoread = cip->bufSize; /* Break it up into blocks. */ #ifdef NO_SIGNALS nread = (read_return_t) SRead(cip->dataSocket, memBuf, ntoread, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect); if (nread == kTimeoutErr) { cip->errNo = result = kErrDataTimedOut; FTPLogError(cip, kDontPerror, "Remote read timed out.\n"); break; } else if (nread < 0) { if (errno == EPIPE) { result = cip->errNo = kErrSocketReadFailed; errno = EPIPE; FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n"); } else if (errno == EINTR) { continue; } else { FTPLogError(cip, kDoPerror, "Remote read failed.\n"); result = kErrSocketReadFailed; cip->errNo = kErrSocketReadFailed; } break; } else if (nread == 0) { break; } #else gCanBrokenDataJmp = 1; if (cip->xferTimeout > 0) (void) alarm(cip->xferTimeout); nread = read(cip->dataSocket, memBuf, (read_size_t) ntoread); if (nread < 0) { if ((gGotBrokenData != 0) || (errno == EPIPE)) { result = cip->errNo = kErrSocketReadFailed; errno = EPIPE; FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n"); } else if (errno == EINTR) { continue; } else { result = cip->errNo = kErrSocketReadFailed; FTPLogError(cip, kDoPerror, "Remote read failed.\n"); } (void) shutdown(cip->dataSocket, 2); break; } else if (nread == 0) { /* At EOF. */ break; } gCanBrokenDataJmp = 0; #endif /* NO_SIGNALS */ memBuf += nread; if (numberOfBytesWrittenToMemBuf != NULL) *numberOfBytesWrittenToMemBuf += (size_t) nread; cip->bytesTransferred += (longest_int) nread; FTPUpdateIOTimer(cip); if ((size_t) nread > numberOfBytesLeftInMemBuf) { /* assertion failure */ result = cip->errNo = kErrBugInLibrary; break; } numberOfBytesLeftInMemBuf -= nread; if (numberOfBytesLeftInMemBuf == 0) { /* Done (but maybe not at EOF of remote file). */ atEOF = 0; if ((cip->bytesTransferred + startPoint) == expectedSize) atEOF = 1; break; } } #if !defined(NO_SIGNALS) if (cip->xferTimeout > 0) (void) alarm(0); gCanBrokenDataJmp = 0; #endif /* NO_SIGNALS */ /* If there hasn't been an error, and you limited * the number of bytes, we need to abort the * remaining data. */ if ((result == kNoErr) && (atEOF == 0)) { FTPAbortDataTransfer(cip); tmpResult = FTPEndDataCmd(cip, 1); if ((tmpResult < 0) && (result == 0) && (tmpResult != kErrDataTransferFailed)) { result = kErrRETRFailed; cip->errNo = kErrRETRFailed; } } else { tmpResult = FTPEndDataCmd(cip, 1); if ((tmpResult < 0) && (result == 0)) { result = kErrRETRFailed; cip->errNo = kErrRETRFailed; } } FTPStopIOTimer(cip); #if !defined(NO_SIGNALS) (void) signal(SIGPIPE, (FTPSigProc) osigpipe); #endif /* NO_SIGNALS */ if (result == kNoErr) { cip->numDownloads++; if (deleteflag == kDeleteYes) { result = FTPDelete(cip, file, kRecursiveNo, kGlobNo); } } return (result); } /* FTPGetOneF */