/*  Try to make this store all collection list in script to save I/O.
 *  MBFWD.C - 6/28/91 - 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 char fbb_direction,conn_direction;
extern short debug;
extern char tmpstr[];
extern char passchr,hfstream,vhfstream;
FILE *tmp_file;  /*FIX 9*/
extern short thebox,kambbs;
extern char hostport;
#define deepmax    8     /* Max nesting depth of indirection in fwd.mb */
#else
#define deepmax    5     /* Max nesting depth of indirection in fwd.mb */
#endif

#define fw_one     0x0001  /* Forward to only one MailBox, given in fcall*/
#define fw_anytime 0x0002  /* Ignore the times in the "G" item           */
#define fw_abort   0x0004  /* Abort forwarding as soon as possible       */
#define fw_tried   0x0008  /* Attempted to forward to this MailBox       */
#define fw_tiout   0x0010  /* Forward timeout flag                       */
#define fw_forced  0x0020  /* Forced forward flag                        */
#define fw_pass    0x0040  /* Bypass forward sub file                    */
#define fw_idle    0x0080  /* Manual forward to idle                     */
#define fw_ecall   0x0100  /* Forced forward ecall                       */

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

word  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 */
/* complain if specified fwdfile can't be found */
      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
         titles();
      }
#endif
   }
   sprintf(tmp->scr, "is %s\n", fwdfile);
   outstr(tmp->scr);
}

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

newffwd()
{
   if(*cport->fld[1]) {
      free(fwdfile);
      fwdfile = strdup(cport->fld[1]);
   }
#ifndef MCH_AMIGA
   printf("<File is %s>\n", fwdfile);
#else
   titles();
#endif
}

/*
 *  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 ' ') {
         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;
#ifdef MCH_AMIGA
   int flag;
#endif

   ufwdn = 0;
   bfwdn = 0;
   read_rec(mfl, 0, (char *)mfhs);
#ifdef MCH_AMIGA
   check_mail();
   unlock_mail();
#endif
   for(h = mfhs->last; h; h--) {
      read_rec(mfl, h, (char *)port->mmhs);
#ifdef MCH_AMIGA
      flag = 0;
/* Find highest numbered message to ALL which isn't busy, killed or held */
      if(!(port->mmhs->stat & (m_busy | m_kill | m_hold))) {
         if(matchn("ALL        ",port->mmhs->to,ln_call)) {
            flag = 1;
            if(port->mmhs->number > all_number) {
               all_number = port->mmhs->number;
            }
         }
      }
/* NOTE THAT THIS NEXT IF statement affects the IF outside this #ifdef
   So be careful about modifying this code

   Don't do an addfwd if there's an ALL.
*/
   if(!flag)
#endif
      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;
   }
#ifdef MCH_AMIGA
/* Don't do this if the port doesn't forward! */
   if((p->flags & p_dofwd) == 0) {
      passlst();
      return;
   }
#endif
   if(strlen(cport->fld[0]) > 3)
      if(!chktime()) {
         passlst();
         return;
      }
   if(fopts & fw_forced)
      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(do_system(&cport->line[0])) perror("Error");
#endif
   }
}

/*
 *  The entry to forwarding.
 */
