/***************************************************************************
 *                                                                         *
 *   WLMSTORU.C                                                            *
 *                                                                         *
 *   Copyright (c) 1997 Galacticomm, Inc.         All Rights Reserved.     *
 *                                                                         *
 *   Worldlink Messaging Storage Utilities.                                *
 *                                                                         *
 *                                            - J. Alvrus   1/14/97        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "wlmstoru.h"

#define FILREV "$Revision: 1.3 $"

CHAR *mailRoot;                    /* root directory for mail processing   */
CHAR *outDir;                      /* outbound files directory             */
CHAR *inDir;                       /* inbound files directory              */
CHAR *tmpDir;                      /* temporary files directory            */

static INT lastErr;                /* last error code                      */
static ULONG lastIdx;              /* index of last message read/written   */

static FILE *storOpenFile(STORFILE *sfp);
static size_t storReadMsgUtil(FILE *fp,ULONG pos,struct storMsgHdr *hdr,
                              VOID *dst,size_t len);
static GBOOL skipBadMsg(FILE *fp,STORFILE *sfp);
static GBOOL checkStorHdr(const struct storFileHdr *hdr);
static GBOOL checkStorVer(const CHAR *ver);
static GBOOL checkMsgHdr(const struct storMsgHdr *hdr);

STORFILE *                         /*   returns pointer to created file    */
storCreate(                        /* create a store file                  */
const CHAR *fileName,              /*   file to create                     */
ULONG startIdx)                    /*   initial value for message index    */
{
     STORFILE *sfp;
     FILE *fp;

     lastErr=STER_MEM;
     if ((sfp=malloc(sizeof(struct storFile))) != NULL) {
          memset(sfp,0,sizeof(struct storFile));
          stlcpy(sfp->filnam,fileName,GCMAXPTH);
          if ((fp=fopen(sfp->filnam,FOPWB)) == NULL) {
               lastErr=STER_IO;
               free(sfp);
               return(NULL);
          }
          memcpy(sfp->filhdr.id,STORID,sizeof(sfp->filhdr.id));
          memcpy(sfp->filhdr.ver,STORVER,sizeof(sfp->filhdr.ver));
          sfp->filhdr.ctrlz=CTRLZ;
          sfp->filhdr.loidx=startIdx;
          sfp->filhdr.hiidx=WLMNOIDX;
          if (fwrite(&sfp->filhdr,1,sizeof(struct storFileHdr),fp)
           != sizeof(struct storFileHdr)) {
               lastErr=STER_IO;
               fclose(fp);
               free(sfp);
               return(NULL);
          }
          fclose(fp);
          lastErr=STER_NONE;
     }
     return(sfp);
}

STORFILE *                         /*   returns pointer to opened file     */
storOpen(                          /* open a store file                    */
const CHAR *fileName)              /*   file to open                       */
{
     STORFILE *sfp;
     FILE *fp;

     lastErr=STER_MEM;
     if ((sfp=malloc(sizeof(struct storFile))) != NULL) {
          memset(sfp,0,sizeof(struct storFile));
          stlcpy(sfp->filnam,fileName,GCMAXPTH);
          if ((fp=storOpenFile(sfp)) == NULL) {
               free(sfp);
               return(NULL);
          }
          fclose(fp);
          lastErr=STER_NONE;
     }
     return(sfp);
}

VOID
storClose(                         /* close a store file                   */
STORFILE *sfp)                     /*   store file to close                */
{
     lastErr=STER_NONE;
     free(sfp);
}

GBOOL
storAddMsg(                        /* add a message to a store file        */
STORFILE *sfp,                     /*   store file to add to               */
const VOID *buf,                   /*   buffer containing contents         */
size_t len)                        /*   length of contents                 */
{
     struct storMsgHdr mhdr;
     GBOOL flg;
     FILE *fp;

     if ((fp=storOpenFile(sfp)) != NULL) {
          memset(&mhdr,0,sizeof(struct storMsgHdr));
          memcpy(mhdr.sig,WLMHDRSIG,sizeof(mhdr.sig));
          lastIdx=mhdr.idx=sfp->filhdr.hiidx == WLMNOIDX
                         ? sfp->filhdr.hiidx=sfp->filhdr.loidx
                         : ++sfp->filhdr.hiidx;
          mhdr.ndx=~mhdr.idx;
          mhdr.len=(ULONG)len;
          fseek(fp,0,SEEK_SET);
          fwrite(&sfp->filhdr,1,sizeof(struct storFileHdr),fp);
          fseek(fp,0,SEEK_END);
          flg=(fwrite(&mhdr,1,sizeof(struct storMsgHdr),fp)
            == sizeof(struct storMsgHdr) && fwrite(buf,1,len,fp) == len);
          lastErr=flg ? STER_NONE : STER_IO;
          fclose(fp);
          return(flg);
     }
     return(FALSE);
}

size_t                             /*   returns # bytes read or 0 if EOF   */
storNextMsg(                       /* read next message from store         */
STORFILE *sfp,                     /*   store file to read from            */
VOID *dst,                         /*   buffer to read into                */
size_t len)                        /*   length of buffer                   */
{
     FILE *fp;
     size_t retsiz;
     GBOOL cont;

     if ((fp=storOpenFile(sfp)) == NULL) {
          return(FALSE);
     }
     do {
          if (sfp->curpos == 0) {
               sfp->curpos=sizeof(struct storFileHdr);
          }
          else {
               sfp->curpos+=sizeof(struct storMsgHdr)+sfp->msghdr.len;
          }
          if (fseek(fp,sfp->curpos,SEEK_SET) != 0) {
               lastErr=STER_IO;
               fclose(fp);
               return(0);
          }
          if (fread(&sfp->msghdr,1,sizeof(struct storMsgHdr),fp)
           != sizeof(struct storMsgHdr)) {
               lastErr=STER_EOF;
               fclose(fp);
               return(0);
          }
          if (checkMsgHdr(&sfp->msghdr) || skipBadMsg(fp,sfp)) {
               cont=(GBOOL)((sfp->msghdr.flg&WLMF_DEL) != 0);
          }
          else {
               lastErr=STER_BADFIL;
               fclose(fp);
               return(0);
          }
     } while (cont);
     retsiz=storReadMsgUtil(fp,sfp->curpos,&sfp->msghdr,dst,len);
     fclose(fp);
     return(retsiz);
}

size_t                             /*   returns # bytes read or 0 if error */
storReadMsg(                       /* read current message from store      */
STORFILE *sfp,                     /*   store file to read from            */
VOID *dst,                         /*   buffer to read into                */
size_t len)                        /*   length of buffer                   */
{
     FILE *fp;
     size_t retsiz;

     if ((fp=storOpenFile(sfp)) != NULL) {
          retsiz=storReadMsgUtil(fp,sfp->curpos,&sfp->msghdr,dst,len);
          fclose(fp);
          if (retsiz != 0) {
               if (checkMsgHdr(&sfp->msghdr)) {
                    lastErr=STER_NONE;
                    return(retsiz);
               }
               lastErr=STER_BADFIL;
          }
     }
     return(0);
}

GBOOL                              /*   returns TRUE if successful         */
storDelMsg(                        /* mark current message deleted         */
STORFILE *sfp)                     /*   store file in use                  */
{
     GBOOL retval;
     FILE *fp;

     if (sfp->curpos == 0 || sfp->msghdr.len == 0) {
          lastErr=STER_BADPOS;
          return(FALSE);
     }
     if ((fp=storOpenFile(sfp)) == NULL) {
          return(FALSE);
     }
     lastErr=STER_IO;
     if (fseek(fp,sfp->curpos,SEEK_SET) != 0) {
          fclose(fp);
          return(FALSE);
     }
     sfp->msghdr.flg|=WLMF_DEL;
     retval=fwrite(&sfp->msghdr,1,sizeof(struct storMsgHdr),fp)
         == sizeof(struct storMsgHdr);
     if (retval) {
          lastErr=STER_NONE;
          lastIdx=sfp->msghdr.idx;
     }
     fclose(fp);
     return(retval);
}

