/*
** Adapted for MSGAPI by Fedor Lizunkov 2:5020/960@FidoNet
*/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "compiler.h"

#ifdef HAS_IO_H
#include <io.h>
#endif

#ifdef HAS_SHARE_H
#include <share.h>
#endif

#ifdef HAS_MALLOC_H
#include <malloc.h>
#endif

#define MSGAPI_HANDLERS

#include "dr.h"
#include "prog.h"
#include "stamp.h"
#include "msgapi.h"
#include "api_jam.h"
#include "api_jamp.h"
#include "apidebug.h"
#include "unused.h"
#include "progprot.h"

#define Jmd ((JAMBASE *)(jm->apidata))
#define MsghJm ((JAMBASE *)(((struct _msgh *)msgh)->sq->apidata))

#ifdef __TURBOC__
#pragma warn -pia*
#pragma warn -ucp
#pragma warn -sig
#endif

#define NOTH 3

static JAMBASE *jbOpen = NULL;
int JamStrictActiveMsgs = 0;

/* Free's up a SubField-Chain */

static void freejamsubfield(JAMSUBFIELD2LIST *subfield)
{
  if (subfield) pfree(subfield);
}

MSGA *MSGAPI JamOpenArea(byte * name, word mode, word type)
{
   MSGA *jm=NULL;
   unsigned long len;

   if( !name || !*name )
   {
      msgapierr = MERR_BADNAME;
      return NULL;
   }

   jm = palloc(sizeof(MSGA));
   if (jm == NULL)
   {
      msgapierr = MERR_NOMEM;
      return NULL;
   }

   memset(jm, '\0', sizeof(MSGA));

   jm->id = MSGAPI_ID;

   if (type & MSGTYPE_ECHO)
   {
      jm->isecho = TRUE;
   }

   if (type & MSGTYPE_NOTH) jm->isecho = NOTH;

   jm->api = (struct _apifuncs *)palloc(sizeof(struct _apifuncs));
   if (jm->api == NULL) {
      msgapierr = MERR_NOMEM;
      pfree(jm);
      return NULL;
   }

   memset(jm->api, '\0', sizeof(struct _apifuncs));

   jm->apidata = (void*)palloc(sizeof(JAMBASE));

   if (jm->apidata == NULL) {
      msgapierr = MERR_NOMEM;
      pfree(jm->api);
      pfree(jm);
      return NULL;
   }

   memset((byte *) jm->apidata, '\0', sizeof(JAMBASE));

   jm->len = sizeof(MSGA);

   jm->num_msg = 0;
   jm->high_msg = 0;
   jm->high_water = (dword) -1;

   if (!Jam_OpenBase(jm, &mode, name)) {
      pfree(jm->api);
      pfree(jm->apidata);
      pfree(jm);
      msgapierr = MERR_BADF;
      return NULL;
   }

   /* fix for corrupted areas */
   lseek(Jmd->IdxHandle, 0, SEEK_END);
   len = tell(Jmd->IdxHandle);
   if (Jmd->HdrInfo.ActiveMsgs > len/IDX_SIZE) {
      Jmd->HdrInfo.ActiveMsgs = len/IDX_SIZE;
      Jmd->modified = 1;
   }
   lseek(Jmd->IdxHandle, 0, SEEK_SET);

   if (JamStrictActiveMsgs || (Jmd->HdrInfo.ActiveMsgs == 0 && len > 0))
       Jam_ActiveMsgs(jm);

   jm->high_water = Jmd->HdrInfo.highwater;
   /* jm->high_msg = Jam_HighMsg(Jmd); */
   jm->high_msg = jm->num_msg = Jmd->HdrInfo.ActiveMsgs;

   jm->type = MSGTYPE_JAM;

   jm->sz_xmsg = sizeof(XMSG);
   *jm->api = jm_funcs;

   msgapierr = 0;
   return jm;
}

static sword _XPENTRY JamCloseArea(MSGA * jm)
{
   dword i;
   JAMBASE *jbptr = jbOpen;

   if (InvalidMh(jm))
   {
      return -1;
   }

   if (Jmd->msgs_open) {
      msgapierr = MERR_EOPEN;
      return -1;
   }

   if (jbptr != Jmd)
       while((jbptr) && ((JAMBASE *)jbptr->jbNext) && ((JAMBASE *)jbptr->jbNext != Jmd))
           jbptr = (JAMBASE *)jbptr->jbNext;

   if (Jmd->modified || Jmd->HdrInfo.highwater != jm->high_water) {
      Jmd->HdrInfo.highwater = jm->high_water;
      Jmd->HdrInfo.ModCounter++;
      Jam_WriteHdrInfo(Jmd);
   }

   if (jm->locked) JamUnlock(jm);

   Jam_CloseFile(Jmd);

   pfree(Jmd->BaseName);
   if (Jmd->actmsg) {
      for (i=0; i<Jmd->HdrInfo.ActiveMsgs; i++)
         freejamsubfield(Jmd->actmsg[i].subfield);
      pfree(Jmd->actmsg);
   }

   if (jbOpen != Jmd)
       jbptr->jbNext = Jmd->jbNext;
   else
       jbOpen = jbOpen->jbNext;

   pfree(jm->api);
   pfree(jm->apidata);
   jm->id = 0L;
   memset(jm, 0, sizeof(MSGA));
   pfree(jm);

   return 0;
}

void JamCloseOpenAreas()
{
    while(jbOpen)
        JamCloseArea(jbOpen->jm);
}

static MSGH *_XPENTRY JamOpenMsg(MSGA * jm, word mode, dword msgnum)
{
   struct _msgh *msgh;

   if (InvalidMh(jm)) {
      return NULL;
   }

   if (mode == MOPEN_CREATE) {
      if ((sdword) msgnum < 0 || msgnum > jm->num_msg) {
          msgapierr = MERR_NOENT;
          return NULL;
      }

      if (msgnum != 0) {
         msgh = Jam_OpenMsg(jm, mode, msgnum);

         if (msgh == NULL) {
            msgapierr = MERR_NOENT;
            return NULL;
         }
      } else {
         msgh = palloc(sizeof(struct _msgh));
         if (msgh == NULL) {
            msgapierr = MERR_NOMEM;
            return NULL;
         }

         memset(msgh, '\0', sizeof(struct _msgh));

         msgh->sq = jm;
         msgh->bytes_written = 0L;
         msgh->cur_pos = 0L;
         msgh->msgnum = msgnum;
         msgh->Hdr.TxtLen = 0;

      } /* endif */

   }
   else if (msgnum == 0) {
      msgapierr = MERR_NOENT;
      return NULL;
   } else {
      msgh = Jam_OpenMsg(jm, mode, msgnum);
      if (msgh == NULL) {
         msgapierr = MERR_NOENT;
         return NULL;
      }
   }

   msgh->mode = mode;
   msgh->id = MSGH_ID;

   MsghJm->msgs_open++;

   return (MSGH *) msgh;
}

static sword _XPENTRY JamCloseMsg(MSGH * msgh)
{
   if (InvalidMsgh(msgh))
   {
      return -1;
   }


    MsghJm->msgs_open--;

    /* Fill the message out to the length that the app said it would be */

    msgh->id = 0L;
    pfree(msgh->ctrl);
    pfree(msgh->lctrl);
    pfree(msgh->SubFieldPtr);
    pfree(msgh);

    return 0;
}

static int openfilejm(char *name, word mode, mode_t permissions)
{
   return sopen(name, mode, SH_DENYNONE, permissions);
}

static int opencreatefilejm(char *name, word mode, mode_t permissions)
{
  int hF =sopen(name, mode, SH_DENYNONE, permissions);

  if ((hF == -1) && (mode & O_CREAT) && (errno == ENOENT) )
  {
     char* slash = strrchr(name, PATH_DELIM);
     if (slash) {
        *slash = '\0';
        _createDirectoryTree(name);
        *slash = PATH_DELIM;
     }
     hF=sopen(name, mode, SH_DENYNONE, permissions);
  }
  return hF;
}

static int Jam_OpenTxtFile(JAMBASE *jambase)
{
   char *txt;

   if (!jambase || !jambase->BaseName) {
      msgapierr = MERR_BADA;
      return 0;
   }

   txt = (char *) palloc(strlen((char*)(jambase->BaseName))+5);
   if (!txt) {
      msgapierr = MERR_NOMEM;
      return 0;
   }
   strcpy(txt, (char*)(jambase->BaseName));
   strcat(txt, EXT_TXTFILE);
   if (jambase->mode == MSGAREA_CREATE)
      jambase->TxtHandle = openfilejm(txt, fop_wpb, jambase->permissions);
   else
      jambase->TxtHandle = openfilejm(txt, fop_rpb, jambase->permissions);
   if ((jambase->TxtHandle == -1) && (jambase->mode == MSGAREA_CRIFNEC)) {
      jambase->mode = MSGAREA_CREATE;
      jambase->TxtHandle = opencreatefilejm(txt, fop_cpb, jambase->permissions);
   }
   pfree(txt);
   if (jambase->TxtHandle == -1) {
      Jam_CloseFile(jambase);
      msgapierr = MERR_NOENT;
      return 0;
   }
   return 1;
}

