/*
 *  MB.C - 9/20/92 -  MailBox mainline code.
 */

/*
 *  Copyright (C) 1987, 1988
 *  On Modules by H. N. Oredson
 *  Copyright (C) 1988, 1989, 1990, 1991, 1992
 *  By the CBBS Group.
 *
 *  It is being developed by the CBBS Group.
 *  It may be freely copied for non-commercial use,
 *  as long as this notice is retained.
 *
 *  This notice and Copyright apply to all modules
 *  referenced by this module.
 *
 */

#include "mb.h"

#ifdef MCH_AMIGA
extern char conn_direction;
extern char *fwdfile;
#ifndef MCH_SYSOP
extern PORTS *devtnc;
#endif
#include "lock.h"

/* Allow user to specify the device name and unit number if they have
   a multi-serial card. But default it to the standard device on unit zero
   If ser_dev_name is zero then ser.c will use the standard name.
*/
char *ser_dev_name = 0L;
long unit = -1L;

extern short kambbs, thebox, debug , tapr_flag;
long BaudRate;
short left = 0, top = 0, width = 640, height = 256;
extern int daylight;
#else
char  *ver = "\nC-BBS V7.2, 20 SEP 1992\n\n";
char  *vers = "[CBBS-7.20-H$]\n";
#endif
char  *nullstr = "";

PORTS *porthd = NULL;
PORTS *cport  = NULL;
PORTS *port;

int   scrmax, dirmax, log_mon;
TMP   *tmp;
MLM   *motd;
char  *helpfile, *infofile;
char  *bbmenu, *symenu, *rmenus, *mumsg;
char  *reqmsg, *qth, *keylst;
char  *mcant, *mdone, *mfind, *mexst, *mnfile, *mnmsg;
char  *mprot, *mtime, *mwhat, *mnport, *mndir, *minuse;
char  achar, rchar, tchar, wchar;
word  s_param, p_window, c_flag, b_flag;
byte  s_mart, s_prompt, s_flag, unt_hr;
char  scmd[24];

#ifdef MCH_AMIGA
#ifndef MCH_SYSOP
char hostport = 'A';
#else
char hostport = '1';
#endif
#endif

