/***************************************************************************
 *                                                                         *
 *   CSFAX.C                                                               *
 *                                                                         *
 *   Copyright (c) 1994-1995 Galacticomm, Inc.    All rights reserved.     *
 *                                                                         *
 *   This is the Fax/Online application agent.                             *
 *                                                                         *
 *                                                - Joe Delekto 10/30/95   *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "galfax.h"
#include "galfxc.h"
#include "galfap.h"

#define   FILREV "$Revision: 1 $"

#define   FAXAPN "GALFAP"          /* fax application appid                */

                                   /* fax/online c/s dynapak names         */
#define   FILVFY "filvfy "         /*   verify a filename existing on serve*/
#define   CRDCHK "crdchk"          /*   perform credit check for current fa*/
#define   FAXFNM "faxfnm "         /*   get fax filenames from the server  */
#define   SFAXKY "faxkey"          /*   see if user has master fax key     */
#define   FAXDAT "faxdat"          /*   complete fax data in dynapak       */
#define   FAXVEW "faxvew "         /*   fax online viewing dynapak         */
#define   FAXEDT "faxedt"          /*   edit fax information, info in dpak */
#define   FAXADD "faxadd"          /*   add fax to database, info in dpak  */
#define   FAXCHN "faxchn"          /*   get fax channels available dpak    */
#define   FAXRCV "faxrcv"          /*   receiving fax now, unsolicited     */
#define   FAXDEL "faxdel"          /*   delete fax from the database       */
#define   FAXRFS "faxrfs"          /*   fax file list refresh, unsolicited */
#define   FAXPNF "faxpnf"          /*   fax phone number information help  */
#define   FXVNFY "fxvnfy "         /*   fax viewing charges dynapak        */

                                   /* fax/online dynapak responses         */
#define   RSPOK  "ok"              /*   ok, write had no problems          */
#define   RSPERR "error"           /*   error, something went awry         */

                                   /* special args for std resp funcs      */
#define   NOMSG  0                 /*   no message for faxrsp()            */
#define   RAWMSG 1                 /*   do the prfmsg() for me             */
#define   CKDMSG 2                 /*   prfbuf already setup, (for args)   */

#define   NOCODE  0                /* no special code for faxrsp()         */

                                   /* special codes for RSPERR resp        */
#define   NONFATAL 0               /*   non-fatal error - client recovers  */
#define   FATAL    1               /*   fatal error - shutdown client      */

#define   UNSRFS "REFRESH"         /* request fax document list refresh    */

#define   dynpak(x) "sau:"x        /* convert to full dynapak name         */
#define   fdynpak(x) "sauf:"x      /* convert to full file dynapak name    */

#define   JNKLEN 50                /* size of junk buffer                  */
#define   NMPGLN 5                 /* max page length (digits)             */
#define   FTGLEN 2                 /* file tag length (1 character)        */

#define   PHONUM 0                 /* phone number item in faxdat dynapak  */
#define   TONAME 1                 /* to name item in faxdat dynapak       */
#define   FRNAME 2                 /* from name item in faxdat dynapak     */
#define   FTOPIC 3                 /* topic item in faxdat dynapak         */
#define   NUMPGS 4                 /* # of pages item in faxdat dynapak    */
#define   FNOTES 5                 /* text block of fax notes              */
#define   SOFFLS 6                 /* start of file list in faxdat dynapak */

#define   NODEFN 0                 /* no code defined for this node        */
#define   NOTYPE 1                 /* no type for this node path           */
#define   WHTMAK 2                 /* white make-up code identifier        */
#define   BLKMAK 3                 /* black make-up code identifier        */
#define   WHTTRM 4                 /* white terminate identifier           */
#define   BLKTRM 5                 /* black terminate identifier           */

#define   DOWHT  0                 /* start with white run-lengths         */
#define   DOBLK  1                 /* black run length state               */
#define   DOEOL  2                 /* wait for EOL state                   */

#define   MAKCNT 27                /* number of make-up codes              */
#define   TRMCNT 64                /* number of terminating codes          */

#define   OPNFNM 0                 /* file opened properly                 */
#define   BADFNM 1                 /* bad file name                        */

#define   PRCSIZ 1024              /* number of bytes in file to prcss     */

#define   EOPMRK 0xFF              /* end of fax page marker               */
#define   FAXZRO 0x00              /* zero, null value for fax byte        */
#define   FAXDLE 0x10              /* fax DLE character                    */
#define   FAXEOL 0x80              /* fax end of line character            */

#define   fapusr ((struct fapdef *)mrqptr)   /* current request memory     */
#define   faptmp ((struct fapdef *)vdatmp)   /* volatile fapdef structure  */

                                   /* special response handling macros     */
#define   cpyrsp(s) stlcpy(rsptmp,(s),MAXDPKV)    /* start new response str*/
#define   delimiter() stlcat(rsptmp,"\t",MAXDPKV) /* add delimiter to rsp  */
#define   catrsp(s) stlcat(rsptmp,(s),MAXDPKV)    /* add to response str   */

