/*	dcp.c
 *
 *	Revised edition of dcp
 *
 *	Stuart Lynne May/87
 *
 *	Copyright (c) Richard H. Lamb 1985, 1986, 1987
 *	Changes Copyright (c) Stuart Lynne 1987
 *
 *	$Id: dcp.c,v 1.2 90/01/16 10:25:00 crash Exp Locker: crash $
 */

#ifndef lint
static char RCSid[] = "$Id: dcp.c,v 1.2 90/01/16 10:25:00 crash Exp Locker: crash $";
#endif /* lint */

/*
 *	"DCP" a uucp clone. Copyright Richard H. Lamb 1985,1986,1987
 *	This program implements a uucico type file transfer and remote
 *	execution type protocol.
 */

#include "dcp.h"

int		pktsize;                /* packet size for pro */
FILE	*logfile;            	/* system log file */
FILE	*syslog;            	/* transmission log file */
FILE	*fw;                 	/* cmdfile pointer */
char	state;					/* system state */
char	cfile[80];              /* work file pointer */
int		remote;                 /* -1 means we're remote ..7 */
int		msgtime;                /* timout setting */
char	fromfile[132];
char	hostfile[132];			/* host version of fromfile */
char	tofile[132];
int		fp;                     /* current disk file ptr */
int		size;                   /* nbytes in buff */
FILE	*fsys;
char	Rmtname[20];
char	rmtname[20];
char	*cctime;
char	proto[5];
/* char	loginseq[256]; */
char 	sysline[BUFSIZ];
char	s_systems[64];
char	s_logfile[64];
char	s_syslog[64];
char 	*flds[60];
int 	kflds;

extern	int		debuglevel;				/* debugging flag */

unsigned int	checksum();



/*
 *	new usage
 *
 *	dcp	[-xn] -r0		slave mode
 *	dcp	[-xn] -shost	call host
 *	dcp	[-xn] -sall		call all hosts
 *	dcp	[-xn] 			call any hosts as required by C. files
 */

complain(s)
char *s;
{
	fprintf(stderr, "Please set your %s environment variable.", s);
}

static void cant(file)
char *file;
{
	fprintf(stderr, "Can't open: \"%s\"\n", file);
	exit( NULL );
}

dcpmain( argc, argv )
int	argc;
char *argv[];
{
	FILE	*ftmp;
	char	line[132];
#ifdef FJE
	short	inhibit_xqt = FALSE;
	short	goto_xqt = FALSE;
#endif /* FJE */

	if (!name || !*name) {
		complain(NAME);
		exit( -1 );
	}
	if (!nodename || !*nodename) {
		complain(NODENAME);
		exit( -1 );
	}
	mkfilename(s_logfile, logdir,  LOGFILE);
	mkfilename(s_syslog,  logdir,  SYSLOG);
	mkfilename(s_systems, confdir, SYSTEMS);

	if ((logfile = FOPEN(s_logfile, "a", 't')) == NULL)
	   cant(s_logfile);
	if ((syslog  = FOPEN(s_syslog,  "a", 't')) == NULL)
	   cant(s_syslog);

	remote = MASTER;
	debuglevel = 1;
	fp = -1;
	fw = (FILE *) NULL;
	strcpy(Rmtname, "any");

	while (--argc) {
		if (**++argv == '-') {
			switch(argv[0][1]) {
#ifdef FJE
			case 'i':				/* "-i" to disable DCXQT() */
				inhibit_xqt = TRUE;
				break;
			case 'c':				/* "-c" to jump down to DCXQT() */
				goto_xqt = TRUE;
				break;
#endif /* FJE */
			case 'x':
				debuglevel = atoi(argv[0]+2);
				break;
			case 's':
				sprintf(Rmtname, "%.7s", argv[0]+2);
				break;
			case 'r':
				remote = atoi(argv[0]+2);
				break;
			default:
				break;
			}
		}
	}
	printmsg( 0, "remote = %d", remote);
	printmsg( 0, "debuglevel = %d", debuglevel);
	printmsg( 0, "Rmtname = %s", Rmtname);
	if (goto_xqt)
		goto try_dcxqt;

	if (remote == MASTER) {
 		printmsg( 0, "Calling %s", Rmtname );
		if (( fsys = FOPEN( s_systems, "r", 't' )) == (char *)NULL )
			exit( FAILED );
		state = 'I';

		while (TRUE) {
			printmsg( 4, "Mstate = %c", state );
			switch (state) {
			case 'I':
				state = getsystem();
				break;
			case 'S':
				state = callup();
				break;
			case 'P':
				state = startup();
				break;
			case 'D':
				state = master();
				break;
			case 'Y':
				state = sysend();
				break;
			case 'G':
				if ( strcmp( Rmtname, "any" ) != SAME )
					state = 'Y';
				else
					state = 'I';
				break;
			}
			if (state == 'A')
				break;
		}
		fclose( fsys );
	} else {
 		printmsg( 0, "Answering %s, speed %d", device, speed );
		if (openline( device, speed ) == -1)
			return(FALSE);
		state = 'L';
		while (TRUE) {
			printmsg( 4, "Sstate = %c", state );
			switch (state) {
			case 'L':
				state = login();
				break;
			case 'I':
				state = startup();
				break;
			case 'R':
				state = slave();
				break;
			case 'Y':
				state = sysend();
				break;
			}
			if (state == 'A')
				break;
		}
		closeline();
	}
	/* scan and process any received files */

try_dcxqt:
	if (!inhibit_xqt && dcxqt())
		printmsg( 0, "ERROR in DCXQT" );

	fclose( syslog );
	fclose( logfile );
	/*
	 *	If we initiated a call (remote == MASTER) and the
	 *	system responded without errors [or at least, without
	 *	an ABORT code 'A'], return 1 to the shell.
	 *
	 *	[The function sysend() returns 'I' if successful in
	 *	terminating the connection.  This allows the state
	 *	machine to start over again with the next machine
	 *	to call, although this isn't implemented :-]
	 */
	return( (remote == MASTER) && (state != 'A') );
}

