/***************************************************************************
 *                                                                         *
 *   GALMHS.C                                                              *
 *                                                                         *
 *   Copyright (c) 1992-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This is online portion of the Worldgroup MHS importer/exporter.  The  *
 *   message format used is based on MHS v1.5 and the SMF-70               *
 *   specifications.  Only a subset is being supported. (was MHSHDL.C)     *
 *                                                                         *
 *                                            - R. Skurnick 2/8/92         *
 *   -File Attachment Support                 - C. Robert   2/1/93         *
 *                                            - C. Kotacka  2/1/93         *
 *   -Separate into DLL using GME             - J. Alvrus   9/25/94        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "fcntl.h"
#include "share.h"
#include "sys\stat.h"
#include "majorbbs.h"
#include "gme.h"
#include "galmhs.h"
#include "mhsalias.h"

#define FILREV "$Revision: 8 $"

#define NONDEL 0                   /* send non-delivery notice             */
#define RETREC 1                   /* send return receipt notice           */
#define MAXKWD 20                  /* max size of incoming message keywords*/
#define MAXLIN 79                  /* max message text line length         */
#define MHSDSZ 80                  /* MHS date information field size      */

/* exporter functions */
const CHAR *mhshlp(VOID);
const CHAR *mhsasp(const CHAR *to,const struct message *msg);
INT sndmhs(const CHAR *to,const struct message *msg,const CHAR *text,
           const CHAR *filatt);
VOID outxt(FILE *fp,const CHAR *cp);

/* importer functions */
VOID mhsproc(INT taskid);
VOID mhsscn2(VOID);
GBOOL mhsscn(VOID);
GBOOL mhsrhd(VOID);
GBOOL mhschkh(VOID);
GBOOL mhsrtxt(VOID);
GBOOL mhssnd(VOID);
GBOOL chk4bad(CHAR *fname);
struct mhskar *keywdok(CHAR *stgptr);
VOID mhsfpst(VOID);
VOID mhsbad(VOID);
VOID bad2sy(VOID);
GBOOL sndbad(VOID);
VOID sndnod(VOID);
VOID sndnot(INT wchtyp);
VOID delimp(VOID);

/* keyword processing functions */
GBOOL attmnt(CHAR *s);
GBOOL touid(CHAR *s);
GBOOL frmadr(CHAR *s);
GBOOL msgsub(CHAR *s);
GBOOL hdlsum(CHAR *s);
GBOOL nottyp(CHAR *s);
GBOOL retrec(CHAR *s);
GBOOL nodnot(CHAR *s);
GBOOL errnum(CHAR *s);
GBOOL msgdte(CHAR *s);
GBOOL nulrou(CHAR *s);

/* miscellaneous functions */
VOID inimhs(VOID);
GBOOL mhscfl(VOID *work,USHORT forum,LONG msgid);
VOID clsmhs(VOID);
VOID clnmhs(VOID);
VOID bldrfl(CHAR *msgpth);
CHAR *makpth(CHAR *pthpre,CHAR *filnam);
VOID zapbck(CHAR *dir);
CHAR *skpspc(CHAR *s);
CHAR *xltcrs(CHAR *s);

struct mhsalias mhsexp={           /* alias block for MHS                  */
     {                             /*   (begin MHS exporter control block) */
     "",                           /*   address prefix for this exporter   */
     "",                           /*   name of exporter                   */
     "",                           /*   description of exporter            */
     "",                           /*   example address to this exporter   */
     "",                           /*   key required to use this exporter  */
     0,                            /*   per-message surcharge              */
     "",                           /*   key required for file attachments  */
     0,                            /*   attachment surcharge               */
     0,                            /*   attachment per-kbyte surcharge     */
     "",                           /*   key required to request return recp*/
     0,                            /*   return receipt request surcharge   */
     "",                           /*   key required to send priority msg  */
     0,                            /*   priority surcharge                 */
     (EXPATT|EXPRRR),              /*   supported features flags           */
     mhshlp,                       /*   get help message for this exporter */
     valmhs,                       /*   is this a valid address vector     */
     mhsasp,                       /*   path+file name for attachments     */
     sndmhs                        /*   send message vector                */
     },                            /*   (end MHS exporter control block)   */
     NULL,                         /*   outgoing message directory         */
     NULL,                         /*   outgoing attachment directory      */
     NULL,                         /*   incoming message directory         */
     NULL,                         /*   incoming attachment directory      */
     "",                           /*   outgoing message address prefix    */
     ""                            /*   outgoing message address suffix    */
};

VOID *mhswrk;                      /* GME work area for importing          */
struct message *mhsmsg;            /* message header for importing         */
CHAR *mhstxt;                      /* message text buffer for importing    */

INT scnmhs,                        /* how often to scan for incoming msgs  */
    lastalias=-1,                  /* index of last alias imported for     */
    mhscur,                        /* MHS current state for incoming mail  */
    mhstsk;                        /* MHS importing task ID                */

ULONG mhsidly;                     /* per-CYCLE MHS import: maximum delay  */

GBOOL impact=FALSE,                /* at least one MHS importer is active  */
     badmhs,                       /* bad import E-mail to Sysop flag      */
     moremsg;                      /* this message continues               */

CHAR *gtwnme,                      /* server gateway name                  */
     mhsfnm[GCMAXPTH],             /* file name hold area                  */
     mhsfn2[GCMAXPTH];             /* file name hold area #2               */

FILE *filfp=NULL;                  /* current import message file stream   */

HMCVFILE mhsmb=NULL;               /* MHS message file pointer             */

struct mhsalias *curalias;         /* pointer to alias being imported      */

