/***************************************************************************
 *                                                                         *
 *   GALMJD.C                                                              *
 *                                                                         *
 *   Copyright (c) 1993-1997 Galacticomm, Inc.  All Rights Reserved.       *
 *                                                                         *
 *   This is Worldgroup online portion of Doors.  This allows users        *
 *   to connect to external computers which are running Doors applications *
 *                                                                         *
 *                                   - B. Love and R. Skurnick 1/28/93     *
 *   C/S agent, support                         -D. Pitchford 11/13/95     *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "remote.h"
#include "gcspsrv.h"
#include "galmjd.h"

#define FILREV "$Revision: 11 $"

static GBOOL mjdinp(VOID);
static INT mjddrsp(VOID);
static VOID mjdipst(VOID);
static VOID mjdlst(INT retstt);
static VOID mjdentn(VOID);
static VOID mjdentb(VOID);
static VOID mjdente(VOID);
static VOID mjdentk(VOID);
static VOID mjdents(VOID);
static VOID mjdentt(VOID);
static VOID mjdenth(VOID);
static VOID mjdentm(VOID);
static VOID mjdentp(VOID);
static VOID mjdchkm(VOID);
static VOID mjdedta(VOID);
static VOID mjdedtd(VOID);
static VOID mjdedtb(VOID);
static VOID mjdedte(VOID);
static VOID mjdedtk(VOID);
static VOID mjdedts(VOID);
static VOID mjdedtt(VOID);
static VOID mjdedtm(VOID);
static VOID mjdedtp(VOID);
static VOID mjddela(VOID);
static VOID mjddelr(VOID);
static VOID mjdwchd(VOID);
static VOID mjdsttd(VOID);
static VOID mjdrtk(VOID);
static VOID mjdaddd(VOID);
static GBOOL mjdfpt(struct mjdapps *app,LONG fpos);
static INT mjdvalc(INT c);
static INT mjdavl(struct mjdapps *aptr);
static VOID mjddsp(struct mjdapps *aptr);
static INT mjdved(VOID);
static VOID mjdrsvp(VOID);
static VOID mjdrpm(INT news);
static VOID mjdpmt(INT news);
static INT mjdconfl(LONG fpos,INT unum);
static VOID mjdcon(INT msg);
static VOID mjdhtrm(INT msg);
static VOID mjdtrml(INT unum,INT rsv,INT msg);
static VOID mjdhwt(INT other);
static VOID mjdthn(VOID);
static VOID mjdhup(VOID);
static VOID mjdfin(VOID);

#define APPNSZ       9             /* maximum size of app name w/ '\0'     */
#define APPDSC      40             /* maximum size of app desc w/ '\0'     */
#define HSKSTG      10             /* maximum size of parse stg w/ '\0'    */

#define DOORSECS    10             /* seconds to wait for door to respond  */

/* BEGIN C/S AGENT PROTOTYPES AND DECLARATIONS */

static VOID init_cslib(VOID);
static VOID mjdread(INT direction,struct saunam *dpknam);
static VOID mjdwrite(struct saunam *dpknam,USHORT length,VOID *value);
static VOID mjdabort(VOID);
static UINT aps2vb(struct mjdapps *aps,struct vbmjdapps *vba);
static VOID vb2aps(struct vbmjdapps *vba,struct mjdapps *aps);
static GBOOL vfystr(struct mjdapps *aps);
static VOID csdinfo(VOID);
static VOID inidoor(VOID);
static GBOOL csmjdfpt(struct mjdapps *app,LONG fpos);
static VOID csses(VOID);
static VOID snd2door(INT hwnd,USHORT length,CHAR *value);
static CHAR *addlf(CHAR *txtbuf);

#define MJDAPPID "GALMJD"          /* Doors App-ID                         */

static
struct agent mjdagt={              /* agent information structure          */
     MJDAPPID,                     /*   appid                              */
     mjdread,                      /*   read-dynapak function pointer      */
     mjdwrite,                     /*   write-dynapak function pointer     */
     NULL,                         /*   file xfer-done function pointer    */
     mjdabort                      /*   abort-request function pointer     */
};

#define DOORDPK "sau:door "        /* door start (read) and write to dpk   */
#define INFOSFX "doorinfo"         /* door info dynapak suffix             */
#define INFODPK "sau:" INFOSFX " " /* door info dynapak name               */

struct vbmjdapps {                 /* Doors app data for VB                */
     SHORT begchn;                 /*   Starting channel for this app.     */
     SHORT endchn;                 /*   Ending channel for this app.       */
     SHORT flags;                  /*   Various bit flags                  */
     SHORT surchg;                 /*   Surcharge for using this app.      */
     SHORT hskmhd;                 /*   method to pass parameters 0-3      */
     SHORT timalw;                 /*   time allowed in the door/0=no limit*/
     CHAR ndkh[APPNSZ+APPDSC+KEYSIZ+HSKSTG+1]; /* aggregate strings (TAB)  */
};

struct flddef vbmjdappsFDA[]={
     {CVTFLD_SHORT  ,6   ,fldoff(vbmjdapps,begchn)   ,NULL},
     {CVTFLD_RTEXT  ,0   ,fldoff(vbmjdapps,ndkh)     ,NULL},
     {CVTFLD_END    ,0   ,0                          ,NULL}
};

static
struct mjdlst {                    /* ongoing request: list doors          */
     INT reqtyp;                   /*   request type (MJDLST)              */
     CHAR suffix[SFXSIZ];          /*   current suffix                     */
} *ml;

static
struct mjdses {                    /* ongoing request: door session        */
     INT reqtyp;                   /*   request type (MJDSES)              */
     CHAR app[APPNSZ];             /*   door being accessed                */
     INT hwnd;                     /*   trigger hwnd to use                */
     INT state;                    /*   connection state (like substt)     */
     INT othchn;                   /*   door channel in use                */
     INT surchg;                   /*   additional consumption rate        */
     LONG fpos;                    /*   user in door btv file position     */
     ULONG tim;                    /*   hrtval time last packet was sent   */
} *ms;

#define MJDLST 1                   /* request type: list doors             */
#define MJDSES 2                   /* request type: door session           */

/* END C/S AGENT PROTOTYPES AND DECLARATIONS */

INT mjdstt;
struct module mjrdoor={            /* module interface block               */
     "",                           /*    description for main menu         */
     NULL,                         /*    user logon supplemental routine   */
     mjdinp,                       /*    input routine if selected         */
     mjdthn,                       /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     mjdhup,                       /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     mjdfin                        /*    finish-up (sys shutdown) routine  */
};

                                   /* non message block related substates  */
#define LSTING      -1             /*   listing Doors                      */
#define LSTENT      -2             /*   list Doors for enter prompt        */
#define RSTING      -3             /*   waiting for rstchn() to complete   */
#define RSPPND      -4             /*   Door responding to initialization  */
#define BYTPST      -5             /*   passthrough mode active            */
#define WT4RST      -6             /*   setting prior to going idle        */
#define WT4PAU      -7             /*   give link some time to reset       */
#define WT4SEL      -8             /*   channel is in idle state           */
#define GETOUT      -9             /*   handle CONCEX when user was not src*/

