/***************************************************************************
 *                                                                         *
 *   SLISP.C                                                               *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   SLIP/CSLIP/PPP connection to an Internet Service Provider             *
 *                                                                         *
 *                                              - R. Stein  1/26/96        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "remote.h"
#include "ftscope.h"
#include "script.h"
#include "tcpip.h"
#include "galhpac.h"
#include "llconfig.h"
#include "cman.h"
#include "slisp.h"
#include "galslisp.h"

#define FILREV "$Revision: 11 $"

static GBOOL rdimeth(VOID);
static INT ispglb(VOID);
static GBOOL ispinp(VOID);
static VOID ispsts(VOID);
static VOID svrisp(struct scrinf *scrptr,UINT len,CHAR *bytes);
static VOID devisp(struct scrinf *scrptr,CHAR *stg);
static VOID admisp(struct scrinf *scrptr,CHAR *stg);
static UINT isp_out_handler(CHAR *ptr,UINT nbytes);
static VOID ispkick(VOID);
static VOID isplatch(VOID);
static VOID audeos(VOID);
static VOID isprst(VOID);
static VOID ispfin(VOID);

INT ispstt;                        /* SLIP-to-ISP module state number      */
struct module ispmodule={          /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     ispinp,                       /*    input routine if selected         */
     ispsts,                       /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     NULL,                         /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     ispfin                        /*    finish-up (sys shutdown) routine  */
};

HMCVFILE ispmb;                    /* handle for GALSLISP.MCV              */
CHAR *ispkey;                      /* key required to use /ISP command     */
CHAR *scrfnm;                      /* script file name                     */
CHAR *imeth;                       /* Int connect meth, SLIP/CSLIP/PPP     */
INT imethi;                        /* Int connect meth, XXXIDX in SLISP.H  */
INT ispddla;                       /* ISP channel shutdown delay           */
INT ispdrem;                       /* shutdown seconds remaining           */
INT ispchan;                       /* channel for connection               */
INT ispunum;                       /* user number for connection (-1=none) */
struct user *ispuptr;              /* user structure for ISP channel       */
GBOOL audscr;                      /* record script errors in audit trail? */
GBOOL audend;                      /* record session time&byt audit trail? */
ULONG ispitk;                      /* lngtck of last data from ISP         */
ULONG ispotk;                      /* lngtck of last data sent to ISP      */
ULONG ispntk;                      /* lngtck of when went online with ISP  */
ULONG ispinb;                      /* count of bytes from ISP, this sess.  */
ULONG ispoub;                      /* count of bytes sent to ISP           */
ULONG ispier;                      /* count of input errors from kernel    */
INT lastier;                       /*   last such input error              */
VOID (*oldrst)(vold);              /* old (*hdlrst)() vector               */
CHAR uscchr[]={'S','C','P'};       /* SLIP type characters for <Alt-U> scn */
CHAR *nametbl[]={                  /* SLIP type names                      */
     "SLIP","CSLIP","PPP"};        /* (cloned from SCP.C)                  */


VOID EXPORT
init__galslisp(VOID)
{
     init__tcpip();
     ispmb=opnmsg("galslisp.mcv");
     ispkey=stgopt(ISPKEY);
     if (rdimeth()) {
          stzcpy(ispmodule.descrp,gmdnam("galslisp.mdf"),MNMSIZ);
          ispstt=register_module(&ispmodule);
          init__galscr();
          scrfnm=stgopt(SCRFNM);
          ispchan=hexopt(ISPCHAN,0x00,0xFF);
          ispddla=numopt(ISPDDLA,0,30);
          audscr=ynopt(AUDSCR);
          audend=ynopt(AUDEND);
          if ((ispunum=usridx(ispchan)) == -1) {
               catastro("ERROR WITH OPTION ISPCHAN:  "
                        "there is no channel %02X on your system.",ispchan);
          }
          ispuptr=usroff(ispunum);
          oldrst=hdlrst;
          hdlrst=isprst;
          dclvda(sizeof(struct scrinf));
          rtkick(1,ispkick);
          slppp_out_handler=isp_out_handler;
     }
     else {
          ispunum=-1;    /* allows more efficient isprst() */
          ispuptr=NULL;
     }
     globalcmd(ispglb);
}

