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

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

/*
 *	"DCP" a uucp clone. Copyright Richard H. Lamb 1985,1986,1987
 *	Get the next system, and other support routines
 */

#include "dcp.h"

#ifdef AMIGA
#  include <libraries/dos.h>	/* For TICKS_PER_SECOND */
#endif

/*#define PROTOS  "trkg"*/
#define PROTOS  		"g"
#define MAXLOGTRY       3

Proto Protolst[] = {
	'g', ggetpkt, gsendpkt, gopenpk, gclosepk,
#if 0
	    'k', kgetpkt, ksendpkt, kopenpk, kclosepk,
	    'r', rgetpkt, rsendpkt, ropenpk, rclosepk,
	    't', tgetpkt, tsendpkt, topenpk, tclosepk,
#endif
	'0'};

#define EOTMSG "\004\r\004\r"

procref         getpkt, sendpkt, openpk, closepk;

#ifdef FJE
#define MAXSYS 20
static short sys_cnt = 0;
static char  sys_name[MAXSYS][8];

already_called(name)
char *name;
{
	register int i;

	for (i=sys_cnt; i-- > 0; )
		if (strncmp(sys_name[i], name, 8) == SAME) return( TRUE );
	return( FALSE );
}
#endif /* FJE */

/*
 *	getsystem
 *		Process an "systems" file entry (like "Systems")
 */

getsystem()
{
	int	i;
	extern int filecount;			/* From dcpxfer.c */

#ifdef FJE
	do {
		if ( fgets( sysline, BUFSIZ, fsys ) == (char *)NULL )
			return( 'A' );
	} while (*sysline == '#' || isspace(*sysline));
#else /* !FJE */
	if ( fgets( sysline, BUFSIZ, fsys ) == (char *)NULL )
		return( 'A' );
#endif /* FJE */

	printmsg( 4, "%s", sysline );
	filecount = 0;
	kflds = getargs( sysline, flds );

	strncpy( rmtname, flds[FLD_REMOTE], 8 );	/* boake2 */
	rmtname[8] = '\0';
	cctime = flds[FLD_CCTIME];					/* Any */
	strcpy( device, flds[FLD_DEVICE] );			/* SER: */

#if 0
	strcpy( type, flds[FLD_TYPE] );				/* DIR */
#endif
	strcpy( speed, flds[FLD_SPEED] );			/* 2400 or phone# */
	strcpy( proto, flds[FLD_PROTO] );			/* g */

	if (debuglevel > 4)
		for (i = FLD_EXPECT; i < kflds; i += 2)
			fprintf( stderr, "expect[%02d]:\t%s\nsend  [%02d]:\t%s\n",
			    i, flds[i], i+1, flds[i+1] );

	printmsg( 2, "remote = %s, call time = %s", rmtname, cctime );
	printmsg( 2, "device = %s ", device );
	printmsg( 2, "speed = %s, protocol = %s", speed, proto );

	fw = (FILE *)NULL;
	if (
	    (checktime( cctime )) ||	/* Any time is okay */
		( strcmp( Rmtname, "all" ) == SAME ) ||
	    ( strcmp( Rmtname, rmtname ) == SAME ) ||
	    ( (strcmp( Rmtname, "any" ) == SAME) && scandir() == 'S' )
	    ) {
		if ( fw != (FILE *)NULL )
			fclose( fw );			/* in case we matched with scandir */
#ifdef FJE
		if ( already_called( rmtname ) )
			return( 'G' );
		/*
		 *	Keep track of the systems we have called so far;
		 *	there's no need to call'em twice...
		 */
		if (sys_cnt < MAXSYS) {
			strncpy(sys_name[sys_cnt], rmtname, 8);
			sys_name[sys_cnt++][8] = '\0';
		}
#endif
		return( 'S' );				/* startup this system */
	} else
		return('G');
}


/*
 *	checkname
 *		Do we know the guy ?
 */