#ifdef MCH_AMIGA
short timezone = -900;     /* Initialize to impossible number of minutes */
extern char tmpstr[];
extern char *continent;
#endif
main(argc, argv)
int argc;
char *argv[];
{
#ifdef MCH_AMIGA
   char *cp,c;
   short i;

/* Might as well do a quick check to make sure the locker is there.
*/
   if(FindPort((UBYTE *)LOCK_NAME) == 0L) {
      printf("Can't find the locker process\n");
      exit(10);
   }
   /* Get the environment variable timezone if it is set.
      Doing it here allows the user to override the value of TZ_CHU
      with the -z argument.
   */
   timezone = gettz("");
   get_reject();
   while(argc > 1) {
      if(argv[1][0] == '-') {
         switch(tolower(argv[1][1])) {

         case 'c':
            continent = ".NA";
            break;
         /* allow user to specify the position of the window on the screen
            -wL/T/W/H
         */
         case 'w':
            cp = &argv[1][2];
            left = atoi(cp);
            while(*cp && (*cp != '/'))cp++;
            if(*cp == 0)break;
            cp++;
            top = atoi(cp);
            while(*cp && (*cp != '/'))cp++;
            if(*cp == 0)break;
            cp++;
            width = atoi(cp);
            while(*cp && (*cp != '/'))cp++;
            if(*cp == 0)break;
            cp++;
            height = atoi(cp);
            setwindow(left,top,width,height);
            break;

         /* In multi mode, specify the port which this copy will open
         */
         case 'p':
            c = toupper(argv[1][2]);
#ifndef MCH_SYSOP
            if((c >= 'A') && (c <= 'P')) {
               hostport = c;
            }
            else {
               printf("Invalid port character\n");
               exit(2);
            }
#else
            if(isdigit(c) && (c != 0)) {
               hostport = argv[1][2];
            }
            else {
               printf("Port character must be a non-zero digit\n");
               exit(3);
            }
#endif
            break;

         case 'n':      /* Serial port name for multi-serial users */
            ser_dev_name = &argv[1][2];
            break;
         case 'u':      /* Serial port unit number for multi-serial */
            unit = (long)atoi(&argv[1][2]);
            break;

         case 'y':      /* Specify different start up fwd file than config*/
            fwdfile = strdup(&argv[1][2]);
            break;

         case 'd':
            debug = 1;
            break;
/* Baud rate is in the host driver */
         case 'b':
            BaudRate = atol(&argv[1][2]);
            break;
         case 't':
            /* tell serial read routine to strip high bit off all bytes
               read from the TNC because it is a TAPR upgrade kit that
               generates NO parity only with the parity bit forced to one
            */
            tapr_flag++;
            break;
         case 'z':
            /* Specify the timezone so that you can keep your amiga
               on your local time but have the time in messages converted
               to UTC. format is -z[-]hh[:mm][d]
               Times WEST of Greenwich are POSITIVE.
               Times EAST of Greenwich are NEgative.
               The optional minutes ":mm" allows places like Newfoundland
               to specify their zone as 3:30.
               If the optional letter 'd' is present at the end of the
               string, then your local time zone is adjusted for daylight
               savings time.

               The -z argument, if set, overrides any value in the TZ_CHU
               environment variable.
            */
            timezone = gettz(&argv[1][2]);
            break;
         }
         argc--;
         argv++;
         continue;
      }
      break;
   }
   /* Just make sure that there isn't already a port with our name on it */
   if(testport()) {
      exit(1);
   }
   /* Call routine that reads my modified init() so that the config.mb
      file is opened early to permit options in config.mb to be read
      before the window and serial port are opened. This allows options
      to be used that affect the opening of the window or serial parameters.
   */
   if (argc > 1)i = setinit(argv[1]); else i = setinit("config.mb");
   if(i)exit(1);
#ifndef MCH_SYSOP
/* There isn't one in the sysop process */
   printf("Serial device name = %s  unit = %ld\n",ser_dev_name,unit);
#endif
   if(initser()) {
      printf("error opening serial port\n");
      exit(1);
   }
   if(openterm()) {
      printf("error opening terminal\n");
      cleanser();
      exit(1);
   }

   if(inittimer()) {
      printf("error opening timer\n");
      cleanser();
      closeterm();
      exit(1);
   }

/* This does more than the quick check at the beginning of main() so
   don't remove this call.
*/
   if(i = findlock()) {
      printf("Can't find the Lock process - %d\n",i);
      cleanser();
      closeterm();
      exit(1);
   }
/* This allows the port driver to count how many processes are running
   without having to be told
*/
   setbusy();

/* Up to this point all routines MUST do their own cleaning up and then
   exit() directly. AFTER setbusy() has been called any further errors
   should cause done() to be called because done() calls clrbusy.
   Otherwise the locking program will have an incorrect count of the number
   of active processes.
   The EXCEPTION to this rule is inittnc() which already cleared up its
   own mess, so I just added a clrbusy as well, rather than try to fix
   it up to use done().
*/

#endif

#ifdef MCH_AMIGA
   /* Compute the first Sunday in April and last Sunday in October
      if daylight savings is being used.
   */
   dstdates();
   if(timezone != -900) {
      i = timezone;
      sprintf(tmpstr,"Timezone = %c%d:%02d %s\n",
                       i<0?'-':' ',i<0?-i/60:i/60,i<0?(-i)%60:i%60,
                       daylight?"(DST)":"");
      ttputs(tmpstr);
      printf("%s",tmpstr);
      i = dst(timezone);
      if(i != timezone) {
         sprintf(tmpstr,"DST time = %c%d:%02d\n",
                       i<0?'-':' ',i<0?-i/60:i/60,i<0?(-i)%60:i%60);
         ttputs(tmpstr);
         printf("%s",tmpstr);
      }
   }
   else {
      cp = "Timezone not set - Assume UTC and no DST\n";
      printf("%s",cp);
      ttputs(cp);
      timezone = 0;
      daylight = 0;
   }
#endif

   /*
      The init() routine also calls the routine that will read TNC.ON
      Do this after calling dstdates so that the TNC.ON file can set the
      TNC date to UTC if required.
   */
  if (argc > 1) init(argv[1]); else init("config.mb");


#ifdef MCH_AMIGA
/* Open up the AREXX public port so that the waitport program can be used
   to start up several CBBS copies in sequence and also allows communication
   from outside processes and between multiple 'mb' processes.
*/
   if(open_rexx())done(1);
#endif
  while (true)
  {
    s_mart = 0;
#ifdef MCH_AMIGA
    kambbs = 0;
    thebox = 0;
    conn_direction = ' ';
    user_title("");
    titles();
#endif
    do_idle();
    setbusy();
    login();
#ifndef MCH_AMIGA
    if (!(port->mode & exclude)) do_cmds();
#else
/*FIX 8  ignore idle terminal caused by monitoring data that looks like a
         "*** CONNected" message.
*/
    if(!(port->mode & idle)) {
       if(!(port->mode & exclude)) do_cmds();
    }
#endif
    logo();
  }
}

