File:  [Power 6/32 Unix Tahoe 4.2BSD] / cci / usr / src / ucb / ftp / ftp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Sun Jul 28 12:29:28 2019 UTC (7 years ago) by root
Branches: bsd, MAIN
CVS tags: v121, HEAD
Power 6/32 Unix version 1.21

#ifndef lint
static char sccsid[] = "@(#)ftp.c	4.11 (Berkeley) 7/26/83";
#endif

#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>

#include <netinet/in.h>
#include <arpa/ftp.h>

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>

#include "ftp_var.h"

struct	sockaddr_in hisctladdr;
struct	sockaddr_in data_addr;
int	data = -1;
int	connected;
struct	sockaddr_in myctladdr;

FILE	*cin, *cout;
FILE	*dataconn();


/*
	Changes made to this file :

	(1) recvrequest() :
		a. Move the logic to check if the local file is successfully
		   opened before sending the RETR cmd to remote server. Just 
		   to make sure user ftp is ready to accept data from server
		   before the RETR cmd is sent.
		b. Add logic to report actual no of bytes received from server
		   and actual size of new file. They are different because ftp
		   user program strips '\n' characters.
		c. Fix for 1st char. lost when print out error message to 
		   indicate local cannot be opened.
	
		d. If you try to retrieve something into a local file, and you
		   don't have the write permission on the file, it should test
		   whether you have write permission on the directory contain-
		   ing the file.  But, previously, if the local file is a path
		   name containing slashes, it does a test on a null pathname
		   rather than on the containing directory. This is wrong.

	(2) empty():	Peter P. 07/02/85
		@  removed because no routine uses it. Peter P. 07/02/85

	(3) getreply():	Peter P. 07/02/85
		a. Prevent the reply getter to get more replies than necessary.
		   only one reply is gotten per call to this routine.
*/

struct hostent *
hookup(host, port)
	char *host;
	int port;
{
	register struct hostent *hp;
	int s, len;

	bzero((char *)&hisctladdr, sizeof (hisctladdr));
	hp = gethostbyname(host);
	if (hp == NULL) {
		static struct hostent def;
		static struct in_addr defaddr;
		static char namebuf[128];
		int inet_addr();

		defaddr.s_addr = inet_addr(host);
		if (defaddr.s_addr == -1) {
			fprintf(stderr, "%s: Unknown host.\n", host);
			return (0);
		}
		strcpy(namebuf, host);
		def.h_name = namebuf;
		hostname = namebuf;
		def.h_addr = (char *)&defaddr;
		def.h_length = sizeof (struct in_addr);
		def.h_addrtype = AF_INET;
		def.h_aliases = 0;
		hp = &def;
	}
	hostname = hp->h_name;
	hisctladdr.sin_family = hp->h_addrtype;
	s = socket(hp->h_addrtype, SOCK_STREAM, 0, 0);
	if (s < 0) {
		perror("ftp: socket");
		return (0);
	}
	if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
		perror("ftp: bind");
		goto bad;
	}
	bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length);
	hisctladdr.sin_port = port;
	if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
		perror("ftp: connect");
		goto bad;
	}
	len = sizeof (myctladdr);
	if (getsockname(s, (char *)&myctladdr, &len) < 0) {
		perror("ftp: getsockname");
		goto bad;
	}
	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);
		goto bad;
	}
	if (verbose)
		printf("Connected to %s.\n", hp->h_name);
	(void) getreply(0); 		/* read startup message from server */
	return (hp);
bad:
	close(s);
	return ((struct hostent *)0);
}

login(hp)
	struct hostent *hp;
{
	char acct[80];
	char *user, *pass;
	int n;

	user = pass = 0;
	ruserpass(hp->h_name, &user, &pass);
	n = command("USER %s", user);
	if (n == CONTINUE)
		n = command("PASS %s", pass);
	if (n == CONTINUE) {
		printf("Account: "); (void) fflush(stdout);
		(void) fgets(acct, sizeof(acct) - 1, stdin);
		acct[strlen(acct) - 1] = '\0';
		n = command("ACCT %s", acct);
	}
	if (n != COMPLETE) {
		fprintf(stderr, "Login failed.\n");
		return (0);
	}
	return (1);
}