static INT *faxchreq;              /* request id array for fax chann.      */

static VOID faxread(INT direction,struct saunam *dpknam);
static VOID faxwrite(struct saunam *dpknam,USHORT length,VOID *value);
static VOID faxxdone(VOID);
static VOID faxabort(VOID);

struct agent faxagt={              /* agent information structure          */
     FAXAPN,                       /*   appid                              */
     faxread,                      /*   read-dynapak function pointer      */
     faxwrite,                     /*   write-dynapak function pointer     */
     faxxdone,                     /*   file xfer-done function pointer    */
     faxabort                      /*   abort-request function pointer     */
};

struct node {                      /* node structure for huffman tree      */
     UINT ctype;                   /*   type of code                       */
     UINT nodeval;                 /*   value to replace in decode         */
     struct node *left;            /*   pointer to left node               */
     struct node *right;           /*   pointer to right node              */
};

struct fapdef {                    /* fax application user vda structure   */
     struct faplst fapitm;         /*   individual fax document struct     */
     CHAR toname[NADSIZ];          /*   who this fax is "to"               */
     CHAR frname[NADSIZ];          /*   who this fax is "from"             */
     CHAR topic[FTPLEN];           /*   topic of this fax                  */
     INT channum;                  /*   saved channel number for abort     */
     FILE *faxfp;                  /*   fax input file                     */
     FILE *fwfp;                   /*   fax processed viewing file         */
     INT state;                    /*   drawing state                      */
     USHORT faxrow;                /*   current row, or scan line          */
     USHORT faxcol;                /*   current pel column                 */
     LONG lincnt;                  /*   line count for all black runlens   */
     GBOOL gotdle;                 /*   got dle in file while processing   */
     INT zercnt;                   /*   number of zeroes in file           */
     ULONG tchgpp;                 /*   total charge for viewing faxes     */
     struct node *curwht;          /*   current location in white tree     */
     struct node *curblk;          /*   current location in black tree     */
};

struct node *blkroot,              /* root to the black huffman tree       */
     *whtroot;                     /* root to the white huffman tree       */

static VOID readoc(VOID);
static VOID faxrsp(CHAR *resp,INT msg,INT msgnum,INT code);
static VOID faxrspu(CHAR *dpak,CHAR *resp,INT msg,INT msgnum,INT code);
static VOID faxbcst(CHAR *dpak,CHAR *resp);
static VOID faxacb(GBOOL ok);
static VOID ftree(struct node *root,USHORT *valary,INT arysiz,INT cval);
static struct node *nalloc(VOID);
VOID cyclefax(VOID);
static VOID prcbyt(CHAR rawbyt);
static VOID skpeol(VOID);

VOID
inicsfax(VOID)                     /* initialize fax/online agent          */
{
     INT loop;

     faxchreq=(INT *)alcmem(nterms*sizeof(INT));
     for (loop=0 ; loop < nterms ; loop++) {
          faxchreq[loop]=-1;
     }
     register_agent(&faxagt);
     dclmrq(sizeof(struct fapdef));
     blkroot=nalloc();
     whtroot=nalloc();
     ftree(whtroot,whtmak,MAKCNT,WHTMAK);
     ftree(whtroot,whttrm,TRMCNT,WHTTRM);
     ftree(blkroot,blkmak,MAKCNT,BLKMAK);
     ftree(blkroot,blktrm,TRMCNT,BLKTRM);
}

static VOID
faxread(                           /* read-dynapak handler                 */
INT direction,                     /*   read direction: 0=eq, 1=gt, -1=lt  */
struct saunam *dpknam)             /*   dynapak name to read               */
{
     INT i,pgloop;
     ULONG tcharge;
     USHORT numpag;
     CHAR *dpkstg,tmpfnm[GCMAXPTH];
     struct user *usp;


