/*
 *  MBFWD.C - 1/31/89 - Autoforwarding.
 *
 *  The target port mode is set to idle if not connected,
 *  set to remote if connected. It is set to disconnect or timeout
 *  by getdat(), but this is checked and fixed up in getln().
 *
 *  All port input is through getln().
 *  All file input is through nxtin().
 */

#include "mb.h"

#ifdef MCH_AMIGA
unsigned short all_number = 0;
extern short debug;
extern char passchr,hfstream,vhfstream,tmpstr[];
FILE *tmp_file;  /*FIX 9*/
#endif

#define deepmax    5     /* Max nesting depth of indirection in fwd.mb */

#define fw_one     0x01  /* Forward to only one MailBox, given in fcall*/
#define fw_anytime 0x02  /* Ignore the times in the "G" item           */
#define fw_abort   0x04  /* Abort forwarding as soon as possible       */
#define fw_tried   0x08  /* Attempted to forward to this MailBox       */
#define fw_tiout   0x10  /* Forward timeout flag                       */
#define fw_forced  0x20  /* Forced forward flag                        */
#define fw_pass    0x40  /* Bypass forward sub file                    */

char  *script;           /* Pointer to where the connect script is     */

byte  fopts;             /* Options, see defines above.                */
char  fwdtyp;
char  *fwdfile;          /* The file name of fwd.mb root file          */
FILE  *ifl[deepmax];     /* File variables for fwd.mb                  */
short deep;              /* Current depth within fwd.mb                */
char  path[80];          /* "G" item saved here, for logging           */
char  fcall[10];         /* Call of the MailBox to forward to          */

/*
 *  YF command: change the name of the forwarding root file.
 */

newfwd()
{
  if (*port->fld[1])
  {
#ifdef MCH_AMIGA
/* FIX 9 */
    if((tmp_file = fopen(port->fld[1],"r")) == NULL) {
       sprintf(tmp->scr,"can't find %s\n",port->fld[1]);
       outstr(tmp->scr);
    }
    else {
      fclose(tmp_file);
#endif
    free(fwdfile);
    fwdfile = strdup(port->fld[1]);
#ifdef MCH_AMIGA
   }
#endif
  }
  sprintf(tmp->scr, "is %s\n", fwdfile);
  outstr(tmp->scr);
}

/*
 *  Add a call to the list of BBS that have messages to be forwarded to them.
 */

addbfwd(cp)
char *cp;
{
  register short i;
  register char *lp;

  for (i = 0, lp = bfwd; i < bfwdn; i++, lp += ln_call)
    if (matchn(cp, lp, ln_call)) return;

  if (bfwdn < bfwdm) { strncpy(lp, cp, ln_call); bfwdn++; }
}

/*
 *  Add a call to the list of users that have unread messges.
 */

addufwd(cp)
char *cp;
{
  register short i;
  register char *lp;

  for (i = 0, lp = ufwd; i < ufwdn; i++, lp += ln_call)
    if (matchn(cp, lp, ln_call)) return;

  if (ufwdn < ufwdm) { strncpy(lp, cp, ln_call); ufwdn++; }
}

/*
 *  Check if a call has a message to be forwarded to it.
 */

findfwd(cp, lp, n)
char *cp, *lp;
short n;
{
  register short i;

  for (i = 0; i < n; i++, lp += ln_call) if (wcm(cp, lp)) return true;
  return false;
}

/*
 *  Add the info from one message header to the lists of calls
 *  of stations that have messages to be forwarded to them.
 *
 *  ufwd - Calls to put in beacon line.
 *  bfwd - Calls that have message TO or AT.
 */

