/***************************************************************************
 *                                                                         *
 *   CSEML.C                                                               *
 *                                                                         *
 *   Copyright (c) 1988-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This is the Worldgroup Client/Server electronic mail service handler. *
 *                                                                         *
 *                                                - J. Alvrus  10/20/94    *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "filexfer.h"
#include "gme.h"
#include "galmsg.h"
#include "emlfor.h"
#include "csef.h"

#define FILREV "$Revision: 26 $"

#define SAFWDEE "sau:safwdee"      /* read/write auto-forwardee dpk name   */
#define SLFMTY  "sau:slfmty"       /* rd/wrt show Forum msgs in E-mail dpk */
#define SCLARPL "sau:sclarpl"      /* rd/wrt clear-after-reply dpk         */
#define EMLTAGS "sau:emailtags"    /* rd/wrt array of tagged E-mail msgs   */
#define CDETAIL "cdetail "         /* carrier detail dpk suffix            */
#define SYSCAR  "syscar "          /* sysop carrier detail dpk suffix      */
#define RDESFX  "rdeml "           /* get new E-mail dpk suffix            */
#define EMSGSFX "emlmsg "          /* read E-mail message dpk suffix       */
#define EHDRSFX "emlhdr "          /* read E-mail header dpk suffix        */
#define AINFSFX "appinf "          /* get app-defined info dpk suffix      */
#define AINFDPK "sau:"AINFSFX      /* get app-defined info dpk name        */
#define SLISFX  "slist "           /* sysop dist list suffix               */
#define ELISFX  "elist "           /* sysop dist list suffix (when editing)*/
#define CRLSFX  "crtlst "          /* create dist list suffix              */
#define ADLSFX  "addlst "          /* add to dist list suffix              */
#define FWDEML  "fwdeml "          /* forward a message suffix             */
#define CPYEML  "cpyeml "          /* copy a message suffix                */
#define BKTRACK "bktrack "         /* backtrack from message suffix        */

static VOID eread(INT direction,struct saunam *dpknam);
static VOID ewrite(struct saunam *dpknam,USHORT length,VOID *value);
static VOID exdone(VOID);
static VOID eabort(VOID);

struct agent emlagt={              /* agent information structure          */
     EMLAPID,                      /*   appid                              */
     eread,                        /*   read-dynapak function pointer      */
     ewrite,                       /*   write-dynapak function pointer     */
     exdone,                       /*   file xfer-done function pointer    */
     eabort                        /*   abort-request function pointer     */
};

struct newslst {                   /* create new sysop list dpk structure  */
     CHAR key[KEYSIZ-1];           /*   key required to use list           */
     SHORT surchg;                 /*   charge to use list                 */
};

struct flddef newslstFDA[] = {
     {CVTFLD_CHAR ,KEYSIZ-1,fldoff(newslst,key)   ,NULL},
     {CVTFLD_SHORT,1       ,fldoff(newslst,surchg),NULL},
     {CVTFLD_END  ,0       ,0                     ,NULL}
};

struct flddef emlaccFDA[] = {
     {CVTFLD_SHORT,2,fldoff(emlacc,flags) ,NULL},
     {CVTFLD_LONG ,1,fldoff(emlacc,txtlen),NULL},
     {CVTFLD_RTEXT,0,fldoff(emlacc,dftuid),NULL},
     {CVTFLD_END  ,0,0                    ,NULL}
};

struct flddef cdetailFDA[] = {
     {CVTFLD_RTEXT,PFXSIZ-1+EXPNSZ-1,fldoff(cdetail,prefix),NULL},
     {CVTFLD_SHORT,1                ,fldoff(cdetail,flags) ,NULL},
     {CVTFLD_RTEXT,0                ,fldoff(cdetail,info)  ,NULL},
     {CVTFLD_END  ,0                ,0                     ,NULL}
};

static CHAR *csekey;                /* key required to use C/S E-mail agent */
static CHAR *annoaid;               /* App-ID to send new mail announcement */
static CHAR *annocmd;               /* cmd line for E-mail client announcmnt*/

static VOID cseanno(VOID);
static VOID emlacc(struct saunam *dpknam);
static SHORT emsgacc(VOID);
static VOID cdetail(INT direction,struct saunam *dpknam,GBOOL sysdpk);
static VOID fillcdet(struct cdetail *cdet,SHORT expi,const struct expinfo *exp);
static VOID chk4new(struct saunam *dpknam);
static VOID cfindnew(VOID);
static VOID findnew(VOID);
static VOID goread(INT direction,struct saunam *dpknam,INT flags);
static VOID crsvrdbuf(VOID);
static VOID rsvrdbuf(VOID);
static VOID creademl(VOID);
static VOID reademl(VOID);
static VOID cmrkoff(VOID);
static VOID mrkoff(VOID);
static VOID ersp2rd(VOID);
static VOID clretag(LONG msgid);
static INT etagidx(LONG msgid,LONG *tagarr,INT numtags);
static VOID sorttags(LONG *tagarr,INT numtags);
static VOID emlatt(struct saunam *dpknam);
static VOID bktrack(struct saunam *dpknam);
static VOID crdbktk(VOID);
static VOID rdbktk(VOID);
static VOID delemsg(struct saunam *dpknam);
static VOID appinf(struct saunam *dpknam);
static VOID rd4ainf(VOID);
static VOID slstinf(INT direction, struct saunam *dpknam);
static VOID elstinf(INT direction, struct saunam *dpknam);
static VOID glstfil(struct saunam *dpknam);
static VOID wlstfil(struct saunam *dpknam,struct filinf *finfo);
static VOID wlstabt(VOID);
static VOID wlstdone(VOID);
static VOID crtslst(struct saunam *dpknam,UINT length,struct newslst *value);
static VOID adlst(struct saunam *dpknam,CHAR *addr);
static VOID fwdeml(struct saunam *dpknam,CHAR *fwdinf);
static VOID cpyeml(struct saunam *dpknam,CHAR *cpyinf);
static VOID reg_dpk(VOID);

