/***************************************************************************
 *                                                                         *
 *   SMTPRECV.C                                                            *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.  All rights reserved.       *
 *                                                                         *
 *   SMTP server for receiving e-mail to Worldgroup's GME from the         *
 *   Internet, per RFC 821.                                                *
 *                                                                         *
 *                               6/1/95 - Bert Love,                       *
 *                                        Scott Brinker,                   *
 *                                        Charles Dunn &                   *
 *                                        Mahesh Neelakanta                *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "sys\stat.h"
#include "gme.h"
#include "remote.h"
#include "tcpip.h"
#include "dns.h"
#include "alias.h"
#include "smtpexp.h"
#include "smtp.h"
#include "galsmtpd.h"
#include "phasedbg.h"
#include "mimeb64.h"

#ifndef ICO2
#define tcpip_errno ips_errno
#define TNFACCP TNFRECV
#define TNFCONN TNFSEND
#endif

#define FILREV "$Revision: 56 $"

#define MAXRCPT 100                /* maximum number of recipients         */
#define TNMLEN    6                /* max len for incoming terminal name   */
#define MAXBYT  4096               /* total bytes we can store locally     */

                                   /* SMTP header parsing state codes      */
#define OPNMFIL   0                /*   open message file & read minfo     */
#define CHKRMT    1                /*   checking for remote users          */
#define HEADER    2                /*   parsing message header state       */
#define MIMSTART  3                /*   start MIME decoding                */
#define MIMDCODE  4                /*   MIME decoding attachment           */
#define CHKMSIZ   5                /*   check message size                 */
#define BODY      7                /*   parsing message body state         */
#define CPYATT    8                /*   copy attachment into temp file     */
#define SUBMIT    9                /*   submitting message state           */
#define CYCIMP    10               /*   GME cycling message import         */

                                   /* SMTP server state codes              */
#define WAITFROM  0                /*   waiting for "MAIL FROM:" line      */
#define WAITRCPT  1                /*   waiting for "RCPT TO:" line(s)     */
#define RECVBODY  2                /*   receiving message body             */
#define REVLOOK   3                /*   doing reverse lookup on host       */

                                   /* log function codes                   */
#define INCMING   0                /*   incoming data                      */
#define OUTGING   1                /*   outgoing data                      */
#define SHOWLOG   2                /*   log entry via shologd()            */
#define LOGINFO   3                /*   log entry for info (no data)       */

static VOID smtdincall(INT gotchn);
static GBOOL smtdinp(VOID);
static GBOOL smtdlin(CHAR *linput);
static VOID smtdsts(VOID);
static VOID smtdfin(VOID);
static VOID smtdscn(VOID);
static VOID filtsk(INT taskid);
static VOID smtdtsk(INT taskid);
static ULONG impSlice(VOID);
static VOID clndir(VOID);
static GBOOL anytime(CHAR *linput);
static VOID clsfil(GBOOL good);
static GBOOL renwrk(CHAR *wrkfil);
static VOID makwrk(VOID);
static INT valusr(const CHAR *fromadr,const CHAR *rcpto,CHAR *locadr);
VOID revStart(const CHAR *domain);
VOID revCallback(struct dns *dnsptr);
VOID revContinue(VOID);
VOID revCancel(VOID);
VOID revDone(VOID);
static VOID distmo(VOID);
static VOID enatmo(VOID);
static VOID finfil(INT taskid);
static VOID prcinp(VOID);
static VOID biglin(VOID);
static VOID smtprf(VOID);
static VOID logsmtd(INT Channel,CHAR *logstr,INT inout);
static VOID shologd(INT Channel,CHAR *header,CHAR *footer,...);
static VOID prsctyp(CHAR *inpstr);
static VOID prscte(CHAR *inpstr);
static VOID prscdsp(CHAR *inpstr);
static CHAR *crushit(CHAR *inpstr);
static VOID prsmim(CHAR *inpstr,CHAR *args[2][10]);
static CHAR *hdrval(CHAR *args[2][10],CHAR *name);
static const CHAR *cvtfname(const CHAR *badfname);
GBOOL isValidFileName(const CHAR *fileName);
GBOOL isValidFileNameChar(CHAR c);
static INT mst_bndr(VOID);
static INT mst_hdr(VOID);
static INT mst_body(VOID);
static INT mst_bgn(VOID);
static INT mst_err(VOID);
static INT mst_end(VOID);
static INT nmsgfile(GBOOL pimeatt);
static INT nattfile(CHAR *fname);
static INT isbndry(CHAR *inpstr);
static INT qprintdc(CHAR *inp,CHAR *outp);
static INT plaincpy(CHAR *inp,CHAR *outp);
static VOID mimdeflt(VOID);
static CHAR *getattnm(CHAR *msgnm,CHAR *attnm);

static INT smtdstt;                /* SMTP server module state number      */

struct module smtdmodule={         /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     smtdinp,                      /*    input routine if selected         */
     smtdsts,                      /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     NULL,                         /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     NULL                          /*    finish-up (sys shutdown) routine  */
};

static HMCVFILE smtdmb=NULL;       /* .MCV file handle for GALSMTPD.MSG    */

                                   /* options from GALSMTPD.MCV            */
GBOOL smtdonl;                     /*   SMTP server online?                */
GBOOL smtdcvtf;                    /*   SMTP convert from addresses?       */
static CHAR *smtdkey;              /*   key required to receive e-mail     */
static GBOOL smtdrej;              /*   reject or ignore calls (smtdonl=0) */
static GBOOL saveHdr;              /*   save headers with messages?        */
static INT smtdprt;                /*   SMTP port number                   */
static INT maxsmtd;                /*   maximum SMTP requests at a time    */
static GBOOL audsmtd;              /*   audit successful SMTP requests?    */
static GBOOL audsmtde;             /*   audit invalid SMTP requests?       */
static GBOOL audsmtdr;             /*   audit invalid relay requests?      */
static GBOOL audsmtdb;             /*   audit access by blocked system?    */
static USHORT smtdtmo;             /*   timeout in seconds                 */
static LONG mindsk;                /*   minimum amount of disk space req'd */
static LONG maxMsgSize=0L;         /*   maximum message size accepted      */
static CHAR wrkdir[GCMAXPTH];      /*   SMTP server work directory         */
static INT smtdqsi;                /*   SMTP server queue scan interval    */
CHAR *smtdpuid;                    /*   SMTP "postmaster" and "root" ID    */
static GBOOL smtdrev;              /*   do reverse lookup on sender?       */
static GBOOL smtdhost;             /*   SMTP mailhost mode enabled?        */
static GBOOL smtdlog;              /*   is receive log currently active?   */
static CHAR *smtdlnam;             /*   receive log file name              */
static INT maxhops;                /*   how many hops msg. can make        */

static CHAR wrkspec[GCMAXPTH];     /* file spec for scanning inbound dir   */
static CHAR tmppath[GCMAXPTH];     /* file spec for general purposes       */
static CHAR *textbuf;              /* pointer to gen purpose text buffer   */
static UINT txtbufsz;              /* size of 'textbuf'                    */
static CHAR *hdrbuf;               /* buffer for attaching headers to msg  */
static CHAR *impbuf;               /* import buffer for GME                */
static struct ffblk smtdfb;        /* current file being imported          */

INT numofals;                      /* number of system aliases             */
CHAR mailals[MAXALS][MAXADR];      /* mail aliases                         */

struct smtdusr {                   /* SMTP VDA info structure              */
     FILE *fp;                     /*   current file pointer               */
     LONG msgsiz;                  /*   current size of msg being received */
     UINT rcvcnt;                  /*   number of bytes in receive buffer  */
     UINT numofhop;                /*   number of hops (Received:) lines   */
     INT revState;                 /*   state of reverse lookup process    */
     INT rcvError;                 /*   error during receipt of body       */
     USHORT sttime;                /*   start timer for timeout purposes   */
     GBOOL gotLongLine;            /*   are we receiving a long line?      */
     GBOOL locip;                  /*   is contact from local IP?          */
     CHAR rcvbuf[MAXBYT];          /*   raw receive buffer                 */
     CHAR filnam[GCMAXPTH];        /*   current file name                  */
     CHAR hdrfrom[MAXADR];         /*   "From:" header address             */
     CHAR hdrsndr[MAXADR];         /*   "Sender:" header address           */
     CHAR hdrmail[MAXADR];         /*   "X-Mail-From:" header address      */
     CHAR revDomain[DNSNSZ];       /*   domain name for reverse lookup     */
     CHAR revTempDomain[DNSNSZ];   /*   temp domain name for reverse lookup*/
     struct dns revDNS;            /*   DNS info for reverse lookup        */
     struct dnshstex revDNSEX;     /*   reverse lookup extended DNS info   */
     struct minfo minfo;           /*   message information                */
     struct rinfo rinfo;           /*   recipient information              */
};

                                   /* reverse lookup states                */
#define REVIDLE  0                 /*   no reverse lookup in progress      */
#define REVCNAME 1                 /*   looking up canonical name          */
#define REVGETMX 2                 /*   looking up MX records              */
#define REVTYPEA 3                 /*   looking up actual address          */

#define CHSETSIZ    32             /* max size of charset string           */

                                   /* return values for isbndry() function */
#define NOBNDRY     1              /*   not a boudary                      */
#define BNDRY       2              /*   boundary                           */
#define CLSBNDRY    3              /*   closing boundary                   */

                                   /* return values for MIME decod. func.  */
#define MR_OK       0              /*   OK                                 */
#define MR_NEXT     1              /*   go to the next body part           */
#define MR_ERR      2              /*   file I/O error                     */
#define MR_CYCLE    3              /*   cycle and come back                */
#define MR_EOF      4              /*   end of message                     */

                                   /* content transfer encoding values     */
#define CTE_7BIT    1              /*   7bit encoding                      */
#define CTE_8BIT    2              /*   8bit encoding                      */
#define CTE_BIN     3              /*   binary encoding                    */
#define CTE_B64     4              /*   base 64 encoding                   */
#define CTE_QPRT    5              /*   quoted printable encoding          */

                                   /* content types                        */
#define MT_UNKN     0              /*   unknown content type               */
#define MT_TEXT     1              /*   plain text type                    */
#define MT_TXHTML   2              /*   text/html type                     */
#define MT_TXUN     3              /*   unknown text type                  */
#define MT_APP      4              /*   application type                   */
#define MT_APPUN    5              /*   application unknown type           */
#define MT_MPMX     6              /*   multipart/mixed type               */
#define MT_MPUN     7              /*   multipart unknown type             */

                                   /* MIME decoding substates              */
#define MST_BNDR    0              /*   find boundary                      */
#define MST_HDR     1              /*   read MIME header                   */
#define MST_BGN     2              /*   begin process MIME body            */
#define MST_BODY    3              /*   process MIME body                  */
#define MST_ERR     4              /*   process MIME file I/O errors       */
#define MST_END     5              /*   end of MIME processing             */

struct mimeinf {                   /* MIME decoding info                   */
     INT type;                     /*   content type/subtype               */
     INT cte;                      /*   content transfer encoding          */
     CHAR fname[GCMAXFNM];         /*   file name for the attachment       */
     CHAR bndry[MIMIDSZ+2];        /*   MIME boundary                      */
     CHAR charset[CHSETSIZ];       /*   text character set                 */
     CHAR typedsc[CHSETSIZ];       /*   message body type                  */
     CHAR subtype[CHSETSIZ];       /*   message body subtype               */
};