VOID EXPORT
initwc__galslisp(VOID)
{
     init__galslisp();
}

static GBOOL                       /*   TRUE=SLIP or CSLIP or PPP          */
rdimeth(VOID)                      /* read IMETH CNF opt from GALTCPIP.MSG */
{
     if ((imeth=msgscan("GALTCPIP.MSG","IMETH")) == NULL) {
          catastro("CAN'T READ IMETH OPTION IN GALTCPIP.MSG");
     }
     imeth=strdup(lastwd(imeth));
     if (sameas(imeth,"SLIP")) {
          imethi=SLPIDX;
     }
     else if (sameas(imeth,"CSLIP")) {
          imethi=CSLIDX;
     }
     else if (sameas(imeth,"PPP")) {
          imethi=PPPIDX;
     }
     else {
          imethi=NONIDX;
          return(FALSE);
     }
     return(TRUE);
}

static INT
ispglb(VOID)                       /* "/isp" global diagnostic command     */
{
     LONG cntovr;

     if (margc == 1 && sameas(margv[0],"/isp") && haskey(ispkey)) {
          setmbk(ispmb);
          if (imethi == NONIDX) {
               prfmsg(ISPNOT,imeth);
          }
          else if (ispuptr->usrcls == BBSPRV
           && ispuptr->state == ispstt) {
               switch(ispuptr->substt) {
               case ISPSCR:
                    prfmsg(ISPSCR,ispchan,scrfnm,ispptr->scrinf.lin);
                    break;
               case ISPRUN:
                    prfmsg(ISPRUN,ispchan,nametbl[imethi],
                                  commas(ul2as(lngtck-ispntk)),
                                  commas(ul2as(ispoub)),
                                  commas(ul2as(ispinb)));
                    break;
               case ISPDWN:
                    prfmsg(ISPDWN,ispdrem);
                    break;
               default:
                    prfmsg(ISPUNK,ispchan,ispuptr->substt);
                    break;
               }
               if (lngtck-ispitk > 1) {
                    prfmsg(ISPSNC,ul2as(lngtck-ispitk));
               }
               if ((cntovr=bturep(ispunum,CNTOVR)) > 0L) {
                    if (ispuptr->flags&NON550) {
                         prfmsg(ISP450,l2as(cntovr));
                    }
                    else {
                         prfmsg(ISP550,l2as(cntovr));
                    }
               }
               if (ispier > 0UL) {
                    prfmsg(ISPIER,commas(ul2as(ispier)),lastier);
               }
          }
          else {
               prfmsg(ISPRST,ispchan);
          }
          rstmbk();
          outprf(usrnum);
          return(1);
     }
     return(0);
}

static GBOOL
ispinp(VOID)                       /* SLIP-to-ISP module input handler     */
{
     ASSERT(0);                    /* (no business being here)             */
     return(TRUE);
}