static dword _XPENTRY JamReadMsg(MSGH * msgh, XMSG * msg, dword offset, dword bytes, byte * text, dword clen, byte * ctxt)
{
   JAMSUBFIELD2ptr SubField;
   dword SubPos, bytesread;
   struct tm *s_time;
   SCOMBO *scombo;
   unsigned char *ftsdate;

   if (InvalidMsgh(msgh))
   {
      return -1L;
   }

   if (msgh->Hdr.Attribute & JMSG_DELETED) {
      return -1L;
   } /* endif */

   if (msg) {
      /* make msg */

      msg->attr = Jam_JamAttrToMsg(msgh);

      memset(msg->from, '\0', XMSG_FROM_SIZE);
      memset(msg->to, '\0', XMSG_TO_SIZE);
      memset(msg->subj, '\0', XMSG_SUBJ_SIZE);
      memset(&(msg->orig), 0, sizeof(msg->orig));
      memset(&(msg->dest), 0, sizeof(msg->dest));

      /* get "from name" line */
      SubPos = 0;
      if ((SubField = Jam_GetSubField(msgh, &SubPos, JAMSFLD_SENDERNAME))) {
         strncpy((char*)(msg->from), (char*)(SubField->Buffer), min(SubField->DatLen, sizeof(msg->from)));
      } /* endif */

      /* get "to name" line */
      SubPos = 0;
      if ((SubField = Jam_GetSubField(msgh, &SubPos, JAMSFLD_RECVRNAME))) {
         strncpy((char*)(msg->to), (char*)(SubField->Buffer), min(SubField->DatLen, sizeof(msg->to)));
      } /* endif */

      /* get "subj" line */
      SubPos = 0;
      if ((SubField = Jam_GetSubField(msgh, &SubPos, JAMSFLD_SUBJECT))) {
         strncpy((char*)(msg->subj), (char*)(SubField->Buffer), min(SubField->DatLen, sizeof(msg->subj)));
      } /* endif */

      if (!msgh->sq->isecho) {
          /* get "orig address" line */
          SubPos = 0;
          if ((SubField = Jam_GetSubField(msgh, &SubPos, JAMSFLD_OADDRESS))) {
             parseAddr(&(msg->orig), SubField->Buffer, SubField->DatLen);
          } /* endif */

          /* get "dest address" line */
          SubPos = 0;
          if ((SubField = Jam_GetSubField(msgh, &SubPos, JAMSFLD_DADDRESS))) {
             parseAddr(&(msg->dest), SubField->Buffer, SubField->DatLen);
          } /* endif */
      } /* endif */


      s_time = gmtime((time_t *)(&(msgh->Hdr.DateWritten)));
      scombo = (SCOMBO*)(&(msg->date_written));
      scombo = TmDate_to_DosDate(s_time, scombo);
      /* ftsdate = msg->__ftsc_date; */
      ftsdate = (unsigned char *)sc_time(scombo, (char *)(msg->__ftsc_date));

      if (msgh->Hdr.DateProcessed) {
         s_time = gmtime((time_t *)(&(msgh->Hdr.DateProcessed)));
         scombo = (SCOMBO*)(&(msg->date_arrived));
         scombo = TmDate_to_DosDate(s_time, scombo);
      }
      else
         ((SCOMBO*)(&(msg->date_arrived)))->ldate = 0;

      msg->replyto = msgh->Hdr.ReplyTo;
      msg->xmreply1st = msgh->Hdr.Reply1st;
      msg->replies[1] = 0;
      msg->xmreplynext  = msgh->Hdr.ReplyNext;
      msg->xmtimesread  = msgh->Hdr.TimesRead;
      msg->xmcost  = msgh->Hdr.Cost;

   } /* endif */

   bytesread = 0;

   if (bytes > 0 && text) {

      /* read text message */

      if (offset > (msgh->Hdr.TxtLen+msgh->lclen)) offset = msgh->Hdr.TxtLen+msgh->lclen;

      msgh->cur_pos = offset;

      if (offset >= msgh->Hdr.TxtLen) {
         offset -= msgh->Hdr.TxtLen;
         if (bytes > msgh->lclen) bytes = msgh->lclen;
         if (offset < msgh->lclen) {
             if (offset < bytes)
                 bytesread = bytes-offset;
             else
                 bytesread = 0;
             strncpy((char*)text, (char*)(msgh->lctrl+offset), bytesread);
         } else {
         } /* endif */
         msgh->cur_pos += bytesread;
      } else {
         if (MsghJm->TxtHandle == 0) Jam_OpenTxtFile(MsghJm);
         lseek(MsghJm->TxtHandle, msgh->Hdr.TxtOffset+offset, SEEK_SET);
         if (bytes > msgh->Hdr.TxtLen-offset) {
            bytesread =  farread(MsghJm->TxtHandle, text, msgh->Hdr.TxtLen-offset);
            bytes -= (msgh->Hdr.TxtLen-offset);
            if (offset < bytes)
                bytes -= offset;
            else bytes = 0;
            if (bytes > msgh->lclen) bytes = msgh->lclen;
            strncpy((char*)(text+bytesread), (char*)(msgh->lctrl), bytes);
            bytesread += bytes;
         } else {
            bytesread = farread(MsghJm->TxtHandle, text, bytes);
         } /* endif */
         msgh->cur_pos += bytesread;
      } /* endif */
      text[bytesread] = '\0';
   }

   if (clen && ctxt) {
      /* read first kludges */
      if (clen > msgh->clen) clen = msgh->clen;
      strncpy((char*)(ctxt), (char*)(msgh->ctrl), clen);
      ctxt[clen] = '\0';
   }


   msgapierr = MERR_NONE;
   return bytesread;
}