do_cmds()
{
  register PORTS *p;
  register char  *i, *o;

  p = port;

  while(true)
  {
    prompt();
    getcmd();
    if (p->mode & gone) return;

/*
 *  Console broke in on a user?
 */

    if (p->flags & p_opreq)
    {
      p->flags clrbit p_opreq;
      prtx(talkm1);
      if (p->tmode)
      {
        cmdtnc();
        convtnc();
      }
      ioport(cport);
      prtx(mumsg);
      cport->fl = NULL;
      term(p);
      ioport(p);
      tncstate();
      if (p->tmode) trantnc(); else convtnc();
      if (p->mode & gone) return;
    }

/*
 *  Check for LINK.
 *  Require "LINKED to" to be first command.
 */

    else if (islink(p->line))
    {
      if (p->cmdcnt) p->msg = mwhat; else
      {
   strcpy(p->line, p->line + 14);
   logina(true);
   remnl(p->line);
   log('C', 'L', ' ', p->line);
   if (p->mode & exclude) return;
      }
    }

/*
 *  Must be a command.
 */

    else
    {
      docmd();
      if (p->errors > p->errmax) p->mode = forced;
      if (p->mode & (gone | idle | logout)) return;
    }
  }
}

/*
 *  Q command: exit to DOS.
 */