static VOID
ispsts(VOID)                       /* SLIP-to-ISP module status handler    */
{
     INT nbytes;
     INT rc;
     INT i;

     if (status != CYCLE) {
          switch (status) {
          case SPXTRM:
          case SPXWDG:
          case RING:
          case LOST2C:
          case LOST25:
               rstchn();
               break;
          }
          return;
     }
     setmbk(ispmb);
     ASSERT(usrnum == ispunum);
     if ((nbytes=btuica(usrnum,vdatmp,vdasiz)) > 0) {
          if (whomon == usrnum) {
               (*ftscope)(FTSINS,vdatmp,nbytes);
          }
          ispitk=lngtck;
          switch (usrptr->substt) {
          case ISPSCR:
               scrinp(&ispptr->scrinf,nbytes,vdatmp);
               break;
          case ISPRUN:
          case ISPDWN:
               ispinb+=nbytes;
               for (i=0 ; i < nbytes ; i++) {
                    rc=slppp_in_handler(vdatmp[i]);
                    if (rc != 0) {
                         lastier=rc;
                         ispier++;
                    }
               }
               break;
          }
     }
     else {
          actdet=0;
     }
     switch (usrptr->substt) {
     case ISPSCR:
          switch (pumpscr(&ispptr->scrinf)) {
          case CNTPSCR:
               btuinj(usrnum,CYCLE);
               break;
          case ENDPSCR:
               if (whomon == ispunum) {
                    (*ftscope)(FTSCMT,spr("***** Script file %s ends"
                               ", %s-to-ISP begins",scrfnm,nametbl[imethi]));
               }
               usrptr->substt=ISPRUN;
               shochl(spr("%s connection to ISP",nametbl[imethi]),
                      uscchr[imethi],baudat(usrptr->baud,0));
               ispntk=lngtck;
               if ((rc=slppp_up()) != 0 && rc != EALREADY) {
                    catastro("SLPPP_UP error %d: %s",rc,tcpErrStg(rc));
               }
               btuinj(usrnum,CYCLE);
               break;
          case KILCHAN:
               if (whomon == ispunum) {
                    (*ftscope)(FTSCMT,spr("***** Script file %s abort",scrfnm));
               }
               if (ispptr->whyend[0] == '\0') {
                    sprintf(ispptr->whyend,"script aborted on line %d",
                            ispptr->scrinf.lin);
               }
               rstchn();
               break;
          }
          break;
     case ISPRUN:
     case ISPDWN:
          if (imethi == PPPIDX) {
               do_pppfsm();
          }
          btuinj(usrnum,CYCLE);
          break;
     }
}

static UINT                        /*   number of bytes actually output    */
isp_out_handler(                   /* SLIP/CSLIP/PPP to ISP handler        */
CHAR *ptr,                         /*   pointer to bytes to go out         */
UINT nbytes)                       /*   number of bytes to go out          */
{
     if (ispuptr->usrcls == BBSPRV
      && ispuptr->state == ispstt
      && (ispuptr->substt == ISPRUN
       || ispuptr->substt == ISPDWN)
      && btuoba(ispunum) >= nbytes) {
          btuxct(ispunum,nbytes,ptr);
          if (whomon == ispunum) {
               (*ftscope)(FTSOUS,ptr,nbytes);
          }
          ispoub+=nbytes;
          return(nbytes);
     }
     return(0);
}

static VOID
ispkick(VOID)                      /* SLIP-to-ISP waiting out modem init   */
{
     INT rc;

     if (ispuptr->usrcls == VACANT
      && ispuptr->state == AWAITC) {
          curusr(ispunum);
          isplatch();
     }
     if (ispuptr->usrcls == BBSPRV
      && ispuptr->state == ispstt
      && ispuptr->substt == ISPDWN) {
          if (ispdrem <= 0 || --ispdrem == 0) {
               curusr(ispunum);
               if ((rc=slppp_down()) != 0) {
                    catastro("SLPPP_DOWN error %d: %s",rc,tcpErrStg(rc));
               }
               audeos();
               (*oldrst)();        /* pick up delayed reset where left off */
          }
     }
     rtkick(1,ispkick);
}

static VOID
isplatch(VOID)                     /* latch onto SLIP-to-ISP channel       */
{                                  /* (expects curusr() to be in effect)   */
     ASSERT(usrnum == ispunum);
     ASSERT(usrptr == usroff(usrnum));
     usrptr->usrcls=BBSPRV;
     usrptr->state=ispstt;
     usrptr->substt=ISPSCR;
     sprintf(usaptr->userid,"(%s to ISP)",nametbl[imethi]);
     btubsz(usrnum,OUTSIZ/2,OUTSIZ/2);
     btutrg(usrnum,OUTSIZ/2);
     btuhwh(usrnum,OUTSIZ/2-40);
     btubbr(usrnum,usrptr->baud);
     shochl(spr("Running script %s",scrfnm),'@',baudat(usrptr->baud,0));
     setmem(ispptr,sizeof(struct ispinfo),0);
     begscr(&ispptr->scrinf,scrfnm,svrisp,NULL,devisp,admisp);
     btuinj(usrnum,CYCLE);
     ispitk=lngtck;
     ispotk=lngtck;
     ispinb=0UL;
     ispoub=0UL;
     if (whomon == ispunum) {
          (*ftscope)(FTSCMT,spr("***** Script file %s begins",scrfnm));
     }
}

