/*****************************************************************************
 *
 * File ..................: msgbase/jammsg.c 
 * Purpose ...............: JAM message base functions
 * Last modification date : 09-Nov-1999
 *
 *****************************************************************************
 *
 * Original written in C++ by Marco Maccaferri for LoraBBS and was 
 * distributed under GNU GPL. This version is modified for use with
 * MBSE BBS and utilities.
 *
 *****************************************************************************
 * Copyright (C) 1997-1999
 *   
 * Michiel Broek		FIDO:		2:2801/16
 * Beekmansbos 10		Internet:	mbroek@ux123.pttnwb.nl
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MBSE BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/


#include "../include/libs.h"
#include "msgtext.h"
#include "msg.h"
#include "jam.h"
#include "jammsg.h"

#define	MAX_TEXT	2048

int		fdHdr = -1;
int		fdJdt = -1;
int		fdJdx = -1;
int		fdJlr = -1;
char		*pSubfield;
char		BaseName[128];
char		*pLine, *pBuff;
char		szBuff[MAX_LINE_LENGTH + 1];
char		szLine[MAX_LINE_LENGTH + 1];
JAMHDRINFO	jamHdrInfo;
JAMHDR		jamHdr;
unsigned long	LastReadRec;



unsigned long AddSubfield(unsigned int, char *);
unsigned long AddSubfield(unsigned int JamSFld, char *SubStr)
{
	JAMSUBFIELD	jamSubfield;
	unsigned long	Len;

	jamSubfield.HiID = 0;
	jamSubfield.LoID = JamSFld;
	jamSubfield.DatLen = strlen(SubStr); /* + 1; */
	Len = jamSubfield.DatLen + sizeof(JAMBINSUBFIELD);
	write(fdHdr, &jamSubfield, sizeof(JAMBINSUBFIELD));
	write(fdHdr, SubStr, strlen(SubStr)); /* + 1); */

	return Len;
}



void JAMset_flags(void);
void JAMset_flags()
{
	jamHdr.Attribute |= (Msg.Local)          ? MSG_LOCAL : 0;
	jamHdr.Attribute |= (Msg.Intransit)      ? MSG_INTRANSIT : 0;
	jamHdr.Attribute |= (Msg.Private)        ? MSG_PRIVATE : 0;
	jamHdr.Attribute |= (Msg.Received)       ? MSG_READ : 0;
	jamHdr.Attribute |= (Msg.Sent)           ? MSG_SENT : 0;
	jamHdr.Attribute |= (Msg.KillSent)       ? MSG_KILLSENT : 0;
	jamHdr.Attribute |= (Msg.ArchiveSent)    ? MSG_ARCHIVESENT : 0;
	jamHdr.Attribute |= (Msg.Hold)           ? MSG_HOLD : 0;
	jamHdr.Attribute |= (Msg.Crash)          ? MSG_CRASH : 0;
	jamHdr.Attribute |= (Msg.Immediate)      ? MSG_IMMEDIATE : 0;
	jamHdr.Attribute |= (Msg.Direct)         ? MSG_DIRECT : 0;
	jamHdr.Attribute |= (Msg.Gate)           ? MSG_GATE : 0;
	jamHdr.Attribute |= (Msg.FileRequest)    ? MSG_FILEREQUEST : 0;
	jamHdr.Attribute |= (Msg.FileAttach)     ? MSG_FILEATTACH : 0;
	jamHdr.Attribute |= (Msg.TruncFile)      ? MSG_TRUNCFILE : 0;
	jamHdr.Attribute |= (Msg.KillFile)       ? MSG_KILLFILE : 0;
	jamHdr.Attribute |= (Msg.ReceiptRequest) ? MSG_RECEIPTREQ : 0;
	jamHdr.Attribute |= (Msg.ConfirmRequest) ? MSG_CONFIRMREQ : 0;
	jamHdr.Attribute |= (Msg.Orphan)         ? MSG_ORPHAN : 0;
	jamHdr.Attribute |= (Msg.Encrypt)        ? MSG_ENCRYPT : 0;
	jamHdr.Attribute |= (Msg.Compressed)     ? MSG_COMPRESS : 0;
	jamHdr.Attribute |= (Msg.Escaped)        ? MSG_ESCAPED : 0;
	jamHdr.Attribute |= (Msg.ForcePU)	 ? MSG_FPU : 0;
	jamHdr.Attribute |= (Msg.Localmail)      ? MSG_TYPELOCAL : 0;
	jamHdr.Attribute |= (Msg.Echomail)	 ? MSG_TYPEECHO : 0;
	jamHdr.Attribute |= (Msg.Netmail)        ? MSG_TYPENET : 0;
	jamHdr.Attribute |= (Msg.News)		 ? MSG_TYPENEWS : 0;
	jamHdr.Attribute |= (Msg.Email)		 ? MSG_TYPEEMAIL : 0;
	jamHdr.Attribute |= (Msg.Nodisplay)	 ? MSG_NODISP : 0;
	jamHdr.Attribute |= (Msg.Locked)         ? MSG_LOCKED : 0;
	jamHdr.Attribute |= (Msg.Deleted)        ? MSG_DELETED : 0;

	jamHdr.ReplyTo = Msg.Original;
	jamHdr.ReplyNext = Msg.Reply;
	jamHdr.DateReceived = Msg.Read;
}




/*
 * Add a message, the structure msg must contain all needed
 * information.
 */