checkname(name)
char name[];
{
	FILE *ff;
	char line[BUFSIZ], tmp[20];		/* can change to 8 if %8s works */

	if ( ( ff = FOPEN( s_systems, "r", 't' )) == (char *)NULL )
		return( FAILED );

	while ( fgets( line, BUFSIZ, ff ) != (char *)NULL ){
#ifdef FJE
		if (*line == '#' || isspace(*line))
			continue;
#endif /* FJE */
		sscanf( line, "%8s ", tmp );
		printmsg( 3, "wanted = %s, found = %s", name, tmp );
		if ( strncmp( tmp, line, 7 ) == SAME ) {
			fclose( ff );
			return ( OK );			/* OK, I like you */
		}
	}
	fclose( ff );
	return( FAILED );				/* Who are you? */
}


/*
 *	checktime
 *		check if we may make a call at this time
 *		------------> to be implemented.  Again.  Didn't think it crucial
 */
checktime(xtime)
char	xtime[];
{
	return(0);						/* OK, go to it */
}


/*
 *	sysend
 *		end UUCP session negotiation
 */
sysend()
{
	char	msg[80];

	msg[1] = '\0';
	msgtime = 2 * MSGTIME;
	wmsg("OOOOOO", 2);
	closeline();
	if ( remote == MASTER )
		return('I');
	return('A');
}


#if 0
/*
 *	delay
 */
ddelay(dtime)
int	dtime;
{
	int	i, j;
	for (i = 0; i < dtime; )
		i++;
}
#endif

/*
 *	wmsg
 *		write a ^P type msg to the remote uucp
 */
wmsg(msg, syn)
int	syn;
char msg[];
{
	int	len;
	len = strlen(msg);
	if (syn == 2)
		swrite("\0\020", 2);
	swrite(msg, len);
	if (syn == 2)
		swrite("\0", 1);
}


/*
 *	rmsg
 *		read a ^P msg from UUCP
 */

rmsg(msg, syn)
int	syn;
char msg[];
{
	int	ii;
	char c, cc[5];

	c = 'a';
	if (syn == 2) {
		while ((c & 0x7f) != '\020') {
			if (sread(cc, 1, msgtime) < 1)
				return(-1);
			/* Don't ask.  MSC needs more than a byte to breathe */
			c = cc[0];
		}
	}
	for (ii = 0; ii < 132 && c ; ii++) {
		if (sread(cc, 1, msgtime) < 1)
			return(-1);
		c = cc[0] & 0x7f;
		if (c == '\r' || c == '\n')
			c = '\0';
		msg[ii] = c;
	}
	return(strlen(msg));
}


/*
 *	startup
 */
startup()
{
	char	msg[80], tmp1[20], tmp2[20];
	if ( remote == MASTER ) {
		msgtime = 2 * MSGTIME;
		if (rmsg(msg, 2) == -1)
			return('Y');
		printmsg( 2, "1st msg = %s", msg );
		if (msg[5] == '=' && strncmp(&msg[6], rmtname, 7))
			return('Y');

#if 0
		/* -Q0 -x16 remote debuglevel set */
		sprintf(msg, "S%.7s -Q0 -x%d", nodename, debuglevel);
#else
		sprintf(msg, "S%.7s", nodename);
#endif
		wmsg(msg, 2);
		if (rmsg(msg, 2) == -1)
			return('Y');
		printmsg( 2, "2nd msg = %s", msg );
		if (strncmp(&msg[1], "OK", 2))
			return('Y');
		if (rmsg(msg, 2) == -1)
			return('Y');
		printmsg( 2, "3rd msg = %s", msg );
		/*
		 *	[FJE] Protocol negotiation right here...
		 *	Note the "index(..., proto[0])" would become a search
		 *		through a list of protocols, looking for the first
		 *		which matched, in order of most efficient (?).  Here,
		 *		"proto" comes from the Systems file...
		 */
		if (msg[0] != 'P' || index(&msg[1], proto[0]) == (char *)NULL) {
			wmsg("UN", 2);
			return('Y');
		}
		sprintf(msg, "U%c", proto[0]);
		wmsg(msg, 2);
		setproto(proto[0]);
		return('D');
	} else {
		msgtime = 2 * MSGTIME;
		sprintf(msg, "Shere=%s", nodename);
		wmsg(msg, 2);
		if (rmsg(msg, 2) == -1)
			return('Y');
		sscanf(&msg[1], "%s %s %s", rmtname, tmp1, tmp2);
		sscanf(tmp2, "-x%d", &debuglevel);
		printmsg( 1, "debug level from remote = %d", debuglevel );
		printmsg( 2, "1st msg from remote = %s", msg );
		if (checkname(rmtname))
			return('Y');
		wmsg("ROK", 2);
		sprintf(msg, "P%s", PROTOS);
		wmsg(msg, 2);
		if (rmsg(msg, 2) == -1)
			return('Y');
		if (msg[0] != 'U' || index(PROTOS, msg[1]) == (char *)NULL )
			return('Y');
		proto[0] = msg[1];
		setproto(proto[0]);
		return('R');
	}
}