static sword _XPENTRY JamWriteMsg(MSGH * msgh, word append, XMSG * msg,
             byte * text, dword textlen, dword totlen, dword clen, byte * ctxt)
{
   /* not supported append if JAM !!!
      the reason is that we need to know ALL of the text before we can set up
      the JAM headers.
    */

   JAMHDR         jamhdrNew;
   JAMIDXREC      jamidxNew;
   JAMSUBFIELD2LISTptr subfieldNew;
   XMSG           msg_old;
   MSGA          *jm;
   dword          x = 0;

   char           ch = 0;
   unsigned char *onlytext=NULL;
   int            didlock = FALSE;
   int            rc = 0;

   assert(append == 0);

   if (InvalidMsgh(msgh)) {
      return -1L;
   }

   if (msgh->mode != MOPEN_CREATE && msgh->mode != MOPEN_WRITE && msgh->mode != MOPEN_RW)
      return -1L;

   jm = msgh->sq;
   Jmd->modified = 1;

   memset(&jamidxNew, '\0', sizeof(JAMIDXREC));
   memset(&jamhdrNew, '\0', sizeof(JAMHDR));

   if (!ctxt)
     clen = 0L;

   if (!text)
     textlen = 0L;

   if (textlen == 0L)
     text = NULL;

   if (clen == 0L)
     ctxt = NULL;

   if (msgh->mode != MOPEN_CREATE)
   {
      if (clen) clen = 0;
      if (ctxt) ctxt = NULL;
   }

   if (clen && ctxt)
   {
       x = strlen((char*)ctxt);
       if (clen < x) clen = x+1;
   }

   subfieldNew = NULL;
   if (msg)
     ConvertXmsgToJamHdr(msgh, msg, &jamhdrNew, &subfieldNew);
   else
     if (msgh->mode != MOPEN_CREATE)
     {
        JamReadMsg(msgh, &msg_old, 0, 0, NULL, 0, NULL);
        ConvertXmsgToJamHdr(msgh, &msg_old, &jamhdrNew, &subfieldNew);
     }

   if (!jm->locked) {
      if (!(didlock = Jam_Lock(jm, 1))) {
         freejamsubfield(subfieldNew);
         msgapierr = MERR_SHARE;
         return -1;
      }
   }

   jamhdrNew.ReplyCRC = jamhdrNew.MsgIdCRC = 0xFFFFFFFFUL;

   if (clen && ctxt)
     ConvertCtrlToSubf(&jamhdrNew, &subfieldNew, clen, ctxt);

   if (textlen && text)
     onlytext = DelimText(&jamhdrNew, &subfieldNew, text, textlen);
   else
     if (msgh->mode != MOPEN_CREATE)
     {
        DelimText(&jamhdrNew, &subfieldNew, msgh->lctrl, msgh->lclen);
        jamhdrNew.TxtOffset = msgh->Hdr.TxtOffset;
        jamhdrNew.TxtLen = msgh->Hdr.TxtLen;
     }

   if (onlytext==NULL) {
      onlytext = calloc(1,1);
      if (!onlytext) {
        freejamsubfield(subfieldNew);
        msgapierr = MERR_NOMEM;
        return -1;
      }
   }

   if (msgh->mode == MOPEN_CREATE)
   {
      /* no logic if msg not present */
      if (msg)
      {
         if (Jmd->TxtHandle == 0) Jam_OpenTxtFile(Jmd);
         if (msgh->msgnum == 0)
         {
            /* new message in end of position */
            lseek(Jmd->IdxHandle, 0, SEEK_END);
            lseek(Jmd->HdrHandle, 0, SEEK_END);
            lseek(Jmd->TxtHandle, 0, SEEK_END);
            jamhdrNew.MsgNum = Jmd->HdrInfo.BaseMsgNum+(tell(Jmd->IdxHandle)/IDX_SIZE);
            msgh->seek_idx = tell(Jmd->IdxHandle);
            msgh->seek_hdr = jamidxNew.HdrOffset = tell(Jmd->HdrHandle);
            jamidxNew.UserCRC = Jam_Crc32(msg->to, strlen((char*)(msg->to)));
            jamhdrNew.TxtOffset = tell(Jmd->TxtHandle);
            jamhdrNew.TxtLen = strlen((char*)onlytext);
            msgh->bytes_written = (dword) farwrite(Jmd->TxtHandle, onlytext, jamhdrNew.TxtLen);
            if (msgh->bytes_written != jamhdrNew.TxtLen)
            {
               setfsize(Jmd->TxtHandle, jamhdrNew.TxtOffset);
               freejamsubfield(subfieldNew);
               msgapierr = MERR_NODS;
               return -1;
            }
            msgh->cur_pos = tell(Jmd->TxtHandle);
            if (!write_hdr(Jmd->HdrHandle, &jamhdrNew))
            {
               setfsize(Jmd->HdrHandle, msgh->seek_hdr);
               setfsize(Jmd->TxtHandle, jamhdrNew.TxtOffset);
               freejamsubfield(subfieldNew);
               free(onlytext); onlytext=NULL;
               msgapierr = MERR_NODS;
               return -1;
            }
            if (!write_subfield(Jmd->HdrHandle, &subfieldNew, jamhdrNew.SubfieldLen))
            {
               setfsize(Jmd->HdrHandle, msgh->seek_hdr);
               setfsize(Jmd->TxtHandle, jamhdrNew.TxtOffset);
               freejamsubfield(subfieldNew);
               free(onlytext);
               msgapierr = MERR_NODS;
               return -1;
            }
            if (!write_idx(Jmd->IdxHandle, &jamidxNew))
            {
               setfsize(Jmd->IdxHandle, msgh->seek_idx);
               setfsize(Jmd->HdrHandle, msgh->seek_hdr);
               setfsize(Jmd->TxtHandle, jamhdrNew.TxtOffset);
               freejamsubfield(subfieldNew);
               free(onlytext);
               msgapierr = MERR_NODS;
               return -1;
            }
            Jmd->HdrInfo.ActiveMsgs++;
#ifdef HARD_WRITE_HDR
            Jmd->HdrInfo.ModCounter++;
            if (Jam_WriteHdrInfo(Jmd))
            {
               setfsize(Jmd->HdrHandle, msgh->seek_hdr);
               setfsize(Jmd->IdxHandle, msgh->seek_idx);
               setfsize(Jmd->TxtHandle, jamhdrNew.TxtOffset);
               freejamsubfield(subfieldNew);
               free(onlytext);
               msgapierr = MERR_NODS;
               return -1;
            }
#endif
            jm->high_msg++;
            if (Jmd->actmsg_read) {
               Jmd->actmsg = (JAMACTMSGptr)farrealloc(Jmd->actmsg, sizeof(JAMACTMSG)*(jm->num_msg+1));
               if(!Jmd->actmsg) {
                 freejamsubfield(subfieldNew);
                 free(onlytext);
                 msgapierr = MERR_NOMEM;
                 return -1;
               }
               Jmd->actmsg[jm->num_msg].IdxOffset = msgh->seek_idx;
               Jmd->actmsg[jm->num_msg].TrueMsg = msgh->seek_hdr;
               Jmd->actmsg[jm->num_msg].UserCRC = jamidxNew.UserCRC;
               memcpy(&(Jmd->actmsg[jm->num_msg].hdr), &jamhdrNew, sizeof(jamhdrNew));
               if (Jmd->actmsg_read == 1)
                  Jmd->actmsg[jm->num_msg].subfield = subfieldNew;
               else { /* not incore subfields */
                  Jmd->actmsg[jm->num_msg].subfield = NULL;
                  freejamsubfield(subfieldNew);
               }
            } else
               freejamsubfield(subfieldNew);
            jm->num_msg++;
         }
         else
         {
            /* new message instead of old message position */
            msgh->Hdr.TxtLen = 0;
            msgh->Hdr.Attribute |= JMSG_DELETED;
            lseek(Jmd->HdrHandle, 0, SEEK_END);
            jamidxNew.HdrOffset = tell(Jmd->HdrHandle);
            jamhdrNew.MsgNum = msgh->Hdr.MsgNum;
            jamidxNew.UserCRC = Jam_Crc32(msg->to, strlen((char*)(msg->to)));
            lseek(Jmd->TxtHandle, 0, SEEK_END);
            jamhdrNew.TxtOffset = tell(Jmd->TxtHandle);
            jamhdrNew.TxtLen = strlen((char*)onlytext);
            msgh->bytes_written = (dword) farwrite(Jmd->TxtHandle, onlytext, jamhdrNew.TxtLen);
            msgh->cur_pos = tell(Jmd->TxtHandle);
            lseek(Jmd->TxtHandle, jamhdrNew.TxtOffset+totlen-1, SEEK_SET);
            farwrite(Jmd->TxtHandle, &ch, 1);
            write_hdr(Jmd->HdrHandle, &jamhdrNew);
            write_subfield(Jmd->HdrHandle, &subfieldNew, jamhdrNew.SubfieldLen);
            lseek(Jmd->IdxHandle, msgh->seek_idx, SEEK_SET);
            write_idx(Jmd->IdxHandle, &jamidxNew);
            lseek(Jmd->HdrHandle, msgh->seek_hdr, SEEK_SET);
            write_hdr(Jmd->HdrHandle, &(msgh->Hdr));
            msgh->seek_hdr = jamidxNew.HdrOffset;
#ifdef HARD_WRITE_HDR
            Jmd->HdrInfo.ModCounter++;
            Jam_WriteHdrInfo(Jmd);
#endif
            /* info from new message to msgh srtuct */
            if (Jmd->actmsg_read) {
               Jmd->actmsg[msgh->msgnum - 1].TrueMsg = msgh->seek_hdr;
               Jmd->actmsg[msgh->msgnum - 1].UserCRC = jamidxNew.UserCRC;
               memcpy(&(Jmd->actmsg[msgh->msgnum-1].hdr), &jamhdrNew, sizeof(jamhdrNew));
               if (Jmd->actmsg_read == 1)
                  Jmd->actmsg[msgh->msgnum - 1].subfield = subfieldNew;
               else { /* subfields not incore */
                  Jmd->actmsg[msgh->msgnum - 1].subfield = NULL;
                  freejamsubfield(subfieldNew);
               }
            } else
               freejamsubfield(subfieldNew);
         } /* endif */
      } /* endif */
   }
   else
   {
      /* change text and SEEN_BY, PATH, VIA kludges posible only (message != create)*/
      ConvertCtrlToSubf(&jamhdrNew, &subfieldNew, msgh->clen, msgh->ctrl);

      if (msg)
         jamidxNew.UserCRC = Jam_Crc32(msg->to, strlen((char*)(msg->to)));
      else
         jamidxNew.UserCRC = msgh->Idx.UserCRC;

      if (text && textlen) {
         if (Jmd->TxtHandle == 0) Jam_OpenTxtFile(Jmd);
         lseek(Jmd->TxtHandle, 0, SEEK_END);
         jamhdrNew.TxtOffset = tell(Jmd->TxtHandle);
         jamhdrNew.TxtLen = strlen((char*)onlytext);
         farwrite(Jmd->TxtHandle, onlytext, jamhdrNew.TxtLen);
      } /* endif */

      if (jamhdrNew.SubfieldLen > msgh->Hdr.SubfieldLen)
      {
         msgh->Hdr.TxtLen = 0;
         msgh->Hdr.Attribute |= JMSG_DELETED;
         lseek(Jmd->HdrHandle, msgh->seek_hdr, SEEK_SET);
         write_hdr(Jmd->HdrHandle, &(msgh->Hdr));
         lseek(Jmd->HdrHandle, 0, SEEK_END);
         msgh->seek_hdr = tell(Jmd->HdrHandle);
      }

      jamhdrNew.MsgNum = msgh->Hdr.MsgNum;
      jamidxNew.HdrOffset = msgh->seek_hdr;

      lseek(Jmd->HdrHandle, msgh->seek_hdr, SEEK_SET);
      write_hdr(Jmd->HdrHandle, &jamhdrNew);
      write_subfield(Jmd->HdrHandle, &subfieldNew, jamhdrNew.SubfieldLen);

      if (Jmd->actmsg_read &&
          Jmd->actmsg[msgh->msgnum - 1].TrueMsg == msgh->seek_hdr &&
          Jmd->actmsg[msgh->msgnum - 1].UserCRC == jamidxNew.UserCRC) {
         /* no index update needed */ ;
      } else {
         lseek(Jmd->IdxHandle, msgh->seek_idx, SEEK_SET);
         write_idx(Jmd->IdxHandle, &jamidxNew);
      }

#ifdef HARD_WRITE_HDR
      Jmd->HdrInfo.ModCounter++;
      Jam_WriteHdrInfo(Jmd);
#endif
      memmove(&(msgh->Idx), &(jamidxNew), sizeof(JAMIDXREC));
      memmove(&(msgh->Hdr), &(jamhdrNew), sizeof(JAMHDR));
      freejamsubfield(msgh->SubFieldPtr);
      msgh->SubFieldPtr = subfieldNew;
      DecodeSubf(msgh);
      if (Jmd->actmsg_read) {
         Jmd->actmsg[msgh->msgnum - 1].TrueMsg = msgh->seek_hdr;
         Jmd->actmsg[msgh->msgnum - 1].UserCRC = jamidxNew.UserCRC;
         memcpy(&(Jmd->actmsg[msgh->msgnum - 1].hdr), &jamhdrNew, sizeof(jamhdrNew));
         if (Jmd->actmsg_read == 1)
            copy_subfield(&(Jmd->actmsg[msgh->msgnum - 1].subfield), subfieldNew);
         else
            Jmd->actmsg[msgh->msgnum - 1].subfield = NULL;
      }

   } /* endif */
   if (didlock) {
      Jam_Unlock(jm);
   } /* endif */

   pfree(onlytext);

   return rc;
}


static sword _XPENTRY JamKillMsg(MSGA * jm, dword msgnum)
{
   JAMIDXREC jamidx;
   JAMHDR    jamhdr;

   if (InvalidMh(jm)) {
      return -1;
   }

   if (jm->locked) return -1L;

   if (msgnum == 0 || msgnum > jm->num_msg) {
      msgapierr = MERR_NOENT;
      return -1L;
   } /* endif */

   if (!Jam_PosHdrMsg(jm, msgnum, &jamidx, &jamhdr)) {
      msgapierr = MERR_BADF;
      return -1L;
   } /* endif */

   if (JamLock(jm) == -1) return -1L;

   lseek(Jmd->HdrHandle, jamidx.HdrOffset, SEEK_SET);
   lseek(Jmd->IdxHandle, Jmd->actmsg[msgnum - 1].IdxOffset, SEEK_SET);

   Jmd->modified = 1;
   Jmd->HdrInfo.ActiveMsgs--;
   jamhdr.TxtLen = 0;
   jamhdr.Attribute |= JMSG_DELETED;
   jamidx.UserCRC = 0xFFFFFFFFL;
   jamidx.HdrOffset = 0xFFFFFFFFL;
   write_idx(Jmd->IdxHandle, &jamidx);
   write_hdr(Jmd->HdrHandle, &jamhdr);
#ifdef HARD_WRITE_HDR
   Jmd->HdrInfo.ModCounter++;
   Jam_WriteHdrInfo(Jmd);
#endif

   if (Jmd->actmsg_read) {
      dword i;
      for (i=0; i<Jmd->HdrInfo.ActiveMsgs; i++)
         freejamsubfield(Jmd->actmsg[i].subfield);
      pfree(Jmd->actmsg);
      Jmd->actmsg_read = 0;
      Jmd->actmsg = NULL;
   }

   Jam_ActiveMsgs(jm);
   jm->num_msg = Jmd->HdrInfo.ActiveMsgs;

   JamUnlock(jm);
   return 0;
}

static sword _XPENTRY JamLock(MSGA * jm)
{
   if (InvalidMh(jm)) {
      return -1;
   }

   /* Don't do anything if already locked */

   if (jm->locked) {
      return 0;
   }

   if (!Jam_Lock(jm, 0))
      return -1;

   jm->locked = TRUE;

   return 0;
}

static sword _XPENTRY JamUnlock(MSGA * jm)
{
   if (InvalidMh(jm)) {
      return -1;
   }

   if (!jm->locked) {
      return -1;
   }

   jm->locked = FALSE;

   if (mi.haveshare) {
      unlock(Jmd->HdrHandle, 0L, 1L);
   }

   return 0;
}

static sword _XPENTRY JamSetCurPos(MSGH * msgh, dword pos)
{
    if (InvalidMsgh(msgh))
        return -1;

    msgh->cur_pos = pos;
    return 0;
}

static dword _XPENTRY JamGetCurPos(MSGH * msgh)
{
    if (InvalidMsgh(msgh))
        return -1;

    return msgh->cur_pos;
}

static UMSGID _XPENTRY JamMsgnToUid(MSGA * jm, dword msgnum)
{
    if (InvalidMh(jm))
        return (UMSGID) -1;

    msgapierr = MERR_NONE;
    if (msgnum > jm->num_msg) return (UMSGID) -1;
    if (msgnum <= 0) return (UMSGID) 0;
    if (!Jmd->actmsg_read) {
       Jam_ActiveMsgs(jm);
       if (msgnum > jm->num_msg) return (UMSGID) -1;
    }
    return (UMSGID) (Jmd->actmsg[msgnum - 1].IdxOffset / 8 + Jmd->HdrInfo.BaseMsgNum);
}