#ifdef MCH_AMIGA
/* Only for export to see if output file is length zero */
#include <stat.h>
/* Temporarily avoid the problem of using tmp->scr */
char callist[1200];
/* Some variables for the export/import functions */
extern FILE *eximfile;
extern int export,import;
#endif
fwd()
{
   register PORTS *p;
#ifdef MCH_AMIGA
   struct stat exstat;
   char callsign[10];
   char eximname[100];
   register char *cp,*st;
   export = import = 0;
#endif

/*
 *  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) {
#ifdef MCH_AMIGA
      kambbs = thebox = 0;
      if(!fbb_direction)s_mart = 0;
#else
      s_mart = 0;
#endif
      ioport(cport);
      strupr(cport->line);
      parse();
      fwdtyp = cport->opt1;
      switch(fwdtyp) {
      case 'E':
         passlst();
         script = tmp->scr;
         *script = '\0';
         break;
#ifdef MCH_AMIGA
      case 'I':   /* Import function falls into export function */
         import = 1;
      case 'X':   /* Export function - falls through to D F G H */
         /* Get our callsign */
         unbl(callsign,cport->user->call,6);
         eximfile = 0;
         /* If a directory spec isn't given then use ram: */
         if(cport->flds == 3) {
            strcpy(eximname,cport->fld[2]);
            cp = &eximname[strlen(eximname)-1];
            if((*cp != ':') && (*cp != '/')) {
               cp++;
               *cp++ = '/';
               *cp = 0;
            }
         }
         else 
            strcpy(eximname,"ram:");
         if(import) sprintf(tmpstr,"%s-%s.exc",callsign,cport->fld[1]);
         else       sprintf(tmpstr,"%s-%s.exc",cport->fld[1],callsign);
         strcat(eximname,tmpstr);
         if(!import)export = 1;
#endif
      case 'D':
      case 'F':
      case 'G':
      case 'H':
         if((p = chkfwd()) isnt NULL) {
#ifdef MCH_AMIGA
            if(export || import) {
               if(export)
                  eximfile = fopen(eximname,"a");
               else
                  eximfile = fopen(eximname,"r");
               if(eximfile == NULL)goto exim_bypass;
               s_mart = bidok | midok | hidok;
            }
#endif
            fopts clrbit fw_tried;
/*
 *  Save tail of list header for logging.
 */
            strcpy(path, cport->line + 6);
            remnl(path);
#ifndef MCH_AMIGA
            strcpy(fcall, cport->fld[1]);
#else
            pcall(fcall,cport->fld[1]);
            /* Make it look as though import/export is logged in */
            if(import || export)p->mode = remote;

            /* This next bit generates a connect script if there
               isn't one already there. An import or export won't call
               cmb() anyway.
            */
#endif
            script = tmp->scr;
            if(!*script) {
               *script = 'C';
               strcpy(script + 1, cport->line + 6);
               *(script + strlen(script) + 1) = '\0';
            }
#ifndef MCH_AMIGA
            dofwd(p);
            dorev(p);
            distnc();
#else
            /* callist points to start of call collection list
               Read all of the calls for this station into memory.
               This allows me to make one pass through the mail.dat file
               instead of one pass for each call that has outgoing mail.
            */
            callist[0] = 0;
            st = nxtin(cport->line);
            /* Check for the filesize limiter */
            filesize = 0;
            if(*cport->line == '<') {
               if(chktime())
                  if(!(fopts & fw_anytime))
                     filesize = atoi(&cport->line[6]);
               st = nxtin(cport->line);
            }
            cp = callist;
            if(fopts & fw_ecall) {
               pcall(cp,tcall);
               cp += ln_call;
               *cp = 0;
            }
            while(!iseof(cport->line) and (st isnt NULL)) {

               pcall(cp,cport->line);
               cp += ln_call;
               *cp = 0;
               st = nxtin(cport->line);
            }
            user_title(fcall);
            /* chkfwd() forces idle ... have to set it to remote for
               a BBS which has called us
            */
            if(fbb_direction)p->mode = remote;
            if(!import)dofwd(p);
            if((s_mart & fbbok) == 0) {
               if(!export)dorev(p);
            }
            user_title("");
            if(!import && !export)distnc();
#endif
            p->mode = idle;
            ioport(cport);
         }
#ifdef MCH_AMIGA
         if(eximfile)fclose(eximfile);
         eximfile = 0;
         /* If we imported a file then remove it */
         if(import) {
            remove((const char *)eximname);
         }
         /* Otherwise check for a zero length export file */
         else {
            if(export) {
               stat(eximname,&exstat);
               if(exstat.st_size == 0)remove((const char *)eximname);
            }
         }
exim_bypass:
         import = export = 0;
#endif
         script = tmp->scr;
         *script = '\0';
         break;

      case 'P':
         dopar();
         break;

      case '!':
         dofdos();
         break;

      case 'Y':
#ifdef MCH_AMIGA
/* Only do the 'YF' at the end of the file if this is NOT the console
   port forwarding time
*/
         if((cport->flags & p_dofwd) == 0)
#endif
         if((cport->opt2 is 'F') and (chktime() ))
            newffwd();
         break;

#ifdef MCH_AMIGA
      case 'L':
      case 'N':
      case 'W':
#endif
      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);