static
struct header {                    /* incoming header information          */
     INT state;                    /*   parsing state                      */
     INT mimstt;                   /*   MIME decoding state                */
     CHAR from[MAXADR];            /*   from address                       */
     CHAR repto[MAXADR];           /*   reply-to address                   */
     INT rcptcnt;                  /*   number of recipients               */
     INT subcnt;                   /*   current recipient being submitted  */
     CHAR date[DATESZ];            /*   date line                          */
     CHAR topic[TPCSIZ];           /*   message topic                      */
     FILE *fp;                     /*   current message file pointer       */
     CHAR filnam[GCMAXPTH];        /*   current message path               */
     FILE *mimfp;                  /*   primary decoded message file       */
     CHAR mimnm[GCMAXPTH];         /*   primary decoded message path       */
     FILE *mimattfp;               /*   attachment file                    */
     CHAR mimattnm[GCMAXPTH];      /*   attachment path                    */
     CHAR attrelnm[GCMAXFNM];      /*   real name of primary attachment    */
     GBOOL attexist;               /*   primary attachment exist           */
     GBOOL cvt2att;                /*   convert next part to attahment     */
     INT partno;                   /*   current message part number        */
     INT partsize;                 /*   maximum length of message part     */
     LONG partpos;                 /*   position in the file of next part  */
     INT tmplrcpt;                 /*   temporary local recipient counter  */
     struct minfo minfo;           /*   message information structure      */
     struct rinfo rinfo;           /*   recipient information structure    */
     struct mimeinf mimeinf;       /*   MIME header info                   */
     struct inetinfo iinfo;        /*   Internet message pointer           */
} header;

/* if incoming Internet message is autoforwarded into another Internet     */
/* address 'inetinfo' points to the original Internet message file         */
/* this way forwarded message will get full Internet header                */
/* IMPORTANT ! current implementation depends on how 'impmsg()' works      */
/* and can be broken by future changes                                     */

struct inetinfo *inetinfo;         /* Internet message pointer             */

static CHAR const hextab[256]={
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 00 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 10 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 20 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,     /* 30 */
     0x08,0x09,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x00,     /* 40 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 50 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 60 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 70 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 80 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* 90 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* A0 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* B0 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* C0 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* D0 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* E0 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,     /* F0 */
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

static const INT noChannel=100;

#ifdef DEBUG
struct smtdusr *smtdptr;
#else
#define smtdptr ((struct smtdusr *)vdaptr)
#endif

ULONG maxTicks;                    /* maximum importer time slice          */
ULONG minTicks;                    /* minimum importer time slice          */
ULONG zerTicks;                    /* no-load importer time slice          */
ULONG rtFactor;                    /* response time reduction factor       */
ULONG chFullPct;                   /* % nterms representing full load      */
ULONG timeDebt=0;                  /* excess time used in importer task    */

VOID
ini_smtrcv(VOID)                   /* SMTP server init called by SMTPSEND  */
{
     CHAR *ptr;

     smtdmb=opnmsg("galsmtpd.mcv");
     smtdcvtf=ynopt(SMTDCVTF);
     if (*(smtdpuid=stgopt(SMTDPUID)) == '\0' || !uidxst(smtdpuid)) {
          free(smtdpuid);
          smtdpuid="Sysop";
     }
     for (numofals=0 ; numofals < MAXALS ; numofals++) {
          ptr=getmsg(MAILAL1+numofals);
          if (*ptr != '\0') {
               stlcpy(mailals[numofals],ptr,MAXADR);
          }
          else {
               break;
          }
     }
     smtdonl=ynopt(SMTDONL);
     smtdrej=ynopt(SMTDREJ);
     if (smtdonl || smtdrej) {
          /* make sure textbuf is always large enough */
          txtbufsz=max(txtlen(),(SMTLSZ*4));
          textbuf=alczer(txtbufsz);
          saveHdr=ynopt(SMTDSHDR);
          if (saveHdr) {
               hdrbuf=alcmem(txtlen());
          }
          impbuf=alczer(GMEWRKSZ);
          smtdkey=stgopt(SMTDKEY);
          smtdprt=numopt(SMTDPRT,1,32767);
          maxsmtd=numopt(MAXSMTD,1,250);
          audsmtd=ynopt(AUDSMTD);
          audsmtde=(ynopt(AUDSMTDE) || audsmtd);
          smtdtmo=(USHORT)numopt(SMTDTMO,0,1800);
          mindsk=(LONG)numopt(MINDSK,1,100)*1024L;
          if (ynopt(LIMSIZ)) {
               maxMsgSize=(LONG)numopt(MAXMSG,1,2000)*1024L*1024L;
          }
          smtdrev=ynopt(SMTDREV);
          maxhops=numopt(MAXHOPS,16,32);
          smtdqsi=numopt(SMTDQSI,5,3600);
          if (!fmdir(stlcpy(wrkdir,rawmsg(WRKDIR),GCMAXPTH))) {
               shocst("SMTP SERVER NOT INTIALIZED",
                      "Unable to create inbound work directory");
               clsmsg(smtdmb);
               smtdmb=NULL;
               return;
          }
          maxTicks=(65536UL*numopt(IMAXTIM,1,32767))/1000;
          minTicks=(65536UL*numopt(IMINTIM,1,32767))/1000;
          zerTicks=(65536UL*numopt(IZERTIM,1,32767))/1000;
          rtFactor=numopt(IRSPTIM,1,32767);
          chFullPct=numopt(INCHAN,1,100);
          smtdhost=ynopt(SMTDHOST);
          if (ynopt(SMTDLOCR)) {
               iprLocalFile=stgopt(SMTDLOCF);
               audsmtdr=ynopt(AUDSMTDR);
          }
          iprBlockFile=stgopt(SMTDBLOK);
          audsmtdb=ynopt(AUDSMTDB);
          smtdlog=ynopt(SMTDLOG);
          smtdlnam=stgopt(SMTDLNAM);
          if (smtdlog) {
               shocst("SMTP RECEIVE LOG ENABLED",
                      "All SMTP receive transactions will be logged");
          }
          regtcpsvr(SMTNAME,smtdprt,SMTBACKLOG,smtdincall);
          dclvda(sizeof(struct smtdusr));
          fixpth(wrkdir);
          sprintf(wrkspec,"%s*.rdy",wrkdir);
          stzcpy(smtdmodule.descrp,gmdnam("galsmtp.mdf"),MNMSIZ);
          smtdstt=register_module(&smtdmodule);
          hook_finalshutdown(smtdfin);
          rtkick(smtdqsi,smtdscn);
          clndir();
     }
     else {
          clsmsg(smtdmb);
          smtdmb=NULL;
     }
}

static VOID
smtdincall(                        /* begin incoming SMTP session          */
INT gotchn)                        /* 1=chan (curusr) assigned, 0=not avail*/
{                                  /* implicit:  clskt=socket to client    */
     INT rjmg;
     CHAR *cp;

#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     setmbk(smtdmb);
     clrprf();
     if (!gotchn) {
          switch (ijreas) {
          case IJRSHUT:
          default:
               rjmg=SRVSHUT;
               break;
          case IJRFULL:
               rjmg=SRVFULL;
               break;
          case IJRDENY:
               rjmg=SRVDENY;
               break;
          }
          sprintf(prfbuf,xlttxv(stpans(getasc(rjmg)),mxmssz),numcdi("TCP/IP"));
          send(clskt,prfbuf,strlen(prfbuf),0);
     }
     else {
          memset(smtdptr,0,sizeof(struct smtdusr));
          usrptr->usrcls=BBSPRV;
          usrptr->state=smtdstt;
          usrptr->substt=WAITFROM;
          usrptr->flags|=NOGLOB+NOINJO;
          btuech(usrnum,0);
          btutrg(usrnum,outbsz-1);
          stansi();
          if (!smtdonl) {
               byetcp(SMTNOTON);
               rejinc(SMTNAME,"SMTP server disabled");
          }
          else if (isBlockedIP(tcpipinf[usrnum].inaddr)) {
               byetcp(SRVDENY);
               if (audsmtdb) {
                    shologd(usrnum,"SMTP SERVER BLOCKED IP",
                            "Blocked IP %s attempted access",
                            inet_ntoa(tcpipinf[usrnum].inaddr));
               }
          }
          else if (numonl(smtdstt) > maxsmtd) {
               byetcp(SMTDMANY,maxsmtd);
               rejinc(SMTNAME,spr("%d SMTP server connections",maxsmtd));
          }
          else if (dskfre(".") < (mindsk)) {
               byetcp(SMTDTAL);
               shologd(usrnum,"SMTP NOT ENOUGH DISK SPACE",
                       "Not enough disk space to process e-mail");
          }
          else {
               cp=spr("SMTP contact from %s",
                      inet_ntoa(tcpipinf[usrnum].inaddr));
               shochl(cp,'M',0x1F);
               logsmtd(usrnum,cp,SHOWLOG);
               sktnfy(TNFRECV,clskt,tcpinc,&tcpipinf[usrnum],usrnum);
               sprintf(usaptr->userid,"(%s)",
                       inet_ntoa(tcpipinf[usrnum].inaddr));
               smtdptr->locip=isLocalIP(tcpipinf[usrnum].inaddr);
               btuinj(usrnum,CYCLE);
               btubsz(usrnum,outbsz,INPSIZ);
               prfmsg(SMTDRDY);
               smtprf();
               enatmo();
          }
     }
     rstmbk();
}

static GBOOL
smtdinp(VOID)                      /* stub input handler (see smtdlin())   */
{
     return(TRUE);
}

static GBOOL                       /*   return FALSE when done             */
smtdlin(                           /* SMTP server input handler            */
CHAR *linput)
{
     INT vu;
     CHAR *ptr;
     GBOOL uidflg;
     CHAR tmpBuffer[MAXBYT];
     struct addrinf addrinf;
     static CHAR dow[7][4]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};

#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     setmbk(smtdmb);
     clrprf();
     /* handle logging and various commands */
     if (usrptr->substt != RECVBODY) {
          stlcpy(tmpBuffer,linput,MAXBYT);
          stlcat(tmpBuffer,spr(" [%d] %s",usrptr->substt,usaptr->userid),MAXBYT);
          logsmtd(usrnum,tmpBuffer,INCMING);
          if (anytime(linput)) {
               return(TRUE);
          }
          if (sameto("RSET",linput)) {
               revCancel();   /* should not be necessary, just to be safe */
               clsfil(FALSE);
               prfmsg(SMTDOK);
               usrptr->substt=WAITFROM;
               smtprf();
               return(TRUE);
          }
          if (sameto("QUIT",linput)) {
               revCancel();   /* should not be necessary, just to be safe */
               clsfil(FALSE);
               byetcp(SMTDBYE,hstdom);
               return(FALSE);
          }
     }
#if defined(EXTRA_LOGGING) && defined(LOG_BODY)
     else {
          logsmtd(usrnum,linput,INCMING);
     }
#endif
     switch (usrptr->substt) {
     case WAITFROM:
          if (sameto("MAIL FROM:",linput)) {
               ASSERT(smtdptr->fp == NULL);

               /* reset per-message info */
               smtdptr->msgsiz=smtdptr->numofhop=0;
               *smtdptr->hdrfrom='\0';
               *smtdptr->hdrsndr='\0';
               *smtdptr->hdrmail='\0';
               setmem(&smtdptr->minfo,sizeof(struct minfo),0);

               makwrk();
               if ((smtdptr->fp=fopen(smtdptr->filnam,FOPWB)) == NULL) {
                    shologd(usrnum,"SMTP WRITE ERROR",
                            "smtdlin() Error opening message file");
                    prfmsg(SMTDLERR);
               }
               else if (fwrite(&smtdptr->minfo,1,sizeof(struct minfo),
                               smtdptr->fp) != sizeof(struct minfo)) {
                    shologd(usrnum,"SMTP WRITE ERROR",
                            "smtdlin() Error writing message info");
                    prfmsg(SMTDLERR);
                    clsfil(FALSE);
               }
               else {
                    stlcpy(smtdptr->minfo.from,
                           stpbkt(crpstr(linput,':')),MAXADR);
                    parseIntAddr(smtdptr->minfo.from,&addrinf);
                    if (smtdrev && !ourHost(addrinf.domain)) {
                         /* ourHost returns true for "", so an empty reverse
                          * path will not be looked-up.
                          */
                         revStart(addrinf.domain);
                    }
                    else {
                         prfmsg(SMTDOK);
                         usrptr->substt=WAITRCPT;
                    }
               }
          }
          else if (sameto("RCPT TO:",linput) || sameto("DATA",linput)) {
               prfmsg(SMTDOSQ);
          }
          else {
               prfmsg(SMTDSYN);
          }
          break;
#if defined(DEBUG)
     case REVLOOK:
          ASSERT(FALSE);
          break;
#endif /* DEBUG */
     case WAITRCPT:
          if (sameto("RCPT TO:",linput)) {
               if (smtdptr->minfo.locrcpt+smtdptr->minfo.rmtrcpt == MAXRCPT) {
                    prfmsg(SMTDROVR);
                    break;         /* (out of case WAITRCPT) */
               }
               setmem(&smtdptr->rinfo,sizeof(struct rinfo),0);
               smtdptr->minfo.qtime=smtdptr->minfo.lsttim=time(NULL);
               vu=valusr(smtdptr->minfo.from,linput,smtdptr->rinfo.to);
               if (vu == BADADDR) {
                    prfmsg(SMTDNON,crpstr(linput,':'));
               }
               else if (vu == RMTADDR && !(smtdhost && smtdptr->locip)) {
                    if (audsmtdr) {
                         shologd(usrnum,"SMTP SERVER RELAY REJECTED",
                                 "%s tried relay to %s",
                                 inet_ntoa(tcpipinf[usrnum].inaddr),
                                 smtdptr->rinfo.to);
                    }
                    prfmsg(SMTDNRLY,crpstr(linput,':'));
               }
               else {
                    if (vu == LOCADDR) {
                         uidflg=islocal(smtdptr->rinfo.to);
                         if (!uidflg
                          || (uidflg && uhskey(smtdptr->rinfo.to,smtdkey))) {
                              smtdptr->rinfo.flags|=LOCRCPT;
                              smtdptr->minfo.locrcpt++;
                         }
                         else {
                              prfmsg(SMTDNKY);
                              break;    /* (out of case WAITRCPT) */
                         }
                    }
                    else {
                         smtdptr->minfo.rmtrcpt++;
                    }
                    if (fwrite(&smtdptr->rinfo,1,sizeof(struct rinfo),
                               smtdptr->fp) != sizeof(struct rinfo)) {
                         shologd(usrnum,"SMTP WRITE ERROR",
                                 "smtdlin() Error writing recipient info");
                         prfmsg(SMTDFULL);
                    }
                    else {
                         prfmsg(SMTDOK);
                    }
               }
          }
          else if (sameto("DATA",linput)) {
               if (smtdptr->minfo.rmtrcpt+smtdptr->minfo.locrcpt == 0) {
                    prfmsg(SMTDOSQ);
               }
               else {
                    smtdptr->minfo.hdrpos=ftell(smtdptr->fp);
                    if (fprintf(smtdptr->fp,
                                "Received: from [%s] by %s id %s; %s, %s\r\n",
                                inet_ntoa(tcpipinf[usrnum].inaddr),
                                smtpHost(),
                                &smtdptr->filnam[strlen(wrkdir)],
                                dow[daytoday()],
                                cvtDate(now(),today())) == EOF) {
                         shologd(usrnum,"SMTP WRITE ERROR",
                                 "smtdlin() Error writing message body");
                         prfmsg(SMTDFULL);
                         fseek(smtdptr->fp,smtdptr->minfo.hdrpos,SEEK_SET);
                    }
                    else {
                         prfmsg(SMTDSML);
                         usrptr->substt=RECVBODY;
                         smtdptr->numofhop++;
                         smtdptr->rcvError=0;
                    }
               }
          }
          else if (sameto("MAIL FROM:",linput)) {
               prfmsg(SMTDOSQ);
          }
          else {
               prfmsg(SMTDSYN);
          }
          break;
     case RECVBODY:
          if (sameas(linput,".")) {     /* end-of-message signal */
               if (smtdptr->rcvError == 0) {
                    smtdptr->minfo.endpos=ftell(smtdptr->fp)-CSTRLEN("\r\n");
                    /* the last "\r\n" written belongs to "\r\n.\r\n" */
                    /* sequence so skip it                            */
                    stlcpy(smtdptr->minfo.name,SMTNAME,QNMSIZ);
                    fseek(smtdptr->fp,0L,SEEK_SET);
                    if (fwrite(&smtdptr->minfo,1,sizeof(struct minfo),
                               smtdptr->fp) != sizeof(struct minfo)) {
                         shologd(usrnum,"SMTP WRITE ERROR",
                                 "smtdlin() Error writing inbound message info");
                         clsfil(FALSE);
                         prfmsg(SMTDLERR);
                    }
                    else {
                         clsfil(TRUE);
                         if (renwrk(smtdptr->filnam)) {
                              prfmsg(SMTDOK);
                         }
                         else {
                              shologd(usrnum,"SMTP SERVER RENAME FAILED",
                                      "smtdlin() Unable to rename work file \"%s\"",
                                      smtdptr->filnam);
                              unlink(smtdptr->filnam);
                              prfmsg(SMTDLERR);
                         }
                    }
               }
               else {
                    clsfil(FALSE);
                    if (smtdptr->rcvError == SMTDHOP) {
                         prfmsg(SMTDHOP,smtdptr->numofhop,maxhops);
                    }
                    else {
                         prfmsg(smtdptr->rcvError);
                    }
               }
               usrptr->substt=WAITFROM;
          }
          else {                   /* still receiving message */
               ASSERT(smtdptr->fp != NULL);

               /* handle transition from header to body */
               if (linput[0] == '\0' && smtdptr->minfo.bdypos == 0L) {

                    /* check various from addresses (if no error) */
                    if (smtdptr->rcvError == 0
                     && !sameas(smtdptr->minfo.from,smtdptr->hdrfrom)
                     && !sameas(smtdptr->minfo.from,smtdptr->hdrsndr)
                     && !sameas(smtdptr->minfo.from,smtdptr->hdrmail)) {
                         if (fprintf(smtdptr->fp,"X-Mail-From: %s\r\n"
                                    ,smtdptr->minfo.from) == EOF) {
                              shologd(usrnum,"SMTP WRITE ERROR",
                                      "smtdlin() Error writing message body");
                              smtdptr->rcvError=SMTDFULL;
                         }
                    }

                    /* add two extra bytes for "\r\n" */
                    smtdptr->minfo.bdypos=ftell(smtdptr->fp)+CSTRLEN("\r\n");
               }

               /* strip preceding dot if any */
               if (*(ptr=linput) == '.') {
                    ptr++;
               }

               /* still in the header, check for various headers */
               if (smtdptr->minfo.bdypos == 0L) {
                    if (sameto("Received:",ptr)) {
                         if (++smtdptr->numofhop > maxhops
                          && smtdptr->rcvError == 0) {
                              smtdptr->rcvError=SMTDHOP;
                         }
                    }
                    else if (sameto("From:",ptr)) {
                         extractAddr(smtdptr->hdrfrom,ptr);
                    }
                    else if (sameto("Sender:",ptr)) {
                         extractAddr(smtdptr->hdrsndr,ptr);
                    }
                    else if (sameto("X-Mail-From:",ptr)) {
                         extractAddr(smtdptr->hdrmail,ptr);
                    }
               }

               /* add data to file (if no error) */
               if (smtdptr->rcvError == 0) {
                    if (maxMsgSize != 0
                     && (smtdptr->msgsiz+=strlen(ptr)+CSTRLEN("\r\n"))
                      > maxMsgSize) {
                         smtdptr->rcvError=SMTD2BIG;
                    }
                    else if (fprintf(smtdptr->fp,"%s\r\n",ptr) == EOF) {
                         shologd(usrnum,"SMTP WRITE ERROR",
                                 "smtdlin() Error writing message body");
                         smtdptr->rcvError=SMTDFULL;
                    }
               }
          }
          break;
     }
     rstmbk();
     smtprf();
     return(TRUE);
}