     if (!stdchk(faxaky)) {
          rejectreq();
          return;
     }
     dfaSetBlk(fapbb);
     setmbk(fapmb);
     dpkstg=cnvs2d(dpknam);
     if (sameto(dynpak(FAXFNM),dpkstg)) {
          stlcpy(fapusr->fapitm.docnam,skpwht(skpwrd(dpkstg)),DOCLEN);
          cycleme(readoc);
          readoc();
     }
     else if (direction == 0) {
          if (sameas(dynpak(SFAXKY),dpkstg)) {
               if (haskey(faxsky)) {
                    cpyrsp(RSPOK);
               }
               else {
                    cpyrsp(RSPERR);
               }
               rsp2read(dpknam,STGLEN,rsptmp,rtextFDA);
          }
          else if (sameas(dynpak(FAXCHN),dpkstg)) {
               *rsptmp='\0';
               for (i=0 ; i < nterms ; i++) {
                    usp=usroff(i);
                    if (faxcap[grpnum[i]] && usp->usrcls == VACANT
                     && usp->state == AWAITC && !(usp->flags&NOHDWE)) {
                         catrsp(spr("%d",channel[i]));
                         delimiter();
                    }
               }
               catrsp("0");
               rsp2read(dpknam,STGLEN,rsptmp,charsFDA);
          }
          else if (sameto(dynpak(FXVNFY),dpkstg)) {
               stlcpy(fapusr->fapitm.docnam,skpwrd(dpkstg)+1,DOCLEN);
               if (dfaAcqEQ(&fapusr->fapitm,fapusr->fapitm.docnam,0)) {
                    tcharge=fapusr->fapitm.numpps*chgpp;
                    cpyrsp(RSPOK);
                    delimiter();
                    prfmsg(VFWCHG,l2as(chgpp),spr("%hu",fapusr->fapitm.numpps),
                           l2as(tcharge));
                    stp4cs(prfbuf);
                    catrsp(prfbuf);
                    delimiter();
                    catrsp(l2as(tcharge));
               }
               else {
                    cpyrsp(RSPERR);
                    delimiter();
                    prfmsg(VDNFND);
                    stp4cs(prfbuf);
                    catrsp(prfbuf);
                    delimiter();
                    catrsp("0");
               }
               rsp2read(dpknam,STGLEN,rsptmp,rtextFDA);
          }
          else if (sameto(fdynpak(FAXVEW),dpkstg)) {
               stlcpy(fapusr->fapitm.docnam,skpwrd(dpkstg)+1,DOCLEN);
               if (dfaAcqEQ(&fapusr->fapitm,fapusr->fapitm.docnam,0)) {
                    tcharge=fapusr->fapitm.numpps*chgpp;
                    if (!(realcr ? rtstcrd(tcharge) : tstcrd(tcharge))) {
                         cpyrsp(RSPERR);
                         delimiter();
                         prfmsg(NECERR);
                         stp4cs(prfbuf);
                         catrsp(prfbuf);
                         delimiter();
                         catrsp("0");
                         rsp2read(dpknam,STGLEN,rsptmp,rtextFDA);
                         return;
                    }
                    fapusr->tchgpp=tcharge;
                    fapusr->state=DOWHT;
                    strcpy(tmpfnm,FAXDIR SLS "VIEWMXXXXXX");
                    if (mktemp(tmpfnm) == NULL) {
                         catastro("CAN'T FIND UNIQUE VIEW-FAX FILE NAME!");
                    }
                    if ((fapusr->faxfp=fopen(fapusr->fapitm.filnam,FOPRB))
                     == NULL) {
                         rejectreq();
                         return;
                    }
                    strcpy(fapusr->fapitm.filnam,tmpfnm);
                    if ((fapusr->fwfp=fopen(fapusr->fapitm.filnam,FOPWB))
                     == NULL) {
                         fclose(fapusr->faxfp);
                         fapusr->faxfp=NULL;
                         rejectreq();
                         return;
                    }
                    fapusr->gotdle=FALSE;
                    fapusr->faxrow=0;
                    fapusr->faxcol=0;
                    fapusr->fapitm.numpps=0;
                    fapusr->lincnt=0;
                    fapusr->curblk=blkroot;
                    fapusr->curwht=whtroot;
                    fread(&numpag,sizeof(USHORT),1,fapusr->faxfp);
                    fwrite(&numpag,sizeof(USHORT),1,fapusr->fwfp);
                    for (pgloop=0 ; pgloop < numpag ; pgloop++) {
                         fwrite(&fapusr->lincnt,sizeof(LONG),1,fapusr->fwfp);
                    }
                    cycleme(cyclefax);
               }
               else {
                    rejectreq();
               }
          }
          else {
               rejectreq();
          }
     }
}

static VOID
readoc(VOID)                       /* multi-directional document read      */
{
     GBOOL result;

     dfaSetBlk(fapbb);
     switch (rqdptr->direction) {
     case 0:
          result=dfaAcqEQ(&fapusr->fapitm,fapusr->fapitm.docnam,0);
          break;
     case 1:
          result=dfaAcqGT(&fapusr->fapitm,fapusr->fapitm.docnam,0);
          break;
     case -1:
          result=dfaAcqLT(&fapusr->fapitm,fapusr->fapitm.docnam,0);
          break;
     }
     if (result) {
          if (haskey(fapusr->fapitm.lock)) {
               cpyrsp(fapusr->fapitm.docnam);
               delimiter();
               catrsp(fapusr->fapitm.docdes);
               delimiter();
               catrsp(fapusr->fapitm.filnam);
               delimiter();
               catrsp(spr("%hu",fapusr->fapitm.numpps));
               if (haskey(faxsky)) {
                    delimiter();
                    catrsp(fapusr->fapitm.lock);
               }
               *namtmp=rqdptr->saunam;
               stlcpy(namtmp->suffix,FAXFNM,SFXSIZ);
               stlcat(namtmp->suffix,fapusr->fapitm.docnam,SFXSIZ);
               rsp2read(namtmp,STGLEN,rsptmp,charsFDA);
          }
          else if (rqdptr->direction == 0) {
               rejectreq();
          }
     }
     else {
          rejectreq();
     }
     dfaRstBlk();
}