INT
storLastErr(VOID)                  /* get last error code                  */
{
     return(lastErr);
}

ULONG
storLastIdx(VOID)                  /* get index of last msg added to a file*/
{
     return(lastIdx);
}

static FILE *                      /*   returns NULL if failure            */
storOpenFile(                      /* open file associated with store      */
STORFILE *sfp)                     /*   store file in use                  */
{
     FILE *fp;

     lastErr=STER_IO;
     if ((fp=fopen(sfp->filnam,FOPRWB)) != NULL) {
          if (fread(&sfp->filhdr,1,sizeof(struct storFileHdr),fp)
           != sizeof(struct storFileHdr)) {
               fclose(fp);
               fp=NULL;
          }
          else if (checkStorHdr(&sfp->filhdr)) {
               lastErr=STER_NONE;
          }
          else {
               lastErr=STER_BADFIL;
               fclose(fp);
               fp=NULL;
          }
     }
     return(fp);
}

static size_t                      /*   returns # bytes read or 0 if error */
storReadMsgUtil(                   /* read current message utility         */
FILE *fp,                          /*   file to read from                  */
ULONG pos,                         /*   position in file to read from      */
struct storMsgHdr *hdr,            /*   header for current message         */
VOID *dst,                         /*   buffer to read into                */
size_t len)                        /*   length of buffer                   */
{
     size_t rdlen,retval;

     if (pos == 0 || hdr->len == 0) {
          lastErr=STER_BADPOS;
          return(FALSE);
     }
     lastErr=STER_IO;
     if (fseek(fp,pos+sizeof(struct storMsgHdr),SEEK_SET) != 0) {
          return(0);
     }
     rdlen=min((size_t)hdr->len,len);
     retval=fread(dst,1,rdlen,fp);
     retval=retval > rdlen ? 0 : retval;
     if (retval != 0) {
          lastErr=STER_NONE;
          lastIdx=hdr->idx;
     }
     return(retval);
}

static GBOOL                       /*   returns TRUE if ok part found      */
skipBadMsg(                        /* skip over corrupted part of file     */
FILE *fp,                          /*   file to read from                  */
STORFILE *sfp)                     /*   store file in use                  */
{
     INT b;
     UCHAR *bp;
     struct storMsgHdr hdr;

     bp=(UCHAR *)&hdr;
     memcpy(&hdr,&sfp->msghdr,sizeof(struct storMsgHdr));
     do {
          if ((b=fgetc(fp)) == EOF) {
               return(FALSE);
          }
          ++sfp->curpos;
          memmove(bp,bp+1,sizeof(struct storMsgHdr)-1);
          bp[sizeof(struct storMsgHdr)-1]=b;
     } while (!checkMsgHdr(&hdr));
     memcpy(&sfp->msghdr,&hdr,sizeof(struct storMsgHdr));
     return(TRUE);
}

static GBOOL                       /*   returns TRUE if header OK          */
checkStorHdr(                      /* check for valid store header         */
const struct storFileHdr *hdr)     /*   header to check                    */
{
     return(memcmp(hdr->id,STORID,CSTRLEN(STORID)) == 0
         && checkStorVer(hdr->ver) && hdr->ctrlz == CTRLZ);
}

static GBOOL                       /*   returns TRUE if version OK         */
checkStorVer(                      /* check for supported version          */
const CHAR *ver)                   /*   version string                     */
{
     return(memcmp(ver,STORVER,CSTRLEN(STORVER)) == 0);
}

static GBOOL                       /*   returns TRUE if header OK          */
checkMsgHdr(                       /* check for valid message header       */
const struct storMsgHdr *hdr)      /*   header to check                    */
{
     return(memcmp(hdr->sig,WLMHDRSIG,CSTRLEN(WLMHDRSIG)) == 0
         && ~hdr->idx == hdr->ndx);
}