/******* set the protocol **********/
setproto(pr)
char pr;
{
	int	i;
	Proto *tproto;

	for (tproto = Protolst;
			tproto->type != '\0' && pr != tproto->type;
					tproto++)
		printmsg( 3, "setproto: %c %c", pr, tproto->type );

	if (tproto->type == '\0') {
		printmsg( 0,
			"setproto:  You said I had '%c', but I can't find it", pr );
		exit(1);
	}
	getpkt = tproto->a;
	sendpkt = tproto->b;
	openpk = tproto->c;
	closepk = tproto->d;
}


int prefix(sh, lg)
register char *sh, *lg;
{
	sh--;
	while (*++sh && (*sh == *lg++))
		;
	return( !*sh );
}

int notin(sh, lg)
char *sh, *lg;
{
	while (*lg) {
		if (prefix(sh, lg++))
			return( FALSE );
	}
	return( TRUE );
}

#define MAXR 300
unsigned int g_timeout = 30;
static char rdvec[MAXR];

int expectstr( str, timeout )
char *str;
int timeout;
{
	char *rp = rdvec;
	int kr;
	char nextch;

	printmsg( 2, "wanted :%s:", prt(str, strlen(str)) );

	*rp = 0;
	if ( strcmp(str, "\"\"") == SAME )
		return( TRUE );
	while ( notin( str, rdvec ) ) {
		kr = sread( &nextch, 1, timeout /* 40 */ );
		if (kr <= 0) {
			printmsg( 4, "read :%s:", prt(rdvec, rp-rdvec));
			return( FALSE );
		}
		if (*rp = (nextch & 0177))
			*++rp = '\0';
		if (rp >= rdvec + MAXR) {
			printmsg( 4, "read :%s:", prt(rdvec, rp-rdvec));
			return( FALSE );
		}
	}
	printmsg( 3, "read :%s:", prt(rdvec, rp-rdvec));
	return( TRUE );
}

int writestr(s)
register char *s;
{
	register char last;
	register char * m;
	int nocr;

	last = '\0';
	nocr = FALSE;
	printmsg( 6, "writing <%s>", prt(s, strlen(s)));
	while (*s) {
		if (last == '\\') {
			switch (*s) {
			case 'p':			/* 1/2 second pause */
			case 'P':
				ticks( (long) TICKS_PER_SECOND >> 1 );
				break;
			case 'd':
			case 'D':			/* delay */
				sleep(2);
				break;
			case 'k':			/* Break signal */
			case 'K':
				ssendbrk(3);
				break;
			case 'c':
			case 'C':			/* end string don't output CR */
				nocr = TRUE;
				break;
			case 'r':
			case 'R':			/* carriage return */
			case 'm':
			case 'M':
				swrite( "\r", 1 );
				break;
			case 'n':
			case 'N':			/* newline */
				swrite( "\n", 1 );
				break;
			case 'b':
			case 'B':			/* backspace */
				swrite( "\b", 1 );
				break;
			case 't':
			case 'T':			/* tab */
				swrite( "\t", 1 );
				break;
			case 's':
			case 'S':			/* space */
				swrite( " ", 1 );
				break;
			case 'w':	/* Set the delay time for expect strings */
			case 'W':	/* up to 120 seconds (2 minutes) */
				g_timeout = 0;
				for (s++; isdigit(*s); )
					g_timeout = g_timeout*10 + (*s++ - '0');
				if (g_timeout > 120)
					g_timeout = 30;
				s--;
				break;
			case 'z':	/* Set the baud rate... */
			case 'Z':
				SIOSpeed( ++s );
				while ( *s != '\0' && *s != '\\' )
					s++;
				if ( *s == '\\' )
					s++;
				break;
			default:
				swrite( s, 1 );
			}
			last = '\0';
		} else if (*s != '\\') {
			swrite( s, 1 );
		} else
			last = *s;
		s++;
	}
	return( nocr );
}