static VOID
faxwrite(                          /* write-dynapak handler                */
struct saunam *dpknam,             /*   dynapak name to write              */
USHORT length,                     /*   length of dynapak value            */
VOID *value)                       /*   dynapak value to write             */
{
     static CHAR tmppho[FAXPSZ];
     CHAR *dpkstg,*faxdat,*faxnte,filtag[FTGLEN];
     INT rcvchan,channum;
     UINT numitems,filelp,npages,ntpgs;
     ULONG tcharge;
     FILE *fp;

     (VOID)length;
     if (!stdchk(faxaky)) {
          rejectreq();
          return;
     }
     dfaSetBlk(fapbb);
     setmbk(fapmb);
     dpkstg=cnvs2d(dpknam);
     faxdat=(CHAR *)value;
     if (sameto(dynpak(FILVFY),dpkstg)) {
          if (!haskey(faxsky)) {
               faxrsp(RSPERR,RAWMSG,NAAERR,NONFATAL);
               return;
          }
          stlcpy(fapusr->fapitm.filnam,fnmcse(skpwrd(dpkstg)+1),GCSTRPTH);
          cntdir(fapusr->fapitm.filnam);
          if (numfils != 1L) {
               faxrsp(RSPERR,RAWMSG,NOSERR,NONFATAL);
          }
          else {
               faxrsp(RSPOK,NOMSG,NOMSG,txtpps(fapusr->fapitm.filnam));
          }
     }
     else if (sameas(dynpak(FAXPNF),dpkstg)) {
          faxrsp(RSPOK,RAWMSG,FXPINF,NOCODE);
     }
     else if (sameas(dynpak(FAXDAT),dpkstg)) {
          numitems=itemcnt(faxdat);
          stlcpy(tmppho,itemidx(faxdat,PHONUM),FAXPSZ);
          stppho(tmppho);
          if (tmppho[0] == '\0') {
               faxrsp(RSPERR,RAWMSG,NVPERR,NONFATAL);
               return;
          }
          stlcpy(fapusr->toname,itemidx(faxdat,TONAME),NADSIZ);
          stlcpy(fapusr->frname,itemidx(faxdat,FRNAME),NADSIZ);
          stlcpy(fapusr->topic,itemidx(faxdat,FTOPIC),FTPLEN);
          npages=atoi(itemidx(faxdat,NUMPGS));
          faxnte=itemidx(faxdat,FNOTES);
          if (faxnte[0] != '\0') {
               strcpy(fapusr->fapitm.filnam,FAXDIR SLS "TEXTMXXXXXX");
               if (mktemp(fapusr->fapitm.filnam) == NULL) {
                    catastro("CAN'T FIND UNIQUE TEXT FILE NAME!");
               }
               puttxt(xltnln(faxnte),"",fapusr->fapitm.filnam);
          }
          else {
               fapusr->fapitm.filnam[0]='\0';
          }
          tcharge=totchg(baschg(tmppho),addchg(tmppho),npages);
          fixpho(tmppho);
          if (!(realcr ? rtstcrd(tcharge) : tstcrd(tcharge))) {
               faxrsp(RSPERR,RAWMSG,NECERR,NONFATAL);
               return;
          }
          if (realcr) {
               rdedcrd(tcharge,1);
          }
          else {
               dedcrd(tcharge,1);
          }
          begin_submit(usaptr->userid,tcharge);
          submit_cover(npages,fapusr->toname,fapusr->frname,fapusr->topic);
          if (fapusr->fapitm.filnam[0] != '\0') {
               submit_text(fapusr->fapitm.filnam,TXTFIL|DELFIL);
          }
          if (numitems >= SOFFLS+1) {
               for (filelp=SOFFLS ; filelp < numitems-1 ; filelp+=2) {
                    stlcpy(fapusr->fapitm.docnam,itemidx(faxdat,filelp),DOCLEN);
                    stlcpy(filtag,itemidx(faxdat,filelp+1),FTGLEN);
                    switch (filtag[0]) {
                    case '$':
                         if (haskey(faxsky)) {
                              stlcpy(fapusr->fapitm.filnam,
                                     itemidx(faxdat,filelp),GCSTRPTH);
                              submit_text(fapusr->fapitm.filnam,TXTFIL);
                         }
                         break;
                    case '#':
                         stlcpy(fapusr->fapitm.docnam,itemidx(faxdat,filelp),
                                DOCLEN);
                         if (dfaAcqEQ(&fapusr->fapitm,fapusr->fapitm.docnam,0)) {
                              if (haskey(fapusr->fapitm.lock)) {
                                   submit_fax(fapusr->fapitm.filnam,FAXFIL);
                              }
                         }
                         break;
                    }
               }
          }
          prfmsg(FXSBOK,l2as(end_submit(tmppho,"")));
          faxrsp(RSPOK,CKDMSG,NOMSG,NOCODE);
     }
     else if (sameas(dynpak(CRDCHK),dpkstg)) {
          stlcpy(tmppho,itemidx(faxdat,0),FAXPSZ);
          stppho(tmppho);
          npages=atoi(itemidx(faxdat,1));
          faxnte=itemidx(faxdat,2);
          if (tmppho[0] == '\0') {
               faxrsp(RSPERR,RAWMSG,NVPERR,NONFATAL);
          }
          else if (!valpho(tmppho)) {
               faxrsp(RSPERR,RAWMSG,NVPERR,NONFATAL);
          }
          else if (!accpho(tmppho,usaptr->userid)) {
               faxrsp(RSPERR,RAWMSG,NAPERR,NONFATAL);
          }
          else {
               if (faxnte[0] != '\0') {
                    strcpy(fapusr->fapitm.filnam,FAXDIR SLS "TEXTMXXXXXX");
                    if (mktemp(fapusr->fapitm.filnam) == NULL) {
                         catastro("CAN'T FIND UNIQUE TEXT FILE NAME!");
                    }
                    ntpgs=puttxt(xltnln(faxnte),"",fapusr->fapitm.filnam);
                    npages+=ntpgs;
                    unlink(fapusr->fapitm.filnam);
               }
               else {
                    ntpgs=0;
               }
               tcharge=totchg(baschg(tmppho),addchg(tmppho),npages);
               if (tcharge != 0) {
                    prfmsg(FXCRCH,l2as(baschg(tmppho)),l2as(addchg(tmppho)),
                           spr("%u",npages),l2as(tcharge));
                    faxrsp(RSPOK,CKDMSG,NOMSG,ntpgs);
               }
               else {
                    faxrsp(RSPOK,RAWMSG,FXNOCH,ntpgs);
               }
          }
     }
     else if (sameas(dynpak(FAXEDT),dpkstg)) {
          if (!haskey(faxsky)) {
               faxrsp(RSPERR,RAWMSG,NAEERR,NONFATAL);
               return;
          }
          stlcpy(faptmp->fapitm.docnam,strupr(itemidx(faxdat,0)),DOCLEN);
          if (dfaAcqEQ(&fapusr->fapitm,faptmp->fapitm.docnam,0)) {
               stlcpy(fapusr->fapitm.docnam,strupr(itemidx(faxdat,1)),DOCLEN);
               stlcpy(fapusr->fapitm.docdes,itemidx(faxdat,2),DESLEN);
               stlcpy(fapusr->fapitm.lock,strupr(itemidx(faxdat,3)),KEYSIZ);
               if (fapusr->fapitm.docnam[0] == '\0') {
                    faxrsp(RSPERR,RAWMSG,NOGDNM,NONFATAL);
                    return;
               }
               if (dfaUpdateDup(&fapusr->fapitm)) {
                    faxrsp(RSPOK,RAWMSG,FEDTSU,NOCODE);
                    faxbcst(FAXRFS,UNSRFS);
               }
               else {
                    faxrsp(RSPERR,RAWMSG,FDCDUP,NONFATAL);
               }
          }
          else {
               faxrsp(RSPERR,RAWMSG,FDOCGN,NONFATAL);
          }
          return;
     }
     else if (sameas(dynpak(FAXDEL),dpkstg)) {
          if (!haskey(faxsky)) {
               faxrsp(RSPERR,RAWMSG,NAEERR,NONFATAL);
               return;
          }
          stlcpy(fapusr->fapitm.docnam,strupr(itemidx(faxdat,0)),DOCLEN);
          if (fapusr->fapitm.docnam[0] == '\0') {
               faxrsp(RSPERR,RAWMSG,NOGDNM,NONFATAL);
               return;
          }
          if (dfaAcqEQ(&fapusr->fapitm,fapusr->fapitm.docnam,0)) {
               dfaDelete();
               faxrsp(RSPOK,RAWMSG,FDELSU,NOCODE);
               faxbcst(FAXRFS,UNSRFS);
          }
          else {
               faxrsp(RSPERR,RAWMSG,FDOCGN,NONFATAL);
          }
          return;
     }
     else if (sameas(dynpak(FAXADD),dpkstg)) {
          if (!haskey(faxsky)) {
               faxrsp(RSPERR,RAWMSG,NAFERR,NONFATAL);
               return;
          }
          stlcpy(fapusr->fapitm.docnam,strupr(itemidx(faxdat,0)),DOCLEN);
          stlcpy(fapusr->fapitm.filnam,fnmcse(itemidx(faxdat,1)),GCSTRPTH);
          stlcpy(fapusr->fapitm.docdes,itemidx(faxdat,2),DESLEN);
          stlcpy(fapusr->fapitm.lock,strupr(itemidx(faxdat,3)),KEYSIZ);
          rcvchan=atoi(itemidx(faxdat,4));
          if (fapusr->fapitm.docnam[0] == '\0') {
               faxrsp(RSPERR,RAWMSG,NOGDNM,NONFATAL);
               return;
          }
          if (rcvchan == 0) {
               if ((fapusr->fapitm.numpps=faxpps(fapusr->fapitm.filnam)) == -1) {
                    faxrsp(RSPERR,RAWMSG,BMFFNM,NONFATAL);
                    return;
               }
               if (dfaInsertDup(&fapusr->fapitm)) {
                    faxrsp(RSPOK,RAWMSG,FADDSU,NOCODE);
                    faxbcst(FAXRFS,UNSRFS);
               }
               else {
                    faxrsp(RSPERR,RAWMSG,UADDOC,NONFATAL);
               }
               return;
          }
          else {
               if ((channum=usridx(rcvchan)) == -1) {
                    faxrsp(RSPERR,RAWMSG,INVCHN,NONFATAL);
                    return;
               }
               if ((fp=fopen(fapusr->fapitm.filnam,FOPWB)) == NULL) {
                    faxrsp(RSPERR,RAWMSG,NOCFIL,NONFATAL);
                    return;
               }
               else {
                    fclose(fp);
                    unlink(fapusr->fapitm.filnam);
               }
               if (faxchreq[channum] == -1) {
                    fapusr->channum=channum;
                    faxchreq[channum]=greqid;
               }
               else {
                    faxrsp(RSPERR,RAWMSG,FCHBSY,NONFATAL);
                    return;
               }
               if (receive_fax(channum,fapusr->fapitm.filnam,faxacb)) {
                    prfmsg(FRCRDY,rcvchan);
                    faxrspu(dynpak(FAXRCV),RSPOK,CKDMSG,NOMSG,NOCODE);
               }
               else {
                    faxchreq[channum]=-1;
                    faxrsp(RSPERR,RAWMSG,FCHBSY,NONFATAL);
               }
          }
     }
     else {
          rejectreq();
     }
}