VOID
inicseml(VOID)                     /* initialize Client/Server E-mail agent*/
{
     register_agent(&emlagt);
     csekey=stgopt(CSEKEY);
     if (ynopt(CSANNO)) {
          annoaid=stgopt(ANNOAID);
          annocmd=stgopt(ANNOCMD);
          hook_announce(cseanno);
          dclanno(sizeof(struct rqinfo));
     }
     reg_dpk();
}

static VOID
cseanno(VOID)                      /* new mail announcer                   */
{
     if (!haskey(csekey)) {
          addanno("");
          return;
     }
     rqiptr=(struct rqinfo *)annomem;
     efwork=rqiptr->u.work;
     vdamsg();
     switch (rqiptr->stt) {
     case 0:
          inigmerq(efwork);
          inormrd(efwork,usaptr->userid,EMLID,firstnew(usaptr->userid,EMLID));
          rqiptr->stt=1;
     case 1:
          switch (nextmsgf(efwork,msg,msgtxt)) {
          case GMEAGAIN:
               break;
          case GMEOK:
               clsgmerq(efwork);
               setmbk(efmb);
               addannom(getmsg(CSNEWEML),annoaid,annocmd);
               break;
          default:
               clsgmerq(efwork);
               addanno("");
               break;
          }
     }
}

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

     dpkstr=cnvs2d(dpknam);
     if (direction == 0 && sameas("sau:emlacc",dpkstr)) {
          if (stdchk(csekey)) {
               emlacc(dpknam);
          }
          else {
               setmem(rsptmp,sizeof(struct emlacc),0);
               rsp2read(dpknam,sizeof(struct emlacc),rsptmp,emlaccFDA);
          }
          return;
     }
     if (!stdchk(csekey)) {
          rejectreq();
          return;
     }
     cssetup();
     if (direction == 0 && sameto("sauf:",dpkstr)
      && sameto(EMLATT,dpknam->suffix)) {
          emlatt(dpknam);
     }
     else if (direction == 0 && sameto("saf:",dpkstr)
           && sameto(SLISFX,dpknam->suffix)) {
          glstfil(dpknam);
     }
     else if (direction == 0 && sameto("saf:",dpkstr)
      && sameto(LEXDPK,dpknam->suffix)) {
          getlex(dpknam);
     }
     else if (sameto("sau:"CDETAIL,dpkstr)) {
          cdetail(direction,dpknam,FALSE);
     }
     else if (sameto("sa:"SYSCAR,dpkstr)) {
          cdetail(direction,dpknam,TRUE);
     }
     else if (direction == 0 && sameas("sau:qrynew",dpkstr)) {
          chk4new(dpknam);
     }
     else if (direction == 0 && sameas(SAFWDEE,dpkstr)) {
          rsp2read(dpknam,strlen(qsptr->fwdee),qsptr->fwdee,rtextFDA);
     }
     else if (direction == 0 && sameas(SLFMTY,dpkstr)) {
          *(SHORT *)rsptmp=qsptr->flags&FORUM2 ? VBTRUE : FALSE;
          rsp2read(dpknam,sizeof(SHORT),rsptmp,shortFDA);
     }
     else if (direction == 0 && sameas(SCLARPL,dpkstr)) {
          *(SHORT *)rsptmp=qsptr->flags&CLARPL ? VBTRUE : FALSE;
          rsp2read(dpknam,sizeof(SHORT),rsptmp,shortFDA);
     }
     else if (direction == 0 && sameas(EMLTAGS,dpkstr)) {
           r2rgdp(direction,dpknam);
     }
     else if (sameto("sau:",dpkstr) && sameto(RDESFX,dpknam->suffix)) {
          goread(direction,dpknam,INCLTXT|CLRTAG);
     }
     else if (sameto("sau:",dpkstr) && sameto(EHDRSFX,dpknam->suffix)) {
          goread(direction,dpknam,0);
     }
     else if (sameto("sau:",dpkstr) && sameto(EMSGSFX,dpknam->suffix)) {
           goread(direction,dpknam,INCLTXT);
     }
     else if (direction == 0 && sameto("sau:",dpkstr)
           && sameto(BKTRACK,dpknam->suffix)) {
          bktrack(dpknam);
     }
     else if (direction == 0 && sameto(AINFDPK,dpkstr)) {
          appinf(dpknam);
     }
     else if (direction >= 0 && sameto("sa:",dpkstr)
           && sameto(SLISFX,dpknam->suffix)) {
          slstinf(direction,dpknam);
     }
     else if (direction >= 0 && sameto("sa:",dpkstr)
           && sameto(ELISFX,dpknam->suffix)) {
          elstinf(direction,dpknam);
     }
     else {
          rejectreq();
     }
}