static VOID
smtdsts(VOID)                      /* status handling                      */
{
     USHORT curtim;

#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     setmbk(smtdmb);
     clrprf();
     if (status == RING) {
          revCancel();
          clsfil(FALSE);
          rstchn();
          return;
     }
     if (status != CYCLE) {
          dfsthn();
          return;
     }
     if (usrptr->flags&BYEBYE) {
          return;
     }
     curtim=btuTicker()-smtdptr->sttime;
     if (curtim > smtdtmo && smtdtmo > 0 && smtdptr->sttime != 0) {
          revCancel();        /* should not be necessary, just to be safe */
          clsfil(FALSE);
          byetcp(SMTDTMOT);
          if (audsmtde) {
               shologd(usrnum,"SMTP SERVER TIMEOUT",
                       "Client connection timed out with %s",
                       inet_ntoa(tcpipinf[usrnum].inaddr));
          }
     }
     else {
          prcinp();
          if (usrptr->state == smtdstt && usrptr->substt != REVLOOK) {
               btuinj(usrnum,CYCLE);
          }
     }
}

static VOID
smtdfin(VOID)                      /* finish-up (system shutdown) routine  */
{
     if (smtmb != NULL) {
          clsmsg(smtmb);
          smtmb=NULL;
     }
     if (smtdmb != NULL) {
          clsmsg(smtdmb);
          smtdmb=NULL;
     }
}

static VOID
smtdscn(VOID)                      /* SMTP inbound mail scan task          */
{
     BEG_PHASE("smtdscn()",0);
     if (fnd1st(&smtdfb,wrkspec,0)) {
          filtsk(-1);
     }
     else {
          rtkick(smtdqsi,smtdscn);
     }
     END_PHASE("smtdscn()",0);
}

static VOID
filtsk(                           /* process incoming SMTP e-mail          */
INT taskid)
{
     GBOOL found=TRUE;

     BEG_PHASE("filtsk()",0);
     if (taskid != -1) {
          found=fndnxt(&smtdfb);
     }
     else {
          taskid=initask(filtsk);
     }
     if (!found) {
          mfytask(taskid,NULL);
          rtkick(smtdqsi,smtdscn);
          return;
     }
     setmem(&header,sizeof(header),0);
     header.state=OPNMFIL;
     stlcpy(header.filnam,wrkdir,sizeof(header.filnam));
     stlcat(header.filnam,smtdfb.ff_name,sizeof(header.filnam));
     mfytask(taskid,smtdtsk);
     END_PHASE("filtsk()",0);
}