/*VARARGS 1*/
command(fmt, args)
	char *fmt;
{

	if (debug) {
		printf("---> ");
		_doprnt(fmt, &args, stdout);
		printf("\n");
		(void) fflush(stdout);
	}
	if (cout == NULL) {
		perror ("No control connection for command");
		return (0);
	}
	_doprnt(fmt, &args, cout);
	fprintf(cout, "\r\n");
	(void) fflush(cout);
	return (getreply(!strcmp(fmt, "QUIT")));
}

#include <ctype.h>

getreply(expecteof)
	int expecteof;
{
	register int n;
	register char c;
	register int code, dig;
	int originalcode = 0, continuation = 0;

	for (;;) {
		dig = n = code = 0;
		while ((c = getc(cin)) != '\n') {
			dig++;
			if (c == EOF) {
				if (expecteof)
					return (0);
				lostpeer();
				exit(1);
			}
			if (verbose && c != '\r' ||
			    (n == '5' && dig > 4))
				putchar(c);
			if (dig < 4 && isdigit(c))
				code = code * 10 + (c - '0');
			if (dig == 4 && c == '-')
				continuation++;
			if (n == 0)
				n = c;
		}
		if (verbose || n == '5') {
			putchar(c);
			(void) fflush (stdout);
		}
		if (continuation && code != originalcode) {
			if (originalcode == 0)
				originalcode = code;
			continue;
		}
/*		if (expecteof || empty(cin)) 	Peter 07/02/85 */
			return (n - '0');
	}
}

/*	not called by anyone anymore... 	Peter P. 07/02/85
empty(f)
	FILE *f;
{
	long mask;
	struct timeval t;

	if (f->_cnt > 0)
		return (0);
	mask = (1 << fileno(f));
	t.tv_sec = t.tv_usec = 0;
	(void) select(20, &mask, 0, 0, &t);
	return (mask == 0);
}
*/

jmp_buf	sendabort;

abortsend()
{

	longjmp(sendabort, 1);
}

sendrequest(cmd, local, remote)
	char *cmd, *local, *remote;
{
	FILE *fin, *dout, *popen();
	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
	char buf[BUFSIZ];
	long bytes = 0, hashbytes = sizeof (buf);
	long wscnt = 0;
	register int c, d;
	struct stat st;
	struct timeval start, stop;

	closefunc = NULL;
	if (setjmp(sendabort))
		goto bad;
	oldintr = signal(SIGINT, abortsend);
	if (strcmp(local, "-") == 0)
		fin = stdin;
	else if (*local == '|') {
		fin = popen(local + 1, "r");
		if (fin == NULL) {
			perror(local + 1);
			goto bad;
		}
		closefunc = pclose;
	} else {
		fin = fopen(local, "r");
		if (fin == NULL) {
			perror(local);
			goto bad;
		}
		closefunc = fclose;
		if (fstat(fileno(fin), &st) < 0 ||
		    (st.st_mode&S_IFMT) != S_IFREG) {
			fprintf(stderr, "%s: not a plain file.", local);
			goto bad;
		}
	}
	if (initconn())
		goto bad;
	if (remote) {
		if (command("%s %s", cmd, remote) != PRELIM)
			goto bad;
	} else
		if (command("%s", cmd) != PRELIM)
			goto bad;
	dout = dataconn("w");
	if (dout == NULL)
		goto bad;
	gettimeofday(&start, (struct timezone *)0);
	switch (type) {

	case TYPE_I:
	case TYPE_L:
		errno = d = 0;
		while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
			if ((d = write(fileno (dout), buf, c)) < 0)
				break;
			bytes += c;
			if (hash) {
				putchar('#');
				fflush(stdout);
			}
		}
		wscnt = bytes;
		if (hash && bytes > 0) {
			putchar('\n');
			fflush(stdout);
		}
		if (c < 0)
			perror(local);
		if (d < 0)
			perror("netout");
		break;

	case TYPE_A:
		while ((c = getc(fin)) != EOF) {
			wscnt++;
			if (c == '\n') {
				while (hash && (bytes >= hashbytes)) {
					putchar('#');
					fflush(stdout);
					hashbytes += sizeof (buf);
				}
				if (ferror(dout))
					break;
				putc('\r', dout);
				bytes++;
			}
			putc(c, dout);
			bytes++;
			if (c == '\r') {
				putc('\0', dout);
				bytes++;
			}
		}
		if (hash) {
			if (bytes < hashbytes)
				putchar('#');
			putchar('\n');
			fflush(stdout);
		}
		if (ferror(fin))
			perror(local);
		if (ferror(dout))
			perror("netout");
		break;
	}
	if (closefunc != NULL)
		(*closefunc)(fin);
	(void) fclose(dout);
	(void) getreply(0);