static VOID
ewrite(                            /* write-dynapak handler                */
struct saunam *dpknam,             /*   dynapak name to write              */
USHORT length,                       /*   length of dynapak value            */
VOID *value)                       /*   dynapak value to write             */
{
     SHORT tmpint;
     CHAR *dpkstr;

     dpkstr=cnvs2d(dpknam);
     if (!stdchk(csekey)) {
          rsp2write(FALSE,0,NULL,NULL);
          return;
     }
     cssetup();
     if (length > 0 && sameto("saf:",dpkstr)
      && sameto(UPLATT,dpknam->suffix)) {
          ASSERT(length == sizeof(struct filinf));
          uplatt(dpknam,(struct filinf *)value);
     }
     else if (length > 0 && sameto("saf:",dpkstr)
      && sameto(SLISFX,dpknam->suffix)) {
          ASSERT(length == sizeof(struct filinf));
          wlstfil(dpknam,(struct filinf *)value);
     }
     else if (sameas(SAFWDEE,dpkstr)) {
          tmpint=setafwd(unpad((char *)value));
          rsp2write(tmpint == VALYES,sizeof(SHORT),&tmpint,shortFDA);
     }
     else if (length == sizeof(SHORT) && sameas(SLFMTY,dpkstr)) {
          if (*(SHORT *)value) {
               qsptr->flags|=FORUM2;
          }
          else {
               qsptr->flags&=~FORUM2;
          }
          rsp2write(TRUE,0,NULL,NULL);
     }
     else if (length == sizeof(SHORT) && sameas(SCLARPL,dpkstr)) {
          if (*(SHORT *)value) {
               qsptr->flags|=CLARPL;
          }
          else {
               qsptr->flags&=~CLARPL;
          }
          rsp2write(TRUE,0,NULL,NULL);
     }
     else if (length%sizeof(LONG) == 0 && sameas(EMLTAGS,dpkstr)) {
          sorttags(value,length/sizeof(LONG));
          writegdp(dpknam,length,value,longsFDA);
          rsp2write(TRUE,0,NULL,NULL);
     }
     else if (length == 0 && sameto("sau:",dpkstr)
      && sameto(EMSGSFX,dpknam->suffix)) {
           delemsg(dpknam);
     }
     else if (sameto("sa:",dpkstr) && sameto(CRLSFX,dpknam->suffix)) {
          crtslst(dpknam,length,(struct newslst *)value);
     }
     else if (sameto("sa:",dpkstr) && sameto(ADLSFX,dpknam->suffix)) {
          adlst(dpknam,unpad((CHAR *)value));
     }
     else if (length == 0 && sameto("sa:",dpkstr)
      && sameto(SLISFX,dpknam->suffix)) {
          *(SHORT *)rsptmp=delslst(dpknam->suffix+sizeof(SLISFX)-1);
          rsp2write(*(SHORT *)rsptmp > GMEAGAIN,sizeof(SHORT),rsptmp,shortFDA);
     }
     else if (length >= sizeof(struct newdpk)-1 && sameto("sa:",dpkstr)
           && sameas(WRTNEW,dpknam->suffix)) {
          wrtnew(dpknam,(struct newdpk *)value);
     }
     else if (length >= sizeof(struct newdpk)-1 && sameto("sa:",dpkstr)
           && sameas(WRTRPL,dpknam->suffix)) {
          wrtrpl(dpknam,(struct newdpk *)value);
     }
     else if (sameto("sa:",dpkstr) && sameto(CCLSFX,dpknam->suffix)) {
          gcclst(dpknam,unpad((CHAR *)value));
     }
     else if (sameto("sa:",dpkstr) && sameto(FWDEML,dpknam->suffix)) {
          fwdeml(dpknam,unpad((CHAR *)value));
     }
     else if (sameto("sa:",dpkstr) && sameto(CPYEML,dpknam->suffix)) {
          cpyeml(dpknam,unpad((CHAR *)value));
     }
     else {
          rsp2write(FALSE,0,NULL,NULL);
     }
}

static VOID
exdone(VOID)                       /* file transfer-done handler           */
{
     cssetup();
     if (iswrite()) {
          if (sameto(UPLATT,rqiptr->dpknam.suffix)) {
               uladone();
          }
          else if (sameto(SLISFX,rqiptr->dpknam.suffix)) {
               wlstdone();
          }
     }
     else {
          if (sameto(EMLATT,rqiptr->dpknam.suffix)) {
               dldone(rqiptr->u.tag);
          }
     }
}