addfwd()
{
  register short i;
  char *p, hcall[6];

  if (!(port->mmhs->stat & (m_busy | m_kill | m_read | m_fwd | m_hold)))
  {
    if (*port->mmhs->bbs is ' ')
    {
#ifdef MCH_AMIGA
      if(matchn("ALL        ",port->mmhs->to,ln_call))
         if(port->mmhs->number > all_number)
            all_number = port->mmhs->number;
#endif
      addufwd(port->mmhs->to);
      addbfwd(port->mmhs->to);
    }
    else
    {
      if (port->mmhs->ext is 1)
      {
        for (i = 0; i < port->mmhs->count; i++) if (port->mmhs->flag[i])
          addbfwd(port->mmhs->call[i]);
      }
      else
      {
        if (matchn(port->mmhs->bbs, cport->user->call, ln_call))
          addufwd(port->mmhs->to);
        else addbfwd(port->mmhs->bbs);
        if (port->mmhs->ext is 2)
        {
          p = port->mmhs->call[0];
          while((p = strchr(p, '.')) isnt NULL)
          {
            p++;
            fill(hcall, ' ', ln_call);
            pcall(hcall, p);
            addbfwd(hcall);
          }
        }
      }
    }
  }
}

/*
 *  Build the two lists of calls of stations that have
 *  messages to be forwarded to them.
 */

bldfwd()
{
  register word  h;

  ufwdn = 0;
  bfwdn = 0;
  read_rec(mfl, 0, (char *)mfhs);
  for (h = mfhs->last; h; h--)
  {
    read_rec(mfl, h, (char *)port->mmhs);
    if (!(port->mmhs->stat & (m_busy | m_kill | m_read | m_fwd | m_hold)))
    addfwd();
  }
}

/*
 *  Return true if the current time is within
 *  the time window for forwarding to this MailBox.
 */

chktime()
{
  register short c, en, st;

  if (fopts & fw_anytime) return true;
  curtim();
  st = 10 * (int)(cport->line[2] - '0') + (int)(cport->line[3] - '0');
  en = 10 * (int)(cport->line[4] - '0') + (int)(cport->line[5] - '0');
  c  = 10 * (int)(l_time[0] - '0') + (int)(l_time[1] - '0');
  if (st <= en) return ((c >= st) and (c <= en));
  return ((c >= st) or (c <= en));
}

/*
 *  Check if it is ok to forward using this list.
 *  Return a pointer to the port to use for forwarding, or NULL.
 */

PORTS *chkfwd()
{
  register PORTS *p;

  if (fopts & fw_one)
  if (!match (fcall, cport->fld[1]))       { passlst(); return NULL; }
  if (!chktime())                          { passlst(); return NULL; }
  if ((p = findport(cport->opt2)) is NULL) { passlst(); return NULL; }
  if (!(p->flags & p_dofwd))               { passlst(); return NULL; }
  if (fopts & fw_abort)                    { passlst(); return NULL; }
  p->mode = idle;
  return p;
}

/*
 *  Check if a disconnect happened.
 */

chkdis()
{
  if (instat()) if (getdat()) if (isdis(port->line))
    { cmdtnc(); port->mode = idle; return true; }
  if (port->flags & p_trans) if (!isdcd())
    { cmdtnc(); port->mode = idle; return true; }
  return false;
}

/*
 *  Read the next input line from FWD.MB,
 *  or from a file referenced from FWD.MB.
 *  Return NULL if no more lines to read.
 */

char *nxtin(p)
char *p;
{
  register char *st, i;

  while (deep >= 0)
  {
    i = 1;
    if ((st = fgets(p, 80, ifl[deep])) is NULL)
    {
      fclose(ifl[deep]);
      deep--;
    }
    else
    {
      if (*p isnt '@') return st;
      if (fopts & fw_pass) return st;
      if (*(p+1) is ' ')
      {
        if (!chktime()) return st;
        i = 6;
      }
      if (deep < (deepmax - 1))
      {
        strupr(p);
        remnl(p);
        deep++;
        if ((ifl[deep] = fopen(p + i, "r")) is NULL) deep--;
      }
    }
  }
  return NULL;
}

/*
 *  Do some DOS commands.
 */
dofdos()
{
  register PORTS *p;

  if((p = findport(cport->opt2)) is NULL) { passlst(); return; }
  if(strlen(cport->fld[0]) > 3) if (!chktime()) { passlst(); return; }
  if(fopts) {
     if(!((fopts & fw_one) and (match(fcall, cport->fld[1])))) {
        passlst();
        return;
     }
  }
/*
 *  Just in case, flush whatever might be currently open.
 */

  clnuser();
  clnmsg();
  clnlog();

  while (nxtin(cport->line) isnt NULL)
  {
    if (iseof(cport->line)) return;
    remnl(cport->line);
#ifndef MCH_AMIGA
    printf("\nRunning: %s\n", cport->line);
    if (system(cport->line)) perror("Error");
#else
    sprintf(tmpstr,"\nRunning: %s\n", cport->line);
    ttputs(tmpstr);
   /* MANX doesn't like above syntax .. gotta explicitly generate address */
    if (system(&cport->line[0])) perror("Error");
#endif
  }
}
/*#endif*/
/*
 *  The entry to fowarding.
 */