/***
 *   void sendthem(str)   send line of login sequence
 *         char *str;
 *
 *   return codes:  none
 */

void sendstr(str)
char *str;
{
	int nw, ns;
	long nulls;

	printmsg( 2, "sending %s", str );

	if (prefix("BREAK", str)) {
		sscanf(&str[5], "%1d", &nulls);
		if (nulls <= 0 || nulls > 10)
			nulls = 3;
		/* send break */
		ssendbrk(nulls);
		return;
	}
	if ( strcmp(str, "EOT") == SAME ) {
		swrite(EOTMSG, strlen(EOTMSG));
		return;
	}
	if ( strcmp(str,"\"\"") == SAME )
		*str = '\0';

	if ( strcmp(str,"") != SAME ) {
		if (!writestr(str))
			swrite ("\r", 1);
	} else
		swrite("\r", 1);
	return;
}

int sendexpect( s, e, timeout )
char *s;
char *e;
int timeout;
{
	sendstr( s );
	return( expectstr( e, timeout ) );
}

dial()
{
	int	flg, kk, jj, ll, firstflg;
	char buf[4], *prsend;
	char *exp;
	char *alternate;
	int	ok;
	int i;

	if ( strcmp( flds[FLD_TYPE], "HAYES" ) != SAME ) {
		printmsg( 0, "dial: unsupported dialer %s", flds[FLD_TYPE] );
		return( FALSE );
	}
	printmsg( 3, "calling host %s", rmtname );
	if (openline(device, "2400" ))
		return( FALSE );

	printmsg( 0, "hayes: trying 2400" );
	if ( sendexpect( "ATZ", "OK", 2 ) != TRUE ) {
		sendexpect( "\\d+++\\d", "OK", 2 );
		if ( sendexpect( "ATZ", "OK", 2 ) != TRUE ) {
			printmsg( 0, "hayes: trying 1200" );
			SIOSpeed( "1200" );
			if ( sendexpect( "ATZ", "OK", 2 ) != TRUE ) {
				sendexpect( "\\d+++\\d", "OK", 2 );
				if ( sendexpect( "ATZ", "OK", 2 ) != TRUE )
					return( FALSE);
			}
		}
	}
	printmsg( 0, "hayes: got modem response" );
	/*
	 *	DO NOT REMOVE THE \d FROM THE LINE BELOW!!!
	 *
	 *	My SupraModem seemed pretty critical about returning an "OK"
	 *	response but not really being interested in user commands for
	 *	another second or so.  And since \d is about 2 seconds...
	 */
	sendexpect( "ATX4\\r\\d", "OK\n\r", 2 );
	printmsg( 4, "attempting to dial :%s:", speed );

#ifdef FJE
	sendstr( speed );
	/*
	 *	Typical character stream after the command to dial:

	 	AT DT 123-4567\n		-- computer sends this, and gets back...

	 	AT DT 123-4567\r
	 	(pauses while dialing; waiting for carrier)
	 	\nCONNECT xx00\r\n

	 */
	if (expectstr( "\r", 10 ) != TRUE) {	/* eat echoing of dial command */
		printmsg(1, "hayes: modem not responding after reset!?\n");
		return( FALSE );
	}
	if (expectstr("\n", 50) != TRUE) {
		printmsg(1, "hayes: modem not responding after dial command!?\n");
		return( FALSE );
	}
	if (expectstr("\r\n", 6) != TRUE) {
		printmsg(1, "hayes: didn't get expected response [%s]",
	 		prt(rdvec, strlen(rdvec)));
	}
	/*
	 *	This block used to be part of the above "if" stmt...
	 */
	{
		register char *resp = rdvec, *prt();
		extern int atoi();

		if (strnicmp( resp, "BUSY", 4 ) == SAME) {
			printmsg(1, "hayes: BUSY -- phone line busy\n");
			return( FALSE );
		} else if (strnicmp( resp, "NO DIALTONE", 11 ) == SAME) {
			printmsg(1, "hayes: NO DIALTONE -- check phone line\n");
			return( FALSE );
		} else if (strnicmp( resp, "NO CARRIER", 10 ) == SAME) {
			printmsg(1, "hayes: NO CARRIER -- answered, but no carrier\n");
			return( FALSE );
		} else if (strnicmp( resp, "NO ANSWER", 9 ) == SAME) {
			printmsg(1, "hayes: NO ANSWER -- call not answered\n");
			return( FALSE );
		} else if (strnicmp( resp, "CONNECT", 7 ) == SAME) {
			printmsg( 3, "hayes: got CONNECT" );
			if (atoi(resp+8)) {
				printmsg( 3, "hayes: speed select %s",
					prt(resp+8,strlen(resp+8)) );
				SIOSpeed( resp+8 );
			}
		} else {
			printmsg(1, "hayes: unknown modem response!  [%s]\n",
		 		prt(resp, strlen(resp)));
			return( FALSE );
		}
	}
# if 0
	else {
		printmsg(1, "hayes: modem not responding to dial command!? [%s]\n",
			prt(rdvec, strlen(rdvec)));
		return( FALSE );
	}
# endif
#else /* !FJE */
	if ( sendexpect( speed, "CONNECT", 40 ) != TRUE ) {
		printmsg( 0, "hayes: no answer");
		return( FALSE );
	}
	printmsg( 3, "hayes: got CONNECT" );
	if ( sread( buf, 5, 5 ) == 5 ) {
		printmsg( 3, "hayes: speed select %s", buf );
		/* set speed appropriately */
		SIOSpeed( buf );
	}
#endif
	return( TRUE );
}