static VOID
faxxdone(VOID)                     /* file transfer-done handler           */
{
     unlink(fapusr->fapitm.filnam);
     if (fapusr->tchgpp > 0) {
          if (realcr) {
               rdedcrd(fapusr->tchgpp,1);
          }
          else {
               dedcrd(fapusr->tchgpp,1);
          }
          setmbk(fapmb);
          prfmsg(VFWCHG,l2as(chgpp),spr("%hu",fapusr->fapitm.numpps),
                 l2as(fapusr->tchgpp));
          faxrspu(dynpak(FXVNFY),RSPOK,CKDMSG,NOMSG,NOCODE);
     }
}

static VOID
faxabort(VOID)                     /* abort-request handler                */
{
     INT savrid;

     savrid=greqid;
     if (faxchreq[fapusr->channum] != -1) {
          abort_receive(fapusr->channum);
          if (ismyreq(savrid,FAXAPN)) {
               curreq(savrid);
          }
     }
     faxchreq[fapusr->channum]=-1;
     if (fapusr->faxfp != NULL) {
          fclose(fapusr->faxfp);
          fapusr->faxfp=NULL;
     }
     if (fapusr->fwfp != NULL) {
          fclose(fapusr->fwfp);
          fapusr->fwfp=NULL;
     }
     if (sameto(FAXVEW,rqdptr->saunam.suffix)) {
          unlink(fapusr->fapitm.filnam);
     }
     cycleme(NULL);
}