int JAM_AddMsg()
{
	int		i, RetVal = TRUE;
	unsigned long	ulMsg = JAM_Highest() + 1L;
	char		*pszText, *Sign= (char *)HEADERSIGNATURE;
	JAMIDXREC	jamIdx;
	int		Oke;

	memset(&jamHdr, 0, sizeof(JAMHDR));
	jamHdr.Signature[0] = Sign[0];
	jamHdr.Signature[1] = Sign[1];
	jamHdr.Signature[2] = Sign[2];
	jamHdr.Signature[3] = Sign[3];
	jamHdr.Revision = CURRENTREVLEV;
	jamHdr.MsgNum = ulMsg;

	jamHdr.DateWritten = Msg.Written;
	jamHdr.DateProcessed = Msg.Arrived;

	JAMset_flags();
	lseek(fdHdr, 0L, SEEK_END);

	jamIdx.UserCRC = 0;
	jamIdx.HdrOffset = tell(fdHdr);
	lseek(fdJdx, 0L, SEEK_END);
	write(fdJdx, &jamIdx, sizeof(JAMIDXREC));

	write(fdHdr, &jamHdr, sizeof(JAMHDR));

	jamHdr.SubfieldLen += AddSubfield(JAMSFLD_SENDERNAME, Msg.From);

	if (Msg.To[0])
		jamHdr.SubfieldLen += AddSubfield(JAMSFLD_RECVRNAME, Msg.To);

	jamHdr.SubfieldLen += AddSubfield(JAMSFLD_SUBJECT, Msg.Subject);

	if (Msg.FromAddress[0] != '\0')
		jamHdr.SubfieldLen += AddSubfield(JAMSFLD_OADDRESS, Msg.FromAddress);

	if (Msg.ToAddress[0] != '\0')
		jamHdr.SubfieldLen += AddSubfield(JAMSFLD_DADDRESS, Msg.ToAddress);

	Msg.Id = jamHdr.MsgNum;
	
	lseek(fdJdt, 0L, SEEK_END);
	jamHdr.TxtOffset = tell(fdJdt);
	jamHdr.TxtLen = 0;

	/*
	 * Read message text from memory, this also contains kludges.
	 * Extract those that are defined by the JAMmb specs, except
	 * the AREA: kludge. This one is only present in bad and dupe
	 * echomail areas and is present for tossbad and tossdupe.
	 */
	if ((pszText = (char *)MsgText_First ()) != NULL)
		do {
			if ((pszText[0] == '\001') || (!strncmp(pszText, "SEEN-BY:", 8))) {
				Oke = FALSE;

				if (!strncmp(pszText, "\001PID: ", 6)) {
					jamHdr.SubfieldLen += AddSubfield(JAMSFLD_PID, pszText + 6);
					Oke = TRUE;
				}

				if (!strncmp(pszText, "\001MSGID: ", 8)) {
					jamHdr.SubfieldLen += AddSubfield(JAMSFLD_MSGID, pszText + 8);
					Oke = TRUE;
				}

				if (!strncmp(pszText, "\001REPLY: ", 8)) {
					jamHdr.SubfieldLen += AddSubfield(JAMSFLD_REPLYID, pszText + 8);
					Oke = TRUE;
				}

				if (!strncmp(pszText, "\001PATH: ", 7)) {
					jamHdr.SubfieldLen += AddSubfield(JAMSFLD_PATH2D, pszText + 7);
					Oke = TRUE;
				}

				if (!strncmp(pszText, "\001Via", 4)) {
					jamHdr.SubfieldLen += AddSubfield(JAMSFLD_TRACE, pszText + 5);
					Oke = TRUE;
				}

				if (!strncmp(pszText, "SEEN-BY: ", 9)) {
					jamHdr.SubfieldLen += AddSubfield(JAMSFLD_SEENBY2D, pszText + 9);
					Oke = TRUE;
				}

				/*
				 * Other non-JAM kludges
				 */
				if ((!Oke) && (pszText[0] == '\001')) {
					jamHdr.SubfieldLen += AddSubfield(JAMSFLD_FTSKLUDGE, pszText + 1);
					Oke = TRUE;
				}

				if (!Oke) {
					for (i = 0; i < strlen(pszText); i++) {
						if (pszText[i] < 32)
							printf("<%x>", pszText[i]);
						else
							printf("%c", pszText[i]);
					}
				}
			} else {
				write(fdJdt, pszText, strlen (pszText));
				jamHdr.TxtLen += strlen (pszText);
				write(fdJdt, "\r", 1);
				jamHdr.TxtLen += 1;
			}
		} while ((pszText = (char *)MsgText_Next ()) != NULL);

	/*
	 * Write final message header
	 */
	lseek(fdHdr, jamIdx.HdrOffset, SEEK_SET);
	write(fdHdr, &jamHdr, sizeof (JAMHDR));


	/*
	 * Update area information
	 */
	lseek(fdHdr, 0L, SEEK_SET);
	read(fdHdr, &jamHdrInfo, sizeof (JAMHDRINFO));
	jamHdrInfo.ActiveMsgs++;
	jamHdrInfo.ModCounter++;
	lseek(fdHdr, 0L, SEEK_SET);
	write(fdHdr, &jamHdrInfo, sizeof (JAMHDRINFO));

	return RetVal;
}



/*
 * Close current message base
 */
void JAM_Close(void)
{
	if (fdJdx != -1)
		close(fdJdx);
	if (fdJdt != -1)
		close(fdJdt);
	if (fdHdr != -1)
		close(fdHdr);
	if (fdJlr != -1)
		close(fdJlr);

	if (pSubfield != NULL)
		free(pSubfield);

	fdHdr = fdJdt = fdJdx = fdJlr = -1;
	pSubfield = NULL;
	Msg.Id = 0L;
}



/*
 * Delete message number
 */
int JAM_Delete(unsigned long ulMsg)
{
	int		RetVal = FALSE;
	JAMIDXREC	jamIdx;

	if (JAM_ReadHeader(ulMsg) == TRUE) {
		jamHdr.Attribute |= MSG_DELETED;

		lseek(fdJdx, tell(fdJdx) - sizeof(jamIdx), SEEK_SET);
		if (read(fdJdx, &jamIdx, sizeof(jamIdx)) == sizeof(jamIdx)) {
			lseek(fdHdr, jamIdx.HdrOffset, SEEK_SET);
			write(fdHdr, &jamHdr, sizeof(JAMHDR));

			lseek(fdHdr, 0L, SEEK_SET);
			read(fdHdr, &jamHdrInfo, sizeof (JAMHDRINFO));
			jamHdrInfo.ActiveMsgs--;
			lseek(fdHdr, 0L, SEEK_SET);
			write(fdHdr, &jamHdrInfo, sizeof (JAMHDRINFO));
			RetVal = TRUE;
		}
	}

	return RetVal;
}



