/* Xfer.c */
#include "Sys.h"
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
#define _xfer_c_ 1
#include "Util.h"
#include "Main.h"
#include "Xfer.h"
#include "RCmd.h"
#include "FTP.h"
#include "Progress.h"
#include "Sio.h"
/* Large buffer to hold blocks of data during transferring. */
char *gXferBuf = NULL;
char *gAsciiSendBuf = NULL;
/* Size of the transfer buffer. */
size_t gXferBufSize = kXferBufSize;
/* Stores whether we had an interrupt occur during the transfer. */
int gXferAbortFlag = 0;
char *gSecondaryBufPtr;
char *gSecondaryBufLimit;
int gUsingBufferGets;
jmp_buf gXferTimeoutJmp;
NetReadProc gNetReadProc;
NetWriteProc gNetWriteProc;
extern int gDebug, gMode;
extern int gStdout;
extern int gNetworkTimeout;
void
InitXferBuffer(void)
{
gXferBufSize = (size_t) kXferBufSize;
gXferBuf = malloc(gXferBufSize);
if (gXferBuf == NULL) {
fprintf(stderr, "Not enough memory for transfer buffer.\n");
Exit(kExitOutOfMemory);
}
} /* InitXferBuffer */
int
BufferGets(char *buf, size_t bufsize, XferSpecPtr xp)
{
int err;
char *src;
char *dst;
char *dstlim;
int len;
int nr;
gUsingBufferGets = 1;
err = 0;
dst = buf;
dstlim = dst + bufsize - 1; /* Leave room for NUL. */
src = gSecondaryBufPtr;
for (; dst < dstlim;) {
if (src >= gSecondaryBufLimit) {
/* Fill the buffer. */
nr = (*gNetReadProc) (xp);
if (nr == 0) {
/* EOF. */
goto done;
} else if (nr < 0) {
/* Error. */
err = -1;
goto done;
}
gSecondaryBufPtr = gXferBuf;
gSecondaryBufLimit = gXferBuf + nr;
src = gSecondaryBufPtr;
}
if (*src == '\r') {
++src;
} else {
if (*src == '\n') {
*dst++ = *src++;
goto done;
}
*dst++ = *src++;
}
}
done:
gSecondaryBufPtr = src;
*dst = '\0';
len = (int) (dst - buf);
if (err < 0)
return (err);
return (len);
} /* BufferGets */
/* We get here upon a signal we can handle during transfers. */
void
XferSigHandler(int sigNum)
{
gXferAbortFlag = sigNum;
#if 1
/* Not a good thing to do in general from a signal handler... */
TraceMsg("XferSigHandler: SIG %d.\n", sigNum);
#endif
return;
} /* XferSigHandler */
/* This initializes a transfer information block to zeroes, and
* also initializes the two Response blocks.
*/
XferSpecPtr
InitXferSpec(void)
{
XferSpecPtr xp;
xp = (XferSpecPtr) calloc(SZ(1), sizeof(XferSpec));
if (xp == NULL)
OutOfMemory();
xp->cmdResp = InitResponse();
xp->xferResp = InitResponse();
return (xp);
} /* InitXferSpec */
/* Disposes the transfer information block, and the responses within it. */
void
DoneWithXferSpec(XferSpecPtr xp)
{
DoneWithResponse(xp->cmdResp);
DoneWithResponse(xp->xferResp);
CLEARXFERSPEC(xp);
free(xp);
} /* DoneWithXferSpec */
void
AbortDataTransfer(XferSpecPtr xp)
{
DebugMsg("Start Abort\n");
SendTelnetInterrupt(); /* Probably could get by w/o doing this. */
/* If we aborted too late, and the server already sent the whole thing,
* it will just respond a 226 Transfer completed to our ABOR.
* But if we actually aborted, we'll get a 426 reply instead, then the
* server will send another 226 reply. So if we get a 426 we'll
* print that quick and get rid of it by NULLing it out; RDataCmd()
* will then do its usual GetResponse and get the pending 226.
*
* If we get the 226 here, we don't want RDataCmd() to try and get
* another response. It will check to see if there is already is
* one, and if so, not get a response.
*/
(void) RCmd(xp->xferResp, "ABOR");
if (xp->xferResp->code == 426) {
TraceMsg("(426) Aborted in time.\n");
ReInitResponse(xp->xferResp);
}
CloseDataConnection(1); /* Must close (by protocol). */
DebugMsg("End Abort\n");
} /* AbortDataTransfer */
static void
AbortXfer(XferSpecPtr xp)
{
Sig_t origIntr, origPipe;
/* Don't interrupt while aborting. */
origIntr = SIGNAL(SIGINT, SIG_IGN);
origPipe = SIGNAL(SIGPIPE, SIG_IGN);
/* It's important to make sure that the local file gets it's times
* set correctly, so that reget works like it should.
* When we call AbortDataTransfer, often the server just hangs up.
*/
if ((xp->netMode == kNetReading) && (xp->outStream != gStdout))
SetLocalFileTimes(xp->doUTime, xp->remoteModTime, xp->localFileName);
if (gXferAbortFlag == SIGPIPE) {
if (gDebug)
EPrintF("\r** Broken pipe: Lost data connection **\n");
else
EPrintF("\r** Lost data connection **\n");
} else if ((gXferAbortFlag == SIGINT) || (gXferAbortFlag == 0)) {
EPrintF("\r** Aborting Transfer **\n");
} else {
EPrintF("\r** Aborting Transfer (%d) **\n", gXferAbortFlag);
}
AbortDataTransfer(xp);
(void) SIGNAL(SIGINT, origIntr);
(void) SIGNAL(SIGPIPE, origPipe);
} /* AbortXfer */
void
EndTransfer(XferSpecPtr xp)
{
if (xp->aborted == 1)
AbortXfer(xp);
else if ((gMode == 'B') && (xp->netMode != kNetReading)) {
/* Send EOF block if using Block mode store. */
BlockModeWrite(xp, NULL, 0);
}
gXferAbortFlag = 0;
/* Always call EndProgress, because that does logging too. */
EndProgress(xp);
(void) SIGNAL(SIGINT, xp->origIntr);
(void) SIGNAL(SIGPIPE, xp->origPipe);
} /* EndTransfer */
void
StartTransfer(XferSpecPtr xp)
{
gXferAbortFlag = 0;
errno = 0;
/* In case we happen to use BufferGets, this line sets the buffer pointer
* so that the first thing BufferGets will do is reset and fill the buffer
* using real I/O.
*/
gSecondaryBufPtr = gXferBuf + gXferBufSize;
gUsingBufferGets = 0;
xp->outIsTTY = (xp->outStream < 0) ? 0 : isatty(xp->outStream);
xp->inIsTTY = (xp->inStream < 0) ? 0 : isatty(xp->inStream);
/* Always call StartProgress, because that initializes the logging * stuff too. */
StartProgress(xp);
if (gMode == 'B') {
gNetReadProc = BlockModeRead;
gNetWriteProc = BlockModeWrite;
} else {
gNetReadProc = StreamModeRead;
gNetWriteProc = StreamModeWrite;
}
xp->origIntr = SIGNAL(SIGINT, XferSigHandler);
xp->origPipe = SIGNAL(SIGPIPE, XferSigHandler);
} /* StartTransfer */
int
StdAsciiFileReceive(XferSpecPtr xp)
{
int nread, nwrote;
int fd;
char *xbuf;
char *i, *o;
int ct;
fd = xp->outStream;
xbuf = gXferBuf;
for (;;) {
nread = (*gNetReadProc) (xp);
if (nread <= 0)
break;
/* In ASCII mode, all end-of-lines are denoted by CR/LF.
* For UNIX, we don't want that, we want just LFs, so
* skip all the CR's.
*/
for (i = o = xbuf, ct = 0; ct < nread; ct++, ++i) {
if (*i != '\r')
*o++ = *i;
}
nread = (int) (o - xbuf);
nwrote = Swrite(fd, xbuf, nread, gNetworkTimeout);
if (nwrote <= 0)
break;
}
return (nread); /* 0 or -1 */
} /* StdAsciiFileReceive */
int
StdAsciiFileSend(XferSpecPtr xp)
{
int nread, nwrote;
int fd;
size_t aBufSize;
char *cp1, *cp2, *lim;
/* For ASCII sends, we have a special case where
* we use another buffer. We don't want to write
* single lines at a time on the stream for
* efficiency (especially with block transfer mode).
* We need to assume that the entire block
* we read from the file could be all \n's which is
* why we only read half the maximum size of the
* transfer buffer (because each \n must be
* converted to \r\n).
*/
aBufSize = (gXferBufSize / 2);
if (gAsciiSendBuf == NULL) {
/* Only allocate this on a need basis,
* since ASCII sends are very rare.
*/
gAsciiSendBuf = malloc(aBufSize + 1);
gAsciiSendBuf[aBufSize] = '\0';
if (gAsciiSendBuf == NULL) {
fprintf(stderr, "Not enough memory for ascii send buffer.\n");
Exit(kExitOutOfMemory);
}
}
fd = xp->inStream;
for (;;) {
nread = Sread(fd, gAsciiSendBuf, aBufSize, gNetworkTimeout, 0);
if (nread <= 0) {
if ((nread < 0) && (errno == EINTR))
continue;
break;
}
cp1 = gAsciiSendBuf;
lim = cp1 + nread;
cp2 = gXferBuf;
while (cp1 < lim) {
if (*cp1 == '\n')
*cp2++ = '\r';
*cp2++ = *cp1++;
}
nread = (int) (cp2 - gXferBuf);
nwrote = (*gNetWriteProc) (xp, gXferBuf, nread);
if (nwrote <= 0)
break;
}
return (nread); /* 0 or -1 */
} /* StdAsciiFileSend */
int
StdFileReceive(XferSpecPtr xp)
{
int nread, nwrote;
int fd;
char *xbuf;
xbuf = gXferBuf;
fd = xp->outStream;
/* Special case the most common ones,
* so we don't repeatedly have to evaluate
* the NetProc.
*/
if (gNetReadProc == StreamModeRead) {
for (;;) {
nread = StreamModeRead(xp);
if (nread <= 0)
break;
nwrote = Swrite(fd, xbuf, nread, gNetworkTimeout);
if (nwrote < 0) {
nread = nwrote;
break;
}
}
} else {
for (;;) {
nread = (*gNetReadProc) (xp);
if (nread <= 0)
break;
nwrote = Swrite(fd, xbuf, nread, gNetworkTimeout);
if (nwrote < 0) {
nread = nwrote;
break;
}
}
}
return (nread); /* 0 or -1 */
} /* StdFileReceive */
int
StdFileSend(XferSpecPtr xp)
{
int nread, nwrote;
int fd;
char *xbuf;
size_t xbsize;
xbuf = gXferBuf;
xbsize = gXferBufSize;
fd = xp->inStream;
nwrote = 0;
/* Special case the most common ones,
* so we don't repeatedly have to evaluate
* the NetProc.
*/
if (gNetWriteProc == StreamModeWrite) {
for (;;) {
nread = Sread(fd, xbuf, xbsize, gNetworkTimeout, 0);
if (nread <= 0) {
if ((nread < 0) && (errno == EINTR))
continue;
break;
}
nwrote = StreamModeWrite(xp, xbuf, nread);
if (nwrote < 0)
break;
}
} else {
for (;;) {
nread = Sread(fd, xbuf, xbsize, gNetworkTimeout, 0);
if (nread <= 0) {
if ((nread < 0) && (errno == EINTR))
continue;
break;
}
nwrote = (*gNetWriteProc) (xp, xbuf, nread);
if (nwrote < 0)
break;
}
}
return (nwrote); /* 0 or -1 */
} /* StdFileSend */
int
StreamModeRead(XferSpecPtr xp)
{
int in;
int nRead;
in = xp->inStream;
for (;;) {
if (gXferAbortFlag > 0) {
xp->aborted = 1;
nRead = -1;
goto done;
}
nRead = Sread(in, gXferBuf, gXferBufSize, gNetworkTimeout, 0);
if (nRead <= 0) {
if (nRead == 0)
break; /* EOF. */
if (errno == EINTR) {
if (xp->doReports)
ProgressReport(xp, kOptionalUpdate);
continue;
}
Error(kDoPerror, "Error occurred during read!\n");
xp->aborted = 1;
nRead = -1;
goto done;
}
break;
}
xp->bytesTransferred += nRead;
if (xp->doReports)
ProgressReport(xp, kOptionalUpdate);
done:
return (nRead);
} /* StreamModeRead */
int
StreamModeWrite(XferSpecPtr xp, char *wbuf, int nRead)
{
int out;
int nPut, totalPut;
out = xp->outStream;
for (totalPut = 0;;) {
if (gXferAbortFlag > 0) {
xp->aborted = 1;
totalPut = -1;
goto done;
}
nPut = Swrite(out, wbuf, (size_t) nRead, gNetworkTimeout);
if (nPut < 0) {
if (errno != EINTR) {
Error(kDoPerror, "Error occurred during write!\n");
xp->aborted = 1;
totalPut = -1;
goto done;
}
} else {
totalPut += nPut;
if (nPut == nRead)
break;
/* Short write; just write the rest * of it. */
wbuf += nPut;
nRead -= nPut;
}
if (xp->doReports)
ProgressReport(xp, kOptionalUpdate);
}
xp->bytesTransferred += nRead;
if (xp->doReports)
ProgressReport(xp, kOptionalUpdate);
done:
return (totalPut);
} /* StreamModeWrite */
int
BlockModeRead(XferSpecPtr xp)
{
int in;
int nRead;
size_t bsize;
FTPBlockHeader h;
in = xp->inStream;
if (xp->atEof != 0) {
nRead = 0;
} else for (;;) {
if (gXferAbortFlag > 0) {
xp->aborted = 1;
nRead = -1;
goto done;
}
nRead = Sread(in, (char *) &h, (size_t) 3, gNetworkTimeout, 1);
if (nRead < 3) {
/* Could not read block header. */
Error(kDoPerror, "Error occurred during read!\n");
xp->aborted = 1;
nRead = -1;
goto done;
}
bsize = (h.byteCount[0] << 8) | (h.byteCount[1]);
if (bsize == 0) {
nRead = 0;
if ((h.desc & kLastBlock) != 0) {
xp->atEof = 1;
break; /* EOF, done. */
}
continue; /* Empty block? */
}
if (gXferAbortFlag > 0) {
xp->aborted = 1;
nRead = -1;
goto done;
}
nRead = Sread(in, gXferBuf, bsize, gNetworkTimeout, 1);
if (nRead <= 0) {
Error(kDoPerror, "Error occurred during read!\n");
xp->aborted = 1;
nRead = -1;
goto done;
}
if ((h.desc & kLastBlock) != 0)
xp->atEof = 1;
break;
}
xp->bytesTransferred += nRead;
if (xp->doReports)
ProgressReport(xp, kOptionalUpdate);
done:
return (nRead);
} /* BlockModeRead */
int
BlockModeWrite(XferSpecPtr xp, char *wbuf, int nRead)
{
int out;
unsigned short c;
int nPut;
FTPBlockHeader h;
out = xp->outStream;
if (xp->atEof != 0) {
/* Already sent EOF. */
return (-1);
}
if (gXferAbortFlag > 0) {
xp->aborted = 1;
nPut = -1;
goto done;
}
h.desc = (unsigned char) kRegularBlock;
if (nRead == 0) {
/* Send EOF block, which was just the header. */
h.desc = kLastBlock;
}
c = ((unsigned short) (nRead & 0xffff));
h.byteCount[0] = (unsigned char) ((c >> 8) & 0xff);
h.byteCount[1] = (unsigned char) (c & 0xff);
nPut = Swrite(out, (char *) &h, (size_t) 3, gNetworkTimeout);
if (nPut <= 0) {
Error(kDoPerror, "Error occurred during write!\n");
xp->aborted = 1;
nPut = -1;
goto done;
}
if (nRead == 0) {
/* Already sent EOF block, which was just the header. */
xp->atEof = 1;
return (1); /* Probably should not return zero. */
}
nPut = Swrite(out, (char *) wbuf, (size_t) nRead, gNetworkTimeout);
if (nPut <= 0) {
Error(kDoPerror, "Error occurred during write!\n");
xp->aborted = 1;
nPut = -1;
goto done;
}
xp->bytesTransferred += nRead;
if (xp->doReports)
ProgressReport(xp, kOptionalUpdate);
done:
return (nPut);
} /* BlockModeWrite */
/* eof */
syntax highlighted by Code2HTML, v. 0.9.1