/*
 *	master
 */

master()
{
	state = 'I';
	while (TRUE) {
		printmsg( 4, "Top level state (master mode) %c", state );
		switch (state) {
		case 'I':
			state = sinit();
			break;
		case 'B':
			state = scandir();
			break;
		case 'S':
			state = send();
			break;
		case 'Q':
			state = sbreak();
			break;
		case 'G':
			state = receive();
			break;
		case 'C':
			state = 'Y';
			break;
		case 'Y':
			state = endp();
			break;
		case 'P':
			return('Y');
		case 'A':
			return('A');
		default:
			return('A');
		}
	}
}

/*
 *	slave
 */

slave()
{
	state = 'I';
	while (TRUE) {
		printmsg( 4, "Top level state (slave mode) %c", state );
		switch (state) {
		case 'I':
			state = rinit();
			break;
		case 'F':
			state = receive();
			break;
		case 'C':
			state = schkdir();
			break;
		case 'T':
			state = 'B';
			break;
		case 'B':
			state = scandir();
			break;
		case 'S':
			state = send();
			break;
		case 'Q':
			state = sbreak();
			break;
		case 'G':
			return('Y');
		case 'Y':
			state = endp();
			break;
		case 'P':
			return('Y');
		case 'A':
			return('A');
		default:
			return('A');
		}
	}
}


/*
 *	receive
 *
 *	This is the state table switcher for receiving files.
 */

receive()
{
	state = 'F';			/* Receive-Init is the start state */

	while (TRUE) {
		printmsg( 4, " receive state: %c", state );
		switch (state) {					/* Do until done */
		case 'F':
			state = rfile();
			break;							/* Receive-File */
		case 'D':
			state = rdata();
			break;							/* Receive-Data */
		case 'C':
			return('C');					/* Complete state */
		case 'A':
			return('Y');					/* "Abort" state */
		default:
			return('Y');
		}
	}
}


/*
 *  send
 *
 *  Send is the state table switcher for sending files.  It loops until
 *  either it finishes or an error is encountered.  The routines called
 *  by send are responsible for changing the state.
 */

send()
{
	fp = -1;						/* reset file getter/opener */
	state = 'F';					/* Send initiate is the start state */
	while (TRUE) {					/* Do this as long as necessary */
		printmsg( 4, "send state: %c", state );
		switch (state) {
		case 'F':
			state = sfile();
			break;					/* Send-File */
		case 'D':
			state = sdata();
			break;					/* Send-Data */
		case 'Z':
			state = seof();
			break;					/* Send-End-of-File */
		case 'B':
			return ('B');			/* Complete */
		case 'A':
			return ('Y');			/* "Abort" */
		default:
			return ('Y');			/* Unknown, fail */
		}
	}
}

/*
 *	A command formatter for DCP. RH Lamb
 *
 *	sets up stdin and stdout on various machines
 *	There is NO command checking so watch what you send and who you
 *	let accsess your machine.  "C rm /usr/*.*" could be executed.
 */
dcxqt()
{
	int	i;
	char	command[60], input[60], output[60], line[BUFSIZ];
	char	*cp;

	printmsg( 2, "\n\nStarting XQT phase..." );
	while (dscandir()) {
		strcpy( line, cfile );
		fw = FOPEN( line, "r", 'b' );		/* imported X file */
#if 0 /* Why? */
		strcpy(cfile, line);
#endif
		printmsg( 2, "dcxqt:%s %ld", cfile, fw );
		input[0]   = '\0';
		output[0]  = '\0';
		command[0] = '\0';
		while ( fgets( line, BUFSIZ, fw ) != (char *)NULL ) {
			cp = index( line, '\n' );
			if ( cp != (char *)NULL )
				*cp = '\0';

			printmsg( 8, "dcxqt: %s", line );
			switch (line[0]) {
			case 'U':	/* User information */
				break;
			case 'I':	/* File to use as stdin */
				strcpy( input,   &line[2] );
				break;
			case 'O':	/* File to use as stdout */
				strcpy( output,  &line[2] );
				break;
			case 'C':	/* Command to execute */
				strcpy( command, &line[2] );
				break;
			case 'R':	/* Hmmm, I don't remember this one */
				break;	/* Maybe error response file? with '!' notation? */
			default :
				break;
			}
		}
		fclose( fw );
		printmsg( 0, "xqt: %s\n", command );
		shell( command, input, output, (char *)NULL );

		unlink(cfile);
 		importpath( hostfile, input );
 		unlink(hostfile);
		importpath( hostfile, output );
 		unlink(hostfile);
	}
	return(0);
}

/*
 *  printmsg
 *
 *  Print error message on standard output if not remote.
 */

/* VARARGS2 */
printmsg(level, fmt, args)
int 	level;
char	*fmt;
long	args;
{
	if ( debuglevel > level ) {
		if ( remote == MASTER ) {
			vprintf( fmt, &args );
			fputc( '\n', stdout );
			fflush(stdout);
		}
		vfprintf( logfile, fmt, &args );
		fputc( '\n', logfile );
		fflush(logfile);
	}
}