/*
 * Search for requested LastRead record.
 */
int JAM_GetLastRead(lastread *LR)
{
	lastread	lr;

	LastReadRec = 0L;
	lseek(fdJlr, 0, SEEK_SET);

	while (read(fdJlr, &lr, sizeof(lastread)) == sizeof(lastread)) {
		if (lr.UserID == LR->UserID) {
			LR->LastReadMsg = lr.LastReadMsg;
			LR->HighReadMsg = lr.HighReadMsg;
			return TRUE;
		}
		LastReadRec++;
	}

	return FALSE;
}



/*
 * Get highest message number
 */
unsigned long JAM_Highest(void)
{
	unsigned long	RetVal = 0L;
	JAMIDXREC	jamIdx;

	if (jamHdrInfo.ActiveMsgs > 0L) {
		lseek(fdJdx, filelength(fdJdx) - sizeof(jamIdx), SEEK_SET);
		if (read(fdJdx, &jamIdx, sizeof(jamIdx)) == sizeof(jamIdx)) {
			lseek(fdHdr, jamIdx.HdrOffset, SEEK_SET);
			read(fdHdr, &jamHdr, sizeof(JAMHDR));
			RetVal = jamHdr.MsgNum;
		}
	}

	Msg.Id = RetVal;

	return RetVal;
}



int JAM_Lock(unsigned long ulTimeout)
{
	char	File[128];
	int	fd = -1, Tries = 0;

	sprintf(File, "%s%s", BaseName, ".LCK");

	while ((fd = creat(File, 0)) == -1 && errno == EACCES) {
		if (++Tries >= ulTimeout) {
			return FALSE;
		}
		sleep(1);
	}

	if (fd == -1)
		return FALSE;

	close(fd);
	return TRUE;
}



/*
 * Get lowest message number 
 */
unsigned long JAM_Lowest(void)
{
	unsigned long	RetVal = 0L;
	JAMIDXREC	jamIdx;

	if (jamHdrInfo.ActiveMsgs > 0L) {
		lseek(fdJdx, 0L, SEEK_SET);
		if (read(fdJdx, &jamIdx, sizeof(jamIdx)) == sizeof(jamIdx)) {
			lseek(fdHdr, jamIdx.HdrOffset, SEEK_SET);
			read(fdHdr, &jamHdr, sizeof(JAMHDR));
			RetVal = jamHdr.MsgNum;
		}
	}

	Msg.Id = RetVal;

	return RetVal;
}



void JAM_New(void)
{
	memset(&Msg, 0, sizeof(Msg));
	MsgText_Clear();
}



int JAM_NewLastRead(lastread LR)
{
	lseek(fdJlr, 0, SEEK_END);
	return (write(fdJlr, &LR, sizeof(lastread)) == sizeof(lastread));
}



int JAM_Next(unsigned long * ulMsg)
{
	int		RetVal = FALSE, MayBeNext = FALSE;
	JAMIDXREC	jamIdx;
	unsigned long	_Msg;

	_Msg = *ulMsg;

	if (jamHdrInfo.ActiveMsgs > 0L) {
		// --------------------------------------------------------------------
		// The first attempt to retrive the next message number suppose that
		// the file pointers are located after the current message number.
		// Usually this is the 99% of the situations because the messages are
		// often readed sequentially.
		// --------------------------------------------------------------------
		if (tell(fdJdx) >= sizeof (jamIdx))
			lseek(fdJdx, tell(fdJdx) - sizeof(jamIdx), SEEK_SET);
		do {
			if (read(fdJdx, &jamIdx, sizeof(jamIdx)) == sizeof(jamIdx)) {
				lseek(fdHdr, jamIdx.HdrOffset, SEEK_SET);
				read(fdHdr, &jamHdr, sizeof (JAMHDR));
				if (MayBeNext == TRUE) {
					if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum > _Msg) {
						_Msg = jamHdr.MsgNum;
						RetVal = TRUE;
					}
				}
				if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum == _Msg)
					MayBeNext = TRUE;
			}
		} while (RetVal == FALSE && tell(fdJdx) < filelength(fdJdx));

		if (RetVal == FALSE && MayBeNext == FALSE) {
		// --------------------------------------------------------------------
		// It seems that the file pointers are not located where they should be
		// so our next attempt is to scan the database from the beginning to
		// find the next message number.
		// --------------------------------------------------------------------
			lseek(fdJdx, 0L, SEEK_SET);
			do {
				if (read(fdJdx, &jamIdx, sizeof(jamIdx)) == sizeof(jamIdx)) {
					lseek(fdHdr, jamIdx.HdrOffset, SEEK_SET);
					read(fdHdr, &jamHdr, sizeof (JAMHDR));
					if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum > _Msg) {
						_Msg = jamHdr.MsgNum;
						RetVal = TRUE;
					}
				}
			} while (RetVal == FALSE && tell(fdJdx) < filelength(fdJdx));
		}

		Msg.Id = 0L;
		if (RetVal == TRUE)
			Msg.Id = _Msg;
	}

	memcpy(ulMsg, &_Msg, sizeof(unsigned long));
	return RetVal;
}



/*
 * Return number of messages
 */
unsigned long JAM_Number(void)
{
	return jamHdrInfo.ActiveMsgs;
}



/*
 * Open specified JAM message base
 */