#ifndef MCH_AMIGA
done()
{
  byte pflg;

  if (sure()) return;
#else
extern short done_flag;
done(askflag)
int askflag;
{
   byte pflg;
   if(!askflag && sure()) return;
   done_flag = 0;
   cursor_off();
   close_rexx();
#endif
  upduser(cport->user);
  log('X', 'Q', ' ', nullstr);
  clslog();
  clsusr();
  clsmsg();
  clsmon();
  iooff();
  clrbusy();
  pflg = getp_flag();
  putp_flag(pflg clrbit p_window);
#ifdef MCH_AMIGA
   endtimer();
   cleanser();
   freefcb();
   closeterm();
   stoplock();
#endif
  exit(0);
}

/*
 *  Give the user a prompt.
 */

prompt()
{
  register PORTS *p, *tp;

  p = port;
  if (p->msg isnt NULL) prtx(p->msg);
  p->msg = NULL;

/*
 *  Tell sysop who this is.
 */

  if (p isnt cport) { ioport(cport); prtx(mumsg); ioport(p); }

/*
 *  Show any connect request seen on the user port.
 */

  if (p->flags & p_req) prtx(reqmsg);
  p->flags clrbit p_req;

/*
 *  Bleed off any connect requests on other ports.
 */

/* There aren't any other ports on the AMIGA */
#ifndef MCH_AMIGA
  for (tp = porthd; tp isnt NULL; tp = tp->next) if (tp->mode & idle)
  if ((tp->dev is p_tnc) and (tp isnt p))
  {
    ioport(tp);
    while (instat()) getdat();
  }
  ioport(p);
#endif
/*
 *  Display the prompt.
 */

  curtim();
  switch (p->mode)
  {
    case local :
    case sysop :
      prtx(symenu);
      break;

    case remote:
      if (!(p->user->options & u_bbs))
      {
   if (s_prompt & s_p_name) if (!(p->user->state & u_name)) prtx(um[4]);
   if (s_prompt & s_p_home) if (!(p->user->state & u_home)) prtx(um[5]);
   if (s_prompt & s_p_zip)  if (!(p->user->state & u_zip))  prtx(um[6]);
   if (s_prompt & s_p_qth)  if (!(p->user->state & u_qth))  prtx(um[7]);
      }

#ifndef MCH_AMIGA
      if (p->user->options & (u_bbs | u_expert)) prtx(bbmenu); else prtx(rmenus);
#else
      if(p->user->options & u_bbs) outstr(">\n");
      else if (p->user->options & u_expert) prtx(bbmenu);
      else prtx(rmenus);
#endif
      break;
  }
}

/*
 *  Log the user off.
 *  Do any defered remote sysop command.
 */

logo()
{
  register PORTS *p;
  register PORTS *tp;
  register char t;

  p = port;

  switch(p->mode)
  {
    case idle   : t = 'A'; break; /* Init bbs logout */
    case logout : t = 'B'; break;
    case discon : t = 'D'; break;
    case exclude: t = 'E'; break;
    case forced : t = 'F'; distnc(); break;
    case timeout: t = 'T'; prtx(mtime); break;
    default     : t = '?'; break;
  }

#ifndef MCH_AMIGA
  log('X', t, ' ', nullstr);
#else
  log('X', t, port->id , nullstr);
#endif

/*
 *  If any port was used in terminal mode by the console,
 *  disconnect it.
 */

  if (p->dev is p_console)
  for (tp = porthd; tp isnt NULL; tp = tp->next)
  if (tp->flags & p_term)
  {
    ioport(tp);
    tp->mode = remote;
    distnc();
    tp->mode = idle;
    tp->flags clrbit p_term;
  }

  ioport(p);

/*
 *  Change TNC back to "standard" if was in "sysop" mode.
 *  May have just THOUGHT we discon, REALLY disconnect.
 */

  if (p->dev is p_tnc)
  {
    if (p->flags & p_clrsys) mkrem();
    p->mode = remote;
    distnc();
  }
  if (p->dev is p_serial) p->flags clrbit p_trans;
#ifdef MCH_AMIGA
  if (p->dev is p_nulmdm) p->flags clrbit p_trans;
#endif
#ifndef MCH_SYSOP
/* Hang up a modem */
   if(p->dev is p_serial) {
      force_modem();
   }
/* If it was the console but the serial device is a modem, then allow
   phone to ring again.
*/
   if(p->dev is p_console) {
      if(devtnc->dev is p_serial) {
         tp = p;
         ioport(devtnc);
         outstr("ATS0=2\n");
         waitcmd(2);
         ioport(tp);
      }
   }
#endif
  p->mode = idle;

  upduser(p->user);

/*
 *  If there was a "defered remote sysop command" do it.
 */

  ioport(cport);
  if (s_param & s_cmd)
  {
    s_param clrbit s_cmd;
    strcpy(cport->line, scmd);
    parse();
    wait(10);               /* Give sysop chance to get HIS system online */
    cport->mode = local;    /* Do command as if console did it */
    docmd();
    cport->mode = idle;
  }

  clnlog();
  clsusr();
  setfwd();
  clsmsg();
}

/*
 *  MailBox is idle.
 *  See if there is anything to do, and do it.
 *  If nothing to do, wait for connect.
 */

#ifndef MCH_AMIGA
do_idle()
{
  register PORTS *p;
  register short curmin, lastmin, curmon;

  for (p = porthd; p isnt NULL; p = p->next) p->ec = p->ecmon;

  getc_flag();
  if (c_flag & p_window) acmd();

  allon();  /* Turn on monitoring and connects */
  clrbusy();
  curtim();
  curmin = 10 * (l_time[2] - '0') + (l_time[3] - '0');
  while(true)
  {

/*
 *  Check the port command flag to see if another window has
 *  issued a command request. If so setup and do the command.
 */

    getc_flag();
    if (c_flag & p_window)
    {
      setbusy();
      alloff();
      acmd();
      allon();
      clrbusy();
    }

    lastmin = curmin;
    curtim();
    curmin = 10 * (l_time[2] - '0') + (l_time[3] - '0');
    curmon = 10 * (l_date[2] - '0') + (l_date[3] - '0');

/*
 *  Once each minute:
 *  Forward now, if it is time on any port.
 */

    if (curmin isnt lastmin) afwd(curmin);
    if (s_param & s_log_on) if (curmon isnt log_mon) chglog();

/*
 *  Look for a login.
 */

    for (p = porthd; p isnt NULL; p = p->next)
    {
      ioport(p);
      if (instat()) switch(p->dev)
      {
   case p_serial:
     p->flags clrbit p_give;
     getdat();
     p->flags setbit p_give;
     if (iscon(p->line)) return;
     break;

   case p_tnc:
     p->flags clrbit p_give;
     getdat();
     p->flags setbit p_give;
     if (iscon(p->line)) return;
     monitor();
     break;

   case p_console:
     if (inchar() is wchar) return;
     break;
      }
      p->flags setbit p_give;
    }
  }
}
#endif
/*
 *  Do commands retrieved from the bios.
 */
#ifndef MCH_AMIGA
acmd()
{
   char need_msg = false;
   char notime = false;

   port->mode = local;
   getcomd();
   if (s_flag & s_dv) begin_lock();
   if (isdigit(port->opt2))
   {
     notime = true;
     port->opt2 = port->opt2 ^ 0x70;
   }
   if ((port->opt1 is 'X') and (findport(port->opt2)))
   {
     if (notime) port->opt2 = ' '; else port->opt2 = 'I';
     readmsg();
     readusr();
     need_msg = true;
   }
   getc_flag();
   putc_flag( c_flag clrbit p_window);
   if (s_flag & s_dv) end_lock();
   if (need_msg or (port->opt2 is 'H'))
   {
     sprintf(port->line, "%c%c", port->opt1, port->opt2);
     parse();
     docmd();
   }
   if (need_msg)
   {
     clnlog();
     setfwd();
     clsmsg();
     clsusr();
   }
   port->mode = idle;
}
#endif
#ifdef MCH_AMIGA
#ifndef MCH_SYSOP
acmd()
{
   char need_msg = false;
   char notime = false;

   if(port->opt1 == 'Q') {
      /* Have to setbusy because done() does a clrbusy() */
      setbusy();
      done(1);
   }
   port->mode = local;
   if (s_flag & s_dv) begin_lock();
   if(port->opt1 == 'X')
   {
     readmsg();
     readusr();
     need_msg = true;
   }
   if (s_flag & s_dv) end_lock();
   if (need_msg or (port->opt2 is 'H'))
   {
     sprintf(port->line, "%c%c", port->opt1, port->opt2);
     parse();
     docmd();
   }
   if (need_msg)
   {
     clnlog();
     setfwd();
     clsmsg();
     clsusr();
   }
   port->mode = idle;
}
#endif
#endif
/* Bring this here from mbfwd to save having to make a separate sysop
   version of mbfwd (Otherwise it could stay there)
*/
#ifdef MCH_SYSOP
all_fwd()
{
  word pflg;
  port->opt1 = 'X';
  putcomd( port->opt1, port->opt2 );
#ifndef MCH_AMIGA
  pflg = getp_flag();
  putc_flag (pflg);
#endif
  port->mode = logout;
}
#endif