INT naliases=0;                    /* number of MHS aliases registered     */
struct mhsalias **aliasarr=NULL;   /* array of MHS alias control blocks    */

INT numrfd;                        /* number of files referenced in msgs   */
CHAR **refdfl;                     /* "referenced in msgs" file list       */

                                   /* MHS importing states                 */
#define MHSIGNORE  -1              /*   no cycle needed, ignore it         */
#define MHSSCN      0              /*   dispatch to mhsscn()               */
#define MHSRHD      1              /*   dispatch to mhsrhd()               */
#define MHSRHD2     2              /*   dispatch to mhsrhd()               */
#define MHSCHKH     3              /*   dispatch to mhschkh()              */
#define MHSTXT      4              /*   dispatch to mhsrtxt()              */
#define MHSTXT2     5              /*   dispatch to mhsrtxt()              */
#define MHSSND      6              /*   dispatch to mhssnd()               */
#define SNDBAD      7              /*   dispatch to sndbad()               */

struct mhskar {                    /* struct for dealing with keywords     */
     CHAR *hdrwrd;                 /*   keyword name                       */
     GBOOL (*routine)(CHAR *);     /*   routine to execute                 */
};

#define HDRSIZ (sizeof(mhskwd)/sizeof(struct mhskar))

struct mhskar mhskwd[]={
     {"ATTACHMENT", attmnt},
     {"DATE",       msgdte},
     {"ERC",        errnum},
     {"FROM",       frmadr},
     {"NDN",        nodnot},
     {"ROC",        nulrou},
     {"RRN",        retrec},
     {"SUBJECT",    msgsub},
     {"SUMMARY",    hdlsum},
     {"TO",         touid},
     {"TYPE",       nottyp}
};

#define CONTSTR "\r<< Continued in next Message >>\r"
#define ATTBGN  "Attachment-name:" /* attachment line in msg begins w/ this*/

struct mhsimp {                    /* current message import control data  */
     USHORT forum;                 /*   forum ID to which message is going */
     LONG lastmsg;                 /*   last message number for TBC        */
     CHAR from[MAXADR];            /*   from address - for replys          */
     CHAR subject[TPCSIZ];         /*   message subject                    */
     CHAR to[UIDSIZ];              /*   on-system destination for msg      */
     CHAR auxto[MAXADR];           /*   who forum message was addressed to */
     CHAR date[MHSDSZ];            /*   date information                   */
     CHAR filatt[GCMAXPTH];        /*   file attachment path+file name     */
     CHAR attname[GCMAXFNM];       /*   file attachment name               */
     INT errcode;                  /*   errcode if notification            */
     CHAR flags;                   /*   misc flags                         */
} mhsimp;

                                   /* bit definitions for mhsimp.flags     */
#define HASSUB      0x01           /*   Subject has been defined           */
#define ISNOTI      0x02           /*   This is only a notification        */
#define REQREC      0x04           /*   Return Receipt Requested           */
#define REQNOD      0x08           /*   Request Non-Delivery Notice        */
#define FILEAT      0x10           /*   File attachment present            */
#define FNDFOR      0x20           /*   Forum message going to found       */
#define FNDUID      0x40           /*   User-ID to whom message going found*/

VOID EXPORT
init__galmhs(VOID)                 /* initialize MHS                       */
{
     if (mhsmb == NULL) {
          mhsmb=opnmsg("GALMHS.MCV");
     }
     else {
          setmbk(mhsmb);
     }
     mhscur=MHSIGNORE;
     hook_shutdown(clsmhs);
     hook_cleanup(clnmhs);
     if (!ynopt(MHSACT)) {
          return;
     }
     stlcpy(mhsexp.exp.prefix,getMsgBlk(MHSPFX),PFXSIZ);
     stlcpy(mhsexp.exp.name,getMsgBlk(MHSNAM),EXPNSZ);
     stlcpy(mhsexp.exp.desc,getMsgBlk(MHSDSC),EXPDSZ);
     stlcpy(mhsexp.exp.exmp,getMsgBlk(MHSXMP),MAXADR);
     stlcpy(mhsexp.exp.wrtkey,getMsgBlk(MHSKEY),KEYSIZ);
     mhsexp.exp.wrtchg=numopt(MHSCHG,-32767,32767);
     stlcpy(mhsexp.exp.attkey,getMsgBlk(MHSATKY),KEYSIZ);
     mhsexp.exp.attchg=numopt(MHSATCH,-32767,32767);
     mhsexp.exp.apkchg=numopt(MHSPKCH,-32767,32767);
     stlcpy(mhsexp.exp.rrrkey,getMsgBlk(MHSRRKY),KEYSIZ);
     mhsexp.exp.rrrchg=numopt(MHSRRCH,-32767,32767);
     mhsexp.outmsg=stgopt(OUTMSG);
     if (*(mhsexp.outfil=stgopt(OUTFIL)) == '\0') {
          free(mhsexp.outfil);
          mhsexp.outfil=NULL;
     }
     mhsexp.inmsg=stgopt(INMSG);
     if (*(mhsexp.infil=stgopt(INFIL)) == '\0') {
          free(mhsexp.infil);
          mhsexp.infil=NULL;
     }
     if (!register_mhsalias(&mhsexp)) {
          usrnum=-1;
          shocst("MHS: Inactive","MHS: Directory Structure Incomplete!");
          free(mhsexp.outmsg);
          if (mhsexp.outfil != NULL) {
               free(mhsexp.outfil);
          }
          free(mhsexp.inmsg);
          if (mhsexp.infil != NULL) {
               free(mhsexp.infil);
          }
          return;
     }
}