#ifdef MCH_AMIGA
   kambbs = thebox = 0;
   conn_direction = ' ';
   user_title("");
#endif
   s_mart = 0;
}

/*
 *  A and AI commands.
 */

#ifndef MCH_AMIGA
/* Moved to mb.c so I don't have to make a SYSOP version of mbfwd just for
   this one routine
*/
all_fwd()
{
   word 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 > 1) {
      fopts setbit fw_one;
      strncpy (fcall, cport->fld[1], ln_callp);
   }
   if(cport->flds is 3) {
      fopts setbit fw_ecall;
      pcall(tcall, cport->fld[2]);
   }

   if((cport->opt2 is 'I')or(cport->opt2 is 'B'))
      fopts setbit fw_anytime;
   if(cport->opt2 is 'B')
      fopts setbit fw_idle;
#ifdef MCH_AMIGA
   conn_direction = '>';
/* Tell fbb that we called */
   fbb_direction = 0;
#endif
   fwd();

/*
 *  Reset the port flags.
 */

   for(p = porthd; p isnt NULL; p = p->next)  {
      p->ec = p->ecmon;
      p->flags clrbit p_dofwd;
   }
   if(fopts & fw_idle) bye();

}

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

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

#ifdef MCH_AMIGA
/* FIX 14 The setunt routine can be called with the TNC as the port
          and in that case it will send all output from an automatic
          untangle to the TNC as well as the console.
*/
   register PORTS *saveport;

   saveport = port;
   conn_direction = '>';
/* Tell fbb that we called */
   fbb_direction = 0;
#endif

/*
 *  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) {

#ifdef MCH_AMIGA
/* Console only forwards from the process running port A */
         if((p->id == 'Z') && (hostport != 'A'))continue;
#endif

         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;
         if(s_flag & s_dv) end_lock();
#ifdef MCH_AMIGA
         lock_user();
#endif
         dwuser();
         if(s_flag & s_dv) begin_lock();
         stale();
         strncpy(ufhs->wpdate, l_date, ln_date);
         write_rec(ufl, 0, (char *)ufhs);
#ifdef MCH_AMIGA
         unlock_user();
#endif
      }
      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')))
      {
#ifdef MCH_AMIGA
         ioport(cport);
#endif
         port->opt2 = 'A';
         setunt();
#ifdef MCH_AMIGA
         ioport(saveport);
#endif
      }

      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;
{
   char rcall[6];

   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(rcall, path + 2);
   rduser(rcall, p->user);
   log('M', 'F', 'R', path);  /* Log start of reverse forward */
#ifdef MCH_AMIGA
   user_title(p->user->call);
   /* dorev can be called if dofwd does not find anything to send!
      Call fbbfwd before setting transparent mode ... it's done in
      fbbfwd.
   */
   if(s_mart & fbbok) {
      fbb_direction = 0;
      fbbfwd(0);
      return;
   }

   if(!import && p->tmode) {
      cmdtnc();
      trantnc();
   }
#else

   if(p->tmode) {
      cmdtnc();
      trantnc();
   }
#endif
   while(true) {
#ifndef MCH_AMIGA
      outstr("F>\n");
      getln();
      if(p->mode & idle) return;
#else
      if(!import)outstr("F>\n");
      while(1) {
         getln();
         if(p->mode & idle) return;
         /* getdat will return a null if the end of file is reached */

         /* Remove comments */
         if(p->line[0] != ';')break;
      }
      if(import && (p->line[0] == 0))break;
#endif
      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;
#ifdef MCH_AMIGA
   register char *st,*cp;
#endif

/*
 *  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();
#ifdef MCH_AMIGA
         else {
            /*  callist = script + strlen(script)+1; */
            st = nxtin(cport->line);
            /* Check for the filesize limiter */
            filesize = 0;
            callist[0] = 0;
            if(*cport->line == '<') {
               if(chktime())
                  if(!(fopts & fw_anytime))
                     filesize = atoi(&cport->line[6]);
               st = nxtin(cport->line);
            }
            cp = callist;
            if(fopts & fw_ecall) {
               pcall(cp,tcall);
               cp += 6;
               *cp = 0;
            }
            while(!iseof(cport->line) and (st isnt NULL)) {
               pcall(cp,cport->line);
               cp += 6;
               *cp = 0;
               st = nxtin(cport->line);
            }
         }
#endif
         break;

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

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

   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;
#ifdef MCH_AMIGA
   /* It seems that FBB does not like "Done" so we'll just drop out */
   if(s_mart & fbbok)
      distnc();
   else
      outstr("*** Done.\n");
#endif
}