static dword _XPENTRY JamUidToMsgn(MSGA * jm, UMSGID umsgid, word type)
{
   dword  msgnum, left, right, current;
   UMSGID umsg;

   if (InvalidMh(jm))
      return -1L;

   msgnum = umsgid - Jmd->HdrInfo.BaseMsgNum + 1;
   if (msgnum <= 0)
      return 0;
   if (!Jmd->actmsg_read) Jam_ActiveMsgs(jm);
   left = 1;
   right = jm->num_msg;
   while (left <= right)
   {
     current = (right + left) / 2;
     umsg = JamMsgnToUid(jm, current);
     if (umsg == -1)
       return 0;
     if (umsg < msgnum)
       left = current + 1;
     else if (umsg > msgnum){
       if( current > 0 ) right = current - 1;
       else right = 0;
     }else
       return current;
   }
   if (type == UID_EXACT) return 0;
   if (type == UID_PREV)
     return right;
   return (left > jm->num_msg) ? jm->num_msg : left;
}

static dword _XPENTRY JamGetHighWater(MSGA * jm)
{
   if (InvalidMh(jm))
      return -1L;

   return JamUidToMsgn(jm, jm->high_water, UID_PREV);
}

static sword _XPENTRY JamSetHighWater(MSGA * jm, dword hwm)
{
   if (InvalidMh(jm))
      return -1L;

   hwm = JamMsgnToUid(jm, hwm);
   if (hwm > jm->high_msg + Jmd->HdrInfo.BaseMsgNum) return -1L;

   jm->high_water = hwm;

   return 0;
}

static dword _XPENTRY JamGetTextLen(MSGH * msgh)
{
   if( InvalidMsgh(msgh) )
     return (dword)0L;
   return (msgh->Hdr.TxtLen+msgh->lclen);
}

static dword _XPENTRY JamGetCtrlLen(MSGH * msgh)
{
   if( InvalidMsgh(msgh) )
     return (dword)0L;
   return (msgh->clen);
}


sword MSGAPI JamValidate(byte * name)
{
    byte temp[PATHLEN];

    if(!name || !*name) return FALSE;

    sprintf((char *) temp, "%s%s", name, EXT_HDRFILE);

    if (!fexist((char *) temp))
        return FALSE;

    sprintf((char *) temp, "%s%s", name, EXT_TXTFILE);

    if (!fexist((char *) temp))
        return FALSE;

    sprintf((char *) temp, "%s%s", name, EXT_IDXFILE);

    if (!fexist((char *) temp))
        return FALSE;

    return TRUE;
}

void Jam_CloseFile(JAMBASE *jambase)
{
   if(!jambase)
   { errno = EINVAL;
     msgapierr = MERR_BADA;
     return;
   }

   if (jambase->HdrHandle != 0 && jambase->HdrHandle != -1) {
      close(jambase->HdrHandle);
      jambase->HdrHandle = 0;
   } /* endif */

   if (jambase->TxtHandle != 0 && jambase->TxtHandle != -1) {
      close(jambase->TxtHandle);
      jambase->TxtHandle = 0;
   } /* endif */

   if (jambase->IdxHandle != 0 && jambase->IdxHandle != -1) {
      close(jambase->IdxHandle);
      jambase->IdxHandle = 0;
   } /* endif */

   if (jambase->LrdHandle != 0 && jambase->LrdHandle != -1) {
      close(jambase->LrdHandle);
      jambase->LrdHandle = 0;
   } /* endif */
}

static int gettz(void)
{
   struct tm *tm;
   time_t t, gt;

   t = time(NULL);
   tzset();
   tm = gmtime (&t);
   tm->tm_isdst = 0;
   gt = mktime(tm);
   tm = localtime (&t);
   tm->tm_isdst = 0;
   return (int)(((long)mktime(tm)-(long)gt));
}

/*  Return values:
 *  0 = error
 *  1 = success
 */
int JamDeleteBase(char *name)
{
   char *fn=NULL;
   int rc = 1;

   if( !name || !*name )
   { errno = EINVAL;
     msgapierr = MERR_BADNAME;
     return 0;
   }

   fn = palloc( strlen(name)+5 );
   if( !fn )
   { errno = ENOMEM;
     msgapierr = MERR_NOMEM;
     return 0;
   }

   sprintf(fn, "%s%s", name, EXT_HDRFILE);
   if (unlink(fn)) rc=0; /* error */
   sprintf(fn, "%s%s", name, EXT_TXTFILE);
   if (unlink(fn)) rc=0; /* error */
   sprintf(fn, "%s%s", name, EXT_IDXFILE);
   if (unlink(fn)) rc=0; /* error */
   sprintf(fn, "%s%s", name, EXT_LRDFILE);
   if (unlink(fn)) rc=0; /* error */

   pfree(fn);
   return rc;
}

/*  Return values:
 *  0 = error
 *  1 = success
 */
int Jam_OpenFile(JAMBASE *jambase, word *mode, mode_t permissions)
{
   char *hdr=NULL, *idx=NULL, *txt=NULL, *lrd=NULL;
   int x;

   if(!jambase || !mode)
   {
      msgapierr = MERR_BADA;
      return 0;
   }

   x = strlen((char*)(jambase->BaseName))+5;

   hdr = (char*) palloc(x);
   idx = (char*) palloc(x);
   txt = (char*) palloc(x);
/*   lrd = (char*) palloc(x);*/

   if( !(hdr && idx && txt /*&& lrd*/) )
   {
     pfree(hdr);
     pfree(idx);
     pfree(txt);
     pfree(lrd);
     errno = ENOMEM;
     msgapierr = MERR_NOMEM;
     return 0;
   }

   sprintf(hdr, "%s%s", jambase->BaseName, EXT_HDRFILE);
   sprintf(txt, "%s%s", jambase->BaseName, EXT_TXTFILE);
   sprintf(idx, "%s%s", jambase->BaseName, EXT_IDXFILE);
/*   sprintf(lrd, "%s%s", jambase->BaseName, EXT_LRDFILE);*/

   if (*mode == MSGAREA_CREATE) {
      jambase->HdrHandle = opencreatefilejm(hdr, fop_wpb, permissions);
      jambase->TxtHandle = openfilejm(txt, fop_wpb, permissions);
      jambase->IdxHandle = openfilejm(idx, fop_wpb, permissions);
      /* jambase->LrdHandle = openfilejm(lrd, fop_wpb, permissions);
      */ jambase->LrdHandle = 0;

      memset(&(jambase->HdrInfo), '\0', sizeof(JAMHDRINFO));
      strcpy((char*)(jambase->HdrInfo.Signature), HEADERSIGNATURE);

      jambase->HdrInfo.DateCreated = time(NULL) + gettz();
      jambase->HdrInfo.ModCounter  = 1;
      jambase->HdrInfo.PasswordCRC = 0xffffffffUL;
      jambase->HdrInfo.BaseMsgNum  = 1;

      write_hdrinfo(jambase->HdrHandle, &(jambase->HdrInfo));

   } else {
      jambase->HdrHandle = openfilejm(hdr, fop_rpb, permissions);
      /* jambase->TxtHandle = openfilejm(txt, fop_rpb, permissions);
      */ jambase->TxtHandle = 0;
      jambase->IdxHandle = openfilejm(idx, fop_rpb, permissions);
      /* jambase->LrdHandle = openfilejm(lrd, fop_rpb, permissions);
      */ jambase->LrdHandle = 0;
   } /* endif */

   if (jambase->HdrHandle == -1 || jambase->TxtHandle == -1 || jambase->IdxHandle == -1) {
      if (*mode != MSGAREA_CRIFNEC) {
         Jam_CloseFile(jambase);
         pfree(hdr);
         pfree(txt);
         pfree(idx);
         pfree(lrd);
         msgapierr = MERR_NOENT;
         return 0;
      }
      *mode = MSGAREA_CREATE;
      jambase->HdrHandle = opencreatefilejm(hdr, fop_cpb, permissions);
      jambase->TxtHandle = openfilejm(txt, fop_cpb, permissions);
      jambase->IdxHandle = openfilejm(idx, fop_cpb, permissions);
      /* jambase->LrdHandle = openfilejm(lrd, fop_cpb, permissions);
      */ jambase->LrdHandle = 0;

      if (jambase->HdrHandle == -1 || jambase->TxtHandle == -1 || jambase->IdxHandle == -1) {
         Jam_CloseFile(jambase);
         pfree(hdr);
         pfree(txt);
         pfree(idx);
         pfree(lrd);
         msgapierr = MERR_NOENT;
         return 0;
      } /* endif */


      memset(&(jambase->HdrInfo), '\0', sizeof(JAMHDRINFO));
      strcpy((char*)(jambase->HdrInfo.Signature), HEADERSIGNATURE);
      jambase->HdrInfo.DateCreated = time(NULL) + gettz();
      jambase->HdrInfo.ModCounter  = 1;
      jambase->HdrInfo.PasswordCRC = 0xffffffffUL;
      jambase->HdrInfo.BaseMsgNum  = 1;

      write_hdrinfo(jambase->HdrHandle, &(jambase->HdrInfo));

   } /* endif */

   pfree(hdr);
   pfree(txt);
   pfree(idx);
   pfree(lrd);

   jambase->mode = *mode;
   jambase->permissions = permissions;
   jambase->modified = 0;

   return 1;
}

/*  Return values:
 *  0 = error
 *  1 = success
 */
static sword MSGAPI Jam_OpenBase(MSGA *jm, word *mode, unsigned char *basename)
{
   if( InvalidMh(jm) || !mode)
   {
      msgapierr = MERR_BADA;
      return 0;
   }
   if( !basename || !*basename )
   {
      msgapierr = MERR_BADNAME;
      return 0;
   }

   Jmd->BaseName = (unsigned char*)palloc(strlen((char*)basename)+1);
   if( !Jmd->BaseName ) {
     errno = ENOMEM;
     msgapierr = MERR_NOMEM;
     return 0;
   }
   strcpy((char*)(Jmd->BaseName), (char*)basename);

   if (!Jam_OpenFile(Jmd, mode, FILEMODE(jm->isecho))) {
      /* Jam_OpenFile() set MSGAPIERR */
      pfree(Jmd->BaseName);
      return 0;
   } /* endif */

   Jmd->jm = jm;

   Jmd->jbNext = jbOpen;
   jbOpen = Jmd;

   lseek(Jmd->HdrHandle, 0, SEEK_SET);
   read_hdrinfo(Jmd->HdrHandle, &(Jmd->HdrInfo));

   return 1;
}

/*  Return values:
 *  0 = error
 *  1 = success
 */