VOID
inimhs(VOID)                       /* initialize MHS import/export         */
{
     static GBOOL initted=FALSE;

     if (initted) {
          return;
     }
     if (mhsmb == NULL) {
          mhsmb=opnmsg("GALMHS.MCV");
     }
     else {
          setmbk(mhsmb);
     }
     mhswrk=(VOID *)alczer(GMEWRKSZ);
     mhsmsg=(struct message *)alcmem(sizeof(struct message));
     mhstxt=(CHAR *)alcmem(txtlen());
     gtwnme=stgopt(GTWNME);
     if (*gtwnme == '\0') {
          catastro("GALMHS: gateway name not configured");
     }
     scnmhs=numopt(SCNMHS,5,3600);
     badmhs=ynopt(BADMHS);
     mhsidly=numopt(MHSIDLY,1,100)*655L;
     if (!setcfl(mhscfl)) {
          catastro("GALMHS: unable to register conflict checker");
     }
     initted=TRUE;
     rstmbk();
}

GBOOL                              /*   returns TRUE if MHS has conflict   */
mhscfl(                            /* MHS conflict checker                 */
VOID *work,                        /*   work area being checked            */
USHORT forum,                      /*   forum to check                     */
LONG msgid)                        /*   message ID to check                */
{
     if (work != mhswrk && msgid == 0L) {
          return((mhscur == MHSSND || mhscur == SNDBAD)
              && forum == mhsmsg->forum);
     }
     return(FALSE);
}

VOID
clsmhs(VOID)                       /* shutdown MHS processing              */
{
     while (mhscur == MHSSND) {
          mhssnd();
     }
     while (mhscur == SNDBAD) {
          sndbad();
     }
     if (filfp != NULL) {
          fclose(filfp);
          filfp=NULL;
     }
     clsmsg(mhsmb);
     mhsmb=NULL;
}

VOID
clnmhs(VOID)                       /* clean out old MHS file attachments   */
{
     INT i,aliasidx;
     CHAR *curfil;
     struct ffblk fb;

     for (aliasidx=0 ; aliasidx < naliases ; ++aliasidx) {
          curalias=aliasarr[aliasidx];
          if (curalias->outmsg != NULL) {
               bldrfl(curalias->outmsg);
               curfil=makpth(curalias->outfil,STAR);
               if (fnd1st(&fb,curfil,0)) {
                    do {
                         curfil=makpth(curalias->outfil,fb.ff_name);
                         if (curfil[strlen(curfil)-1] == '.') {
                              curfil[strlen(curfil)-1]='\0';
                         }
                         for (i=0 ; i < numrfd ; ++i) {
                              if (sameas(curfil,refdfl[i])) {
                                   break;
                              }
                         }
                         if (i == numrfd) {
                              unlink(curfil);
                         }
                    } while (fndnxt(&fb));
               }
               if (numrfd > 0) {
                    for (i=0 ; i < numrfd ; ++i) {
                         free(refdfl[i]);
                    }
                    free(refdfl);
               }
          }
     }
}

VOID
bldrfl(                            /* build "referenced in msgs" file list */
CHAR *msgpth)                      /*   path to find outgoing MHS msgs     */
{
     FILE *fp;
     CHAR *curfil;
     struct ffblk fb;

     numrfd=0;
     refdfl=NULL;
     curfil=makpth(msgpth,STAR);
     cntdir(curfil);
     if (numfils == 0L || !fnd1st(&fb,curfil,0)) {
          return;
     }
     refdfl=(CHAR **)alczer(sizeof(CHAR *)*(INT)numfils);
     do {
          curfil=makpth(msgpth,fb.ff_name);
          if ((fp=fopen(curfil,FOPRA)) != NULL) {
               while (fgets(vdatmp,vdasiz,fp) != NULL) {
                    if (sameto(ATTBGN,vdatmp)) {
                         refdfl[numrfd]=
                              alcdup(unpad(skpwht(&vdatmp[sizeof(ATTBGN)-1])));
                         ++numrfd;
                         break;
                    }
               }
               fclose(fp);
          }
     } while (fndnxt(&fb));
}

CHAR *
makpth(                            /* make a path out of prefix and file   */
CHAR *pthpre,                      /*   path prefix to use                 */
CHAR *filnam)                      /*   file name to use                   */
{
     static CHAR retbuf[GCMAXPTH];

     stlcpy(retbuf,pthpre,GCMAXPTH);
     if (retbuf[strlen(retbuf)-1] != SL) {
          stlcat(retbuf,SLS,GCMAXPTH);
     }
     stlcat(retbuf,filnam,GCMAXPTH);
     return(retbuf);
}

/* The following functions deal with MHS aliases:                          */

GBOOL                              /*   returns FALSE if bad directories   */
register_mhsalias (                /* set up an MHS alias                  */
struct mhsalias *alias)            /*   alias definition block             */
{
     INT i;
     GBOOL impflg,expflg;
     CHAR newpth[GCMAXPTH],oldpth[GCMAXPTH];