/*
 *	callup
 *		script processor - nothing fancy!
 */
callup()
{
	int	flg, kk, jj, ll, firstflg;
	char *prsend;

	char *exp;
	char *alternate;
	int	ok;
	int i;

	printmsg( 0, "calling host %s", rmtname );

	if ( strcmp( flds[FLD_TYPE], "DIR" ) != SAME ) {
		if ( dial() == FALSE )
			return( 'G' );
	} else if (openline(device, speed))
		return( 'G' );

	for (i = 6; i < kflds; i+=2) {
		exp = flds[i];
		printmsg( 3, "callup: expect %d of %d  \"%s\"", i, kflds, exp );
		ok = FALSE;
		while (ok != TRUE) {
			alternate = index( exp, '-' );
			if (alternate != (char *)NULL)
				*alternate++ = '\0';

			ok = expectstr( exp, g_timeout );
			printmsg( 3, "got %s", ok != TRUE ? "?" : "that" );
			if ( ok == TRUE )
				break;
			if ( alternate == (char *)NULL ) {
				printmsg( 0, "LOGIN FAILED" );
				return( 'Y' );
			}
			exp = index( alternate, '-' );
			if ( exp != (char *)NULL )
				*exp++ = '\0';

			printmsg( 3, "send alternate" );
			sendstr( alternate );
		}
		printmsg(3, "callup: send %d of %d \"%s\"", i+1,kflds, flds[i+1]);
		sleep(1);
		sendstr(flds[i+1]);
	}
	return('P');
}

#if 0
/*
 *	slowrite
 *		comunication slow write.  needed for auto-baud modems
 */
slowrite(st)
register char *st;
{
	int	len, j;
	char c;

	len = strlen(st);
	printmsg( 3, "sent %s", st );
	for (j = 0; j < len; j++) {
		swrite(&st[j], 1);
		ddelay(80000);
	}
}
#endif

/*
 *	scandir
 */

#include "ndir.h"

#define Direct	struct direct

dcmp(a, b)
Direct *a, *b;
{
	return( strcmp(b->d_name, a->d_name) );
}

/*
 *	scandir
 *		scan work dir for C. files matching current remote host (rmtname)
 *
 *	[FJE] There's foom for optimization here:  cache the directory entries
 *		so we don't have to search again.  Primarily an advantage when
 *		running off of floppies.  Same goes for "dscandir()", below.
 *	[FJE] The directory caching has been implemented.  It even worked
 *		first try!!!  There _MUST BE_ something wrong!
 *
 *	return:
 *		A	- abort, couldn't open directory
 *		Y	- at least one file exists, but can't open it
 *		S	- at least one file found, FILE * in "fw"
 *		Q	- no files that match
 */