static VOID
eabort(VOID)                       /* abort-request handler                */
{
     cssetup();
     if (sameto(EMLATT,rqiptr->dpknam.suffix)) {
          if (rqiptr->stt == 0) {
               clsgmerq(efwork);
          }
          else {
               dlabt(rqiptr->u.tag);
          }
     }
     else if (sameas(WRTNEW,rqiptr->dpknam.suffix)
           || sameas(WRTRPL,rqiptr->dpknam.suffix)) {
          wrtabt();
     }
     else if (sameto(UPLATT,rqiptr->dpknam.suffix)) {
          uplabt();
     }
     else if (sameto(SLISFX,rqiptr->dpknam.suffix)) {
          wlstabt();
     }
     else {
          if (gmerqopn(efwork)) {
               clsgmerq(efwork);
          }
          if (rqiptr->flags&BUFINU) {
               unrarea(wrmpool,rqiptr->wrthdl);
          }
     }
}

static VOID
emlacc(                            /* get user's E-mail access             */
struct saunam *dpknam)             /*   dynapak name to read               */
{
     SHORT tmpint;
     struct emlacc *eacc;

     tmpint=emsgacc();
     if (haskey(massky)) {
          tmpint|=EAMSLST;
     }
     if (haskey(edstky)) {
          tmpint|=EAEDLST;
     }
     eacc=(struct emlacc *)rsptmp;
     eacc->flags=tmpint;
     if (alwcpy) {
          eacc->ccmax=usrptr->flags&MASTER ? -1 : csmaxcc;
     }
     else {
          eacc->ccmax=0;
     }
     eacc->txtlen=(LONG)(TXTLEN-1);
     stlcpy(eacc->dftuid,(CHAR *)gmeEmlSysUID(),UIDSIZ);
     rsp2read(dpknam,fldoff(emlacc,dftuid)+strlen(eacc->dftuid),rsptmp,
              emlaccFDA);
}

static SHORT
emsgacc(VOID)                      /* form E-mail per-message access flags */
{
     SHORT tmpint;

     tmpint=haskey(emlkey) ? EAWRITE : 0;
     if (alweat && haskey(eatkey)) {
          tmpint|=EAATTACH;
     }
     if (alwrrr && haskey(rrrkey)) {
          tmpint|=EARECREQ;
     }
     if (alwpri && haskey(prikey)) {
          tmpint|=EAPRIMSG;
     }
     if (haskey(fprlock)) {
          tmpint|=EAINDATT;
     }
     return(tmpint);
}

static VOID
cdetail(                           /* get details on carrier               */
INT direction,                     /*   direction for read                 */
struct saunam *dpknam,             /*   dynapak name to read               */
GBOOL sysdpk)                      /*   check user's access for carrier?   */
{
     INT i,n;
     CHAR *cp,tmppfx[PFXSIZ+1];
     struct cdetail *cdet;
     const struct expinfo *exp;

     n=numexp();
     *namtmp=*dpknam;
     cp=strchr(namtmp->suffix,' ');
     if (n == 0 || cp == NULL || (sysdpk && !haskey(forsys))) {
          rejectreq();
          return;
     }
     stlcpy(tmppfx,++cp,PFXSIZ+1);
     *cp='\0';
     if (direction == 0) {
          if ((i=expidx(tmppfx)) == NOIDX) {
               rejectreq();
               return;
          }
          exp=expinf(i);
          if (sysdpk || haskey(exp->wrtkey)) {
               fillcdet(cdet=(struct cdetail *)rsptmp,i,exp);
               rsp2read(NULL,fldoff(cdetail,info)+strlen(cdet->info),rsptmp,
                        cdetailFDA);
               return;
          }
     }
     else {
          if ((cp=strchr(tmppfx,':')) != NULL) {
               *cp='\0';
          }
          if (direction > 0) {
               i=0;
               while (i < n && stricmp(expinf(i)->prefix,tmppfx) <= 0) {
                    ++i;
               }
               while (!sysdpk && i < n && !haskey(expinf(i)->wrtkey)) {
                    ++i;
               }
          }
          else {
               i=n-1;
               while (i >= 0 && stricmp(expinf(i)->prefix,tmppfx) >= 0) {
                    --i;
               }
               while (!sysdpk && i >= 0 && !haskey(expinf(i)->wrtkey)) {
                    --i;
               }
          }
          if (i >= 0 && i < n) {
               stlcat(namtmp->suffix,(exp=expinf(i))->prefix,SFXSIZ-1);
               chrcat(namtmp->suffix,':');
               fillcdet(cdet=(struct cdetail *)rsptmp,i,exp);
               rsp2read(namtmp,fldoff(cdetail,info)+strlen(cdet->info),rsptmp,
                        cdetailFDA);
               return;
          }
     }
     rejectreq();
}

static VOID
fillcdet(                          /* fill in carrier details structure    */
struct cdetail *cdet,              /*   details buffer                     */
SHORT expi,                        /*   exporter index                     */
const struct expinfo *exp)         /*   exporter info structure            */
{
     CHAR *cp;

     c2bcpy(cdet->prefix,exp->prefix,PFXSIZ-1);
     c2bcpy(cdet->name,exp->name,EXPNSZ-1);
     cdet->flags=0;
     if ((exp->flags&EXPATT) && haskey(exp->attkey)) {
          cdet->flags|=EAATTACH;
     }
     if ((exp->flags&EXPRRR) && haskey(exp->rrrkey)) {
          cdet->flags|=EARECREQ;
     }
     if ((exp->flags&EXPPRI) && haskey(exp->prikey)) {
          cdet->flags|=EAPRIMSG;
     }
     cp=stlcpy(cdet->info,exp->desc,EXPDSZ);
     cp+=strlen(cp);
     *cp++=FLDSEP;
     stlcpy(cp,exp->exmp,MAXADR);
     cp+=strlen(cp);
     *cp++=FLDSEP;
     stlcpy(cp,exphlp(expi),MAXDPKV-sizeof(struct cdetail)-strlen(cdet->info));
     stp4cs(cp);
}