     ASSERT(alias != NULL);
     impflg=FALSE;
     if (alias->inmsg != NULL) {
          if (*alias->inmsg == '\0') {
               return(FALSE);
          }
          zapbck(alias->inmsg);
          for (i=0 ; i < naliases ; ++i) {
               if (aliasarr[i]->inmsg != NULL
                && sameas(normspec(newpth,alias->inmsg),
                          normspec(oldpth,aliasarr[i]->inmsg))) {
                    catastro("GALMHS: inbound message directory conflict\n"
                             "Importer \"%s:\" conflicts with \"%s:\"",
                             alias->exp.prefix,aliasarr[i]->exp.prefix);
               }
          }
          if (alias->infil == NULL || *alias->infil == '\0') {
               alias->infil=alcdup(spr("%s" SLS "PARCEL",alias->inmsg));
          }
          else {
               zapbck(alias->infil);
          }
          if (!fmdir(alias->inmsg) || !fmdir(alias->infil)) {
               return(FALSE);
          }
          impflg=TRUE;
     }
     expflg=FALSE;
     if (alias->outmsg != NULL) {
          if (*alias->outmsg == '\0') {
               return(FALSE);
          }
          zapbck(alias->outmsg);
          if (alias->outfil == NULL || *alias->outfil == '\0') {
               alias->outfil=alcdup(spr("%s" SLS "PARCEL",alias->outmsg));
          }
          else {
               zapbck(alias->outfil);
          }
          if (!fmdir(alias->outmsg) || !fmdir(alias->outfil)) {
               return(FALSE);
          }
          alias->exp.flags&=(EXPATT|EXPRRR);
          if (alias->exp.sndmsg == NULL || alias->exp.attspc == NULL) {
               catastro("GALMHS: invalid exporter alias (prefix: \"%s:\")",
                        alias->exp.prefix);
          }
          if (alias->exp.helpmsg == NULL) {
               alias->exp.helpmsg=mhshlp;
          }
          if (alias->exp.valadr == NULL) {
               alias->exp.valadr=valmhs;
          }
          expflg=TRUE;
     }
     if (!alcpar(&aliasarr,naliases,SMLBLK)) {
          catastro("GALMHS: not enough memory to register MHS alias\n"
                   "(prefix: \"%s:\")",alias->exp.prefix);
     }
     aliasarr[naliases]=alias;
     ++naliases;
     inimhs();
     if (impflg && !impact) {
          impact=TRUE;
          rtkick(5,mhsscn2);
     }
     if (expflg) {
          register_exp(&alias->exp);
     }
     return(TRUE);
}

CHAR *
aliasasp(                          /* get outbound att file path+name      */
struct mhsalias *alias,            /*   for this exporter alias            */
const CHAR *to,                    /*   address to which message being sent*/
const struct message *msg)         /*   header of message being sent       */
{
     UINT ctr;
     static CHAR attpath[GCMAXPTH];

     (VOID)to;
     (VOID)msg;
     ctr=0U;
     do {
          stlcpy(attpath,alias->outfil,GCMAXPTH);
          stlcat(attpath,SLS,GCMAXPTH);
          stlcat(attpath,tmpnam(NULL),GCMAXPTH);
          if (++ctr == TMP_MAX) {
               catastro("Too many temporary files in outbound attachment\n"
                        "directory: %s",alias->outfil);
          }
     } while (isfile(attpath));
     return(attpath);
}

INT                                /*   returns GME error codes            */
sndalias(                          /* send a message for an MHS alias      */
struct mhsalias *alias,            /*   exporter alias being used          */
const CHAR *to,                    /*   to field (MHS address)             */
const struct message *msg,         /*   header of message to send off      */
const CHAR *text,                  /*   message text buffer                */
const CHAR *filatt)                /*   path+file name of attachment       */
{
     INT handle,savusn;
     FILE *fp;
     CHAR sumlin[MAXADR];
     CHAR outnam[GCMAXPTH];

     strcpy(outnam,alias->outmsg);
     strcat(outnam,SLS);
     if ((handle=creattemp(outnam,0)) != -1) {
          close(handle);
          if ((fp=fopen(outnam,FOPWA)) != NULL) {
               fprintf(fp,"SMF-70\n");
               fprintf(fp,"To: %s%s%s\n",alias->outpfx,to,alias->outsfx);
               fprintf(fp,"From: MBBS@%s {MBBS: %s}\n",gtwnme,msg->from);
               fprintf(fp,"Subject: %s\n",msg->topic);
               if (msg->forum != EMLID && !sameas(msg->to,ALLINF)) {
                    stlcpy(sumlin,msg->to,MAXADR);
                    if (strchr(sumlin,'@') == NULL && uidxst(sumlin)) {
                         stlcat(sumlin,"@",MAXADR);
                         stlcat(sumlin,gtwnme,MAXADR);
                    }
                    fprintf(fp,"Summary: MBBS: %s\n",sumlin);
               }
               if (msg->flags&FILATT) {
                    fprintf(fp,"Attachment-name: %s\n",filatt);
               }
               fprintf(fp,"MCB-options: %s\n\n",
                       (msg->flags&RECREQ) ? "NYYNPNP" : "NNYNPNP");
               outxt(fp,text);
               fclose(fp);
               return(GMEOK);
          }
     }
     savusn=usrnum;
     usrnum=-1;
     shocst("MHS FILE OPEN ERROR",
            "Couldn't create file in %s",alias->outmsg);
     usrnum=savusn;
     return(GMEERR);
}

const CHAR *
mhsGatewayName(VOID)               /* get MHS gateway name                 */
{                                  /* (replaces exported gtwnme variable)  */
     return(gtwnme);
}

/* The following functions deal with exporting messages using MHS:         */

const CHAR *
mhshlp(VOID)                       /* get help on MHS exporter             */
{
     if (mhsmb != NULL) {
          setmbk(mhsmb);
          sprintf(vdatmp,getMsgBlk(MHSHLP),mhsexp.exp.prefix);
          rstmbk();
          return(vdatmp);
     }
     return("");
}

GBOOL
valmhs(                            /* does addr contain valid MHS '@' char */
const CHAR *adr)                   /* ('!' is considered valid for MG/I)   */
{
     return(strchr(adr,'@') != NULL || strchr(adr,'!') != NULL);
}

