reactos/modules/rosapps/applications/net/ncftp/libncftp/rcmd.c

1039 lines
24 KiB
C

/* rcmd.c
*
* Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
* All rights reserved.
*
*/
#include "syshdrs.h"
#if !defined(NO_SIGNALS) && (USE_SIO || !defined(SIGALRM) || !defined(SIGPIPE) || !defined(SIGINT))
# define NO_SIGNALS 1
#endif
#ifndef NO_SIGNALS
#ifdef HAVE_SIGSETJMP
static sigjmp_buf gBrokenCtrlJmp;
#else
static jmp_buf gBrokenCtrlJmp;
#endif /* HAVE_SIGSETJMP */
static void
BrokenCtrl(int UNUSED(signumIgnored))
{
LIBNCFTP_USE_VAR(signumIgnored); /* Shut up gcc */
#ifdef HAVE_SIGSETJMP
siglongjmp(gBrokenCtrlJmp, 1);
#else
longjmp(gBrokenCtrlJmp, 1);
#endif /* HAVE_SIGSETJMP */
} /* BrokenCtrl */
#endif /* NO_SIGNALS */
/* A 'Response' parameter block is simply zeroed to be considered init'ed. */
ResponsePtr
InitResponse(void)
{
ResponsePtr rp;
rp = (ResponsePtr) calloc(SZ(1), sizeof(Response));
if (rp != NULL)
InitLineList(&rp->msg);
return (rp);
} /* InitResponse */
/* If we don't print it to the screen, we may want to save it to our
* trace log.
*/
void
TraceResponse(const FTPCIPtr cip, ResponsePtr rp)
{
LinePtr lp;
if (rp != NULL) {
lp = rp->msg.first;
if (lp != NULL) {
PrintF(cip, "%3d: %s\n", rp->code, lp->line);
for (lp = lp->next; lp != NULL; lp = lp->next)
PrintF(cip, " %s\n", lp->line);
}
}
} /* TraceResponse */
void
PrintResponse(const FTPCIPtr cip, LineListPtr llp)
{
LinePtr lp;
if (llp != NULL) {
for (lp = llp->first; lp != NULL; lp = lp->next)
PrintF(cip, "%s\n", lp->line);
}
} /* PrintResponse */
static void
SaveLastResponse(const FTPCIPtr cip, ResponsePtr rp)
{
if (rp == NULL) {
cip->lastFTPCmdResultStr[0] = '\0';
cip->lastFTPCmdResultNum = -1;
DisposeLineListContents(&cip->lastFTPCmdResultLL);
} else if ((rp->msg.first == NULL) || (rp->msg.first->line == NULL)) {
cip->lastFTPCmdResultStr[0] = '\0';
cip->lastFTPCmdResultNum = rp->code;
DisposeLineListContents(&cip->lastFTPCmdResultLL);
} else {
(void) STRNCPY(cip->lastFTPCmdResultStr, rp->msg.first->line);
cip->lastFTPCmdResultNum = rp->code;
/* Dispose previous command's line list. */
DisposeLineListContents(&cip->lastFTPCmdResultLL);
/* Save this command's line list. */
cip->lastFTPCmdResultLL = rp->msg;
}
} /* SaveLastResponse */
void
DoneWithResponse(const FTPCIPtr cip, ResponsePtr rp)
{
/* Dispose space taken up by the Response, and clear it out
* again. For some reason, I like to return memory to zeroed
* when not in use.
*/
if (rp != NULL) {
TraceResponse(cip, rp);
if (cip->printResponseProc != 0) {
if ((rp->printMode & kResponseNoProc) == 0)
(*cip->printResponseProc)(cip, rp);
}
if ((rp->printMode & kResponseNoSave) == 0)
SaveLastResponse(cip, rp);
else
DisposeLineListContents(&rp->msg);
(void) memset(rp, 0, sizeof(Response));
free(rp);
}
} /* DoneWithResponse */
/* This takes an existing Response and recycles it, by clearing out
* the current contents.
*/
void
ReInitResponse(const FTPCIPtr cip, ResponsePtr rp)
{
if (rp != NULL) {
TraceResponse(cip, rp);
if (cip->printResponseProc != 0) {
if ((rp->printMode & kResponseNoProc) == 0)
(*cip->printResponseProc)(cip, rp);
}
if ((rp->printMode & kResponseNoSave) == 0)
SaveLastResponse(cip, rp);
else
DisposeLineListContents(&rp->msg);
(void) memset(rp, 0, sizeof(Response));
}
} /* ReInitResponse */
#ifndef NO_SIGNALS
/* Since the control stream is defined by the Telnet protocol (RFC 854),
* we follow Telnet rules when reading the control stream. We use this
* routine when we want to read a response from the host.
*/
int
GetTelnetString(const FTPCIPtr cip, char *str, size_t siz, FILE *cin, FILE *cout)
{
int c;
size_t n;
int eofError;
char *cp;
cp = str;
--siz; /* We'll need room for the \0. */
if ((cin == NULL) || (cout == NULL)) {
eofError = -1;
goto done;
}
for (n = (size_t)0, eofError = 0; ; ) {
c = fgetc(cin);
checkChar:
if (c == EOF) {
eof:
eofError = -1;
break;
} else if (c == '\r') {
/* A telnet string can have a CR by itself. But to denote that,
* the protocol uses \r\0; an end of line is denoted \r\n.
*/
c = fgetc(cin);
if (c == '\n') {
/* Had \r\n, so done. */
goto done;
} else if (c == EOF) {
goto eof;
} else if (c == '\0') {
c = '\r';
goto addChar;
} else {
/* Telnet protocol violation! */
goto checkChar;
}
} else if (c == '\n') {
/* Really shouldn't get here. If we do, the other side
* violated the TELNET protocol, since eoln's are CR/LF,
* and not just LF.
*/
PrintF(cip, "TELNET protocol violation: raw LF.\n");
goto done;
} else if (c == IAC) {
/* Since the control connection uses the TELNET protocol,
* we have to handle some of its commands ourselves.
* IAC is the protocol's escape character, meaning that
* the next character after the IAC (Interpret as Command)
* character is a telnet command. But, if there just
* happened to be a character in the text stream with the
* same numerical value of IAC, 255, the sender denotes
* that by having an IAC followed by another IAC.
*/
/* Get the telnet command. */
c = fgetc(cin);
switch (c) {
case WILL:
case WONT:
/* Get the option code. */
c = fgetc(cin);
/* Tell the other side that we don't want
* to do what they're offering to do.
*/
(void) fprintf(cout, "%c%c%c",IAC,DONT,c);
(void) fflush(cout);
break;
case DO:
case DONT:
/* Get the option code. */
c = fgetc(cin);
/* The other side said they are DOing (or not)
* something, which would happen if our side
* asked them to. Since we didn't do that,
* ask them to not do this option.
*/
(void) fprintf(cout, "%c%c%c",IAC,WONT,c);
(void) fflush(cout);
break;
case EOF:
goto eof;
default:
/* Just add this character, since it was most likely
* just an escaped IAC character.
*/
goto addChar;
}
} else {
addChar:
/* If the buffer supplied has room, add this character to it. */
if (n < siz) {
*cp++ = c;
++n;
}
}
}
done:
*cp = '\0';
return (eofError);
} /* GetTelnetString */
#endif /* NO_SIGNALS */
/* Returns 0 if a response was read, or (-1) if an error occurs.
* This reads the entire response text into a LineList, which is kept
* in the 'Response' structure.
*/
int
GetResponse(const FTPCIPtr cip, ResponsePtr rp)
{
longstring str;
int eofError;
str16 code;
char *cp;
int continuation;
volatile FTPCIPtr vcip;
#ifdef NO_SIGNALS
int result;
#else /* NO_SIGNALS */
volatile FTPSigProc osigpipe;
int sj;
#endif /* NO_SIGNALS */
/* RFC 959 states that a reply may span multiple lines. A single
* line message would have the 3-digit code <space> then the msg.
* A multi-line message would have the code <dash> and the first
* line of the msg, then additional lines, until the last line,
* which has the code <space> and last line of the msg.
*
* For example:
* 123-First line
* Second line
* 234 A line beginning with numbers
* 123 The last line
*/
#ifdef NO_SIGNALS
vcip = cip;
#else /* NO_SIGNALS */
osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenCtrl);
vcip = cip;
#ifdef HAVE_SIGSETJMP
sj = sigsetjmp(gBrokenCtrlJmp, 1);
#else
sj = setjmp(gBrokenCtrlJmp);
#endif /* HAVE_SIGSETJMP */
if (sj != 0) {
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
FTPShutdownHost(vcip);
vcip->errNo = kErrRemoteHostClosedConnection;
return(vcip->errNo);
}
#endif /* NO_SIGNALS */
#ifdef NO_SIGNALS
cp = str;
eofError = 0;
if (cip->dataTimedOut > 0) {
/* Give up immediately unless the server had already
* sent a message. Odds are since the data is timed
* out, so is the control.
*/
if (SWaitUntilReadyForReading(cip->ctrlSocketR, 0) == 0) {
/* timeout */
Error(cip, kDontPerror, "Could not read reply from control connection -- timed out.\n");
FTPShutdownHost(vcip);
cip->errNo = kErrControlTimedOut;
return (cip->errNo);
}
}
result = SReadline(&cip->ctrlSrl, str, sizeof(str) - 1);
if (result == kTimeoutErr) {
/* timeout */
Error(cip, kDontPerror, "Could not read reply from control connection -- timed out.\n");
FTPShutdownHost(vcip);
cip->errNo = kErrControlTimedOut;
return (cip->errNo);
} else if (result == 0) {
/* eof */
eofError = 1;
rp->hadEof = 1;
if (rp->eofOkay == 0)
Error(cip, kDontPerror, "Remote host has closed the connection.\n");
FTPShutdownHost(vcip);
cip->errNo = kErrRemoteHostClosedConnection;
return (cip->errNo);
} else if (result < 0) {
/* error */
Error(cip, kDoPerror, "Could not read reply from control connection");
FTPShutdownHost(vcip);
cip->errNo = kErrInvalidReplyFromServer;
return (cip->errNo);
}
if (str[result - 1] == '\n')
str[result - 1] = '\0';
#else /* NO_SIGNALS */
/* Get the first line of the response. */
eofError = GetTelnetString(cip, str, sizeof(str), cip->cin, cip->cout);
cp = str;
if (*cp == '\0') {
if (eofError < 0) {
/* No bytes read for reply, and EOF detected. */
rp->hadEof = 1;
if (rp->eofOkay == 0)
Error(cip, kDontPerror, "Remote host has closed the connection.\n");
FTPShutdownHost(vcip);
cip->errNo = kErrRemoteHostClosedConnection;
(void) signal(SIGPIPE, osigpipe);
return(cip->errNo);
}
}
#endif /* NO_SIGNALS */
if (!isdigit((int) *cp)) {
Error(cip, kDontPerror, "Invalid reply: \"%s\"\n", cp);
cip->errNo = kErrInvalidReplyFromServer;
#ifndef NO_SIGNALS
(void) signal(SIGPIPE, osigpipe);
#endif
return (cip->errNo);
}
rp->codeType = *cp - '0';
cp += 3;
continuation = (*cp == '-');
*cp++ = '\0';
(void) STRNCPY(code, str);
rp->code = atoi(code);
(void) AddLine(&rp->msg, cp);
if (eofError < 0) {
/* Read reply, but EOF was there also. */
rp->hadEof = 1;
}
while (continuation) {
#ifdef NO_SIGNALS
result = SReadline(&cip->ctrlSrl, str, sizeof(str) - 1);
if (result == kTimeoutErr) {
/* timeout */
Error(cip, kDontPerror, "Could not read reply from control connection -- timed out.\n");
FTPShutdownHost(vcip);
cip->errNo = kErrControlTimedOut;
return (cip->errNo);
} else if (result == 0) {
/* eof */
eofError = 1;
rp->hadEof = 1;
if (rp->eofOkay == 0)
Error(cip, kDontPerror, "Remote host has closed the connection.\n");
FTPShutdownHost(vcip);
cip->errNo = kErrRemoteHostClosedConnection;
return (cip->errNo);
} else if (result < 0) {
/* error */
Error(cip, kDoPerror, "Could not read reply from control connection");
FTPShutdownHost(vcip);
cip->errNo = kErrInvalidReplyFromServer;
return (cip->errNo);
}
if (str[result - 1] == '\n')
str[result - 1] = '\0';
#else /* NO_SIGNALS */
eofError = GetTelnetString(cip, str, sizeof(str), cip->cin, cip->cout);
if (eofError < 0) {
/* Read reply, but EOF was there also. */
rp->hadEof = 1;
continuation = 0;
}
#endif /* NO_SIGNALS */
cp = str;
if (strncmp(code, cp, SZ(3)) == 0) {
cp += 3;
if (*cp == ' ')
continuation = 0;
++cp;
}
(void) AddLine(&rp->msg, cp);
}
if (rp->code == 421) {
/*
* 421 Service not available, closing control connection.
* This may be a reply to any command if the service knows it
* must shut down.
*/
if (rp->eofOkay == 0)
Error(cip, kDontPerror, "Remote host has closed the connection.\n");
FTPShutdownHost(vcip);
cip->errNo = kErrRemoteHostClosedConnection;
#ifndef NO_SIGNALS
(void) signal(SIGPIPE, osigpipe);
#endif
return(cip->errNo);
}
#ifndef NO_SIGNALS
(void) signal(SIGPIPE, osigpipe);
#endif
return (kNoErr);
} /* GetResponse */
/* This creates the complete command text to send, and writes it
* on the stream.
*/
#ifdef NO_SIGNALS
static int
SendCommand(const FTPCIPtr cip, const char *cmdspec, va_list ap)
{
longstring command;
int result;
if (cip->ctrlSocketW != kClosedFileDescriptor) {
#ifdef HAVE_VSNPRINTF
(void) vsnprintf(command, sizeof(command) - 1, cmdspec, ap);
command[sizeof(command) - 1] = '\0';
#else
(void) vsprintf(command, cmdspec, ap);
#endif
if ((strncmp(command, "PASS", SZ(4)) != 0) || ((strcmp(cip->user, "anonymous") == 0) && (cip->firewallType == kFirewallNotInUse)))
PrintF(cip, "Cmd: %s\n", command);
else
PrintF(cip, "Cmd: %s\n", "PASS xxxxxxxx");
(void) STRNCAT(command, "\r\n"); /* Use TELNET end-of-line. */
cip->lastFTPCmdResultStr[0] = '\0';
cip->lastFTPCmdResultNum = -1;
result = SWrite(cip->ctrlSocketW, command, strlen(command), (int) cip->ctrlTimeout, 0);
if (result < 0) {
cip->errNo = kErrSocketWriteFailed;
Error(cip, kDoPerror, "Could not write to control stream.\n");
return (cip->errNo);
}
return (kNoErr);
}
return (kErrNotConnected);
} /* SendCommand */
#else /* NO_SIGNALS */
static int
SendCommand(const FTPCIPtr cip, const char *cmdspec, va_list ap)
{
longstring command;
int result;
volatile FTPCIPtr vcip;
volatile FTPSigProc osigpipe;
int sj;
if (cip->cout != NULL) {
#ifdef HAVE_VSNPRINTF
(void) vsnprintf(command, sizeof(command) - 1, cmdspec, ap);
command[sizeof(command) - 1] = '\0';
#else
(void) vsprintf(command, cmdspec, ap);
#endif
if ((strncmp(command, "PASS", SZ(4)) != 0) || ((strcmp(cip->user, "anonymous") == 0) && (cip->firewallType == kFirewallNotInUse)))
PrintF(cip, "Cmd: %s\n", command);
else
PrintF(cip, "Cmd: %s\n", "PASS xxxxxxxx");
(void) STRNCAT(command, "\r\n"); /* Use TELNET end-of-line. */
cip->lastFTPCmdResultStr[0] = '\0';
cip->lastFTPCmdResultNum = -1;
osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenCtrl);
vcip = cip;
#ifdef HAVE_SIGSETJMP
sj = sigsetjmp(gBrokenCtrlJmp, 1);
#else
sj = setjmp(gBrokenCtrlJmp);
#endif /* HAVE_SIGSETJMP */
if (sj != 0) {
(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
FTPShutdownHost(vcip);
if (vcip->eofOkay == 0) {
Error(cip, kDontPerror, "Remote host has closed the connection.\n");
vcip->errNo = kErrRemoteHostClosedConnection;
return(vcip->errNo);
}
return (kNoErr);
}
result = fputs(command, cip->cout);
if (result < 0) {
(void) signal(SIGPIPE, osigpipe);
cip->errNo = kErrSocketWriteFailed;
Error(cip, kDoPerror, "Could not write to control stream.\n");
return (cip->errNo);
}
result = fflush(cip->cout);
if (result < 0) {
(void) signal(SIGPIPE, osigpipe);
cip->errNo = kErrSocketWriteFailed;
Error(cip, kDoPerror, "Could not write to control stream.\n");
return (cip->errNo);
}
(void) signal(SIGPIPE, osigpipe);
return (kNoErr);
}
return (kErrNotConnected);
} /* SendCommand */
#endif /* NO_SIGNALS */
/* For "simple" (i.e. not data transfer) commands, this routine is used
* to send the command and receive one response. It returns the codeType
* field of the 'Response' as the result, or a negative number upon error.
*/
/*VARARGS*/
int
FTPCmd(const FTPCIPtr cip, const char *const cmdspec, ...)
{
va_list ap;
int result;
ResponsePtr rp;
if (cip == NULL)
return (kErrBadParameter);
if (strcmp(cip->magic, kLibraryMagic))
return (kErrBadMagic);
rp = InitResponse();
if (rp == NULL) {
result = kErrMallocFailed;
cip->errNo = kErrMallocFailed;
Error(cip, kDontPerror, "Malloc failed.\n");
return (cip->errNo);
}
va_start(ap, cmdspec);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(cip->ctrlTimeout);
#endif /* NO_SIGNALS */
result = SendCommand(cip, cmdspec, ap);
va_end(ap);
if (result < 0) {
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(0);
#endif /* NO_SIGNALS */
return (result);
}
/* Get the response to the command we sent. */
result = GetResponse(cip, rp);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(0);
#endif /* NO_SIGNALS */
if (result == kNoErr)
result = rp->codeType;
DoneWithResponse(cip, rp);
return (result);
} /* FTPCmd */
/* This is for debugging the library -- don't use. */
/*VARARGS*/
int
FTPCmdNoResponse(const FTPCIPtr cip, const char *const cmdspec, ...)
{
va_list ap;
if (cip == NULL)
return (kErrBadParameter);
if (strcmp(cip->magic, kLibraryMagic))
return (kErrBadMagic);
va_start(ap, cmdspec);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(cip->ctrlTimeout);
#endif /* NO_SIGNALS */
(void) SendCommand(cip, cmdspec, ap);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(0);
#endif /* NO_SIGNALS */
va_end(ap);
return (kNoErr);
} /* FTPCmdNoResponse */
int
WaitResponse(const FTPCIPtr cip, unsigned int sec)
{
int result;
fd_set ss;
struct timeval tv;
int fd;
#ifdef NO_SIGNALS
fd = cip->ctrlSocketR;
#else /* NO_SIGNALS */
if (cip->cin == NULL)
return (-1);
fd = fileno(cip->cin);
#endif /* NO_SIGNALS */
if (fd < 0)
return (-1);
FD_ZERO(&ss);
FD_SET(fd, &ss);
tv.tv_sec = (unsigned long) sec;
tv.tv_usec = 0;
result = select(fd + 1, SELECT_TYPE_ARG234 &ss, NULL, NULL, &tv);
return (result);
} /* WaitResponse */
/* For "simple" (i.e. not data transfer) commands, this routine is used
* to send the command and receive one response. It returns the codeType
* field of the 'Response' as the result, or a negative number upon error.
*/
/*VARARGS*/
int
RCmd(const FTPCIPtr cip, ResponsePtr rp, const char *cmdspec, ...)
{
va_list ap;
int result;
if (cip == NULL)
return (kErrBadParameter);
if (strcmp(cip->magic, kLibraryMagic))
return (kErrBadMagic);
va_start(ap, cmdspec);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(cip->ctrlTimeout);
#endif /* NO_SIGNALS */
result = SendCommand(cip, cmdspec, ap);
va_end(ap);
if (result < 0) {
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(0);
#endif /* NO_SIGNALS */
return (result);
}
/* Get the response to the command we sent. */
result = GetResponse(cip, rp);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(0);
#endif /* NO_SIGNALS */
if (result == kNoErr)
result = rp->codeType;
return (result);
} /* RCmd */
/* Returns -1 if an error occurred, or 0 if not.
* This differs from RCmd, which returns the code class of a response.
*/
/*VARARGS*/
int
FTPStartDataCmd(const FTPCIPtr cip, int netMode, int type, longest_int startPoint, const char *cmdspec, ...)
{
va_list ap;
int result;
int respCode;
ResponsePtr rp;
if (cip == NULL)
return (kErrBadParameter);
if (strcmp(cip->magic, kLibraryMagic))
return (kErrBadMagic);
result = FTPSetTransferType(cip, type);
if (result < 0)
return (result);
/* Re-set the cancellation flag. */
cip->cancelXfer = 0;
/* To transfer data, we do these things in order as specifed by
* the RFC.
*
* First, we tell the other side to set up a data line. This
* is done below by calling OpenDataConnection(), which sets up
* the socket. When we do that, the other side detects a connection
* attempt, so it knows we're there. Then tell the other side
* (by using listen()) that we're willing to receive a connection
* going to our side.
*/
if ((result = OpenDataConnection(cip, cip->dataPortMode)) < 0)
goto done;
/* If asked, attempt to start at a later position in the remote file. */
if (startPoint != (longest_int) 0) {
if ((startPoint == kSizeUnknown) || ((result = SetStartOffset(cip, startPoint)) != 0))
startPoint = (longest_int) 0;
}
cip->startPoint = startPoint;
/* Now we tell the server what we want to do. This sends the
* the type of transfer we want (RETR, STOR, LIST, etc) and the
* parameters for that (files to send, directories to list, etc).
*/
va_start(ap, cmdspec);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(cip->ctrlTimeout);
#endif /* NO_SIGNALS */
result = SendCommand(cip, cmdspec, ap);
va_end(ap);
if (result < 0) {
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(0);
#endif /* NO_SIGNALS */
goto done;
}
/* Get the response to the transfer command we sent, to see if
* they can accomodate the request. If everything went okay,
* we will get a preliminary response saying that the transfer
* initiation was successful and that the data is there for
* reading (for retrieves; for sends, they will be waiting for
* us to send them something).
*/
rp = InitResponse();
if (rp == NULL) {
Error(cip, kDontPerror, "Malloc failed.\n");
cip->errNo = kErrMallocFailed;
result = cip->errNo;
goto done;
}
result = GetResponse(cip, rp);
#ifndef NO_SIGNALS
if (cip->ctrlTimeout > 0)
(void) alarm(0);
#endif /* NO_SIGNALS */
if (result < 0)
goto done;
respCode = rp->codeType;
DoneWithResponse(cip, rp);
if (respCode > 2) {
cip->errNo = kErrCouldNotStartDataTransfer;
result = cip->errNo;
goto done;
}
/* Now we accept the data connection that the other side is offering
* to us. Then we can do the actual I/O on the data we want.
*/
cip->netMode = netMode;
if ((result = AcceptDataConnection(cip)) < 0)
goto done;
return (kNoErr);
done:
(void) FTPEndDataCmd(cip, 0);
return (result);
} /* FTPStartDataCmd */
void
FTPAbortDataTransfer(const FTPCIPtr cip)
{
ResponsePtr rp;
int result;
if (cip->dataSocket != kClosedFileDescriptor) {
PrintF(cip, "Starting abort sequence.\n");
SendTelnetInterrupt(cip); /* Probably could get by w/o doing this. */
result = FTPCmdNoResponse(cip, "ABOR");
if (result != kNoErr) {
/* Linger could cause close to block, so unset it. */
(void) SetLinger(cip, cip->dataSocket, 0);
CloseDataConnection(cip);
PrintF(cip, "Could not send abort command.\n");
return;
}
if (cip->abortTimeout > 0) {
result = WaitResponse(cip, (unsigned int) cip->abortTimeout);
if (result <= 0) {
/* Error or no response received to ABOR in time. */
(void) SetLinger(cip, cip->dataSocket, 0);
CloseDataConnection(cip);
PrintF(cip, "No response received to abort request.\n");
return;
}
}
rp = InitResponse();
if (rp == NULL) {
Error(cip, kDontPerror, "Malloc failed.\n");
cip->errNo = kErrMallocFailed;
result = cip->errNo;
return;
}
result = GetResponse(cip, rp);
if (result < 0) {
/* Shouldn't happen, and doesn't matter if it does. */
(void) SetLinger(cip, cip->dataSocket, 0);
CloseDataConnection(cip);
PrintF(cip, "Invalid response to abort request.\n");
DoneWithResponse(cip, rp);
return;
}
DoneWithResponse(cip, rp);
/* A response to the abort request has been received.
* Now the only thing left to do is close the data
* connection, making sure to turn off linger mode
* since we don't care about straggling data bits.
*/
(void) SetLinger(cip, cip->dataSocket, 0);
CloseDataConnection(cip); /* Must close (by protocol). */
PrintF(cip, "End abort.\n");
}
} /* FTPAbortDataTransfer */
int
FTPEndDataCmd(const FTPCIPtr cip, int didXfer)
{
int result;
int respCode;
ResponsePtr rp;
if (cip == NULL)
return (kErrBadParameter);
if (strcmp(cip->magic, kLibraryMagic))
return (kErrBadMagic);
CloseDataConnection(cip);
result = kNoErr;
if (didXfer) {
/* Get the response to the data transferred. Most likely a message
* saying that the transfer completed succesfully. However, if
* we tried to abort the transfer using ABOR, we will have a response
* to that command instead.
*/
rp = InitResponse();
if (rp == NULL) {
Error(cip, kDontPerror, "Malloc failed.\n");
cip->errNo = kErrMallocFailed;
result = cip->errNo;
return (result);
}
result = GetResponse(cip, rp);
if (result < 0)
return (result);
respCode = rp->codeType;
DoneWithResponse(cip, rp);
if (respCode != 2) {
cip->errNo = kErrDataTransferFailed;
result = cip->errNo;
} else {
result = kNoErr;
}
}
return (result);
} /* FTPEndDataCmd */
int
BufferGets(char *buf, size_t bufsize, int inStream, char *secondaryBuf, char **secBufPtr, char **secBufLimit, size_t secBufSize)
{
int err;
char *src;
char *dst;
char *dstlim;
int len;
int nr;
int haveEof = 0;
err = 0;
dst = buf;
dstlim = dst + bufsize - 1; /* Leave room for NUL. */
src = (*secBufPtr);
for ( ; dst < dstlim; ) {
if (src >= (*secBufLimit)) {
/* Fill the buffer. */
/* Don't need to poll it here. The routines that use BufferGets don't
* need any special processing during timeouts (i.e. progress reports),
* so go ahead and just let it block until there is data to read.
*/
nr = (int) read(inStream, secondaryBuf, secBufSize);
if (nr == 0) {
/* EOF. */
haveEof = 1;
goto done;
} else if (nr < 0) {
/* Error. */
err = -1;
goto done;
}
(*secBufPtr) = secondaryBuf;
(*secBufLimit) = secondaryBuf + nr;
src = (*secBufPtr);
if (nr < (int) secBufSize)
src[nr] = '\0';
}
if (*src == '\r') {
++src;
} else {
if (*src == '\n') {
/* *dst++ = *src++; */ ++src;
goto done;
}
*dst++ = *src++;
}
}
done:
(*secBufPtr) = src;
*dst = '\0';
len = (int) (dst - buf);
if (err < 0)
return (err);
if ((len == 0) && (haveEof == 1))
return (-1);
return (len); /* May be zero, if a blank line. */
} /* BufferGets */
/* eof */