*
 * Simple File Transfer Program
 * 
 * This program will act as both the client and the server in
 * this file transfer, depending on values provided on the
 * command line. To run it as a client specify the remote
 * hostname as the first argument the local filename to be
 * transferred as the second argument. If you run this program
 * without argument, it will act as a server and will wait for
 * a connection from a client.
 * 
 * This program is designed to work on both SunOS, a derivative
 * of Berkeley 4.2 Unix, Unix System V (running on a Unisys
 * U6000), and on CTOS II.
 * 
 * 
 * Compiletime flags: define DEBUG to see a trace of most
 * things as they happen.  Undefined produces a quiet
 * runfile. define CTOS to compile code for CTOS. define
 * U6000 to compile code for the U6000 running Unix System V.
 * 
 *
*  HISTORY OF CHANGES *
*  UCF 17648039: Socket example used to not compile under BTOS C. *


include stdio.h
include <systypes.h>
include syssocket.h


ifndef CTOS
* CTOS Microsoft C 6.1 defines __CTOS__ *
if defined( __CTOS__)
define CTOS
* BtosC 2.1 defines __BTOSC__ *
elif defined(__BTOSC__)
define CTOS
endif
endif


* Special CTOS includes *
ifdef CTOS
* These two are undefined in CTOS. *
define O_RDONLY 0
define O_WRONLY 1
define send ssend
else

include sysfile.h

endif

include errno.h
include string.h
include netinetin.h
include netdb.h

*
 * Also  On the U6000 you must compile explicitly with the
 * socket library: cc DU6000 [DDEBUG] osftp sftp.c
 * liblibsocket.a
 *
ifdef U6000
include fcntl.h
endif


* Definitions used throughout the program *
define TX 1
define RCV 2
define XFERPORT 4096
define BUFSZ 512
define EOT 4

ifdef __BTOSC__
extern int      errno;
endif

**  Global Variables  **

char           *hostname;		* Name of machine to transfer
								 * file to, as taken from argv *
char           *fname;			* Name of file to be
								 * transfered, as taken from
								 * argv *
int             s;				* File Descriptor used for
								 * Socket *
int             n;				* Value returned from
								 * function calls *
int             fdesc;			* File Descriptor used for
								 * referencing the file *
struct sockaddr_in server,
                client;			* Client and server socket
								 * addresses *
struct hostent *hp;				* Pointer to struct w host
								 * address *
char            buf[BUFSZ];		* sendrecv buffer *



main(int argc, char *argv[])
{
	int             trswitch;

	hello_msg();				* Display startup messages *

	trswitch = check_args(argc, argv);	* Insure correctness of
										 * input params *

	if (trswitch == TX)
		transmit();				* Be a client process *
	else
		receive();				* Be a server process *
}



* Display startup messages *
int
hello_msg()
{
	printf(\n\n\n          Simple File Transfer Program\n\n);
	printf(This version compiled to run under );
if defined( __CTOS__)
	printf(CTOS with Micrsoft C 6.1\n);
elif defined(__BTOSC__)
	printf(CTOS with BtosC 1.2\n);
elif defined(CTOS)
	printf(CTOS\n);
else
	printf(Unix);
ifdef U6000
	printf( System V on a Unisys U6000\n);
else
	printf(\n);
endif
endif
}


int
check_args(count, vect)
	int             count;
	char           *vect[];
{
	if (count > 3) {
		printf(Usage: sftp [hostname fname]\n);
		exit(1);
	}
	if (count == 3) {
		hostname = vect[1];
		fname = vect[2];
		return (TX);
	} else
		return (RCV);
}




int
transmit()
{								* Be a client *
	int             bytes_read,
	                bytes_sent,
	                total = 0;

	printf(Preparing to transmit file s to s\n, fname, hostname);

	* Open the file to be transfered in the readonly mode *
	if ((fdesc = open(fname, O_RDONLY)) < 0) {
		printf(Unable to open s to transfer.\n, fname);
		exit(1);
	}
ifdef DEBUG
	printf(File successfully opened.\n);
endif

	*
	 * Set up socket connections... AF_INET is the
	 * communications domain, internet, SOCK_STREAM is the
	 * type of connection requested, a bytestream socket
	 * connection, and IPPROTO_TCP specifies the protocol to
	 * be used, in this case TCP.
	 *
	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	checkp(s, socket);
	server.sin_family = AF_INET;* Set domain *
	server.sin_port = htons(XFERPORT);	* Set up transfer port *
	hp = gethostbyname(hostname);	* Get network address for
									 * host *
	checkp((hp == NULL ? 1 : 0), gethostbyname);

	*
	 * Copy the h_addr field of the hostent struct returned by
	 * gethostbyname to hp into the server address.
	 *
	memcpy((char *) server.sin_addr.s_addr, hp>h_addr, hp>h_length);

	n = connect(s, server, sizeof(server));	* Connect to remote
												 * host *
	checkp(n, connect to host);

	n = send(s, fname, strlen(fname), 0);	* Send name of file to
											 * follow *
	checkp(n, fname send);

	n = recv(s, buf, BUFSZ, 0);	* Receive ACK from server *
	checkp(n, server ack);
	if ((n > 2  buf[0] == 'A'  buf[1] == 'C'  buf[2] == 'K')) {
		printf(Remote failed to ack: s\n, buf);
		exit(1);
	}
	printf(Sending file...\n);

	* Read from input file, send to socket, i.e., do transfer *
	while ((bytes_read = read(fdesc, buf, BUFSZ)) > 0) {
		checkp(bytes_read, block read from disk);

		bytes_sent = send(s, buf, bytes_read, 0);
		checkp(bytes_sent, send data);
		total += bytes_sent;

	}

	* End of session *
	n = close(fdesc);
	checkp(n, close transfered file);
ifdef CTOS
	n = sclose(s);
else
	n = close(s);
endif
	checkp(n, close socket);
	printf(File successfully transfered, d bytes\n, total);
	exit(0);
}