static
CHAR vlddos[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$&'()-@^_`";

static
CHAR valhsk[]="ABCSTU";

static
CHAR *passtg[]={"","No Passing","Drop File","Parms Passed"};

static
CHAR *xfrbuf,                      /* transfer buffer for passthrough      */
     *sysapp,                      /* key required for sysop access        */
     *mjdakey;                     /* key required for c/s agent access    */

static
HMCVFILE mjdmb;                    /* Doors message file block pointer     */

static
DFAFILE *mjdbb;                    /* Doors btrieve file block pointer     */

struct mjdapps {                   /* Doors valid application data         */
     SHORT flags;                  /*   Various bit flags                  */
     SHORT begchn;                 /*   Starting channel for this app.     */
     SHORT endchn;                 /*   Ending channel for this app.       */
     SHORT surchg;                 /*   Surcharge for using this app.      */
     SHORT hskmhd;                 /*   method to pass parameters 0-3      */
     SHORT timalw;                 /*   time allowed in the door/0=no limit*/
     CHAR appname[APPNSZ];         /*   Name of application                */
     CHAR appdesc[APPDSC];         /*   Short description of application   */
     CHAR keyreq[KEYSIZ];          /*   Key required to use this app.      */
     CHAR hskstg[HSKSTG];          /*   parse string for sending parms     */
};

                                   /* Definition of bit flags for datafile */
#define MJDDIS      1              /*   application is currently disabled  */
#define MJDNOH      2              /*   does not require handshaking       */
#define MJDCAB      4              /*   user can "abort" this application  */
#define MJDXCL      8              /*   only one user in Door at a time    */

static
struct mjdapps mjdedt,             /* work area for add/modify/delete app  */
               *mjddsk;            /* btrieve transfer area - one shot use */

struct mjdusr {                    /* Doors user data structure            */
     INT flags;                    /*   Various bit flags                  */
     INT othchn;                   /*   channel this is linked to          */
     INT wttime;                   /*   time to wait for door to respond   */
     INT discnt;                   /*   number of DISCHR's received        */
     INT distmr;                   /*   disconnect count timer             */
     INT w4rst;                    /*   on a link channel time to clear    */
     LONG fpos;                    /*   file position in app database      */
     struct mjdapps app;           /*   application associated to this link*/
     CHAR cntd;                    /*   are any apps available to list     */
     CHAR dftinp;                  /*   default input character            */
     struct mjdses *ses;           /*   c/s session (if MJDCSL flag)       */
};

                                   /* Definition of bit flags for userdata */
#define MJDLNK      1              /*   this channel is a link channel     */
#define MJDCSL      2              /*   this link channel is hooked to c/s */

static
INT mjdinu=-1;                     /* apps editor in use -1=no or usrnum   */

                                   /* Options from GALMJD.MSG              */
INT rlsprt;                        /*   release port after door termination*/
CHAR dischr;                       /*   character signaling a disconnect   */

#define mjdptr ((struct mjdusr *)vdaptr)    /* volatile data pointer casts */
#define mjdarr(u) ((struct mjdusr *)vdaoff(u))

VOID EXPORT
init__galmjd(VOID)                 /* initialize Doors module              */
{
     stzcpy(mjrdoor.descrp,gmdnam("GALMJD.MDF"),MNMSIZ);
     mjdstt=register_module(&mjrdoor);
     mjdmb=opnmsg("galmjd.mcv");
     mjdbb=dfaOpen("galmjd2.dat",sizeof(struct mjdapps),NULL);
     mjddsk=(struct mjdapps *)alcmem(sizeof(struct mjdapps));
     xfrbuf=alcmem(outbsz);
     dclvda(sizeof(struct mjdusr));
     dischr=chropt(DISCHR);
     rlsprt=ynopt(RLSPRT);
     sysapp=stgopt(SYSAPP);
     init_cslib();
     rtkick(1,mjdrtk);
}

static GBOOL
mjdinp(VOID)                       /* input status handler for Doors       */
{
     setmbk(mjdmb);
     dfaSetBlk(mjdbb);
     chkdft(mjdptr->dftinp);
     if ((margc == 0 || (usrptr->flags&INJOIP)) && !(mjdptr->flags&MJDLNK)) {
          if (usrptr->substt == GETOUT) {
               btulok(usrnum,0);
               return(FALSE);
          }
          else if (usrptr->substt != LSTING && usrptr->substt != LSTENT) {
               if (usrptr->substt < 0) {
                    return(TRUE);
               }
               numcat=maxcat+1;
               mjdpmt(usrptr->substt);
          }
          else {
               mjdptr->fpos=0L;
               btuclo(usrnum);
               mjdpmt(usrptr->substt == LSTING ? INTPMT : WCHAPP);
          }
     }
     else if (margc == 1 && sameas(margv[0],"X") && !(mjdptr->flags&MJDLNK)) {
          btumil(usrnum,DFTIMX);
          if (usrnum == mjdinu) {
               mjdinu=-1;
          }
          switch (usrptr->substt) {
          case INTRO:
          case INTROS:
          case INTPMT:
          case INTPMTS:
               return(FALSE);
          default:
               if (!(usrptr->flags&CONCEX)) {
                    numcat=maxcat+1;
                    mjdpmt(INTPMT);
               }
               condex();
          }
     }
     else {
          do {
               bgncnc();
               switch (usrptr->substt) {
               case 0:
                    cncchr();
                    usrptr->flags&=~X2MAIN;
                    mjdpmt(INTRO);
                    break;
               case INTRO:
               case INTROS:
               case INTPMT:
               case INTPMTS:
                    switch (cncchr()) {
                    case '?':
                         cncall();
                         mjdpmt(INTRO);
                         break;
                    case 'E':
                         mjdpmt(WCHAPP);
                         break;
                    case 'L':
                         mjdlst(LSTING);
                         break;
                    case 'A':
                         if (mjdved()) {
                              setmem(&mjdedt,sizeof(struct mjdapps),0);
                              mjdpmt(ENTAPP);
                         }
                         break;
                    case 'M':
                         if (mjdved()) {
                              mjdpmt(MODAPP);
                         }
                         break;
                    case 'D':
                         if (mjdved()) {
                              mjdpmt(DELAPP);
                         }
                         break;
                    default:
                         cncall();
                         prfmsg(CNOTIL);
                         mjdpmt(usrptr->substt);
                    }
                    break;
               case ENTAPP:
                    mjdentn();
                    break;
               case ENTDSC:
                    strcpy(mjdedt.appdesc,cncall());
                    mjdpmt(BEGCHN);
                    break;
               case BEGCHN:
                    mjdentb();
                    break;
               case ENDCHN:
                    mjdente();
                    break;
               case ENTKEY:
                    mjdentk();
                    break;
               case ENTSUR:
                    mjdents();
                    break;
               case ENTTIM:
                    mjdentt();
                    break;
               case ENTHSK:
                    mjdenth();
                    break;
               case ENTHMT:
                    mjdentm();
                    break;
               case ENTHST:
                    mjdentp();
                    break;
               case MODAPP:
                    mjdchkm();
                    break;
               case EDTAPP:
               case EDTPMT:
                    mjdedta();
                    break;
               case MODDSC:
                    mjdedtd();
                    break;
               case MODBCH:
                    mjdedtb();
                    break;
               case MODECH:
                    mjdedte();
                    break;
               case MODKEY:
                    mjdedtk();
                    break;
               case MODSUR:
                    mjdedts();
                    break;
               case MODTIM:
                    mjdedtt();
                    break;
               case MODHMT:
                    mjdedtm();
                    break;
               case MODHST:
                    mjdedtp();
                    break;
               case DELAPP:
                    mjddela();
                    break;
               case DELRUS:
                    mjddelr();
                    break;
               case WCHAPP:
                    mjdwchd();
                    break;
               case LSTING:
               case LSTENT:
               case WT4RST:
               case WT4PAU:
               case WT4SEL:
                    prf("");
                    break;
               case RSPPND:
                    if (!mjddrsp()) {
                         return(TRUE);
                    }
                    mjdipst();
                    return(TRUE);
               case BYTPST:
                    return(TRUE);
               case GETOUT:
                    btulok(usrnum,0);
                    return(FALSE);
               default:
                    cncall();
               }
          } while (!endcnc());
     }
     outprf(usrnum);
     return(TRUE);
}

static INT
mjddrsp(VOID)                      /* response from door to initialize     */
{
     INT unum;

     cncall();
     if (margc == 1 && sameas(margv[0],"OK")) {
          return(1);
     }
     if (!(mjdptr->flags&MJDCSL)) {
          unum=usrnum;
          curusr(mjdptr->othchn);
          mjdcon(INVRSP);
          curusr(unum);
     }
     mjdrsvp();
     return(0);
}

static VOID
mjdipst(VOID)                      /* initiate character passthrough       */
{
     INT othchn;
     struct user *uptr;
     CHAR buf[80];

     if (!(mjdptr->flags&MJDCSL)) {
          othchn=usrnum;
          curusr(mjdptr->othchn);
          prfmsg(CONNTD);
          if (mjdarr(mjdptr->othchn)->app.flags&MJDCAB) {
               if (dischr != ' ') {
                    prfmsg(TODABT,dischr);
               }
          }
          outprf(usrnum);
          curusr(othchn);
     }
     uptr=usroff(mjdptr->othchn);
     if (mjdptr->flags&MJDCSL) {
          uptr->crdrat+=(mjdptr->ses->surchg=mjdptr->app.surchg);
     }
     else {
          uptr->crdrat+=mjdptr->app.surchg;
          uptr->flags|=NOINJO;
     }
     mjdptr->wttime=0;
     sprintf(buf,"%s Door Active [%02X]",mjdptr->app.appname,
                                         channel[mjdptr->othchn]);
     shochl(buf,'D',baudat(usrptr->baud,0));
     if (!(mjdptr->flags&MJDCSL)) {
          btulok(mjdptr->othchn,0);
     }
     btuinj(usrnum,CYCLE);
     usrptr->substt=BYTPST;
     if (!(mjdptr->flags&MJDCSL)) {
          btubsz(usrnum,outbsz,INPSIZ);
#ifdef GCDOS
          btuhwh(usrnum,outbsz-20);
#endif
          btutrg(usrnum,outbsz-1);
#ifdef GCDOS
          btuhwh(mjdptr->othchn,INPSIZ-20);
#endif
          btutrg(mjdptr->othchn,INPSIZ-1);
          btuinj(mjdptr->othchn,CYCLE);
          uptr->substt=BYTPST;
     }
     else {
          btubsz(usrnum,outbsz/2,outbsz/2);
#ifdef GCDOS
          btuhwh(usrnum,outbsz/2-20);
#endif
          btutrg(usrnum,outbsz/2-1);
          mjdptr->ses->state=BYTPST;
     }
}

static VOID
mjdlst(                            /* begin list of all avail apps         */
INT retstt)
{
     mjdptr->cntd=0;
     if (dfaAcqLO(mjddsk,0)) {
          if (mjdavl(mjddsk)) {
               mjddsp(mjddsk);
          }
          else {
               prf("");
          }
          mjdptr->fpos=dfaAbs();
          btuinj(usrnum,CYCLE);
          usrptr->substt=retstt;
     }
     else {
          prfmsg(NONAVL);
          mjdpmt(INTPMT);
     }
}

static VOID
mjdentn(VOID)                      /* enter an application name            */
{
     CHAR *ptr;

     cncall();
     if (margc != 1) {
          prfmsg(ONEWRD);
          mjdpmt(ENTAPP);
          return;
     }
     for (ptr=strupr(input) ; *ptr != '\0' ; ptr++) {
          if (!mjdvalc(*ptr)) {
               prfmsg(INVCHR);
               mjdpmt(ENTAPP);
               return;
          }
     }
     if (strlen(input) > APPNSZ-1) {
          prfmsg(TOOLNG,APPNSZ-1);
          mjdpmt(ENTAPP);
     }
     else if (dfaQueryEQ(input,0)) {
          prfmsg(DUPAPP);
          mjdpmt(ENTAPP);
     }
     else {
          stzcpy(mjdedt.appname,input,APPNSZ);
          mjdpmt(ENTDSC);
     }
}

static VOID
mjdentb(VOID)                      /* enter first channel to access app    */
{
     INT begchn,msg=ENDCHN;

     if ((begchn=cnchex()) < 0 || begchn > 255) {
          cncall();
          prfmsg(INVHEX2);
          msg=usrptr->substt;
     }
     else {
          mjdedt.begchn=begchn;
     }
     mjdpmt(msg);
}

static VOID
mjdente(VOID)                      /* enter last channel to access app     */
{
     INT endchn,msg=ENTKEY;

     if ((endchn=cnchex()) < 0 || endchn > 255) {
          cncall();
          prfmsg(INVHEX2);
          msg=usrptr->substt;
     }
     else if (endchn < mjdedt.begchn) {
          cncall();
          prfmsg(LESHEX);
          msg=usrptr->substt;
     }
     else {
          mjdedt.endchn=endchn;
     }
     mjdpmt(msg);
}

static VOID
mjdentk(VOID)                      /* enter key required to access app     */
{
     INT msg=ENTSUR;
     CHAR *keyptr;

     if (morcnc() != '.') {
          keyptr=cncwrd();
          if (keynam(keyptr) == 1) {
               strcpy(mjdedt.keyreq,keyptr);
          }
          else {
               prfmsg(INVKEY);
               msg=usrptr->substt;
          }
     }
     else {
          cncchr();
     }
     mjdpmt(msg);
}

static VOID
mjdents(VOID)                      /* enter surcharge for this app         */
{
     mjdedt.surchg=atoi(cncall());
     mjdpmt(ENTTIM);
}

static VOID
mjdentt(VOID)                      /* enter max time allowed in door       */
{
     INT msg=ENTHSK;
     INT timlmt;

     if (isdigit(morcnc())) {
          timlmt=cncint();
          if (timlmt > -1 && timlmt <= 1440) {
               mjdedt.timalw=timlmt;
          }
          else {
               prfmsg(INVTIM);
               msg=ENTTIM;
          }
     }
     else {
          cncall();
          prfmsg(INVNUM);
          msg=ENTTIM;
     }
     mjdpmt(msg);
}

static VOID
mjdenth(VOID)                      /* enter handshake defaults             */
{
     CHAR c;

     if ((c=cncyesno()) == 'Y') {
          mjdpmt(ENTHMT);
     }
     else if (c == 'N') {
          mjdedt.flags|=MJDNOH;
          mjdaddd();
     }
     else {
          prfmsg(YESONO);
          mjdpmt(ENTHSK);
     }
     cncall();
}

static VOID
mjdentm(VOID)                      /* check for method of passing parms    */
{
     INT opt;

     if ((opt=cncint()) > 0 && opt < 4) {
          if ((mjdedt.hskmhd=opt) == 3) {
               mjdpmt(ENTHST);
          }
          else {
               mjdaddd();
          }
     }
     else {
          cncall();
          mjdpmt(ENTHMT);
     }
}

static VOID
mjdentp(VOID)                      /* validate string to pass              */
{
     CHAR *ptr;

     cncall();
     if (strlen(input) > HSKSTG-1) {
          prfmsg(INVHST,HSKSTG-1);
          mjdpmt(ENTHST);
     }
     else {
          for (ptr=strupr(input) ; *ptr != NULL ; ptr++) {
               if (strchr(valhsk,*ptr) == NULL) {
                    prfmsg(INVBYT);
                    mjdpmt(ENTHST);
                    return;
               }
          }
          stzcpy(mjdedt.hskstg,input,HSKSTG);
          mjdaddd();
     }
}

static VOID
mjdchkm(VOID)                      /* check for application to modify      */
{
     INT msg=EDTAPP;
     CHAR *stg;

     if (strlen((stg=cncwrd())) > APPNSZ-1) {
          prfmsg(TOOLNG);
          msg=usrptr->substt;
     }
     else if (dfaQueryEQ(strupr(stg),0)) {
          dfaAbsRec(&mjdedt,0);
     }
     else {
          cncall();
          prfmsg(NOSAPP);
          msg=usrptr->substt;
     }
     mjdpmt(msg);
}

static VOID
mjdedta(VOID)                      /* select an app option to be modified  */
{
     static INT msgno[]={0,0,MODDSC,MODBCH,MODECH,MODKEY,
                         MODSUR,MODTIM,0,0,0,MODHMT,MODHST};
     CHAR c;
     INT opt,msg=EDTAPP;

     if ((c=toupper(morcnc())) == 'S') {
          cncchr();
          dfaGetEQ(NULL,mjdedt.appname,0);
          dfaUpdate(&mjdedt);
          mjdinu=-1;
          prfmsg(APPUPD);
          msg=INTPMT;
     }
     else if (c == '?') {
          cncchr();
     }
     else if ((opt=cncint()) > 0 && opt < 13) {
          if (((mjdedt.flags&MJDNOH) && (opt == 11 || opt == 12))
            || (mjdedt.hskmhd != 3 && opt == 12)) {
               cncall();
               prfmsg(CNOTIL);
               mjdpmt(EDTPMT);
               return;
          }
          if (msgno[opt] != 0) {
               msg=msgno[opt];
          }
          else {
               switch (opt) {
               case 1:
                    mjdedt.flags^=MJDDIS;
                    break;
               case 8:
                    mjdedt.flags^=MJDNOH;
                    if (mjdedt.flags&MJDNOH) {
                         mjdedt.hskmhd=1;
                         mjdedt.hskstg[0]='\0';
                    }
                    else {
                         msg=MODHMT;
                    }
                    break;
               case 9:
                    mjdedt.flags^=MJDCAB;
                    break;
               case 10:
                    mjdedt.flags^=MJDXCL;
                    break;
               }
          }
     }
     else {
          cncall();
          prfmsg(CNOTIL);
          msg=EDTPMT;
     }
     mjdpmt(msg);
}

static VOID
mjdedtd(VOID)                      /* modify application description       */
{
     if (morcnc() == '.') {
          cncall();
     }
     else {
          strcpy(mjdedt.appdesc,cncall());
     }
     mjdpmt(EDTPMT);
}

static VOID
mjdedtb(VOID)                      /* modify application first channel     */
{
     INT begchn,msg=EDTPMT;

     if (morcnc() == '.') {
          cncall();
     }
     else if ((begchn=cnchex()) < 0 || begchn > 255) {
          cncall();
          prfmsg(INVHEX2);
          msg=usrptr->substt;
     }
     else if (begchn > mjdedt.endchn) {
          msg=usrptr->substt;
          cncall();
          prfmsg(LESHEX);
     }
     else {
          mjdedt.begchn=begchn;
     }
     mjdpmt(msg);
}

static VOID
mjdedte(VOID)                      /* modify application last channel      */
{
     INT endchn,msg=EDTPMT;

     if (morcnc() == '.') {
          cncall();
     }
     else if ((endchn=cnchex()) < 0 || endchn > 255) {
          cncall();
          prfmsg(INVHEX2);
          msg=usrptr->substt;
     }
     else if (endchn < mjdedt.begchn) {
          msg=usrptr->substt;
          cncall();
          prfmsg(LESHEX);
     }
     else {
          mjdedt.endchn=endchn;
     }
     mjdpmt(msg);
}

static VOID
mjdedtk(VOID)                      /* modify key required to access app     */
{
     CHAR c,*keyptr;
     INT msg=EDTPMT;

     if ((c=morcnc()) == '.') {
          cncall();
     }
     else if (c == '~') {
          cncchr();
          mjdedt.keyreq[0]='\0';
     }
     else {
          keyptr=cncwrd();
          if (keynam(keyptr) == 1) {
               strcpy(mjdedt.keyreq,keyptr);
          }
          else {
               prfmsg(INVKEY);
               msg=usrptr->substt;
          }
     }
     mjdpmt(msg);
}

static VOID
mjdedts(VOID)                      /* modify surcharge for this app        */
{
     if (morcnc() == '.') {
          cncall();
     }
     else {
          mjdedt.surchg=atoi(cncall());
     }
     mjdpmt(EDTPMT);
}

static VOID
mjdedtt(VOID)                      /* modify time limit for this app       */
{
     INT timlmt,msg=EDTPMT;
     CHAR c;

     if ((c=morcnc()) == '.') {
          cncall();
     }
     else if (!isdigit(c)) {
          prfmsg(INVNUM);
          msg=MODTIM;
          cncall();
     }
     else {
          timlmt=cncint();
          if (timlmt > -1 && timlmt <= 1440) {
               mjdedt.timalw=timlmt;

          }
          else {
               prfmsg(INVTIM);
               msg=MODTIM;
          }
     }
     mjdpmt(msg);
}

static VOID
mjdedtm(VOID)                      /* modify handshaking parameter method  */
{
     INT opt,msg=EDTAPP;

     if ((opt=cncint()) > 0 && opt < 4) {
          if ((mjdedt.hskmhd=opt) == 3) {
               msg=MODHST;
          }
     }
     else {
          cncall();
     }
     mjdpmt(msg);
}

static VOID
mjdedtp(VOID)                      /* validate new string to pass          */
{
     CHAR *ptr;

     cncall();
     if (strlen(input) > HSKSTG-1) {
          prfmsg(INVHST,HSKSTG-1);
          mjdpmt(MODHST);
     }
     else {
          for (ptr=strupr(input) ; *ptr != NULL ; ptr++) {
               if (strchr(valhsk,*ptr) == NULL) {
                    prfmsg(INVBYT);
                    mjdpmt(MODHST);
                    return;
               }
          }
          stzcpy(mjdedt.hskstg,input,HSKSTG);
          mjdpmt(EDTAPP);
     }
}

static VOID
mjddela(VOID)                      /* check application before deletion    */
{
     INT msg=DELRUS;
     CHAR *stg;

     if (strlen((stg=cncwrd())) > APPNSZ-1) {
          prfmsg(TOOLNG);
          msg=usrptr->substt;
     }
     else if (dfaQueryEQ(strupr(stg),0)) {
          dfaGetAbs(&mjdedt,(mjdptr->fpos=dfaAbs()),0);
     }
     else {
          cncall();
          prfmsg(NOSAPP);
          msg=usrptr->substt;
     }
     mjdpmt(msg);
}

static VOID
mjddelr(VOID)                      /* confirm application deletion         */
{
     INT usn;

     if (cncyesno() == 'Y') {
          if ((usn=mjdconfl(mjdptr->fpos,usrnum)) != -1) {
               prfmsg(CONFDEL,uacoff(usn)->userid);
          }
          else {
               dfaGetAbs(NULL,mjdptr->fpos,0);
               dfaDelete();
               prfmsg(APPDEL);
          }
     }
     cncall();
     if (usrnum == mjdinu) {
          mjdinu=-1;
     }
     mjdpmt(INTPMT);
}

static VOID
mjdwchd(VOID)                      /* select Door to run                   */
{
     CHAR *stg;
     INT msg;
     LONG creds,fpos;

     if (morcnc() == '?') {
          cncall();
          mjdlst(LSTENT);
          return;
     }
     strupr(stg=cncwrd());
     if (strlen(stg) > APPNSZ-1) {
          prfmsg(TOOLNG);
          msg=usrptr->substt;
     }
     else if (dfaAcqEQ(mjddsk,stg,0) && haskey(mjddsk->keyreq)) {
          fpos=dfaAbs();
          cncall();
          if (mjdavl(mjddsk)) {
               if (!(mjddsk->flags&MJDXCL) || (mjdconfl(fpos,-1) == -1)) {
                    creds=usrptr->crdrat+mjddsk->surchg;
                    if (creds <= 0L || tstcrd(creds)) {
                         if (mjdfpt(mjddsk,fpos)) {
                              return;
                         }
                         else {
                              prfmsg(NOCHNA);
                              msg=INTPMT;
                         }
                    }
                    else {
                         prfmsg(LKCRED);
                         msg=WCHAPP;
                    }
               }
               else {
                    prfmsg(DOORIN);
                    msg=WCHAPP;
               }
          }
          else {
               prfmsg(NOTAVL);
               msg=WCHAPP;
          }
     }
     else {
          cncall();
          prfmsg(NOSAPP);
          msg=WCHAPP;
     }
     mjdpmt(msg);
}


static VOID
mjdsttd(VOID)                      /* start talking to the link            */
{
     CHAR *ptr,*hskptr;
     CHAR stg[APPNSZ+10+80];

     btulok(usrnum,0);
     if (mjdptr->app.flags&MJDNOH) {
          mjdipst();
     }
     else{
          othuap=uacoff(mjdptr->othchn);
          sprintf(stg,"MBBS: %s %d",mjdptr->app.appname,mjdptr->app.hskmhd);
          switch (mjdptr->app.hskmhd) {
          case 2:
               strcat(stg," '");
               strcat(stg,othuap->userid);
               strcat(stg,"'");
               if (mjdptr->app.timalw == 0) {
                    strcat(stg,spr(" %d",32767));
               }
               else {
                    strcat(stg,spr(" %d",mjdptr->app.timalw));
               }
               if ((mjdptr->flags&MJDCSL) || (othuap->ansifl&ANSON)) {
                    strcat(stg," GR");
               }
               else {
                    strcat(stg," NG");
               }
               break;
          case 3:
               for (ptr=mjdptr->app.hskstg ; *ptr != '\0' ; ptr++) {
                    if ((hskptr=strchr(valhsk,*ptr)) != NULL) {
                         switch (*hskptr) {
                         case 'A':
                              if ((mjdptr->flags&MJDCSL) || (othuap->ansifl&ANSON)) {
                                   strcat(stg," GR");
                              }
                              else {
                                   strcat(stg," NG");
                              }
                              break;
                         case 'B':
                              strcat(stg," BAUD");
                              break;
                         case 'C':
                              strcat(stg," COM");
                              break;
                         case 'S':
                              strcat(stg," SHORT");
                              break;
                         case 'T':
                              strcat(stg," TIME=");
                              if (mjdptr->app.timalw == 0) {
                                   strcat(stg,spr("%d",32767));
                              }
                              else {
                                   strcat(stg,spr("%d",mjdptr->app.timalw));
                              }
                              break;
                         case 'U':
                              strcat(stg," '");
                              strcat(stg,othuap->userid);
                              strcat(stg,"'");
                              break;
                         }
                    }
               }
               break;
          }
          strcat(stg,"\r");
          btuxmt(usrnum,stg);
          mjdptr->wttime=DOORSECS;
          usrptr->substt=RSPPND;
     }
}

static VOID
mjdrtk(VOID)                       /* once a second rtkick - multi-purpose */
{
     struct mjdusr *mjdoth;
     struct user *ousp;
     INT ousr;
     LONG creds;

     setmbk(mjdmb);
     for (ousr=0 ; ousr < nterms ; ousr++) {
          ousp=usroff(ousr);
          if (ousp->state == mjdstt) {
               mjdoth=mjdarr(ousr);
               if (ousp->substt == BYTPST && !(mjdoth->flags&MJDLNK)
                 && (creds=ousp->crdrat+mjdoth->app.surchg) > 0L) {
                    if (!otstcrd(ousr,creds,0)) {
                         curusr(ousr);
                         mjdhtrm(OUTCRD2);
                         continue;
                    }
               }
               if (mjdoth->discnt == 0 || mjdoth->discnt > 3) {
                    mjdoth->discnt=mjdoth->distmr=0;
               }
               else {
                    mjdoth->distmr++;
               }
               if (mjdoth->discnt < 3 && mjdoth->distmr > 2) {
                    mjdoth->discnt=mjdoth->distmr=0;
               }
               else if (mjdoth->discnt == 3 && mjdoth->distmr > 3) {
                    mjdoth->discnt=mjdoth->distmr=0;
                    curusr(ousr);
                    mjdhtrm(APPTRM2);
               }
               if (ousp->substt == RSPPND) {
                    if (mjdoth->wttime > 0) {
                         if (--mjdoth->wttime == 0) {
                              if (mjdoth->flags&MJDCSL) {
                                   curusr(othusn=mjdoth->othchn);
                                   prfmsg(TIMOUT);
                                   injoth();
                              }
                              else {
                                   curusr(mjdoth->othchn);
                                   mjdcon(TIMOUT);
                              }
                              curusr(ousr);
                              mjdrsvp();
                         }
                    }
               }
               if (ousp->substt == WT4PAU) {
                    if (--mjdoth->w4rst == 0) {
                         btulok(ousr,0);
                         ousp->substt=WT4SEL;
                    }
               }
               if (kilipg == 1 && kilctr == -1
                 && (ousp->substt == WT4SEL || ousp->substt == WT4PAU)) {
                    curusr(ousr);
                    rstchn();
               }
          }
     }
     rtkick(1,mjdrtk);
}

static VOID
mjdaddd(VOID)                      /* add Door to database                 */
{

     mjdedt.flags|=MJDCAB;
     dfaInsert(&mjdedt);
     prfmsg(THEAPP1,mjdedt.appname,mjdedt.appdesc,spr("%X",mjdedt.begchn),
                    spr("%X",mjdedt.endchn),
                    mjdedt.keyreq[0] == '\0' ? "<none>" : mjdedt.keyreq,
                    mjdedt.surchg,
                    mjdedt.timalw == 0 ? "<no limit>"
                                       : spr("%d minute(s)",mjdedt.timalw),
                    (mjdedt.flags&MJDNOH) ? "No" : "Yes",
                    (mjdedt.flags&MJDNOH) ? ""
                                          : spr("(%s)",passtg[mjdedt.hskmhd]));

     mjdinu=-1;
     btumil(usrnum,DFTIMX);
     mjdpmt(INTRO);
}

static GBOOL                       /*   returns success/failure            */
mjdfpt(                            /* find and sieze port for app          */
struct mjdapps *app,               /*   app being used                     */
LONG fpos)                         /*   abspos of door's btv record        */
{
     INT chn,unum;
     ULONG flags;

     for (chn=app->begchn ; chn <= app->endchn ; chn++) {
          if ((othusn=usridx(chn)) != -1) {
               othusp=usroff(othusn);
               if ((othusp->usrcls == BBSPRV && othusp->state == mjdstt
                 && othusp->substt == WT4SEL)
                 || (othusp->usrcls == VACANT && (othusp->flags&ISRIAL)
                 && othusp->state == AWAITC && !(othusp->flags&NOHDWE))
                 || (othusp->usrcls == VACANT && (othusp->flags&IS2698)
                 && othusp->state == AWAITC && !(othusp->flags&NOHDWE)
                 && (app->flags&MJDNOH) && haskey(sysapp))) {
                    strcpy(mjdptr->app.appname,app->appname);
                    mjdptr->othchn=othusn;
                    mjdptr->fpos=fpos;
                    unum=usrnum;
                    curusr(othusn);
                    flags=usrptr->flags;
                    rstchn();
                    usrptr->flags=(flags|NOZAP|NOINJO);
                    usrptr->usrcls=BBSPRV;
                    usrptr->state=mjdstt;
                    usrptr->substt=RSTING;
                    setmem(mjdptr,sizeof(struct mjdusr),0);
                    mjdptr->flags|=MJDLNK;
                    mjdptr->othchn=unum;
                    movmem(app,&(mjdptr->app),sizeof(struct mjdapps));
                    shochl(spr("Opening Door for channel %02X",channel[unum]),
                           'O',baudat(usrptr->baud,0));
                    curusr(unum);
                    btulok(usrnum,1);
                    btucli(usrnum);
                    mjdpmt(WT4APP);
                    return(TRUE);
               }
          }
     }
     return(FALSE);
}

static INT
mjdvalc(                           /* is application character valid       */
INT c)
{
     return(strchr(vlddos,c) != NULL);
}

static INT
mjdavl(                            /* is application online?               */
struct mjdapps *aptr)
{
     return((!(aptr->flags&MJDDIS) || haskey(sysapp)) && haskey(aptr->keyreq));
}

static VOID
mjddsp(                            /* display 1 application listing        */
struct mjdapps *aptr)
{
     if (!mjdptr->cntd) {
          prfmsg(APPHDR);
          mjdptr->cntd=1;
     }
     prfmsg(APPDET,aptr->appname,aptr->appdesc,
                   aptr->surchg,(aptr->flags&MJDDIS) ? "(offline)" : "");
}

static INT
mjdved(VOID)                       /* Is user allowed into apps editor     */
{
     if (haskey(sysapp)) {
          if (mjdinu > -1) {
               cncall();
               prfmsg(EDTINU);
               mjdpmt(usrptr->substt);
               return(0);
          }
          mjdinu=usrnum;
          return(1);
     }
     cncall();
     prfmsg(CNOTIL);
     mjdpmt(usrptr->substt);
     return(0);
}

static VOID
mjdrsvp(VOID)                      /* resetting link & host w/poss reserve */
{
     ULONG flags;

     if (mjdptr->flags&MJDCSL) {
          if (mjdptr->ses != NULL) {
               mjdptr->ses->app[0]='\0';
          }
     }
     else {
          mjdarr(mjdptr->othchn)->app.appname[0]='\0';
          mjdarr(mjdptr->othchn)->fpos=0L;
     }
     flags=usrptr->flags;
     rstchn();
     setmem(mjdptr,sizeof(struct mjdusr),0);
     if (rlsprt || kilipg || !(flags&ISRIAL)) {
          return;
     }
     usrptr->flags=(flags|NOZAP|NOINJO);
     usrptr->usrcls=BBSPRV;
     usrptr->state=mjdstt;
     usrptr->substt=WT4RST;
     btuclo(usrnum);
     shochl("Door Reserved Channel",'R',baudat(usrptr->baud,0));
     mjdptr->flags|=MJDLNK;
}

static VOID
mjdrpm(                            /* reprompt for specified usrnum        */
INT news)                          /* (no parameters allowed for prfmsg()) */
{
     if (isripu()) {
          if (news == INTPMT) {
               news=INTRO;
          }
          else if (news == INTPMTS) {
               news=INTROS;
          }
     }
     prfmsg(usroff(usrnum)->substt=news);
     mjdarr(usrnum)->dftinp=getdft();
     outprf(usrnum);
}

static VOID
mjdpmt(                            /* prompt user and set substate         */
INT news)
{
     INT hiopt;

     if (isripu()) {
          switch (news) {
          case INTPMT:
               news=INTRO;
               break;
          case INTPMTS:
               news=INTROS;
               break;
          case EDTPMT:
               news=EDTAPP;
          }
     }
     usrptr->substt=news;
     if (!morcnc()) {
          switch(usrptr->substt) {
          case INTRO:
               condex();
               if (haskey(sysapp)) {
                    usrptr->substt=INTROS;
               }
               prfmsg(usrptr->substt);
               break;
          case INTPMT:
               condex();
               if (haskey(sysapp)) {
                    usrptr->substt=INTPMTS;
               }
               prfmsg(usrptr->substt);
               break;
          case ENTAPP:
          case MODAPP:
          case DELAPP:
          case WCHAPP:
               btumil(usrnum,APPNSZ-1);
               prfmsg(news);
               break;
          case ENTDSC:
          case MODDSC:
               btumil(usrnum,APPDSC-1);
               prfmsg(news);
               break;
          case ENTKEY:
          case MODKEY:
               btumil(usrnum,KEYSIZ-1);
               prfmsg(news);
               break;
          case ENTHST:
          case MODHST:
               btumil(usrnum,HSKSTG-1);
               prfmsg(news);
               break;
          case EDTAPP:
               btumil(usrnum,DFTIMX);
               prfmsg(news,mjdedt.appname,
                          (mjdedt.flags&MJDDIS) ? "Offline" : "Available",
                           mjdedt.appdesc,
                           spr("%X",mjdedt.begchn),
                           spr("%X",mjdedt.endchn),
                           mjdedt.keyreq[0] == '\0' ? "<none>" : mjdedt.keyreq,
                           mjdedt.surchg,
                           mjdedt.timalw == 0 ? "<no limit>"
                                              : spr("%d minute(s)",mjdedt.timalw),
                          (mjdedt.flags&MJDNOH) ? "Disabled" : "Enabled",
                          (mjdedt.flags&MJDCAB) ? "Enabled" : "Disabled",
                          (mjdedt.flags&MJDXCL) ? "Yes" : "No");
               if (!(mjdedt.flags&MJDNOH)) {
                    prfmsg(EDTAPP1,passtg[mjdedt.hskmhd]);
                    if (mjdedt.hskmhd == 3) {
                         prfmsg(EDTAPP2,mjdedt.hskstg);
                    }
               }
               prfmsg(EDTFTR);
               break;
          case EDTPMT:
               btumil(usrnum,DFTIMX);
               if (mjdedt.flags&MJDNOH) {
                    hiopt=10;
               }
               else if (mjdedt.hskmhd != 3) {
                    hiopt=11;
               }
               else {
                    hiopt=12;
               }
               prfmsg(news,hiopt);
               break;
          case DELRUS:
               btumil(usrnum,DFTIMX);
               prfmsg(news,mjdedt.appname);
               break;
          default:
               btumil(usrnum,DFTIMX);
               prfmsg(news);
          }
          mjdptr->dftinp=getdft();
     }
}

static INT                         /*   returns usrnum of user in door or-1*/
mjdconfl(                          /* is door in use?                      */
LONG fpos,                         /*   btrieve abspos() of door's record  */
INT unum)                          /*   usrnum to exclude (A/A), -1==none  */
{
     INT oth;
     struct user *op;
     INT ureq,sreq;
     struct mjdses *mses;

     for (op=usroff(oth=0) ; oth < nterms ; op=usroff(++oth)) {
          if (op->usrcls > SUPIPG) {
               if (op->flags&ISGCSU) {
                    for (ureq=0 ; ureq < MAXREQS ; ureq++) {
                         sreq=srvrqid(oth,ureq);
                         if (ismyreq(sreq,MJDAPPID)) {
                              mses=(struct mjdses *)mrqoff(sreq);
                              if (mses->reqtyp == MJDSES
                               && fpos == mses->fpos) {
                                   return(oth);
                              }
                         }
                    }
               }
               else if (oth != unum
                     && op->state == mjdstt
                     && fpos == mjdarr(oth)->fpos) {
                    return(oth);
               }
          }
     }
     return(-1);
}

static VOID
mjdcon(                            /* perform condex exit or reprompt      */
INT msg)
{
     prfmsg(msg);
     if (usrptr->flags&CONCEX) {
          outprf(usrnum);
          usrptr->substt=GETOUT;
          btulok(usrnum,1);
          btucli(usrnum);
          btuinj(usrnum,CRSTG);
     }
     else {
          mjdrpm(haskey(sysapp) ? INTPMTS : INTPMT);
          btulok(usrnum,0);
     }
}

static VOID
mjdhtrm(                           /* host terminating the link session    */
INT msg)
{
     INT unum;

     unum=usrnum;
     curusr(mjdptr->othchn);
     mjdtrml(unum,1,msg);
}

static VOID
mjdtrml(                           /* low level terminate link             */
INT unum,
INT rsv,
INT msg)
{
     if (rsv) {
          mjdrsvp();
     }
     else {
          rstchn();
     }
     curusr(unum);
     if (!(usrptr->flags&ISGCSU)) {
          btutrg(usrnum,0);
          btucli(usrnum);
          btuclo(usrnum);
          mjdcon(msg);
          usrptr->flags&=~NOINJO;
          usrptr->crdrat=mmucrr;
     }
}

static VOID
mjdhwt(                            /* handle disconnecting link on hangup  */
INT other)
{
     INT othchn;

     othchn=usrnum;
     curusr(other);
     mjdrsvp();
     curusr(othchn);
}

static VOID
mjdthn(VOID)                       /* Doors status handler                 */
{
     static INT indfst=0;
     INT iba,oba,xfrbyts,bytred;
     INT othchn;
     CHAR *ptr;
     INT cnt;
     INT holdusn,unum,lnum;
     struct mjdusr *mjdoth;

     setmbk(mjdmb);
     dfaSetBlk(mjdbb);
     switch (status) {
     case CYCLE:
          if (usrptr->state == mjdstt) {
               if (usrptr->substt == BYTPST) {
                    if (mjdptr->flags&MJDCSL) {
                         if (mjdptr->ses != NULL
                          && mjdptr->ses->state == BYTPST) {
                              xfrbyts=btuibw(usrnum);
                              if (xfrbyts > MAXDPKV) {
                                   xfrbyts=MAXDPKV;
                              }
                              if (xfrbyts > 0 && vtmsndok(mjdptr->othchn)) {
                                   bytred=btuica(usrnum,xfrbuf,xfrbyts);
                                   vtmsend(mjdptr->ses->hwnd,bytred,xfrbuf);
                              }
                              else {
                                   actdet=0;
                              }
                         }
                    }
                    else {
                         iba=btuibw(usrnum);
                         oba=btuoba(othchn=mjdptr->othchn);
                         xfrbyts=min(iba,oba);
                         if (xfrbyts <= 0) {
                              btuinj(usrnum,CYCLE);
                              actdet=0;
                              return;
                         }
                         bytred=btuica(usrnum,xfrbuf,xfrbyts);
                         mjdoth=mjdarr(mjdptr->othchn);
                         if (!(mjdptr->flags&MJDLNK) && (mjdoth->app.flags&MJDCAB)) {
                              for (ptr=xfrbuf,cnt=0 ; cnt < bytred ; ptr++,cnt++) {
                                   if (*ptr == dischr && dischr != ' ') {
                                        mjdptr->discnt++;
                                   }
                                   else {
                                        mjdptr->discnt=0;
                                   }
                              }
                         }
                         btuxct(othchn,bytred,xfrbuf);
                    }
                    btuinj(usrnum,CYCLE);
               }
               else if (usrptr->substt == LSTING || usrptr->substt == LSTENT) {
                    if (btuoba(usrnum) > OUTSIZ-160) {
                         dfaGetAbs(mjddsk,mjdptr->fpos,0);
                         if (dfaQueryNX()) {
                              dfaGetAbs(mjddsk,(mjdptr->fpos=dfaAbs()),0);
                              if (mjdavl(mjddsk)) {
                                   mjddsp(mjddsk);
                                   outprf(usrnum);
                              }
                         }
                         else {
                              if (usrptr->substt == LSTENT) {
                                   if (isripu()) {
                                        prfmsg(ENDLST);
                                   }
                                   prfmsg(usrptr->substt=WCHAPP);
                              }
                              else {
                                   prfmsg(mjdptr->cntd ? ENDLST : NONAVL);
                                   usrptr->substt=haskey(sysapp)
                                                  ? (isripu() ? INTROS : INTPMTS)
                                                  : (isripu() ? INTRO : INTPMT);
                                   prfmsg(usrptr->substt);
                              }
                              mjdptr->fpos=0L;
                              mjdptr->dftinp=getdft();
                              outprf(usrnum);
                              return;
                         }
                    }
                    else {
                         actdet=0;
                    }
                    btuinj(usrnum,CYCLE);
               }
          }
          break;
     case RING:
     case LOST2C:
     case 254:
     case 255:
          switch (usrptr->substt) {
          case WT4APP:
               mjdhwt(mjdptr->othchn);
               dfsthn();
               break;
          case RSPPND:
               if (mjdptr->flags&MJDCSL) {
                    mjdptr->ses->app[0]='\0';
                    rstchn();
                    setmem(mjdptr,sizeof(struct mjdusr),0);
               }
               else {
                    holdusn=usrnum;
                    othchn=mjdptr->othchn;
                    mjdarr(othchn)->app.appname[0]='\0';
                    mjdarr(othchn)->fpos=0L;
                    rstchn();
                    setmem(mjdptr,sizeof(struct mjdusr),0);
                    curusr(othchn);
                    btulok(usrnum,0);
                    prfmsg(INVRSP);
                    mjdrpm(haskey(sysapp) ? INTPMTS : INTPMT);
                    curusr(holdusn);
               }
               break;
          case BYTPST:
               if (mjdptr->flags&MJDLNK) {
                    lnum=usrnum;
                    unum=mjdptr->othchn;
                    mjdtrml(unum,status == LOST2C ? 1 : 0,APPTRM2);
                    curusr(lnum);
               }
               else {
                    mjdhtrm(APPTRM2);
                    dfsthn();
               }
               break;
          case WT4RST:
          case WT4PAU:
               rstchn();
               break;
          case WT4SEL:
               if (status != LOST2C) {
                    rstchn();
               }
               break;
          default:
               if (!indfst) {
                    indfst=1;
                    dfsthn();
               }
               indfst=0;
          }
          break;
     case CMN2OK:
          if (usrptr->substt == WT4RST) {
               usrptr->substt=WT4PAU;
               mjdptr->w4rst=15;
               break;
          }
          else if ((mjdptr->flags&MJDLNK) && usrptr->substt == RSTING) {
               mjdsttd();
          }
     default:
          if (!indfst) {
               indfst=1;
               dfsthn();
          }
          indfst=0;
     }
}

static VOID
mjdhup(VOID)                       /* Doors hang up handler                */
{
     if (mjdptr->flags&MJDLNK) {
          return;
     }
     if (usrptr->state == mjdstt) {
          switch (usrptr->substt) {
          case WT4APP:
          case BYTPST:
               mjdhwt(mjdptr->othchn);
          }
          setmem(mjdptr,sizeof(struct mjdusr),0);
     }
     if (usrnum == mjdinu) {
          mjdinu=-1;
     }
}

static VOID
mjdfin(VOID)                       /* shutdown Doors module                */
{
     dfaClose(mjdbb);
     clsmsg(mjdmb);
}

/* BEGIN C/S AGENT FUNCTIONS */

static VOID
init_cslib(VOID)                   /* initialize Doors for C/S mode        */
{
     mjdakey=stgopt(MJDAKEY);
     dclmrq(max(sizeof(struct mjdlst),sizeof(struct mjdses)));
     register_agent(&mjdagt);
     register_dpkfda(MJDAPPID,INFODPK,vbmjdappsFDA);
}

static VOID
mjdread(                           /* read-dynapak handler                 */
INT direction,                     /*   read direction: 0=eq, 1=gt, -1=lt  */
struct saunam *dpknam)             /*   dynapak name to read               */
{
     CHAR *dpkstg;

     if (stdchk(mjdakey)) {
          *namtmp=*dpknam;
          dpkstg=cnvs2d(namtmp);
          if (samepato(INFODPK,dpkstg)) {
               ml=(struct mjdlst *)mrqptr;
               ml->reqtyp=MJDLST;
               stlcpy(ml->suffix,dpknam->suffix,SFXSIZ);
               cycleme(csdinfo);
               csdinfo();
               return;
          }
          if (direction == 0) {
               setmbk(mjdmb);
               if (samepato(DOORDPK,dpkstg)) {
                    inidoor();
                    return;
               }
               if (samepat("sau:prefs",dpkstg)) {
                    strcpy(rsptmp,getMsgBlk(MJDCSCAP));
                    strcat(rsptmp,"\t");
                    strcat(rsptmp,getMsgBlk(MJDCSCRD));
                    strcat(rsptmp,"\t");
                    strcat(rsptmp,getMsgBlk(MJDCSOFF));
                    strcat(rsptmp,"\t");
                    strcat(rsptmp,haskey(sysapp) ? "1" : "0");
                    strcat(rsptmp,"\t");
                    strcat(rsptmp,addlf(getMsgBlk(WT4APP)));
                    strcat(rsptmp,"\t");
                    rsp2read(NULL,strlen(rsptmp),rsptmp,rtextFDA);
                    return;
               }
          }
     }
     rejectreq();
}

static VOID
mjdwrite(                          /* write-dynapak handler                */
struct saunam *dpknam,             /*   dynapak name to write              */
USHORT length,                     /*   length of dynapak value            */
VOID *value)                       /*   dynapak value to write             */
{
     CHAR *dpkstg,*s;
     struct mjdapps *m;
     GBOOL exists;
     INT usn;

     *namtmp=*dpknam;
     dpkstg=cnvs2d(namtmp);
     if (stdchk(mjdakey)) {
          if (samepato(DOORDPK,dpkstg)) {
               snd2door(atoi(lastwd(namtmp->suffix)),length,(CHAR *)value);
               return;
          }
          if (samepato(INFODPK,dpkstg) && haskey(sysapp)) {
               m=(struct mjdapps *)mjdbb->data;
               s=skpwht(skpwrd(namtmp->suffix));
               setmbk(mjdmb);
               dfaSetBlk(mjdbb);
               exists=dfaAcqEQ(m,s,0);
               if (length == 0) { /* delete operation */
                    if (exists) {
                         if ((usn=mjdconfl(dfaAbs(),-1)) != -1) {
                              prfmsg(CONFDEL,uacoff(usn)->userid);
                              r2wprf(FALSE);
                         }
                         else {
                              dfaDelete();
                              prfmsg(APPDEL);
                              r2wprf(TRUE);
                         }
                    }
                    else {
                         prfmsg(NOSAPP);
                         r2wprf(TRUE); /* intentional */
                    }
               }
               else {
                    vb2aps((struct vbmjdapps *)value,m);
                    if (!vfystr(m)) {
                         r2wprf(FALSE);
                    }
                    else {
                         if (exists) { /* modify */
                              dfaUpdate(m);
                              prfmsg(APPUPD);
                         }
                         else { /* create */
                              dfaInsert(m);
                              prfmsg(APPADD);
                         }
                         r2wprf(TRUE);
                    }
               }
               return;
          }
     }
     rejectreq();
}

static VOID
mjdabort(VOID)                     /* dynapak abort routine                */
{
     ms=(struct mjdses *)mrqptr;
     if (ms->surchg != 0) {
          usrptr->crdrat-=ms->surchg;
          ms->surchg=0;
     }
     if (ms->reqtyp != MJDSES) {
          return;
     }
     switch (ms->state) {
     case WT4APP:
     case BYTPST:
          mjdhwt(ms->othchn);
     }
}

static UINT                        /*   returns size of vba for VB         */
aps2vb(                            /* copy structures mjdapps to vbmjdapps */
struct mjdapps *aps,               /*   (from) mjdapps structure           */
struct vbmjdapps *vba)             /*   (to) vbmjdapps structure           */
{
     vba->begchn=aps->begchn;
     vba->endchn=aps->endchn;
     vba->flags=aps->flags;
     vba->surchg=aps->surchg;
     vba->hskmhd=aps->hskmhd;
     vba->timalw=aps->timalw;
     sprintf(vba->ndkh,"%s\t%s\t%s\t%s\t",
                       aps->appname,aps->appdesc,aps->keyreq,aps->hskstg);
     ASSERT(strlen(vba->ndkh) < APPNSZ+APPDSC+KEYSIZ+HSKSTG);
     return(fldoff(vbmjdapps,ndkh)+strlen(vba->ndkh));
}

static VOID
vb2aps(                            /* copy structures vbmjdapps to mjdapps */
struct vbmjdapps *vba,             /*   (from) vbmjdapps structure         */
struct mjdapps *aps)               /*   (to) mjdapps structure             */
{
     aps->begchn=vba->begchn;
     aps->endchn=vba->endchn;
     aps->flags=vba->flags;
     aps->surchg=vba->surchg;
     aps->hskmhd=vba->hskmhd;
     aps->timalw=vba->timalw;
     stzcpy(aps->appname,itemidx(vba->ndkh,0),APPNSZ);
     stzcpy(aps->appdesc,itemidx(vba->ndkh,1),APPDSC);
     stzcpy(aps->keyreq,itemidx(vba->ndkh,2),KEYSIZ);
     stzcpy(aps->hskstg,itemidx(vba->ndkh,3),HSKSTG);
}

static GBOOL                       /*   returns FALSE if no good           */
vfystr(                            /* verify structure, prfmsg errors      */
struct mjdapps *aps)               /*   mjdapps structure                  */
{
     CHAR *ptr;

     strupr(aps->appname);
     strupr(aps->hskstg);
     if (aps->begchn < 0 || aps->begchn >= MAXNTERM
      || aps->endchn < 0 || aps->endchn >= MAXNTERM) {
          prfmsg(INVHEX2);
          return(FALSE);
     }
     if (aps->endchn < aps->begchn) {
          prfmsg(LESHEX);
          return(FALSE);
     }
     if (!sameas(firstwd(aps->appname),aps->appname)) {
          prfmsg(ONEWRD);
          return(FALSE);
     }
     for (ptr=aps->appname ; *ptr != '\0' ; ptr++) {
          if (!mjdvalc(*ptr)) {
               prfmsg(INVCHR);
               return(FALSE);
          }
     }
     if (aps->keyreq[0] != '\0' && keynam(aps->keyreq) == 0) {
          prfmsg(INVKEY);
          return(FALSE);
     }
     if (aps->timalw < 0 || aps->timalw > 1440) {
          prfmsg(INVTIM);
          return(FALSE);
     }
     for (ptr=aps->hskstg ; *ptr != NULL ; ptr++) {
          if (strchr(valhsk,*ptr) == NULL) {
               prfmsg(INVBYT);
               return(FALSE);
          }
     }
     return(TRUE);
}

static VOID
csdinfo(VOID)                      /* return info on doors                 */
{
     CHAR *s;
     GBOOL got=FALSE;
     struct mjdapps *m;
     struct vbmjdapps *v;
     UINT len;

     ml=(struct mjdlst *)mrqptr;
     if (sameas(firstwd(ml->suffix),INFOSFX)) {
          v=(struct vbmjdapps *)rsptmp;
          m=(struct mjdapps *)mjdbb->data;
          s=skpwht(skpwrd(ml->suffix));
          dfaSetBlk(mjdbb);
          switch (rqdptr->direction) {
          case 0:
               got=dfaAcqEQ(m,s,0);
               break;
          case 1:
               got=dfaAcqGT(m,s,0);
               break;
          case -1:
               got=dfaAcqLT(m,s,0);
               break;
          }
          dfaRstBlk();
          if (got) {
               strcpy(s,m->appname);
               if (haskey(m->keyreq)) {
                    len=aps2vb(m,v);
                    *namtmp=rqdptr->saunam;
                    strcpy(namtmp->suffix,ml->suffix);
                    rsp2read(namtmp,len,v,vbmjdappsFDA);
                    return;
               }
               else if (rqdptr->direction != 0) {
                    /* keep cycling */
                    return;
               }
          }
     }
     rejectreq();
}

static VOID
inidoor(VOID)                      /* begin door for c/s                   */
{                                  /*   namtmp implicit                    */
     CHAR app[APPNSZ];
     LONG creds,fpos;

     ms=(struct mjdses *)mrqptr;
     ms->reqtyp=MJDSES;
     stzcpy(app,firstwd(skpwht(skpwrd(namtmp->suffix))),APPNSZ);
     ms->hwnd=atoi(nextwd());
     dfaSetBlk(mjdbb);
     if (dfaAcqEQ(mjddsk,app,0) && haskey(mjddsk->keyreq)) {
          fpos=dfaAbs();
          if (mjdavl(mjddsk)) {
               if (!(mjddsk->flags&MJDXCL) || (mjdconfl(fpos,-1) == -1)) {
                    creds=usrptr->crdrat+mjddsk->surchg;
                    if (creds <= 0L || tstcrd(creds)) {
                         if (csmjdfpt(mjddsk,fpos)) {
                              cycleme(csses);
                              return;
                         }
                         else {
                              prfmsg(NOCHNA);
                         }
                    }
                    else {
                         prfmsg(LKCRED);
                    }
               }
               else {
                    prfmsg(DOORIN);
               }
          }
          else {
               prfmsg(NOTAVL);
          }
     }
     else {
          prfmsg(NOSAPP);
     }
     rejectreq();
     othusn=usrnum;
     injoth();
}

static GBOOL                       /*   returns success/failure            */
csmjdfpt(                          /* find and sieze port for app          */
struct mjdapps *app,               /*   app being used                     */
LONG fpos)                         /*   abspos of door's btv record        */
{
     INT chn,unum;
     ULONG flags;

     for (chn=app->begchn ; chn <= app->endchn ; chn++) {
          if ((othusn=usridx(chn)) != -1) {
               othusp=usroff(othusn);
               if ((othusp->usrcls == BBSPRV && othusp->state == mjdstt
                 && othusp->substt == WT4SEL)
                || (othusp->usrcls == VACANT && (othusp->flags&ISRIAL)
                 && othusp->state == AWAITC && !(othusp->flags&NOHDWE))
                || (othusp->usrcls == VACANT && (othusp->flags&IS2698)
                 && othusp->state == AWAITC && !(othusp->flags&NOHDWE)
                 && (app->flags&MJDNOH) && haskey(sysapp))) {
                    ms->fpos=fpos;
                    strcpy(ms->app,app->appname);
                    ms->othchn=othusn;
                    unum=usrnum;
                    curusr(othusn);
                    flags=usrptr->flags;
                    rstchn();
                    usrptr->flags=(flags|NOZAP|NOINJO);
                    usrptr->usrcls=BBSPRV;
                    usrptr->state=mjdstt;
                    usrptr->substt=RSTING;
                    setmem(mjdptr,sizeof(struct mjdusr),0);
                    mjdptr->flags|=MJDLNK;
                    mjdptr->flags|=MJDCSL;
                    mjdptr->othchn=unum;
                    mjdptr->ses=ms;
                    movmem(app,&(mjdptr->app),sizeof(struct mjdapps));
                    shochl(spr("Opening Door for channel %02X",channel[unum]),
                           'O',baudat(usrptr->baud,0));
                    curusr(unum);
                    ms->state=WT4APP;
                    return(TRUE);
               }
          }
     }
     return(FALSE);
}

static VOID
csses(VOID)                        /* session cycle routine                */
{
     LONG creds;
     INT rqid;

     rqid=greqid;
     ms=(struct mjdses *)mrqptr;
     creds=usrptr->crdrat+ms->surchg;
     if (creds > 0L) {
          if (!tstcrd(creds)) {
               othusn=usrnum;
               setmbk(mjdmb);
               prfmsg(OUTCRD2);
               injoth();
               curusr(ms->othchn);
               mjdrsvp(); /* will set ms->app[0]='\0' */
               curreq(rqid);
          }
     }
     if (ms->app[0] == '\0') {
          if (ms->surchg != 0) {
               usrptr->crdrat-=ms->surchg;
               ms->surchg=0;
          }
          rejectreq();
     }
}

static VOID
snd2door(
INT hwnd,
USHORT length,
CHAR *value)
{
     INT i,oldid,s,oc,oba;
     struct user *usp;

     for (i=0 ; i < MAXREQS ; i++) {
          s=srvrqid(usrnum,i);
          if (greqid != s && ismyreq(s,MJDAPPID)) {
               oldid=greqid;
               curreq(s);
               ms=(struct mjdses *)mrqptr;
               if (ms->reqtyp == MJDSES && ms->hwnd == hwnd) {
                    oc=ms->othchn;
                    curreq(oldid);
                    if (oc >= 0 && oc <= nterms) {
                         usp=usroff(oc);
                         if (mjdarr(oc)->othchn == usrnum
                          && usp->state == mjdstt
                          && usp->substt == BYTPST) {
                              oba=btuoba(oc);
                              btuxct(oc,min(oba,length),value);
                              rsp2write(TRUE,0,NULL,NULL);
                              return;
                         }
                    }
                    break;
               }
               curreq(oldid);
          }
     }
     rejectreq();
}

static CHAR *
addlf(                             /* adds line feeds after C/Rs           */
CHAR *txtbuf)
{
     CHAR *ptr=txtbuf;

     while ((ptr=strchr(ptr,'\r')) != NULL) {
          ptr++;
          movmem(ptr,ptr+1,strlen(ptr)+1);
          *ptr='\n';
     }
     return(txtbuf);
}

/* END C/S AGENT FUNCTIONS */