static VOID
svrisp(                            /* SLIP-to-ISP server output            */
struct scrinf *scrptr,             /*   pointer to script info structure   */
UINT len,                          /*   length of output to handle         */
CHAR *bytes)                       /*   pointer to output to handle        */
{
     (VOID)scrptr;
     btuxct(ispunum,len,bytes);
     if (whomon == ispunum) {
          (*ftscope)(FTSOUS,bytes,len);
     }
     ispotk=lngtck;
}

static VOID
devisp(                            /* device command for SLIP-to-ISP       */
struct scrinf *scrptr,
CHAR *stg)
{
     (VOID)scrptr;
     btucmd(ispunum,stg);
}

static VOID
admisp(                            /* admin handler for SLIP-to-ISP        */
struct scrinf *scrptr,
CHAR *stg)
{
     INT unmsav;

     unmsav=usrnum;
     curusr(ispunum);
     if (audscr) {
          shocst(spr("ISP SCRIPT %s",stg),
                 "%s line %d: %s",scrfnm,scrptr->lin,scrptr->inplin);
     }
     curusr(usrnum=unmsav);
}

static VOID
audeos(VOID)                       /* audit end-of-session info            */
{                                  /*   expects curusr() to be in effect   */
     CHAR *whyend;

     ASSERT(usrnum == ispunum);
     if (audend) {
          whyend=ispptr->whyend;
          if (*whyend == '\0') {
               if (kilipg || errcod != 1) {
                    strcpy(whyend,"system shutdown");
               }
               else {
                    switch (status) {
                    case RING:
                         strcpy(whyend,"kill channel");
                         break;
                    case LOST2C:
                         strcpy(whyend,"lost carrier");
                         break;
                    case LOST25:
                    case SPXTDN:
                    case SPXWDG:
                         strcpy(whyend,"disconnect");
                         break;
                    default:
                         if (!(usroff(usrnum)->flags&ACTIVE)) {
                              strcpy(whyend,"inactivity timeout");
                         }
                         else {
                              sprintf(whyend,"status %d",status);
                         }
                         break;
                    }
               }
          }
          if (whomon == ispunum) {
               (*ftscope)(FTSCMT,spr("***** %s-to-ISP ends",nametbl[imethi]));
          }
          shocst(spr("%s-TO-ISP SESSION ENDS",nametbl[imethi]),
                 "%s bytes, %s seconds, %s",ul2as(ispinb+ispoub),
                                            ul2as(lngtck-ispntk),
                                            ispptr->whyend);
     }
}

static VOID
isprst(VOID)                       /* SLIP-to-ISP special reset handling   */
{
     INT rc;
     struct user *u;


     if (usrnum == ispunum) {
          u=usroff(usrnum);
          if (u->usrcls == BBSPRV && u->state == ispstt) {
               switch (u->substt) {
               case ISPSCR:
                    endscr(&ispptr->scrinf);
                    break;
               case ISPRUN:
                    if ((kilipg || (errcod != 1 && errcod < 20)) && ispddla > 0) {
                         u->substt=ISPDWN;
                         shochl(spr("%s-to-ISP delayed shutdown",
                                nametbl[imethi]),
                                uscchr[imethi],baudat(usrptr->baud,0));
                         ispdrem=ispddla;
                         return;   /* put off reset for this channel!      */
                    }
                    if ((rc=slppp_down()) != 0) {
                         catastro("SLPPP_DOWN error %d: %s",rc,tcpErrStg(rc));
                    }
                    audeos();
                    break;
               case ISPDWN:
                    return;        /* keep putting off reset!              */
               }
          }
     }
     (*oldrst)();
}

static VOID
ispfin(VOID)                       /* SLIP-to-ISP shutdown handling        */
{
     if (ispuptr->usrcls == BBSPRV
      && ispuptr->state == ispstt
      && ispuptr->substt == ISPDWN) {
          bturst(ispunum);         /* last chance for "resetting" channel  */
     }
}