fwd()
{
  register PORTS *p;

/*
 *  If no fwd.mb file, then do nothing.
 */

  deep = 0;
  if ((ifl[deep] = fopen(fwdfile, "r")) is NULL) return;

  bldfwd();

  script = tmp->scr;
  *script = '\0';

  while(nxtin(cport->line) isnt NULL)
  {
    bidok = false;
    hidok = false;
    ioport(cport);
    strupr(cport->line);
    parse();
    fwdtyp = cport->opt1;
    switch (fwdtyp)
    {
      case 'E':
        passlst();
        script = tmp->scr;
        *script = '\0';
        break;
      case 'D':
      case 'F':
      case 'G':
      case 'H':
        if ((p = chkfwd()) isnt NULL)
        {
          fopts clrbit fw_tried;
/*
 *  Save tail of list header for logging.
 */
          strcpy(path, cport->line + 6);
          remnl(path);
          script = tmp->scr;
          if (!*script)
          {
            *script = 'C';
            strcpy(script + 1, cport->line + 6);
            *(script + strlen(script) + 1) = '\0';
          }
          dofwd(p);
          dorev(p);
          distnc();
          p->mode = idle;
          ioport(cport);
        }
        script = tmp->scr;
        *script = '\0';
        break;

      case 'P':
        dopar();
        break;

      case '!':
        dofdos();
        break;

      case 'C':
      case 'R':
      case 'S':
        strcpy(script, cport->line);
        script += (strlen(cport->line) + 1);
        *script = '\0';
        break;

      default:  break;
    }
  }

/*
 *  Put us back on the port we were on when we got here.
 */

  ioport(cport);
  bidok = false;
  hidok = false;
}

/*
 *  A and AI commands.
 */
#ifndef MCH_AMIGA
all_fwd()
{
  byte pflg;
  port->opt1 = 'X';
  putcomd( port->opt1, port->opt2 );

  pflg = getp_flag();
  putc_flag (pflg);
  port->mode = logout;
}
#endif
/*
 *  X and XI commands.
 */

sfwd()
{
  register PORTS *p;

/*
 *  If remote sysop did the command, just remember it for later.
 */

  if (port->mode & sysop) { savecmd(); return; }

/*
 *  Set up parameters for fwd()
 */

  fopts = fw_forced;
  for (p = porthd; p isnt NULL; p = p->next)
  {
    p->ec = p->ecuser;
    p->flags setbit p_dofwd;
  }

  if (cport->flds is 2)
  {
    fopts setbit fw_one;
    strncpy (fcall, cport->fld[1], ln_callp);
  }

  if (cport->opt2 is 'I') fopts setbit fw_anytime;

  fwd();

/*
 *  Reset the port flags.
 */

  for (p = porthd; p isnt NULL; p = p->next)
  {
    p->ec = p->ecmon;
    p->flags clrbit p_dofwd;
  }
}

/*
 *  Automatic forwarding.
 *  Called once each minute when the MailBox is idle.
 */