done:
	gettimeofday(&stop, (struct timezone *)0);
	signal(SIGINT, oldintr);
	if (bytes > 0 && verbose) { 
		if (fin != stdin)
			printf("Actual size of %s is %d bytes\n", local, wscnt);
		ptransfer("sent", bytes, &start, &stop);
	}
	return;
bad:
	if (data >= 0)
		(void) close(data), data = -1;
	if (closefunc != NULL && fin != NULL)
		(*closefunc)(fin);
	goto done;
}

jmp_buf	recvabort;

abortrecv()
{

	longjmp(recvabort, 1);
}

recvrequest(cmd, local, remote, mode)
	char *cmd, *local, *remote, *mode;
{
	FILE *fout, *din, *popen();
	int (*closefunc)(), pclose(), fclose(), (*oldintr)();
	char buf[BUFSIZ];
	long wrcnt = 0;
	long bytes = 0, hashbytes = sizeof (buf);
	register int c, d;
	struct timeval start, stop;

	din = NULL;
	closefunc = NULL;
	if (setjmp(recvabort))
		goto bad;
	oldintr = signal(SIGINT, abortrecv);
	if (strcmp(local, "-") && *local != '|')
		if (access(local, 2) < 0) {
		    if (errno == ENOENT) {
			char *dir = rindex(local, '/');

			if (dir != NULL)
				*dir = 0;
			if (access(dir ? local : ".", 2) < 0) {
				perror(local);
				goto bad;
			}
			if (dir != NULL)
				*dir = '/';
		   } 
		   else {
			perror(local);
			goto bad;
		   }
		}
	if (strcmp(local, "-") == 0)
		fout = stdout;
	else if (*local == '|') {
		fout = popen(local + 1, "w");
		closefunc = pclose;
	} else {
		fout = fopen(local, mode);
		closefunc = fclose;
	}
	if (fout == NULL) {
		if (*local == '|') perror(local + 1);
			else perror(local);
		goto bad;
	}
	if (initconn())
		goto bad;
	if (remote) {
		if (command("%s %s", cmd, remote) != PRELIM)
			goto bad;
	} else
		if (command("%s", cmd) != PRELIM)
			goto bad;
	din = dataconn("r");
	if (din == NULL)
		goto bad;
	gettimeofday(&start, (struct timezone *)0);
	switch (type) {

	case TYPE_I:
	case TYPE_L:
		errno = d = 0;
		while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
			if ((d = write(fileno(fout), buf, c)) < 0)
				break;
			bytes += d;
			if (hash) {
				putchar('#');
				fflush(stdout);
			}
		}
		wrcnt = bytes;
		if (hash && bytes > 0) {
			putchar('\n');
			fflush(stdout);
		}
		if (c < 0)
			perror("netin");
		if (d < 0)
			perror(local);
		break;

	case TYPE_A:
		while ((c = getc(din)) != EOF) {
			bytes++;
			if (c == '\r') {
				while (hash && (bytes >= hashbytes)) {
					putchar('#');
					fflush(stdout);
					hashbytes += sizeof (buf);
				}
				c = getc(din);
				bytes++;
				if (c != '\n') {
					if (ferror (fout))
						break;
					putc ('\r', fout);
					wrcnt++;
				}
				if (c == '\0') {
					continue;
				}
			}
			putc (c, fout);
			wrcnt++;
		}
		if (hash) {
			if (bytes < hashbytes)
				putchar('#');
			putchar('\n');
			fflush(stdout);
		}
		if (ferror (din))
			perror ("netin");
		if (ferror (fout))
			perror (local);
		break;
	}
	(void) fclose(din);
	if (closefunc != NULL)
		(*closefunc)(fout);
	(void) getreply(0);