static VOID
chk4new(                           /* check for new E-mail                 */
struct saunam *dpknam)             /*   dynapak name to read               */
{
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,EMLID,firstnew(usaptr->userid,EMLID));
     movmem(dpknam,&rqiptr->dpknam,sizeof(struct saunam));
     findnew();
}

static VOID
cfindnew(VOID)                     /* cycled checking for new mail         */
{
     cssetup();
     findnew();
}

static VOID
findnew(VOID)                      /* checking for new mail                */
{
     vdamsg();
     switch (nextmsgf(efwork,msg,msgtxt)) {
     case GMEAGAIN:
          cycleme(cfindnew);
          break;
     case GMEOK:
          clsgmerq(efwork);
          rsp2read(&rqiptr->dpknam,sizeof(LONG),&msg->msgid,longFDA);
          break;
     default:
          clsgmerq(efwork);
          rejectreq();
          break;
     }
}

static VOID
goread(                            /* read E-mail handler                  */
INT direction,                     /*   read direction: 0=eq, 1=gt, -1=lt  */
struct saunam *dpknam,             /*   dynapak name in use                */
INT flags)                         /*   read flags: include text, clear tag*/
{
     CHAR *cp;
     LONG msgid;

     cp=strchr(dpknam->suffix,' ');
     if (cp == NULL) {
          msgid=0L;
     }
     else {
          if (!lsfxval((ULONG *)&msgid,cp+1)) {
               rejectreq();
               return;
          }
     }
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,EMLID,msgid);
     rqiptr->stt=(signed char)direction;
     rqiptr->flags=(unsigned char)flags;
     rqiptr->dpknam=*dpknam;
     rsvrdbuf();
}

static VOID
crsvrdbuf(VOID)                    /* cycled reserve buffer for read       */
{
     cssetup();
     cycleme(NULL);
     rsvrdbuf();
}

static VOID
rsvrdbuf(VOID)                     /* reserve pool buffer for read         */
{
     rqiptr->wrthdl=rsvarea(wrmpool);
     if (rqiptr->wrthdl == NOHDL) {
          cycleme(crsvrdbuf);
     }
     else {
          rqiptr->flags|=BUFINU;
          cswrtup();
          reademl();
     }
}

static VOID
creademl(VOID)                     /* cycled read E-mail message           */
{
     cssetup();
     cswrtup();
     cycleme(NULL);
     reademl();
}

static VOID
reademl(VOID)                      /* read E-mail message                  */
{
     LONG msgid;

     switch (readir(rqiptr->stt,efwork,msg,msgtxt)) {
     case GMEAGAIN:
          cycleme(creademl);
          break;
     case GMEOK:
          rqiptr->flags|=(CHAR)(msg->flags&RECREQ);
          if (forreal() && (rqiptr->flags&INCLTXT)) {
               mrkoff();
          }
          else {
               ersp2rd();
               clsgmerq(efwork);
               unrarea(wrmpool,rqiptr->wrthdl);
          }
          break;
     case GMENFND:
          if (rqiptr->stt == 0) {
               ASSERT(strchr(rqiptr->dpknam.suffix,' ') != NULL);
               ASSERT(lsfxval((ULONG *)&msgid,
                              strchr(rqiptr->dpknam.suffix,' ')+1));
               lsfxval((ULONG *)&msgid,
                       strchr(rqiptr->dpknam.suffix,' ')+1);
               clretag(msgid);
          }
     default:
          clsgmerq(efwork);
          unrarea(wrmpool,rqiptr->wrthdl);
          rejectreq();
          break;
     }
}

static VOID
cmrkoff(VOID)                      /* cycled mark-message-read process     */
{
     cssetup();
     cswrtup();
     cycleme(NULL);
     mrkoff();
}

static VOID
mrkoff(VOID)                       /* mark message read function           */
{
     SHORT rc;

     switch (rc=markreadf(efwork,msg,msgtxt)) {
     case GMEAGAIN:
          cycleme(cmrkoff);
          return;
     case GMEAFWD:
     case GMERRG:
     case GMEOK:
          switch (rc) {
          case GMEAFWD:
               rrgnot(gmexinf());
               break;
          case GMERRG:
               rrgnot(msg->from);
               break;
          }
          ersp2rd();
          break;
     default:
          rejectreq();
          break;
     }
     clsgmerq(efwork);
     unrarea(wrmpool,rqiptr->wrthdl);
}

