/*
 *  MBGATE.C - 6/12/87	 - Gateway routines.
 *			- by David B. Toth, VE3GYQ
 *			- if it doesn't work, you can blame it on
 *			   somebody else
 *
 */

#include "mb.h"

char *gm[num_gm];
char *nullstr  ;                    /* Nov11,89 */
static char *lm = "*** LINKED to $S\n";

PORTS *okport(id)
char id;
{
	register PORTS *p;

	if ((p = findport(id)) is NULL) { 
		port->msg = mnport; 
		return NULL; 
	}
	if (p is port)		  { 
		port->msg = mcant;	
		return NULL; 
	}
	if (p->mode isnt idle)	  { 
		port->msg = minuse; 
		return NULL; 
	}
	return p;
}

#if	!CONNUSER_NO
/*
 * connuser: routine to connect to someone in the user file.
 * tcall is passed as the eventual target station, with its
 * user record in tuser.
 */

connuser()
{
	register PORTS *s;
	register short link;
	char gatecall[ln_call];

	pcall(tcall, port->fld[1]);
	rduser(tcall, tuser);
	if (tuser->rn is 0) { 
		port->msg = mfind; 
		return; 
	}

/*
**  If user linked thru a GateWay, connect to the gateway.
*/

	link = false;
	if (tuser->port is 'L')
	{
		if (!*tuser->path) { 
			port->msg = mcant; 
			return; 
		}
		pcall(gatecall, tuser->path);
		rduser(gatecall, tuser); /* read GateWay's call into a user record */
		if (tuser->rn is 0) { 
			port->msg = mfind; 
			return; 
		}
		link = true;
	}

	if ((s = okport(tuser->port)) is NULL) return;
	if (!(s->priv & p_gate)) { 
		port->msg = gm[0]; 
		return; 
	}

	if (link) rduser(gatecall, s->user); 
	else rduser(tcall, s->user);

/*
**  Build the connect line to send to the tnc.
*/

	unbl(port->line, tuser->call, ln_call);

	if (*tuser->path)
		sprintf(tmp->scr, "C %s-%d V %s\n", port->line, tuser->ssid, tuser->path);
	else
		sprintf(tmp->scr, "C %s-%d\n", port->line, tuser->ssid);

	ctnc(s, link);
        log ('G', 'E', ' ', nullstr); /* Nov11,89 */
}
#endif

#if	!CTNCV_NO
/*
 * ctnc: makes the connection between the master and slave ports
 * when the user specifies the port and connect path.
 */

ctncv()
{
	register PORTS *s;

	if ((s = okport(port->opt2)) is NULL) return;
	if (!(s->priv & p_gate)) { 
		port->msg = gm[0]; 
		return; 
	}

	pcall(tcall, port->fld[1]); 
	rduser(tcall, s->user);

	if (port->flds > 2)
		sprintf(tmp->scr,"C %s V %s\n", port->fld[1], port->fld[3]);
	else sprintf(tmp->scr, "C %s\n", port->fld[1]);

	ctnc(s, false);
        log ('G', 'E', ' ', nullstr);  /* Nov11,89 */
}
#endif

#if !(CTNCV_NO & CONNUSER_NO)
ctnc(s, link)
PORTS *s;
short link;
{
	register PORTS *m;

	m = port;
	m->lport = s; 
	s->lport = m;

/*
**  Send the connect string to the tnc.
*/

	s->mode = remote;
	ioport(s);
	contnc(tmp->scr);

/*
**  Tell the user "attempting connect".
*/

	ioport(m); 
	prtx(gm[2]);

/*
**  Special case for in-memory link, in case contnc failed.
*/

	if (s->mode is idle)
	{
		port->msg = gm[3];
		return;
	}

/*
**  Wait for the connection.
*/

	settmr(&m->expire, 60);
	while (chktmr(m->expire))
	{
/*
**  User wants to give up?
*/
		ioport(m);
		if (instat()) if(getdat()) if (*m->line is ctl_w)
		{
                        log ('G', 'A', port->opt2, port->fld[1]);
                        ioport(s);
			distnc();
			s->mode = idle;
			ioport(m);
			port->msg = gm[5];
			return;
		}
/*
**  Something from the slave tnc.
*/
		ioport(s);
		if (instat()) if (getdat())
		{
			if (isdis(s->line))
			{
				waitcmd();
				s->mode = idle;
				ioport(m);
				log ('G', 'A', port->opt2, port->fld[1]);
				port->msg = gm[3];
				return;
			}

			else if (iscon(s->line) or
			    ((s->dev is p_serial) or (port->dev is p_memory) or (s->dev is p_hayes)))
			{

                                log ('G', 'C', m->opt2, m->fld[1]);
                                if (m isnt cport)
				{
					ioport(m); 
					prtx(gm[4]); 
					prtx(lm);
					ioport(s); 
					prtx(lm);
				}
				if (link)
				{
					outstr("G\nC "); 
					outnb(tcall, ln_call); 
					outchar('\n');
				}

                                if (m is cport)
                                     term(m, s, false );    /* Nov11,89 */
                                else tncslinked(m, s);

				ioport(s);
				distnc();
				s->mode = idle;
				m->lport = cport; 
				s->lport = cport;
				ioport(m);
                                log ('G', 'E', ' ', nullstr);   /* Nov11,89 */
				return;
			}
		}
	}
/*
**  Fall through if timer times out.
*/
	ioport(s);
	distnc();
	s->mode = idle;
	ioport(m);
	log ('G', 'A', port->opt2, port->fld[1]);
	port->msg = gm[3];
}
#endif