int JAM_Open(char *Msgbase)
{
	int	RetVal = FALSE;
	char	File[128];
	char	*Signature = (char *)HEADERSIGNATURE;

	fdJdt = fdJdx = fdJlr = -1;
	pSubfield = NULL;

	sprintf(File, "%s%s", Msgbase, EXT_HDRFILE);
	if ((fdHdr = open(File, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)) != -1) {
		if (read(fdHdr, &jamHdrInfo, sizeof(JAMHDRINFO)) != sizeof(JAMHDRINFO)) {
			memset(&jamHdrInfo, 0, sizeof(JAMHDRINFO));
			jamHdrInfo.Signature[0] = Signature[0];
			jamHdrInfo.Signature[1] = Signature[1];
			jamHdrInfo.Signature[2] = Signature[2];
			jamHdrInfo.Signature[3] = Signature[3];
			jamHdrInfo.DateCreated = time(NULL);
			jamHdrInfo.BaseMsgNum = 1;

			lseek(fdHdr, 0, SEEK_SET);
			write(fdHdr, &jamHdrInfo, sizeof(JAMHDRINFO));
		}

		if (jamHdrInfo.Signature[0] == Signature[0] &&
		    jamHdrInfo.Signature[1] == Signature[1] &&
		    jamHdrInfo.Signature[2] == Signature[2] &&
		    jamHdrInfo.Signature[3] == Signature[3]) {
			sprintf(File, "%s%s", Msgbase, EXT_TXTFILE);
			fdJdt = open(File, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
			sprintf(File, "%s%s", Msgbase, EXT_IDXFILE);
			fdJdx = open(File, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
			sprintf(File, "%s%s", Msgbase, EXT_LRDFILE);
			fdJlr = open(File, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
			RetVal = TRUE;

			strcpy(BaseName, Msgbase);
		} else {
			close(fdHdr);
			fdHdr = -1;
		}
	} else
		memset(&jamHdrInfo, 0, sizeof(JAMHDRINFO));

	Msg.Id = 0L;

	return RetVal;
}



/*
 * Pack deleted messages from the message base. The messages are
 * renumbered on the fly.   LR update
 */
void JAM_Pack(void)
{
	int		fdnHdr, fdnJdx, fdnJdt, fdnJlr;
	int		ToRead, Readed;
	char		File[128], New[128], *Subfield, *Temp;
	JAMIDXREC	jamIdx;
	unsigned long	NewNumber = 0;
	lastread	LR;

	sprintf(File, "%s%s", BaseName, ".$dr");
	fdnHdr = open(File, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
	sprintf(File, "%s%s", BaseName, ".$dt");
	fdnJdt = open(File, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
	sprintf(File, "%s%s", BaseName, ".$dx");
	fdnJdx = open(File, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
	sprintf(File, "%s%s", BaseName, ".$lr");
	fdnJlr = open(File, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);

	if (fdnHdr != -1 && fdnJdt != -1 && fdnJdx != -1 && fdnJlr != -1) {
		lseek(fdHdr, 0L, SEEK_SET);
		if (read(fdHdr, &jamHdrInfo, sizeof(JAMHDRINFO)) == sizeof(JAMHDRINFO)) {
			write(fdnHdr, &jamHdrInfo, sizeof(JAMHDRINFO));
			while (read(fdHdr, &jamHdr, sizeof(JAMHDR)) == sizeof(JAMHDR)) {
				if (jamHdr.Attribute & MSG_DELETED) {
					if (jamHdr.SubfieldLen > 0L)
						lseek (fdHdr, jamHdr.SubfieldLen, SEEK_CUR);
				} else {
					jamIdx.UserCRC = 0;
					jamIdx.HdrOffset = tell(fdnHdr);
					write(fdnJdx, &jamIdx, sizeof(JAMIDXREC));

					lseek(fdJdt, jamHdr.TxtOffset, SEEK_SET);
					jamHdr.TxtOffset = tell(fdnJdt);
					NewNumber++;

					lseek(fdJlr, 0, SEEK_SET);
					while (read(fdJlr, &LR, sizeof(lastread)) == sizeof(lastread)) {
						/*
						 * Test if one of the lastread pointer is the current
						 * old message number.
						 */
						if ((LR.LastReadMsg == jamHdr.MsgNum) || (LR.HighReadMsg == jamHdr.MsgNum)) {
							/*
							 * Adjust the matching numbers
							 */
							if (LR.LastReadMsg == jamHdr.MsgNum)
								LR.LastReadMsg = NewNumber;
							if (LR.HighReadMsg == jamHdr.MsgNum)
								LR.HighReadMsg = NewNumber;
							lseek(fdJlr, - sizeof(lastread), SEEK_CUR);
							write(fdJlr, &LR, sizeof(lastread));
						}
					}

					jamHdr.MsgNum = NewNumber;
					write(fdnHdr, &jamHdr, sizeof(JAMHDR));

					if (jamHdr.SubfieldLen > 0L) {
						if ((Subfield = (char *)malloc ((size_t)(jamHdr.SubfieldLen + 1))) != NULL) {
							read (fdHdr, Subfield, (size_t)jamHdr.SubfieldLen);
							write (fdnHdr, Subfield, (size_t)jamHdr.SubfieldLen);
							free(Subfield);
						}
					}

					if ((Temp = (char *)malloc (MAX_TEXT)) != NULL) {
						do {
							if ((ToRead = MAX_TEXT) > jamHdr.TxtLen)
								ToRead = (int)jamHdr.TxtLen;
								Readed = (int)read (fdJdt, Temp, ToRead);
								write (fdnJdt, Temp, Readed);
								jamHdr.TxtLen -= Readed;
						} while (jamHdr.TxtLen > 0);
						free(Temp);
					}
				}
			}
		}

		/*
		 * Now copy the lastread file
		 */

		lseek(fdJlr, 0, SEEK_SET);
		while (read(fdJlr, &LR, sizeof(lastread)) == sizeof(lastread))
			write(fdnJlr, &LR, sizeof(lastread));

		close(fdnHdr);
		close(fdnJdt);
		close(fdnJdx);
		close(fdnJlr);
		fdnHdr = fdnJdt = fdnJdx = fdnJlr = -1;

		close(fdHdr);
		close(fdJdt);
		close(fdJdx);
		close(fdJlr);
		fdHdr = fdJdt = fdJdx = fdJlr = -1;

		sprintf(File, "%s%s", BaseName, ".$dr");
		sprintf(New, "%s%s", BaseName, EXT_HDRFILE);
		unlink(New);
		rename(File, New);
		sprintf(File, "%s%s", BaseName, ".$dt");
		sprintf(New, "%s%s", BaseName, EXT_TXTFILE);
		unlink(New);
		rename(File, New);
		sprintf(File, "%s%s", BaseName, ".$dx");
		sprintf(New, "%s%s", BaseName, EXT_IDXFILE);
		unlink(New);
		rename(File, New);
		sprintf(File, "%s%s", BaseName, ".$lr");
		sprintf(New, "%s%s", BaseName, EXT_LRDFILE);
		unlink(New);
		rename(File, New);

		JAM_Open(BaseName);
	}

	if (fdnHdr != -1)
		close(fdnHdr);
	sprintf(File, "%s%s", BaseName, ".$dr");
	unlink(File);
	if (fdnJdt != -1)
		close(fdnJdt);
	sprintf(File, "%s%s", BaseName, ".$dt");
	unlink(File);
	if (fdnJdx != -1)
		close(fdnJdx);
	sprintf(File, "%s%s", BaseName, ".$dx");
	unlink(File);
	if (fdnJlr != -1)
		close(fdnJlr);
	sprintf(File, "%s%s", BaseName, ".$lr");
	unlink(File);
}



int JAM_Previous (unsigned long *ulMsg)
{
	int		RetVal = FALSE, MayBeNext = FALSE;
	long		Pos;
	JAMIDXREC	jamIdx;
	unsigned long	_Msg;

	_Msg = *ulMsg;

	if (jamHdrInfo.ActiveMsgs > 0L) {
	// --------------------------------------------------------------------
	// The first attempt to retrive the next message number suppose that
	// the file pointers are located after the current message number.
	// Usually this is the 99% of the situations because the messages are
	// often readed sequentially.
	// --------------------------------------------------------------------
		if (tell (fdJdx) >= sizeof (jamIdx)) {
			Pos = tell (fdJdx) - sizeof (jamIdx);
			do {
				lseek (fdJdx, Pos, SEEK_SET);
				if (read (fdJdx, &jamIdx, sizeof (jamIdx)) == sizeof (jamIdx)) {
					lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
					read (fdHdr, &jamHdr, sizeof (JAMHDR));
					if (MayBeNext == TRUE) {
						if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum < _Msg) {
							_Msg = jamHdr.MsgNum;
							RetVal = TRUE;
						}
					}
					if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum == _Msg)
						MayBeNext = TRUE;
				}
				Pos -= sizeof (jamIdx);
			} while (RetVal == FALSE && Pos >= 0L);
		}

		if (RetVal == FALSE && MayBeNext == FALSE) {
		// --------------------------------------------------------------------
		// It seems that the file pointers are not located where they should be
		// so our next attempt is to scan the database from the end to find
		// the next message number.
		// --------------------------------------------------------------------
			Pos = filelength (fdJdx) - sizeof (jamIdx);
			do {
				lseek (fdJdx, Pos, SEEK_SET);
				if (read (fdJdx, &jamIdx, sizeof (jamIdx)) == sizeof (jamIdx)) {
					lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
					read (fdHdr, &jamHdr, sizeof (JAMHDR));
					if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum < _Msg) {
						_Msg = jamHdr.MsgNum;
						RetVal = TRUE;
					}
				}
				Pos -= sizeof (jamIdx);
			} while (RetVal == FALSE && Pos >= 0L);
		}

		Msg.Id = 0L;
		if (RetVal == TRUE)
			Msg.Id = _Msg;
	}

	memcpy(ulMsg, &_Msg, sizeof(unsigned long));
	return (RetVal);
}



int JAM_ReadHeader (unsigned long ulMsg)
{
	int		i, RetVal = FALSE;
	unsigned char	*pPos;
	unsigned long	ulSubfieldLen, tmp;
	JAMIDXREC	jamIdx;
	JAMBINSUBFIELD	*jamSubField;

	tmp = Msg.Id;
	JAM_New ();
	Msg.Id = tmp;

	if (Msg.Id == ulMsg) {
	// --------------------------------------------------------------------
	// The user is requesting the header of the last message retrived
	// so our first attempt is to read the last index from the file and
	// check if this is the correct one.
	// --------------------------------------------------------------------
		lseek (fdJdx, tell (fdJdx) - sizeof (jamIdx), SEEK_SET);
		if (read (fdJdx, &jamIdx, sizeof (jamIdx)) == sizeof (jamIdx)) {
			lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
			read (fdHdr, &jamHdr, sizeof (JAMHDR));
			if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum == ulMsg)
				RetVal = TRUE;
		}
	}

	if (((Msg.Id + 1) == ulMsg) && (RetVal == FALSE)) {
	//---------------------------------------------------------------------
	// If the user is requesting the header of the next message we attempt
	// to read the next header and check if this is the correct one.
	// This is EXPERIMENTAL
	//---------------------------------------------------------------------
		if (read(fdJdx, &jamIdx, sizeof(jamIdx)) == sizeof(jamIdx)) {
			lseek(fdHdr, jamIdx.HdrOffset, SEEK_SET);
			read(fdHdr, &jamHdr, sizeof(JAMHDR));
			if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum == ulMsg)
				RetVal = TRUE;
		}
	}


	if (RetVal == FALSE) {
	// --------------------------------------------------------------------
	// The message request is not the last retrived or the file pointers
	// are not positioned where they should be, so now we attempt to
	// retrive the message header scanning the database from the beginning.
	// --------------------------------------------------------------------
		Msg.Id = 0L;
		lseek (fdJdx, 0L, SEEK_SET);
		do {
			if (read (fdJdx, &jamIdx, sizeof (jamIdx)) == sizeof (jamIdx)) {
				lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
				read (fdHdr, &jamHdr, sizeof (JAMHDR));
				if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum == ulMsg)
					RetVal = TRUE;
			}
		} while (RetVal == FALSE && tell (fdJdx) < filelength (fdJdx));
	}

	if (RetVal == TRUE) {
		Msg.Current = Msg.Id = ulMsg;

		Msg.Local          = (unsigned char)((jamHdr.Attribute & MSG_LOCAL)       ? TRUE : FALSE);
		Msg.Intransit      = (unsigned char)((jamHdr.Attribute & MSG_INTRANSIT)   ? TRUE : FALSE);
		Msg.Private        = (unsigned char)((jamHdr.Attribute & MSG_PRIVATE)     ? TRUE : FALSE);
		Msg.Received       = (unsigned char)((jamHdr.Attribute & MSG_READ)        ? TRUE : FALSE);
		Msg.Sent           = (unsigned char)((jamHdr.Attribute & MSG_SENT)        ? TRUE : FALSE);
		Msg.KillSent       = (unsigned char)((jamHdr.Attribute & MSG_KILLSENT)    ? TRUE : FALSE);
		Msg.ArchiveSent    = (unsigned char)((jamHdr.Attribute & MSG_ARCHIVESENT) ? TRUE : FALSE);
		Msg.Hold           = (unsigned char)((jamHdr.Attribute & MSG_HOLD)        ? TRUE : FALSE);
		Msg.Crash          = (unsigned char)((jamHdr.Attribute & MSG_CRASH)       ? TRUE : FALSE);
		Msg.Immediate      = (unsigned char)((jamHdr.Attribute & MSG_IMMEDIATE)   ? TRUE : FALSE);
		Msg.Direct         = (unsigned char)((jamHdr.Attribute & MSG_DIRECT)      ? TRUE : FALSE);
		Msg.Gate	   = (unsigned char)((jamHdr.Attribute & MSG_GATE)        ? TRUE : FALSE);
		Msg.FileRequest    = (unsigned char)((jamHdr.Attribute & MSG_FILEREQUEST) ? TRUE : FALSE);
		Msg.FileAttach     = (unsigned char)((jamHdr.Attribute & MSG_FILEATTACH)  ? TRUE : FALSE);
		Msg.TruncFile      = (unsigned char)((jamHdr.Attribute & MSG_TRUNCFILE)   ? TRUE : FALSE);
		Msg.KillFile       = (unsigned char)((jamHdr.Attribute & MSG_KILLFILE)    ? TRUE : FALSE);
		Msg.ReceiptRequest = (unsigned char)((jamHdr.Attribute & MSG_RECEIPTREQ)  ? TRUE : FALSE);
		Msg.ConfirmRequest = (unsigned char)((jamHdr.Attribute & MSG_CONFIRMREQ)  ? TRUE : FALSE);
		Msg.Orphan         = (unsigned char)((jamHdr.Attribute & MSG_ORPHAN)      ? TRUE : FALSE);
		Msg.Encrypt        = (unsigned char)((jamHdr.Attribute & MSG_ENCRYPT)     ? TRUE : FALSE);
		Msg.Compressed     = (unsigned char)((jamHdr.Attribute & MSG_COMPRESS)    ? TRUE : FALSE);
		Msg.Escaped        = (unsigned char)((jamHdr.Attribute & MSG_ESCAPED)     ? TRUE : FALSE);
		Msg.ForcePU        = (unsigned char)((jamHdr.Attribute & MSG_FPU)         ? TRUE : FALSE);
		Msg.Localmail	   = (unsigned char)((jamHdr.Attribute & MSG_TYPELOCAL)   ? TRUE : FALSE);
		Msg.Echomail       = (unsigned char)((jamHdr.Attribute & MSG_TYPEECHO)    ? TRUE : FALSE);
		Msg.Netmail        = (unsigned char)((jamHdr.Attribute & MSG_TYPENET)     ? TRUE : FALSE);
		Msg.News           = (unsigned char)((jamHdr.Attribute & MSG_TYPENEWS)    ? TRUE : FALSE);
		Msg.Email          = (unsigned char)((jamHdr.Attribute & MSG_TYPEEMAIL)   ? TRUE : FALSE);
		Msg.Nodisplay      = (unsigned char)((jamHdr.Attribute & MSG_NODISP)      ? TRUE : FALSE);
		Msg.Locked         = (unsigned char)((jamHdr.Attribute & MSG_LOCKED)      ? TRUE : FALSE);
		Msg.Deleted        = (unsigned char)((jamHdr.Attribute & MSG_DELETED)     ? TRUE : FALSE);

		Msg.Written = jamHdr.DateWritten;
		Msg.Arrived = jamHdr.DateProcessed;
		Msg.Read    = jamHdr.DateReceived;

		Msg.Original = jamHdr.ReplyTo;
		Msg.Reply = jamHdr.ReplyNext;

		if (pSubfield != NULL)
			free (pSubfield);
		pSubfield = NULL;

		if (jamHdr.SubfieldLen > 0L) {
			ulSubfieldLen = jamHdr.SubfieldLen;
			pPos = pSubfield = (unsigned char *)malloc ((size_t)(ulSubfieldLen + 1));
			if (pSubfield == NULL)
				return (FALSE);

			read (fdHdr, pSubfield, (size_t)jamHdr.SubfieldLen);

			while (ulSubfieldLen > 0L) {
				jamSubField = (JAMBINSUBFIELD *)pPos;
				pPos += sizeof (JAMBINSUBFIELD);
				/*
				 * The next check is to prevent a segmentation
				 * fault by corrupted subfields.
				 */
				if ((jamSubField->DatLen < 0) || (jamSubField->DatLen > jamHdr.SubfieldLen))
					return FALSE;

				switch (jamSubField->LoID) {
				case JAMSFLD_SENDERNAME:
					memcpy (Msg.From, pPos, (int)jamSubField->DatLen);
					Msg.From[(int)jamSubField->DatLen] = '\0';
					break;

				case JAMSFLD_RECVRNAME:
					memcpy (Msg.To, pPos, (int)jamSubField->DatLen);
					Msg.To[(int)jamSubField->DatLen] = '\0';
					break;

				case JAMSFLD_SUBJECT:
					memcpy (Msg.Subject, pPos, (int)jamSubField->DatLen);
					Msg.Subject[(int)jamSubField->DatLen] = '\0';
					break;

				case JAMSFLD_OADDRESS:
					memcpy (Msg.FromAddress, pPos, (int)jamSubField->DatLen);
					Msg.FromAddress[(int)jamSubField->DatLen] = '\0';
					break;

				case JAMSFLD_DADDRESS:
					memcpy (Msg.ToAddress, pPos, (int)jamSubField->DatLen);
					Msg.ToAddress[(int)jamSubField->DatLen] = '\0';
					break;

				case JAMSFLD_MSGID:
					memcpy (Msg.Msgid, pPos, (int)jamSubField->DatLen);
					Msg.Msgid[(int)jamSubField->DatLen] = '\0';
					break;

				default:
					break;
				}
				ulSubfieldLen -= sizeof (JAMBINSUBFIELD) + jamSubField->DatLen;
				if (ulSubfieldLen > 0)
					pPos += (int)jamSubField->DatLen;
			}
		}
		/*
		 *  In the original BBS we found that GEcho was not
		 *  setting the FromAddress. We take it from the MSGID
		 *  if there is one.
		 */
		if ((!strlen(Msg.FromAddress)) && (strlen(Msg.Msgid))) {
			for (i = 0; i < strlen(Msg.Msgid); i++) {
				if ((Msg.Msgid[i] == '@') || (Msg.Msgid[i] == ' '))
					break;
				Msg.FromAddress[i] = Msg.Msgid[i];
			}
		}
	}

	return (RetVal);
}