static VOID
ersp2rd(VOID)                      /* rsp2read() after reading email msg   */
{
     CHAR *cp;

     if (rqiptr->stt != 0) {
          cp=strchr(rqiptr->dpknam.suffix,' ');
          if (cp == NULL) {
               stlcat(rqiptr->dpknam.suffix," ",SFXSIZ);
          }
          else {
               *(cp+1)='\0';
          }
          stlcat(rqiptr->dpknam.suffix,longstr(msg->msgid),SFXSIZ);
     }
     msg->flags|=(rqiptr->flags&(CHAR)RECREQ);
     if (forreal() && (rqiptr->flags&CLRTAG)) {
          clretag(msg->msgid);
     }
     msg2dpk(msg,msgtxt,(struct msgdpk *)rsptmp,rqiptr->flags&INCLTXT);
     if (msg->forum == EMLID) {
          ((struct msgdpk *)rsptmp)->axes=(CHAR)emsgacc();
     }
     else {
          ((struct msgdpk *)rsptmp)->axes=(CHAR)foracc(msg->forum);
     }
     rsp2read(&rqiptr->dpknam,
              sizeof(struct msgdpk)+strlen(((struct msgdpk *)rsptmp)->info),
              rsptmp,msgdpkFDA);
}

static VOID
clretag(                           /* clear any email tag                  */
LONG msgid)                        /*   associated with this message ID    */
{                                  /*   NOTE: this function uses rsptmp    */
     INT i,n,tmplen;
     LONG *tagarr;
     struct saunam tmpsau;

     cnvd2s(EMLTAGS,&tmpsau);
     if ((tmplen=readgdpEQ(&tmpsau,MAXDPKV,rsptmp,longsFDA)) > 0) {
          n=tmplen/sizeof(LONG);
          tagarr=(LONG *)rsptmp;
          if ((i=etagidx(msgid,tagarr,n)) >= 0) {
               ASSERT(tagarr[i] == msgid);
               movmem(&tagarr[i+1],&tagarr[i],sizeof(LONG)*(n-i-1));
               writegdp(&tmpsau,sizeof(LONG)*(n-1),tagarr,longsFDA);
          }
     }
}

static INT                         /*   returns index or NOIDX if not found*/
etagidx(                           /* get index of message                 */
LONG msgid,                        /*   with this message ID               */
LONG *tagarr,                      /*   in this tag array                  */
INT numtags)                       /*   that has this many elements        */
{
     INT lo,md,hi;
     LONG dif;

     ASSERT(tagarr != NULL);
     ASSERT(numtags > 0);
     lo=0;
     hi=numtags-1;
     while (lo <= hi) {
          md=lo+(hi-lo)/2;
          if ((dif=msgid-tagarr[md]) < 0L) {
               if (md == lo) {
                    break;
               }
               hi=md-1;
          }
          else if (dif > 0) {
               if (md == hi) {
                    break;
               }
               lo=md+1;
          }
          else {
               return(md);
          }
     }
     return(NOIDX);
}

static VOID
sorttags(                          /* sort email tags                      */
LONG *tagarr,                      /*   in this array                      */
INT numtags)                       /*   which has this many elements       */
{
     INT i,j;
     LONG tmp;

     for (i=0 ; i < numtags-1 ; ++i) {
          for (j=i+1 ; j < numtags ; ++j) {
               if (tagarr[j] < tagarr[i]) {
                    tmp=tagarr[i];
                    tagarr[i]=tagarr[j];
                    tagarr[j]=tmp;
               }
          }
     }
}

static VOID
emlatt(                            /* get attachment to E-mail message     */
struct saunam *dpknam)             /*   dynapak name in use                */
{
     LONG msgid;

     if (!lsfxval((ULONG *)&msgid,&dpknam->suffix[sizeof(EMLATT)-1])) {
          rejectreq();
          return;
     }
     rqiptr->stt=0;
     rqiptr->dpknam=*dpknam;
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,EMLID,msgid);
     rd4att();
}

static VOID
bktrack(                           /* backtrack from E-mail message        */
struct saunam *dpknam)             /*   dpk name in use                    */
{
     LONG msgid;

     if (!lsfxval((ULONG *)&msgid,&dpknam->suffix[sizeof(BKTRACK)-1])) {
          rejectreq();
          return;
     }
     rqiptr->dpknam=*dpknam;
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,EMLID,msgid);
     rdbktk();
}

static VOID
crdbktk(VOID)                      /* cycled backtrack                     */
{
     cssetup();
     rdbktk();
}

static VOID
rdbktk(VOID)                       /* backtrack from message               */
{
     vdamsg();
     switch (*(SHORT *)rsptmp=readparf(efwork,msg,msgtxt)) {
     case GMEAGAIN:
          cycleme(crdbktk);
          break;
     case GMEOK:
          if (forreal() && markreadf(efwork,msg,msgtxt) != GMEOK) {
               clsgmerq(efwork);
               rejectreq();
               return;
          }
          msg2dpk(msg,msgtxt,(struct msgdpk *)rsptmp,TRUE);
          rsp2read(&rqiptr->dpknam,
                   sizeof(struct msgdpk)+strlen(((struct msgdpk *)rsptmp)->info),
                   rsptmp,msgdpkFDA);
          break;
     default:
          rejectreq();
          break;
     }
     clsgmerq(efwork);
}