const CHAR *
mhsasp(                            /* outbound att file path+name for MHS  */
const CHAR *to,                    /*   address to which message being sent*/
const struct message *msg)         /*   header of message being sent       */
{
     return(aliasasp(&mhsexp,to,msg));
}

INT                                /*   returns GME error codes            */
sndmhs(                            /* send MHS message                     */
const CHAR *to,                    /*   to field (MHS address)             */
const struct message *msg,         /*   header of message to send off      */
const CHAR *text,                  /*   message text buffer                */
const CHAR *filatt)                /*   path+file name of attachment       */
{
     return(sndalias(&mhsexp,to,msg,text,filatt));
}

VOID
outxt(                             /* output msg text (trans '\r' to '\n') */
FILE *fp,                          /*   into this file                     */
const CHAR *cp)                    /*   from this string                   */
{
     for ( ; *cp != '\0' ; ++cp) {
          if (*cp == '\r') {
               fputc('\n',fp);
          }
          else {
               fputc(*cp,fp);
          }
     }
}

VOID
zapbck(                            /* remove trailing backslash if present */
CHAR *dir)                         /*   from this directory name           */
{
     INT pos;

     if (dir[pos=strlen(dir)-1] == SL) {
          dir[pos]='\0';
     }
}

/* The following routines deal with background importing of MHS messages:  */

VOID
mhsproc(                           /* incoming mail process dispatcher     */
INT taskid)                        /*   task ID in use                     */
{
     GBOOL cont;
     ULONG begintim,interval;
     static ULONG debtim=0L;

     if (debtim >= mhsidly) {
          debtim-=mhsidly;
          return;
     }
     begintim=hrtval()-debtim;
     do {
          switch (mhscur) {
          case MHSIGNORE:
               cont=FALSE;
               break;
          case MHSSCN:
               cont=mhsscn();
               break;
          case MHSRHD:
          case MHSRHD2:
               cont=mhsrhd();
               break;
          case MHSCHKH:
               cont=mhschkh();
               break;
          case MHSTXT:
          case MHSTXT2:
               cont=mhsrtxt();
               break;
          case MHSSND:
               cont=mhssnd();
               break;
          case SNDBAD:
               cont=sndbad();
               break;
          default:
               catastro("GALMHS: Invalid mhsproc() substate:  %d",mhscur);
          }
     } while ((interval=hrtval()-begintim) < mhsidly && cont);
     if (!cont) {
          debtim=0L;
          mfytask(taskid,NULL);
     }
     else {
          debtim=(interval > mhsidly ? interval-mhsidly : 0L);
     }
}

VOID
mhsscn2(VOID)                      /* rtkick'able version of mhsscn()      */
{
     mhsscn();
}

GBOOL
mhsscn(VOID)                       /* scan for incoming MHS messages       */
{
     INT nchecked;
     struct ffblk fb;

     for (nchecked=0 ; nchecked < naliases ; ++nchecked) {
          if (++lastalias >= naliases) {
               lastalias=0;
          }
          curalias=aliasarr[lastalias];
          if (curalias->inmsg != NULL) {
               stlcpy(mhsfnm,curalias->inmsg,GCMAXPTH);
               stlcat(mhsfnm,SLS STAR,GCMAXPTH);
               if (fnd1st(&fb,mhsfnm,0)) {
                    do {
                         if (fb.ff_fsize != 0L && !chk4bad(fb.ff_name)) {
                              stlcpy(mhsfnm,curalias->inmsg,GCMAXPTH);
                              stlcat(mhsfnm,SLS,GCMAXPTH);
                              stlcat(mhsfnm,fb.ff_name,GCMAXPTH);
                              filfp=gcfsopen(mhsfnm,FOPRA,GSH_DENYRW);
                              if (filfp != NULL) {
                                   if (mhscur == MHSIGNORE) {
                                        mhstsk=initask(mhsproc);
                                   }
                                   mhscur=MHSRHD;
                                   return(TRUE);
                              }
                         }
                    } while (fndnxt(&fb));
               }
          }
     }
     mhscur=MHSIGNORE;
     rtkick(scnmhs,mhsscn2);
     return(FALSE);
}

GBOOL
mhsrhd(VOID)                       /* read and process the incoming header */
{
     static struct mhskar *kptr;
     static CHAR *sptr,*dptr;
     static CHAR keywd[MAXKWD];

     if (mhscur == MHSRHD) {
          setmem(&mhsimp,sizeof(struct mhsimp),0);
     }
     if ((sptr=fgets(vdatmp,vdasiz,filfp)) != NULL) {
          if (*sptr == '\n') {
               mhscur=MHSCHKH;
               return(TRUE);
          }
          dptr=keywd;
          while (*sptr != ':' && *sptr != '\0' && dptr-keywd < MAXKWD-1) {
              *dptr++=*sptr++;
          }
          if (*sptr != ':') {
               mhsbad();
               return(TRUE);
          }
          *dptr='\0';
          if ((kptr=keywdok(keywd)) == NULL) {
               mhsbad();
               return(TRUE);
          }
          sptr++;
          while (isspace(*sptr)) {
               sptr++;
          }
          if (!(*(kptr->routine))(sptr)) {
               if (mhscur != SNDBAD) {
                    mhscur=MHSSCN;
               }
               return(TRUE);
          }
          mhscur=MHSRHD2;
          return(TRUE);
     }
     mhsbad();
     return(TRUE);
}