static MSGH *Jam_OpenMsg(MSGA * jm, word mode, dword msgnum)
{
   struct _msgh *msgh;
/*   JAMIDXREC    idx; */
/*   JAMHDR       hdr; */

   unused(mode);

   if( InvalidMh(jm) )
      return NULL;

   if (msgnum == MSGNUM_CUR) {
      msgnum = jm->cur_msg;
   } else if (msgnum == MSGNUM_NEXT) {
      msgnum = jm->cur_msg+1;
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      } /* endif */
      jm->cur_msg = msgnum;
   } else if (msgnum == MSGNUM_PREV) {
      msgnum = jm->cur_msg-1;
      if (msgnum == 0) {
        msgapierr = MERR_NOENT;
        return NULL;
      } /* endif */
      jm->cur_msg = msgnum;
   } else {
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      } /* endif */
   }

   msgh = palloc(sizeof(struct _msgh));
   if (msgh == NULL) {
      msgapierr = MERR_NOMEM;
      return NULL;
   }

   memset(msgh, '\0', sizeof(struct _msgh));

   msgh->sq = jm;
   msgh->msgnum = msgnum;

   msgh->mode = mode;
   msgh->id = MSGH_ID;

/*   msgh->Idx.HdrOffset = 0xffffffff; */
/*   msgh->Idx.UserCRC   = 0xffffffff; */

   if (!Jmd->actmsg_read) {
      Jam_ActiveMsgs(jm);
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      }
   }

   if (Jmd->actmsg) {
      msgh->seek_idx = Jmd->actmsg[msgnum-1].IdxOffset;
      msgh->Idx.HdrOffset = Jmd->actmsg[msgnum-1].TrueMsg;
      msgh->Idx.UserCRC = Jmd->actmsg[msgnum-1].UserCRC;
      if (msgh->Idx.HdrOffset != 0xffffffffUL) {
         msgh->seek_hdr = msgh->Idx.HdrOffset;
         memcpy(&(msgh->Hdr), &(Jmd->actmsg[msgnum-1].hdr), sizeof(msgh->Hdr));
         if (stricmp((char*)&msgh->Hdr, "JAM") != 0) {
            pfree(msgh);
            return NULL;
         } else {
         } /* endif */
         if (mode == MOPEN_CREATE) return (MSGH *)msgh;

         msgh->SubFieldPtr = 0;

         if (Jmd->actmsg[msgnum-1].subfield)
            copy_subfield(&(msgh->SubFieldPtr), Jmd->actmsg[msgnum-1].subfield);
         else {
               lseek(Jmd->HdrHandle, Jmd->actmsg[msgnum-1].TrueMsg+HDR_SIZE, SEEK_SET);
               read_subfield(Jmd->HdrHandle, &(msgh->SubFieldPtr), &(Jmd->actmsg[msgnum-1].hdr.SubfieldLen));
         }
         DecodeSubf(msgh);
         return (MSGH *) msgh;
      }
   }

   pfree(msgh);
   return NULL;
}

JAMSUBFIELD2ptr Jam_GetSubField( MSGH *msgh, dword *SubPos, word what)
{
   JAMSUBFIELD2ptr SubField;
   JAMSUBFIELD2LISTptr SubFieldList;
   dword i;

   if( InvalidMsgh(msgh) )
   {
      msgapierr = MERR_BADA;
      return 0;
   }

   SubFieldList = msgh->SubFieldPtr;
   SubField = msgh->SubFieldPtr->subfield;
   for (i=*SubPos; i<SubFieldList->subfieldCount; i++, SubField++) {
      if (SubField->LoID == what) {
         *SubPos = i;
         return SubField;
      }
   }

   return NULL;
}

char *Jam_GetKludge(MSGA *jm, dword msgnum, word what)
{
   JAMSUBFIELD2LISTptr subf;
   JAMSUBFIELD2ptr subfptr;
   dword i;
   char *res;

   if( InvalidMh(jm) )
      return NULL;

   if (msgnum == MSGNUM_CUR) {
      msgnum = jm->cur_msg;
   } else if (msgnum == MSGNUM_NEXT) {
      msgnum = jm->cur_msg+1;
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      } /* endif */
      jm->cur_msg = msgnum;
   } else if (msgnum == MSGNUM_PREV) {
      msgnum = jm->cur_msg-1;
      if (msgnum == 0) {
        msgapierr = MERR_NOENT;
        return NULL;
      } /* endif */
      jm->cur_msg = msgnum;
   } else {
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      } /* endif */
   }

   if (!Jmd->actmsg_read) {
      Jam_ActiveMsgs(jm);
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      }
   }

   if (!Jmd->actmsg)
      return NULL;
   subf = Jmd->actmsg[msgnum-1].subfield;
   if (subf == NULL) {
      lseek(Jmd->HdrHandle, Jmd->actmsg[msgnum-1].TrueMsg+HDR_SIZE, SEEK_SET);
      read_subfield(Jmd->HdrHandle, &subf, &(Jmd->actmsg[msgnum-1].hdr.SubfieldLen));
   }
   for (i=0, subfptr = subf->subfield; i<subf->subfieldCount; i++, subfptr++)
      if (subfptr->LoID == what) {
         res = palloc(subfptr->DatLen + 1);
         if (res == NULL) {
            if (Jmd->actmsg[msgnum-1].subfield == NULL) pfree(subf);
            msgapierr = MERR_NOMEM;
            return NULL;
         }
         memcpy(res, subfptr->Buffer, subfptr->DatLen);
         res[subfptr->DatLen] = '\0';
         if (Jmd->actmsg[msgnum-1].subfield == NULL) pfree(subf);
         return res;
      }

   if (Jmd->actmsg[msgnum-1].subfield == NULL) pfree(subf);
   return NULL;
}

JAMHDR *Jam_GetHdr(MSGA *jm, dword msgnum)
{
   if( InvalidMh(jm) )
      return NULL;

   msgapierr = MERR_NONE;
   if (msgnum == MSGNUM_CUR) {
      msgnum = jm->cur_msg;
   } else if (msgnum == MSGNUM_NEXT) {
      msgnum = jm->cur_msg+1;
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      } /* endif */
      jm->cur_msg = msgnum;
   } else if (msgnum == MSGNUM_PREV) {
      msgnum = jm->cur_msg-1;
      if (msgnum == 0) {
        msgapierr = MERR_NOENT;
        return NULL;
      } /* endif */
      jm->cur_msg = msgnum;
   } else {
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      } /* endif */
   }

   if (!Jmd->actmsg_read) {
      Jam_ActiveMsgs(jm);
      if (msgnum > jm->num_msg) {
         msgapierr = MERR_NOENT;
         return NULL;
      }
   }

   if (!Jmd->actmsg)
      return NULL;

   return &(Jmd->actmsg[msgnum-1].hdr);
}

void Jam_WriteHdr(MSGA *jm, JAMHDR *jamhdr, dword msgnum)
{
   if( InvalidMh(jm) || !jamhdr )
   {
      msgapierr = MERR_BADA;
      return;
   }
   msgapierr = MERR_NONE;

   if (!Jmd->actmsg_read) Jam_ActiveMsgs(jm);

   if (!Jmd->actmsg)
      return;

   memcpy(&(Jmd->actmsg[msgnum-1].hdr), jamhdr, sizeof(JAMHDR));
   lseek(Jmd->HdrHandle, Jmd->actmsg[msgnum-1].TrueMsg, SEEK_SET);
   write_hdr(Jmd->HdrHandle, jamhdr);
}

dword Jam_HighMsg(JAMBASEptr jambase)
{
   dword highmsg;

   if( !jambase )
   {
      msgapierr = MERR_BADA;
      return 0;
   }
   msgapierr = MERR_NONE;

   lseek(jambase->IdxHandle, 0, SEEK_END);
   highmsg = tell(jambase->IdxHandle);
   return (highmsg / IDX_SIZE);
}

void Jam_ActiveMsgs(MSGA *jm)
{
   if( InvalidMh(jm) )
   {
      msgapierr = MERR_BADA;
      return;
   }
   msgapierr = MERR_NONE;

   read_allidx(Jmd);
   jm->num_msg = Jmd->HdrInfo.ActiveMsgs;
}

dword Jam_PosHdrMsg(MSGA * jm, dword msgnum, JAMIDXREC *jamidx, JAMHDR *jamhdr)
{

   if( InvalidMh(jm) ) return 0;
   if( !jamidx )
   {
      msgapierr = MERR_BADA;
      return 0;
   }
   msgapierr = MERR_NONE;

   if (!Jmd->actmsg_read) Jam_ActiveMsgs(jm);

   jamidx->HdrOffset = Jmd->actmsg[msgnum-1].TrueMsg;

   if (jamidx->HdrOffset == 0xffffffffUL) return 0;

   if (lseek(Jmd->HdrHandle, jamidx->HdrOffset, SEEK_SET) == -1) return 0;

   if (read_hdr(Jmd->HdrHandle, jamhdr) == 0) return 0;

   if (jamhdr->Attribute & JMSG_DELETED) return 0;

   return 1;
}


static int near Jam_Lock(MSGA *jm, int force)
{
    return !(mi.haveshare && ((force) ? waitlock(Jmd->HdrHandle, 0L, 1L)
                                      :     lock(Jmd->HdrHandle, 0L, 1L) == -1));
}

static void near Jam_Unlock(MSGA * jm)
{
    if (mi.haveshare)
    {
        unlock(Jmd->HdrHandle, 0L, 1L);
    }
}

sword Jam_WriteHdrInfo(JAMBASEptr jambase)
{
   if( !jambase )
   {
      msgapierr = MERR_BADA;
      return -1;
   }

   msgapierr = MERR_NONE;
   if (lseek(jambase->HdrHandle, 0, SEEK_SET) == -1) return -1;
   if (write_hdrinfo(jambase->HdrHandle, &(jambase->HdrInfo)) == 0) return -1;
   jambase->modified = 0;
   return 0;
}

static dword Jam_JamAttrToMsg(MSGH *msgh)
{
   dword attr = 0;

   if( InvalidMsgh(msgh) )
   {
      msgapierr = MERR_BADA;
      return 0;
   }

   if (msgh->Hdr.Attribute & JMSG_LOCAL)       attr |= MSGLOCAL;
   if (msgh->Hdr.Attribute & JMSG_PRIVATE)     attr |= MSGPRIVATE;
   if (msgh->Hdr.Attribute & JMSG_READ)        attr |= MSGREAD;
   if (msgh->Hdr.Attribute & JMSG_SENT)        attr |= MSGSENT;
   if (msgh->Hdr.Attribute & JMSG_KILLSENT)    attr |= MSGKILL;
   if (msgh->Hdr.Attribute & JMSG_HOLD)        attr |= MSGHOLD;
   if (msgh->Hdr.Attribute & JMSG_CRASH)       attr |= MSGCRASH;
   if (msgh->Hdr.Attribute & JMSG_FILEREQUEST) attr |= MSGFRQ;
   if (msgh->Hdr.Attribute & JMSG_FILEATTACH)  attr |= MSGFILE;
   if (msgh->Hdr.Attribute & JMSG_INTRANSIT)   attr |= MSGFWD;
   if (msgh->Hdr.Attribute & JMSG_RECEIPTREQ)  attr |= MSGRRQ;
   if (msgh->Hdr.Attribute & JMSG_ORPHAN)      attr |= MSGORPHAN;
   if (msgh->Hdr.Attribute & JMSG_CONFIRMREQ)  attr |= MSGCPT;
   if (msgh->Hdr.Attribute & JMSG_LOCKED)      attr |= MSGLOCKED;
   if (msgh->Hdr.Attribute & JMSG_DIRECT)      attr |= MSGXX2;
   if (msgh->Hdr.Attribute & JMSG_IMMEDIATE)   attr |= MSGIMM;

   return attr;
}