static VOID
smtdtsk(                           /* SMTP Server task routine (import)    */
INT taskid)
{
     CHAR *fgetret;
     ULONG timeSlice,begTime,timeUsed;
     LONG fpos;
     INT rc,retval,msiz,len,i,nread;
     GBOOL firsttim;
     static FILE *tmpatt;
     static CHAR tmpattnm[GCMAXPTH];
     static CHAR smtlin[SMTLSZ+1];
     static struct addrinf addrinf;
     static struct message msg;

     BEG_PHASE("smtdtsk()",header.state);
     (VOID)taskid;
     if (timeDebt >= (timeSlice=impSlice())) {
          timeDebt-=timeSlice;
          END_PHASE("smtdtsk()",header.state);
          return;
     }
     begTime=hrtval()-timeDebt;
     setmbk(smtdmb);
     do {
          switch (header.state) {
          case OPNMFIL:
               if ((header.fp=fopen(header.filnam,FOPRB)) == NULL) {
                    shologd(noChannel,"SMTP FILE OPEN ERROR",
                            "smtdtsk() Mail file: \"%s\"",header.filnam);
                    mfytask(taskid,filtsk);
                    END_PHASE("smtdtsk()",header.state);
                    timeDebt=0;
                    return;
               }
               if (fread(&header.minfo,1,sizeof(struct minfo),header.fp)
                != sizeof(struct minfo)) {
                    shologd(noChannel,"SMTP READ ERROR",
                            "smtdtsk() Error reading message info");
                    finfil(taskid);
                    END_PHASE("smtdtsk()",header.state);
                    return;
               }
               if (!sameas(header.minfo.name,SMTNAME)) {
                    shologd(noChannel,"SMTP READ ERROR",
                            "smtdtsk() Invalid message info");
                    finfil(taskid);
                    END_PHASE("smtdtsk()",header.state);
                    return;
               }
               header.iinfo.fp=header.fp;
               header.iinfo.hdrpos=header.minfo.hdrpos;
               header.iinfo.bdypos=header.minfo.bdypos;
               header.tmplrcpt=header.minfo.locrcpt;
               if (header.minfo.locrcpt == 0) {
                    header.state=CHKRMT;
                    break;
               }
               fseek(header.fp,header.minfo.hdrpos,SEEK_SET);
               if (saveHdr) {
                    len=min(TXTLEN,
                            (INT)((header.minfo.bdypos-CSTRLEN("\r\n"))
                                 -header.minfo.hdrpos));
                    if ((len=fread(textbuf,1,len,header.fp)) < 0) {
                         len=0;
                    }
                    textbuf[len]='\0';
                    stpcr(textbuf);
                    *hdrbuf='\0';
                    cfgSetStr(SMTPSECTHDR,NULL,textbuf,hdrbuf,TXTLEN);
                    fseek(header.fp,header.minfo.hdrpos,SEEK_SET);
               }
               header.state=HEADER;
               break;
          case HEADER:
               header.state=CHKMSIZ;
               mimdeflt();
               header.mimeinf.bndry[0]='\0';
               header.attexist=FALSE;
               header.attrelnm[0]='\0';
               for (fgetret=(CHAR*)1,smtlin[0]=textbuf[0]='\0',firsttim=TRUE
                  ; fgetret != NULL ; ) {
                    stlcpy(textbuf,smtlin,txtbufsz);
                    while ((fgetret=fgets(smtlin,SMTLSZ,header.fp)) != NULL) {
                         /* get rid of "\r\n" */
                         smtlin[strlen(smtlin)-CSTRLEN("\r\n")]='\0';
                         strrpl(smtlin,'\t',' ');
                         if (firsttim || smtlin[0] == ' ') {
                              stlcat(textbuf,smtlin,txtbufsz);
                              smtlin[0]='\0';
                              firsttim=FALSE;
                         }
                         else {
                              break;
                         }
                    }
                    unpad(textbuf);
                    if (sameto("From:",textbuf)) {
                         if (!extractAddr(header.from,textbuf)
                          || !smtval(header.from)) {
                              *header.from='\0';
                              if (audsmtde) {
                                   shologd(noChannel,"SMTP INVALID FROM ADDR",
                                           "smtdtsk() %s",textbuf);
                              }
                         }
                    }
                    else if (sameto("Reply-To:",textbuf)) {
                         if (!extractAddr(header.repto,textbuf)
                          || !smtval(header.repto)) {
                              *header.repto='\0';
                              if (audsmtde) {
                                   shologd(noChannel,"SMTP INVALID REPLY-TO ADDR",
                                           "smtdtsk() %s",textbuf);
                              }
                         }
                    }
                    else if (sameto("Subject:",textbuf)) {
                         stlcpy(header.topic,crpstr(textbuf,':'),TPCSIZ);
                    }
                    else if (sameto("Date:",textbuf)) {
                         stlcpy(header.date,crpstr(textbuf,':'),DATESZ);
                    }
                    else if (sameto("MIME-Version:",textbuf)
                          && sameas(crpstr(textbuf,':'),"1.0")) {
                         header.state=MIMSTART;
                    }
                    else if (sameto("Content-Type:",textbuf)) {
                         prsctyp(crpstr(textbuf,':'));
                    }
                    else if (sameto("Content-Transfer-Encoding:",textbuf)) {
                         prscte(crpstr(textbuf,':'));
                    }
                    else if (sameto("Content-Disposition:",textbuf)) {
                         prscdsp(crpstr(textbuf,':'));
                    }
                    if (smtlin[0] == '\0') {
                         break;
                    }
               }
               break;
          case CHKMSIZ:
               /* leave room for first '\r', '\0' and whatever else        */
               /* comes along                                              */
               if (header.minfo.endpos-header.minfo.bdypos > TXTLEN-4) {
                    header.partno=1;
                    /* leave room for initial "\r", "part X of Y...", and  */
                    /* "Continued ..." messages                            */
                    header.partsize=(TXTLEN-1)-(strlen(getasc(SMTDPTN))
                                               +strlen(getasc(SMTDCNM))+1);
               }
               else {
                    header.partno=0;
                    header.partsize=(INT)(header.minfo.endpos
                                         -header.minfo.bdypos);
               }
               header.state=BODY;
               header.partpos=header.minfo.bdypos;
               break;
          case BODY:
               fseek(header.fp,header.partpos,SEEK_SET);
               strcpy(textbuf,"\r");
               msiz=(INT)min((LONG)header.partsize,
                             (header.minfo.endpos-header.partpos));
               len=strlen(textbuf);
               if (header.partno > 1) {
                    sprintf(&textbuf[len],getasc(SMTDPTN),header.partno);
               }
               len=strlen(textbuf);
               if (fread(&textbuf[len],1,msiz,header.fp) != msiz) {
                    shologd(noChannel,"SMTP READ ERROR",
                            "smtdtsk() Error reading message body");
                    finfil(taskid);
                    END_PHASE("smtdtsk()",header.state);
                    return;
               }
               textbuf[len+msiz]='\0';
               if (header.partno > 0) {
                    header.partpos=fpos=ftell(header.fp);
                    if (header.partpos < header.minfo.endpos) {
                         for (i=msiz+len-1
                            ; textbuf[i] != '\n' && textbuf[i] != '\r'
                            ; i--,fpos--) {
                              if (header.partpos-fpos >= msiz - 1) {
                                   fpos=header.partpos;
                                   i=msiz+len-1;
                                   break;
                              }
                         }
                         header.partpos=fpos;
                         strcpy(&textbuf[++i],getasc(SMTDCNM));
                    }
                    else {
                         header.partno=-1;
                    }
               }
               strstp(textbuf,'\n');
               fseek(header.fp,sizeof(struct minfo),SEEK_SET);
               header.state=SUBMIT;
               break;
          case MIMSTART:
               header.state=MIMDCODE;
               if (header.mimeinf.bndry[0] == '\0') {
                    header.mimstt=MST_BGN;
               }
               else {
                    header.mimstt=MST_BNDR;
               }
               break;
          case MIMDCODE:
               switch (header.mimstt) {
               case MST_BNDR:
                    switch (mst_bndr()) {
                    case MR_OK:
                         header.mimstt=MST_HDR;
                         mimdeflt();
                         break;
                    case MR_EOF:
                         header.mimstt=MST_END;
                         break;
                    }
                    break;
               case MST_HDR:
                    switch (mst_hdr()) {
                    case MR_OK:
                         header.mimstt=MST_BGN;
                         break;
                    case MR_EOF:
                         header.mimstt=MST_END;
                         break;
                    }
                    break;
               case MST_BGN:
                    switch (mst_bgn()) {
                    case MR_OK:
                         header.mimstt=MST_BODY;
                         break;
                    default:
                         header.mimstt=MST_ERR;
                    }
                    break;
               case MST_BODY:
                    retval=mst_body();
                    if (retval == MR_NEXT || retval == MR_EOF) {
                         if (header.cvt2att) {
                              if (header.attrelnm[0] == '\0') {
                                   stlcpy(header.attrelnm,
                                          header.mimeinf.fname,GCMAXFNM);
                              }
                              header.attexist=TRUE;
                              fclose(header.mimattfp);
                              header.mimattfp=NULL;
                         }
                         header.mimstt=(retval == MR_NEXT) ? MST_HDR : MST_END;
                         mimdeflt();
                    }
                    else if (retval == MR_ERR) {
                         header.mimstt=MST_ERR;
                    }
                    break;
               case MST_ERR:
                    mst_err();
                    header.mimstt=MST_END;
                    break;
               case MST_END:
                    if (header.mimattfp != NULL) {
                         fclose(header.mimattfp);
                         header.mimattfp=NULL;
                    }
                    mst_end();
                    header.state=CHKRMT;
                    break;
               }
               break;
          case SUBMIT:
               if (header.minfo.locrcpt == 0) {
                    header.minfo.locrcpt=header.tmplrcpt;
                    if (header.partno == 0 || header.partno == -1) {
                         header.state=CHKRMT;
                    }
                    else {
                         header.partno++;
                         header.state=BODY;
                    }
                    break;
               }
               if (fread(&header.rinfo,1,sizeof(struct rinfo),header.fp)
                != sizeof(struct rinfo)) {
                    shologd(noChannel,"SMTP READ ERROR",
                            "smtdtsk() Error reading recipient info");
                    finfil(taskid);
                    END_PHASE("smtdtsk()",header.state);
                    return;
               }
               if (header.rinfo.flags&LOCRCPT) {
                    header.minfo.locrcpt--;
                    setmem(&msg,sizeof(struct message),0);
                    if (header.repto[0] != '\0') {
                         parseIntAddr(header.repto,&addrinf);
                    }
                    else if (header.from[0] != '\0') {
                         parseIntAddr(header.from,&addrinf);
                    }
                    else {
                         parseIntAddr(header.minfo.from,&addrinf);
                    }
                    smtpCvtFrom(msg.from,&addrinf);
                    stlcpy(msg.to,header.rinfo.to,MAXADR);
                    stlcpy(msg.topic,
                           header.topic[0] != '\0' ? header.topic : "<none>",
                           TPCSIZ);
                    msg.crdate=msg.crtime=0U;
                    if ((header.partno == 0 || header.partno == 1)
                     && header.minfo.flags&HASATT) {
                         msg.flags|=FILATT|FILAPV;
                         stlcpy(msg.attname,header.minfo.relnam,GCMAXFNM);
                         getattnm(header.filnam,header.mimattnm);
                         header.mimattfp=fopen(header.mimattnm,FOPRB);
                         if (header.mimattfp == NULL) {
                              shologd(noChannel,"SMTP ERROR",
                                     "smtdtsk() Error opening attachment file");
                              finfil(taskid);
                              END_PHASE("smtdtsk()",header.state);
                              return;
                         }
                         stlcpy(tmpattnm,ulname(&msg),sizeof(tmpattnm));
                         chmod(tmpattnm,S_IWRITE|S_IREAD);
                         if ((tmpatt=fopen(tmpattnm,FOPWB)) == NULL) {
                              shologd(noChannel,"SMTP ERROR",
                                      "smtdtsk() Error creating temp. attachment file");
                              fclose(header.mimattfp);
                              finfil(taskid);
                              END_PHASE("smtdtsk()",header.state);
                              return;
                         }
                         header.state=CPYATT;
                    }
                    else {
                         msg.flags&=~FILATT;
                         msg.flags&=~FILAPV;
                         tmpattnm[0]='\0';
                         header.state=CYCIMP;
                    }
                    inigmerq(impbuf);
                    if (saveHdr && header.partno == 0 || header.partno == 1) {
                         gmeSetAppInfo(impbuf,hdrbuf);
                    }
               }
               break;
          case CPYATT:
               nread=fread(vdatmp,1,vdasiz,header.mimattfp);
               if (nread < 0) {
                    shologd(noChannel,"SMTP ERROR",
                            "smtdtsk() Error reading attachment file");
                    fclose(header.mimattfp);
                    fclose(tmpatt);
                    unlink(tmpattnm);
                    finfil(taskid);
                    END_PHASE("smtdtsk()",header.state);
                    return;
               }
               else if (nread == 0) {
                    fclose(header.mimattfp);
                    fclose(tmpatt);
                    header.state=CYCIMP;
               }
               else {
                    if (fwrite(vdatmp,1,nread,tmpatt) != nread) {
                         shologd(noChannel,"SMTP ERROR",
                                 "smtdtsk() Error writing attachment file");
                         fclose(header.mimattfp);
                         fclose(tmpatt);
                         unlink(tmpattnm);
                         finfil(taskid);
                         END_PHASE("smtdtsk()",header.state);
                         return;
                    }
               }
               break;
          case CYCIMP:
               inetinfo=&header.iinfo;
               rc=impmsg(impbuf,&msg,textbuf,tmpattnm);
               inetinfo=NULL;
               switch (rc) {
               default:
                    if (audsmtde) {
                         shologd(noChannel,"SMTP IMPORT ERROR",
                                 "smtdtsk() Return code from GME: %d - State: %d",
                                 rc,header.state);
                    }
                    if (header.partno == 0 || header.partno == 1) {
                         strcpy(msg.to,msg.from);
                         strcpy(msg.from,maildaem);
                         strcpy(msg.topic,"SMTP import error #");
                         stlcat(msg.topic,spr("%d",rc),TPCSIZ);
                         msg.flags&=~FILATT;
                         simpsnd(&msg,textbuf,"");
                    }
                    header.state=SUBMIT;
                    break;
               case GMEAGAIN:
                    header.state=CYCIMP;
                    break;
               case GMEOK:
               case GMEAFWD:
               case GMERRG:
                    header.state=SUBMIT;
                    if (audsmtd) {
                         shologd(noChannel,"SMTP MAIL RECEIVED",
                                 "From: %s To: %s",crpstr(msg.from,':'),msg.to);
                    }
                    if (onsys(msg.to)) {
                         clrmlt();
                         prfmlt(SMTDEML);
                         injoth();
                    }
                    break;
               }
               break;
          case CHKRMT:
               fclose(header.fp);
               header.fp=NULL;
               if (header.minfo.rmtrcpt > 0) {
                    stlcpy(tmppath,smtobd,sizeof(tmppath));
                    if (!makeUniqueFName(tmppath,"$$H")) {
                         shocst("SMTP OPEN FILE ERROR",
                                "smtdtsk(): Could not open message file");
                    }
                    else {
                         if (rename(header.filnam,tmppath) == -1) {
                              shocst("SMTP RENAME FILE ERROR",
                                     "smtdtsk(): Could not rename message file");
                         }
                    }
               }
               finfil(taskid);
               END_PHASE("smtdtsk()",header.state);
               return;
          }
          timeUsed=hrtval()-begTime;    /* NOTE: relies on unsigned wrap   */
     } while (timeUsed < timeSlice);
     timeDebt=timeUsed-timeSlice;
     END_PHASE("smtdtsk()",header.state);
}

static ULONG
impSlice(VOID)                     /* import time slice based on load      */
{
     ULONG n,retval;

     if ((n=nliniu()) == 0) {
          timeDebt=0;
          return(zerTicks);
     }
     retval=maxTicks-((n-1)*(maxTicks-minTicks))/((chFullPct*nterms)/100);
     if ((n=rsptim/rtFactor) < retval) {
          retval-=n;
          if (retval > maxTicks) {
               retval=maxTicks;
          }
          else if (retval < minTicks) {
               retval=minTicks;
          }
     }
     else {
          retval=minTicks;
     }
     return(retval);
}

static VOID
clndir(VOID)                       /* removes WRK files from inbound dir   */
{
     INT clncnt;
     struct ffblk fb;

     clncnt=0;
     sprintf(tmppath,"%s*.wrk",wrkdir);
     if (fnd1st(&fb,tmppath,0)) {
          do {
               sprintf(tmppath,"%s%s",wrkdir,fb.ff_name);
               unlink(tmppath);
               clncnt++;
          } while (fndnxt(&fb));
     }
     if (clncnt > 0) {
          shocst("SMTP WORK FILE CLEANUP",
                 "Removed %d old work file(s)",clncnt);
     }
}

static GBOOL                       /*   TRUE=always-valid cmd, handled     */
anytime(                           /* checks input for always-valid cmd    */
CHAR *linput)                      /*   line of input to check             */
{
     GBOOL retval=TRUE;
     CHAR *ptr,*userid;

     clrprf();
     setmbk(smtdmb);
     if (sameto("NOOP",linput)) {
          prfmsg(SMTDOK);
     }
     else if (sameto("HELO",linput)) {
          prfmsg(SMTDOK);
     }
     else if (sameto("HELP",linput)) {
          prfmsg(SMTDHLP2);
     }
     else if (sameto("VRFY",linput)) {
          if ((ptr=crpstr(linput,' ')) == linput) {
               prfmsg(SMTDARQ);
          }
          else if ((userid=(*usrofals)(ptr)) == NULL) {
               prfmsg(SMTDUNF,ptr);
          }
          else {
               prfmsg(smtihst ? SMTDUSRH : SMTDUSR,userid,(*alsofusr)(userid));
          }
     }
     else if (sameto("SEND",linput)
           || sameto("SOML",linput)
           || sameto("SAML",linput)
           || sameto("TURN",linput)
           || sameto("EXPN",linput)) {
          prfmsg(SMTDNIMP);
     }
     else {
          retval=FALSE;
     }
     smtprf();
     rstmbk();
     return(retval);
}

static VOID
clsfil(                            /* SMTP server close current mail file  */
GBOOL good)                        /*   TRUE=good message received         */
{
#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     if (smtdptr->fp != NULL) {
          fclose(smtdptr->fp);
          smtdptr->fp=NULL;
          if (!good) {
               unlink(smtdptr->filnam);
          }
     }
}

static GBOOL
renwrk(                            /* rename "work" file to "ready" file   */
CHAR *wrkfil)                      /*   "work" file to rename              */
{
     CHAR *ptr;

     stlcpy(tmppath,wrkfil,sizeof(tmppath));
     if ((ptr=strrchr(tmppath,'.')) != NULL) {
          *ptr='\0';
     }
     stlcat(tmppath,".rdy",sizeof(tmppath));
     return(rename(wrkfil,tmppath) == 0);
}

static VOID                        /*   returns pointer to work file name  */
makwrk(VOID)                       /* make work file for incoming mail     */
{
     CHAR *ptr;
     CHAR tmpfil[FNEXSZ];
     USHORT tiebrk=0;

#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     while (1) {
          if (tiebrk == 1000U) {
               catastro("SMTP SERVER: Cannot create work file!");
          }
          stzcpy(smtdptr->filnam,wrkdir,GCMAXPTH);
          stlcat(smtdptr->filnam,
                 stzcpy(tmpfil,spr("%x%x.rdy",now(),tiebrk++),FNEXSZ),GCMAXPTH);
          if (access(smtdptr->filnam,0) == -1) {
               ptr=strrchr(smtdptr->filnam,'.');
               *ptr='\0';
               stlcat(smtdptr->filnam,".wrk",GCMAXPTH);
               if (access(smtdptr->filnam,0) == -1) {
                    return;
               }
          }
     }
}

static INT
valusr(                            /* is this a valid user on our system?  */
const CHAR *fromadr,               /*   MAIL FROM address                  */
const CHAR *rcpto,                 /*   RCPT TO line                       */
CHAR *locadr)                      /*   buffer for converted local address */
{
     INT rc;
     CHAR wrkbuf[MAXADR];

     BEG_PHASE("valusr()",header.state);
     if (!extractAddr(wrkbuf,rcpto)) {
          return(BADADDR);
     }
     switch (rc=xltInt2GME(wrkbuf,fromadr)) {
     case LOCADDR:
     case RMTADDR:
          stlcpy(locadr,wrkbuf,MAXADR);
          break;
     }
     return(rc);
}

VOID
revStart(                          /* start reverse lookup process         */
const CHAR *domain)                /*   domain name to look up             */
{
#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     ASSERT(domain != NULL);
     ASSERT(smtdptr->revState == REVIDLE);
#if defined(EXTRA_LOGGING)
     logsmtd(usrnum,spr("reverse lookup requested on %s",domain),LOGINFO);
#endif
     distmo();
     usrptr->substt=REVLOOK;
     smtdptr->revState=REVCNAME;
     stlcpy(smtdptr->revDomain,domain,DNSNSZ);
     stlcpy(smtdptr->revDNS.name,domain,DNSNSZ);
     smtdptr->revDNS.numaddr=1;
     smtdptr->revDNS.callbk=revCallback;
     dnsn2at(&smtdptr->revDNS,TYPECNAME);
}

VOID
revCallback(                       /* callback function for DNS lookup     */
struct dns *dnsptr)                /*   filled in DNS info structure       */
{
#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     /* callback received outside normal context */
     if (usrptr->state != smtdstt
      || usrptr->substt != REVLOOK
      || smtdptr->revState == REVIDLE) {
          if (audsmtde) {
               shologd(usrnum,"SMTP DNS C/B TOO LATE","DNS callback too late");
          }
          return;
     }
#if defined(EXTRA_LOGGING)
     logsmtd(usrnum
            ,spr("reverse lookup callback; sub=%d, rev=%d, stat=%d"
                ,usrptr->substt,smtdptr->revState,dnsptr->status)
            ,LOGINFO);
#endif
     /* we called dnsabt(), caller will handle everything */
     if (dnsptr->status == DNSABT) {
#if defined(EXTRA_LOGGING)
          logsmtd(usrnum,"DNS operation aborted",LOGINFO);
#endif
          return;
     }

     setmbk(smtdmb);
     clrprf();
     switch (smtdptr->revState) {
     case REVCNAME:
          if (dnsptr->status < 0) {
#if defined(EXTRA_LOGGING)
               logsmtd(usrnum,"CNAME lookup failed",LOGINFO);
#endif
               stlcpy(smtdptr->revDNS.name,smtdptr->revDomain,DNSNSZ);
          }
#if defined(EXTRA_LOGGING)
          else {
               logsmtd(usrnum,"CNAME lookup succeeded",LOGINFO);
          }
          logsmtd(usrnum,"now doing MX lookup",LOGINFO);
#endif
          memset(&smtdptr->revDNSEX,0,sizeof(struct dnshstex));
          smtdptr->revDNSEX.buf=smtdptr->revTempDomain;
          smtdptr->revDNSEX.bufsiz=DNSNSZ;
          smtdptr->revDNSEX.maxofhst=1;
          smtdptr->revState=REVGETMX;
          dnsn2ex(dnsptr,TYPEMX,&smtdptr->revDNSEX);
          break;
     case REVGETMX:
          if (dnsptr->status < 0) {
#if defined(EXTRA_LOGGING)
               logsmtd(usrnum,"MX lookup failed, trying TYPEA",LOGINFO);
#endif
               stlcpy(smtdptr->revDNS.name,smtdptr->revDomain,DNSNSZ);
               smtdptr->revState=REVTYPEA;
               dnsn2at(dnsptr,TYPEA);
          }
          else {
#if defined(EXTRA_LOGGING)
               logsmtd(usrnum,"MX lookup succeeded, continuing",LOGINFO);
#endif
               revContinue();
          }
          break;
     case REVTYPEA:
          if (dnsptr->status < 0) {
#if defined(EXTRA_LOGGING)
               logsmtd(usrnum,"TYPEA lookup failed, rejecting",LOGINFO);
#endif
               switch (dnsptr->status) {
               case DNSNOX:
               case DNSUNK:
                    prfmsg(SMTDRVNO,smtdptr->minfo.from);
                    break;
               default:
                    prfmsg(SMTDRVER);
                    break;
               }
               revDone();
               clsfil(FALSE);
               usrptr->substt=WAITFROM;
          }
          else {
#if defined(EXTRA_LOGGING)
               logsmtd(usrnum,"TYPEA lookup succeeded, continuing",LOGINFO);
#endif
               revContinue();
          }
          break;
     default:
          ASSERT(FALSE);
     }
     smtprf();
     rstmbk();
}

VOID
revContinue(VOID)                  /* lookup succeeded, continue           */
{
     revDone();
     prfmsg(SMTDOK);
     usrptr->substt=WAITRCPT;
}

VOID
revCancel(VOID)                    /* cancel reverse lookup if in progress */
{
     if (usrptr->substt == REVLOOK) {
          dnsabt();
          revDone();
     }
}

VOID
revDone(VOID)                      /* done with reverse lookup             */
{
     enatmo();
     smtdptr->revState=REVIDLE;
     btuinj(usrnum,CYCLE);
}

static VOID
distmo(VOID)                       /* disable channel timeout              */
{
#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     smtdptr->sttime=0;
}

static VOID
enatmo(VOID)                       /* enable channel timeout               */
{
#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     if ((smtdptr->sttime=btuTicker()) == 0) {
          smtdptr->sttime++;
     }
}

static VOID
finfil(                            /* finish up current mail file          */
INT taskid)                        /*   task to cancel                     */
{
     BEG_PHASE("finfil()",header.state);
     timeDebt=0;
     if (header.fp != NULL) {
          fclose(header.fp);
     }
     unlink(header.filnam);
     getattnm(header.filnam,tmppath);
     unlink(tmppath);
     mfytask(taskid,filtsk);
     END_PHASE("finfil()",header.state);
}

static VOID
prcinp(VOID)                       /* process input line from SMTP client  */
{
     INT nread,nroom;
     CHAR *ptr;

#ifdef DEBUG
     smtdptr=(struct smtdusr *)vdaptr;
#endif
     while ((nroom=MAXBYT-smtdptr->rcvcnt-1) > 0
         && (nread=btuica(usrnum,smtdptr->rcvbuf+smtdptr->rcvcnt,nroom)) > 0) {

          /* reset timer since we've received input */
          enatmo();

          /* prepare input for parsing */
          smtdptr->rcvcnt=memstp(smtdptr->rcvbuf,smtdptr->rcvcnt+nread,'\0');
          smtdptr->rcvbuf[smtdptr->rcvcnt]='\0';
          smtdptr->rcvcnt=strlen(strstp(smtdptr->rcvbuf,'\r'));

          /* check if this is the continuation of a long line */
          if (smtdptr->gotLongLine) {
               if ((ptr=strchr(smtdptr->rcvbuf,'\n')) == NULL) {
                    smtdptr->rcvbuf[0]='\0';
                    smtdptr->rcvcnt=0;
                    continue;      /* try getting more input */
               }
               else {
                    /* At this point we've found the end of the long line; */
                    /* we can now tell the sender that we didn't like that */
                    /* (if we're not in the process of receiving the body) */
                    /* and resume normal processing.                       */
                    if (usrptr->substt != RECVBODY) {
                         setmbk(smtdmb);
                         prfmsg(SMTDLTL);
                         smtprf();
                         rstmbk();
                    }
                    smtdptr->gotLongLine=FALSE;
                    smtdptr->rcvcnt=strlen(strmove(smtdptr->rcvbuf,ptr+1));
               }
          }

          /* parse input into lines and submit to line input handler */
          while ((ptr=strchr(smtdptr->rcvbuf,'\n')) != NULL
           && usrptr->substt != REVLOOK) {
           /*  We need to stop calling smtdlin() while in the reverse lookup
               stage.  Processing can continue after the reverse lookup has
               finished. */
               *ptr='\0';
               if (!smtdlin(smtdptr->rcvbuf)) {
                    smtdptr->rcvbuf[0]='\0';
                    smtdptr->rcvcnt=0;
                    btucli(usrnum);
                    break;
               }
               smtdptr->rcvcnt=strlen(strmove(smtdptr->rcvbuf,ptr+1));
          }

          /* check for an excessively long line */
          if (smtdptr->rcvcnt >= MAXBYT-1) {
               biglin();
          }
     }
}

static VOID
biglin(VOID)                       /* process a big line (fills rcvbuf)    */
{
     smtdptr->gotLongLine=TRUE;
     if (usrptr->substt == RECVBODY && smtdptr->rcvError == 0) {
          smtdptr->rcvError=SMTDLTL;
     }
     smtdptr->rcvbuf[0]='\0';
     smtdptr->rcvcnt=0;
}

static VOID
smtprf(VOID)                       /* our own outprf() function            */
{
     if (prfptr != prfbuf) {
          stpans(prfbuf);
          logsmtd(usrnum,prfbuf,OUTGING);
          outprf(usrnum);
          clrprf();
     }
}

static VOID
logsmtd(                           /* log receive transaction              */
INT Channel,
CHAR *logstr,
INT type)
{
     FILE *logfp;
     CHAR *typeStr;
     CHAR fnbuf[GCMAXPTH];
     CHAR fnroot[GCMAXPTH];
     CHAR fnpath[GCMAXPTH];
     static CHAR wrkbuf[SMTLSZ];

     if (!smtdlog) {
          return;
     }

     (VOID)fileparts(GCPART_PATH, smtdlnam, fnpath, GCMAXPTH);
     (VOID)fileparts(GCPART_FILE, smtdlnam, fnroot, GCMAXPTH);

     sprintf(fnbuf, "%s%s.%03i", fnpath, fnroot, Channel);

     if ((logfp=fopen(fnbuf,FOPAA)) == NULL) {
          smtdlog=FALSE;
          shocst("SMTP RECEIVE LOG OPEN ERROR",
                 "Could not open \"%s\".  Logging disabled.",smtdlnam);
          return;
     }
     stzcpy(wrkbuf,logstr,SMTLSZ);
     switch (type) {
     case OUTGING:
          typeStr="-->";
          break;
     case INCMING:
          typeStr="<--";
          break;
     case SHOWLOG:
          typeStr="***";
          break;
     case LOGINFO:
          typeStr="---";
          break;
     default:
          ASSERT(FALSE);
          typeStr="";
     }
     if (fprintf(logfp,"%s %s: %s%s\n",ncdate(today()),nctime(now()),
                 typeStr,unpad(wrkbuf)) == EOF) {
          smtdlog=FALSE;
          shocst("SMTP RECEIVE LOG WRITE ERROR",
                 "Could not write to receive log.  Logging disabled.");
     }
     fclose(logfp);
}

static VOID
shologd(                            /* prints to audit trail, logs to file  */
INT Channel,
CHAR *header,
CHAR *footer,
...)
{
     va_list ftlist;
     static CHAR wrkbuf[SMTLSZ*2];
     CHAR logbuf[(AUDBRIEFSIZ-1)+2+((SMTLSZ*2)-1)+1];

     va_start(ftlist,footer);
     if (vsprintf(wrkbuf,footer,ftlist) >= (SMTLSZ*2)) {
          catastro("GALSMTP: Receiving log buffer overrun error.");
     }
     va_end(ftlist);
     shocst(header,"%.*s",AUDDETSIZ-1,wrkbuf);
     sprintf(logbuf,"%.*s: %.*s",AUDBRIEFSIZ-1,header,(SMTLSZ*2)-1,wrkbuf);
     logsmtd(Channel,logbuf,SHOWLOG);
}

static VOID
prsctyp(                           /* parse MIME content type              */
CHAR *inpstr)                      /*   MIME content type string           */
{
     CHAR *args[2][10],*ptr;

     BEG_PHASE("prsctyp()",header.state);
     crushit(inpstr);
     prsmim(inpstr,args);
     if ((ptr=strchr(args[0][0],'/')) != NULL) {
          stlcpy(header.mimeinf.subtype,++ptr,CHSETSIZ);
     }
     if (args[0][0][0] != '\0') {
          stlcpy(header.mimeinf.typedsc,args[0][0],CHSETSIZ);
     }
     if (sameto("text",args[0][0])) {
          stlcpy(header.mimeinf.charset,hdrval(args,"charset"),CHSETSIZ);
          if (sameas("text/plain",args[0][0])) {
               header.mimeinf.type=MT_TEXT;
          }
          else if (sameas("text/html",args[0][0])) {
               header.mimeinf.type=MT_TXHTML;
          }
          else {
               header.mimeinf.type=MT_TXUN;
          }
     }
     else if (sameto("multipart",args[0][0])) {
          if (header.mimeinf.bndry[0] == '\0') {
               strcpy(header.mimeinf.bndry,"--");
               stlcat(header.mimeinf.bndry,hdrval(args,"boundary"),MIMIDSZ);
          }
          if (sameas("multipart/mixed",args[0][0])) {
               header.mimeinf.type=MT_MPMX;
          }
          else {
               header.mimeinf.type=MT_MPUN;
          }
     }
     else if (sameto("application",args[0][0])) {
          if (sameas("application/octet-stream",args[0][0])) {
               header.mimeinf.type=MT_APP;
          }
          else {
               header.mimeinf.type=MT_APPUN;
          }
     }
     else {
          header.mimeinf.type=MT_UNKN;
     }
     ptr=hdrval(args,"name");
     if (ptr[0] != '\0') {
          stlcpy(header.mimeinf.fname,cvtfname(ptr),GCMAXFNM);
     }
     END_PHASE("prsctyp()",header.state);
}

static VOID
prscte(                            /* parse MIME content transfer encoding */
CHAR *inpstr)                      /*   MIME cte string                    */
{
     CHAR *args[2][10];

     BEG_PHASE("prscte()",header.state);
     crushit(inpstr);
     prsmim(inpstr,args);
     header.mimeinf.cte=CTE_7BIT;
     if (sameas("7bit",args[0][0])) {
          header.mimeinf.cte=CTE_7BIT;
     }
     else if (sameas("8bit",args[0][0])) {
          header.mimeinf.cte=CTE_8BIT;
     }
     else if (sameas("binary",args[0][0])) {
          header.mimeinf.cte=CTE_BIN;
     }
     else if (sameas("base64",args[0][0])) {
          header.mimeinf.cte=CTE_B64;
     }
     else if (sameas("quoted-printable",args[0][0])) {
          header.mimeinf.cte=CTE_QPRT;
     }
     END_PHASE("prscte()",header.state);
}

static VOID
prscdsp(                           /* parse MIME content disposition       */
CHAR *inpstr)                      /*   MIME cdsp string                   */
{
     CHAR *args[2][10],*ptr;

     BEG_PHASE("prsccdsp()",header.state);
     crushit(inpstr);
     prsmim(inpstr,args);
     ptr=hdrval(args,"filename");
     if (ptr[0] != '\0') {
          stlcpy(header.mimeinf.fname,cvtfname(ptr),GCMAXFNM);
     }
     END_PHASE("prsccdsp()",header.state);
}

static CHAR *                      /*   returns pointer to "crushed" stg   */
crushit(                           /* removes non-printable ACSII chars    */
CHAR *inpstr)
{
     GBOOL inquoted=FALSE;
     CHAR *ptr;

     for (ptr=inpstr ; *ptr != '\0' ; ptr++) {
          if (!inquoted) {
               if (*ptr == '"') {
                    inquoted=TRUE;
               }
               else if (*ptr == ';') {
                    *ptr='\xFF';
               }
               else if (*ptr < '!' || *ptr > '~') {
                    movmem(ptr+1,ptr,strlen(ptr));
                    ptr--;
               }
          }
          else if (*ptr == '"') {
              inquoted=FALSE;
          }
     }
     return(inpstr);
}

static VOID
prsmim(                            /* parse MIME header string             */
CHAR *inpstr,                      /*   input string                       */
CHAR *args[2][10])                 /*   array of pointers to args/values   */
{
     CHAR *ptr=inpstr,*secptr;
     INT i,j;
     static CHAR empt[]="";

     setmem(args,sizeof(CHAR *)*20,0);
     for (i=0 ; i < 10 ; i++) {
          args[0][i]=ptr;
          if ((secptr=strchr(ptr,'\xFF')) != NULL) {
               *secptr='\0';
               ptr=++secptr;
               if (ptr == '\0') {
                    break;
               }
          }
          else {
               break;
          }
     }
     for (i=0 ; i < 10 ; i++) {
          if (args[0][i] != NULL) {
               if ((secptr=strchr(args[0][i],'=')) != NULL) {
                    *secptr='\0';
                    if (*(++secptr) == '"') {
                         secptr++;
                    }
                    if (secptr[strlen(secptr)-1] == '"') {
                         secptr[strlen(secptr)-1]='\0';
                    }
                    args[1][i]=secptr;
               }
          }
          else {
               break;
          }
     }
     for (i=0 ; i < 10 ; i++) {
          for (j=0 ; j < 2 ; j++) {
               if (args[j][i] == NULL) {
                    args[j][i]=empt;
               }
          }
     }
}

static CHAR *                      /*   returns pointer to argument value  */
hdrval(                            /* get argument value                   */
CHAR *args[2][10],                 /*   array of arguments/values          */
CHAR *name)                        /*   argument to search for             */
{
     INT i;
     static CHAR empt[]="";

     for (i=0 ; i < 10 ; i++) {
          if (sameas(args[0][i],name)) {
               return(args[1][i]);
          }
     }
     return(empt);
}

static const CHAR *                /*   return pointer to "good" file name */
cvtfname(                          /* convert file name to DOS convention  */
const CHAR *badfname)
{
     CHAR *cp,*ext;
     static CHAR fname[GCMAXFNM];

     BEG_PHASE("cvtfname()",header.state);
     if (isValidFileName(badfname)) {
          stlcpy(fname,badfname,GCMAXFNM);
     }
     else {
          /* copy name into return buffer, removing any path info */
          if ((cp=strrchr(badfname,'\\')) != NULL
           || (cp=strrchr(badfname,'/')) != NULL
           || (cp=strrchr(badfname,':')) != NULL) {
               stlcpy(fname,cp+1,GCMAXFILE+1);
          }
          else {
               stlcpy(fname,badfname,GCMAXFILE+1);
          }
          /* find and remove any partial extension */
          if ((cp=strchr(fname,'.')) == NULL) {
               cp=&fname[strlen(fname)];
          }
          else {
               *cp='\0';
          }
          /* make sure file name isn't blank */
          if (*fname == '\0') {
               cp=stpcpy(fname,"FILE");
          }
          /* find and append actual extension */
          if ((ext=strrchr(badfname,'.')) != NULL) {
               stlcpy(cp,ext,CSTRLEN(".")+GCMAXEXT+1);
          }
          ASSERT(strlen(fname) < GCMAXFNM);
          /* replace any characters that aren't allowed in file names */
          for (cp=fname ; *cp != '\0' ; ++cp) {
               if (*cp != '.' && !isValidFileNameChar(*cp)) {
                    *cp='_';
               }
          }
          /* replace entire name if it's reserved */
          if (rsvnam(fname)) {
               strcpy(fname,"FILE.EXT");
          }
          ASSERT(isValidFileName(fname));
     }
     END_PHASE("cvtfname()",header.state);
     return(fnmcse(fname));
}

/***************************************************************************
 * NOTE: the following two functions were added because the isvalfn() and  *
 * isvalfc() functions in FIOAPI do not work correctly in this application.*
 ***************************************************************************/

GBOOL
isValidFileName(                   /* is this a valid DOS file name?       */
const CHAR *fileName)              /*   file name to check                 */
{
     const CHAR *cp;
     INT i;

     for (i=1,cp=fileName ; *cp != '\0' && *cp != '.' ; ++i,++cp) {
          if (i > GCMAXFILE || !isValidFileNameChar(*cp)) {
               return(FALSE);
          }
     }
     if (*cp++ == '.') {
          for (i=1 ; *cp != '\0' ; ++i,++cp) {
               if (i > GCMAXEXT || !isValidFileNameChar(*cp)) {
                    return(FALSE);
               }
          }
     }
     return(!rsvnam(fileName));
}

GBOOL
isValidFileNameChar(               /* is this a valid DOS file name char?  */
CHAR c)                            /*   character to test                  */
{
     return(c >  ' ' && c != '.' && c != '"' && c != '/' && c != '\\'
         && c != '[' && c != ']' && c != ':' && c != ';' && c != '|'
         && c != '<' && c != '>' && c != '+' && c != '=' && c != ',');
}

static INT                         /*   indicates if a bndry and type      */
isbndry(                           /* check if input string is a bonudary  */
CHAR *inpstr)
{
     INT len;

     if (header.mimeinf.bndry[0] == '\0') {
          return(NOBNDRY);
     }
     len=strlen(header.mimeinf.bndry);
     if (strncmp(header.mimeinf.bndry,inpstr,len) == 0) {
          if (inpstr[len] == '\r') {
               return(BNDRY);
          }
          else if ((inpstr[len] == '-') && (inpstr[len+1] == '-')) {
               return(CLSBNDRY);
          }
     }
     return(NOBNDRY);
}

static INT
mst_bndr(VOID)                     /* search for MIME boundary             */
{
     INT i;

     BEG_PHASE("mst_bndr()",header.state);
     if (header.mimeinf.bndry[0] == '\0') {
          return(MR_EOF);
     }
     for (i=0 ; i < NUMLINS ; i++) {
          if (fgets(vdatmp,SMTLSZ,header.fp) != NULL) {
               switch (isbndry(vdatmp)) {
               case CLSBNDRY:
                    return(MR_EOF);
               case BNDRY:
                    return(MR_OK);
               }
          }
          else {
               return(MR_EOF);
          }
     }
     return(MR_CYCLE);
}

static INT
mst_hdr(VOID)                      /* read MIME header                     */
{
     INT i;
     LONG fpos;
     CHAR *retval;
     GBOOL firsttim;
     static CHAR smtlin[SMTLSZ+1];

     BEG_PHASE("mst_hdr()",header.state);
     for (retval=(CHAR*)1,smtlin[0]=vdatmp[0]='\0',i=0,firsttim=TRUE ;
                                                       retval != NULL ; i++) {
          stlcpy(vdatmp,smtlin,vdasiz);
          while ((retval=fgets(smtlin,SMTLSZ,header.fp)) != NULL) {
               /* get rid of "\r\n" */
               smtlin[strlen(smtlin)-CSTRLEN("\r\n")]='\0';
               if (smtlin[0] == '\t') {
                    smtlin[0]=' ';
               }
               if (firsttim || smtlin[0] == ' ') {
                    stlcat(vdatmp,smtlin,vdasiz);
                    smtlin[0]='\0';
                    firsttim=FALSE;
               }
               else {
                    break;
               }
          }
          if (sameto("Content-Type:",vdatmp)) {
               prsctyp(crpstr(vdatmp,':'));
          }
          else if (sameto("Content-Transfer-Encoding:",vdatmp)) {
               prscte(crpstr(vdatmp,':'));
          }
          else if (sameto("Content-Disposition:",vdatmp)) {
               prscdsp(crpstr(vdatmp,':'));
          }
          if (smtlin[0] == '\0') {
               return(MR_OK);
          }
          else if (i == NUMLINS) {
               /* smtlin + \r\n */
               fpos=ftell(header.fp)-strlen(smtlin)-CSTRLEN("\r\n");
               fseek(header.fp,fpos,SEEK_SET);
               return(MR_CYCLE);
          }
     }
     return(MR_EOF);
}

static INT                         /*   returns OK/Error                   */
mst_bgn(VOID)                      /* prepare to start body processing     */
{
     CHAR *ptr;

     BEG_PHASE("mst_bgn()",header.state);
     if (header.mimfp == NULL && nmsgfile(TRUE) != MR_OK) {
          return(MR_ERR);
     }
     if ((header.mimeinf.type == MT_TEXT || header.mimeinf.type == MT_TXUN)
      && header.mimeinf.fname[0] == '\0') {
          header.cvt2att=FALSE;
     }
     else {
          header.cvt2att=TRUE;
     }
     if (header.mimeinf.type == MT_TEXT && !header.cvt2att) {
          return(MR_OK);
     }
     else if (header.mimeinf.type == MT_TXUN && !header.cvt2att) {
          setmbk(smtdmb);
          sprintf(vdatmp,getasc(SMTDAPL1),header.mimeinf.subtype,
                                          header.mimeinf.charset);
          rstmbk();
          if (fwrite(vdatmp,strlen(vdatmp),1,header.mimfp) != 1) {
               shologd(noChannel,"SMTP MIME WRITE ERROR",
                       "mst_bgn(): Error writing message body");
               return(MR_ERR);
          }
          else {
               return(MR_OK);
          }
     }
     else {
          if (header.mimeinf.fname[0] == '\0') {
               strcpy(header.mimeinf.fname,"FILE.EXT");
          }
          /* this is a kludge to make Netscape happy                       */
          /* when Netscape attaches HTML page as a document it has         */
          /* text/html Content-Type.  It may or may not be a file name.    */
          /* If file name is present it is an URL i.e."www.sun.com" or     */
          /* www.sun.com/index.html. We make sure our real file name always*/
          /* has .HTM extention so we can have a proper Content-Type if we */
          /* have to send that e-mail back to Internet (via POP3 for       */
          /* instance)                                                     */
          if (header.mimeinf.type == MT_TXHTML) {
               if ((ptr=strchr(header.mimeinf.fname,'.')) != NULL) {
                    *ptr='\0';
               }
               stlcat(header.mimeinf.fname,".HTM",
                      sizeof(header.mimeinf.fname));
          }
          if (header.mimattfp == NULL) {
               return(nmsgfile(FALSE));
          }
          else {
               return(MR_OK);
          }
     }
}

static INT
mst_body(VOID)                     /* decode body and output into msg or att*/
{
     INT (*dcodefun)(CHAR *inp,CHAR *outp);
     FILE *fp;
     INT i,outpsiz;

     BEG_PHASE("mst_body()",header.state);
     switch (header.mimeinf.cte) {
     case CTE_B64:
          dcodefun=base64dc;
          break;
     case CTE_QPRT:
          dcodefun=qprintdc;
          break;
     default:
          dcodefun=plaincpy;
     }
     if (header.cvt2att) {
          fp=header.mimattfp;
     }
     else {
          fp=header.mimfp;
     }
     for (i=0 ; i < NUMLINS ; i++) {
          if (fgets(vdatmp,SMTLSZ,header.fp) == NULL) {
               return(MR_EOF);
          }
          switch (isbndry(vdatmp)) {
          case BNDRY:
               return(MR_NEXT);
          case CLSBNDRY:
               return(MR_EOF);
          }
          outpsiz=dcodefun(vdatmp,vdatmp+SMTLSZ+1);
          if (outpsiz != 0) {
               if (fwrite(vdatmp+SMTLSZ+1,1,outpsiz,fp) != outpsiz) {
                    shologd(noChannel,"SMTP WRITE ERROR",
                            "mst_body(): Error writing message info");
                    return(MR_ERR);
               }
          }
     }
     return(MR_CYCLE);
}

static INT
mst_err(VOID)                      /* handles file I/O errors              */
{
     CHAR *ptr;

     BEG_PHASE("mst_err()",header.state);
     if (header.mimfp != NULL) {
          setmbk(smtdmb);
          ptr=getasc(SMTDMIER);
          rstmbk();
          if (fwrite(ptr,strlen(ptr),1,header.mimfp) != 1) {
               shologd(noChannel,"SMTP MIME WRITE ERROR",
                       "mst_err(): Error writing message body");
               return(MR_ERR);
          }
          return(MR_OK);
     }
     else {
          return(MR_ERR);
     }
}

static INT
mst_end(VOID)                      /* end of MIME decoding                 */
{
     INT retval=MR_OK;
     LONG endpos;
     static struct minfo minfo;

     BEG_PHASE("mst_end()",header.state);
     if (header.mimfp != NULL) {
          endpos=ftell(header.mimfp);
          fseek(header.mimfp,0L,SEEK_SET);
          if (fread(&minfo,1,sizeof(minfo),header.mimfp) != sizeof(minfo)) {
               shologd(noChannel,"SMTP MIME READ ERROR",
                       "mst_end(): Error reading message info");
               retval=MR_ERR;
          }
          else {
               fseek(header.mimfp,0L,SEEK_SET);
               minfo.endpos=endpos;
               if (header.attexist) {
                    minfo.flags|=HASATT;
                    stlcpy(minfo.relnam,header.attrelnm,GCMAXFNM);
               }
               if (fwrite(&minfo,1,sizeof(minfo),header.mimfp) != sizeof(minfo)) {
                    shologd(noChannel,"SMTP MIME WRITE ERROR",
                            "mst_end(): Error writing message info");
                    retval=MR_ERR;
               }
          }
          fclose(header.mimfp);
     }
     if (retval != MR_OK) {
          unlink(header.mimnm);
          unlink(getattnm(header.mimnm,tmppath));
     }
     return(retval);
}

static INT                         /*   returns MR_OK/MR_ERR               */
nmsgfile(                          /* creates new msg file for MIME decod  */
GBOOL primfile)
{
     USHORT tiebrk=0;
     INT hdrsiz,nread;
     GBOOL fsttim,skipline;
     LONG fpos;
     INT retval=MR_OK;
     FILE *fp;
     CHAR *ptr;
     static struct minfo minfo;

     BEG_PHASE("nmsgfile()",header.state);
     header.mimattfp=NULL;
     tmppath[0]=header.mimattnm[0]='\0';
     while (1) {
          if (tiebrk == 1000U) {
               catastro("SMTP MIME: Cannot create work file!");
          }
          stzcpy(tmppath,wrkdir,GCMAXPTH);
          stlcat(tmppath,spr("%x%x.wrk",now(),tiebrk++),GCMAXPTH);
          if (access(tmppath,0) == -1) {
               stlcpy(&tmppath[strlen(tmppath)-3],"rdy",GCMAXPTH);
               if (access(tmppath,0) == -1) {
                    break;
               }
          }
     }
     if ((fp=fopen(tmppath,FOPWRB)) == NULL) {
          shologd(noChannel,"SMTP MIME OPEN ERROR",
                  "nmsgfile() Mail file: \"%s\"",tmppath);
          return(MR_ERR);
     }
     fpos=ftell(header.fp);
     fseek(header.fp,0L,SEEK_SET);
     for (hdrsiz=(INT)header.minfo.hdrpos,fsttim=TRUE ; hdrsiz > 0 ;
                                                        hdrsiz-=nread) {
          nread=min(hdrsiz,vdasiz);
          if (fread(vdatmp,1,nread,header.fp) != nread) {
               shologd(noChannel,"SMTP MIME READ ERROR",
                       "nmsgfile() Error reading message info");
               retval=MR_ERR;
               break;
          }
          if (fsttim) {
               fsttim=FALSE;
               memcpy(&minfo,vdatmp,sizeof(struct minfo));
          }
          if (fwrite(vdatmp,1,nread,fp) != nread) {
               shologd(noChannel,"SMTP MIME WRITE ERROR",
                       "nmsgfile() Error writing message info");
               retval=MR_ERR;
               break;
          }
     }
     if (retval == MR_OK) {
          skipline=FALSE;
          while (fgets(vdatmp,SMTLSZ,header.fp) != NULL) {
               if (sameto("MIME-Version:",vdatmp)
                || sameto("Content-Type:",vdatmp)
                || sameto("Content-Transfer-Encoding:",vdatmp)
                || sameto("Content-Disposition:",vdatmp)) {
                    skipline=TRUE;
               }
               else if (skipline && (vdatmp[0] == ' ' || vdatmp[0] == '\t')) {
               /* this is a continuation of the line we just skiped        */
                    skipline=TRUE;
               }
               else {
                    skipline=FALSE;
                    if (fputs(vdatmp,fp) < 0) {
                         shologd(noChannel,"SMTP MIME WRITE ERROR",
                                 "nmsgfile() Error writing message header");
                         retval=MR_ERR;
                         break;
                    }
               }
               if (vdatmp[0] == '\r') {
                    break;
               }
          }
     }
     if (retval == MR_OK) {
          minfo.rmtrcpt=0;
          minfo.bdypos=ftell(fp);
          if (!primfile) {
               minfo.flags|=HASATT;
               stlcpy(minfo.relnam,header.mimeinf.fname,GCMAXFNM);
               setmbk(smtdmb);
               ptr=getasc(SMTDATTN);
               rstmbk();
               if (fwrite(ptr,strlen(ptr),1,fp) != 1) {
                    shologd(noChannel,"SMTP MIME WRITE ERROR",
                            "nmsgfile() Error writing message body");
                    retval=MR_ERR;
               }
          }
          minfo.endpos=ftell(fp);
          fseek(fp,0L,SEEK_SET);
          if (fwrite(&minfo,1,sizeof(minfo),fp) != sizeof(minfo)) {
               shologd(noChannel,"SMTP MIME WRITE ERROR",
                       "nmsgfile() Error writing message info");
               retval=MR_ERR;
          }
          fseek(fp,0L,SEEK_END);
     }
     if (retval == MR_OK) {
          retval=nattfile(tmppath);
     }
     if (retval == MR_OK) {
          if (primfile) {
               strcpy(header.mimnm,tmppath);
               header.mimfp=fp;
          }
          else {
               fclose(fp);
          }
     }
     else {
          if (fp != NULL) {
               fclose(fp);
          }
          unlink(tmppath);
          if (header.mimattfp != NULL) {
               fclose(header.mimattfp);
          }
          header.mimattfp=NULL;
          unlink(header.mimattnm);
     }
     fseek(header.fp,fpos,SEEK_SET);
     return(retval);
}

static INT                         /*   returns MR_OK/MR_ERR               */
nattfile(                          /* creates new att file for MIME decod  */
CHAR *fname)                       /*   msg file name                      */
{
     BEG_PHASE("nattfile()",header.state);
     getattnm(fname,header.mimattnm);
     if ((header.mimattfp=fopen(header.mimattnm,FOPWRB)) == NULL) {
          shologd(noChannel,"SMTP MIME OPEN ERROR",
                  "nattfile() Attachment file: \"%s\"",header.mimattnm);
          return(MR_ERR);
     }
     else {
          return(MR_OK);
     }
}

static INT                         /*   returns number of output bytes     */
plaincpy(                          /* copy input to output                 */
CHAR *inp,
CHAR *outp)
{
     BEG_PHASE("plaincpy()",header.state);
     strcpy(outp,inp);
     return(strlen(inp));
}

static INT                         /*   returns number of output bytes     */
qprintdc(                          /* decode quoted printable string       */
CHAR *inp,
CHAR *outp)
{
     INT len,i,outpsiz,sch;
     CHAR ch,inputch;

     BEG_PHASE("qprintdc()",header.state);
     depad(inp);
     len=strlen(inp);
     for (i=outpsiz=0,sch=0 ; i < len ; i++) {
          if (((inputch=inp[i]) == '=') && (sch == 0)) {
               sch++;
          }
          else {
               if (sch == 1) {
                    ch=hextab[inputch]<<4;
                    sch++;
               }
               else if (sch == 2) {
                    ch|=hextab[inputch];
                    outp[outpsiz++]=ch;
                    sch=0;
               }
               else {
                    outp[outpsiz++]=inputch;
               }
          }
     }
     if (sch == 0) {
          outp[outpsiz++]='\r';
          outp[outpsiz++]='\n';
     }
     return(outpsiz);
}

static VOID
mimdeflt(VOID)                     /* set default values in MIME header    */
{
     header.mimeinf.type=MT_TEXT;
     header.mimeinf.cte=CTE_7BIT;
     header.mimeinf.fname[0]='\0';
     stlcpy(header.mimeinf.typedsc,"<unknown>",CHSETSIZ);
     stlcpy(header.mimeinf.subtype,"<unknown>",CHSETSIZ);
     stlcpy(header.mimeinf.charset,"<unknown>",CHSETSIZ);
}

static CHAR *
getattnm(                          /* creates att. file name from msg file */
CHAR *msgnm,                       /*   message file name                  */
CHAR *attnm)                       /*   attachment file name               */
{
     CHAR *ptr;

     BEG_PHASE("getattnm()",header.state);
     stlcpy(attnm,msgnm,GCMAXPTH);
     if ((ptr=strchr(attnm,'.')) != NULL) {
          stlcpy(ptr,".att",GCMAXPTH);
     }
     else {
          stlcat(attnm,".att",GCMAXPTH);
     }
     return(attnm);
}