#if	!MTNCV_NO
/*
 * mtnc: allows monitoring of the slave port by the master port.
 * returns if it sees anything from the master port.
 */

mtncv()
{
	register PORTS *m, *s;
	register int linecount;

	if (port->opt2 is ' ') { 
		shports(); 
		return; 
	}
	if ((s = okport(port->opt2)) is NULL) return;
	if (!(s->priv & p_mon)) { 
		port->msg = gm[0]; 
		return; 
	}

	m = port;
	m->lport = s; 
	s->lport = m;
        log ('G', 'M', port->opt2, nullstr);   /* Nov11,89 */
	prtx(gm[6]);
	ioport(s); 
	onecmd(t_mon);
	settmr(&s->expire, s->mtime);
	linecount = s->mcount;
	while (linecount and chktmr(s->expire))
	{
		ioport(s);
		if (instat()) if (getdat())
		{
			ioport(m);
			outstr(s->line);
			linecount--;
		}

		ioport(m);
		if (instat()) if (getdat()) linecount = 0;
	}
	ioport(s); 
	onecmd(t_moff);
	ioport(m);
	m->lport = cport; 
	s->lport = cport;
        log ('G', 'E', ' ', nullstr);
}
#endif

#if	!UTNCV_NO
/*
 * utnc: allows master port to send unproto frames through the
 * selected slave port. Sets slave port CONOK ON to allow a
 * connect to occur from the slave port.
 */

utncv()
{
	register PORTS *m, *s;

	if (port->opt2 is ' ') { 
		shports(); 
		return; 
	}
	if ((s = okport (port->opt2)) is NULL) return;
	if (!(s->priv & p_mon)) { 
		port->msg = gm[0]; 
		return; 
	}

	m = port;
	m->lport = s; 
	s->lport = m;

        log ('G', 'U', port->opt2, nullstr);
	utnc (m, s);
        log ('G', 'E', ' ', nullstr);

	ioport(m);
	s->mode = idle;
	m->lport = cport; 
	s->lport = cport;
}

utnc(m, s)
PORTS *m, *s;
{
	prtx(gm[1]);
	ioport(s); 
	onecmd(t_con); 
	convtnc();
	settmr(&m->expire, m->ctime);
	while(chktmr(m->expire))
	{
		if (xtalk(m, s))
		{
			if ((*m->line is ctl_w) or (m->mode & discon))
			{
				ioport(s); 
				cmdtnc(); 
				onecmd(t_coff);
				return;
			}
		}

		ioport(s);
		if (instat())
		{
			getcmd();
			if (iscon(s->line))
			{
				s->mode = remote;
				pcall(tcall, port->fld[3]); 
				rduser(tcall, s->user);
				ioport(m); 
				prtx(lm); 
				ioport(s); 
				prtx(lm);
				tncslinked(m, s);
				ioport(s); 
				cmdtnc(); 
				onecmd(t_coff);
				return;
			}
		}
	}
	m->mode = timeout;
}
#endif

/*
 * xtalk: handles the cross-connection of data between tncs.
 * fails with loss of tnc on either side ... lets calling fn
 * clean up the tncs.
 */

xtalk(pf, pt)
PORTS *pf, *pt;
{
	ioport(pf);
	if (instat()) if (getdat())
	{
		if (!(pf->mode & discon)) if (!isdis(pf->line) and (*pf->line isnt ctl_w))
		{ 
			ioport(pt); 
			outstr(pf->line); 
		}
		return true;
	}
	return false;
}

/*
 * tncslinked: always disconnects slave and sets it to idle before
 * returning ... lets calling routine take care of master port
 */

tncslinked(m, s)
PORTS *m, *s;
{
	settmr(&m->expire, m->ctime);
	while(chktmr(m->expire))
	{
		if (xtalk(s, m))
		{
			if (s->mode & discon)
			{
				ioport(s); 
				cmdtnc(); 
				s->mode = idle; 
				return;
			}
			else
			{
				settmr(&m->expire, m->ctime);
			}
		}

		if (xtalk(m, s))
		{
			if ((m->mode & discon) or (*m->line is ctl_w))
			{
				ioport(s); 
				distnc(); 
				s->mode=idle; 
				return;
			}
			else
			{
				settmr(&m->expire, m->ctime);
			}
		}
	}

	/*
 *  On timeout, fall thru, let caller say timeout and dump user.
 */

	ioport(s); 
	distnc(); 
	s->mode = idle;
	ioport(m); 
	m->mode = timeout;
}