static dword Jam_MsgAttrToJam(XMSG *msg)
{
   dword attr = 0;

   if( InvalidMsg(msg) )
      return 0;

   if (msg->attr & MSGLOCAL)   attr |= JMSG_LOCAL;
   if (msg->attr & MSGPRIVATE) attr |= JMSG_PRIVATE;
   if (msg->attr & MSGREAD)    attr |= JMSG_READ;
   if (msg->attr & MSGSENT)    attr |= JMSG_SENT;
   if (msg->attr & MSGKILL)    attr |= JMSG_KILLSENT;
   if (msg->attr & MSGHOLD)    attr |= JMSG_HOLD;
   if (msg->attr & MSGCRASH)   attr |= JMSG_CRASH;
   if (msg->attr & MSGFRQ)     attr |= JMSG_FILEREQUEST;
   if (msg->attr & MSGFILE)    attr |= JMSG_FILEATTACH;
   if (msg->attr & MSGFWD)     attr |= JMSG_INTRANSIT;
   if (msg->attr & MSGRRQ)     attr |= JMSG_RECEIPTREQ;
   if (msg->attr & MSGORPHAN)  attr |= JMSG_ORPHAN;
   if (msg->attr & MSGCPT)     attr |= JMSG_CONFIRMREQ;
   if (msg->attr & MSGLOCKED)  attr |= JMSG_LOCKED;
   if (msg->attr & MSGXX2)     attr |= JMSG_DIRECT;
   if (msg->attr & MSGIMM)     attr |= JMSG_IMMEDIATE;

   return attr;
}

static int StrToSubfield(const unsigned char *str, dword lstr, dword *len, JAMSUBFIELD2ptr subf)
{
   /* warning: str is read only and NOT nul-terminated! */
   const unsigned char *kludge;
   word subtypes;

   if ( !str || !subf)
   { msgapierr = MERR_BADA;
     return 0;
   }

   msgapierr = MERR_NONE;
   while (lstr > 0)
      if (str[lstr-1] == '\r')
         lstr--;
      else
         break;

   kludge = str;
   subtypes = JAMSFLD_FTSKLUDGE;

   switch (*str) {
      case 'F':  if (lstr>5 && strncmp((char*)str, "FMPT ", 5) == 0)
                    return 0;
                 else if (lstr>6 && strncmp((char*)str, "FLAGS ", 6) == 0) {
                    kludge = str+6;
                    subtypes = JAMSFLD_FLAGS;
                 }
                 break;
      case 'I':  if (lstr>4 && strncmp((char*)str, "INTL ", 4) == 0)
                    return 0;
                 break;
      case 'M':  if (lstr>7 && strncmp((char*)str, "MSGID: ", 7) == 0) {
                    kludge = str+7;
                    subtypes = JAMSFLD_MSGID;
                 }
                 break;
      case 'P':  if (lstr>6 && strncmp((char*)str, "PATH: ", 6) == 0) {
                    kludge = str+6;
                    subtypes = JAMSFLD_PATH2D;
                 } else if (lstr>5 && strncmp((char*)str, "PID: ", 5) == 0) {
                    kludge = str+5;
                    subtypes = JAMSFLD_PID;
                 }
                 break;
      case 'R':  if (lstr>7 && strncmp((char*)str, "REPLY: ", 7) == 0) {
                    kludge = str+7;
                    subtypes = JAMSFLD_REPLYID;
                 }
                 break;
      case 'S':  if (lstr>9 && strncmp((char*)str, "SEEN-BY: ", 9) == 0) {
                    kludge = str+9;
                    subtypes = JAMSFLD_SEENBY2D;
                 }
                 break;
      case 'T':  if (lstr>5 && strncmp((char*)str, "TOPT ", 5) == 0)
                    return 0;
                 else if (lstr>7 && strncmp((char *)str, "TZUTC: ", 7) == 0) {
                    kludge = str+7;
                    subtypes = JAMSFLD_TZUTCINFO;
                 }
                 break;
      case 'V':  if (lstr>4 && strncmp((char*)str, "Via ", 4) == 0) {
                    kludge = str+4;
                    subtypes = JAMSFLD_TRACE;
                 }
   }
   subf->LoID = subtypes;
   subf->HiID = 0;
   subf->DatLen = lstr-(kludge-str);
   memcpy(subf->Buffer, kludge, subf->DatLen);
   subf->Buffer[subf->DatLen] = '\0';
   if(len) *len = sizeof(JAMBINSUBFIELD)+subf->DatLen;
   return 1;
}

static int NETADDRtoSubf(NETADDR addr, dword *len, word opt, JAMSUBFIELD2ptr subf)
{
   /* opt = 0 origaddr */
   /* opt = 1 destaddr */

   if (!subf)
   { msgapierr = MERR_BADA;
     return 0;
   }

   if (addr.zone==0 && addr.net==0 && addr.node==0 && addr.point==0)
      return 0;
   if (addr.point) {
      sprintf((char*)(subf->Buffer), "%d:%d/%d.%d", addr.zone, addr.net, addr.node, addr.point);
   } else {
      sprintf((char*)(subf->Buffer), "%d:%d/%d", addr.zone, addr.net, addr.node);
   } /* endif */

   subf->DatLen = strlen((char*)(subf->Buffer));
   if(len){
     *len = subf->DatLen + sizeof(JAMBINSUBFIELD);
   }
   subf->LoID = opt ? JAMSFLD_DADDRESS : JAMSFLD_OADDRESS;
   subf->HiID = 0;

   msgapierr = MERR_NONE;
   return 1;
}

static int FromToSubjTOSubf(word jamsfld, unsigned char *txt, dword *len, JAMSUBFIELD2ptr subf)
{
   if (!subf)
   { msgapierr = MERR_BADA;
     return 0;
   }
   subf->LoID = jamsfld;
   subf->HiID = 0;
   if(txt) memmove(subf->Buffer, txt, subf->DatLen = strlen((char*)txt));
   else{ subf->Buffer = NULL; subf->DatLen = 0; }
   if(len) *len = subf->DatLen + sizeof(JAMBINSUBFIELD);

   return 1;
}

static void MSGAPI ConvertXmsgToJamHdr(MSGH *msgh, XMSG *msg, JAMHDRptr jamhdr, JAMSUBFIELD2LISTptr *subfield)
{
   JAMSUBFIELD2ptr SubFieldCur;
   struct tm stm, *ptm;
   dword clen, sublen;

   memset(jamhdr, '\0', sizeof(JAMHDR));

   if( InvalidMsgh(msgh) || InvalidMsg(msg) )
      return;

   clen = msgh->sq->isecho ? 3 : 5;
   sublen = sizeof(JAMSUBFIELD2LIST)+sizeof(JAMSUBFIELD2)*clen+37+37+73+
            (msgh->sq->isecho ? 0 : 30*2);
   *subfield = palloc(sublen);
   if (*subfield==NULL) {
      msgapierr = MERR_NOMEM;
      return;
   }

   subfield[0]->arraySize = sublen;
   subfield[0]->subfieldCount = 0;
   subfield[0]->subfield[0].Buffer = (unsigned char *)&(subfield[0]->subfield[clen+1]);

   jamhdr->Attribute = Jam_MsgAttrToJam(msg);
   if (msgh->sq->isecho != NOTH) {
      if (msgh->sq->isecho) {
         jamhdr->Attribute |= JMSG_TYPEECHO;
      } else {
         jamhdr->Attribute |= JMSG_TYPENET;
      } /* endif */
   }
   strcpy((char*)(jamhdr->Signature), HEADERSIGNATURE);
   jamhdr->Revision = CURRENTREVLEV;
   if (((SCOMBO*)&(msg->date_arrived))->ldate) {
      /* save arrived date for sqpack */
      ptm = &stm;
      ptm = DosDate_to_TmDate((SCOMBO*)(&(msg->date_arrived)), ptm);
      jamhdr->DateProcessed = mktime(ptm) + gettz();
   }
   else
      jamhdr->DateProcessed = time(NULL) + gettz();
   ptm = &stm;
   ptm = DosDate_to_TmDate((SCOMBO*)(&(msg->date_written)), ptm);
   jamhdr->DateWritten = mktime(ptm) + gettz();

   sublen = 0;

   /* From Name */

   SubFieldCur = subfield[0]->subfield;
   if (FromToSubjTOSubf(JAMSFLD_SENDERNAME, msg->from, &clen, SubFieldCur)) {
      SubFieldCur[1].Buffer = SubFieldCur->Buffer+SubFieldCur->DatLen+1;
      subfield[0]->subfieldCount++;
      SubFieldCur++;
      sublen += clen;
   } /* endif */

   /* To Name */

   if (FromToSubjTOSubf(JAMSFLD_RECVRNAME, msg->to, &clen, SubFieldCur)) {
      SubFieldCur[1].Buffer = SubFieldCur->Buffer+SubFieldCur->DatLen+1;
      subfield[0]->subfieldCount++;
      SubFieldCur++;
      sublen += clen;
   } /* endif */

   /* Subject */

   if (FromToSubjTOSubf(JAMSFLD_SUBJECT, msg->subj, &clen, SubFieldCur)) {
      SubFieldCur[1].Buffer = SubFieldCur->Buffer+SubFieldCur->DatLen+1;
      subfield[0]->subfieldCount++;
      SubFieldCur++;
      sublen += clen;
   } /* endif */

   if (!msgh->sq->isecho) {

      /* Orig Address */

      if (NETADDRtoSubf(msg->orig, &clen, 0, SubFieldCur)) {
         SubFieldCur[1].Buffer = SubFieldCur->Buffer+SubFieldCur->DatLen+1;
         subfield[0]->subfieldCount++;
         SubFieldCur++;
         sublen += clen;
      } /* endif */

      /* Dest Address */

      if (NETADDRtoSubf(msg->dest, &clen, 1, SubFieldCur)) {
         SubFieldCur[1].Buffer = SubFieldCur->Buffer+SubFieldCur->DatLen+1;
         subfield[0]->subfieldCount++;
         SubFieldCur++;
         sublen += clen;
      } /* endif */

   }
   assert(SubFieldCur->Buffer<=(byte *)*subfield+subfield[0]->arraySize);
   assert((byte *)(SubFieldCur+1)<=subfield[0]->subfield[0].Buffer);

   jamhdr->SubfieldLen = sublen;
   jamhdr->PasswordCRC = 0xFFFFFFFFUL;

   jamhdr->ReplyTo = msg->replyto;
   jamhdr->Reply1st = msg->xmreply1st;
   jamhdr->ReplyNext = msg->xmreplynext;
   jamhdr->TimesRead = msg->xmtimesread;
   jamhdr->Cost = msg->xmcost;
   msgapierr = MERR_NONE;
}