int
receive()
{								* Be a server *
	int             ns;			* New socket from 'accept' *
	int             client_len = sizeof(client);
	int             bytes_rcvd,
	                bytes_wrote,
	                total = 0;

	*
	 * Set up socket connections... AF_INET is the
	 * communications domain, internet, SOCK_STREAM is the
	 * type of connection requested, a bytestream socket
	 * connection, and IPPROTO_TCP specifies the protocol to
	 * be used, in this case TCP.
	 *
	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	checkp(s, socket);


	server.sin_family = AF_INET;* Set domain *
	server.sin_port = htons(XFERPORT);	* Set transfer port *
	server.sin_addr.s_addr = htonl(INADDR_ANY);

	n = bind(s, server, sizeof(server));	* Bind server's address
											 * to socket *
	checkp(n, bind address);

	n = listen(s, 1);			* Listen for a connection on
								 * the socket *
	checkp(n, listen);
	client.sin_family = AF_INET;* Set the client's comm
								 * domain *
	client.sin_addr.s_addr = htonl(INADDR_ANY);	* Accept any Internet
												 * address *
	client.sin_port = htons(0);

	*
	 * Begin loopforever to accept connections on the socket
	 * and such
	 *
	for (;;) {
		printf(***Ready for transfer***\n);
		total = 0;
		ns = accept(s, client, client_len);
		checkp(ns, accept);
		bytes_rcvd = recv(ns, buf, BUFSZ, 0);
		checkp(bytes_rcvd, recv filename);

		fname = buf;			* Copy buffer into filename *
		fname[bytes_rcvd] = '\0';

ifdef DEBUG
		printf(File name received: s\n, fname);
endif

ifndef CTOS
		fdesc = open(fname, O_WRONLY | O_CREAT, 0666);	* Open fname for
														 * writing *
		checkp(fdesc, open file for writing);
else
		fdesc = creat(fname, O_WRONLY);	* Open for CTOS 
										 * creates or overwrites *
		checkp(fdesc, create file for writing or open for overwriting);
endif
		if (fdesc >= 0) {		* Then file was opened
								 * successfully, send an ACK *
			printf(Receiving file s...\n, fname);
			strcpy(buf, ACK);
			n = send(ns, buf, 3, 0);
			checkp(n, send ack to client);
		} else {				* Error, probably file
								 * already exists, but
								 * nackbomb anyway *
			strcpy(buf, Error opening file on Server);
			n = send(ns, buf, 28, 0);
			checkp(n, send nack to client);
			checkp(fdesc, open file);	* NOTE: this will cause
										 * pgm to exit. *
		}

		* Receive the actual file *
		while ((bytes_rcvd = recv(ns, buf, BUFSZ, 0)) > 0) {
			checkp(bytes_rcvd, recv data);
			bytes_wrote = write(fdesc, buf, bytes_rcvd);
			checkp(bytes_wrote, block wrote to disk);
			total += bytes_rcvd;
		}

		n = close(fdesc);		* Close new file *
		checkp(n, close file);
ifdef CTOS
		n = sclose(ns);			* Close new socket *
else
		n = close(ns);
endif
		checkp(n, close socket);

		printf(File received, d bytes\n, total);

	}							* END LoopForever *

}





int
checkp(n, msg)					* Check the value returned
								 * from a function call, if   *
	int             n;			* negative, display an
								 * appropriate error.
								 * Defining   *
	char           *msg;		* DEBUG at compile will cause
								 * a trace of all relavant *
{								* function calls.                                     *
	if (n < 0) {
		printf(Program terminated: error u in s\n, errno, msg);
		exit(1);
	}
ifdef DEBUG
	else
		printf(s succeded, n = d\n, msg, n);
endif
}