afwd(curmin)
int curmin;
{
  register PORTS *p;
  register short anyfwd;

/*
 *  Mark the ports that should forward at this minute.
 */

  fopts = 0;
  anyfwd = false;
  for (p = porthd; p isnt NULL; p = p->next) if (p->fwdmin is curmin)
  {
    p->flags setbit p_dofwd;
    p->ec = p->ecuser;
    anyfwd = true;
  }

/*
 *  If any ports forward at this minute, do the forwarding.
 */

  if (anyfwd)
  {
    setbusy();
    alloff();   /* Turn off connects and monitoring */
    if (s_flag & s_dv) begin_lock();
    readmsg();
    readusr();

/*
 *  Write out mon and calls heard files
 */
    clsmon();

/*
 *  Once a day, do wp and stale
 */
    if (!matchn(ufhs->wpdate, l_date, ln_date))
    {
      port->opt1 = '\0';
      port->flds = 1;
      dwuser();
      stale();
      strncpy(ufhs->wpdate, l_date, ln_date);
      write_rec(ufl, 0, (char *)ufhs);
    }
    if (s_flag & s_dv) end_lock();

/*
 * Check and see if the mailfile should be compressed
 */
    if (s_param & s_unt)
    if (!matchn(mfhs->date, l_date, ln_date))
    if (unt_hr <= (10 *(l_time[0] - '0') + (l_time[1] - '0')))
    {
      port->opt2 = 'A';
      setunt();
    }

    fwd();
    clnlog();
    setfwd();
    clsmsg();
    clsusr();
    allon();    /* Turn on monitoring and connects */
    clrbusy();
  }

/*
 *  Reset the port flags.
 */

  for (p = porthd; p isnt NULL; p = p->next)
  {
    p->ec = p->ecmon;
    p->flags clrbit p_dofwd;
  }
}

/*
 *  Reverse forwarding.
 *  Accept messages from the remote MailBox after forwarding to it.
 */

dorev(p)
PORTS *p;
{
  ioport(p);

  if (fwdtyp is 'G') return;
  if (fwdtyp is 'D') return;

  if (p->mode & idle)
  {
    if (fwdtyp is 'F') return;
    if (fwdtyp is 'H') if (fopts & fw_tried) return;
    cmb();
    if (p->mode & idle) return;
  }

  pcall(fcall, path + 2);
  rduser(fcall, p->user);
  log ('M', 'F', 'R', path);  /* Log start of reverse forward */

  if (p->tmode)
  {
     cmdtnc();
     trantnc();
  }

  while(true)
  {
    outstr("F>\n");
    getln();
    if (p->mode & idle) return;
    parse();

    if (p->opt1 isnt 'S') return;
    {
      sndmsg();
      if (p->mode & gone) return;
      addfwd();
    }
  }
}

/*
 *  F> command: do reverse forward to logged user.
 */

fwdcmd()
{
  register PORTS *p;
  register short ok;

/*
 *  If no fwd.mb file, then do nothing.
 */

  p = port;
  deep = 0;
  if ((ifl[deep] = fopen(fwdfile, "r")) is NULL) { p->mode = forced; return; }

  ioport(cport);

/*
 *  Find the users E, F, G, or H list in the fwd.mb file.
 */

  ok = false;
  while(!ok and (nxtin(cport->line) isnt NULL))
  {
    strupr(cport->line);
    parse();
    fwdtyp = cport->opt1;

    switch(fwdtyp)
    {
      case 'D' :
      case 'E' :
      case 'F' :
      case 'G' :
      case 'H' :
        pcall(fcall, cport->fld[1]);
        ok = matchn(fcall, p->user->call, ln_call);

        if (!ok) passlst();
        break;

      case '!' :
      case 'P' :
        passlst();
        break;

      default  : ;   /* 'C', 'R', 'S' */
    }
  }

  ioport(p);

  if (ok)
  {
    log ('M', 'F', 'S', nullstr);      /* Log start of forwarding */
    bldfwd();
    remnl(cport->line);
    strcpy( path, cport->line+6 );
    dofwd(p);
    log ('M', 'F', 'E', nullstr);      /* Log end of forwarding */
  }

  while (deep >= 0) { fclose(ifl[deep]); deep--; }
  if(p->mode is idle) return;
  outstr("*** Done.\n");
}

/*
 *  Pass a sublist in the forwarding file.
 */

passlst()
{
  fopts setbit fw_pass;
  while (true)
  {
    if (nxtin(cport->line) is NULL) {fopts clrbit fw_pass; return;}
    if (iseof(cport->line)) {fopts clrbit fw_pass; return;}
  }
}

/*
 *  Set tnc parameters.
 */

dopar()
{
  register PORTS *p;

  if ((p = findport(cport->opt2)) is NULL) { passlst(); return; }

  if (strlen(cport->fld[0]) > 3) if (!chktime()) { passlst(); return; }

  ioport(p);
  while (nxtin(cport->line) isnt NULL)
  {
    if (iseof(cport->line)) { ioport(cport); return; }
    onetnc(cport->line);
  }
  ioport(cport);
}