done:
	gettimeofday(&stop, (struct timezone *)0);
	signal(SIGINT, oldintr);
	if (bytes > 0 && verbose) {
		ptransfer("received", bytes, &start, &stop);
		if (fout != stdout)
			printf("Actual size of %s is %d bytes\n", local, wrcnt);
	}
	return;
bad:
	if (data >= 0) {
/*
/*		if (din != NULL) {
/*			printf("\nstart drainning..\n");
/*			if (!empty(din))
/*				for(;;) { c=getc(din);
/*					if (c == EOF) break;
/*				}
/*		}
/*		(void) getreply(0);
/**/
		(void) close(data), data = -1;
	}
	if (closefunc != NULL && fout != NULL)
		(*closefunc)(fout);
	goto done;
}

/*
 * Need to start a listen on the data channel
 * before we send the command, otherwise the
 * server's connect may fail.
 */
static int sendport = -1;

initconn()
{
	register char *p, *a;
	int result, len;

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, 0);
	if (data < 0) {
		perror("ftp: socket");
		return (1);
	}
	if (!sendport)
		if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) {
			perror("ftp: setsockopt (resuse address)");
			goto bad;
		}
	if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
		perror("ftp: bind");
		goto bad;
	}
	if (options & SO_DEBUG &&
	    setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
		perror("ftp: setsockopt (ignored)");
	len = sizeof (data_addr);
	if (getsockname(data, (char *)&data_addr, &len) < 0) {
		perror("ftp: getsockname");
		goto bad;
	}
	if (listen(data, 1) < 0) {
		perror("ftp: listen");
		goto bad;
	}
	if (sendport) {
		a = (char *)&data_addr.sin_addr;
		p = (char *)&data_addr.sin_port;
#define	UC(b)	(((int)b)&0xff)
		result =
		    command("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]));
		if (result == ERROR && sendport == -1) {
			sendport = 0;
			goto noport;
		}
		return (result != COMPLETE);
	}
	return (0);
bad:
	(void) close(data), data = -1;
	return (1);
}

FILE *
dataconn(mode)
	char *mode;
{
	struct sockaddr_in from;
	int s, fromlen = sizeof (from);

	s = accept(data, &from, &fromlen, 0);
	if (s < 0) {
		perror("ftp: accept");
		(void) close(data), data = -1;
		return (NULL);
	}
	(void) close(data);
	data = s;
	return (fdopen(data, mode));
}

ptransfer(direction, bytes, t0, t1)
	char *direction;
	long bytes;
	struct timeval *t0, *t1;
{
	struct timeval td;
	long ms;
	float bs;

	tvsub(&td, t1, t0);
	ms = (td.tv_sec * 1000) + (td.tv_usec / 1000);
#define	nz(x)	((x) == 0 ? 1 : (x))
	bs = ((bytes * NBBY * 1000) / (float) nz(ms)) / NBBY;
	printf("%ld bytes %s in %d.%02d seconds (%.2g Kbytes/s)\n",
		bytes, direction, td.tv_sec, td.tv_usec / 10000, bs / 1024.);
}

tvadd(tsum, t0)
	struct timeval *tsum, *t0;
{

	tsum->tv_sec += t0->tv_sec;
	tsum->tv_usec += t0->tv_usec;
	if (tsum->tv_usec > 1000000)
		tsum->tv_sec++, tsum->tv_usec -= 1000000;
}

tvsub(tdiff, t1, t0)
	struct timeval *tdiff, *t1, *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;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.