static VOID
delemsg(                           /* delete an email message              */
struct saunam *dpknam)             /*   dynapak name in use                */
{
     SHORT rc;
     LONG msgid;

     ASSERT(sameto(EMSGSFX,dpknam->suffix));
     if (!lsfxval((ULONG *)&msgid,&dpknam->suffix[sizeof(EMSGSFX)-1])) {
          rejectreq();
          return;
     }
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,EMLID,msgid);
     if ((rc=delmsg(efwork)) == GMEOK) {
          clretag(msgid);
     }
     clsgmerq(efwork);
     rsp2write(rc == GMEOK,sizeof(SHORT),&rc,shortFDA);
}

static VOID
appinf(                            /* get app-defined info for a message   */
struct saunam *dpknam)             /*   dpk name in use                    */
{
     CHAR *pfid,*pmid;
     LONG msgid;
     UINT tmpfid;
     USHORT forid;
     CHAR tmpbuf[SFXSIZ];

     ASSERT(sameto(AINFSFX,dpknam->suffix));
     stlcpy(tmpbuf,dpknam->suffix,SFXSIZ);
     pfid=tmpbuf+CSTRLEN(AINFSFX);
     if ((pmid=strchr(pfid,' ')) == NULL) {
          rejectreq();
          return;
     }
     *pmid++='\0';
     if (!isfxval(&tmpfid,pfid) || !lsfxval((ULONG *)&msgid,pmid)) {
          rejectreq();
          return;
     }
     forid=(USHORT)tmpfid;
     if (!fidok(forid)) {
          rejectreq();
          return;
     }
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,forid,msgid);
     cycleme(rd4ainf);
     rd4ainf();
}

static VOID
rd4ainf(VOID)                      /* read msg for its app-defined info    */
{
     INT rc;

     cssetup();
     vdamsg();
     if ((rc=readmsgf(efwork,msg,msgtxt)) != GMEAGAIN) {
          *rsptmp='\0';
          if (rc > GMEAGAIN) {
               stlcpy(rsptmp,gmeGetAppInfo(),MAXDPKV);
          }
          clsgmerq(efwork);
          rsp2read(NULL,STGLEN,rsptmp,rtextFDA);
     }
}

static VOID
slstinf(                           /* get sysop list info                  */
INT direction,                     /*   direction of read (0 or 1)         */
struct saunam *dpknam)             /*   dpk name in use                    */
{
     CHAR *cp;
     CHAR tmpkey[KEYSIZ];

     ASSERT(sameto(SLISFX,dpknam->suffix));
     switch (direction) {
     case 0:
          cp=dpknam->suffix+sizeof(SLISFX)-1;
          if (dlstxst(cp)) {
               getslst(cp,tmpkey,(SHORT *)rsptmp);
               if (haskey(tmpkey)) {
                    rsp2read(dpknam,sizeof(SHORT),rsptmp,shortFDA);
                    return;
               }
          }
          break;
     case 1:
          movmem(dpknam,&rqiptr->dpknam,sizeof(struct saunam));
          cp=rqiptr->dpknam.suffix+sizeof(SLISFX)-1;
          while (nxtslst(cp,tmpkey,(SHORT *)rsptmp) == GMEOK) {
               if (haskey(tmpkey)) {
                    rsp2read(&rqiptr->dpknam,sizeof(SHORT),rsptmp,shortFDA);
                    return;
               }
          }
          break;
     default:
          ASSERT(FALSE);
     }
     rejectreq();
}

static VOID
elstinf(                           /* get sysop list info (when editing)   */
INT direction,                     /*   direction of read (0 or 1)         */
struct saunam *dpknam)             /*   dpk name in use                    */
{
     SHORT tmpchg;
     CHAR *cp;
     CHAR tmpkey[KEYSIZ];

     ASSERT(sameto(ELISFX,dpknam->suffix));
     if (haskey(edstky)) {
          switch (direction) {
       case 0:
               cp=dpknam->suffix+sizeof(ELISFX)-1;
               if (dlstxst(cp)) {
                    getslst(cp,tmpkey,&tmpchg);
                    c2bcpy(((struct newslst *)rsptmp)->key,tmpkey,KEYSIZ-1);
                    ((struct newslst *)rsptmp)->surchg=tmpchg;
                    rsp2read(dpknam,sizeof(SHORT),rsptmp,shortFDA);
                    return;
               }
               break;
          case 1:
               movmem(dpknam,&rqiptr->dpknam,sizeof(struct saunam));
               cp=rqiptr->dpknam.suffix+sizeof(ELISFX)-1;
               if (nxtslst(cp,tmpkey,&tmpchg) == GMEOK) {
                    c2bcpy(((struct newslst *)rsptmp)->key,tmpkey,KEYSIZ-1);
                    ((struct newslst *)rsptmp)->surchg=tmpchg;
                    rsp2read(&rqiptr->dpknam,sizeof(struct newslst),rsptmp,
                             newslstFDA);
                    return;
               }
               break;
          default:
               ASSERT(FALSE);
          }
     }
     rejectreq();
}

static VOID
glstfil(                           /* get sysop list file                  */
struct saunam *dpknam)             /*   dpk name in use                    */
{
     CHAR *cp;