static VOID
faxrsp(                            /* standard reply to writes             */
CHAR *resp,                        /*   response string "ok", "error", etc.*/
INT msg,                           /*   msg (0-none,1-prfmsg,2-prfbuf)     */
INT msgnum,                        /*   prfmsg() message pointer           */
INT code)                          /*   special code to attach to response */
{
     setmbk(fapmb);
     switch(msg) {
     case NOMSG:
          *prfbuf='\0';
          break;
     case RAWMSG:
          prfmsg(msgnum);
     case CKDMSG:                  /* fall through to prepare for cs resp  */
          stp4cs(prfbuf);
          break;
     }
     cpyrsp(resp);
     delimiter();
     catrsp(prfbuf);
     delimiter();
     catrsp(spr("%d",code));
     rstmbk();
     rsp2write(TRUE,STGLEN,rsptmp,rtextFDA);
}

static VOID
faxrspu(                           /* send unsolicited dynapaks            */
CHAR *dpak,                        /*   developer form dynapak string      */
CHAR *resp,                        /*   response string "ok", "error", etc.*/
INT msg,                           /*   msg (0-none,1-prfmsg,2-prfbuf)     */
INT msgnum,                        /*   prfmsg() message pointer           */
INT code)                          /*   special code to attach to response */
{
     struct saunam rfdpk;

     if (cnvd2s(dpak,&rfdpk)) {
          setmbk(fapmb);
          switch(msg) {
          case NOMSG:
               *prfbuf=0;
               break;
          case RAWMSG:
               prfmsg(msgnum);
          case CKDMSG:                  /* fall through to prepare for C/S */
               stp4cs(prfbuf);
               break;
          }
          cpyrsp(resp);
          delimiter();
          catrsp(prfbuf);
          delimiter();
          catrsp(spr("%d",code));
          rstmbk();
          if (qroom(usrnum,NORMAL)) {
               senddpk(usrnum,FAXAPN,NORMAL,&rfdpk,STGLEN,rsptmp,rtextFDA);
          }
     }
}

