/* You may need this for declarations of fd_set, etc. */
#ifdef SYSSELECTH
# include <sys/select.h>
#else
#ifdef STRICT_PROTOS
#ifndef Select
extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
#endif
#endif
#endif
/* ftp.c globals */
struct sockaddr_in hisctladdr;
struct sockaddr_in data_addr;
int data = -1;
int abrtflag = 0;
struct sockaddr_in myctladdr;
FILE *cin = NULL, *cout = NULL;
char *reply_string = NULL;
jmp_buf sendabort, recvabort;
int progress_meter = dPROGRESS;
int cur_progress_meter;
int sendport = -1; /* use PORT cmd for each data connection */
int code; /* return/reply code for ftp command */
string indataline;
int cpend; /* flag: if != 0, then pending server reply */
char *xferbuf; /* buffer for local and remote I/O */
size_t xferbufsize; /* size in bytes, of the transfer buffer. */
long next_report;
long bytes;
long now_sec;
long file_size;
struct timeval start, stop;
int buffer_only = 0; /* True if reading into redir line
* buffer only (not echoing to
* stdout).
*/
/* This registers the user's username, password, and account with the remote
* host which validates it. If we get on, we also do some other things, like
* enter a log entry and execute the startup macro.
*/
int Login(char *userNamePtr, char *passWordPtr, char *accountPtr, int doInit)
{
string userName;
string str;
int n;
int sentAcct = 0;
int userWasPrompted = 0;
int result = CMDERR;
time_t now;
if (userNamePtr == NULL) {
/* Prompt for a username. */
(void) sprintf(str, "Login Name (%s): ", uinfo.username);
++userWasPrompted;
if (Gets(str, userName, sizeof(userName)) == NULL)
goto done;
else if (userName[0]) {
/* User didn't just hit return. */
userNamePtr = userName;
} else {
/*
* User can hit return if he wants to enter his username
* automatically.
*/
if (*uinfo.username != '\0')
userNamePtr = uinfo.username;
else
goto done;
}
}
/* Send the user name. */
n = command(str);
if (n == CONTINUE) {
if (passWordPtr == NULL) {
if (((strcmp("anonymous", userName) == 0) ||
(strcmp("ftp", userName) == 0)) && (*anon_password != '\0'))
passWordPtr = anon_password;
else {
/* Prompt for a password. */
++userWasPrompted;
passWordPtr = Getpass("Password:");
}
}
/* The remote site is requesting us to send the password now. */
(void) sprintf(str, "PASS %s", passWordPtr);
n = command(str);
if (n == CONTINUE) {
/* The remote site is requesting us to send the account now. */
if (accountPtr == NULL) {
/* Prompt for a username. */
(void) sprintf(str, "ACCT %s", Getpass("Account:"));
++userWasPrompted;
} else {
(void) sprintf(str, "ACCT %s", accountPtr);
}
++sentAcct; /* Keep track that we've sent the account already. */
n = command(str);
}
}
/* If you specified an account, and the remote-host didn't request it
* (maybe it's optional), we will send the account information.
*/
if (!sentAcct && accountPtr != NULL) {
(void) sprintf(str, "ACCT %s", accountPtr);
(void) command(str);
}
/* See if remote host dropped connection. Some sites will let you log
* in anonymously, only to tell you that they already have too many
* anon users, then drop you. We do a no-op here to see if they've
* ditched us.
*/
n = quiet_command("NOOP");
if (n == TRANSIENT)
goto done;
#ifdef SYSLOG
syslog(LOG_INFO, "%s connected to %s as %s.",
uinfo.username, hostname, userNamePtr);
#endif
/* Save which sites we opened to the user's logfile. */
if (logf != NULL) {
(void) time(&now);
(void) fprintf(logf, "%s opened at %s",
hostname,
ctime(&now));
}
/* Let the user know we are logged in, unless he was prompted for some
* information already.
*/
if (!userWasPrompted)
if (NOT_VQUIET)
(void) printf("Logged into %s.\n", hostname);
if ((doInit) && (macnum > 0)) {
/* Run the startup macro, if any. */
/* If macnum is non-zero, the init macro was defined from
* ruserpass. It would be the only macro defined at this
* point.
*/
(void) strcpy(line, "$init");
makeargv();
(void) domacro(margc, margv);
}
/* This stub runs 'CommandWithFlags' above, telling it to wait for
* reply after the command is sent.
*/
int command(char *cmd)
{
return (CommandWithFlags(cmd, WAIT_FOR_REPLY));
} /* command */
/* This stub runs 'CommandWithFlags' above, telling it to NOT wait for
* reply after the command is sent.
*/
int command_noreply(char *cmd)
{
return(CommandWithFlags(cmd, DONT_WAIT_FOR_REPLY));
} /* command */
int quiet_command(char *cmd)
{
register int oldverbose, result;
switch(FileType(local)) {
case IS_FILE:
(void) Strncpy(fullLocal, lcwd);
(void) Strncat(fullLocal, "/");
(void) Strncat(fullLocal, local);
break;
case IS_PIPE:
doLastReport = 0;
local = Strncpy(fullLocal, local);
break;
case IS_STREAM:
default:
doLastReport = 0;
local = Strncpy(fullLocal, receiving ? "stdout" : "stdin");
}
if (doLastReport)
(void) progress_report(1); /* tell progress proc to cleanup. */
tvsub(&td, &stop, &start);
s = td.tv_sec + (td.tv_usec / 1000000.0);
bsstr[0] = '\0';
if (s != 0.0) {
bs = (float)bytes / s;
if (bs > 1024.0)
sprintf(bsstr, "%.2f K/s", bs / 1024.0);
else
sprintf(bsstr, "%.2f Bytes/sec", bs);
}
if (doLastReport) switch(cur_progress_meter) {
case pr_none:
zz:
(void) printf("%s: %ld bytes %s in %.2f seconds, %s.\n",
local, bytes, direction, s, bsstr);
break;
case pr_kbytes:
case pr_percent:
(void) printf("%s%ld bytes %s in %.2f seconds, %s.\n",
cur_progress_meter == pr_kbytes ? "\b\b\b\b\b\b" : "\b\b\b\b",
bytes, direction, s, bsstr);
Echo(stdin, 1);
break;
case pr_philbar:
(void) printf("\n");
Echo(stdin, 1);
goto zz;
case pr_dots:
for (; dots < 10; dots++)
(void) fputc('.', stdout);
(void) fputc('\n', stdout);
Echo(stdin, 1);
goto zz;
}
/* Save transfers to the logfile. */
/* if a simple path is given, try to log the full path */
if (*remote != '/') {
(void) Strncpy(fullRemote, cwd);
(void) Strncat(fullRemote, "/");
(void) Strncat(fullRemote, remote);
} else
(void) Strncpy(fullRemote, remote);
/*
* The ls() function can specify a directory to list along with ls flags,
* if it sends the flags first followed by the directory name.
*
* So far, we don't care about the remote directory being listed. I put
* it now so I won't forget in case I need to do something with it later.
*/
remote_dir[0] = 0;
if (remote != NULL) {
cp = index(remote, LS_FLAGS_AND_FILE);
if (cp == NULL)
(void) Strncpy(remote_dir, remote);
else {
*cp++ = ' ';
(void) Strncpy(remote_dir, cp);
}
}
} /* GetLSRemoteDir */
int AdjustLocalFileName(char *local)
{
char *dir;
/* See if the file exists, and if we can overwrite it. */
if ((access(local, 0) == 0) && (access(local, 2) < 0))
goto noaccess;
/*
* Make sure we are writing to a valid local path.
* First check the local directory, and see if we can write to it.
*/
if (access(local, 2) < 0) {
dir = rindex(local, '/');
if (errno != ENOENT && errno != EACCES) {
/* Report an error if it's one we can't handle. */
PERROR("AdjustLocalFileName", local);
return -1;
}
/* See if we have write permission on this directory. */
if (dir != NULL) {
/* Special case: /filename. */
if (dir != local)
*dir = 0;
if (access(dir == local ? "/" : local, 2) < 0) {
/*
* We have a big long pathname, like /a/b/c/d,
* but see if we can write into the current
* directory and call the file ./d.
*/
if (access(".", 2) < 0) {
(void) strcpy(local, " and .");
goto noaccess;
}
(void) strcpy(local, dir + 1); /* use simple filename. */
} else
*dir = '/';
} else {
/* We have a simple path name (file name only). */
if (access(".", 2) < 0) {
noaccess: PERROR("AdjustLocalFileName", local);
return -1;
}
}
}
return (NOERR);
} /* AdjustLocalFileName */
int SetToAsciiForLS(int is_retr, int currenttype)
{
int oldt = -1, oldv;
if (filetype == IS_STREAM) {
fout = stdout;
} else if (filetype == IS_PIPE) {
/* If it is a pipe, the pipecmd will have a | as the first char. */
++local;
fout = popen(local, "w");
*oldintp = Signal(SIGPIPE, abortrecv);
} else {
fout = fopen(local, mode);
}
if (fout == NULL)
PERROR("OpenOutputFile", local);
return (fout);
} /* OpenOutputFile */
void ReceiveBinary(FILE *din, FILE *fout, int *do_reports, char *localfn)
{
int c, d, do2;
errno = 0; /* Clear any old error left around. */
do2 = *do_reports; /* A slight optimization :-) */
bytes = 0; /* Init the byte-transfer counter. */
for (;;) {
/* Read a block from the input stream. */
c = read(fileno(din), xferbuf, (int)xferbufsize);
/* If c is zero, then we've read the whole file. */
if (c == 0)
break;
/* Check for errors that may have occurred while reading. */
if (c < 0) {
/* Error occurred while reading. */
if (errno != EPIPE)
PERROR("ReceiveBinary", "netin");
bytes = -1;
break;
}
/* Write out the same block we just read in. */
d = write(fileno(fout), xferbuf, c);
/* Check for write errors. */
if ((d < 0) || (ferror(fout))) {
/* Error occurred while writing. */
PERROR("ReceiveBinary", "outfile");
break;
}
if (d < c) {
(void) fprintf(stderr, "%s: short write\n", localfn);
break;
}
GetLSRemoteDir(remote, remote_dir);
if ((filetype = FileType(local)) == IS_FILE) {
if (AdjustLocalFileName(local))
goto xx;
}
file_size = -1;
if (filetype == IS_FILE)
file_size = GetDateAndSize(remote, (unsigned long *) &remfTime);
if (initconn())
goto xx;
oldtype = SetToAsciiForLS(is_retr, curtype);
/* Issue the NLST command but don't wait for the reply. Some FTP
* servers make the data connection before issuing the
* "150 Opening ASCII mode data connection for /bin/ls" reply.
*/
if (IssueCommand(cmd, remote))
goto xx;
if (setjmp(recvabort)) {
#ifdef TRY_ABOR
goto Abort;
#else
/* Just read the rest of the stream without doing anything with
* the results.
*/
(void) Signal(SIGINT, SIG_IGN);
(void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */
while (read(fileno(din), xferbuf, (int)xferbufsize) > 0)
;
(void) fprintf(stderr, "\rAborted. \n");
#endif
} else {
oldintr = Signal(SIGINT, abortrecv);
if (curtype == TYPE_A)
ReceiveAscii(din, fout, &do_reports, local, 1);
else
ReceiveBinary(din, fout, &do_reports, local);
result = NOERR;
/* Don't interrupt us now, since we finished successfully. */
(void) Signal(SIGPIPE, SIG_IGN);
(void) Signal(SIGINT, SIG_IGN);
}
CloseData();
(void) getreply(0);
goto xx;
Abort:
/* Abort using RFC959 recommended IP,SYNC sequence */
(void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */
(void) Signal(SIGINT, SIG_IGN);
if (!cpend || !cout) goto xx;
(void) fprintf(cout,"%c%c",IAC,IP);
(void) fflush(cout);
msg = IAC;
/* send IAC in urgent mode instead of DM because UNIX places oob mark */
/* after urgent byte rather than before as now is protocol */
if (send(fileno(cout),&msg,1,MSG_OOB) != 1)
PERROR("recvrequest", "abort");
(void) fprintf(cout,"%cABOR\r\n",DM);
(void) fflush(cout);
FD_ZERO(&mask);
FD_SET(fileno(cin), &mask);
if (din)
FD_SET(fileno(din), &mask);
if ((nfnd = empty(&mask,10)) <= 0) {
if (nfnd < 0)
PERROR("recvrequest", "abort");
lostpeer(0);
}
if (din && FD_ISSET(fileno(din), &mask)) {
while ((read(fileno(din), xferbuf, xferbufsize)) > 0)
;
}
if ((getreply(0)) == ERROR && code == 552) { /* needed for nic style abort */
CloseData();
(void) getreply(0);
}
(void) getreply(0);
result = -1;
CloseData();
xx:
CloseOutputFile(fout, filetype, local, remfTime);
dbprintf("outfile closed.\n");
if (din)
(void) fclose(din);
if (is_retr)
end_progress("received", local, remote);
if (oldintr)
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
dbprintf("recvrequest result = %d.\n", result);
if (oldtype >= 0)
ResetOldType(oldtype);
bytes = 0L;
return (result);
} /* recvrequest */
/*
* Need to start a listen on the data channel
* before we send the command, otherwise the
* server's connect may fail.
*/
#ifdef TERM_FTP
/*
* Need to request that the server go into passive mode and then get the
* address and port for the term server to connect to.
*/
int initconn(void)
{
int result;
int n[6];
int s;