     ASSERT(sameto(SLISFX,dpknam->suffix));
     cp=dpknam->suffix+sizeof(SLISFX)-1;
     if (slstfil(cp,(CHAR *)rsptmp) == GMEOK) {
          rsp2read(dpknam,STGLEN,rsptmp,NULL);
          return;
     }
     rejectreq();
}

static VOID
wlstfil(                           /* write new distribution list file     */
struct saunam *dpknam,             /*   dpk name in use                    */
struct filinf *finfo)              /*   file dynapak info structure        */
{
     ASSERT(dpknam != NULL);
     ASSERT(finfo != NULL);
     ASSERT(sameto(SLISFX,dpknam->suffix));
     (VOID)finfo;
     if (!haskey(edstky)) {
          *(SHORT *)rsptmp=GMEACC;
          rsp2write(FALSE,sizeof(SHORT),rsptmp,shortFDA);
     }
     rqiptr->dpknam=*dpknam;
     stlcpy(rqiptr->u.uplinf.attpath,udlnam(),GCMAXPTH);
     ok2write(rqiptr->u.uplinf.attpath);
}

static VOID
wlstabt(VOID)                      /* upload of list aborted               */
{
     unlink(rqiptr->u.uplinf.attpath);
}

static VOID
wlstdone(VOID)                     /* upload of list complete, give to GME */
{
     SHORT rc;

     ASSERT(sameto(SLISFX,rqiptr->dpknam.suffix));
     rc=setslst(rqiptr->dpknam.suffix+sizeof(SLISFX)-1,rqiptr->u.uplinf.attpath);
     if (rc < GMEAGAIN) {
          unlink(rqiptr->u.uplinf.attpath);
     }
     rsp2write(rc > GMEAGAIN,sizeof(SHORT),&rc,shortFDA);
}

static VOID
crtslst(                           /* create new sysop distribution list   */
struct saunam *dpknam,             /*   dpk name in use                    */
UINT length,                       /*   length of dynapak                  */
struct newslst *newlst)            /*   new list structure                 */
{
     SHORT rc;
     CHAR *cp;
     CHAR tmpkey[KEYSIZ];

     if (length != sizeof(struct newslst)) {
          rsp2write(FALSE,0,NULL,NULL);
          return;
     }
     inigmerq(efwork);
     cp=dpknam->suffix+sizeof(CRLSFX)-1;
     b2ccpy(tmpkey,newlst->key,KEYSIZ);
     rc=newslst(efwork,cp,tmpkey,newlst->surchg);
     if (rc == GMEOK) {
          clsgmerq(efwork);
     }
     rsp2write(rc == GMEOK,sizeof(SHORT),&rc,shortFDA);
}

static VOID
adlst(                             /* add an entry to a server-side list   */
struct saunam *dpknam,             /*   dynapak name in use                */
CHAR *addr)                        /*   address to add                     */
{
     SHORT rc;
     CHAR *cp;

     inigmerq(efwork);
     cp=dpknam->suffix+sizeof(ADLSFX)-1;
     rc=edtslst(efwork,cp);
     if (rc == GMEOK) {
          rc=addslst(efwork,addr);
          if (rc == GMEOK) {
               clsgmerq(efwork);
          }
     }
     rsp2write(rc == GMEOK,sizeof(SHORT),&rc,shortFDA);
}

static VOID
fwdeml(                            /* forward an E-mail message            */
struct saunam *dpknam,             /*   dynapak name in use                */
CHAR *fwdinf)                      /*   address to fwd to/comments         */
{
     LONG msgid;

     if (!lsfxval((ULONG *)&msgid,&dpknam->suffix[sizeof(FWDEML)-1])) {
          rsp2write(FALSE,0,NULL,NULL);
          return;
     }
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,EMLID,msgid);
     csfwd(dpknam,fwdinf);
}

static VOID
cpyeml(                            /* copy an E-mail message               */
struct saunam *dpknam,             /*   dynapak name in use                */
CHAR *cpyinf)                      /*   address to copy to/comments        */
{
     LONG msgid;

     if (!lsfxval((ULONG *)&msgid,&dpknam->suffix[sizeof(CPYEML)-1])) {
          rsp2write(FALSE,0,NULL,NULL);
          return;
     }
     inigmerq(efwork);
     inormrd(efwork,usaptr->userid,EMLID,msgid);
     cscopy(dpknam,cpyinf);
}

static VOID
reg_dpk(                               /* register dynapaks for conversion */
VOID)
{
     register_dpkfda("GALEML","saf:"UPLATT,filinfFDA);
     register_dpkfda("GALEML","saf:"SLISFX,filinfFDA);
     register_dpkfda("GALEML","sa:"CRLSFX,newslstFDA);
     register_dpkfda("GALEML",SLFMTY,shortFDA);
     register_dpkfda("GALEML",SCLARPL,shortFDA);
     register_dpkfda("GALEML",EMLTAGS,longsFDA);
     register_dpkfda("GALEML","sa:"WRTNEW,newdpkFDA);
     register_dpkfda("GALEML","sa:"WRTRPL,newdpkFDA);
}