static void resize_subfields(JAMSUBFIELD2LISTptr *subfield, dword newcount,
                             dword len)
{
   dword offs;
   int i;
   JAMSUBFIELD2LISTptr SubField;

   if( !subfield || len==0 )
   {
        msgapierr = MERR_BADA;
        return;
   }

   SubField = palloc(len);
   if (!SubField) {
      msgapierr = MERR_NOMEM;
      return;
   }

   SubField->arraySize = len;
   SubField->subfieldCount = subfield[0]->subfieldCount;
   if (subfield[0]->subfieldCount == 0)
      SubField->subfield[0].Buffer = (unsigned char *)&(SubField->subfield[SubField->subfieldCount + newcount]);
   else {
      memcpy(SubField->subfield, subfield[0]->subfield,
             SubField->subfieldCount * sizeof(JAMSUBFIELD2));
      SubField->subfield[SubField->subfieldCount].Buffer =
         subfield[0]->subfield[SubField->subfieldCount-1].Buffer +
         subfield[0]->subfield[SubField->subfieldCount-1].DatLen;
   }
   offs=(byte *)&(SubField->subfield[newcount])-subfield[0]->subfield[0].Buffer;
   for (i=subfield[0]->subfieldCount; i>=0; i--)
      SubField->subfield[i].Buffer += offs;
   memcpy(SubField->subfield[0].Buffer, subfield[0]->subfield[0].Buffer,
    subfield[0]->arraySize-((char *)(subfield[0]->subfield[0].Buffer)-(char *)(*subfield)));

   freejamsubfield(*subfield);
   *subfield = SubField;
   assert(subfield[0]->subfield[subfield[0]->subfieldCount].Buffer<=(byte *)*subfield+subfield[0]->arraySize);
   assert((byte *)&(subfield[0]->subfield[newcount])==subfield[0]->subfield[0].Buffer);
   msgapierr = MERR_NONE;
}

static void MSGAPI ConvertCtrlToSubf(JAMHDRptr jamhdr, JAMSUBFIELD2LISTptr
                                   *subfield, dword clen, unsigned char *ctxt)
{
   JAMSUBFIELD2ptr SubField;
   dword len, i;
   unsigned char *ptr;

   if( !subfield || !ctxt || !jamhdr )
   {
        msgapierr = MERR_BADA;
        return;
   }

   /* count new subfields */
   i = *ctxt ? 2 : 1;
   for (ptr=ctxt, len=0; len<clen; len++)
     if (*ptr++=='\001') i++;
   resize_subfields(subfield, subfield[0]->subfieldCount+i,
                    subfield[0]->arraySize + clen + i + i*sizeof(JAMSUBFIELD2));
   SubField = &(subfield[0]->subfield[subfield[0]->subfieldCount]);

   for (ptr=ctxt, len=0; len<=clen; len++, ptr++) {
      if (*ptr=='\0' || len==clen || *ptr=='\001') {
         if (*ctxt && *ctxt!='\001' &&
             StrToSubfield(ctxt, ptr-ctxt, &i, SubField)) {
            SubField[1].Buffer = SubField->Buffer+SubField->DatLen+1;
            jamhdr->SubfieldLen += i;
            subfield[0]->subfieldCount++;
            if (SubField->LoID==JAMSFLD_MSGID)
               jamhdr->MsgIdCRC=Jam_Crc32(SubField->Buffer, SubField->DatLen);
            else if (SubField->LoID==JAMSFLD_REPLYID)
               jamhdr->ReplyCRC=Jam_Crc32(SubField->Buffer, SubField->DatLen);
            SubField++;
         }
         if (*ptr=='\0' || len==clen)
            break;
         ctxt = ptr+1;
      }
   }
   assert(SubField->Buffer<=(byte *)*subfield+subfield[0]->arraySize);
   assert((byte *)(SubField+1)<=subfield[0]->subfield[0].Buffer);
   msgapierr = MERR_NONE;
}

unsigned char *DelimText(JAMHDRptr jamhdr, JAMSUBFIELD2LISTptr *subfield,
                         unsigned char *text, size_t textlen)
{
   JAMSUBFIELD2ptr SubField;
   dword clen, firstlen, i, len;
   unsigned char *onlytext, *first, *ptr, *curtext;

   if( !subfield || !jamhdr )
   {
        msgapierr = MERR_BADA;
        return NULL;
   }

   if (text && textlen)
   {
       if (text[textlen-1] != '\r')
           textlen++;
       onlytext = curtext = (unsigned char*)palloc(textlen + 1);
       if (!onlytext) {
          msgapierr = MERR_NOMEM;
          return NULL;
       }
       *onlytext = '\0';

       /* count subfields */
       i = 1;
       len = 0;
       for (first = text; first<text+textlen; first = ptr+1) {
          ptr = (unsigned char *)strchr((char*)first, '\r');
          if (ptr==NULL) ptr=text+textlen;
          if (*first == '\001' || strncmp((char *)first, "SEEN-BY: ", 9) == 0) {
             if (*first == '\001') first++;
             i++;
             len += (ptr-first);
          }
       }
       resize_subfields(subfield, subfield[0]->subfieldCount+i,
                     subfield[0]->arraySize + len + i + i*sizeof(JAMSUBFIELD2));
       SubField = &(subfield[0]->subfield[subfield[0]->subfieldCount]);

       first = text;
       while (*first) {
           ptr = (unsigned char *)strchr((char *)first, '\r');
           if (ptr) *ptr = '\0';
           firstlen = ptr ? (ptr-first) : strlen((char *)first);
           if ((firstlen > 9 && strncmp((char *)first, "SEEN-BY: ", 9) == 0)  ||
               *first == '\001') {

               if (*first == '\001') {
                   first++;
                   firstlen--;
               }

               if (StrToSubfield(first, firstlen, &clen, SubField)) {
                  SubField[1].Buffer = SubField->Buffer+SubField->DatLen+1;
                  jamhdr->SubfieldLen += clen;
                  subfield[0]->subfieldCount++;
                  if (SubField->LoID==JAMSFLD_MSGID)
                     jamhdr->MsgIdCRC=Jam_Crc32(SubField->Buffer, SubField->DatLen);
                  else if (SubField->LoID==JAMSFLD_REPLYID)
                     jamhdr->ReplyCRC=Jam_Crc32(SubField->Buffer, SubField->DatLen);
                  SubField++;
               }
           } else {
               assert((curtext - onlytext) + firstlen +1  <= textlen);
               strcpy((char *)curtext, (char *)first);
               curtext+=firstlen;
               *curtext++ = '\r';
               *curtext = '\0';
           } /* endif */
           if (ptr) {
               *ptr = '\r';
               first = ptr+1;
           } else {
               first += firstlen;
           } /* endif */
       } /* endwhile */
       assert(SubField->Buffer<=(byte *)*subfield+subfield[0]->arraySize);
       assert((byte *)(SubField+1)<=subfield[0]->subfield[0].Buffer);
   }
   else
   {
       onlytext = NULL;
   }

   msgapierr = MERR_NONE;
   return onlytext;
}

void parseAddr(NETADDR *netAddr, unsigned char *str, dword len)
{
   char *strAddr, *ptr, *tmp, ch[10];

   if(!str || !netAddr)
   {
        msgapierr = MERR_BADA;
        return;
   }

   strAddr = (char*)calloc(len+1, sizeof(char*));
   if (!strAddr) {
        msgapierr = MERR_NOMEM;
        return;
   }
   memset(netAddr, '\0', sizeof(NETADDR));

   strncpy(strAddr, (char *)str, len);

   ptr = strchr(strAddr, '@');

   if (ptr)  *ptr = '\0';

   ptr = strchr(strAddr, ':');
   if (ptr) {
      memset(ch, '\0', sizeof(ch));
      strncpy(ch, strAddr, ptr-strAddr);
      netAddr->zone = atoi(ch);
      tmp = ++ptr;
   } else {
      tmp = strAddr;
      netAddr->zone = 0;
   } /* endif */

   ptr = strchr(tmp, '/');
   if (ptr) {
      memset(ch, '\0', sizeof(ch));
      strncpy(ch, tmp, ptr-tmp);
      netAddr->net = atoi(ch);
      tmp = ++ptr;
   } else {
      netAddr->net = 0;
   } /* endif */

   ptr = strchr(tmp, '.');
   if (ptr) {
      memset(ch, '\0', sizeof(ch));
      strncpy(ch, tmp, ptr-tmp);
      netAddr->node = atoi(ch);
      ptr++;
      netAddr->point = atoi(ptr);
   } else {
      netAddr->node = atoi(tmp);
      netAddr->point = 0;
   } /* endif */
   msgapierr = MERR_NONE;
}

static void addkludge(char **line, char *kludge, char *ent, char *lf, dword len)
{
   if( !line || !*line || !kludge || !ent || !lf )
   {
        msgapierr = MERR_BADA;
        return;
   }

    strcpy(*line, kludge);
    *line += strlen(kludge);
    strncpy(*line, ent, len);
    (*line)[len]='\0';
    *line += strlen(*line);
    strcpy(*line, lf);
    *line += strlen(*line);
}