/*
 *  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;
   }
#ifdef MCH_AMIGA
/* Don't do this if the port doesn't forward at this time */
   if((p->flags & p_dofwd) == 0) {
      passlst();
      return;
   }
#endif

   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()
{
#ifdef MCH_AMIGA
/* Some stuff required for my extra script commands */
   register char *loop;       /* Address of beginning of loop */
   register int limit;        /* Loop counter */
   register int i;

   loop = 0;
#endif
   port->flags setbit p_dotmr;
   port->mode = remote;

/*
 * Execute the connect script.
 */
   while(*script) {
      switch(*script++) {
#ifdef MCH_AMIGA
      /* Loop at most N times - no nesting allowed */
      case 'L':
         loop = script;
         limit = atoi(loop);
         if(limit <= 0)limit = 1;
         break;
      /* Next count of loop */
      case 'N':
         limit--;
         /* If counted down to zero then exit loop with failure */
         if(limit == 0) {
            distnc();
            port->mode = idle;
            return;
         }
         /* Otherwise reset loop to try again */
         script = loop;
         break;
      /* Delay N seconds - default is one */
      case 'W':
         i = atoi(script);
         if((i <= 0) || (i > 60))i = 60;
         Delay(50L*i);
         break;
#endif
      case 'C':
         contnc(script);
         if(port->mode & idle) return;
#ifndef MCH_AMIGA
         if(port->dev is p_tnc)
            do {
               getln();
               if(port->mode & idle) return;
            }
         while(!iscon(port->line));
#else
/* Gotta put the thing into transparent mode if it is required */
         if(port->dev is p_tnc) {
            do {
               getln();
               if(port->mode & idle) return;
            } while(!iscon(port->line));
            if(port->tmode) {
               cmdtnc();
               trantnc();
            }
         }
#endif
         break;

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

      case 'R':
         getln();
         if(port->mode & idle) return;
         strupr(port->line);
         if(*script isnt '!')
#ifndef MCH_AMIGA
            if(!search(port->line, script, strlen(script)-1))
#else
/* The new Manx 5.0 compiler uses a LONG for strlen AND for
   sizeof so must change things a bit
*/
            if(!search(port->line, script, (short)strlen(script)-1))
#endif
            {
#ifdef MCH_AMIGA
/* If we are in a loop then failure just means keep going in the loop */
               if(loop) break;
#endif
               distnc();
               port->mode = idle;
               return;
            }
#ifdef MCH_AMIGA
/* And success means exit the loop */
            else {
               if(loop) {
                  while(1) {
                  while(*script++);
                     if(*script == 'N')break;
                  }
                  loop = 0;
               }
            }
#endif
         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(s_mart) {
      outstr(vers);
#ifdef MCH_AMIGA
/* If we called them and they are an fbb system, don't call eat().
*/
      if(s_mart & fbbok) {
         return;
      }
#endif
      eat();
   }
}

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

dofwd(p)
PORTS *p;
{
   register char *st;
   ioport(p);
   fmsg();
   if(fopts & fw_tried) {
      if(p->mode & idle) {
         p->mmhs->stat clrbit m_busy;
         wt_mmhs();
#ifndef MCH_AMIGA
/* Can't do passlst here any more. We are already at the end of the list*/
         passlst();
#endif
         return ;
      }
   }
}

/*
 *  Send the message text.
 */

xmtmsg()
{
   register FILE *fl;
#ifdef MCH_AMIGA
   register char *cp,*pp;
#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
         for(cp = tmp->scr,pp = &tmpstr[0];
                                      *cp && (pp < &tmpstr[280]);cp++) {
            if(export) {
               *pp++ = *cp;
               continue;
            }
            if(*cp == passchr)continue;
            /* insert the pass character for stream characters */
            if(passchr) {
               if((*cp == vhfstream) || (*cp == hfstream)) {
                  *pp++ = passchr;
               }
               *pp++ = *cp;
               continue;
            }
            if((*cp >= ' ') && (*cp <= 0176)) {
               *pp++ = *cp;
               continue;
            }
            /*
               Allow BEL BS HT LF VT FF and CR through.
               All others are omitted
            */
            if((*cp >= 7) && (*cp <= '\r')) {
               *pp++ = *cp;
               continue;
            }
         }
         *pp = 0;
         outstr(tmpstr);
#else
         outstr(tmp->scr);
#endif
      }
      fclose (fl);
   }
   if(fwdtyp is 'D') {
      outstr(".\n");
      outstr("y\n");
   }
   else {
#ifndef MCH_AMIGA
      outchar(cpmeof);
      outchar('\n');
#else
      if(export) {
         outstr("/EX\n");
      }
      else {
         outchar(cpmeof);
         outchar('\n');
      }
#endif
   }
   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, extnr, hasit;

   hasit = true;   /* just initialize it to something */

   p = port;
#ifdef MCH_AMIGA
   check_mail();
   unlock_mail();
#endif
   for(h = mfhs->last; h; h--) {
      if(s_flag & s_dv) begin_lock();
      read_rec(mfl, h, (char *)p->mmhs);
#ifndef MCH_AMIGA
/* Put all this in check_ok() so that fbb can sort out whether to send a
   message or not
*/

      ok = (!(p->mmhs->stat & (m_kill|m_read|m_fwd|m_hold|m_busy)));

      if(ok)
         if(filesize)
            ok = filesize > p->mmhs->size;
/*
 *  Decide whether to forward this one.
 */
      if(ok) {
#ifndef MCH_AMIGA
         if (p->mmhs->ext is 1)
#else
/* Fix bug. A bulletin should be forwarded to the connected station
   IF and ONLY IF the connected station has not yet received it.
   This code sends it back to the calling station if one of the stations
   in the distribution list for the bulletin is also in the collection
   list for the connected station

   NOT SURE I'VE GOT THIS RIGHT SO I'VE TAKEN IT OUT AGAIN
*/
         if((p->mmhs->ext == 1) /* && matchn(fcall,tcall,ln_call)*/)
#endif
         {
            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);
      }
#else
/* kludge a way to get extnr passed through. check_ok returns extnr if it
   is set (which implies ok and -1 if ok but extnr not set. Zero otherwise.
   extnr is only used  if ->ext == 1  which is only way it could be set
*/
      ok = check_ok();
      if(ok > 0)extnr = ok-1;

#endif

      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;
#ifdef MCH_AMIGA
         if(s_mart & fbbok) {
         /* This is ugly .. have to release the first message to be sent so
            that fbbfwd can find it again. There's got to be a better way!
            But the forwarding routine doesn't do anything until it has
            found the first message to forward.
         */
            p->mmhs->stat clrbit m_busy;
            wt_mmhs();
            pcall(hcall, path + 2);
            rduser(hcall, p->user);
            fbbfwd(h);
            return;
         }
#endif
/*ttputs("FMSG\n");*/
         if(*p->mmhs->bbs is ' ')
            prtx("S$B $G < $P");
         else
            if((s_mart&hidok)and(p->mmhs->ext is 2))
               prtx("S$B $G @ $h < $P");
            else
               prtx("S$B $G @ $A < $P");
#ifndef MCH_AMIGA
         if(s_mart&bidok) if (*p->mmhs->bid isnt ' ') {
#else
         if(s_mart&(bidok|midok)) if (*p->mmhs->bid isnt ' ') {
#endif
            outstr(" $");
            a = p->line;
            b = p->mmhs->bid;
            unbl(a, b, ln_bid);
            outstr(p->line);
         }
#ifdef MCH_AMIGA
/* If we are talking to THEBOX and this message has a lifetime, then
   send the lifetime
*/
         if(thebox && p->mmhs->lifetime) {
            sprintf(tmpstr," #%u",p->mmhs->lifetime);
            outstr(tmpstr);
         }
#endif
         outchar('\n');
#ifndef MCH_AMIGA
         getln();
#else
         if(!export)getln();
         else strcpy(p->line,"OK");
#endif
         if(p->mode & idle) return;
         if(s_mart) {
            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');
            getln();
            if(p->mode & idle) return;
         }
         if(!hasit) {
            curtim();
            prtx(mm[4]);
            if(!xmtmsg()) {
               fopts setbit fw_tiout;
               return;
            }
         }
#ifndef MCH_AMIGA
         eat();
#else
         if(!export)eat();
#endif
         if(p->mode & idle) {
            fopts setbit fw_tiout;
            return;
         }
/*
 *  The message actually forwarded.
 *  Kill it, mark it, or whatever.
 */
if(debug)printf("MARKIT\n");
         sprintf(p->line, "%u %s", p->mmhs->number, path);
         log ('M', 'F', ' ', p->line);
       
         markdis(extnr);

         p->mmhs->stat clrbit m_busy;
         if(p->mmhs->ext isnt 1) {
            pcall(p->mmhs->fwdc, fcall);
            p->mmhs->fport = p->id;
         }
         write_rec(mfl, p->mmhs->rn, (char *)p->mmhs);
         makehdr2();
      }
   }
#ifdef MCH_AMIGA
   /* Only get here if the loop didn't find a message to send. But still
      have to call fbbfwd to receive stuff from the other end
   */
   if(s_mart & fbbok) {
      fbbfwd(0);
   }
#endif
}
markdis(nr)
int nr;
{
  int i, kill, kok;
  kill = false;
  kok = false;
/*  If cc: list exists, mark this call as forwarded. */

  if (port->mmhs->ext is 1)
  {
     port->mmhs->flag[nr] = false;
     for (i = 0; i < port->mmhs->count; i++)
     if (port->mmhs->flag[i]) kok = true;
  }

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

  if (kill) port->mmhs->stat setbit m_kill;
  else if (!kok) port->mmhs->stat setbit m_fwd;

}
#ifdef MCH_AMIGA
check_ok()
{
   int ok,extnr;
   register int i;
   register char *cp;
   register char *c;
   register PORTS *p;
   char hcall[6];


   extnr = -1;
   p = port;
   ok = (!(p->mmhs->stat & (m_kill|m_read|m_fwd|m_hold|m_busy)));
   if (ok) if(filesize)  ok = filesize > p->mmhs->size;
/*   if(ok) ok = findfwd(tcall, bfwd, bfwdn); */
/*
 *  Decide whether to forward this one.
 */
   if(ok) {
      ok = false;
      for(cp = callist;*cp && !ok;cp+=ln_call) {
         strncpy(tcall,cp,ln_call);
/* Fix bug. A bulletin should be forwarded to the connected station
   IF and ONLY IF the connected station has not yet received it.
   The old code sent it back to the calling station if one of the stations
   in the distribution list for the bulletin was also in the collection
   list for the connected station
   I've removed it. Not sure I've fixed it properly.
*/
         if((p->mmhs->ext == 1)/* && matchn(fcall,tcall,ln_call)*/) {
            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);
         }
#ifdef MCH_AMIGA
/* If the TO field is a callsign and the connected BBS has them in its
   colection list, then send it anyway.
*/
         if(!ok && iscall(p->mmhs->to))ok = wcm(tcall,p->mmhs->to);
#endif
      }
   }
   if(extnr >= 0) {
/* Have to return one more than extnr because it can be zero */
      return(extnr+1);
   }
   if(ok) {
      return(-1);
   }
   return(0);
}
#endif