/*
 *  Get a line from the current port.
 *  Handle disconnect, timeout, and ^F.
 */

getln()
{
  while (!getdat());
  if (port->mode & forced) fopts setbit fw_abort;
  if (port->mode & gone) { distnc(); port->mode = idle; }
}

/*
 *  Eat the remote MailBox prompt.
 */

eat()
{
  register char c;

  if (fwdtyp is 'D') c = ':'; else c = '>';
  while(true) {
    getln();
    if (!(port->mode & remote)) return;
    if (port->line[0] is '[') isbid();
    if ((port->mode & remote) and (port->line[strlen(port->line) - 2] is c)) {
      return;
    }
  } 
}

/*
 *  Connect to a remote Mailox.
 *  Port mode idle at entry.
 *  On return, port mode is remote if the connect worked, else idle.
 */

cmb()
{
  port->flags setbit p_dotmr;
  port->mode = remote;

/*
 *  Execute the connect script.
 */

  while(*script)
  {
    switch (*script++)
    {
      case 'C' :
        contnc(script);
        if (port->mode & idle) return;
        if (port->dev is p_tnc) do
        {
          getln();
          if (port->mode & idle) return;
        }
        while (!iscon(port->line));
        break;

      case 'S' :
        outstr(script);
        break;

      case 'R' :
        getln();
        if (port->mode & idle) return;
        strupr(port->line);
        if (*script isnt '!')
        if (!search(port->line, script, strlen(script)-1))
          { distnc(); port->mode = idle; return; }
        break;

       default: ;
    }
    while(*script++);  /* Move to next script line */
  }

/*
 *  In theory, now connected to MailBox. Eat login messge.
 */

  eat();
  if (!(port->mode & remote)) return;
  if (bidok) {
#ifndef MCH_AMIGA
    outstr("[CBBS-6.0-H$]\n");
#else
   send_title();
#endif
    eat();
  }
}

/*
 *  Forward whatever messages go to this MailBox.
 */

dofwd(p)
PORTS *p;
{
  register char *st;

  st = nxtin(cport->line);
  while (!iseof(cport->line) and (st isnt NULL))
  {
    ioport(cport);
    parse();
    ioport(p);
    pcall (tcall, cport->fld[0]);

    if (findfwd(tcall, bfwd, bfwdn))
    {
      fmsg();
      if (fopts & fw_tried) if (p->mode & idle)
      {
         p->mmhs->stat clrbit m_busy;
         wt_mmhs();
         passlst();
         return;
      }
    }

    st = nxtin(cport->line);
  }
}

/*
 *  Send the message text.
 */

xmtmsg()
{
  register FILE *fl;
#ifdef MCH_AMIGA
   register char *cp;
#endif

  sprintf(port->line, "%s%u", msgdir, port->mmhs->number);
  if ((fl = fopen(port->line, "r")) is NULL) nofile(port->line); else
  {
    fseek(fl, (long)RECSIZE, 0);
    while(fgets(tmp->scr, scrmax, fl) isnt NULL)
    {
      if (chkdis()) { fclose(fl); return false; }
#ifdef MCH_AMIGA
      cp = tmp->scr;
      while(*cp) {
         /* insert the pass character for stream characters */
         if((*cp == vhfstream) && passchr) {
            outchar(passchr);
         }
         else {
            if((*cp == hfstream) && passchr) {
               outchar(passchr);
            }
         }
         /* Don't send fancy ctl chars such as ^Q ^S etc.*/
         if(*cp < 0)continue;
         if((*cp == '\021') || (*cp == '\023'))continue;
         outchar(*cp++);
      }
#else
      outstr(tmp->scr);
#endif
    }
    fclose (fl);
  }
  if (fwdtyp is 'D')
  {
    outstr(".\n");
    outstr("y\n");
  }
  else
  {
    outchar (cpmeof);
    outchar ('\n');
  }
  return true;
}

/*
 *  Forward all messages TO or AT tcall
 *  to the MailBox we are connected to.
 */

