📄 rcmd.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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -