/***************************************************************************
 *                                                                         *
 *   POP3.C                                                                *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.  All Rights Reserved.       *
 *                                                                         *
 *   POP3 server for retrieving e-mail via the Internet, per RFC 1725.     *
 *                                                                         *
 *                               7/13/95 - Bert Love,                      *
 *                                         Charles Dunn &                  *
 *                                         Ilya Minkin                     *
 *                                                                         *
 ***************************************************************************/

#include <sys/stat.h>
#include "gcomm.h"
#include "majorbbs.h"
#include "gme.h"
#include "remote.h"
#include "tcpip.h"
#include "dns.h"
#include "alias.h"
#include "pop3.h"
#include "smtpexp.h"
#include "galpop3.h"
#include "md5util.h"
#include "ftg.h"
#include "mimectyp.h"

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

#define FILREV "$Revision: 26 $"

#define MAXBYT    4096             /* maximum size of receive buffer       */
#define MAXMSG    768              /* maximum num of msgs in a maildrop    */
#define AUTHSZ    64               /* max size of APOP authentication str  */

                                   /* POP3 states                          */
#define AUTHUSR     0              /*   AUTHORIZATION state 1 (USER)       */
#define AUTHPSW     1              /*   AUTHORIZATION state 2 (PASS)       */
#define BLDDROP     2              /*   building maildrop state            */
#define MIMECODE    3              /*   MIME encoding file attachment      */
#define TRANS       4              /*   TRANSACTION state                  */
#define SENDLIST    5              /*   responding to LIST command         */
#define SENDUIDL    6              /*   responding to LIST command         */
#define RETRMSG     7              /*   responding to RETR command         */
#define MARKMSG     8              /*   marking message as been read       */
#define UPDATE      9              /*   UPDATE state                       */
#define PRGFIN      10             /*   finish up                          */

                                   /* file attachment state codes          */
#define START       0              /*   beginning attachment checks        */
#define WRTHDR      1              /*   write header in message body       */
#define CHK4BIN     2              /*   check for bin data in atchmnt      */
#define WRTBAD      3              /*   bad attachment, add notice to msg  */
#define CPYATT      4              /*   copying atchmnt in ascii mode      */
#define MIMECD      5              /*   MIME encoding attachment           */
#define MSGRDY      6              /*   message ready to be sent           */

                                   /* mark message state codes             */
#define MKGETMSG    0              /*   get next message                   */
#define MKWAIT      1              /*   wait for available text buffer     */
#define MKTAGMSG    2              /*   tag message as been read           */

                                   /* blddrop() return codes               */
#define BD_OK       0              /* next message retreived               */
#define BD_LAST     1              /* last message retreived               */
#define BD_CYCLE    2              /* GME requires cycling                 */
#define BD_MIME     3              /* go to MIME encoding                  */

static VOID pop3incall(INT gotchn);
static GBOOL pop3inp(VOID);
static VOID pop3lin(CHAR *linput);
static VOID pop3sts(VOID);
static VOID pop3fin(VOID);
static VOID clndir(VOID);
static VOID enatmo(VOID);
static VOID distmo(VOID);
static VOID prcinp(VOID);
static VOID biglin(VOID);
static VOID pop3prf(VOID);
static VOID popuser(CHAR *linput);
static VOID popapop(CHAR *linput);
static VOID poppass(CHAR *linput);
static VOID poprpop(CHAR *linput);
static VOID popquit(VOID);
static VOID popstat(VOID);
static VOID poplist(CHAR *linput);
static VOID popretr(CHAR *linput);
static VOID popdele(CHAR *linput);
static VOID popnoop(VOID);
static VOID poprset(VOID);
static VOID poptop(CHAR *linput);
static VOID popuidl(CHAR *linput);
static VOID popxtnd(CHAR *linput);
static CHAR *getarg(CHAR *cmdstr);
static INT  blddrop(VOID);
static GBOOL prpdrop(VOID);
static CHAR *fixdot(CHAR *buffer,INT bufsiz);
static VOID endmsg(VOID);
static VOID drpsiz(INT *nomsgs,LONG *size);
static VOID sendlist(VOID);
static VOID senduidl(VOID);
static GBOOL retrmsg(VOID);
static VOID clsfil(VOID);
static VOID prgmsg(VOID);
static VOID prgfin(INT msgno);
static VOID pop3mim(VOID);
static VOID poperr(CHAR *linput);
static GBOOL valuact(CHAR *userid,CHAR *passwd,GBOOL isapop);
static LONG getmno(INT idx);
static VOID genastr(VOID);
static INT msg2drop(struct message *msg,CHAR *txt);
static VOID rebufmsg(INT subjid,INT msgid);
static GBOOL dbeatmsg(VOID);
static VOID popcharg(VOID);
static GBOOL markmsg(VOID);

static INT pop3stt;                /* POP3 server module state number      */

struct module pop3module={         /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     pop3inp,                      /*    input routine if selected         */
     pop3sts,                      /*    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            */
     pop3fin                       /*    finish-up (sys shutdown) routine  */
};

static HMCVFILE pop3mb;            /* .MCV file pointer for GALPOP3.MSG    */

                                   /* options from GALPOP3.MCV             */
static INT pop3chg;                /*   charge per message read            */
static INT pop3kch;                /*   surcharg per k-byte read           */
static GBOOL pop3onl;              /*   POP3 server online?                */
static GBOOL pop3rej;              /*   reject or ignore calls (pop3onl=0) */
static INT pop3prt;                /*   POP3 port number                   */
static INT maxpop3;                /*   maximum POP3 requests at a time    */
static GBOOL audpop3;              /*   audit successful POP3 requests?    */
static GBOOL audpop3e;             /*   audit invalid POP3 requests?       */
static UINT pop3tmo;               /*   timeout in seconds                 */
static LONG mindsk;                /*   minimum amount of disk space req'd */
static CHAR wrkdir[GCMAXPTH];      /*   POP3 server work directory         */
static UINT drptmo;                /*   drop file build timeout value      */
static CHAR *pop3key;              /*   key required to use POP3           */
GBOOL pop3log;                     /*   is receive log currently active?   */
CHAR *pop3lnam;                    /*   receive log file name              */
GBOOL pop3newm;                    /*   retreive new mail only             */

struct maildrp {                   /* information for a maildrop           */
     LONG filpos;                  /*   starting file position of msg      */
     LONG msgid;                   /*   GME message ID                     */
     LONG msgsiz;                  /*   current message size               */
     CHAR flags;                   /*   message flags                      */
};
                                   /* maildrop structure flag values       */
#define MSGDEL    0x01             /*   message flagged for deletion       */
#define BINATT    0x02             /*   file attachment is a binary file   */

                                   /* TOP command substates                */
#define TOPNOT    0                /*   standard RETR command              */
#define TOPHDR    1                /*   sending message header             */
#define TOPBODY   2                /*   sending message body               */


struct pop3usr {                   /* POP3 VDA info structure              */
     FILE *fp;                     /*   maildrop file pointer              */
     CHAR filnam[GCMAXPTH];        /*   maildrop file name                 */
     USHORT sttime;                /*   start timer for timeout purposes   */
     UINT drptmr;                  /*   drop file build timer              */
     INT rcvcnt;                   /*   number of bytes in receive buffer  */
     INT nummsg;                   /*   number of messages in maildrop     */
     INT mimstt;                   /*   MIME encode state number           */
     INT markstt;                  /*   mark message state number          */
     FILE *mimfp;                  /*   MIME attachment file pointer       */
     CHAR mimfnm[GCMAXPTH];        /*   MIME attachment file name          */
     CHAR relnam[GCMAXFNM];        /*   user-specified attachment name     */
     CHAR rcvbuf[MAXBYT];          /*   raw receive buffer                 */
     CHAR wrkbuf[GMEWRKSZ];        /*   work buffer for GME                */
     CHAR userid[UIDSIZ];          /*   valid User-ID for GME calls        */
     CHAR tcpid[UIDSIZ];           /*   TCP/IP related User-ID             */
     CHAR authstr[AUTHSZ];         /*   APOP authentication string         */
     CHAR mimid[MIMIDSZ];          /*   MIME boundary                      */
     LONG counter;                 /*   general purpose counter            */
     INT clskt;                    /*   client socket                      */
     INT topstt;                   /*   substate for TOP command           */
     INT topcntr;                  /*   line counter for TOP command       */
     LONG emllim;                  /*   last message read                  */
     GBOOL hascrd;                 /*   user has credits (so far)          */
     INT curmsg;                   /*   current message been retreived     */
     struct message taggedMsg;     /*   tagged message                     */
     struct maildrp maildrp[MAXMSG]; /* maildrop message file data         */
};

struct tempbuf {
     CHAR *buf;
     INT locked;
} tempbuf;

#define pop3ptr ((struct pop3usr *)vdaptr)

VOID EXPORT
init__galpop3(VOID)                /* POP3 server initialization routine   */
{
     pop3mb=opnmsg("galpop3.mcv");
     pop3onl=ynopt(POP3ONL);
     pop3rej=ynopt(POP3REJ);
     if (pop3onl || pop3rej) {
          init__tcpip();
          init__galme();
          init__galsmtp();
          setmbk(pop3mb);
          pop3chg=numopt(POP3CHG,-32767,32767);
          pop3kch=numopt(POP3KCH,-32767,32767);
          pop3prt=numopt(POP3PRT,1,32767);
          maxpop3=numopt(MAXPOP3,1,250);
          audpop3=ynopt(AUDPOP3);
          audpop3e=(ynopt(AUDPOP3E) || audpop3);
          pop3tmo=(UINT)numopt(POP3TMO,600,1800);
          mindsk=(LONG)numopt(MINDSK,1,100)*1024L;
          stlcpy(wrkdir,getmsg(WRKDIR),GCMAXPTH);
          drptmo=(UINT)numopt(DRPTMO,0,1800);
          pop3log=ynopt(POP3LOG);
          pop3lnam=stgopt(POP3LNAM);
          pop3key=stgopt(POP3KEY);
          pop3newm=ynopt(POP3NEWM);
          if (pop3log) {
               shocst("POP3 RECEIVE LOG ENABLED",
                      "All POP3 transactions will be logged");
               if (maxpop3 > 1) {
                    maxpop3=1;
                    shocst("POP3 CHANNELS LIMITED",
                           "POP3 channels limited to one due to logging");
               }
          }
          dclvda(sizeof(struct pop3usr));
          dclvda(TXTLEN);
          tempbuf.buf=alczer(TXTLEN);
          tempbuf.locked=0;
          if (fmdir(wrkdir)) {
               fixpth(wrkdir);
               regtcpsvr(POP3NAM,pop3prt,POP3BACKLOG,pop3incall);
               stzcpy(pop3module.descrp,gmdnam("galpop3.mdf"),MNMSIZ);
               pop3stt=register_module(&pop3module);
               clndir();
          }
          else {
               shocst("POP3 SERVER NOT INTIALIZED",
                      "Unable to create work directory");
               clsmsg(pop3mb);
          }
     }
     else {
          clsmsg(pop3mb);
     }
}

VOID EXPORT
initwc__galpop3(VOID)
{
     init__galpop3();
}

static VOID
pop3incall(                        /* begin incoming POP3 session          */
INT gotchn)                        /* 1=chan (curusr) assigned, 0=not avail*/
{                                  /* implicit:  clskt=socket to client    */
     setmbk(pop3mb);
     clrprf();
     if (!gotchn) {
          sprintf(prfbuf,xlttxv(stpans(getasc(kilipg || errcod != 1 ?
                                SRVSHUT : SRVFULL)),mxmssz),numcdi("TCP/IP"));
          send(clskt,prfbuf,strlen(prfbuf),0);
     }
     else {
          setmem(pop3ptr,sizeof(struct pop3usr),0);
          usrptr->usrcls=BBSPRV;
          usrptr->state=pop3stt;
          usrptr->substt=AUTHUSR;
          usrptr->flags|=NOGLOB+NOINJO;
          btuech(usrnum,0);
          btutrg(usrnum,outbsz-1);
          stansi();
          if (!pop3onl) {
               byetcp(POP3OFF);
               rejinc(POP3NAM,"POP3 server disabled");
          }
          else if (numonl(pop3stt) > maxpop3) {
               byetcp(POP3MANY,maxpop3);
               rejinc(POP3NAM,spr("%d POP3 server connections",maxpop3));
          }
          else if (dskfre(".") < mindsk) {
               byetcp(POP3TAL);
               poplog("POP3 NOT ENOUGH DISK SPACE",
                      "Not enough disk space for POP3 session");
          }
          else {
               shochl(spr("POP3 contact from %s",
                          inet_ntoa(tcpipinf[usrnum].inaddr)),'P',0x1F);
               logpop3(spr("POP3 contact from %s",
                       inet_ntoa(tcpipinf[usrnum].inaddr)),SHOWLOG);
               pop3ptr->clskt=clskt;
               sktnfy(TNFRECV,clskt,tcpinc,&tcpipinf[usrnum],usrnum);
               sprintf(pop3ptr->tcpid,"(%s)",
                       inet_ntoa(tcpipinf[usrnum].inaddr));
               stlcpy(usaptr->userid,pop3ptr->tcpid,UIDSIZ);
               btuinj(usrnum,CYCLE);
               btubsz(usrnum,outbsz/2,outbsz/2);
               genastr();
               prfmsg(POP3RDY,pop3ptr->authstr);
               pop3prf();
               enatmo();
          }
     }
     rstmbk();
}

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

static VOID
pop3sts(VOID)                      /* status handling                      */
{
     INT tmpnum,retval;
     LONG tmpsiz,msgno;

     setmbk(pop3mb);
     clrprf();
     if (status == RING) {
          if (usrptr->substt == UPDATE || usrptr->substt == PRGFIN) {
               sktcnc(TNFRECV,pop3ptr->clskt);
               btuinj(usrnum,CYCLE);
          }
          else {
               prgfin(-1);
          }
          return;
     }
     if (status != CYCLE) {
          return;
     }
     if (pop3tmo > 0 && pop3ptr->sttime != 0
      && btuTicker()-pop3ptr->sttime > pop3tmo) {
          prgfin(POP3TMOT);
          if (audpop3e) {
               poplog("POP3 SERVER TIMEOUT",
                      "Client connection timed out with %s",
                      pop3ptr->tcpid);
          }
     }
     else {
          if (usrptr->substt == BLDDROP) {
               distmo();
               if ((retval=blddrop()) == BD_OK) {
                    retval=blddrop();
               }
               if (retval == BD_LAST) {
                    drpsiz(&tmpnum,&tmpsiz);
                    prfmsg(POP3MDR,tmpnum,pop3ptr->userid,l2as(tmpsiz));
                    fclose(pop3ptr->fp);
                    pop3ptr->fp=fopen(pop3ptr->filnam,FOPRB);
                    usrptr->substt=TRANS;
                    pop3ptr->rcvbuf[0]='\0';
                    pop3ptr->rcvcnt=0U;
                    pop3prf();
                    enatmo();
               }
               else if (retval == BD_MIME) {
                    usrptr->substt=MIMECODE;
                    pop3ptr->mimstt=START;
               }
          }
          else if (usrptr->substt == MIMECODE) {
               pop3mim();
          }
          else if (usrptr->substt == SENDLIST) {
               sendlist();
          }
          else if (usrptr->substt == SENDUIDL) {
               senduidl();
          }
          else if (usrptr->substt == RETRMSG) {
               distmo();
               if (!retrmsg()) {
                    if (pop3ptr->topstt == TOPNOT && pop3ptr->hascrd) {
                         popcharg();
                    }
                    if (pop3ptr->topstt == TOPNOT && pop3ptr->hascrd
                     && (msgno=getmno(pop3ptr->curmsg)) != 0L) {
                         msgctx(pop3ptr->wrkbuf,msgno);
                         usrptr->substt=MARKMSG;
                         pop3ptr->markstt=MKGETMSG;
                    }
                    else {
                         usrptr->substt=TRANS;
                         enatmo();
                    }
               }
          }
          else if (usrptr->substt == MARKMSG) {
               if (!markmsg()) {
                    usrptr->substt=TRANS;
                    enatmo();
               }
          }
          else if (usrptr->substt == UPDATE) {
               prgmsg();
          }
          else if (usrptr->substt == PRGFIN) {
               prgfin(POP3QUIT);
          }
          else {
               prcinp();
          }
          btuinj(usrnum,CYCLE);
     }
}

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

     while ((nroom=MAXBYT-pop3ptr->rcvcnt-1) > 0
         && (nread=btuica(usrnum,pop3ptr->rcvbuf+pop3ptr->rcvcnt,nroom)) > 0) {
          pop3ptr->rcvcnt=memstp(pop3ptr->rcvbuf,pop3ptr->rcvcnt+nread,'\0');
          pop3ptr->rcvbuf[pop3ptr->rcvcnt]='\0';
          pop3ptr->rcvcnt=strlen(strstp(pop3ptr->rcvbuf,'\r'));
          while ((ptr=strchr(pop3ptr->rcvbuf,'\n')) != NULL) {
               *ptr='\0';
               pop3lin(pop3ptr->rcvbuf);
               strcpy(pop3ptr->rcvbuf,ptr+1);
               pop3ptr->rcvcnt=strlen(pop3ptr->rcvbuf);
          }
          if (pop3ptr->rcvcnt >= MAXBYT-1) {
               biglin();
               pop3ptr->rcvcnt=0U;
          }
     }
}

static VOID
biglin(VOID)                       /* process a big line (fills rcvbuf)    */
{
     setmbk(pop3mb);
     prfmsg(POP3LTL);
     pop3prf();
     rstmbk();
}

static VOID
pop3lin(                           /* POP3 server input handler            */
CHAR *linput)
{
     setmbk(pop3mb);
     enatmo();
     clrprf();
     logpop3(linput,INCMING);
     switch (usrptr->substt) {
     case AUTHUSR:
          if (sameto("USER",linput)) {
               popuser(linput);
          }
          else if (sameto("APOP",linput)) {
               popapop(linput);
          }
          else if (sameto("QUIT",linput)) {
               popquit();
          }
          else {
               poperr(linput);
          }
          break;
     case AUTHPSW:
          if (sameto("PASS",linput)) {
               poppass(linput);
          }
          else if (sameto("RPOP",linput)) {
               poprpop(linput);
          }
          else if (sameto("QUIT",linput)) {
               popquit();
          }
          else {
               poperr(linput);
          }
          break;
     case TRANS:
          if (sameto("STAT",linput)) {
               popstat();
          }
          else if (sameto("LIST",linput)) {
               poplist(linput);
          }
          else if (sameto("RETR",linput)) {
               popretr(linput);
          }
          else if (sameto("DELE",linput)) {
               popdele(linput);
          }
          else if (sameto("NOOP",linput)) {
               popnoop();
          }
          else if (sameto("RSET",linput)) {
               poprset();
          }
          else if (sameto("TOP",linput)) {
               poptop(linput);
          }
          else if (sameto("UIDL",linput)) {
               popuidl(linput);
          }
          else if (sameto("XTND",linput)) {
               popxtnd(linput);
          }
          else if (sameto("QUIT",linput)) {
               popquit();
          }
          else {
               poperr(linput);
          }
          break;
     }
     rstmbk();
     pop3prf();
}

static VOID
popuser(                           /* process USER command                 */
CHAR *linput)
{
     CHAR *argptr;

     if ((argptr=getarg(linput)) != NULL) {
          stlcpy(pop3ptr->userid,argptr,UIDSIZ);
          usrptr->substt=AUTHPSW;
          prfmsg(POP3PASS);
     }
     else {
          prfmsg(POP3NARG);
     }
}

static VOID
popapop(                           /* process APOP command                 */
CHAR *linput)
{
     CHAR *secptr,*argptr;

     if ((argptr=getarg(linput)) != NULL
      && (secptr=strrchr(argptr,' ')) != NULL) {
          *secptr='\0';
          secptr++;
          strcpy(pop3ptr->userid,argptr);
          if (valuact(pop3ptr->userid,secptr,TRUE)) {
               if ((pop3chg > 0 || pop3kch > 0)
                    && !ntstcrd(pop3ptr->userid,1L,FALSE)) {
                    if (audpop3e) {
                         poplog("POP3 APOP FAILURE",
                                   "User %s does not have credits",
                                   (argptr != NULL) ? argptr : "");
                    }
                    usrptr->substt=AUTHUSR;
                    prfmsg(POP3CRD);
                    return;
               }
               if (audpop3) {
                    poplog("POP3 APOP OK","User %s accepted",
                              pop3ptr->userid);
               }
               shochl(pop3ptr->userid,'P',0x1F);
               pop3ptr->drptmr=btuTicker();
               if (prpdrop()) {
                    usrptr->substt=BLDDROP;
                    pop3ptr->hascrd=TRUE;
               }
               else {
                    usrptr->substt=AUTHUSR;
               }
               return;
          }
     }
     if (audpop3e) {
          poplog("POP3 APOP FAILURE","Unauthorized user: %s",
                                     (argptr != NULL) ? argptr : "");
     }
     usrptr->substt=AUTHUSR;
     prfmsg(POP3DNA);
}

static VOID
poppass(                           /* process PASS command                 */
CHAR *linput)
{
     CHAR *argptr;

     if ((argptr=getarg(linput)) != NULL) {
          if (valuact(pop3ptr->userid,argptr,FALSE)) {
               if ((pop3chg > 0 || pop3kch > 0)
                && !ntstcrd(pop3ptr->userid,1L,FALSE)) {
                    if (audpop3e) {
                         poplog("POP3 PASS FAILURE",
                                "User %s does not have credits",
                                pop3ptr->userid);
                    }
                    usrptr->substt=AUTHUSR;
                    prfmsg(POP3CRD);
                    return;
               }
               if (audpop3) {
                    poplog("POP3 PASS OK","User %s accepted",pop3ptr->userid);
               }
               shochl(pop3ptr->userid,'P',0x1F);
               pop3ptr->drptmr=btuTicker();
               if (prpdrop()) {
                    usrptr->substt=BLDDROP;
                    pop3ptr->hascrd=TRUE;
               }
               else {
                    usrptr->substt=AUTHUSR;
               }
               return;
          }
          else {
               prfmsg(POP3BPW);
               usrptr->substt=AUTHUSR;
          }
     }
     else {
          prfmsg(POP3NARG);
     }
     if (audpop3e) {
          poplog("POP3 PASS FAILURE","Unauthorized user: %s",pop3ptr->userid);
     }

}

static VOID
poprpop(                           /* process RPOP command                 */
CHAR *linput)
{
     (VOID)linput;
     prfmsg(POP3NIMP);
}

static VOID
popquit(VOID)                      /* process QUIT command                 */
{
     if (usrptr->substt == TRANS) {
          usrptr->substt=UPDATE;
          pop3ptr->counter=0L;
     }
     else {
          prgfin(POP3QUIT);
     }
}

static VOID
popstat(VOID)                      /* process STAT command                 */
{
     INT nomsgs;
     LONG size;

     drpsiz(&nomsgs,&size);
     prfmsg(POP3STAT,nomsgs,l2as(size));
}

static VOID
poplist(                           /* process LIST command                 */
CHAR *linput)
{
     CHAR *argptr;
     INT msgno;

     if ((argptr=getarg(linput)) != NULL) {
          if ((msgno=atoi(argptr)) <= 0
           || (msgno > pop3ptr->nummsg)
           || (pop3ptr->maildrp[msgno-1].flags&MSGDEL)) {
               prfmsg(POP3NSM);
          }
          else {
               prfmsg(POP3LST1,msgno,l2as(pop3ptr->maildrp[msgno-1].msgsiz));
          }
     }
     else {
          popstat();
          usrptr->substt=SENDLIST;
          pop3ptr->counter=0L;
     }
}

static VOID
popretr(                           /* process RETR command                 */
CHAR *linput)
{
     CHAR *argptr;
     INT msgno;

     if ((argptr=getarg(linput)) != NULL) {
          if ((msgno=atoi(argptr)) <= 0
           || (msgno > pop3ptr->nummsg)
           || (pop3ptr->maildrp[msgno-1].flags&MSGDEL)) {
               prfmsg(POP3NSM);
          }
          else {
               prfmsg(POP3RETR,l2as(pop3ptr->maildrp[msgno-1].msgsiz));
               pop3ptr->counter=pop3ptr->maildrp[msgno-1].msgsiz;
               fseek(pop3ptr->fp,pop3ptr->maildrp[msgno-1].filpos,SEEK_SET);
               pop3ptr->topstt=TOPNOT;
               pop3ptr->curmsg=msgno-1;
               usrptr->substt=RETRMSG;
               logpop3(getmsg(POP3SMB),OUTGING);
          }
     }
     else {
          prfmsg(POP3NSM);
     }
}

static VOID
popdele(                           /* process DELE command                 */
CHAR *linput)
{
     CHAR *argptr;
     INT msgno;

     if ((argptr=getarg(linput)) != NULL) {
          if ((msgno=atoi(argptr)) <= 0
           || (msgno > pop3ptr->nummsg)
           || (pop3ptr->maildrp[msgno-1].flags&MSGDEL)) {
               prfmsg(POP3NSM);
          }
          else {
               pop3ptr->maildrp[msgno-1].flags|=MSGDEL;
               prfmsg(POP3DEL,msgno);
          }
     }
     else {
          prfmsg(POP3NSM);
     }
}

static VOID
popnoop(VOID)                      /* process NOOP command                 */
{
     prfmsg(POP3NOOP);
}

static VOID
poprset(VOID)                      /* process RSET command                 */
{
     INT i;

     for (i=0 ; i < pop3ptr->nummsg ; i++) {
          pop3ptr->maildrp[i].flags&=~MSGDEL;
     }
     prfmsg(POP3RSET);
}

static VOID
poptop(                            /* process TOP command                  */
CHAR *linput)
{
     CHAR *argptr,*secptr;
     INT msgno;

     if ((argptr=getarg(linput)) != NULL) {
          if ((secptr=getarg(argptr)) != NULL) {
               *(secptr-1)='\0';
               pop3ptr->topcntr=atoi(secptr);
               if (pop3ptr->topcntr < 0) {
                    pop3ptr->topcntr=0;
               }
          }
          else {
               pop3ptr->topcntr=0;
          }
          if ((msgno=atoi(argptr)) <= 0
           || (msgno > pop3ptr->nummsg)
           || (pop3ptr->maildrp[msgno-1].flags&MSGDEL)) {
               prfmsg(POP3NSM);
          }
          else {
               prfmsg(POP3RETR,l2as(pop3ptr->maildrp[msgno-1].msgsiz));
               pop3ptr->counter=pop3ptr->maildrp[msgno-1].msgsiz;
               fseek(pop3ptr->fp,pop3ptr->maildrp[msgno-1].filpos,SEEK_SET);
               pop3ptr->topstt=TOPHDR;
               usrptr->substt=RETRMSG;
               pop3ptr->curmsg=msgno-1;
               logpop3(getmsg(POP3SMB),OUTGING);
          }
     }
     else {
          prfmsg(POP3NSM);
     }
}

static VOID
popuidl(                           /* process UIDL command                 */
CHAR *linput)
{
     CHAR *argptr;
     INT msgno;
     LONG msgid;

     if ((argptr=getarg(linput)) != NULL) {
          if ((msgno=atoi(argptr)) <= 0
           || (msgno > pop3ptr->nummsg)
           || (pop3ptr->maildrp[msgno-1].flags&MSGDEL)
           || ((msgid=getmno(msgno-1)) == 0)) {
               prfmsg(POP3NSM);
          }
          else {
               prfmsg(POP3UID1,msgno,l2as(msgid));
          }
     }
     else {
          prfmsg(POP3UID2);
          usrptr->substt=SENDUIDL;
          pop3ptr->counter=0L;
     }
}

static VOID
popxtnd(                           /* process XTND command                 */
CHAR *linput)
{
     (VOID)linput;
     prfmsg(POP3NIMP);
}

static VOID
poperr(                            /* process bad command                  */
CHAR *linput)
{
     prfmsg(POP3ERR,linput);
}

static CHAR *
getarg(                            /* return ptr to POP3 command argument  */
CHAR *cmdstr)                      /*   POP3 command string                */
{
     CHAR *ptr;

     if ((ptr=strchr(cmdstr,' ')) != NULL) {
          ptr++;
          while (isspace(*ptr)) {
               ptr++;
          }
          return(ptr);
     }
     return(NULL);
}

static VOID
pop3fin(VOID)                      /* finish-up (system shutdown) routine  */
{
     clsmsg(pop3mb);
}

static VOID
clndir(VOID)                       /* removes all files from work dir      */
{
     INT clncnt;
     struct ffblk fb;
     char clnspec[GCMAXPTH];

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

static GBOOL
valuact(                           /* POP3 validate user acct on this sys  */
CHAR *userid,
CHAR *passwd,
GBOOL isapop)
{
     CHAR *tmpuid,*digest;
     GBOOL retval=FALSE;
     struct usracc usr,*uaccptr=NULL;

     if ((tmpuid=(*usrofals)(userid)) == NULL) {
          return(FALSE);
     }
     if (onsysn(tmpuid,TRUE)) {
          uaccptr=othuap;
     }
     else {
          dfaSetBlk(accbb);
          if (dfaAcqEQ(&usr,tmpuid,0)) {
               uaccptr=&usr;
          }
          dfaRstBlk();
     }
     if (uaccptr != NULL && !(uaccptr->flags&(SUSPEN|DELTAG))) {
          strcpy(userid,uaccptr->userid);
          if (isapop) {
               digest=strmd5(spr("%s%s",pop3ptr->authstr,uaccptr->psword));
               retval=(sameas(passwd,digest) && uhskey(userid,pop3key));
          }
          else {
               retval=(sameas(uaccptr->psword,passwd) && uhskey(userid,pop3key));
          }
          pop3ptr->emllim=(pop3newm) ? uaccptr->emllim : FIRSTM;
     }
     return(retval);
}

static VOID
enatmo(VOID)                       /* enable channel timeout               */
{
     if ((pop3ptr->sttime=btuTicker()) == 0) {
          pop3ptr->sttime++;
     }
}

static VOID
pop3prf(VOID)                      /* our own outprf() function            */
{
     if (prfbuf[0] != '\0') {
          stpans(prfbuf);
          logpop3(prfbuf,OUTGING);
          outprf(usrnum);
          clrprf();
     }
}

static INT                         /*   return next msg/last msg/cycle     */
blddrop(VOID)                      /* build maildrop file                  */
{
     INT retval;
     CHAR *ptr;
     struct message msg;

     if (pop3ptr->nummsg == MAXMSG-1
      || (drptmo > 0U && (btuTicker()-pop3ptr->drptmr) >= drptmo)) {
          rebufmsg(POP3LES,POP3LEB);
          retval=BD_LAST;
     }
     else {
          switch (nextmsg(pop3ptr->wrkbuf,&msg,vdatmp)) {
          case GMEOK:
               retval=msg2drop(&msg,vdatmp);
               break;
          case GMEAGAIN:
               retval=BD_CYCLE;
               break;
          case GMECRD:
          case GMEACC:
               clrprf();
               prfmsg(POP3ICB,now(),hstdom,
                              (*alsofusr)("Sysop"),smtihst ? hstdom : domain,
                              pop3ptr->userid,
                              cvtdate(now(),today()));
               ptr=strrpl(stpans(prfbuf),'\r','\n');
               fwrite(ptr,strlen(ptr),1,pop3ptr->fp);
               clrprf();
               endmsg();
          default:
               retval=BD_LAST;
               break;
          }
     }
     return(retval);
}

static INT                         /*   returns done/MIME encoding         */
msg2drop(                          /* add next msg to maildrop file        */
struct message *msg,
CHAR *txt)
{
     const CHAR *hdr;
     CHAR tmpbuf[MAXADR];

     pop3ptr->maildrp[pop3ptr->nummsg].filpos=ftell(pop3ptr->fp);
     pop3ptr->maildrp[pop3ptr->nummsg].msgid=msg->msgid;
     clrprf();
     hdr=gmeGetAppInfo();
     if (!NULSTR(hdr)
      && *cfgGetStr(SMTPSECTHDR,NULL,"",prfbuf,outbsz,hdr) != '\0') {
          strmove(prfbuf,skptwht(prfbuf));
          strrpl(prfbuf,'\r','\n');
          stlcat(prfbuf,"\n",outbsz);
     }
     else {
          sprintf(&prfbuf[strlen(prfbuf)],"Message-ID: <%ld@%s>\n",msg->msgid,hstdom);
          stlcpy(tmpbuf,msg->from,MAXADR);
          if (xltGME2Int(tmpbuf)) {
               sprintf(&prfbuf[strlen(prfbuf)],"From: %s\n",tmpbuf);
          }
          else {
               sprintf(&prfbuf[strlen(prfbuf)],"From: %s@%s\n",
                       (*alsofusr)(msg->from),smtpHost());
          }
          stlcpy(tmpbuf,msg->to,MAXADR);
          xltGME2Int(tmpbuf);
          sprintf(&prfbuf[strlen(prfbuf)],"To: %s\n",tmpbuf);
          sprintf(&prfbuf[strlen(prfbuf)],"Date: %s\n",
                  cvtdate(msg->crtime,msg->crdate));
          sprintf(&prfbuf[strlen(prfbuf)],"Subject: %s\n",msg->topic);
     }
     fwrite(prfbuf,strlen(prfbuf),1,pop3ptr->fp);
     if (msg->flags&FILATT) {
          clrprf();
          gmimid(pop3ptr->mimid,msg->msgid);
          sprintf(&prfbuf[strlen(prfbuf)],"MIME-Version: 1.0\n");
          sprintf(&prfbuf[strlen(prfbuf)],
                  "Content-Type: multipart/mixed; boundary=\"%s\"\n",
                  pop3ptr->mimid);
          sprintf(&prfbuf[strlen(prfbuf)],strrpl(getmsg(POP3MWR),'\r','\n'));
          sprintf(&prfbuf[strlen(prfbuf)],"\n--%s\n",pop3ptr->mimid);
          sprintf(&prfbuf[strlen(prfbuf)],"Content-Type: text/plain\n");
          sprintf(&prfbuf[strlen(prfbuf)],"Content-Transfer-Encoding: 7bit\n");
          fwrite(prfbuf,strlen(prfbuf),1,pop3ptr->fp);
     }
     clrprf();
     fixdot(strrpl(txt,'\r','\n'),TXTLEN);
     fwrite(txt,strlen(txt),1,pop3ptr->fp);
     if (msg->flags&FILATT) {
          strcpy(pop3ptr->mimfnm,dlname(msg));
          strcpy(pop3ptr->relnam,msg->attname);
          return(BD_MIME);
     }
     else {
          endmsg();
          return(BD_OK);
     }
}

static GBOOL
prpdrop(VOID)                      /* prepare to build maildrop file       */
{
     pop3ptr->rcvbuf[0]='\0';
     pop3ptr->rcvcnt=0U;
     stlcpy(pop3ptr->filnam,wrkdir,GCMAXPTH);
     stlcat(pop3ptr->filnam,spr("POP%x.DRP",usrnum),GCMAXPTH);
     if ((pop3ptr->fp=fopen(pop3ptr->filnam,FOPAA)) == NULL) {
          poplog("POP3 OPEN FAILURE","Unable to open maildrop file");
          prfmsg(POP3MDE);
          return(FALSE);
     }
     inigmerq(pop3ptr->wrkbuf);
     inormrd(pop3ptr->wrkbuf,pop3ptr->userid,EMLID,pop3ptr->emllim);
     return(TRUE);
}

static CHAR *
fixdot(                            /* applies RFC single period rule       */
CHAR *buffer,
INT bufsiz)
{
     char *ptr=buffer;

     while ((ptr=strchr(ptr,'\n')) != NULL) {
          if (ptr-buffer >= bufsiz) {
               break;
          }
          if (*(++ptr) == '.') {
               movmem(ptr+1,ptr+2,strlen(ptr));
               *(++ptr)='.';
          }
     }
     return(buffer);
}

static VOID
endmsg(VOID)                       /* end building current message         */
{
     fprintf(pop3ptr->fp,"\n.\n");
     pop3ptr->maildrp[pop3ptr->nummsg].msgsiz=ftell(pop3ptr->fp)
                                   -pop3ptr->maildrp[pop3ptr->nummsg].filpos;
     pop3ptr->nummsg++;
     usrptr->substt=BLDDROP;
}

static VOID
drpsiz(                            /* get drop size and number of msgs     */
INT *nomsgs,                       /*   number of messages in drop file    */
LONG *size)                        /*   total number of octets in drop     */
{
     INT i;

     for (i=0,*nomsgs=0,*size=0L ; i < pop3ptr->nummsg ; i++) {
          if (!(pop3ptr->maildrp[i].flags&MSGDEL)) {
               (*nomsgs)++;
               (*size)+=pop3ptr->maildrp[i].msgsiz;
          }
     }
}

static VOID
sendlist(VOID)                     /* responding to LIST command           */
{
     INT i,numlin;

     numlin=min(btuoba(usrnum)/20,pop3ptr->nummsg-(INT)pop3ptr->counter);
     for (i=0 ; i < numlin ; i++,pop3ptr->counter++) {
          if (!(pop3ptr->maildrp[i].flags&MSGDEL)) {
               prfmsg(POP3LST2,(INT)(pop3ptr->counter+1),
                      l2as(pop3ptr->maildrp[(INT)pop3ptr->counter].msgsiz));
          }
     }
     if (pop3ptr->nummsg == (INT)pop3ptr->counter) {
          prf(".\n");
          usrptr->substt=TRANS;
     }
     pop3prf();
}

static VOID
senduidl(VOID)                     /* responding to UIDL command           */
{
     INT i,numlin;
     LONG msgid;

     numlin=min(btuoba(usrnum)/20,pop3ptr->nummsg-(INT)pop3ptr->counter);
     for (i=0 ; i < numlin ; i++,pop3ptr->counter++) {
          if (!(pop3ptr->maildrp[(INT)pop3ptr->counter].flags&MSGDEL)
           && (msgid=getmno((INT)pop3ptr->counter)) != 0) {
               prfmsg(POP3UID3,(INT)(pop3ptr->counter+1),l2as(msgid));
          }
     }
     if (pop3ptr->nummsg == (INT)pop3ptr->counter) {
          prf(".\n");
          usrptr->substt=TRANS;
     }
     pop3prf();
}

static GBOOL
retrmsg(VOID)                      /* responding to RETR command           */
{
     INT size;
     GBOOL retval=TRUE;
     CHAR *ptr, *secptr;

     if (pop3ptr->topstt == TOPNOT && !pop3ptr->hascrd) {
          return(dbeatmsg());
     }
     if ((size=(INT)min((LONG)btuoba(usrnum),pop3ptr->counter)) == 0) {
          return(retval);
     }
     clrprf();
     if ((fread(prfbuf,1,size,pop3ptr->fp) != size) || ferror(pop3ptr->fp)) {
          poplog("POP3 READ FAILURE","Unable to read maildrop file");
          prfmsg(POP3LERR);
          prf("\n.\n");
          retval=FALSE;
          pop3prf();
     }
     else {
          pop3ptr->counter-=(LONG)size;
          prfbuf[size]='\0';
          if (pop3ptr->counter == 0L) {
               retval=FALSE;
          }
          ptr=secptr=prfbuf;
          if (pop3ptr->topstt == TOPHDR) {
               while ((ptr=strchr(ptr,'\n')) != NULL) {
                    if ((ptr-secptr) == 1) {
                         ptr++;
                         if (pop3ptr->topcntr == 0) {
                              strcpy(ptr,".\r\n");
                              retval=FALSE;
                         }
                         else {
                              pop3ptr->topstt=TOPBODY;
                         }
                         break;
                    }
                    secptr=++ptr;
               }
          }
          if (pop3ptr->topstt == TOPBODY) {
               for ( ; pop3ptr->topcntr > 0 ; pop3ptr->topcntr--) {
                    if (sameas(ptr,".\r\n")) {
                         retval=FALSE;
                         break;
                    }
                    if ((ptr=strchr(ptr,'\n')) != NULL) {
                         ptr++;
                    }
                    else {
                         break;
                    }
               }
               if (pop3ptr->topcntr == 0) {
                    strcpy(ptr,".\r\n");
                    retval=FALSE;
               }
          }
          stpans(prfbuf);
          btuxct(usrnum,strlen(prfbuf),prfbuf);
          clrprf();
     }
     return(retval);
}

static GBOOL                       /*   return TRUE if finished            */
markmsg(VOID)                      /* mark message as been read            */
{
     GBOOL retval=TRUE;
     char tag[TSLENG];

     switch(pop3ptr->markstt) {
     case MKGETMSG:
          switch (readmsg(pop3ptr->wrkbuf,&pop3ptr->taggedMsg,vdatmp)) {
          case GMEOK:
               if (pop3ptr->taggedMsg.flags&FILATT) {
                    switch (tagatt(pop3ptr->wrkbuf,&pop3ptr->taggedMsg,tag)) {
                    case GMEOK:
                         gdlatt(pop3ptr->userid,tag);
                         pop3ptr->markstt=MKWAIT;
                         break;
                    case GMECRD:
                         pop3ptr->hascrd=FALSE;
                         retval=FALSE;
                         break;
                    default:
                         pop3ptr->markstt=MKWAIT;
                         break;
                    }
               }
               else {
                    pop3ptr->markstt=MKWAIT;
               }
               break;
          case GMEAGAIN:
               break;
          case GMECRD:
          case GMEACC:
               pop3ptr->hascrd=FALSE;
               retval=FALSE;
               break;
          default:
               retval=FALSE;
               break;
          }
          break;
     case MKWAIT:
          if (tempbuf.locked > 0) {
               if (tempbuf.locked++ > 1024) {
                    retval=FALSE;
               }
               break;
          }
          else {
               tempbuf.locked=1;
               pop3ptr->markstt=MKTAGMSG;
          }
     case MKTAGMSG:
          switch (markread(pop3ptr->wrkbuf,&pop3ptr->taggedMsg,tempbuf.buf)) {
          case GMEAGAIN:
               break;
          case GMEOK:
          case GMERRG:
          case GMEAFWD:
          default:
               tempbuf.locked=0;
               retval=FALSE;
               break;
          }
          break;
     }
     return(retval);
}

static VOID
popcharg(VOID)                     /* deduct POP3 charges from user accnt  */
{
     LONG charge=(LONG)pop3kch*pop3ptr->maildrp[pop3ptr->curmsg].msgsiz/1024L
                +(LONG)pop3chg;

     if (charge != 0L) {
          if (!ndedcrd(pop3ptr->userid,charge,FALSE,TRUE)) {
               pop3ptr->hascrd=FALSE;
          }
     }
}

static VOID
clsfil(VOID)                       /* closes maildrop file                 */
{
     if (pop3ptr->fp != NULL) {
          fclose(pop3ptr->fp);
          unlink(pop3ptr->filnam);
     }
}

static VOID
prgmsg(VOID)                       /* deletes flagged messages             */
{
     LONG msgno;

     for ( ; pop3ptr->counter < (LONG)pop3ptr->nummsg ; pop3ptr->counter++) {
          if (pop3ptr->maildrp[(INT)pop3ptr->counter].flags&MSGDEL
           && (msgno=getmno((INT)pop3ptr->counter)) != 0L) {
               msgctx(pop3ptr->wrkbuf,msgno);
               delmsg(pop3ptr->wrkbuf);
               pop3ptr->maildrp[(INT)pop3ptr->counter].flags&=~MSGDEL;
               break;
          }
     }
     if (pop3ptr->counter == pop3ptr->nummsg) {
          usrptr->substt=PRGFIN;
     }
}

static VOID
prgfin(                            /* finish up POP3 session               */
INT msgno)                         /*   message to output                  */
{
     clsfil();
     if (usrptr->substt != AUTHUSR && usrptr->substt != AUTHPSW) {
          if (gmerqopn(pop3ptr->wrkbuf)) {
               clsgmerq(pop3ptr->wrkbuf);
          }
     }
     if (msgno != -1) {
          if (audpop3) {
               poplog("POP3 SESSION ENDED",
                      "%s ended POP3 session",pop3ptr->userid);
          }
          byetcp(msgno);
     }
     else {
          if (audpop3e) {
               poplog("POP3 SESSION ABORTED",
                      "%s dropped connection",pop3ptr->userid);
          }
          rstchn();
     }
     usrptr->substt=AUTHUSR;
}

static VOID
distmo(VOID)                       /* disable timeout                      */
{
     pop3ptr->sttime=0;
}

static VOID
genastr(VOID)                      /* generate APOP authentication string  */
{
     sprintf(pop3ptr->authstr,"<%x.%x%x@%0.50s>",usrnum,today(),now(),domain);
}

static VOID
pop3mim(VOID)                      /* MIME encode a file attachment        */
{
     INT i,nread,nout,mimipg;
     CHAR fileExt[16];

     setmbk(pop3mb);
     switch (pop3ptr->mimstt) {
     case START:
          if ((pop3ptr->mimfp=fopen(pop3ptr->mimfnm,FOPRB)) == NULL) {
               shocst("POP3 OPEN FILE ERROR",
                      "pop3mim(START): Could not open attachment file");
               pop3ptr->mimstt=WRTBAD;
          }
          else {
               pop3ptr->mimstt=CHK4BIN;
          }
          break;
     case CHK4BIN:
          if ((nread=fread(vdatmp,1,vdasiz,pop3ptr->mimfp)) < 0
           || ferror(pop3ptr->mimfp)) {
               shocst("POP3 READ FILE ERROR",
                      "pop3mim(CHK4BIN): Unable to read attachment file");
               pop3ptr->mimstt=WRTBAD;
               break;
          }
          else {
               for (i=0 ; i < nread ; i++) {
                    if (vdatmp[i] > 126
                     || (vdatmp[i] < 32
                     && (vdatmp[i] != TAB
                     && vdatmp[i] != 0x0D
                     && vdatmp[i] != 0x0A))) {
                         fseek(pop3ptr->mimfp,0L,SEEK_SET);
                         pop3ptr->mimstt=WRTHDR;
                         pop3ptr->maildrp[pop3ptr->nummsg].flags|=BINATT;
                         break;
                    }
               }
               if (nread < vdasiz) {
                    fseek(pop3ptr->mimfp,0L,SEEK_SET);
                    pop3ptr->mimstt=WRTHDR;
               }
          }
          if (pop3ptr->mimstt != WRTHDR) {
               break;
          }
     case WRTHDR:
          clrprf();
          fileparts(GCPART_EXTN,pop3ptr->relnam,fileExt,sizeof(fileExt));
          prfmsg(pop3ptr->maildrp[pop3ptr->nummsg].flags&BINATT ? BININC1
                 : ASCINC1,pop3ptr->mimid,gmimctyp(fileExt),
                           pop3ptr->relnam,pop3ptr->relnam);
          strrpl(stpans(prfbuf),'\r','\n');
          fwrite(prfbuf,strlen(prfbuf),1,pop3ptr->fp);
          clrprf();
          pop3ptr->mimstt=pop3ptr->maildrp[pop3ptr->nummsg].flags&BINATT
                          ? MIMECD : CPYATT;
          break;
     case WRTBAD:
          prfmsg(BADATT);
          strrpl(stpans(prfbuf),'\r','\n');
          fwrite(prfbuf,strlen(prfbuf),1,pop3ptr->fp);
          clrprf();
          pop3ptr->mimstt=MSGRDY;
          break;
     case CPYATT:
          if ((nread=fread(vdatmp,1,vdasiz-1,pop3ptr->mimfp)) < 0
           || ferror(pop3ptr->mimfp)) {
               shocst("POP3 READ FILE ERROR",
                      "pop3mim(CPYATT): Unable to read attachment");
               pop3ptr->mimstt=WRTBAD;
          }
          else {
               vdatmp[nread]='\0';
               nout=strlen(strstp(vdatmp,'\r'));
               if (fwrite(vdatmp,1,nout,pop3ptr->fp) != nout) {
                    shocst("POP3 WRITE FILE ERROR",
                           "pop3mim(CPYATT): Unable to copy attachment");
                    pop3ptr->mimstt=WRTBAD;
               }
               else if (nread != vdasiz-1) {
                    pop3ptr->mimstt=MSGRDY;
               }
          }
          break;
     case MIMECD:
          mimipg=mimecode(pop3ptr->mimfp,pop3ptr->fp,"\n",1);
          if (ferror(pop3ptr->fp)) {
               shocst("POP3 WRITE FILE ERROR",
                      "pop3mim(MIMECD): Unable to copy attachment");
               pop3ptr->mimstt=WRTBAD;
          }
          else if (!mimipg) {
               pop3ptr->mimstt=MSGRDY;
          }
          if (pop3ptr->mimstt != MSGRDY) {
               break;
          }
     case MSGRDY:
          fprintf(pop3ptr->fp,"\n--%s--\n",pop3ptr->mimid);
          fclose(pop3ptr->mimfp);
          endmsg();
          break;
     }
     rstmbk();
}

static VOID
rebufmsg(                          /* generate rebuff message              */
INT subjid,
INT msgid)
{
     CHAR *ptr,pop3rbfs[TPCSIZ];

     pop3ptr->maildrp[pop3ptr->nummsg].filpos=ftell(pop3ptr->fp);
     fprintf(pop3ptr->fp,"Message-ID: <R%d@%s>\n",now(),hstdom);
     fprintf(pop3ptr->fp,"From: %s@%s\n",(*alsofusr)("Sysop"),
                              smtihst ? hstdom : domain);
     fprintf(pop3ptr->fp,"To: %s\n",pop3ptr->userid);
     fprintf(pop3ptr->fp,"Date: %s\n",cvtdate(now(),today()));
     fprintf(pop3ptr->fp,"Subject: %s\n\n",
                              unpad(stzcpy(pop3rbfs,getmsg(subjid),TPCSIZ)));
     clrprf();
     prfmsg(msgid);
     ptr=strrpl(stpans(prfbuf),'\r','\n');
     fwrite(ptr,strlen(ptr),1,pop3ptr->fp);
     endmsg();
     clrprf();
}

static GBOOL
dbeatmsg(VOID)                     /* generate dead beat message           */
{
     CHAR *ptr;

     clrprf();
     prfmsg(POP3ICB,now(),hstdom,
                    (*alsofusr)("Sysop"),smtihst ? hstdom : domain,
                    pop3ptr->userid,
                    cvtdate(now(),today()));
     prf("\n.\n");
     ptr=strrpl(stpans(prfbuf),'\r','\n');
     if (strlen(ptr) < btuoba(usrnum)) {
          pop3prf();
          clrprf();
          return(FALSE);
     }
     else {
          clrprf();
          return(TRUE);
     }
}

static LONG                        /*   returns message number             */
getmno(                            /* get message number                   */
INT idx)                           /*   index of message in drop file      */
{
     return(pop3ptr->maildrp[idx].msgid);
}