scandir()
{
	int	fn, len, i;
	char cname[40], tmp[132];

	DIR *dirp;
	struct direct *dp;

#ifdef FJE
	static Direct *cache = 0, *malloc(), *realloc();
	static short count;

	if (count) {
		strcpy(cfile, cache[--count].d_name);
		printmsg( 3, "scandir: uncaching %s", cfile );
		return( -1 );
	} else {
		if (cache)	free(cache);
		cache = malloc(sizeof(Direct));
		count = 0;
	}
#endif /* FJE */
	if ((dirp = opendir( spooldir )) == (DIR *)NULL ) {
		fprintf( stderr, "couldn't open dir %s\n", spooldir );
		return( 'A' );
	}
	sprintf(cname, CALLFILE, rmtname);
	len = strlen(cname);
	while ((dp = readdir(dirp)) != (struct direct *)NULL) {
		if (strnicmp(cname, dp->d_name, len) == SAME) {
#ifndef FJE
			printmsg( 3, "scandir: file = %s", dp->d_name );
			strcpy(cfile, dp->d_name);
			closedir( dirp );
			if ((fw = FOPEN( cfile, "r", 't' )) == (char *)NULL )
				return('Y');
			return('S');
#else
			if ((count % 8) == 0)
				cache = realloc(cache, sizeof(Direct) * (count+8));
			strcpy(cache[count++].d_name, dp->d_name);
			printmsg( 3, "scandir: caching %s", dp->d_name );
#endif /* FJE */
		}
	}
	closedir( dirp );
#ifdef FJE
	if (count) {
		qsort(cache, count, sizeof(Direct), dcmp);
		strcpy(cfile, cache[--count].d_name);
		printmsg(3, "scandir: %hd files; uncaching %s", count+1, cfile);
		return( -1 );
	}
#endif /* FJE */
	return('Q');
}

/*
 *	dscandir -- looks for X.<rmtname> files
 *		scan the directory, [FJE] using a cache of the current
 *		contents.  When the cache is empty, the directory is scanned
 *		again to see if anything new has come in.
 *
 *	returns:
 *		ret(0) if no $SPOOLDIR or no X. files in $SPOOLDIR
 *		ret(-1) if at least one file found
 */

dscandir()
{
	int	fn, len, i;
	char cname[40], tmp[132];
	DIR *dirp;
	Direct *dp;

#ifdef FJE
	static Direct *cache = 0, *malloc(), *realloc();
	static short count;

	if (count) {
		strcpy(cfile, cache[--count].d_name);
		printmsg( 3, "dscandir: uncaching %s", cfile );
		return( -1 );
	} else {
		if (cache)	free(cache);
		cache = (Direct *) malloc(sizeof(Direct));
		count = 0;
	}
#endif /* FJE */
	if ((dirp = opendir( spooldir )) == (DIR *)NULL ) {
		fprintf( stderr, "couldn't open dir %s\n", spooldir );
		return(0);
	}
	sprintf(cname, XQTFILE, rmtname);
	len = strlen(cname);
	while ((dp = readdir(dirp)) != (Direct *)NULL) {
		if ( strnicmp( cname, dp->d_name, len ) == SAME ) {
#ifndef FJE
			printmsg( 3, "dscandir: file = %s", dp->d_name );
			strcpy(cfile, dp->d_name);
			closedir( dirp );
			return( -1 );
#else
			if ((count % 8) == 0)
				cache = realloc(cache, sizeof(Direct) * (count+8));
			strcpy(cache[count++].d_name, dp->d_name);
			printmsg( 3, "dscandir: caching %s", dp->d_name );
#endif /* FJE */
		}
	}
	closedir( dirp );
#ifdef FJE
	if (count) {
		qsort(cache, count, sizeof(Direct), dcmp);
		strcpy(cfile, cache[--count].d_name);
		printmsg(3, "dscandir: %hd files; uncaching %s", count+1, cfile);
		return( -1 );
	}
#endif /* FJE */
	return( 0 );
}