static VOID
faxbcst(                           /* broadcast unsolc. to all C/S ppl     */
CHAR *dpak,                        /*   developer dynapak string           */
CHAR *resp)                        /*   response to pass along             */
{
     struct saunam rfdpk;
     INT i;

     setmem(&rfdpk,sizeof(struct saunam),0);
     rfdpk.usrid[0]='\0';
     stlcpy(rfdpk.sysid,msysid,SIDSIZ);
     stlcpy(rfdpk.appid,FAXAPN,AIDSIZ);
     stlcpy(rfdpk.suffix,dpak,SFXSIZ);
     for (i=0 ; i < nterms ; i++) {
          if (usroff(i)->flags&ISGCSU) {
               if (qroom(i,NORMAL)) {
                    senddpk(i,FAXAPN,NORMAL,&rfdpk,STGLEN,resp,charsFDA);
               }
          }
     }
}

static VOID
faxacb(                            /* receive fax whndun() handler         */
GBOOL ok)                          /*   fax received ok?  yes/no           */
{
     INT savusr;

     if (faxchreq[usrnum] == -1) {
          return;
     }
     savusr=usrnum;
     curreq(faxchreq[usrnum]);
     setmbk(fapmb);
     dfaSetBlk(fapbb);
     faxchreq[savusr]=-1;
     if (ok) {
          fapusr->fapitm.numpps=faxpps(fapusr->fapitm.filnam);
          if (dfaInsertDup(&fapusr->fapitm)) {
               faxrsp(RSPOK,RAWMSG,FADDSU,NOCODE);
               faxbcst(FAXRFS,UNSRFS);
          }
          else {
               faxrsp(RSPERR,RAWMSG,UADDOC,NONFATAL);
          }
     }
     else {
          faxrsp(RSPERR,RAWMSG,RCFERR,NONFATAL);
     }
     curusr(savusr);
     rstmbk();
     dfaRstBlk();
}

VOID
cyclefax(VOID)                     /* cycle based view file processing     */
{
     INT bytdon;
     CHAR faxbyt;

     bytdon=0;
     while (!feof(fapusr->faxfp) && (bytdon < PRCSIZ)) {
          faxbyt=fgetc(fapusr->faxfp);
          bytdon++;
          if (fapusr->gotdle) {
               fapusr->gotdle=FALSE;
               switch (faxbyt) {
               case FAXDLE:
                    prcbyt(revtbl[FAXDLE]);
                    break;
               case EOPMRK:
                    fseek(fapusr->fwfp,
                          (LONG)((fapusr->fapitm.numpps*sizeof(LONG))+
                                 sizeof(USHORT)),SEEK_SET);
                    fwrite(&fapusr->lincnt,sizeof(LONG),1,fapusr->fwfp);
                    fseek(fapusr->fwfp,0L,SEEK_END);
                    fapusr->faxrow=0;
                    fapusr->faxcol=0;
                    fapusr->lincnt=0;
                    fapusr->fapitm.numpps++;
                    break;
               default:
                    break;
               }
          }
          else {
               switch (faxbyt) {
               case FAXZRO:
                    prcbyt(revtbl[faxbyt]);
                    fapusr->zercnt++;
                    break;
               case FAXDLE:
                    fapusr->zercnt=0;
                    fapusr->gotdle=TRUE;
                    break;
               case FAXEOL:
                    if (fapusr->zercnt) {
                         fapusr->zercnt=0;
                         fapusr->faxcol=0;
                         fapusr->faxrow++;
                         skpeol();
                         fapusr->state=DOWHT;
                    }
                    else {
                         prcbyt(revtbl[FAXEOL]);
                    }
                    break;
               default:
                    fapusr->zercnt=0;
                    prcbyt(revtbl[faxbyt]);
                    break;
               }
          }
     }
     if (feof(fapusr->faxfp)) {
          fseek(fapusr->fwfp,
                (LONG)((fapusr->fapitm.numpps*sizeof(LONG))+sizeof(USHORT)),
                SEEK_SET);
          fwrite(&fapusr->lincnt,sizeof(LONG),1,fapusr->fwfp);
          fclose(fapusr->fwfp);
          fclose(fapusr->faxfp);
          fapusr->fwfp=fapusr->faxfp=NULL;
          cycleme(NULL);
          cpyrsp(fapusr->fapitm.filnam);
          rsp2read(NULL,STGLEN,rsptmp,charsFDA);
     }
}

