This is ftp.c in view mode; [Download] [Up]
/* ftp.c */
/* $RCSfile: ftp.c,v $
* $Revision: 14020.12 $
* $Date: 93/07/09 11:30:28 $
*/
#include "sys.h"
#include <sys/types.h>
#include <sys/param.h>
#include <setjmp.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/file.h>
#include <string.h>
#include <time.h>
#ifdef NO_UTIMEH
struct utimbuf {time_t actime; time_t modtime;};
#else
# include <utime.h>
#endif
#ifdef SYSLOG
# include <syslog.h>
#endif
/* You may need this for declarations of fd_set, etc. */
#ifdef SYSSELECTH
# include <sys/select.h>
#else
#ifdef STRICT_PROTOS
#ifndef linux
extern int select (int, void *, void *, void *, struct timeval *);
#endif
#endif
#endif
#ifndef NO_UNISTDH /* for prototypes only. */
# include <unistd.h>
#endif
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <pwd.h>
#include <ctype.h>
#include "util.h"
#include "ftp.h"
#include "cmds.h"
#include "main.h"
#include "ftprc.h"
#include "getpass.h"
#include "defaults.h"
#include "copyright.h"
#ifdef TERM_FTP
extern int compress_toggle;
#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).
*/
/* ftp.c externs */
extern FILE *logf;
extern string cwd, anon_password;
extern Hostname hostname;
extern int verbose, debug, macnum, margc;
extern int curtype, creating;
extern int options, activemcmd, paging;
extern int ansi_escapes, logged_in, macnum;
extern char *line, *margv[];
extern char *tcap_normal, *tcap_boldface;
extern char *tcap_underline, *tcap_reverse;
extern struct userinfo uinfo;
extern struct macel macros[];
extern struct lslist *lshead, *lstail;
extern int is_ls;
#ifdef GATEWAY
extern string gateway;
extern string gate_login;
#endif
#ifdef TERM_FTP
#include "client.h"
extern int lcompression, rcompression;
int compress_toggle = 0;
#endif
#ifdef TERM_FTP
int hookup(char *host, unsigned int port)
{
int s;
lcompression = rcompression = compress_toggle;
if ((s = connect_server(0)) < 0) {
perror("ftp: connect to term server");
return -1;
}
send_command(s, C_PORT, 0, "%s:%d", host, ntohs(port));
send_command(s, C_DUMB, 1, 0);
cin = fdopen(s, "r");
cout = fdopen(s, "w");
if (cin == NULL || cout == NULL) {
fprintf(stderr, "ftp: fdopen failed.\n");
if (cin)
fclose(cin);
if (cout)
fclose(cout);
code = -1;
goto bad;
}
Strncpy(hostname, host);
if (verbose)
printf("Connected to %s.\n", hostname);
if (getreply(0) > 2) { /* read startup message from server */
if (cin)
fclose(cin);
if (cout)
fclose(cout);
code = -1;
goto bad;
}
return 0;
bad:
(void) close(s);
return code;
}
#else
int hookup(char *host, unsigned int port)
{
register struct hostent *hp = 0;
int s, len, hErr = -1;
string errstr;
bzero((char *)&hisctladdr, sizeof (hisctladdr));
#ifdef BAD_INETADDR
hisctladdr.sin_addr = inet_addr(host);
#else
hisctladdr.sin_addr.s_addr = inet_addr(host);
#endif
if (hisctladdr.sin_addr.s_addr != -1) {
hisctladdr.sin_family = AF_INET;
(void) Strncpy(hostname, host);
} else {
hp = gethostbyname(host);
if (hp == NULL) {
#ifdef HERROR
extern int h_errno;
if (h_errno == HOST_NOT_FOUND)
(void) printf("%s: unknown host\n", host);
else (void) fprintf(stderr, "%s: gethostbyname herror (%d): ",
host, h_errno);
herror(NULL);
#else
(void) printf("%s: unknown host\n", host);
#endif
goto done;
}
hisctladdr.sin_family = hp->h_addrtype;
bcopy(hp->h_addr_list[0],
(caddr_t)&hisctladdr.sin_addr, hp->h_length);
(void) Strncpy(hostname, hp->h_name);
}
s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
if (s < 0) {
PERROR("hookup", "socket");
goto done;
}
hisctladdr.sin_port = port;
#ifdef SOCKS
while (Rconnect(s, (struct sockaddr *) &hisctladdr, (int) sizeof (hisctladdr)) < 0) {
#else
while (Connect(s, &hisctladdr, sizeof (hisctladdr)) < 0) {
#endif
if (hp && hp->h_addr_list[1]) {
(void) sprintf(errstr, "connect error to address %s",
inet_ntoa(hisctladdr.sin_addr));
PERROR("hookup", errstr);
hp->h_addr_list++;
bcopy(hp->h_addr_list[0],
(caddr_t)&hisctladdr.sin_addr, hp->h_length);
(void) fprintf(stdout, "Trying %s...\n",
inet_ntoa(hisctladdr.sin_addr));
(void) close(s);
s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
if (s < 0) {
PERROR("hookup", "socket");
goto done;
}
continue;
}
PERROR("hookup", "connect");
switch (errno) {
case ENETDOWN:
case ENETUNREACH:
case ECONNABORTED:
case ETIMEDOUT:
case ECONNREFUSED:
case EHOSTDOWN:
hErr = -2; /* we can re-try later. */
}
goto bad;
}
len = sizeof (myctladdr);
if (Getsockname(s, (char *)&myctladdr, &len) < 0) {
PERROR("hookup", "getsockname");
goto bad;
}
cin = fdopen(s, "r");
cout = fdopen(s, "w");
if (cin == NULL || cout == NULL) {
(void) fprintf(stderr, "ftp: fdopen failed.\n");
close_streams(0);
goto bad;
}
if (IS_VVERBOSE)
(void) printf("Connected to %s.\n", hostname);
if (getreply(0) > 2) { /* read startup message from server */
close_streams(0);
if (code == 421)
hErr = -2; /* We can try again later. */
goto bad;
}
#ifdef SO_OOBINLINE
{
int on = 1;
if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on))
< 0 && debug) {
PERROR("hookup", "setsockopt");
}
}
#endif /* SO_OOBINLINE */
hErr = 0;
goto done;
bad:
(void) close(s);
done:
return (hErr);
} /* hookup */
#endif
/* 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;
}
}
if (passWordPtr == NULL) {
if ((strcmp("anonymous", userName) == 0) && (*anon_password))
passWordPtr = anon_password;
else {
/* Prompt for a password. */
++userWasPrompted;
passWordPtr = Getpass("Password:");
}
}
#ifdef GATEWAY
if (*gateway)
(void) sprintf(str, "USER %s@%s",
(*gate_login ? gate_login : dGATEWAY_LOGIN),
hostname);
else
#endif
(void) sprintf(str, "USER %s", userNamePtr);
/* Send the user name. */
n = command(str);
if (n == CONTINUE) {
/* 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 (n != COMPLETE) {
(void) printf("Login failed.\n");
goto done;
}
/* 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 == 4)
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);
}
_cd(NULL); /* Init cwd variable. */
result = NOERR;
logged_in = 1;
done:
return (result);
} /* Login */
/*ARGSUSED*/
void cmdabort SIG_PARAMS
{
(void) printf("\n");
(void) fflush(stdout);
abrtflag++;
} /* cmdabort */
int CommandWithFlags(char *cmd, int flags)
{
int r;
Sig_t oldintr;
string str;
abrtflag = 0;
dbprintf("cmd: \"%s\" (length %d)\n", cmd, (int) strlen(cmd));
if (cout == NULL) {
(void) sprintf(str, "%s: No control connection for command", cmd);
PERROR("command", str);
return (0);
}
oldintr = Signal(SIGINT, /* cmdabort */ SIG_IGN);
#ifndef BROKEN_MEMCPY
if (cout != NULL)
(void) fprintf(cout, "%s\r\n", cmd);
#else
{
/*
* The fprintf() above gives me a core-dump in memcpy()...
* This does the trick though...
*/
char *p = cmd;
while (*p)
fputc(*p++, cout);
fputc('\r', cout);
fputc('\n', cout);
}
#endif /* !SCO324 */
(void) fflush(cout);
cpend = 1;
r = (flags == WAIT_FOR_REPLY) ? getreply(strcmp(cmd, "QUIT") == 0) : 0;
if (abrtflag && oldintr != SIG_IGN && oldintr != NULL)
(*oldintr)(0);
(void) Signal(SIGINT, oldintr);
return(r);
} /* CommandWithFlags */
/* 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;
oldverbose = verbose;
verbose = V_QUIET;
result = command(cmd);
verbose = oldverbose;
return (result);
} /* quiet_command */
int verbose_command(char *cmd)
{
register int oldverbose, result;
oldverbose = verbose;
verbose = V_VERBOSE;
result = command(cmd);
verbose = oldverbose;
return (result);
} /* quiet_command */
int getreply(int expecteof)
{
register int c, n;
int dig;
char *cp, *end, *dp;
int thiscode, originalcode = 0, continuation = 0;
Sig_t oldintr;
if (cin == NULL)
return (-1);
/* oldintr = Signal(SIGINT, SIG_IGN); */
oldintr = Signal(SIGINT, cmdabort);
end = reply_string + RECEIVEDLINELEN - 2;
for (;abrtflag==0;) {
dig = n = thiscode = code = 0;
cp = reply_string;
for (;abrtflag==0;) {
c = fgetc(cin);
if (c == IAC) { /* handle telnet commands */
switch (c = fgetc(cin)) {
case WILL:
case WONT:
c = fgetc(cin);
(void) fprintf(cout, "%c%c%c",IAC,DONT,c);
(void) fflush(cout);
break;
case DO:
case DONT:
c = fgetc(cin);
(void) fprintf(cout, "%c%c%c",IAC,WONT,c);
(void) fflush(cout);
break;
default:
break;
}
continue;
}
dig++;
if (c == EOF) {
if (expecteof) {
(void) Signal(SIGINT, oldintr);
code = 221;
return (0);
}
lostpeer(0);
if (NOT_VQUIET) {
(void) printf("421 Service not available, remote server has closed connection\n");
(void) fflush(stdout);
}
code = 421;
return(4);
}
if (cp < end && c != '\r')
*cp++ = c;
if (c == '\n')
break;
if (dig < 4 && isdigit(c))
code = thiscode = code * 10 + (c - '0');
else if (dig == 4 && c == '-') {
if (continuation)
code = 0;
continuation++;
}
if (n == 0)
n = c;
} /* end for(;;) #2 */
*cp = '\0';
switch (verbose) {
case V_QUIET:
/* Don't print anything. */
break;
case V_ERRS:
if (n == '5') {
dp = reply_string;
goto stripCode;
}
break;
case V_IMPLICITCD:
case V_TERSE:
dp = NULL;
if (n == '5' && verbose == V_TERSE)
dp = reply_string;
else {
switch (thiscode) {
case 230:
case 214:
case 332:
case 421: /* For ftp.apple.com, etc. */
dp = reply_string;
break;
case 220:
/*
* Skip the foo FTP server ready line.
*/
if (strstr(reply_string, "ready.") == NULL)
dp = reply_string;
break;
case 250:
/*
* Print 250 lines if they aren't
* "250 CWD command successful."
*/
if (strncmp(reply_string + 4, "CWD ", (size_t) 4))
dp = reply_string;
}
}
if (dp == NULL) break;
stripCode:
/* Try to strip out the code numbers, etc. */
if (isdigit(*dp++) && isdigit(*dp++) && isdigit(*dp++)) {
if (*dp == ' ' || *dp == '-') {
dp++;
if (*dp == ' ') dp++;
} else dp = reply_string;
} else {
int spaces;
dp = reply_string;
for (spaces = 0; spaces < 4; ++spaces)
if (dp[spaces] != ' ')
break;
if (spaces == 4)
dp += spaces;
}
goto printLine;
case V_VERBOSE:
dp = reply_string;
printLine: (void) fputs(dp, stdout);
} /* end switch */
if (continuation && code != originalcode) {
if (originalcode == 0)
originalcode = code;
continue;
}
if (n != '1')
cpend = 0;
(void) Signal(SIGINT,oldintr);
if (code == 421 || originalcode == 421)
lostpeer(0);
if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN && oldintr)
(*oldintr)(0);
break;
} /* end for(;;) #1 */
return (n - '0');
} /* getreply */
static int empty(struct fd_set *mask, int sec)
{
struct timeval t;
t.tv_sec = (long) sec;
t.tv_usec = 0;
return(Select(32, mask, NULL, NULL, &t));
} /* empty */
static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
{
tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
if (tdiff->tv_usec < 0)
tdiff->tv_sec--, tdiff->tv_usec += 1000000;
} /* tvsub */
/* Variables private to progress_report code. */
static int barlen;
static long last_dot;
static int dots;
int start_progress(int sending, char *local)
{
long s;
str32 spec;
cur_progress_meter = progress_meter;
if ((cur_progress_meter > pr_last) || (cur_progress_meter < 0))
cur_progress_meter = dPROGRESS;
if ((file_size <= 0) && ((cur_progress_meter == pr_percent) || (cur_progress_meter == pr_philbar) || (cur_progress_meter == pr_last)))
cur_progress_meter = pr_kbytes;
if (!ansi_escapes && (cur_progress_meter == pr_philbar))
cur_progress_meter = pr_dots;
(void) Gettimeofday(&start);
now_sec = start.tv_sec;
switch (cur_progress_meter) {
case pr_none:
break;
case pr_percent:
(void) printf("%s: ", local);
goto zz;
case pr_kbytes:
(void) printf("%s: ", local);
goto zz;
case pr_philbar:
(void) printf("%s%s file: %s %s\n",
tcap_boldface,
sending ? "Sending" : "Receiving",
local,
tcap_normal
);
barlen = 63;
for (s = file_size; s > 0; s /= 10L) barlen--;
(void) sprintf(spec, " 0 %%%ds %%ld bytes.\r", barlen);
(void) printf(spec, " ", file_size);
goto zz;
case pr_dots:
last_dot = (file_size / 10) + 1;
dots = 0;
(void) printf("%s: ", local);
zz:
(void) fflush(stdout);
echo(stdin, 0);
} /* end switch */
return (cur_progress_meter);
} /* start_progress */
int progress_report(int finish_up)
{
int size;
long perc;
str32 spec;
next_report += xferbufsize;
(void) Gettimeofday(&stop);
if ((stop.tv_sec > now_sec) || finish_up && file_size) {
switch (cur_progress_meter) {
case pr_none:
break;
case pr_percent:
perc = 100L * bytes / file_size;
if (perc > 100L) perc = 100L;
(void) printf("\b\b\b\b%3ld%%", perc);
(void) fflush(stdout);
break;
case pr_philbar:
size = (int) ((float)barlen * ((float) (bytes > file_size ?
file_size : bytes)/file_size));
(void) sprintf(spec, "%%3ld%%%% 0 %%s%%%ds%%s\r", size);
(void) printf(
spec,
100L * (bytes > file_size ? file_size : bytes) / file_size,
tcap_reverse,
" ",
tcap_normal
);
(void) fflush(stdout);
break;
case pr_kbytes:
if ((bytes / 1024) > 0) {
(void) printf("\b\b\b\b\b\b%5ldK", bytes / 1024);
(void) fflush(stdout);
}
break;
case pr_dots:
if (bytes > last_dot) {
(void) fputc('.', stdout);
(void) fflush(stdout);
last_dot += (file_size / 10) + 1;
dots++;
}
} /* end switch */
now_sec = stop.tv_sec;
} /* end if we updated */
return (UserLoggedIn());
} /* progress_report */
void end_progress(char *direction, char *local, char *remote)
{
struct timeval td;
float s, bs = 0.0;
char *cp, *bsstr;
string str;
if (bytes <= 0)
return;
(void) progress_report(1); /* tell progress proc to cleanup. */
tvsub(&td, &stop, &start);
s = td.tv_sec + (td.tv_usec / 1000000.0);
if (s != 0.0)
bs = bytes / s;
if (bs > 1024.0) {
bs /= 1024.0;
bsstr = "K/s.\n";
} else
bsstr = "Bytes/s.\n";
if (NOT_VQUIET) switch(cur_progress_meter) {
case pr_none:
zz:
(void) printf("%s: %ld bytes %s in %.2f seconds, %.2f %s", local, bytes, direction, s, bs, bsstr);
break;
case pr_kbytes:
case pr_percent:
(void) printf("%s%ld bytes %s in %.2f seconds, %.2f %s",
cur_progress_meter == pr_kbytes ? "\b\b\b\b\b\b" : "\b\b\b\b",
bytes, direction, s, bs, bsstr);
echo(stdin, 1);
break;
case pr_philbar:
(void) printf("\n");
echo(stdin, 1);
break;
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 (rindex(remote, '/') == NULL && cwd != NULL) {
(void) sprintf(str, "%s/%s", cwd, remote);
cp = str;
} else
cp = remote;
if (logf != NULL) {
(void) fprintf(logf, "\t-> \"%s\" %s, %.2f %s", cp, direction, bs, bsstr);
}
#ifdef SYSLOG
if (direction[0] == 'r')
syslog (LOG_INFO, "%s received %s as %s from %s (%ld bytes).",
uinfo.username, cp, local, hostname, bytes);
else
syslog (LOG_INFO, "%s sent %s as %s to %s (%ld bytes).",
uinfo.username, local, cp, hostname, bytes);
#endif
} /* end_progress */
void close_file(FILE **fin, int filetype)
{
if (*fin != NULL) {
if (filetype == IS_FILE) {
(void) fclose(*fin);
*fin = NULL;
} else if (filetype == IS_PIPE) {
(void) pclose(*fin);
*fin = NULL;
}
}
} /* close_file */
/*ARGSUSED*/
void abortsend SIG_PARAMS
{
activemcmd = 0;
abrtflag = 0;
(void) fprintf(stderr, "\nSend aborted.\n");
echo(stdin, 1);
longjmp(sendabort, 1);
} /* abortsend */
int sendrequest(char *cmd, char *local, char *remote)
{
FILE *fin, *dout = NULL;
Sig_t oldintr, oldintp;
string str;
register int c, d;
struct stat st;
int filetype, result = NOERR;
int do_reports = 0;
char *mode;
register char *bufp;
dbprintf("cmd: %s; rmt: %s; loc: %s.\n", cmd, remote, local);
oldintr = NULL;
oldintp = NULL;
mode = "w";
bytes = file_size = 0L;
if (setjmp(sendabort)) {
while (cpend) {
(void) getreply(0);
}
if (data >= 0) {
(void) close(data);
data = -1;
}
if (oldintr)
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
result = -1;
goto xx;
}
oldintr = Signal(SIGINT, abortsend);
file_size = -1;
if (strcmp(local, "-") == 0) {
fin = stdin;
filetype = IS_STREAM;
} else if (*local == '|') {
filetype = IS_PIPE;
oldintp = Signal(SIGPIPE,SIG_IGN);
fin = popen(local + 1, "r");
if (fin == NULL) {
PERROR("sendrequest", local + 1);
(void) Signal(SIGINT, oldintr);
(void) Signal(SIGPIPE, oldintp);
result = -1;
goto xx;
}
} else {
filetype = IS_FILE;
fin = fopen(local, "r");
if (fin == NULL) {
PERROR("sendrequest", local);
(void) Signal(SIGINT, oldintr);
result = -1;
goto xx;
}
if (fstat(fileno(fin), &st) < 0 ||
(st.st_mode&S_IFMT) != S_IFREG) {
(void) fprintf(stdout, "%s: not a plain file.\n", local);
(void) Signal(SIGINT, oldintr);
(void) fclose(fin);
result = -1;
goto xx;
}
file_size = st.st_size;
}
if (initconn()) {
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
result = -1;
close_file(&fin, filetype);
goto xx;
}
if (setjmp(sendabort))
goto Abort;
#ifdef TRY_NOREPLY
if (remote) {
(void) sprintf(str, "%s %s", cmd, remote);
(void) command_noreply(str);
} else {
(void) command_noreply(cmd);
}
dout = dataconn(mode);
if (dout == NULL)
goto Abort;
if(getreply(0) != PRELIM) {
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
close_file(&fin, filetype);
return -1;
}
#else
if (remote) {
(void) sprintf(str, "%s %s", cmd, remote);
if (command(str) != PRELIM) {
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
close_file(&fin, filetype);
goto xx;
}
} else {
if (command(cmd) != PRELIM) {
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
close_file(&fin, filetype);
goto xx;
}
}
dout = dataconn(mode);
if (dout == NULL)
goto Abort;
#endif
(void) Gettimeofday(&start);
oldintp = Signal(SIGPIPE, SIG_IGN);
if (do_reports = (filetype == IS_FILE && NOT_VQUIET))
do_reports = start_progress(1, local);
switch (curtype) {
case TYPE_I:
case TYPE_L:
errno = d = 0;
while ((c = read(fileno(fin), xferbuf, (int)xferbufsize)) > 0) {
bytes += c;
for (bufp = xferbuf; c > 0; c -= d, bufp += d)
if ((d = write(fileno(dout), bufp, c)) <= 0)
break;
/* Print progress indicator. */
if (do_reports)
do_reports = progress_report(0);
}
if (c < 0)
PERROR("sendrequest", local);
if (d <= 0) {
if (d == 0 && !creating)
(void) fprintf(stderr, "netout: write returned 0?\n");
else if (errno != EPIPE)
PERROR("sendrequest", "netout");
bytes = -1;
}
break;
case TYPE_A:
next_report = xferbufsize;
while ((c = getc(fin)) != EOF) {
if (c == '\n') {
if (ferror(dout))
break;
(void) putc('\r', dout);
bytes++;
}
(void) putc(c, dout);
bytes++;
/* Print progress indicator. */
if (do_reports && bytes > next_report)
do_reports = progress_report(0);
}
if (ferror(fin))
PERROR("sendrequest", local);
if (ferror(dout)) {
if (errno != EPIPE)
PERROR("sendrequest", "netout");
bytes = -1;
}
break;
}
Done:
close_file(&fin, filetype);
if (dout)
(void) fclose(dout);
(void) getreply(0);
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
if (do_reports)
end_progress("sent", local, remote);
xx:
return (result);
Abort:
result = -1;
if (!cpend)
goto xx;
if (data >= 0) {
(void) close(data);
data = -1;
}
goto Done;
} /* sendrequest */
/*ARGSUSED*/
void abortrecv SIG_PARAMS
{
activemcmd = 0;
abrtflag = 0;
(void) fprintf(stderr,
#ifdef TRY_ABOR
"(abort)\n");
#else
"\nAborting, please wait...");
#endif
(void) fflush(stderr);
echo(stdin, 1);
longjmp(recvabort, 1);
} /* abortrecv */
void GetLSRemoteDir(char *remote, char *remote_dir)
{
char *cp;
/*
* 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;
/*
* 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 (!is_retr) {
if (currenttype != TYPE_A) {
oldt = currenttype;
oldv = verbose;
if (!debug)
verbose = V_QUIET;
(void) setascii(0, NULL);
verbose = oldv;
}
}
return oldt;
} /* SetToAsciiForLS */
int IssueCommand(char *ftpcmd, char *remote)
{
string str;
int result = NOERR;
if (remote)
(void) sprintf(str, "%s %s", ftpcmd, remote);
else
(void) Strncpy(str, ftpcmd);
#ifdef TRY_NOREPLY
if (command_noreply(str) != PRELIM)
#else
if (command(str) != PRELIM)
#endif
result = -1;
return (result);
} /* IssueCommand */
FILE *OpenOutputFile(int filetype, char *local, char *mode, Sig_t *oldintp)
{
FILE *fout;
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;
}
/* Update the byte counter. */
bytes += (long) c;
/* Print progress indicator. */
if (do2 != 0)
do2 = progress_report(0);
}
*do_reports = do2; /* Update the real do_reports variable. */
} /* ReceiveBinary */
void AddRedirLine(char *str2)
{
register struct lslist *new;
(void) Strncpy(indataline, str2);
new = (struct lslist *) malloc((size_t) sizeof(struct lslist));
if (new != NULL) {
if ((new->string = NewString(str2)) != NULL) {
new->next = NULL;
if (lshead == NULL)
lshead = lstail = new;
else {
lstail->next = new;
lstail = new;
}
}
}
} /* AddRedirLine */
void ReceiveAscii(FILE *din, FILE *fout, int *do_reports, char *localfn, int
lineMode)
{
string str2;
int nchars = 0, c;
char *linePtr;
int do2 = *do_reports, stripped;
next_report = xferbufsize;
bytes = errno = 0;
if (lineMode) {
while ((linePtr = FGets(str2, din)) != NULL) {
bytes += (long) RemoveTrailingNewline(linePtr, &stripped);
if (is_ls || debug > 0)
AddRedirLine(linePtr);
/* Shutup while getting remote size and mod time. */
if (!buffer_only) {
c = fputs(linePtr, fout);
if (c != EOF) {
if (stripped > 0)
c = fputc('\n', fout);
}
if ((c == EOF) || (ferror(fout))) {
PERROR("ReceiveAscii", "outfile");
break;
}
}
/* Print progress indicator. */
if (do2 && bytes > next_report)
do2 = progress_report(0);
}
} else while ((c = getc(din)) != EOF) {
linePtr = str2;
while (c == '\r') {
bytes++;
if ((c = getc(din)) != '\n') {
if (ferror(fout))
goto break2;
/* Shutup while getting remote size and mod time. */
if (!buffer_only)
(void) putc('\r', fout);
if (c == '\0') {
bytes++;
goto contin2;
}
if (c == EOF)
goto contin2;
}
}
/* Shutup while getting remote size and mod time. */
if (!buffer_only)
(void) putc(c, fout);
bytes++;
/* Print progress indicator. */
if (do2 && bytes > next_report)
do2 = progress_report(0);
/* No seg violations, please */
if (nchars < sizeof(str2) - 1) {
*linePtr++ = c; /* build redir string */
nchars++;
}
contin2:
/* Save the input line in the buffer for recall later. */
if (c == '\n' && is_ls) {
*--linePtr = 0;
AddRedirLine(str2);
nchars = 0;
}
} /* while ((c = getc(din)) != EOF) */
break2:
if (ferror(din)) {
if (errno != EPIPE)
PERROR("ReceiveAscii", "netin");
bytes = -1;
}
if (ferror(fout)) {
if (errno != EPIPE)
PERROR("ReceiveAscii", localfn);
}
*do_reports = do2;
} /* ReceiveAscii */
void CloseOutputFile(FILE *f, int filetype, char *name, time_t mt)
{
struct utimbuf ut;
if (f != NULL) {
(void) fflush(f);
if (filetype == IS_FILE) {
(void) fclose(f);
if (mt != (time_t)0) {
ut.actime = ut.modtime = mt;
(void) utime(name, &ut);
}
} else if (filetype == IS_PIPE) {
(void)pclose(f);
}
}
} /* close_file */
void ResetOldType(int oldtype)
{
int oldv;
if (oldtype >= 0) {
oldv = verbose;
if (!debug)
verbose = V_QUIET;
(void) SetTypeByNumber(oldtype);
verbose = oldv;
}
} /* ResetOldType */
int FileType(char *fname)
{
int ft = IS_FILE;
if (strcmp(fname, "-") == 0)
ft = IS_STREAM;
else if (*fname == '|')
ft = IS_PIPE;
return (ft);
} /* FileType */
void CloseData(void) {
if (data >= 0) {
(void) close(data);
data = -1;
}
} /* CloseData */
int recvrequest(char *cmd, char *local, char *remote, char *mode)
{
FILE *fout = NULL, *din = NULL;
Sig_t oldintr = NULL, oldintp = NULL;
int oldtype = 0, is_retr;
int nfnd;
char msg;
struct fd_set mask;
int filetype, do_reports = 0;
string remote_dir;
time_t remfTime = 0;
int result = -1;
dbprintf("---> cmd: %s; rmt: %s; loc: %s; mode: %s.\n",
cmd, remote, local, mode);
is_retr = strcmp(cmd, "RETR") == 0;
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 ((fout = OpenOutputFile(filetype, local, mode, &oldintp)) == NULL)
goto xx;
if ((din = dataconn("r")) == NULL)
goto Abort;
#ifdef TRY_NOREPLY
/* Now get the reply we skipped above. */
(void) getreply(0);
#endif
do_reports = NOT_VQUIET && is_retr && filetype == IS_FILE;
if (do_reports)
do_reports = start_progress(0, local);
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 (do_reports)
end_progress("received", local, remote);
if (oldintr)
(void) Signal(SIGINT, oldintr);
if (oldintp)
(void) Signal(SIGPIPE, oldintp);
dbprintf("recvrequest result = %d.\n", result);
ResetOldType(oldtype);
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;
if (data != -1)
(void) close(data);
result = command("PASV");
if (result == COMPLETE) {
if (sscanf(reply_string, "%*[^(](%d,%d,%d,%d,%d,%d)",
&n[0], &n[1], &n[2], &n[3], &n[4], &n[5]) != 6) {
printf("Cannot parse PASV response: %s\n",
reply_string);
return 1;
}
close(data);
lcompression = rcompression = compress_toggle;
if ((s = connect_server(0)) < 0) {
perror("ftp: connect to term server");
data = -1;
return 1;
}
data = s;
send_command(s, C_PORT, 0, "%d.%d.%d.%d:%d",
n[0], n[1], n[2], n[3], 256*n[4] + n[5]);
send_command(s, C_COMPRESS, 1, "n");
send_command(s, C_DUMB, 1, 0);
return 0;
}
return 1;
}
FILE *
dataconn(lmode)
char *lmode;
{
return (fdopen(data, lmode));
}
#else /* TERM_FTP */
int initconn(void)
{
register char *p, *a;
int result, len, tmpno = 0;
int on = 1, rval;
string str;
Sig_t oldintr;
oldintr = Signal(SIGINT, SIG_IGN);
noport:
data_addr = myctladdr;
if (sendport)
data_addr.sin_port = 0; /* let system pick one */
if (data != -1)
(void) close (data);
data = socket(AF_INET, SOCK_STREAM, 0);
if (data < 0) {
PERROR("initconn", "socket");
if (tmpno)
sendport = 1;
rval = 1; goto Return;
}
if (!sendport)
if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
PERROR("initconn", "setsockopt (reuse address)");
goto bad;
}
#ifdef SOCKS
if (Rbind(data, (struct sockaddr *)&data_addr, sizeof (data_addr), hisctladdr.sin_addr.s_addr) < 0) {
#else
if (Bind(data, &data_addr, sizeof (data_addr)) < 0) {
#endif
PERROR("initconn", "bind");
goto bad;
}
if (options & SO_DEBUG &&
setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
PERROR("initconn", "setsockopt (ignored)");
len = sizeof (data_addr);
if (Getsockname(data, (char *)&data_addr, &len) < 0) {
PERROR("initconn", "getsockname");
goto bad;
}
#ifdef SOCKS
if (Rlisten(data, 1) < 0)
#else
if (listen(data, 1) < 0)
#endif
PERROR("initconn", "listen");
if (sendport) {
a = (char *)&data_addr.sin_addr;
p = (char *)&data_addr.sin_port;
#define UC(x) (int) (((int) x) & 0xff)
(void) sprintf(str, "PORT %d,%d,%d,%d,%d,%d",
UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
result = command(str);
if (result == ERROR && sendport == -1) {
sendport = 0;
tmpno = 1;
goto noport;
}
rval = (result != COMPLETE); goto Return;
}
if (tmpno)
sendport = 1;
rval = 0; goto Return;
bad:
(void) close(data), data = -1;
if (tmpno)
sendport = 1;
rval = 1;
Return:
(void) Signal(SIGINT, oldintr);
return (rval);
} /* initconn */
FILE *
dataconn(char *mode)
{
struct sockaddr_in from;
FILE *fp;
int s, fromlen = sizeof (from);
#ifdef SOCKS
s = Raccept(data, (struct sockaddr *) &from, &fromlen);
#else
s = Accept(data, &from, &fromlen);
#endif
if (s < 0) {
PERROR("dataconn", "accept");
(void) close(data), data = -1;
fp = NULL;
} else {
(void) close(data);
data = s;
fp = fdopen(data, mode);
}
return (fp);
} /* dataconn */
#endif
/* eof ftp.c */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.