GBOOL
mhschkh(VOID)                      /* check header components              */
{
     if (!(*curalias->exp.valadr)(mhsimp.from)) {
          mhsbad();
     }
     else if ((mhsimp.flags&FNDFOR)
           && ((mhsimp.flags&FNDUID) || mhsimp.forum != EMLID)) {
          moremsg=FALSE;
          mhscur=MHSTXT;
     }
     else {
          sndnod();
          unlink(mhsimp.filatt);
     }
     return(TRUE);
}

GBOOL
mhsrtxt(VOID)                      /* read and process the incoming text   */
{
     const CHAR *cp;
     static INT cnt;
     static UINT totbyt,maxsiz;
     static CHAR *ptr,buf[MAXLIN+1];
     static INT c;

     if (mhscur == MHSTXT) {
          setmem(mhsmsg,sizeof(struct message),0);
          mhsmsg->forum=mhsimp.forum;
          stlcpy(mhsmsg->from,curalias->exp.prefix,MAXADR);
          stlcat(mhsmsg->from,":",MAXADR);
          stlcat(mhsmsg->from,mhsimp.from,MAXADR);
          if (mhsmsg->forum == EMLID) {
               stlcpy(mhsmsg->to,mhsimp.to,UIDSIZ);
          }
          else {
               if (mhsimp.flags&FNDUID) {
                    stlcpy(mhsmsg->to,mhsimp.auxto,MAXADR);
               }
               else {
                    stlcpy(mhsmsg->to,ALLINF,MAXADR);
               }
          }
          stlcpy(mhsmsg->topic,(mhsimp.flags&HASSUB) ? mhsimp.subject
                                                     : "<< None >>",TPCSIZ);
          if (mhsimp.flags&FILEAT) {
               cp=tpcfnm(mhsmsg->topic);
               if (cp != NULL) {
                    stlcpy(mhsmsg->attname,cp,GCMAXFNM);
               }
               mhsmsg->flags|=FILATT;
               if (mhsmsg->forum == EMLID) {
                    mhsmsg->flags|=FILAPV;
               }
               mhsimp.flags&=~FILEAT;
          }
          cnt=0;
          if (moremsg) {
               sprintf(mhstxt,"\r<< Continued from Message #%ld >>\r\r",
                       mhsimp.lastmsg);
          }
          else {
               strcpy(mhstxt,"\r");
          }
          moremsg=FALSE;
          totbyt=strlen(mhstxt);
          maxsiz=TXTLEN-sizeof(CONTSTR);
     }
     if (totbyt+MAXLIN+1 < maxsiz) {
          ptr=NULL;
          while (cnt < MAXLIN && (c=fgetc(filfp)) != EOF) {
               if (c == '\n' || c == '\r') {
                    buf[cnt++]='\r';
                    buf[cnt]='\0';
                    strcat(mhstxt,buf);
                    totbyt=strlen(mhstxt);
                    cnt=0;
                    mhscur=MHSTXT2;
                    return(TRUE);
               }
               else {
                    if (c == ' ') {
                         ptr=&buf[cnt];
                    }
                    buf[cnt++]=c;
               }
          }
          if (c == EOF) {
               buf[cnt]='\0';
               stlcat(mhstxt,buf,TXTLEN);
          }
          if (cnt >= MAXLIN) {
               if (ptr != NULL) {
                    *ptr='\0';
                    buf[cnt]='\0';
                    stlcat(mhstxt,buf,TXTLEN);
                    stlcat(mhstxt,"\r",TXTLEN);
                    cnt=strlen(++ptr);
                    movmem(ptr,buf,cnt+1);
               }
               else {
                    buf[cnt++]='\r';
                    buf[cnt]='\0';
                    cnt=0;
                    stlcat(mhstxt,buf,TXTLEN);
               }
               totbyt=strlen(mhstxt);
               mhscur=MHSTXT2;
               return(TRUE);
          }
     }
     if (c != EOF) {
          moremsg=TRUE;
          stlcat(mhstxt,CONTSTR,TXTLEN);
     }
     else {
          moremsg=FALSE;
     }
     inigmerq(mhswrk);
     mhscur=MHSSND;
     return(TRUE);
}

GBOOL
mhssnd(VOID)                       /* submit message to GME                */
{
     switch (impmsg(mhswrk,mhsmsg,mhstxt,mhsimp.filatt)) {
     case GMEAGAIN:
          break;
     case GMEAFWD:
     case GMEOK:
          if (moremsg) {
               mhsimp.lastmsg=mhsmsg->msgid;
               mhscur=MHSTXT;
          }
          else {
               mhsfpst();
          }
          break;
     default:
          mhsbad();
          break;
     }
     return(TRUE);
}

/* The following functions process incoming MHS message keywords:          */

GBOOL
touid(                             /* process "TO" keyword data            */
CHAR *s)                           /*   string to process                  */
{
     INT i,siz,spc;
     CHAR uid[UIDSIZ];

     s=strchr(s,'{');
     if (s != NULL) {
          s++;
          if (sameto("MBBS:",s=skpspc(s))) {
               s=skpspc(s+5);
          }
          else if (sameto("WG:",s)) {
               s=skpspc(s+3);
          }
          i=0;
          siz=(*s == FORIDC ? FORNSZ : UIDSIZ-1);
          do {
               uid[i++]=*s++;
          } while (i < siz && *s != '\0' && *s != '}');
          uid[i]='\0';
          for (spc = i-1; spc > 0 ; --spc) {
               if (isspace(uid[spc])) {
                    uid[spc]='\0';
               }
               else {
                    break;
               }
          }
          stlcpy(mhsimp.to,uid,UIDSIZ); /* for NDN's */
          if (uid[0] == FORIDC) {
               mhsimp.forum=getfid(&uid[1]);
               if (mhsimp.forum != EMLID) {
                    mhsimp.flags|=FNDFOR;
               }
          }
          else {
               if (uidxst(uid)) {
                    mhsimp.forum=EMLID;
                    mhsimp.flags|=(FNDUID|FNDFOR);
               }
          }
     }
     return(TRUE);
}

