
/*
 *  MBGATE.C - 1/26/88 - 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];

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;
}

/*
 *  C Command: 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]);
  if (!rduser(tcall, tuser)) { 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);
/*
 *  Read GateWay's call into a user record.
 */
    if (!rduser(gatecall, tuser)) { port->msg = mfind; return; }
    link = true;
  }

  if ((s = okport(tuser->port)) is NULL) return;
  if (port->mode & remote) 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);

  s->ec = s->ecuser;
  ctnc(s, link);
  s->ec = s->ecmon;
  log ('G', 'E', ' ', nullstr);
}

/*
 *  C Command: make 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]);

  s->ec = s->ecuser;
  ctnc(s, false);
  s->ec = s->ecmon;
  log ('G', 'E', ' ', nullstr);
}

ctnc(s, link)
PORTS *s;
short link;
{
  register PORTS *m;
  int open_gate;
  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)
  {
    m->msg = gm[3];
    return;
  }

/*
 *  Wait for the connection.
 */

  settmr(&m->expire, 60);
  while (chktmr(m->expire))
  {
/*
 *  User wants to give up?
 */
    ioport(m);
    open_gate = false;
    if (instat()) if(getdat()) if (*m->line is ctl_w) open_gate = true;
    if (!isdcd()) open_gate = true;
    if (open_gate)
    {
      log ('G', 'A', m->opt2, m->fld[1]);
      ioport(s); distnc();
      s->mode = idle;
      ioport(m);
      m->msg = gm[5];
      return;
    }
/*
 *  Something from the slave tnc.
 */
    ioport(s);
    if (instat()) if (getdat())
    {
      if (isdis(s->line))
      {
        waitcmd(0);
        s->mode = idle;
        ioport(m);
        log ('G', 'A', m->opt2, m->fld[1]);
        m->msg = gm[3];
        return;
      }

      else if (iscon(s->line) or (s->dev is p_serial))
      {
        log ('G', 'C', m->opt2, m->fld[1]);

        if (m isnt cport)
        {
          ioport(m); prtx(gm[4]); prtx(lm);
          ioport(s); wait(10); prtx(lm);  /* wait 10 sec. to send msg */
        }

        if (link)
        {
          outstr("G\nC "); outnb(tcall, ln_call); outchar('\n');
        }

/*
 *  Special case of connecting
 *  from the console.
 */
        if (m is cport)
        {
          m->fl = NULL;
          term(s);
          ioport(s);
          tncstate();
          if (s->mode is discon)
          {
            s->mode = idle;
            m->lport = cport; s->lport = cport;
            ioport(m);
            return;
          }
          else    /* just go back to cmd line and leave slave connected */
          {
            convtnc();
            s->mode = idle;
            ioport(m);
            return;
          }
        }

        else tncslinked(m, s);

        ioport(s);
        distnc();
        s->mode = idle;
        m->lport = cport; s->lport = cport;
        ioport(m);
        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];
}

/*
 *  M command: Monitor the slave port from the master port.
 *  return if 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);
  prtx(gm[6]);
  ioport(s);
  onecmd(t_mon);
  settmr(&s->expire, s->mtime);
  linecount = s->mcount;
  s->ec = s->ecuser;
  while (linecount and chktmr(s->expire))
  {
    ioport(s);
    if (instat()) if (getdat())
    {
      ioport(m);
      outstr(s->line);
      linecount--;
    }

    ioport(m);
    if (m->flags & p_trans) if(!isdcd()) linecount = 0;
    if (instat()) if (getdat()) linecount = 0;
  }

  s->ec = s->ecmon;
  ioport(s);
  onecmd(t_moff);
  ioport(m);
  m->lport = cport;
  s->lport = cport;
  log ('G', 'E', ' ', nullstr);
}

/*
 *  U Command: master port sends 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;
  log ('G', 'U', m->opt2, nullstr);
  m->lport = s;
  s->lport = m;
  s->ec = s->ecuser;
  utnc (m, s);
  s->ec = s->ecmon;
  s->mode = idle;
  m->lport = cport;
  s->lport = cport;
  ioport(m);
  log ('G', 'E', ' ', nullstr);
}

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;
}

/*
 * 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;
  }
  if (pf->flags & p_trans)
    if (!isdcd())
      {
      pf->mode = discon;
      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;
}
