+++ /dev/null
-/* 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 */