GBOOL
frmadr(                            /* process "FROM" keyword data          */
CHAR *s)                           /*   string to process                  */
{
     CHAR *ptr;

     ptr=strchr(s,'\n');
     if (ptr != NULL) {
          *ptr='\0';
     }
     ptr=strchr(s,'\r');
     if (ptr != NULL) {
          *ptr='\0';
     }
     if (*s != '\0') {
          stlcpy(mhsimp.from,s,MAXADR);
     }
     return(TRUE);
}

GBOOL
msgsub(                            /* process "SUBJECT" keyword data       */
CHAR *s)                           /*   string to process                  */
{
     INT len;

     stlcpy(mhsimp.subject,s,TPCSIZ);
     len=strlen(mhsimp.subject);
     if (len != 0 && mhsimp.subject[len-1] == '\n') {
          mhsimp.subject[len-1]='\0';
     }
     mhsimp.flags|=HASSUB;
     return(TRUE);
}

GBOOL
hdlsum(                            /* process "SUMMARY" keyword data       */
CHAR *s)                           /*   string to process                  */
{
     CHAR *ptr;

     s=skpwht(s);
     if (sameto("MBBS: ",s)) {
          s+=strlen("MBBS: ");
          ptr=strchr(s,'\n');      /* Zap the NL */
          if (ptr!= NULL) {
               *ptr='\0';
          }
          ptr=strchr(s,'\r');      /* Zap the CR */
          if (ptr!= NULL) {
               *ptr='\0';
          }
          if ((ptr=strchr(s,'@')) != NULL) {
               if (sameas(ptr+1,gtwnme)) {
                    *ptr='\0';
               }
          }
          mhsimp.flags|=FNDUID;
          stlcpy(mhsimp.auxto,s,MAXADR);
     }
     return(TRUE);
}

GBOOL
nottyp(                            /* process "TYPE" keyword data          */
CHAR *s)                           /*   string to process                  */
{                                  /* 0=Message 1=Some notification        */
     if (*s == '1') {
          mhsimp.flags|=ISNOTI;
     }
     return(TRUE);
}

GBOOL
retrec(                            /* process "RRN" keyword data           */
CHAR *s)                           /*   string to process                  */
{                                  /* Return Receipt Requested?            */
     if (*s != 'Y' && *s != 'N') {
          mhsbad();
          return(FALSE);
     }
     if (*s == 'Y') {
          mhsimp.flags|=REQREC;
     }
     return(TRUE);
}

GBOOL
attmnt(                            /* process "ATTACHMENT" line            */
CHAR *s)                           /*   string to process                  */
{
     if (*s != '\0') {
          stlcpy(mhsimp.filatt,curalias->infil,GCMAXPTH);
          stlcat(mhsimp.filatt,SLS,GCMAXPTH);
          stlcat(mhsimp.filatt,s,GCMAXPTH);
          unpad(mhsimp.filatt);
          if (isfile(mhsimp.filatt)) {
               mhsimp.flags|=FILEAT;
          }
          else {
               *mhsimp.filatt='\0';
          }
     }
     return(TRUE);
}

GBOOL
nodnot(                            /* process "NDN" keyword data           */
CHAR *s)                           /*   string to process                  */
{                                  /* Non-delivery note Requested?         */
     if (*s != 'Y' && *s != 'N') {
          mhsbad();
          return(FALSE);
     }
     else if (*s == 'Y') {
          mhsimp.flags|=REQNOD;
     }
     return(TRUE);
}

GBOOL
errnum(                            /* process "ERC" keyword data           */
CHAR *s)                           /*   string to process                  */
{                                  /* What's wrong if this is non-delivery */
     mhsimp.errcode=atoi(s);
     return(TRUE);
}

GBOOL
msgdte(                            /* process "DATE" keyword data          */
CHAR *s)                           /*   string to process                  */
{
     CHAR *ddptr;

     ddptr=strchr(s,'\n');
     if (ddptr != NULL) {
          *ddptr='\0';
     }
     ddptr=strchr(s,'\r');
     if (ddptr != NULL) {
          *ddptr='\0';
     }
     if (*s != '\0') {
          stlcpy(mhsimp.date,s,MHSDSZ);
     }
     return(TRUE);
}

GBOOL
nulrou(                            /* dummy keyword processor function     */
CHAR *s)                           /*   string to process                  */
{
     (VOID)s;
     return(TRUE);
}

/* miscellaneous importing utility functions follow:                       */

VOID
mhsfpst(VOID)                      /* finish up posting messages           */
{
     if (onsys(mhsimp.to)) {
          setmbk(mhsmb);
          clrmlt();
          if (mhsimp.forum == EMLID) {
               prfmlt(MHSEML);
          }
          else {
               prfmlt(MHSFOR,getfnm(mhsimp.forum));
          }
          injoth();
     }
     if (mhsimp.flags&REQREC) {
          sndnot(RETREC);
     }
     delimp();
     mhscur=MHSSCN;
}