static VOID
prcbyt(                            /* process a raw T.4 format byte        */
CHAR rawbyt)
{
     USHORT bitlp,onbit,byt,offset,runtyp;

     byt=(USHORT)rawbyt;
     byt<<=8;
     for (bitlp=0 ; bitlp < 8 ; bitlp++) {
          runtyp=NODEFN;
          onbit=(byt&0x8000)>>15;
          switch (fapusr->state) {
          case DOWHT:
               if ((onbit ? fapusr->curwht->right : fapusr->curwht->left)
                != NULL) {
                    fapusr->curwht=(onbit ? fapusr->curwht->right
                                          : fapusr->curwht->left);
                    byt<<=1;
               }
               else {
                    bitlp--;
                    runtyp=fapusr->curwht->ctype;
               }
               if (runtyp != NODEFN) {
                    if (runtyp != NOTYPE) {
                         fapusr->faxcol+=fapusr->curwht->nodeval;
                         if (runtyp == WHTTRM) {
                              fapusr->state=DOBLK;
                         }
                    }
                    else {
                         fapusr->state=DOEOL;
                    }
                    fapusr->curwht=whtroot;
               }
               break;
          case DOBLK:
               if ((onbit ? fapusr->curblk->right : fapusr->curblk->left)
                != NULL) {
                    fapusr->curblk=(onbit ? fapusr->curblk->right
                                          : fapusr->curblk->left);
                    byt<<=1;
               }
               else {
                    bitlp--;
                    runtyp=fapusr->curblk->ctype;
               }
               if (runtyp != NODEFN) {
                    if (runtyp != NOTYPE) {
                         offset=fapusr->faxcol;
                         fapusr->faxcol+=fapusr->curblk->nodeval-1;
                         fwrite(&fapusr->faxrow,sizeof(USHORT),1,fapusr->fwfp);
                         fwrite(&offset,sizeof(USHORT),1,fapusr->fwfp);
                         fwrite(&fapusr->faxcol,sizeof(USHORT),1,fapusr->fwfp);
                         fapusr->lincnt++;
                         fapusr->faxcol++;
                         if (runtyp == BLKTRM) {
                              fapusr->state=DOWHT;
                         }
                    }
                    else {
                         fapusr->state=DOEOL;
                    }
                    fapusr->curblk=blkroot;
               }
               break;
          case DOEOL:
               fapusr->curblk=blkroot;
               fapusr->curwht=whtroot;
               return;
          }
     }
}

static VOID
skpeol(VOID)                       /* skip the next scan line in file      */
{
     CHAR c;
     INT zercnt=0;
     GBOOL gotdle=FALSE;

     while (!feof(fapusr->faxfp)) {
          if ((c=fgetc(fapusr->faxfp)) == FAXDLE && !gotdle) {
               gotdle=TRUE;
          }
          else if (gotdle && c == EOPMRK) {
               fseek(fapusr->faxfp,-2L,SEEK_CUR);
               break;
          }
          else if (gotdle) {
               gotdle=FALSE;
          }
          if (c == '\0') {
               zercnt++;
          }
          else if (c == 128 && zercnt > 0) {
               break;
          }
          else {
               zercnt=0;
          }
     }
}

static VOID
ftree(                             /* fill a binary tree with huffman data */
struct node *root,                 /*   root node for this tree            */
USHORT *valary,                    /*   huffman code strings (hex)         */
INT arysiz,                        /*   number of code strings             */
INT cval)                          /*   white, black code strings          */
{
     struct node *tptr;
     USHORT data,bitcnt;
     INT loop,bloop;

     for (loop=0 ; loop < arysiz ; loop++) {
          data=*valary++;
          bitcnt=*valary++;
          tptr=root;
          for (bloop=0 ; bloop < bitcnt ; bloop++) {
               if ((data&0x8000)>>15) {
                    if (tptr->right == NULL) {
                         tptr->right=nalloc();
                    }
                    tptr=tptr->right;
               }
               else {
                    if (tptr->left == NULL) {
                         tptr->left=nalloc();
                    }
                    tptr=tptr->left;
               }
               data<<=1;
          }
          tptr->ctype=cval;
          if (arysiz == TRMCNT) {
               tptr->nodeval=loop;
          }
          else {
               tptr->nodeval=(64*(loop+1));
          }
     }
}

static struct node *               /*   return a pointer to new node       */
nalloc(VOID)                       /* allocate a new node in the tree      */
{
     struct node *cptr;

     cptr=(struct node *)alcmem(sizeof(struct node));
     cptr->left=NULL;
     cptr->right=NULL;
     cptr->ctype=NOTYPE;
     cptr->nodeval=0;
     return(cptr);
}