/*
 * Read message
 */
int JAM_Read(unsigned long ulMsg, int nWidth)
{
	int		RetVal = FALSE, SkipNext;
	int		i, nReaded, nCol, nRead;
	unsigned char	*pPos;
	unsigned long	ulTxtLen, ulSubfieldLen;
	JAMIDXREC	jamIdx;
	JAMBINSUBFIELD	*jamSubField;
	LDATA		*Bottom = NULL, *New;

	MsgText_Clear();

	if ((RetVal = JAM_ReadHeader(ulMsg)) == TRUE) {
		lseek (fdJdx, tell (fdJdx) - sizeof (jamIdx), SEEK_SET);
		read (fdJdx, &jamIdx, sizeof (jamIdx));
		lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
		read (fdHdr, &jamHdr, sizeof (JAMHDR));

		if (pSubfield != NULL)
			free (pSubfield);
		pSubfield = NULL;

		if (jamHdr.SubfieldLen > 0L) {
			ulSubfieldLen = jamHdr.SubfieldLen;
			pPos = pSubfield = (unsigned char *)malloc ((size_t)(ulSubfieldLen + 1));
			if (pSubfield == NULL)
				return (FALSE);

			read (fdHdr, pSubfield, (size_t)jamHdr.SubfieldLen);

			while (ulSubfieldLen > 0L) {
				jamSubField = (JAMBINSUBFIELD *)pPos;
				pPos += sizeof (JAMBINSUBFIELD);

				/*
				 * Check for corrupted subfields
				 */
				if ((jamSubField->DatLen < 0) || (jamSubField->DatLen > jamHdr.SubfieldLen))
					return FALSE;

				switch (jamSubField->LoID) {
				case JAMSFLD_MSGID:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					memset(&Msg.Msgid, 0, sizeof(Msg.Msgid));
					sprintf(Msg.Msgid, "%s", szBuff);
					sprintf (szLine, "\001MSGID: %s", szBuff);
					MsgText_Add2(szLine);
					break;

				case JAMSFLD_REPLYID:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					sprintf (szLine, "\001REPLY: %s", szBuff);
					MsgText_Add2(szLine);
					break;

				case JAMSFLD_PID:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					sprintf (szLine, "\001PID: %s", szBuff);
					MsgText_Add2(szLine);
					break;

				case JAMSFLD_TRACE:
					memcpy(szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					sprintf (szLine, "\001Via %s", szBuff);
					MsgText_Add2(szLine);
					break;

				case JAMSFLD_FTSKLUDGE:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					if (!strncmp(szBuff, "AREA:", 5))
						sprintf(szLine, "%s", szBuff);
					else {
						sprintf (szLine, "\001%s", szBuff);
						if (strncmp(szLine, "\001REPLYADDR:", 11) == 0) {
							sprintf(Msg.ReplyAddr, "%s", szLine+12);
						}
						if (strncmp(szLine, "\001REPLYTO:", 9) == 0) {
							sprintf(Msg.ReplyTo, "%s", szLine+10);
						}
						if (strncmp(szLine, "\001REPLYADDR", 10) == 0) {
							sprintf(Msg.ReplyAddr, "%s", szLine+11);
						}
						if (strncmp(szLine, "\001REPLYTO", 8) == 0) {
							sprintf(Msg.ReplyTo, "%s", szLine+9);
						}
					}
					MsgText_Add2(szLine);
					break;

				case JAMSFLD_SEENBY2D:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					sprintf (szLine, "SEEN-BY: %s", szBuff);
					if ((New = (LDATA *)malloc(sizeof(LDATA))) != NULL) {
						memset(New, 0, sizeof(LDATA));
						New->Value = strdup(szLine);
						if (Bottom != NULL) {
							while (Bottom->Next != NULL)
								Bottom = Bottom->Next;
							New->Previous = Bottom;
							New->Next = Bottom->Next;
							if (New->Next != NULL)
								New->Next->Previous = New;
							Bottom->Next = New;
						}
						Bottom = New;
					}
					break;

				case JAMSFLD_PATH2D:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					sprintf (szLine, "\001PATH: %s", szBuff);
					if ((New = (LDATA *)malloc(sizeof(LDATA))) != NULL) {
						memset(New, 0, sizeof(LDATA));
						New->Value = strdup(szLine);
						if (Bottom != NULL) {
							while (Bottom->Next != NULL)
								Bottom = Bottom->Next;
							New->Previous = Bottom;
							New->Next = Bottom->Next;
							if (New->Next != NULL)
								New->Next->Previous = New;
							Bottom->Next = New;
						}
						Bottom = New;
					}
					break;

				case JAMSFLD_FLAGS:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					sprintf (szLine, "\001FLAGS %s", szLine);
					MsgText_Add2(szLine);
					break;

				case JAMSFLD_TZUTCINFO:
					memcpy (szBuff, pPos, (int)jamSubField->DatLen);
					szBuff[(int)jamSubField->DatLen] = '\0';
					sprintf (szBuff, "\001TZUTC %s", szLine);
					MsgText_Add2(szLine);
					break;

				default:
					break;
				}

				ulSubfieldLen -= sizeof (JAMBINSUBFIELD) + jamSubField->DatLen;
				if (ulSubfieldLen > 0)
					pPos += (int)jamSubField->DatLen;
			}
		}

		lseek (fdJdt, jamHdr.TxtOffset, SEEK_SET);
		ulTxtLen = jamHdr.TxtLen;
		pLine = szLine;
		nCol = 0;
		SkipNext = FALSE;

		do {
			if ((unsigned long)(nRead = sizeof (szBuff)) > ulTxtLen)
				nRead = (int)ulTxtLen;

			nReaded = (int)read (fdJdt, szBuff, nRead);

			for (i = 0, pBuff = szBuff; i < nReaded; i++, pBuff++) {
				if (*pBuff == '\r') {
					*pLine = '\0';
					if (pLine > szLine && SkipNext == TRUE) {
						pLine--;
						while (pLine > szLine && *pLine == ' ')
							*pLine-- = '\0';
						if (pLine > szLine)
							MsgText_Add3(szLine, (int)(strlen (szLine) + 1));
					} else 
						if (SkipNext == FALSE)
							MsgText_Add2(szLine);
					SkipNext = FALSE;
					pLine = szLine;
					nCol = 0;
				} else
					if (*pBuff != '\n') {
						*pLine++ = *pBuff;
						nCol++;
						if (nCol >= nWidth) {
							*pLine = '\0';
							if (strchr (szLine, ' ') != NULL) {
								while (nCol > 1 && *pLine != ' ') {
									nCol--;
									pLine--;
								}
								if (nCol > 0) {
									while (*pLine == ' ')
										pLine++;
									strcpy (szWrp, pLine);
								}
								*pLine = '\0';
							} else
								szWrp[0] = '\0';
							MsgText_Add2(szLine);
							strcpy (szLine, szWrp);
							pLine = strchr (szLine, '\0');
							nCol = (int)strlen (szLine);
							SkipNext = TRUE;
						}
					}
			}

			ulTxtLen -= nRead;
		} while (ulTxtLen > 0);

		if (Bottom != NULL) {
			while (Bottom->Previous != NULL)
				Bottom = Bottom->Previous;
			MsgText_Add2(Bottom->Value);

			while (Bottom->Next != NULL) {
				Bottom = Bottom->Next;
				MsgText_Add2(Bottom->Value);
			}
			while (Bottom != NULL) {
				if (Bottom->Previous != NULL)
					Bottom->Previous->Next = Bottom->Next;
				if (Bottom->Next != NULL)
					Bottom->Next->Previous = Bottom->Previous;
				New = Bottom;
				if (Bottom->Next != NULL)
					Bottom = Bottom->Next;
				else if (Bottom->Previous != NULL)
					Bottom = Bottom->Previous;
				else
					Bottom = NULL;
				free(New);
			}
		}
	}

	return (RetVal);
}



int JAM_SetLastRead(lastread LR)
{
	if (lseek(fdJlr, LastReadRec * sizeof(lastread), SEEK_SET) != -1)
		if (write(fdJlr, &LR, sizeof(lastread)) == sizeof(lastread))
			return TRUE;
	return FALSE;
}



/*
 * Unlock the message base
 */
void JAM_UnLock(void)
{
	char	File[128];

	sprintf(File, "%s%s", BaseName, ".LCK");

	if (unlink(File) == -1)
		printf("Unlock error\n");
}



/*
 * Write message header
 */
int JAM_WriteHeader (unsigned long ulMsg)
{
	int		RetVal = FALSE;
	JAMIDXREC	jamIdx;

	if (Msg.Id == ulMsg) {
	// --------------------------------------------------------------------
	// The user is requesting to write the header of the last message
	// retrived so our first attempt is to read the last index from the
	// file and check if this is the correct one.
	// --------------------------------------------------------------------
		lseek (fdJdx, tell (fdJdx) - sizeof (jamIdx), SEEK_SET);
		if (read (fdJdx, &jamIdx, sizeof (jamIdx)) == sizeof (jamIdx)) {
			lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
			read (fdHdr, &jamHdr, sizeof (JAMHDR));
			if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum == ulMsg)
				RetVal = TRUE;
		}
	}

	if (RetVal == FALSE) {
	// --------------------------------------------------------------------
	// The message requested is not the last retrived or the file pointers
	// are not positioned where they should be, so now we attempt to
	// retrive the message header scanning the database from the beginning.
	// --------------------------------------------------------------------
		Msg.Id = 0L;
		lseek (fdJdx, 0L, SEEK_SET);
		do {
			if (read (fdJdx, &jamIdx, sizeof (jamIdx)) == sizeof (jamIdx)) {
				lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
				read (fdHdr, &jamHdr, sizeof (JAMHDR));
				if (!(jamHdr.Attribute & MSG_DELETED) && jamHdr.MsgNum == ulMsg)
					RetVal = TRUE;
			}
		} while (RetVal == FALSE && tell (fdJdx) < filelength (fdJdx));
	}

	if (RetVal == TRUE) {
		Msg.Id = jamHdr.MsgNum;
		jamHdr.Attribute &= MSG_DELETED;
		JAMset_flags();
		
		lseek (fdHdr, jamIdx.HdrOffset, SEEK_SET);
		write (fdHdr, &jamHdr, sizeof (JAMHDR));
	}

	return RetVal;
}