VOID
mhsbad(VOID)                       /* something wrong with msg to import   */
{
     CHAR *ptr;

     fclose(filfp);
     filfp=NULL;
     stlcpy(mhsfn2,mhsfnm,GCMAXPTH);
     ptr=strchr(mhsfn2,'.');
     if (ptr == NULL) {
          stlcat(mhsfn2,".BAD",GCMAXPTH);
     }
     else {
          stlcpy(ptr,".BAD",GCMAXPTH);
     }
     if (mhsimp.flags&ISNOTI) {
          delimp();
     }
     else if (rename(mhsfnm,mhsfn2) == 0) {
          if (badmhs) {
               bad2sy();
               return;
          }
     }
     mhscur=MHSSCN;
}

VOID
bad2sy(VOID)                  /* send email to Sysop about a .BAD message  */
{
     setmem(mhsmsg,sizeof(struct message),0);
     mhsmsg->forum=EMLID;
     strcpy(mhsmsg->from,"Sysop");
     strcpy(mhsmsg->to,"Sysop");
     strcpy(mhsmsg->topic,"* MHS Import Notice *");
     setmbk(mhsmb);
     prfmsg(INVMHS,mhsfnm);
     stlcpy(mhstxt,xltcrs(stpans(prfbuf)),TXTLEN);
     clrprf();
     inigmerq(mhswrk);
     mhscur=SNDBAD;
}

GBOOL
sndbad(VOID)                       /* send bad import message              */
{
     if (gsndmsg(mhswrk,mhsmsg,mhstxt,NULL) != GMEAGAIN) {
          mhscur=MHSSCN;
     }
     return(TRUE);
}

GBOOL
chk4bad(fname)                     /* check for ".BAD" extension           */
CHAR *fname;
{
     CHAR *ptr;

     ptr=strchr(fname,'.');
     if (ptr != NULL) {
          return(sameas(ptr,".BAD"));
     }
     return(FALSE);
}

struct mhskar *
keywdok(                           /* check valid keyword list             */
CHAR *stgptr)                      /*   in this string                     */
{
     INT cond;
     struct mhskar *low,*mid,*high;

     low=&mhskwd[0];
     high=&mhskwd[HDRSIZ-1];
     while (low <= high) {
          mid=low+((INT)(high-low))/2;
          if ((cond=stricmp(stgptr,mid->hdrwrd)) < 0) {
               if (mid == low) {
                    break;
               }
               high=mid-1;
          }
          else if (cond > 0) {
               if (mid == high) {
                    break;
               }
               low=mid+1;
          }
          else {
               return(mid);
          }
     }
     return(NULL);
}

VOID
sndnod(VOID)                       /* NDN requested? Delete MHS import     */
{
     if (mhsimp.flags&REQNOD) {
          sndnot(NONDEL);
     }
     delimp();
     mhscur=MHSSCN;
}

VOID
sndnot(                            /* send a return notice                 */
INT wchtyp)                        /*   0=Non Delivery 1=Return Receipt    */
{
     INT handle;
     struct message *msg;
     FILE *fp;

     if (curalias->outmsg != NULL) {
          stlcpy(mhsfn2,curalias->outmsg,GCMAXPTH);
          stlcat(mhsfn2,SLS,GCMAXPTH);
          if ((handle=creattemp(mhsfn2,0)) != -1) {
               close(handle);
               if ((fp=fopen(mhsfn2,FOPWA)) != NULL) {
                    fprintf(fp,"SMF-70\n");
                    fprintf(fp,"MCB-Type: 1\n");
                    fprintf(fp,"To: %s\n", mhsimp.from);
                    fprintf(fp,"From: MBBS@%s {MBBS: %s}\n", gtwnme,mhsimp.to);
                    fprintf(fp,"Subject: %s\n",
                            wchtyp == NONDEL ? "Non-Delivery Notification"
                                             : "Return Receipt Notification");
                    setmbk(mhsmb);
                    prfmsg(wchtyp == NONDEL ? MHSNOD : MHSREC,
                           *mhsimp.date == '\0' ? "Unknown" : mhsimp.date,
                           mhsimp.to);
                    rstmbk();
                    fputc('\n',fp);
                    fputs(xltcrs(stpans(prfbuf)),fp);
                    clrprf();
                    fclose(fp);
               }
          }
     }
     else if (curalias->exp.sndmsg != NULL) {
          msg=(struct message *)vdatmp;
          setmem(msg,sizeof(struct message),0);
          msg->forum=EMLID;
          stlcpy(msg->to,mhsimp.from,MAXADR);
          sprintf(msg->from,"MBBS@%s {MBBS: %s}",gtwnme,mhsimp.to);
          strcpy(msg->topic,wchtyp == NONDEL ? "Non-Delivery Notification"
                                             : "Return Receipt Notification");
          setmbk(mhsmb);
          prfmsg(wchtyp == NONDEL ? MHSNOD : MHSREC,
                 *mhsimp.date == '\0' ? "Unknown" : mhsimp.date,
                 mhsimp.to);
          rstmbk();
          (*curalias->exp.sndmsg)(msg->to,msg,xltcrs(stpans(prfbuf)),NULL);
     }
}

CHAR *                             /*   ptr to first non-whitespace char   */
skpspc(                            /* skip all whitespace characters       */
CHAR *s)                           /*   in this string                     */
{
     while (isspace(*s)) {
          ++s;
     }
     return(s);
}

CHAR *                             /*   copy of pointer to string          */
xltcrs(                            /* translate '\r' to '\n'               */
CHAR *s)                           /*   in this string                     */
{
     CHAR *cp;

     cp=s;
     while ((cp=strchr(cp,'\r')) != NULL) {
          *cp='\n';
     }
     return(s);
}

VOID
delimp(VOID)                       /* delete a message from import dir     */
{
     fclose(filfp);
     filfp=NULL;
     chmod(mhsfnm,S_IWRITE);
     unlink(mhsfnm);
}