void DecodeSubf(MSGH *msgh)
{
   dword  SubPos;
   JAMSUBFIELD2ptr SubField;
   JAMSUBFIELD2LISTptr sfl;
   char *ptr, *pctrl, *plctrl, *fmpt, *topt;
   char orig[30], dest[30];
   dword  i;

   if( InvalidMsgh(msgh) ) return;

   msgh->ctrl = (unsigned char*)palloc(msgh->SubFieldPtr->arraySize+65);
   msgh->lctrl = (unsigned char*)palloc(msgh->SubFieldPtr->arraySize+65);
   if (!(msgh->ctrl && msgh->lctrl)) {
      pfree(msgh->ctrl);
      pfree(msgh->lctrl);
      msgapierr = MERR_NOMEM;
      return;
   }

   *(msgh->ctrl)=*(msgh->lctrl)='\0';
   pctrl = (char *)(msgh->ctrl);
   plctrl = (char *)(msgh->lctrl);
   orig[0] = dest[0] = '\0';

   if (!msgh->sq->isecho) {
      SubPos = 0;
      if ((SubField = Jam_GetSubField(msgh, &SubPos, JAMSFLD_OADDRESS)))
         strncpy(orig, (char *)(SubField->Buffer), min(SubField->DatLen, sizeof(orig)));
      SubPos = 0;
      if ((SubField = Jam_GetSubField(msgh, &SubPos, JAMSFLD_DADDRESS)))
         strncpy(dest, (char *)(SubField->Buffer), min(SubField->DatLen, sizeof(dest)));
      fmpt = topt = NULL;
      if (orig[0]) {
         ptr = strchr(orig, '@');
         if (ptr) *ptr = '\0';
         ptr = strchr(orig, '.');
         if (ptr) {
            *ptr++ = '\0';
            if (atoi(ptr) != 0) fmpt = ptr;
         }
      }
      if (dest[0]) {
         ptr = strchr(dest, '@');
         if (ptr) *ptr = '\0';
         ptr = strchr(dest, '.');
         if (ptr) {
            *ptr++ = '\0';
            if (atoi(ptr) != 0) topt = ptr;
         }
      }
      if (orig[0] && dest[0]) {
         strcpy(pctrl, "\001" "INTL "); pctrl+=strlen(pctrl);
         strcpy(pctrl, dest); pctrl+=strlen(pctrl);
         strcpy(pctrl, " "); pctrl++;
         strcpy(pctrl, orig); pctrl+=strlen(pctrl);
      }
      if (fmpt)
         addkludge(&pctrl, "\001" "FMPT ", "", fmpt, 0);
      if (topt)
         addkludge(&pctrl, "\001" "TOPT ", "", topt, 0);
      orig[0] = dest[0] = '\0';
   }

   SubPos = 0;

   sfl = msgh->SubFieldPtr;

   SubField = &(sfl->subfield[0]);
   for (i=0; i<sfl->subfieldCount; i++, SubField++) {

      if (SubField->LoID == JAMSFLD_MSGID)
         addkludge(&pctrl, "\001MSGID: ", (char *)(SubField->Buffer), "", SubField->DatLen);
      else if (SubField->LoID == JAMSFLD_REPLYID)
         addkludge(&pctrl, "\001REPLY: ", (char *)(SubField->Buffer), "", SubField->DatLen);
      else if (SubField->LoID == JAMSFLD_PID)
         addkludge(&pctrl, "\001PID: ", (char *)(SubField->Buffer), "", SubField->DatLen);
      else if (SubField->LoID == JAMSFLD_TRACE)
         addkludge(&plctrl, "\001Via ", (char *)(SubField->Buffer), "\r", SubField->DatLen);
      else if (SubField->LoID == JAMSFLD_FTSKLUDGE) {
         if (strncasecmp((char *)(SubField->Buffer), "Via", 3) == 0 ||
             strncasecmp((char *)(SubField->Buffer), "Recd", 4) == 0)
              addkludge(&plctrl, "\001", (char *)(SubField->Buffer), "\r", SubField->DatLen);
         else
              addkludge(&pctrl, "\001", (char *)(SubField->Buffer), "", SubField->DatLen);
      }
      else if (SubField->LoID == JAMSFLD_FLAGS)
         addkludge(&pctrl, "\001" "FLAGS ", (char *)(SubField->Buffer), "", SubField->DatLen);
      else if (SubField->LoID == JAMSFLD_PATH2D)
         addkludge(&plctrl, "\001PATH: ", (char *)(SubField->Buffer), "\r", SubField->DatLen);
      else if (SubField->LoID == JAMSFLD_SEENBY2D)
         addkludge(&plctrl, "SEEN-BY: ", (char *)(SubField->Buffer), "\r", SubField->DatLen);
      else if (SubField->LoID == JAMSFLD_TZUTCINFO)
         addkludge(&pctrl, "\001TZUTC: ", (char *)(SubField->Buffer), "", SubField->DatLen);
   } /* endwhile */
   msgh->clen  = pctrl -(char *)msgh->ctrl;
   msgh->lclen = plctrl-(char *)msgh->lctrl;
   assert(msgh->clen <msgh->SubFieldPtr->arraySize+65);
   assert(msgh->lclen<msgh->SubFieldPtr->arraySize+65);
   msgh->ctrl  = (unsigned char *)realloc(msgh->ctrl,  msgh->clen  + 1);
   msgh->lctrl = (unsigned char *)realloc(msgh->lctrl, msgh->lclen + 1);
   if (!(msgh->ctrl && msgh->lctrl)) {
      msgapierr = MERR_NOMEM;
      return;
   }
   msgapierr = MERR_NONE;
}

/***********************************************************************
**
**  Crc32 lookup table
**
***********************************************************************/
static long crc32tab[256]= {
              0L,  1996959894L,  -301047508L, -1727442502L,   124634137L,
     1886057615L,  -379345611L, -1637575261L,   249268274L,  2044508324L,
     -522852066L, -1747789432L,   162941995L,  2125561021L,  -407360249L,
    -1866523247L,   498536548L,  1789927666L,  -205950648L, -2067906082L,
      450548861L,  1843258603L,  -187386543L, -2083289657L,   325883990L,
     1684777152L,   -43845254L, -1973040660L,   335633487L,  1661365465L,
      -99664541L, -1928851979L,   997073096L,  1281953886L,  -715111964L,
    -1570279054L,  1006888145L,  1258607687L,  -770865667L, -1526024853L,
      901097722L,  1119000684L,  -608450090L, -1396901568L,   853044451L,
     1172266101L,  -589951537L, -1412350631L,   651767980L,  1373503546L,
     -925412992L, -1076862698L,   565507253L,  1454621731L,  -809855591L,
    -1195530993L,   671266974L,  1594198024L,  -972236366L, -1324619484L,
      795835527L,  1483230225L, -1050600021L, -1234817731L,  1994146192L,
       31158534L, -1731059524L,  -271249366L,  1907459465L,   112637215L,
    -1614814043L,  -390540237L,  2013776290L,   251722036L, -1777751922L,
     -519137256L,  2137656763L,   141376813L, -1855689577L,  -429695999L,
     1802195444L,   476864866L, -2056965928L,  -228458418L,  1812370925L,
      453092731L, -2113342271L,  -183516073L,  1706088902L,   314042704L,
    -1950435094L,   -54949764L,  1658658271L,   366619977L, -1932296973L,
      -69972891L,  1303535960L,   984961486L, -1547960204L,  -725929758L,
     1256170817L,  1037604311L, -1529756563L,  -740887301L,  1131014506L,
      879679996L, -1385723834L,  -631195440L,  1141124467L,   855842277L,
    -1442165665L,  -586318647L,  1342533948L,   654459306L, -1106571248L,
     -921952122L,  1466479909L,   544179635L, -1184443383L,  -832445281L,
     1591671054L,   702138776L, -1328506846L,  -942167884L,  1504918807L,
      783551873L, -1212326853L, -1061524307L,  -306674912L, -1698712650L,
       62317068L,  1957810842L,  -355121351L, -1647151185L,    81470997L,
     1943803523L,  -480048366L, -1805370492L,   225274430L,  2053790376L,
     -468791541L, -1828061283L,   167816743L,  2097651377L,  -267414716L,
    -2029476910L,   503444072L,  1762050814L,  -144550051L, -2140837941L,
      426522225L,  1852507879L,   -19653770L, -1982649376L,   282753626L,
     1742555852L,  -105259153L, -1900089351L,   397917763L,  1622183637L,
     -690576408L, -1580100738L,   953729732L,  1340076626L,  -776247311L,
    -1497606297L,  1068828381L,  1219638859L,  -670225446L, -1358292148L,
      906185462L,  1090812512L,  -547295293L, -1469587627L,   829329135L,
     1181335161L,  -882789492L, -1134132454L,   628085408L,  1382605366L,
     -871598187L, -1156888829L,   570562233L,  1426400815L,  -977650754L,
    -1296233688L,   733239954L,  1555261956L, -1026031705L, -1244606671L,
      752459403L,  1541320221L, -1687895376L,  -328994266L,  1969922972L,
       40735498L, -1677130071L,  -351390145L,  1913087877L,    83908371L,
    -1782625662L,  -491226604L,  2075208622L,   213261112L, -1831694693L,
     -438977011L,  2094854071L,   198958881L, -2032938284L,  -237706686L,
     1759359992L,   534414190L, -2118248755L,  -155638181L,  1873836001L,
      414664567L, -2012718362L,   -15766928L,  1711684554L,   285281116L,
    -1889165569L,  -127750551L,  1634467795L,   376229701L, -1609899400L,
     -686959890L,  1308918612L,   956543938L, -1486412191L,  -799009033L,
     1231636301L,  1047427035L, -1362007478L,  -640263460L,  1088359270L,
      936918000L, -1447252397L,  -558129467L,  1202900863L,   817233897L,
    -1111625188L,  -893730166L,  1404277552L,   615818150L, -1160759803L,
     -841546093L,  1423857449L,   601450431L, -1285129682L, -1000256840L,
     1567103746L,   711928724L, -1274298825L, -1022587231L,  1510334235L,
      755167117L
};


/***********************************************************************
**
**  JAM_Crc32 - Calculate CRC32 on a data block
**
**  Note: This function returns data, NOT a status code!
**
***********************************************************************/
dword Jam_Crc32(unsigned char* buff, dword len)
{
    dword crc = 0xffffffffUL;
    unsigned char *ptr = buff;

    if( buff )
      for (; len; len--, ptr++)
        crc=(crc >> 8) ^ crc32tab [(int) ((crc^tolower(*ptr)) & 0xffUL)];
    return crc;
}


static dword _XPENTRY JamGetHash(HAREA mh, dword msgnum)
{
  XMSG xmsg;
  HMSG msgh;
  dword rc = 0l;

  if ((msgh=JamOpenMsg(mh, MOPEN_READ, msgnum))==NULL)
    return (dword) 0l;

  if (JamReadMsg(msgh, &xmsg, 0L, 0L, NULL, 0L, NULL)!=(dword)-1)
  {
    rc = SquishHash(xmsg.to) | (xmsg.attr & MSGREAD) ? 0x80000000l : 0;
  }

  JamCloseMsg(msgh);

  msgapierr=MERR_NONE;
  return rc;
}

static UMSGID _XPENTRY JamGetNextUid(HAREA ha)
{
  if (InvalidMh(ha))
    return 0L;

  if (!ha->locked)
  {
    msgapierr=MERR_NOLOCK;
    return 0L;
  }

  msgapierr=MERR_NONE;
  return ha->high_msg+1;
}