fmsg()
{
  char *a, *b, *c;
  char hcall[6];
  register PORTS *p;
  register word h;
  register short i, ok, kill, extnr, kok, hasit;
 
  hasit = true;   /* just initialize it to something */

  p = port;
  for (h = mfhs->last; h; h--)
  {
    if (s_flag & s_dv) begin_lock();
    read_rec(mfl, h, (char *)p->mmhs);

    ok = (!(p->mmhs->stat & (m_kill | m_read | m_fwd | m_hold | m_busy)));
/*
 *  Decide whether to forward this one.
 */
    if (ok)
    {
      if (p->mmhs->ext is 1)
      {
        ok = false;
        for (i = 0; !ok and (i < p->mmhs->count); i++) if (p->mmhs->flag[i])
          if (wcm(tcall, p->mmhs->call[i])) { ok = true; extnr = i; }
      }
      else if (*p->mmhs->bbs isnt ' ')
      {
        ok = wcm(tcall, p->mmhs->bbs);
        if (!ok) if (p->mmhs->ext is 2)
        {
          c = p->mmhs->call[0];
          while(!ok and ((c=strchr(c, '.')) isnt NULL))
          {
            c++;
            fill(hcall, ' ', ln_call);
            pcall(hcall, c);
            ok = wcm(tcall, hcall);
          }
        }
      }
      else ok = wcm(tcall, p->mmhs->to);
    }

    if (ok)
    {
      p->mmhs->stat setbit m_busy;
      wt_mmhs();
    }
    if (s_flag & s_dv) end_lock();

/*
 *  If we DO forward this one, forward this one.
 */
    if (ok)
    {
      if (fopts & fw_tiout) { wait (p->ftime); fopts clrbit fw_tiout; }
      fopts setbit fw_tried;
      if (p->mode & idle) cmb();
      if (p->mode & idle) return;

      if (fwdtyp is 'D') prtx("mail $G\n");

      if (*p->mmhs->bbs is ' ') prtx("S$B $G < $P");
      else if((hidok)and(p->mmhs->ext is 2)) prtx("S$B $G @ $h < $P");
      else prtx("S$B $G @ $A < $P");

      if (bidok) if (*p->mmhs->bid isnt ' ') {
        outstr(" $");
        a = p->line;
        b = p->mmhs->bid;
        unbl(a, b, ln_bid);
        outstr(p->line);
      }
      outchar('\n');
      getln(); if (p->mode & idle) return;
      if (bidok)
      {
        if(*p->line < ' ') { getln(); if(p->mode & idle) return; }
        switch(*p->line)
        {
          case 'O': hasit = false;
                    outstr(p->mmhs->title);
                    outchar('\n'); break;
          case 'N': hasit = true; break;
          default : p->mmhs->stat clrbit m_busy;
                    wt_mmhs();
                    return;
        }
      }
      else
      {
        hasit = false;        /* you'll want to send msg */
        outstr(p->mmhs->title); outchar('\n');
        if (fwdtyp isnt 'D')
        {
          getln(); if (p->mode & idle) return;
        }
      }
      if (!hasit)
      {
        curtim();
        if (port->dev is p_tnc) prtx(mm[4]);
        if (!xmtmsg()) { fopts setbit fw_tiout; return;}
      }
      eat(); if (p->mode & idle){ fopts setbit fw_tiout; return; }
/*
 *  The message actually forwarded.
 *  Kill it, mark it, or whatever.
 */
      sprintf(p->line, "%u %s", p->mmhs->number, path);
      log ('M', 'F', ' ', p->line);

      kill = false;
      kok = false;
/*
 *  If cc: list exists, mark this call as forwarded.
 */
      if (p->mmhs->ext is 1)
      {
        p->mmhs->flag[extnr] = false;
        for (i = 0; i < p->mmhs->count; i++) if (p->mmhs->flag[i]) kok = true;
      }

      if (!kok)
      {
        if ((p->mmhs->type is 'F') or (p->mmhs->type is 'B'))
        kill = s_param & s_fkill;
        else kill = s_param & s_kill;
      }

      if (kill) p->mmhs->stat setbit m_kill;
      else
        if (!kok) p->mmhs->stat setbit m_fwd;
      p->mmhs->stat clrbit m_busy;
      write_rec(mfl, p->mmhs->rn, (char *)p->mmhs);
      makehdr2();
    }
  }
}
