smsprotocols/smsstack/smsprot/Src/smspclass0stor.cpp
author Pat Downey <patd@symbian.org>
Tue, 13 Jul 2010 14:53:59 +0100
branchRCL_3
changeset 50 2ac16fe2d995
parent 0 3553901f7fa8
child 24 6638e7f4bd8f
child 42 3adadc800673
permissions -rw-r--r--
Re-merge addition of wapstack to fid bug 1398.

// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Implements CPreallocatedFile.
// 
//

/**
 @file
*/
#include "smspclass0stor.h"
#include "gsmubuf.h"
#include "gsmunonieoperations.h"

const TInt KSizeOfPreallocatedFileVersion = sizeof(CPreallocatedFile::TPreAllocatedFileVersion);
const TInt KSizeOfNumberOfEntrySection = sizeof(TInt);
//size of 32-bit checksum
const TInt KSizeOfChecksum = sizeof(TUint32);
const TInt KSizeOfPreAllocatedStoreEntry = sizeof(TSmsPreAllocatedFileStoreReassemblyEntry);
const TInt KSizeOfGsmSmsSlotEntry = sizeof(TGsmSmsSlotEntry);
const TInt KSizeOfIndexEntry = sizeof(TInt);
const TInt KSizeOfSmsGsmPDU = sizeof(RMobileSmsMessaging::TMobileSmsGsmTpdu);
const TInt KSizeOfAContainer = KSizeOfGsmSmsSlotEntry + KSizeOfIndexEntry + KSizeOfSmsGsmPDU;
const TInt KBeginOfMasterHeaderSection = KSizeOfPreallocatedFileVersion;

/**
Static factory constructor. Uses two phase 
construction and leaves nothing on the CleanupStack.

@param aFs  File Server handle.
@param aFileName  Permanent file store name.
@param aThirdUid  Uid of file.
@leave KErrNoMemory

@return A pointer to the newly created CSmsPermanentFileStore object.

@pre A connected file server session must be passed as parameter.
@post CSmsPermanentFileStore object is now fully initialised

@internalComponent
*/
CSmsPermanentFileStore* CSmsPermanentFileStore::NewL(RFs& aFs, const TDesC& aFileName, const TUid& aThirdUid)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::NewL()");
	CSmsPermanentFileStore*  self = new (ELeave) CSmsPermanentFileStore(aFs, aThirdUid);
	CleanupStack::PushL(self);
	self->ConstructL(aFileName);
	CleanupStack::Pop(self);

	return self;
	}

void CSmsPermanentFileStore::ConstructL(const TDesC& aFileName)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::ConstructL()");
	iFileName = aFileName.AllocL();
	}

/**
 *  Constructor.
*/
CSmsPermanentFileStore::CSmsPermanentFileStore(RFs& aFs, const TUid& aThirdUid)
							: iFs(aFs), iFileName(NULL), iThirdUid(aThirdUid), iFileStore(NULL), iEntryArray(KFlatArrayGranularity)
	{
	}

/**
 *  Destructor. It destroys all the member variables.
*/
CSmsPermanentFileStore::~CSmsPermanentFileStore()
	{
	delete iFileName;
	}

/**
It checks & returns whether the file exist or not. If file is there whether
it is corrupted or not.

@internalComponent
*/
TBool CSmsPermanentFileStore::IsFileOK()
	{
	TBool retBool(EFalse);
	TUidType uidtype(KPermanentFileStoreLayoutUid,KSARStoreUid,iThirdUid);
	TEntry entry;
	TInt ret=iFs.Entry(iFileName->Des(), entry);
	//  Check file existence & corruption
	if ((ret == KErrNone) && (entry.iType == uidtype))
		{
		retBool = ETrue;
		}
	return retBool;
	}

/**
It creates a permanent store file.

@internalComponent
*/
void CSmsPermanentFileStore::CreateL()
	{
	LOGSMSPROT1("CSmsPermanentFileStore::CreateL()");
	TUidType uidtype(KPermanentFileStoreLayoutUid,KSARStoreUid,iThirdUid);
	iFileStore=CPermanentFileStore::ReplaceL(iFs, iFileName->Des(), EFileShareExclusive|EFileStream|EFileRead|EFileWrite);
	iFileStore->SetTypeL(uidtype);
	iEntryArray.Reset();
	ExternalizeEntryArrayL();
	iFileStore->CommitL();
	// Close it to make sure that file is created correctly (defensive approach).
	Close();
	//Again Open the file
	OpenL();
	}

/**
It opens the permanent store file and internalizes the entries.

@internalComponent
*/
void CSmsPermanentFileStore::OpenL()
	{
	LOGSMSPROT1("CSmsPermanentFileStore::OpenL()");
	iFileStore=CPermanentFileStore::OpenL(iFs,iFileName->Des(),EFileShareExclusive|EFileStream|EFileRead|EFileWrite);
	InternalizeEntryArrayL();
	}

/**
It closes the permanent store file.

@internalComponent
*/
void CSmsPermanentFileStore::Close()
	{
	LOGSMSPROT1("CSmsPermanentFileStore::Close()");
	delete iFileStore;
	iFileStore = NULL;
	iEntryArray.Reset();
	}

/*
This function cleans its entries against the passed entries.
This is needed because there might be a scenario where user has deleted a message
but the corresponding message is not deleted from permanent store file
due to out-of-disk condition. But the entry is invalid because
it is no more in the pre-allocated file which contains master header info.
And also at the time of forwarding an incomplete message a forwarded 
message has not been deleted due to above reason.
This function also compacts the store after deletion.

@param aEntryArray entray array against whose clean up is carried out.

@internalComponent
*/
void CSmsPermanentFileStore::CleanupEntriesWithCompactL(const CArrayFix<TSmsPreAllocatedFileStoreReassemblyEntry>& aEntryArray)
	{
    // Ignore in code coverage - a previous CleanupEntries would need to have failed with KErrDiskFull
	BULLSEYE_OFF
	LOGSMSPROT1("CSmsPermanentFileStore::CleanupEntriesWithCompactL()");

	iCompact = ETrue;
	CleanupEntriesL(aEntryArray);
	BULLSEYE_RESTORE	
	}

/*
This function cleans its entries against the passed entries.
This is needed because there migth be a scenario where user has deleted a message
but the corresponding message is not deleted from permanent store file
due to out-of-disk condition. But the entry is invalid because
it is no more in the pre-allocated file which contains master header info.
And also at the time of forwarding an incomplete message a forwarded 
message has not been deleted due to above reason.

@param aEntryArray entray array against whose clean up is carried out.

@internalComponent
*/
void CSmsPermanentFileStore::CleanupEntriesL(const CArrayFix<TSmsPreAllocatedFileStoreReassemblyEntry>& aEntryArray)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::CleanupEntriesL()");

	TInt reassemblyCount = iEntryArray.Count();
	TInt index, index2;

	for (index = 0;  index < reassemblyCount;  index++)
		{
		for (index2 = 0;  index2 < aEntryArray.Count();  index2++)
			{
			if (iEntryArray[index].Reference() == aEntryArray[index2].Reference()  &&
			    iEntryArray[index].Total() == aEntryArray[index2].Total()  &&
			    iEntryArray[index].PduType() == aEntryArray[index2].PduType()  &&
				iEntryArray[index].Storage() == aEntryArray[index2].Storage()  &&
				iEntryArray[index].Description2() == aEntryArray[index2].Description2())
				{
				if (aEntryArray[index2].NumberOfPDUsForwardToClient() > 0)
					{
					//Internalize the entries.
					CSmsBuffer*  buffer = CSmsBuffer::NewL();
					CSmsMessage*  smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer);
					CleanupStack::PushL(smsMessage);

					CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
					CleanupStack::PushL(indexArray);
					CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
					CleanupStack::PushL(smsArray);

					InternalizeEntryL(index, *smsMessage, *indexArray, *smsArray);
					TInt noOfForwardedEntries = 0;
					for (TInt j=indexArray->Count(); j>0 ;j--)
						{
						TUint8 bitMapIndex = (indexArray->At(j-1)-1)/8;
						TUint8 bitPos = (indexArray->At(j-1)-1)%8;
						TUint8 bitMap;
						TSmsPreAllocatedFileStoreReassemblyEntry entry;
						entry = aEntryArray[index2];
						entry.GetBitMap(bitMapIndex, bitMap);
						TUint8 tmpBitMap = 1;
						tmpBitMap <<= bitPos;
						if (tmpBitMap == (bitMap & tmpBitMap))
							{
							noOfForwardedEntries++;
							indexArray->Delete(j-1);
							smsArray->Delete(j-1);
							}
						}
					if (noOfForwardedEntries > 0)
						{
						TStreamId streamId = iEntryArray[index].DataStreamId();
						ExternalizeEntryL(streamId, *smsMessage, *indexArray, *smsArray);
						}
					CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray
					}
				break;
				}
			}
		if (index2 == aEntryArray.Count())
			{
			DeleteEntryL(index);
			}
		}
	}

/**
It internalizes its entry array.

@internalComponent
*/
void CSmsPermanentFileStore::InternalizeEntryArrayL()
	{
	LOGSMSPROT1("CSmsPermanentFileStore::InternalizeEntryArrayL()");

	iEntryArray.Reset();
	TStreamId headerid=iFileStore->Root();
	RStoreReadStream stream;
	stream.OpenLC(*iFileStore,headerid);
	TInt count=stream.ReadInt32L();
	for (TInt i=0; i<count; i++)
		{
		TSmsReassemblyEntry sarentry;
		stream >> sarentry;
		iEntryArray.AppendL(sarentry);
		}
	CleanupStack::PopAndDestroy();  //  stream
	}

/**
It externalizes its entry array.

@internalComponent
*/
void CSmsPermanentFileStore::ExternalizeEntryArrayL()
	{
	LOGSMSPROT4("CSmsPermanentFileStore::ExternalizeEntryArrayL(): this=0x%08X count=%d headerid=%d]",
			 this, iEntryArray.Count(), iFileStore->Root().Value());

	TStreamId headerid=iFileStore->Root();
	RStoreWriteStream stream;
	if (headerid==KNullStreamId)
		{
		headerid=stream.CreateLC(*iFileStore);
		iFileStore->SetRootL(headerid);
		}
	else
		{
		stream.ReplaceLC(*iFileStore,headerid);
		}

	TInt count1=iEntryArray.Count();
	TInt count2=0;
	TInt i=0;

	for (; i<count1; i++)
		{
		if (!iEntryArray[i].IsDeleted())
			{
			count2++;
			}
		}
	stream.WriteInt32L(count2);
	for (i=0; i<count1; i++)
		{
		if (!iEntryArray[i].IsDeleted())
			{
			stream << iEntryArray[i];
			}
		}

	stream.CommitL();
	CleanupStack::PopAndDestroy(&stream);
	}

/*
It adds the new message in permanent store file.

@param aIndex (output) index number on which message is added.
@param aSmsMessage reference to sms message to be added.
@param aGsmSms reference to GsmSms object to be added.

@internalComponent
*/
void CSmsPermanentFileStore::AddNewMessageL(TInt& aIndex, CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::AddNewMessageL");

	CArrayFix<TInt>* indexArray=new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
	CleanupStack::PushL(indexArray);
	CArrayFixFlat<TGsmSms>* smsArray=new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
	CleanupStack::PushL(smsArray);
	//Index must initialize to 0.
	TInt index = 0;
	if (!aSmsMessage.IsDecoded())
		{
		index = aSmsMessage.SmsPDU().ConcatenatedMessagePDUIndex();
		}
	indexArray->AppendL(index);
	smsArray->AppendL(aGsmSms);

	TStreamId streamid = KNullStreamId;
	ExternalizeEntryL(streamid, aSmsMessage, *indexArray, *smsArray);

	TSmsReassemblyEntry tmpEntry;
	//Fill-up entry information...
	CReassemblyStoreUtility::PopulateEntry(tmpEntry, aSmsMessage, 1);
	// Update Data stream id.
	tmpEntry.SetDataStreamId(streamid);

	aIndex = iEntryArray.Count();
	AddEntryL(tmpEntry);
	CleanupStack::PopAndDestroy(2);	//indexArray, smsArray
	}

/*
It updates the existing message in permanent store file.

@param aIndex index number on which message is to be updated.
@param aSmsMessage reference to sms message to be updated.
@param aIndexArray array of index of all the PDUs.
@param aSmsArray array of sms of all the PDUs.

@internalComponent
*/
void CSmsPermanentFileStore::UpdateExistingMessageL(TInt aIndex, const CSmsMessage& aSmsMessage,const CArrayFix<TInt>& aIndexArray,const CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::UpdateExistingMessageL()");
	TStreamId  streamid = iEntryArray[aIndex].DataStreamId();
	ExternalizeEntryL(streamid, aSmsMessage, aIndexArray, aSmsArray);
	TSmsReassemblyEntry entry;
	CReassemblyStoreUtility::PopulateEntry(entry, aSmsMessage, aSmsArray.Count());
	entry.SetDataStreamId(streamid);
	ChangeEntryL(aIndex, entry);
	}

/*
It matches the entry with entry in permanent store file & returns
the index. If not found it returns KErrNotFound.

@param aEntry reference to entry information.
@param aIndex (output) returns the index number.

@internalComponent
*/
void CSmsPermanentFileStore::MatchEntryToExistingMessage(const TReassemblyEntry& aEntry,
													TInt& aIndex)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::MatchEntryToExistingMessage()");

	aIndex = KErrNotFound;

	//
	// Search the reassembly store for a matching entry...
	//
	TInt reassemblyCount = iEntryArray.Count();
	for (TInt  index = 0;  index < reassemblyCount;  index++)
		{
		TSmsReassemblyEntry&  entry = iEntryArray[index];

		if (entry.Reference() == aEntry.Reference()  &&
			entry.Total() == aEntry.Total()  &&
			entry.PduType() == aEntry.PduType()  &&
			entry.Storage() == aEntry.Storage()  &&
			entry.Description2() == aEntry.Description2())
			{
			//
			// Found it!
			//
			aIndex = index;
			break;
			}
		}

	LOGSMSPROT2("CSmsPermanentFileStore::MatchEntryToExistingMessage(): aIndex=%d", aIndex);
	}

/*
It updates the log server id of the message.

@param aIndex index number of the message to be updated.
@param aLogServerId log server id.

@internalComponent
*/
void CSmsPermanentFileStore::UpdateLogServerIdL(TInt& aIndex, TLogId aLogServerId)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::UpdateLogServerIdL");

	TSmsReassemblyEntry entry;
	entry = iEntryArray[aIndex];

	if (entry.LogServerId() != aLogServerId)
		{
		entry.SetLogServerId(aLogServerId);
		ChangeEntryL(aIndex, entry);
		}
	}

/*
It sets the message passed to client or not.

@param aIndex index number of the message to be updated.
@param aBool boolean value indicating whether message is passes or not.

@internalComponent
*/
void CSmsPermanentFileStore::SetPassedToClientL(TInt aIndex, TBool aBool)
	{
	LOGSMSPROT2("CSmsPermanentFileStore::SetPassedToClientL(): aIndex=%d", aIndex);

	TSmsReassemblyEntry entry;
	entry = iEntryArray[aIndex];

	if (entry.PassedToClient() != aBool)
		{
		entry.SetPassedToClient(aBool);
		ChangeEntryL(aIndex, entry);
		}
	}

/*
It adds the new entry in the existing entry array.

@param aEntry entry to be added.

@internalComponent
*/
void CSmsPermanentFileStore::AddEntryL(TSmsReassemblyEntry& aEntry)
	{
	iEntryArray.AppendL(aEntry);
	iEntryArray[iEntryArray.Count()-1].SetIsAdded(ETrue);
	}

/*
It changes the existing entry with new entry.

@param aIndex index number of the entry which will be changed.
@param aNewEntry entry to be updated.

@internalComponent
*/
void CSmsPermanentFileStore::ChangeEntryL(TInt aIndex,const TSmsReassemblyEntry& aNewEntry)
	{
	LOGSMSPROT2("CSmsPermanentFileStore::ChangeEntryL(): aIndex=%d", aIndex);

	iEntryArray[aIndex].SetIsDeleted(ETrue);
	iEntryArray.InsertL(aIndex,aNewEntry);
	iEntryArray[aIndex].SetIsAdded(ETrue);
	}

/*
It deletes the entry.

@param aIndex index number of the entry which will be deleted.

@internalComponent
*/
void CSmsPermanentFileStore::DeleteEntryL(TInt aIndex)
	{
	iFileStore->DeleteL(iEntryArray[aIndex].DataStreamId());
	iEntryArray[aIndex].SetIsDeleted(ETrue);
	}

/*
It externalizes(writes) the entry in permanent store file.

@param aStreamId stream id which needs to externalized.
@param aSmsMessage reference to sms message which needs to be externalized.
@param aIndexArray refence to array of index which needs to be externalized.
@param aSmsArray refence to array of sms which needs to be externalized.

@internalComponent
*/
void CSmsPermanentFileStore::ExternalizeEntryL(TStreamId& aStreamId,const CSmsMessage& aSmsMessage,const CArrayFix<TInt>& aIndexArray,const CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGSMSPROT2("CSmsPermanentFileStore::ExternalizeEntryL Start [sid=%d]", aStreamId.Value());

	RStoreWriteStream writestream;
	if (aStreamId==KNullStreamId)
		aStreamId=writestream.CreateLC(*iFileStore);
	else
		writestream.ReplaceLC(*iFileStore,aStreamId);
	writestream << aSmsMessage;
	TInt count=aIndexArray.Count();
	writestream.WriteInt32L(count);
	TInt i=0;
	for (; i<count; i++)
		writestream.WriteInt32L(aIndexArray[i]);
	count=aSmsArray.Count();
	writestream.WriteInt32L(count);
	for (i=0; i<count; i++)
		{
		RMobileSmsMessaging::TMobileSmsGsmTpdu pdu;
		pdu=aSmsArray[i].Pdu();
		writestream << pdu;
		}
	writestream.CommitL();
	CleanupStack::PopAndDestroy();

	LOGSMSPROT2("CClass0PermanentFileStore::ExternalizeEntryL End [count=%d]", count);
	}

/*
It internalizes(reads) the entry from permanent store file.

@param aIndex index number of the message to be internalized.
@param aSmsMessage (output) reference to sms message.
@param aIndexArray (output) refence to array of index.
@param aSmsArray (output) refence to array of sms.

@internalComponent
*/
void CSmsPermanentFileStore::InternalizeEntryL(const TInt aIndex, CSmsMessage& aSmsMessage, CArrayFix<TInt>& aIndexArray, CArrayFix<TGsmSms>& aSmsArray)
	{
	TSmsReassemblyEntry&  entry = iEntryArray[aIndex];
	LOGSMSPROT2("CSmsPermanentFileStore::InternalizeEntryL Start [sid=%d]", entry.DataStreamId().Value());
	RStoreReadStream readstream;
	readstream.OpenLC(*iFileStore, entry.DataStreamId());
	readstream >> aSmsMessage;
	TInt count=readstream.ReadInt32L();
	TInt i;
	for (i=0; i<count; i++)
		{
		TInt index=readstream.ReadInt32L();
		aIndexArray.AppendL(index);
		}
	count=readstream.ReadInt32L();
	if(count!=aIndexArray.Count())
		{
		User::Leave(KErrCorrupt);
		}
	for (i=0; i<count; i++)
		{
		RMobileSmsMessaging::TMobileSmsGsmTpdu pdu;
		readstream >> pdu;
		TGsmSms sms;
		sms.SetPdu(pdu);
		aSmsArray.AppendL(sms);
		}
	CleanupStack::PopAndDestroy();
	//Set other properties of CSmsMessage
	aSmsMessage.SetStorage(entry.Storage());
	aSmsMessage.SetLogServerId(entry.LogServerId());
	aSmsMessage.SetTime(entry.Time());
	LOGSMSPROT2("CSmsPermanentFileStore::InternalizeEntryL End [count=%d]", count);
	}

/*
It removes the PDUs from permanent store file.
This function is needed because after forwarding the incomplete message
to client, the corresponding PDUs needs to be be removed from permanent
store file.
This functionality is specific to class 0 re-assembly store.

@param aIndex index number of the message to be removed.
@param aStartPos starting pos of pdu to be removed.
@param aEndPos end pos of pdu to be removed.

@internalComponent
*/
void CSmsPermanentFileStore::RemovePDUsL(TInt aIndex, TInt aStartPos, TInt aEndPos)
	{
	LOGSMSPROT1("CSmsPermanentFileStore::RemovePDUsL");

	CSmsBuffer*  buffer = CSmsBuffer::NewL();
	CSmsMessage*  smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer);
	CleanupStack::PushL(smsMessage);

	CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
	CleanupStack::PushL(indexArray);
	CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
	CleanupStack::PushL(smsArray);

	InternalizeEntryL(aIndex, *smsMessage, *indexArray, *smsArray);

	TInt count = indexArray->Count();
	for (TInt i=count; i>0; i--)
		{
		if ((indexArray->At(i-1) >= aStartPos) && (indexArray->At(i-1) <= aEndPos))
			{
			indexArray->Delete(i-1);
			smsArray->Delete(i-1);
			}
		}

	/*
	There are 3 scenarios in this case:
	1. If all the entries are removed,
	then there is no need to store the entry in this permanent file.
	2. If few entries are removed,
	then externalize the remaining entries. Update count field also.
	3. If no entries are removed, then do nothing.
	*/
	if (indexArray->Count()==0)
		{
		DeleteEntryL(aIndex);
		}
	else if (count!=indexArray->Count())
		{
		TStreamId  streamid = iEntryArray[aIndex].DataStreamId();
		ExternalizeEntryL(streamid, *smsMessage, *indexArray, *smsArray);

		TSmsReassemblyEntry entry;
		entry = iEntryArray[aIndex];
		entry.SetCount(indexArray->Count());
		ChangeEntryL(aIndex, entry);
		}
	CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray
	}

/**
 *  Sets the permanent store as in-transaction.
 *  
 *  The function checks the validity of the call and leaves KErrAccessDenied if
 *  invalid.
 *  @capability None
 */
void CSmsPermanentFileStore::BeginTransactionL()
	{
    LOGSMSPROT4("CSmsPermanentFileStore::BeginTransactionL [this=0x%08X iInTransaction=%d iFileStore=0x%08X]", this, iInTransaction, iFileStore);

	if (iFileStore == NULL || iInTransaction)
		{
		LOGSMSPROT1("WARNING CSmsPermanentFileStore::BeginTransactionL leaving with KErrAccessDenied");
		User::Leave(KErrAccessDenied);
		}

	iInTransaction = ETrue;
	} // CSmsPermanentFileStore::BeginTransactionL

/**
 *  It reverts the transaction.
 */
void CSmsPermanentFileStore::Revert()
	{
	LOGSMSPROT3("CSmsPermanentFileStore::Revert(): this=0x%08X, iInTransaction=%d",
    		 this, iInTransaction);

	iFileStore->Revert();
	iInTransaction = EFalse;
	ReinstateDeletedEntries();
	} // CSmsPermanentFileStore::Revert

/**
 *  It commits the transaction. Then it compact the permanent store file.
 */
void CSmsPermanentFileStore::DoCommitAndCompactL()
	{
	LOGSMSPROT1("CSmsPermanentFileStore::DoCommitAndCompactL()");

	LOGSMSPROTTIMESTAMP();
	iFileStore->CommitL();
	LOGSMSPROTTIMESTAMP();

	iCommitCount--;
	if ((iCommitCount < 0) || (iCompact))
		{
		iCommitCount = KNumStoreCommitsBeforeCompaction;
		iFileStore->CompactL();
		iFileStore->CommitL();
		iCompact = EFalse;
		}
	} // CSmsPermanentFileStore::DoCommitAndCompactL

/**
 *  It commits the transaction.
 */
void CSmsPermanentFileStore::CommitTransactionL()
	{
	LOGSMSPROT4("CSmsPermanentFileStore::CommitTransactionL(): this=0x%08X iInTransaction=%d iFileStore=0x%08X",
    		 this, iInTransaction, iFileStore);

	ExternalizeEntryArrayL();

#ifdef _SMS_LOGGING_ENABLED
	TRAPD(err, DoCommitAndCompactL());
	if (err != KErrNone)
		{
		LOGGSMU2("WARNING! could not CommitL/CompactL due to %d", err);
		User::Leave(err);
		}
#else
	DoCommitAndCompactL();
#endif

	iInTransaction = EFalse;
	RemoveDeletedEntries();
	}

/**
 *  It removes the deleted entries from entry arry.
 *	This function is called after commit.
 */
void CSmsPermanentFileStore::RemoveDeletedEntries()
	{
	LOGSMSPROT1("CSmsPermanentFileStore::RemoveDeletedEntries()");

	TInt count=iEntryArray.Count();
	while (count--)
		{
		TSmsReassemblyEntry& entry = iEntryArray[count];

		if (entry.IsDeleted())
			{
			iEntryArray.Delete(count);
			}
		else
			{
			entry.SetIsAdded(EFalse);
			}
		}
	} // CSmsPermanentFileStore::RemoveDeletedEntries

/**
 *  It reinstate the deleted/added entries from entry arry.
 *	This function is called after revert operation.
 */
void CSmsPermanentFileStore::ReinstateDeletedEntries()
	{
	LOGSMSPROT1("CSmsPermanentFileStore::ReinstateDeletedEntries()");

	TInt count=iEntryArray.Count();
	while (count--)
		{
		TSmsReassemblyEntry& entry = iEntryArray[count];

		if (entry.IsAdded())
			{
			iEntryArray.Delete(count);
			}
		else
			{
			entry.SetIsDeleted(EFalse);
			}
		}
	} // CSmsPermanentFileStore::ReinstateDeletedEntries

/**
 *  Constructor
 *  
 *  @capability None
 */
TSmsPreAllocatedFileStoreReassemblyEntry::TSmsPreAllocatedFileStoreReassemblyEntry():
	TSmsReassemblyEntry(),
	iPreAllocatedStorageId(0),
	iStatus(RMobileSmsStore::EStoredMessageUnknownStatus),
	iTimeOffset(0),
	iDecodedOnSim(EFalse),
	iForwardToClient(EFalse),
	iForwardedCount(0),
	iBitMap(NULL)
	{
	//Have to externalize so initialize to 0.
	for (TInt i=0; i<KBitMapLen; i++)
		{
		iBitMap.Append(0x00);
		}
	}

/*
It returns the reference of a particular bit-map index.

@param aIndex index number of the bit-map.
@param aBitMap bit-map value.

@internalComponent
*/
void TSmsPreAllocatedFileStoreReassemblyEntry::GetBitMap(TUint8 aIndex, TUint8& aBitMap)
	{
	aBitMap = iBitMap[aIndex];
	}

/*
It sets the value of a bit-map.

@param aIndex index number of the bit-map.
@param aBitMap bit-map value

@internalComponent
*/
void TSmsPreAllocatedFileStoreReassemblyEntry::SetBitMap(TUint8 aIndex, TUint8 aBitMap)
	{
	iBitMap[aIndex] = aBitMap;
	}

/**
Static factory constructor. Uses two phase 
construction and leaves nothing on the CleanupStack.

@param aFs  File Server handle.
@param aFileName  Permanent file store name.
@param aMaxClass0Msg max class 0 message that can be stored.
@param aMaxPDUSeg max number of pdus that can be stored.
@param aVersion version number of pre-allocated file.

@return A pointer to the newly created CPreallocatedFile object.

@pre A connected file server session must be passed as parameter.
@post CPreallocatedFile object is now fully initialised

@internalComponent
*/
CPreallocatedFile* CPreallocatedFile::NewL(RFs& aFs, const TDesC& aFileName, TInt aMaxClass0Msg, TInt aMaxPDUSeg, TPreAllocatedFileVersion aVersion)
	{
	LOGSMSPROT1("CPreallocatedFile::NewL()");
	CPreallocatedFile*  self = new (ELeave) CPreallocatedFile(aFs, aMaxClass0Msg, aMaxPDUSeg, aVersion);
	CleanupStack::PushL(self);
	self->ConstructL(aFileName);
	CleanupStack::Pop(self);

	return self;
	}

void CPreallocatedFile::ConstructL(const TDesC& aFileName)
	{
	LOGSMSPROT1("CPreallocatedFile::ConstructL()");
	iFileName = aFileName.AllocL();
	}

/**
 *  Constructor.
*/
CPreallocatedFile::CPreallocatedFile(RFs& aFs, TInt aMaxClass0Msg, TInt aMaxPDUSeg, TPreAllocatedFileVersion aVersion)
	:iFs(aFs), iEntryArray(KFlatArrayGranularity), iReinstateEntryInfo(KFlatArrayGranularity), iMaxClass0Msg(aMaxClass0Msg), iMaxPDUSeg(aMaxPDUSeg), iVersion(aVersion)
	{
	/*
	Format of File:
	Version Number, Header Section & Data Section
	Version Number - Interger Value.
	Header Section: Number of entries, Array of Entries, Array of PDU identifier section, Checksum.
	Data Section: Array of Container. Each container contains Sms slot informatiuon, index number & PDU.
	*/

	// Calculate the size of each section.

	// Entry section will always contain one more entry than configured one by the user.
	// Because it will provide one extra slot to store the new message for time being
	// before forwarding the oldest message.
	iSizeOfEntrySection = (KSizeOfPreAllocatedStoreEntry * (iMaxClass0Msg + 1));

	iSizeOfStorageIdentifierSection = ((sizeof(TInt))*(iMaxPDUSeg+1));

	TInt sizeOfHeaderSection = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection + KSizeOfChecksum;

	TInt sizeOfDataSection = KSizeOfAContainer  * (iMaxPDUSeg+1);

	// Calculate the size of File.
	iSizeOfFile = KSizeOfPreallocatedFileVersion + 2 * sizeOfHeaderSection + sizeOfDataSection;

	iBeginOfDuplicateHeaderSection = KSizeOfPreallocatedFileVersion + sizeOfHeaderSection;
	iBeginOfDataSection = KSizeOfPreallocatedFileVersion + 2 * sizeOfHeaderSection;
	}

/**
 *  Destructor. It destroys all the member variables.
*/
CPreallocatedFile::~CPreallocatedFile()
	{
	delete iFileName;
	}

/**
It checks & returns whether the file exist or not. If file is there whether
it is corrupted or not.

@internalComponent
*/
TBool CPreallocatedFile::IsFileOK()
	{
	LOGSMSPROT1("CPreallocatedFile::IsFileOK()");

	TEntry entry;
	//  Check file exists
	TInt ret=iFs.Entry(iFileName->Des(), entry);
	// Check the size of file, if size does not match then assume that file
	// is corrupted, then return KErrNotFound.
	if ((ret == KErrNone) && (entry.iSize != iSizeOfFile))
		{
		ret = KErrNotFound;
		}

	if (ret == KErrNone)
		{
		return ETrue;
		}
	else
		{
		return EFalse;
		}
	}

/**
It creates a pre-allocated file.

@internalComponent
*/
void CPreallocatedFile::CreateL()
	{
	LOGSMSPROT1("CPreallocatedFile::CreateL");

	User::LeaveIfError(iFile.Replace(iFs, iFileName->Des(), EFileWrite));
	User::LeaveIfError(iFile.SetSize(iSizeOfFile));
	iFile.Flush();

	// Externalize Version Number
	//TInt version = iVersion;
	TPtr8 memPtr((TUint8*) &iVersion, KSizeOfPreallocatedFileVersion, KSizeOfPreallocatedFileVersion);
	iFile.Write(0, memPtr);

	//Externalize Header Information
	ExternalizeEntryArray();

	// Initialize Storage Identifier Section.
	TInt storageIdentifier=0;
	memPtr.Set((TUint8*) &storageIdentifier, sizeof(storageIdentifier), sizeof(storageIdentifier));
	TInt pos = KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
	TInt pos2 = 0;
	for (TInt count=0; count<iMaxPDUSeg+1; count++)
		{
		pos2 = pos + count * sizeof(storageIdentifier);
		iFile.Write(KBeginOfMasterHeaderSection + pos2, memPtr);
		//iFile.Write(iBeginOfDuplicateHeaderSection + pos2, memPtr);
		}

	// Initialize Checksum
	PutChecksumValueL();

	// Close it to make sure that file is created correctly (defensive approach).
	iFile.Close();
	//Again Open the file
	OpenL();
	}

/**
It opens the pre-allocated file. Then it internalizes all the entry information.

@internalComponent
*/
void CPreallocatedFile::OpenL()
	{
	User::LeaveIfError(iFile.Open(iFs, iFileName->Des(), EFileShareExclusive|EFileRead|EFileWrite));
	// Check the validity of the data.
	CheckDataL();
	// Internalize data
	InternalizeEntryArrayL();
	}

/**
It closes the pre-allocated file.

@internalComponent
*/
void CPreallocatedFile::Close()
	{
	iFile.Close();
	iEntryArray.Reset();
	iReinstateEntryInfo.Reset();
	}

/**
It internalizes the entry info from pre-allocated file.

@internalComponent
*/
void CPreallocatedFile::InternalizeEntryArrayL()
	{
	iEntryArray.Reset();
	TInt numberOfMessage;
	TPtr8 memPtr((TUint8*) &numberOfMessage, sizeof(numberOfMessage), sizeof(numberOfMessage));
	iFile.Read(KBeginOfMasterHeaderSection, memPtr);

	TSmsPreAllocatedFileStoreReassemblyEntry tmpClass0ReassemblyStore;
	memPtr.Set((TUint8*) &tmpClass0ReassemblyStore, KSizeOfPreAllocatedStoreEntry, KSizeOfPreAllocatedStoreEntry);

	TInt pos = 0;
	for (TInt count=0; count < numberOfMessage; count++)
		{
		pos = sizeof(TInt) + (count * KSizeOfPreAllocatedStoreEntry);
		iFile.Read(KBeginOfMasterHeaderSection + pos, memPtr);
		iEntryArray.AppendL(tmpClass0ReassemblyStore);
		}
	}

/**
It externalizes the entry info to pre-allocated file.

@internalComponent
*/
void CPreallocatedFile::ExternalizeEntryArray()
	{
	TInt count=iEntryArray.Count();
	TInt numberOfMessage=0;
	TInt i=0;

	for (; i<count; i++)
		{
		if (!iEntryArray[i].IsDeleted())
			{
			numberOfMessage++;
			}
		}

	//Externalize number of mesages.
	TPtr8 memPtr((TUint8*) &numberOfMessage, sizeof(numberOfMessage), sizeof(numberOfMessage));
	iFile.Write(KBeginOfMasterHeaderSection, memPtr);

	TInt entryNumber = 0;
	//Externalize all the entries.
	for (i=0; i<count; i++)
		{
		if (!iEntryArray[i].IsDeleted())
			{
			//At the time of externalizing don't externalize IsAdded(), IsDeleted() value
			TBool isAdded = iEntryArray[i].IsAdded();
			TBool isDeleted = iEntryArray[i].IsDeleted();
			iEntryArray[i].SetIsAdded(EFalse);
			iEntryArray[i].SetIsDeleted(EFalse);
			memPtr.Set((TUint8*) &iEntryArray[i], KSizeOfPreAllocatedStoreEntry, KSizeOfPreAllocatedStoreEntry);
			iFile.Write(KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + (entryNumber * KSizeOfPreAllocatedStoreEntry), memPtr);
			//Again update IsAdded(), IsDeleted() with previous value
			iEntryArray[i].SetIsAdded(isAdded);
			iEntryArray[i].SetIsDeleted(isDeleted);
			entryNumber++;
			}
		}

	count = numberOfMessage;
	/*
	Externalize extra entry information with default information.
	This initialization is required because of checksum.
	*/
	TSmsPreAllocatedFileStoreReassemblyEntry tmpClass0ReassemblyStore;
	memPtr.Set((TUint8*) &tmpClass0ReassemblyStore, KSizeOfPreAllocatedStoreEntry, KSizeOfPreAllocatedStoreEntry);
	TInt pos = 0;
	for (; count<iMaxClass0Msg + 1; count++)
		{
		pos = KSizeOfNumberOfEntrySection + (count * KSizeOfPreAllocatedStoreEntry);
		iFile.Write(KBeginOfMasterHeaderSection + pos, memPtr);
		}
	iFile.Flush();
	}

/*
It adds the new message in pre-allocated file.

@param aIndex (output) index number on which message is added.
@param aSmsMessage reference to sms message to be added.
@param aGsmSms reference to GsmSms object to be added.

@internalComponent
*/
void CPreallocatedFile::AddNewMessageL(TInt& aIndex, CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms)
	{
	LOGSMSPROT1("CPreallocatedFile::AddNewMessageL");
	//Gets the next free slot where the message will be stored.
	TInt nextFreeSlot = GetFreeContainer();
	TInt pduIndex=aSmsMessage.IsDecoded()? 0: aSmsMessage.SmsPDU().ConcatenatedMessagePDUIndex();
	if (aSmsMessage.Storage() == CSmsMessage::ESmsSIMStorage  ||
		aSmsMessage.Storage() == CSmsMessage::ESmsCombinedStorage)
		{
		const TGsmSmsSlotEntry&  newSlot = aSmsMessage.iSlotArray[0];
		ExternalizeEntry(nextFreeSlot, newSlot, pduIndex, aGsmSms);
		}
	else
		{
		ExternalizeEntry(nextFreeSlot, pduIndex, aGsmSms);
		}

	/*
	Gets the unique id which will identify the containers where the 
	PDU related to this messsage are stored.
	*/
	TInt freeStorageId = GetFreeStorageId();
	AddStorageIdL(nextFreeSlot, freeStorageId);

	TSmsPreAllocatedFileStoreReassemblyEntry tmpEntry;
	//Fill-up entry information...
	CReassemblyStoreUtility::PopulateEntry(tmpEntry, aSmsMessage, 1);
	tmpEntry.SetPreAllocatedStorageId(freeStorageId);
	//The properties below need to be set only once when a PDU of a new message arrives.
	tmpEntry.SetStatus((RMobileSmsStore::TMobileSmsStoreStatus)aSmsMessage.Status());
	tmpEntry.SetUTCOffset(aSmsMessage.UTCOffset());
	tmpEntry.SetDecodedOnSIM(aSmsMessage.DecodedOnSim());
	tmpEntry.SetForwardToClient(aSmsMessage.ForwardToClient());

	// Add & extrenalize entry array.
	aIndex = iEntryArray.Count();
	AddEntryL(tmpEntry);
	}

/*
It updates the existing message in permanent store file.

@param aIndex index number on which message is to be updated.
@param aSmsMessage reference to sms message to be updated.
@param aPduIndex index of the PDU to be updated.
@param aSms sms of the PDU to be updated.

@internalComponent
*/
void CPreallocatedFile::UpdateExistingMessageL(TInt aIndex, const CSmsMessage& aSmsMessage, TInt aPduIndex, const TGsmSms& aSms)
	{
	LOGSMSPROT1("CPreallocatedFile::UpdateExistingMessageL()");
	TInt preAllocatedStorageId = iEntryArray[aIndex].PreAllocatedStorageId();
	if (preAllocatedStorageId == KErrNotFound)
		{
		/*
		This condition arises when part of message is stored in permanent store file & 
		other parts arrive when system is out of disk.
		*/
		preAllocatedStorageId = GetFreeStorageId();
		iEntryArray[aIndex].SetPreAllocatedStorageId(preAllocatedStorageId);
		}
	// Externalize Entry in one of free containers.
	TInt freeSlot = GetFreeContainer();
	if (aSmsMessage.Storage() == CSmsMessage::ESmsSIMStorage  ||
		aSmsMessage.Storage() == CSmsMessage::ESmsCombinedStorage)
		{
		const TGsmSmsSlotEntry&  newSlot = aSmsMessage.iSlotArray[0];
		ExternalizeEntry(freeSlot, newSlot, aPduIndex, aSms);
		}
	else
		{
		ExternalizeEntry(freeSlot, aPduIndex, aSms);
		}

	AddStorageIdL(freeSlot, preAllocatedStorageId);
	TSmsPreAllocatedFileStoreReassemblyEntry entry;
	entry = iEntryArray[aIndex];
	/*
	This value must be set because this may the first PDU of an 
	existing message which is stored in pre-allocated store file.
	*/
	entry.SetPreAllocatedStorageId(preAllocatedStorageId);
	entry.SetCount(entry.Count()+1);
	ChangeEntryL(aIndex, entry);
	}

/*
It matches the entry with entry in pre-allocated file & returns
the index. If not found it returns KErrNotFound.

@param aEntry reference to entry information.
@param aIndex (output) returns the index number.

@internalComponent
*/
void CPreallocatedFile::MatchEntryToExistingMessage(const TReassemblyEntry& aEntry,
													TInt& aIndex)
	{
	LOGSMSPROT1("CPreallocatedFile::MatchEntryToExistingMessage()");

	aIndex = KErrNotFound;

	//
	// Search the reassembly store for a matching entry...
	//
	TInt reassemblyCount = iEntryArray.Count();

	for (TInt  index = 0;  index < reassemblyCount;  index++)
		{
		TSmsPreAllocatedFileStoreReassemblyEntry&  entry = iEntryArray[index];

		if (entry.Reference() == aEntry.Reference()  &&
		    entry.Total() == aEntry.Total()  &&
		    entry.PduType() == aEntry.PduType()  &&
			entry.Storage() == aEntry.Storage()  &&
			entry.Description2() == aEntry.Description2())
			{
			//
			// Found it!
			//
			aIndex = index;
			break;
			}
		}

	LOGSMSPROT2("CPreallocatedFile::MatchEntryToExistingMessage(): aIndex=%d", aIndex);
	}

/*
It updates the log server id of the message.

@param aIndex index number of the message to be updated.
@param aLogServerId log server id.

@internalComponent
*/
void CPreallocatedFile::UpdateLogServerIdL(TInt& aIndex, TLogId aLogServerId)
	{
	LOGSMSPROT1("CPreallocatedFile::UpdateLogServerId");

	TSmsPreAllocatedFileStoreReassemblyEntry entry;
	entry = iEntryArray[aIndex];

	if (entry.LogServerId() != aLogServerId)
		{
		entry.SetLogServerId(aLogServerId);
		ChangeEntryL(aIndex, entry);
		}
	}

/*
It sets the message passed to client or not.

@param aIndex index number of the message to be updated.
@param aBool boolean value indicating whether message is passes or not.

@internalComponent
*/
void CPreallocatedFile::SetPassedToClientL(TInt aIndex, TBool aBool)
	{
	LOGSMSPROT2("CPreallocatedFile::SetPassedToClientL(): aIndex=%d", aIndex);

	TSmsPreAllocatedFileStoreReassemblyEntry entry;
	entry = iEntryArray[aIndex];

	if (entry.PassedToClient() != aBool)
		{
		entry.SetPassedToClient(aBool);
		ChangeEntryL(aIndex, entry);
		}
	}

/*
It adds the new entry in the existing entry array.

@param aEntry entry to be added.

@internalComponent
*/
void CPreallocatedFile::AddEntryL(TSmsPreAllocatedFileStoreReassemblyEntry& aEntry)
	{
	LOGSMSPROT1("CPreallocatedFile::AddEntryL");
	iEntryArray.AppendL(aEntry);
	iEntryArray[iEntryArray.Count()-1].SetIsAdded(ETrue);
	}

/*
It changes the existing entry with new entry.

@param aIndex index number of the entry which will be changed.
@param aNewEntry entry to be updated.

@internalComponent
*/
void CPreallocatedFile::ChangeEntryL(TInt aIndex, const TSmsPreAllocatedFileStoreReassemblyEntry& aNewEntry)
	{
	LOGSMSPROT2("CPreallocatedFile::ChangeEntryL(): aIndex=%d", aIndex);
	iEntryArray[aIndex].SetIsDeleted(ETrue);
	iEntryArray.InsertL(aIndex,aNewEntry);
	iEntryArray[aIndex].SetIsAdded(ETrue);
	}

/*
It deletes the entry.

@param aIndex index number of the entry which will be deleted.

@internalComponent
*/
void CPreallocatedFile::DeleteEntryL(TInt aIndex)
	{
	LOGSMSPROT2("CPreallocatedFile::DeleteEntryL(): aIndex=%d", aIndex);
	if (iEntryArray[aIndex].PreAllocatedStorageId() != KErrNotFound)
		{
		ClearEntryL(iEntryArray[aIndex].PreAllocatedStorageId(), iEntryArray[aIndex].Count());
		}
	iEntryArray[aIndex].SetIsDeleted(ETrue);
	}

/*
It searches all the container & clears all the entries which
contain the PDUs of the message (uniquely identified by aStorageId).

NOTE:
	It keeps the record of removed entries (container id, pre-allocated storage id 
	& operation performed (add/delete)) in iReinstateEntryInfo variable.
	It will be required in case of revert operation. As the container which
	refers to the container which contains actual PDU is freed,
	so it will not be a problem to restore the removed PDUs.
	In case of revert operation, the removed information can be found in
	iReinstateEntryInfo variable which can be restored easily. 
	The container can be replaced with previous pre-allocated storage id.
	After commiting the transaction, this varaible will be re-initialized.

@param aStorageId unique id of message to be cleared.
@param aNumberOfPDUs number of PDUs to be cleared.

@internalComponent
*/
void CPreallocatedFile::ClearEntryL(TInt aStorageId, TInt aNumberOfPDUs)
	{
	LOGSMSPROT1("CPreallocatedFile::ClearEntryL");

	//Read storage id.
	TInt storageId;
	TPtr8 memPtr((TUint8*) &storageId, sizeof(storageId), sizeof(storageId));
	TInt beginOfStorageIdSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
	TInt beginOfStorageId = 0;
	TInt count;
	TInt count2(0);

	TInt cleanStorageId=0;
	TPtr8 memPtr2((TUint8*) &cleanStorageId, sizeof(cleanStorageId), sizeof(cleanStorageId));

	for (count=0; count<iMaxPDUSeg+1; count++)
		{
		TReinstateEntryInfo entry;
		entry.iPreAllocatedStorageId=aStorageId;
		entry.iFlag=EEntryIsDeleted;

		beginOfStorageId = beginOfStorageIdSection + (count * sizeof(storageId));
		iFile.Read(beginOfStorageId, memPtr);
		if (storageId == aStorageId)
			{
			count2++;
			iFile.Write(beginOfStorageId, memPtr2);
			entry.iContainerId=count+1;
			iReinstateEntryInfo.AppendL(entry);
			}
		if (count2 >= aNumberOfPDUs)
			{
			break;
			}
		}
	iFile.Flush();
	}

/*
It updates the storage identifier section. It updates the corresponding container
in the storage identifier section specifying the PDU where it is stored.

NOTE:
	It keeps the record of added entries in iReinstateEntryInfo variable.
	It will be required in case of revert operation.
	After commiting the transaction, this varaible should be re-initialized.

@param aIndex index number of the container where PDU has been stored..
@param aStorageId unique id of added message.

@internalComponent
*/
void CPreallocatedFile::AddStorageIdL(TInt aIndex, TInt aStorageId)
	{
	//Put storage id in master header section
	TPtr8 memPtr((TUint8*) &aStorageId, sizeof(aStorageId), sizeof(aStorageId));
	TInt beginOfStorageIdentifierSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
	iFile.Write( beginOfStorageIdentifierSection + ((aIndex - 1) * sizeof(TInt)), memPtr);

	TReinstateEntryInfo entry;
	entry.iContainerId=aIndex;
	entry.iPreAllocatedStorageId=aStorageId;
	entry.iFlag=EEntryIsAdded;
	iReinstateEntryInfo.AppendL(entry);
	}

/*
It returns the next free available free container where next PDU can be stored.

It goes through storage identifier section, it reads the conents of all storage
identfier & return that container which is free.
All the free container is identifiable by value 0. If the value is not 0 then it
contains the storage id of a particular message.

@internalComponent
*/
TInt CPreallocatedFile::GetFreeContainer()
	{
	// Get the next free slot.
	TInt storageId;
	TPtr8 memPtr((TUint8*) &storageId, sizeof(storageId), sizeof(storageId));
	TInt beginOfStorageIdSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
	TInt beginOfStorageId = 0;
	TInt count;

	for (count=0; count<iMaxPDUSeg+1; count++)
		{
		beginOfStorageId = beginOfStorageIdSection + (count * sizeof(storageId));
		iFile.Read(beginOfStorageId, memPtr);
		if (storageId == 0)
			{
			break;
			}
		}

	// Count should never equal to iMaxPDUSeg+1
	__ASSERT_DEBUG(count < iMaxPDUSeg+1, SmspPanic(KSmspPanicPreallocatedFileNoFreeContainer));
	
	return (count + 1);
	}

/*
It returns the next free storage id.

Storage id is used to identify the messages stored in pre-allocated file.
A message might contain multiple PDUs and all those PDUs will be stored in 
different container. This storage id will be used to identify on which 
container the PDUs of a message are stored.

This logic is based on the max number of messages that can be stored in
pre-allocated file at one time. So free storage id is always used from
this pool of numbers (from 1 till (max calls 0 message+2)).

@internalComponent
*/
TInt CPreallocatedFile::GetFreeStorageId()
	{
	TInt count;

	for (count=1; count < iMaxClass0Msg + 2; count++)
		{
		TInt numberOfEntries = iEntryArray.Count();
		TInt count2;
		for (count2 = 0; count2 < numberOfEntries; count2++)
			{
			if (count == iEntryArray[count2].PreAllocatedStorageId())
				{
				break;
				}
			}
			if (count2 == numberOfEntries)
				{
				break;
				}
		}
		return count;
	}

/*
It externalizes(writes) the entry in permanent store file.

@param aContainerId container id where the sms pdu needs to externalized.
@param aSmsSlot reference to slot information.
@param aIndex index number of PDU.
@param aGsmSms refence to sms pdu.

@internalComponent
*/
void CPreallocatedFile::ExternalizeEntry(TInt aContainerId, const TGsmSmsSlotEntry& aSmsSlot, TInt aIndex, const TGsmSms& aGsmSms)
	{
	LOGSMSPROT3("CPreallocatedFile::ExternalizeEntry() 1: aContainerId=%d, aIndex=%d", aContainerId, aIndex);

	// Container id must not be greater than max pdu segment.
	TInt pos = iBeginOfDataSection + ((aContainerId - 1) * (KSizeOfGsmSmsSlotEntry + sizeof(TInt) + KSizeOfSmsGsmPDU));
	// Store slot info
	TPtr8 memPtr((TUint8*) &aSmsSlot, sizeof(aSmsSlot), sizeof(aSmsSlot));
	iFile.Write(pos, memPtr);
	// Store the PDU index value
	memPtr.Set((TUint8*) &aIndex, sizeof(aIndex), sizeof(aIndex));
	iFile.Write(pos + KSizeOfGsmSmsSlotEntry, memPtr);
	// Store the TGsmSms value
	RMobileSmsMessaging::TMobileSmsGsmTpdu pdu;
	pdu = aGsmSms.Pdu();
	memPtr.Set((TUint8*) &pdu, KSizeOfSmsGsmPDU, KSizeOfSmsGsmPDU);
	iFile.Write(pos + KSizeOfGsmSmsSlotEntry + sizeof(TInt), memPtr);

	__ASSERT_DEBUG(pos + KSizeOfGsmSmsSlotEntry + sizeof(TInt) + KSizeOfSmsGsmPDU <= iSizeOfFile,
			       SmspPanic(KSmspPanicPreallocatedFileCorrupt));
	}

/*
It externalizes(writes) the entry in permanent store file.

@param aContainerId container id where the sms pdu needs to externalized.
@param aIndex index number of PDU.
@param aGsmSms refence to sms pdu.

@internalComponent
*/
void CPreallocatedFile::ExternalizeEntry(TInt aContainerId, TInt aIndex, const TGsmSms& aGsmSms)
	{
	LOGSMSPROT3("CPreallocatedFile::ExternalizeEntry() 2: aContainerId=%d, aIndex=%d", aContainerId, aIndex);

	// Container id must not be greater than max pdu segment.
	TInt pos = iBeginOfDataSection + ((aContainerId - 1) * (KSizeOfGsmSmsSlotEntry + sizeof(TInt) + KSizeOfSmsGsmPDU));
	// Store the PDU index value
	TPtr8 memPtr((TUint8*) &aIndex, sizeof(aIndex), sizeof(aIndex));
	iFile.Write(pos + KSizeOfGsmSmsSlotEntry, memPtr);
	// Store the TGsmSms value
	RMobileSmsMessaging::TMobileSmsGsmTpdu pdu;
	pdu = aGsmSms.Pdu();
	memPtr.Set((TUint8*) &pdu, KSizeOfSmsGsmPDU, KSizeOfSmsGsmPDU);
	iFile.Write(pos + KSizeOfGsmSmsSlotEntry + sizeof(TInt), memPtr);

	__ASSERT_DEBUG(pos + KSizeOfGsmSmsSlotEntry + sizeof(TInt) + KSizeOfSmsGsmPDU <= iSizeOfFile,
			       SmspPanic(KSmspPanicPreallocatedFileCorrupt));
	}

/*
It internalizes(reads) the entry from permanent store file.

@param aIndex index number of the message to be internalized.
@param aSmsMessage (output) reference to sms message.
@param aIndexArray (output) refence to array of index.
@param aSmsArray (output) refence to array of sms.

@internalComponent
*/
void CPreallocatedFile::InternalizeEntryL(const TInt aIndex, CSmsMessage& aSmsMessage, CArrayFix<TInt>& aIndexArray, CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGSMSPROT1("CPreallocatedFile::InternalizeEntryL");
	TSmsPreAllocatedFileStoreReassemblyEntry&  entry = iEntryArray[aIndex];
	//Set other properties of CSmsMessage
	aSmsMessage.SetStorage(entry.Storage());
	aSmsMessage.SetStatus((NMobileSmsStore::TMobileSmsStoreStatus)entry.Status());
	aSmsMessage.SetLogServerId(entry.LogServerId());
	aSmsMessage.SetTime(entry.Time());
	aSmsMessage.SetUTCOffset(entry.UTCOffset());
	aSmsMessage.SetDecodedOnSIM(entry.DecodedOnSIM());
	aSmsMessage.SetForwardToClient(entry.ForwardToClient());
	aSmsMessage.SetToFromAddressL(entry.Description2());

	LOGSMSPROT2("CPreallocatedFile::InternalizeEntryL Start [sid=%d]", entry.PreAllocatedStorageId());
	if (entry.PreAllocatedStorageId()==KErrNotFound)
		{
		return;
		}

	TInt beginOfStorageIdSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
	//This will be used to read the storgae id which will indicate 
	//that corresponding container contains actual PDU
	TInt storageId=0;
	TPtr8 memPtr1((TUint8*) &storageId, sizeof(storageId), sizeof(storageId));

	TInt storageIdPos = 0;
	TInt dataPos = 0;
	//This will be used to read the slot information
	TGsmSmsSlotEntry smsSlotEntry;
	TPtr8 memPtr2((TUint8*) &smsSlotEntry, KSizeOfGsmSmsSlotEntry, KSizeOfGsmSmsSlotEntry);
	//This will be used to read the concatenated PDU index.
	TInt index=0;
	TPtr8 memPtr3((TUint8*) &index, sizeof(index), sizeof(index));
	//This will be used to read PDU.
	RMobileSmsMessaging::TMobileSmsGsmTpdu pdu;
	TPtr8 memPtr4((TUint8*) &pdu, KSizeOfSmsGsmPDU, KSizeOfSmsGsmPDU);
	TGsmSms sms;

	//Number of PDUs stored in this pre-allocated file
	TInt noOfPDUs = iEntryArray[aIndex].Count();
	//Will indicate how many number of PDUs already read from pre-allocated file.
	TInt noOfPDUsRead = 0;
	// Can also get the number of pdu stored in pre-allocated file from count's variable.
	for (TInt count=0; count<iMaxPDUSeg+1; count++)
		{
		storageIdPos = beginOfStorageIdSection + count * sizeof(TInt);
		iFile.Read(storageIdPos, memPtr1);
		if (storageId == entry.PreAllocatedStorageId())
			{//This means corresponding container contains the actual PDU
			noOfPDUsRead++;
			//Find the position of container where actaul PDU is stored.
			dataPos = iBeginOfDataSection + ((count) * (KSizeOfGsmSmsSlotEntry + sizeof(TInt) + KSizeOfSmsGsmPDU));
			//Read slot information
			if (entry.Storage() == CSmsMessage::ESmsSIMStorage  ||
				entry.Storage() == CSmsMessage::ESmsCombinedStorage)
				{
				iFile.Read(dataPos + KSizeOfGsmSmsSlotEntry, memPtr2);
				aSmsMessage.AddSlotL(smsSlotEntry);
				}
			//Read index information
			iFile.Read(dataPos + KSizeOfGsmSmsSlotEntry, memPtr3);
			aIndexArray.AppendL(index);
			//Read PDU
			iFile.Read(dataPos + KSizeOfGsmSmsSlotEntry + sizeof(index), memPtr4);
			sms.SetPdu(pdu);
			aSmsArray.AppendL(sms);
			if (noOfPDUsRead == noOfPDUs)
				{
				break;
				}
			}
		}

	LOGSMSPROT2("CPreallocatedFile::InternalizeEntryL End [noOfPDUsRead=%d]", noOfPDUsRead);
	}

/*
It removes the PDUs from pre-allocated store file.
This function is needed because after forwarding the incomplete message
to client, the corresponding PDUs needs to be be removed from permanent
store file.
This functionality is specific to class 0 re-assembly store.

@param aIndex index number of the message to be removed.
@param aStartPos starting pos of pdu to be removed.
@param aEndPos end pos of pdu to be removed.

@internalComponent
*/
void CPreallocatedFile::RemovePDUsL(TInt aIndex, TInt aStartPos, TInt aEndPos)
	{
	LOGSMSPROT1("CPreallocatedFile::RemovePDUsL");

	if ((aStartPos < 1) || (aEndPos > 256) || (aStartPos > aEndPos))
		{
		User::Leave(KErrArgument);
		}

	LOGSMSPROT2("CPreallocatedFile::RemovePDUsL Start [sid=%d]", iEntryArray[aIndex].PreAllocatedStorageId());
	if (iEntryArray[aIndex].PreAllocatedStorageId()==KErrNotFound)
		{
		return;
		}

	TInt beginOfStorageIdSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
	//This will be used to read the storgae id which will indicate 
	//that corresponding container contains actual PDU
	TInt storageId=0;
	TPtr8 memPtr1((TUint8*) &storageId, sizeof(storageId), sizeof(storageId));

	//This will be used to cleanup the storage id.
	TInt cleanStorageId=0;
	TPtr8 memPtr2((TUint8*) &cleanStorageId, sizeof(cleanStorageId), sizeof(cleanStorageId));

	//This will be used to read the concatenated PDU index.
	TInt index=0;
	TPtr8 memPtr3((TUint8*) &index, sizeof(index), sizeof(index));

	//Number of PDUs stored in this pre-allocated file
	TInt noOfPDUs = iEntryArray[aIndex].Count();
	//Will indicate how many number of PDUs already read from pre-allocated file.
	TInt noOfPDUsRead = 0;
	// Can also get the number of pdu stored in pre-allocated file from count's variable.
	TInt noOfEntriesRemoved=0;
	for (TInt count=0; count<iMaxPDUSeg+1; count++)
		{
		TInt storageIdPos = beginOfStorageIdSection + count * sizeof(TInt);
		iFile.Read(storageIdPos, memPtr1);
		if (storageId == iEntryArray[aIndex].PreAllocatedStorageId())
			{//This means corresponding container contains the actual PDU
			noOfPDUsRead++;
			//Find the position of container where actaul PDU is stored.
			TInt dataPos = iBeginOfDataSection + ((count) * (KSizeOfGsmSmsSlotEntry + sizeof(TInt) + KSizeOfSmsGsmPDU));
			//Read index information
			iFile.Read(dataPos + KSizeOfGsmSmsSlotEntry, memPtr3);

			if ((index >= aStartPos) && (index <= aEndPos))
				{
				noOfEntriesRemoved++;
				//Remove the PDU entry.
				iFile.Write(storageIdPos, memPtr2);
				}

			if (noOfPDUsRead == noOfPDUs)
				{
				break;
				}
			}
		}

	/*
	There are 3 scenarios in this case:
	1. If all the entries need to be removed,
	then there is no need to store the entry in this permanent file.
	2. If few entries need to be removed,
	then externalize the remaining entries. Update count field also.
	3. If no entries need to be removed, then do nothing.
	*/
	TSmsPreAllocatedFileStoreReassemblyEntry entry;
	entry = iEntryArray[aIndex];

	if (entry.Count() == noOfEntriesRemoved)
		{
		entry.SetPreAllocatedStorageId(KErrNotFound);
		entry.SetCount(0);
		ChangeEntryL(aIndex, entry);
		}
	else if (noOfEntriesRemoved > 0)
		{
		entry.SetCount(entry.Count() - noOfEntriesRemoved);
		ChangeEntryL(aIndex, entry);
		}
	}

/*
It stores the forwarded message info in forwarded pdu bit-map.

@param aIndex index number of the forwarded message.
@param aStartPos starting pos of forwarded pdu.
@param aEndPos end pos of forwarded pdu.

@internalComponent
*/
void CPreallocatedFile::StoreForwardedPDUsInfoL(TInt aIndex, TInt aStartPos, TInt aEndPos)
	{
	TSmsPreAllocatedFileStoreReassemblyEntry entry;
	entry = iEntryArray[aIndex];

	TUint8 startBitMapIndex = (aStartPos-1)/8;
	TUint8 endBitMapIndex = (aEndPos-1)/8;
	TUint8 startBitPos = (aStartPos-1)%8;
	TUint8 endBitPos = (aEndPos-1)%8;

	if (startBitMapIndex == endBitMapIndex)
		{
		TUint8 bitMap;
		entry.GetBitMap(startBitMapIndex, bitMap);
		TUint8 tmpBitMap = (0 | 0xFF);
		tmpBitMap <<= ((8 - endBitPos) - 1);
		tmpBitMap >>= (8 - ((endBitPos - startBitPos)+1));
		tmpBitMap <<= startBitPos;
		bitMap |= tmpBitMap;
		entry.SetBitMap(startBitMapIndex, bitMap);
		}
	else
		{
		TUint8 bitMap;
		entry.GetBitMap(startBitMapIndex, bitMap);
		TUint8 tmpBitMap = (0 | 0xFF);
		tmpBitMap >>= (8 - ((7 - startBitPos)+1));
		tmpBitMap <<= startBitPos;
		bitMap |= tmpBitMap;
		entry.SetBitMap(startBitMapIndex, bitMap);

		entry.GetBitMap(endBitMapIndex, bitMap);
		tmpBitMap = (0 | 0xFF);
		tmpBitMap <<= ((8 - endBitPos) - 1);
		tmpBitMap >>= (8 - ((endBitPos - 0)+1));
		tmpBitMap <<= 0;
		bitMap |= tmpBitMap;
		entry.SetBitMap(endBitMapIndex, bitMap);
		}

	TInt noOfForwardedPDUs = (aEndPos - aStartPos) + 1;
	entry.SetNumberOfPDUsForwardToClient(entry.NumberOfPDUsForwardToClient()+noOfForwardedPDUs);

	ChangeEntryL(aIndex, entry);
	}

/*
It checks the validity of master header section by comparing checksum value.
If the checksum value does not match, then it copies data from duplicate header section to
master header section.
*/
void CPreallocatedFile::CheckDataL()
	{
	TUint32 checksum;
	TInt checksumPos = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection;

	TPtr8 memPtr((TUint8*) &checksum, sizeof(checksum), sizeof(checksum));
	iFile.Read(checksumPos, memPtr);

	if (ChecksumValue() != checksum)
		{
		CopyDuplicateToMasterL();
		}
	}

/*
It writes the checksum value & then copies header information from master section to duplicate section.
*/
void CPreallocatedFile::PutChecksumValueL()
	{
	TUint32 checksum = ChecksumValue();
	TInt checksumPos = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection;

	TPtr8 memPtr((TUint8*) &checksum, sizeof(checksum), sizeof(checksum));
	iFile.Write(checksumPos, memPtr);
	iFile.Flush();
	CopyMasterToDuplicateL();
	}

/*
It calculates & returns the checksum value of master header section.
*/
TUint32 CPreallocatedFile::ChecksumValue()
	{
	TInt sizeOfDataToBeChecksum = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection;

	TUint32 checksum(0);

	TUint32 tmpValue;
	TPtr8 memPtr((TUint8*) &tmpValue, sizeof(tmpValue), sizeof(tmpValue));

	for (TInt i = 0; i < sizeOfDataToBeChecksum/4 ; i++)
		{
		iFile.Read(KBeginOfMasterHeaderSection + (i*4), memPtr);
		checksum += tmpValue;
		}
	return 	checksum;
	}

/*
It copies header information from master copy to duplicate copy.
*/
void CPreallocatedFile::CopyMasterToDuplicateL()
	{
	TInt sizeOfDataToBeCopied = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection + 4;

	HBufC8* heapBuffer = HBufC8::NewL(sizeOfDataToBeCopied);
	CleanupStack::PushL(heapBuffer);
	TPtr8 memPtr(heapBuffer->Des());
	iFile.Read(KBeginOfMasterHeaderSection, memPtr);
	iFile.Write(iBeginOfDuplicateHeaderSection, memPtr);
	iFile.Flush();
	CleanupStack::PopAndDestroy(heapBuffer);
	}

/*
It copies header information from duplicate copy to master copy.
*/
void CPreallocatedFile::CopyDuplicateToMasterL()
	{
	TInt sizeOfDataToBeCopied = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection + 4;

	HBufC8* heapBuffer = HBufC8::NewL(sizeOfDataToBeCopied);
	CleanupStack::PushL(heapBuffer);
	TPtr8 memPtr(heapBuffer->Des());
	iFile.Read(iBeginOfDuplicateHeaderSection, memPtr);
	iFile.Write(KBeginOfMasterHeaderSection, memPtr);
	iFile.Flush();
	CleanupStack::PopAndDestroy(heapBuffer);
	}

/*
It returns the total number of PDUs stored in pre-allocated file.
*/
TInt CPreallocatedFile::NumberOfPDUStored()
	{
	TInt noOfPDUs = 0;
	for (TInt i = 0; i < iEntryArray.Count(); i++)
		{
		noOfPDUs += iEntryArray[i].Count();
		}
	return noOfPDUs;
	}

/**
 *  Sets the pre-allocated file store as in-transaction.
 *  
 *  The function checks the validity of the call and leaves KErrAccessDenied if
 *  invalid.
 *  @capability None
 */
void CPreallocatedFile::BeginTransactionL()
	{
	LOGSMSPROT3("CPreallocatedFile::BeginTransactionL [this=0x%08X iInTransaction=%d]", this, iInTransaction);

	if (iInTransaction)
		{
	    LOGGSMU1("WARNING CPreallocatedFile::BeginTransactionL leaving with KErrAccessDenied");
		User::Leave(KErrAccessDenied);
		}

	iInTransaction = ETrue;
	}

/**
 *  It commits the transaction.
 */
void CPreallocatedFile::CommitTransactionL()
	{
	LOGSMSPROT3("CPreallocatedFile::CommitTransactionL(): this=0x%08X iInTransaction=%d",
				this, iInTransaction);

	ExternalizeEntryArray();
	//Commit
	PutChecksumValueL();
	iInTransaction = EFalse;
	iReinstateEntryInfo.Reset();
	RemoveDeletedEntries();
	}

/*
It reverts the transaction.
*/
void CPreallocatedFile::Revert()
	{
	LOGSMSPROT3("CPreallocatedFile::Revert(): this=0x%08X, iInTransaction=%d",
    		 this, iInTransaction);

	ReinstateEntries();
	ExternalizeEntryArray();
	iInTransaction = EFalse;
	ReinstateDeletedEntries();
	}

/**
 *  It removes the deleted entries from entry arry.
 *	This function is called after commit.
 */
void CPreallocatedFile::RemoveDeletedEntries()
	{
	LOGSMSPROT1("CPreallocatedFile::RemoveDeletedEntries()");

	TInt count=iEntryArray.Count();
	while (count--)
		{
		TSmsPreAllocatedFileStoreReassemblyEntry& entry = iEntryArray[count];

		if (entry.IsDeleted())
			{
			iEntryArray.Delete(count);
			}
		else
			{
			entry.SetIsAdded(EFalse);
			}
		}
	} // CPreallocatedFile::RemoveDeletedEntries

/**
 *  It reinstate the deleted/added entries from entry arry.
 *	This function is called after revert operation.
 */
void CPreallocatedFile::ReinstateDeletedEntries()
	{
	LOGSMSPROT1("CPreallocatedFile::ReinstateDeletedEntries()");

	TInt count=iEntryArray.Count();
	while (count--)
		{
		TSmsPreAllocatedFileStoreReassemblyEntry& entry = iEntryArray[count];

		if (entry.IsAdded())
			{
			iEntryArray.Delete(count);
			}
		else
			{
			entry.SetIsDeleted(EFalse);
			}
		}
	} // CPreallocatedFile::ReinstateDeletedEntries

/**
 *  It reinstate the deleted/added entries from the container.
 *	This function is called at the time of revert operation.
 *	Unlike permanent store file which supports revert. pre-allocated
 *	file (raw data file) supports the revert mecahnism using this function.
 */
void CPreallocatedFile::ReinstateEntries()
	{
	LOGSMSPROT1("CPreallocatedFile::ReinstateEntries()");

	TInt containerId;
	TInt storageId;
	TPtr8 memPtr((TUint8*) &storageId, sizeof(storageId), sizeof(storageId));

	TInt count = iReinstateEntryInfo.Count();

	for (TInt i=0; i<count; i++)
		{
		if (iReinstateEntryInfo[i].iFlag == EEntryIsDeleted)
			{
			//Add the Pre-allocated storage info in the container.
			storageId = iReinstateEntryInfo[i].iPreAllocatedStorageId;
			containerId = iReinstateEntryInfo[i].iContainerId;
			TInt beginOfStorageIdentifierSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
			iFile.Write( beginOfStorageIdentifierSection + ((containerId - 1) * sizeof(TInt)), memPtr);
			}
		else	//EEntryIsAdded
			{
			//Delete the Pre-allocated storage info from the container.
			storageId = 0;
			containerId = iReinstateEntryInfo[i].iContainerId;
			TInt beginOfStorageIdentifierSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection;
			iFile.Write( beginOfStorageIdentifierSection + ((containerId - 1) * sizeof(TInt)), memPtr);
			}
		}
	iFile.Flush();
	iReinstateEntryInfo.Reset();
	}

/*
It returns the index of oldest message in pre-allocated file.
*/
TInt CPreallocatedFile::GetOldestMessageEntryIndex()
	{
	LOGSMSPROT1("CPreallocatedFile::GetOldestMessageEntryIndex()");

	TInt index = KErrNotFound;
	TTime time;
	time.UniversalTime();

	for (TInt index2=0; index2 < iEntryArray.Count(); index2++)
		{
		if ((iEntryArray[index2].Time() < time) && (iEntryArray[index2].Count()>0))
			{
			time = iEntryArray[index2].Time();
			index = index2;
			}
		}
	return index;
	}

/**
Static factory constructor. Uses two phase 
construction and leaves nothing on the CleanupStack.

@param aClass0ReassemblyStore reference to class 0 reasembly store.
@param aGuardTimeout guard time out in hours.

@return A pointer to the newly created CGuardTimer object.

@internalComponent
*/
CGuardTimer* CGuardTimer::NewL(CClass0SmsReassemblyStore& aClass0ReassemblyStore, TInt aGuardTimeout)
	{
	LOGSMSPROT1("CGuardTimer::NewL()");

	CGuardTimer* timer = new(ELeave) CGuardTimer(aClass0ReassemblyStore, aGuardTimeout);
	CleanupStack::PushL(timer);
	timer->ConstructL();
	CleanupStack::Pop();
	return timer;
	} // CGuardTimer::NewL

/**
 *  Constructor
 */
CGuardTimer::CGuardTimer(CClass0SmsReassemblyStore& aClass0ReassemblyStore, TInt aGuardTimeout)
	:CTimer(KSmsSessionPriority),
	iClass0ReassemblyStore(aClass0ReassemblyStore),
	iTimeoutInSecs(aGuardTimeout*3600)
	{
	CActiveScheduler::Add(this);
	} //CGuardTimer::CGuardTimer

/**
 *  Destructor
 */
CGuardTimer::~CGuardTimer()
	{
	Cancel();
	} // CGuardTimer::~CGuardTimer

/**
 *  Enable the Gurad Timer
 */
void CGuardTimer::EnableGuardTimer()
	{
	LOGSMSPROT1("CGuardTimer::EnableGuardTimer()");
	if (!IsActive())
		{
		TTime nextTimeOut;
		iClass0ReassemblyStore.GetNextGuardTimeout(nextTimeOut);
		At(nextTimeOut);
		}
	} //CGuardTimer::EnableGuardTimer

/**
 *  Disable the Gurad Timer
 */
void CGuardTimer::DisableGuardTimer()
	{
	Cancel();
	}

/**
 *  Timer completed
 */
void CGuardTimer::RunL()
	{
	LOGSMSPROT2("CGuardTimer::RunL [iStatus=%d]", iStatus.Int());
	iClass0ReassemblyStore.ProcessTimeoutMessageL();
	EnableGuardTimer();
	} // CGuardTimer::RunL

/**
Static factory constructor. Uses two phase 
construction and leaves nothing on the CleanupStack.

@param aFs  File Server handle.
@param aSmsComm reference to MSmsComm event handler.

@return A pointer to the newly created CClass0SmsReassemblyStore object.

@pre A connected file server session must be passed as parameter.
@post CClass0SmsReassemblyStore object is now fully initialised

@internalComponent
*/
CClass0SmsReassemblyStore* CClass0SmsReassemblyStore::NewL(RFs& aFs, MSmsComm& aSmsComm)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::NewL()");

	CClass0SmsReassemblyStore*  self = new (ELeave) CClass0SmsReassemblyStore(aFs, aSmsComm);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);

	return self;
	}

/**
 *  C'tor
 */
CClass0SmsReassemblyStore::CClass0SmsReassemblyStore(RFs& aFs, MSmsComm& aSmsComm)
	:CReassemblyStore(aFs), iSmsComm(aSmsComm), iPermanentFileStore(NULL), iPreallocatedFile(NULL),
	iGuardTimer(NULL), iInTransaction(EFalse), iDiskSpaceStatus(ESmsDiskSpaceAvailable)
	{
	}

/**
 *  D'tor
 */
CClass0SmsReassemblyStore::~CClass0SmsReassemblyStore()
	{
	delete iPermanentFileStore;
	delete iPreallocatedFile;
	delete iGuardTimer;
	}

/*
It constructs permanent and pre-allocated file to store the class 0 messages.
It creates a guard timer so that messages can be deleted if all the constituent
PDU of a message is not received within configured time-frame.
It also reserves 4KB disk space so that reserved space can be used to 
delete a stream from permanent file store in out-of-disk condition.

@internalComponent
*/
void CClass0SmsReassemblyStore::ConstructL()
	{
	//Reserve Drive Space
	User::LeaveIfError(iFs.ReserveDriveSpace(KStoreDrive, 4*1024));

	//Read Configuration file
	ReadConfigurableClass0SmsSettingsL(iMaxClass0Msg, iMaxPDUSeg, iGuardTimeOut);
	//Create Permanent store file
	TFileName permanentStoreFileName;
	CReassemblyStoreUtility::PrivatePath(iFs, permanentStoreFileName);
	permanentStoreFileName.Append(KClass0ReassemblyStoreName);
	iPermanentFileStore = CSmsPermanentFileStore::NewL(iFs, permanentStoreFileName, KClass0ReassemblyStoreUid);
	//Create Pre-allocated file
	TFileName preAllocatedFileName;
	CReassemblyStoreUtility::PrivatePath(iFs, preAllocatedFileName);
	preAllocatedFileName.Append(KPreAllocatedFileName);
	iPreallocatedFile = CPreallocatedFile::NewL(iFs, preAllocatedFileName, iMaxClass0Msg, iMaxPDUSeg);
	//Create Gurad Timer
	iGuardTimer = CGuardTimer::NewL(*this, iGuardTimeOut);
	}

/*
It reads class 0 re-assembly store configuration information from smswap.sms.esk file. 
It reads the following information: MaxClass0Messages, NumberOfPDUSegements, GuardTimeOut.
If any of the above element is missing it takes the default value.

@internalComponent
*/
void CClass0SmsReassemblyStore::ReadConfigurableClass0SmsSettingsL(TInt& aMaxClass0Msg, TInt& aMaxPDUSeg, TInt& aGuardTimeOut)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::ReadConfigurableClass0SmsSettingsL()");

	aMaxClass0Msg = KMaxNumberOfClass0MessagesInReassemblyStore;
	aMaxPDUSeg    = KNumberOfPDUSegmentsStoredInOODCondition;
	aGuardTimeOut = KGuardTimeOut;

	CESockIniData*  ini = NULL;
	TRAPD(ret, ini=CESockIniData::NewL(_L("smswap.sms.esk")));
	if(ret!=KErrNone)
		{
		LOGSMSPROT2("CESockIniData::NewL() returned=%d", ret);
		}
	else
		{
		CleanupStack::PushL(ini);

		TInt var(0);
		if(ini->FindVar(_L("ReassemblyStore"),_L("MaxClass0Messages"),var))
			{
			if (var > 0)
				{
				LOGSMSPROT2("MaxClass0Messages [%d]", var);
				aMaxClass0Msg = var;
				}
			}

		if(ini->FindVar(_L("ReassemblyStore"),_L("NumberOfPDUSegements"),var))
			{
			if (var > 0)
				{
				LOGSMSPROT2("MaxClass0Messages [%d]", var);
				aMaxPDUSeg = var;
				}
			}

		if(ini->FindVar(_L("ReassemblyStore"),_L("GuardTimeOut"),var))
			{
			if (var > 0)
				{
				LOGSMSPROT2("MaxClass0Messages [%d]", var);
				aGuardTimeOut = var;
				}
			}

		CleanupStack::PopAndDestroy(ini);
		}

	LOGSMSPROT4("CClass0SmsReassemblyStore::ReadConfigurableClass0SmsSettingsL(): aMaxClass0Msg=%d, aMaxPDUSeg=%d, aGuardTimeOut=%d",
			    aMaxClass0Msg, aMaxPDUSeg, aGuardTimeOut);
	}

/**
It opens the class 0 re-assembly store.
Then it populates the entry information (all the messages stored in re-assembly store).

@internalComponent
*/
void CClass0SmsReassemblyStore::OpenStoreL()
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::OpenStoreL()");
	TFileName pathName;
	CReassemblyStoreUtility::PrivatePath(iFs, pathName);
	//Create the directory if it is not created.
	iFs.MkDirAll(pathName);
	// If any one file becomes corrupt or does not exist then we have to create both files.
	if (iPreallocatedFile->IsFileOK() && iPermanentFileStore->IsFileOK())
		{
		iPreallocatedFile->OpenL();
		iPermanentFileStore->OpenL();
		}
	else
		{
		iPreallocatedFile->CreateL();
		iPermanentFileStore->CreateL();
		}
	PopulateEntryArrayL(iEntryArray);
	iGuardTimer->EnableGuardTimer();
	}

/**
It closes the class 0 re-assembly store.

@internalComponent
*/
void CClass0SmsReassemblyStore::Close()
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::CloseStore()");
	iGuardTimer->DisableGuardTimer();
	iEntryArray.Reset();
	iPreallocatedFile->Close();
	iPermanentFileStore->Close();
	}

/**
It populates the entry information stored in class 0 reassembly store.

@param aEntryArray reference to entry array.

@internalComponent
*/
void CClass0SmsReassemblyStore::PopulateEntryArrayL(CArrayFix<TReassemblyEntry>& aEntryArray)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::PopulateEntryArrayL()");
	aEntryArray.Reset();
	//Populate Entries from Pre-allocated file.
	for (TInt count = 0; count < iPreallocatedFile->Entries().Count(); count++)
		{
		TReassemblyEntry entry;
		entry.SetReference(iPreallocatedFile->Entries()[count].Reference());
		entry.SetTotal(iPreallocatedFile->Entries()[count].Total());
		entry.SetCount(iPreallocatedFile->Entries()[count].Count()+iPreallocatedFile->Entries()[count].NumberOfPDUsForwardToClient());
		entry.SetLogServerId(iPreallocatedFile->Entries()[count].LogServerId());
 		
 		// Check that descriptor2 (sms address )is not corrupted
 		TPtrC ptr = iPreallocatedFile->Entries()[count].Description2();
		
		if (ptr.Length() <= CSmsAddress::KSmsAddressMaxAddressLength)
			{
 			entry.SetDescription2(iPreallocatedFile->Entries()[count].Description2());
 			}
	
		entry.SetPduType(iPreallocatedFile->Entries()[count].PduType());
		entry.SetStorage(iPreallocatedFile->Entries()[count].Storage());
		entry.SetTime(iPreallocatedFile->Entries()[count].Time());
		entry.SetPassedToClient(iPreallocatedFile->Entries()[count].PassedToClient());
		aEntryArray.AppendL(entry);
		}

	/*
	Then populate the entries from permanent store file. It is needed
	because permanent store file might contain few mesages.
	But before populating the entry information it is needed to cleanup 
	the entries in permanent store file. It is needed to ensure that permanent file
	clean itself with pre-allocated file. There migth be a scenario where 
	user has deleted a message but the corresponding message has not been 
	deleted from permanent store file due to out-of-disk condition. 
	And also at the time of forwarding an incomplete message a forwarded 
	message has not been deleted due to above reason. But the entry/PDU is 
	invalid because it is no more in the pre-allocated file which 
	contains master header info.
	*/
	TInt ret = CleanReassemblyEntries();

	if (ret == KErrNone)
		{
		/*
		In this case permanent store file contains correct information.
		So populate Entry information from Permanent store file.
		In this case only count information needs to be updated.
		*/
		TInt permanentFileStoreIndex;
		for (TInt i=0; i<aEntryArray.Count(); i++)
			{
			permanentFileStoreIndex=KErrNotFound;
			iPermanentFileStore->MatchEntryToExistingMessage(aEntryArray[i], permanentFileStoreIndex);
			if (permanentFileStoreIndex!=KErrNotFound)
				{
				aEntryArray[i].SetCount(aEntryArray[i].Count() + iPermanentFileStore->Entries()[permanentFileStoreIndex].Count());
				if (aEntryArray[i].Count() > aEntryArray[i].Total())
					{
					aEntryArray[i].SetCount(aEntryArray[i].Total());
					}
				}
			}
		}
	else if (ret == KErrDiskFull)
		{
		LOGSMSPROT1("CleanReassemblyEntries() returns KErrDiskFull");
		/*
		In this case permanent store file contains incorrect information.
		For example forwarded message might be still stored in this store.
		Because it has not been deleted due to out-of-disk condition.
		So be careful at the time of updating the count information
		about the message.
		*/
		TInt permanentFileStoreIndex;
		for (TInt i=0; i<aEntryArray.Count(); i++)
			{
			permanentFileStoreIndex=KErrNotFound;
			iPermanentFileStore->MatchEntryToExistingMessage(aEntryArray[i], permanentFileStoreIndex);
			if (permanentFileStoreIndex!=KErrNotFound)
				{
				TSmsPreAllocatedFileStoreReassemblyEntry entry = iPreallocatedFile->Entries()[i];
				if (entry.NumberOfPDUsForwardToClient() > 0)
					{
					//Internalize the entries.
					CSmsBuffer*  buffer = CSmsBuffer::NewL();
					CSmsMessage*  smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer);
					CleanupStack::PushL(smsMessage);

					CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
					CleanupStack::PushL(indexArray);
					CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
					CleanupStack::PushL(smsArray);
					//Internalize to check whether removed PDUs still stored in Permanent store file.
					iPermanentFileStore->InternalizeEntryL(permanentFileStoreIndex, *smsMessage, *indexArray, *smsArray);
					TInt noOfForwardedEntries=0;
					for (TInt j=0; j<indexArray->Count() ;j++)
						{
						TUint8 bitMapIndex = (indexArray->At(j)-1)/8;
						TUint8 bitPos = (indexArray->At(j)-1)%8;
						TUint8 bitMap;
						entry.GetBitMap(bitMapIndex, bitMap);
						TUint8 tmpBitMap = 1;
						tmpBitMap <<= bitPos;
						if (tmpBitMap == (bitMap & tmpBitMap))
							{
							noOfForwardedEntries++;
							}
						}
					TInt count = iPermanentFileStore->Entries()[permanentFileStoreIndex].Count() - noOfForwardedEntries;
					aEntryArray[i].SetCount(aEntryArray[i].Count() + count);

					CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray
					}
				else
					{
					aEntryArray[i].SetCount(aEntryArray[i].Count() + iPermanentFileStore->Entries()[permanentFileStoreIndex].Count());
					}
				}
			}
		}
	else
		{
		User::Leave(ret);
		}
	}

/**
It sets the disk space status.
If disk space is full, then class 0 re-assembly store stores the incoming message in
pre-allocated file. Otherwise it stores the message in permanent store file.
*/
void CClass0SmsReassemblyStore::SetDiskSpaceState(TSmsDiskSpaceMonitorStatus aDiskSpaceStatus)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::SetDiskSpaceState()");
	iDiskSpaceStatus = aDiskSpaceStatus;
	}

/*
It adds the new message in class 0 reassembly store.
It first tries to add the message in permanent store file. If it is
successful, then it adds the entry information in pre-allocated file.
Because pre-allocated file contains master header entry information.
Otherwise (if disk space is full), it adds the message in pre-allocated file.

@param aSmsMessage reference to sms message to be added.
@param aGsmSms reference to GsmSms object to be added.

@internalComponent
*/
void CClass0SmsReassemblyStore::AddNewMessageL(CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::AddNewMessageL");

	// Add entry in permanent store file
	TInt index;
	TInt ret = KErrNone;

	if (iDiskSpaceStatus == ESmsDiskSpaceFull)
		{
		ret = KErrDiskFull;
		}
	else
		{
		TRAP(ret, iPermanentFileStore->AddNewMessageL(index, aSmsMessage, aGsmSms));
		}

	if (ret == KErrNone)
		{
		// Add a new entry in pre-allocated file
		TSmsPreAllocatedFileStoreReassemblyEntry tmpEntry;
		//Fill-up entry information...
		CReassemblyStoreUtility::PopulateEntry(tmpEntry, aSmsMessage, 1);

		//The properties below need to be set only once when a PDU of a new message arrives.
		tmpEntry.SetStatus((RMobileSmsStore::TMobileSmsStoreStatus)aSmsMessage.Status());
		tmpEntry.SetUTCOffset(aSmsMessage.UTCOffset());
		tmpEntry.SetDecodedOnSIM(aSmsMessage.DecodedOnSim());
		tmpEntry.SetForwardToClient(aSmsMessage.ForwardToClient());
		//Set KErrNotFound because message corresponding to 
		//this entry is not in this pr-allocated file.
		tmpEntry.SetPreAllocatedStorageId(KErrNotFound);
		//Has to set 0 because PDU is stored in Permanent store file instead of pre-allocated file.
		tmpEntry.SetCount(0);

		iPreallocatedFile->AddEntryL(tmpEntry);
		}
	else if (ret == KErrDiskFull)
		{
		// Add the new message in pre-allocated file
		iPreallocatedFile->AddNewMessageL(index, aSmsMessage, aGsmSms);
		}
	else
		{
		User::Leave(ret);
		}
	}

/*
It updates the existing message in class 0 re-assembly store.

@param aSmsMessage reference to sms message to be updated.
@param aGsmSms reference to GsmSms object to be added.
@param aDuplicateMsgRef boolean value (output) indicates whether the added PDU is a duplicate or not.
@param aDuplicateSlot boolean value (output) indicates whether the added PDU is a duplicate enumerated PDU.

@internalComponent
*/
void CClass0SmsReassemblyStore::UpdateExistingMessageL(CSmsMessage& aSmsMessage, const TGsmSms& aGsmSms, TBool& aDuplicateMsgRef, TBool& aDuplicateSlot)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::UpdateExistingMessageL()");

	aDuplicateMsgRef = EFalse;
	aDuplicateSlot   = EFalse;

	CSmsBuffer*  buffer = CSmsBuffer::NewL();
	CSmsMessage*  smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer);
	CleanupStack::PushL(smsMessage);

	CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
	CleanupStack::PushL(indexArray);
	CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
	CleanupStack::PushL(smsArray);

	//Internalize stored message in permanent store file
	TReassemblyEntry entry;
	CReassemblyStoreUtility::PopulateEntry(entry, aSmsMessage, 1);
	TInt permanentStoreIndex;
	iPermanentFileStore->MatchEntryToExistingMessage(entry, permanentStoreIndex);
	if (permanentStoreIndex!=KErrNotFound)
		{
		iPermanentFileStore->InternalizeEntryL(permanentStoreIndex, *smsMessage, *indexArray, *smsArray);
		}

	//Internalize stored message in pre-allocated file
	TInt preAllocatedFileIndex;
	iPreallocatedFile->MatchEntryToExistingMessage(entry, preAllocatedFileIndex);
	if (preAllocatedFileIndex!=KErrNotFound)
		{
		iPreallocatedFile->InternalizeEntryL(preAllocatedFileIndex, *smsMessage, *indexArray, *smsArray);
		}
	else
		{
		//This condition should never arise
		User::Leave(KErrNotFound);
		}

	//
	// Check if this is a duplicated enumerated PDU (e.g. on the SIM or phone memory)
	// or a duplicated PDU (e.g. in the Reassembly Store)...
	//
	TInt  concatPDUIndex = aSmsMessage.SmsPDU().ConcatenatedMessagePDUIndex();

	if (smsMessage->Storage() == CSmsMessage::ESmsSIMStorage  ||
		smsMessage->Storage() == CSmsMessage::ESmsCombinedStorage)
		{
		//
		// In most cases this PDU is being enumerated, but not always. It is
		// possible for the PDU to be stored on the SIM first before it is
		// received.
		//
		const TGsmSmsSlotEntry&  newSlot = aSmsMessage.iSlotArray[0];
		TInt  slotArrayCount = smsMessage->iSlotArray.Count();

		for (TInt slotNum = 0;  slotNum < slotArrayCount;  slotNum++ )
			{
			const TGsmSmsSlotEntry&  slot = smsMessage->iSlotArray[slotNum];

			if (slot.iIndex == newSlot.iIndex  && slot.iStore == newSlot.iStore)
				{
				LOGSMSPROT1("CSmsReassemblyStore::UpdateExistingMessageL(): Duplicate enumerated PDU.");

				// It is a duplicate that was already stored on the SIM...
				aDuplicateSlot = ETrue;
				break;
				}
			}
		}

	TInt  indexArrayCount = indexArray->Count();

	for (TInt index = 0;  index < indexArrayCount;  index++ )
		{
		if (indexArray->At(index) == concatPDUIndex)
			{
			LOGSMSPROT1("CSmsReassemblyStore::UpdateExistingMessageL(): Duplicate concatenated PDU.");

			// The PDU is already stored in the reassembly store.
			aDuplicateMsgRef = ETrue;
			break;
			}
		}

	//Check whether this PDU is part of forwarded PDU.
	TSmsPreAllocatedFileStoreReassemblyEntry preAllocatedFileEntry = iPreallocatedFile->Entries()[preAllocatedFileIndex];
	if (preAllocatedFileEntry.NumberOfPDUsForwardToClient() > 0)
		{
		TUint8 bitMapIndex = (concatPDUIndex-1)/8;
		TUint8 bitPos = (concatPDUIndex-1)%8;
		TUint8 bitMap;
		preAllocatedFileEntry.GetBitMap(bitMapIndex, bitMap);
		TUint8 tmpBitMap = 1;
		tmpBitMap <<= bitPos;
		if (tmpBitMap == (bitMap & tmpBitMap))
			{
			aDuplicateMsgRef = ETrue;
			}		
		}

	if (aDuplicateMsgRef  ||  aDuplicateSlot)
		{
		CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, smsArray, indexArray
		return;
		}

	//
	// If the PDU is stored then add the slot information...
	//
	if (aSmsMessage.Storage() == CSmsMessage::ESmsSIMStorage  ||
		aSmsMessage.Storage() == CSmsMessage::ESmsCombinedStorage)
		{
		smsMessage->AddSlotL(aSmsMessage.iSlotArray[0]);
		}

	//
	// If the PDU is Unsent or Unread, then store that information...
	//
	NMobileSmsStore::TMobileSmsStoreStatus  status = aSmsMessage.Status();

	if (status == NMobileSmsStore::EStoredMessageUnsent  ||
		status == NMobileSmsStore::EStoredMessageUnread)
		{
		smsMessage->SetStatus(status);
		}

	/*
	Below logic is written in keeping view of different scenarios that might occur here:
	(1) Previous PDUs are in Permanent store file, this PDU arrives in out-of-disk condition (so will be stored in Pre-allocated file).
	(2) Previous PDUs are in Pre-allocated file (means PDU arrives in out-of-disk condition), this PDU arrives in normal condition.
	(3) Previous PDUs are in Permanent store file, this PDU also arrives in normal condition.
	(4) Previous PDUs are in Pre-allocated file (means PDU arrives in out-of-disk condition), this PDU also arrives in out-of-disk condition.
	*/

	//Always first try to store the message in permanent store file.
	//In case of out-of-disk condition store the message in pre-allocated file
	TInt ret = KErrNone;
	if (iDiskSpaceStatus == ESmsDiskSpaceFull)
		{
		ret = KErrDiskFull;
		}
	else
		{
		if (permanentStoreIndex==KErrNotFound)
			{
			// See condition (2)
			TInt tmpIndex;
			TRAP(ret, iPermanentFileStore->AddNewMessageL(tmpIndex, aSmsMessage, aGsmSms));
			}
		else
			{
			CSmsBuffer*  tmpBuffer = CSmsBuffer::NewL();
			CSmsMessage*  tmpSmsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, tmpBuffer);
			CleanupStack::PushL(tmpSmsMessage);

			indexArray->Reset();
			smsArray->Reset();
			iPermanentFileStore->InternalizeEntryL(permanentStoreIndex, *tmpSmsMessage, *indexArray, *smsArray);
			/*
			Check & remove those PDU from permanent store file which has already been 
			forwarded to client. Forwarded PDU might still remain in permanent store 
			file if it is deleted at the time of out-of-disk condition.
			*/
			if (preAllocatedFileEntry.NumberOfPDUsForwardToClient()>0)
				{
				for (TInt j=indexArray->Count(); j>0 ;j--)
					{
					TUint8 bitMapIndex = (indexArray->At(j-1)-1)/8;
					TUint8 bitPos = (indexArray->At(j-1)-1)%8;
					TUint8 bitMap;
					preAllocatedFileEntry.GetBitMap(bitMapIndex, bitMap);
					TUint8 tmpBitMap = 1;
					tmpBitMap <<= bitPos;
					if (tmpBitMap == (bitMap & tmpBitMap))
						{
						indexArray->Delete(j-1);
						smsArray->Delete(j-1);
						}
					}
				}

			indexArray->AppendL(concatPDUIndex);
			smsArray->AppendL(aGsmSms);

			TRAP(ret, iPermanentFileStore->UpdateExistingMessageL(permanentStoreIndex, *smsMessage, *indexArray, *smsArray));

			CleanupStack::PopAndDestroy(1); //tmpSmsMessage
			}
		}

	if (ret == KErrDiskFull)
		{
		iPreallocatedFile->UpdateExistingMessageL(preAllocatedFileIndex, aSmsMessage, concatPDUIndex, aGsmSms);
		}
	else if (ret!=KErrNone)
		{
		User::Leave(ret);
		}

	CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray
	}

/*
It retrieves class 0 message from re-assembly store.
The message which match the passed entry information is returned from this function.

@param aEntry reference to entry information.
@param aSmsMessage reference to returned sms message.

@internalComponent
*/
void CClass0SmsReassemblyStore::RetrieveMessageL(const TReassemblyEntry& aEntry, CSmsMessage& aSmsMessage)
	{
	CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
	CleanupStack::PushL(indexArray);
	CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
	CleanupStack::PushL(smsArray);

	GetSmsEntriesL(aEntry, aSmsMessage, *indexArray, *smsArray);

	//There is no need to decode a single segment message
	if (aEntry.Total()>1)
		{
		if (smsArray->Count() == aEntry.Total())
			{
			aSmsMessage.DecodeMessagePDUsL(*smsArray);
			}
		}
	CleanupStack::PopAndDestroy(2, indexArray); //smsArray, indexArray
	}

/*
It deletes the entry from re-assembly store.

@param aEntry reference to entry information.

@internalComponent
*/
void CClass0SmsReassemblyStore::DeleteEntryL(const TReassemblyEntry& aEntry)
	{
	TInt index;
	iPreallocatedFile->MatchEntryToExistingMessage(aEntry, index);
	if (index == KErrNotFound)
		{
		//This condition should never arise
		User::Leave(KErrNotFound);
		}
	iPreallocatedFile->DeleteEntryL(index);
	iPermanentFileStore->MatchEntryToExistingMessage(aEntry, index);
	if (index != KErrNotFound)
		{
		TRAP_IGNORE(iPermanentFileStore->DeleteEntryL(index));
		}
	}

/*
It updates the log server id of the passed entry (message).

@param aEntry reference to entry information.
@param aLogId log id of the message to be updated.

@internalComponent
*/
void CClass0SmsReassemblyStore::UpdateLogServerIdL(const TReassemblyEntry& aEntry, TLogId aLogServerId)
	{
	TInt index;
	iPreallocatedFile->MatchEntryToExistingMessage(aEntry, index);
	if (index == KErrNotFound)
		{
		//This condition should never arise
		User::Leave(KErrNotFound);
		}
	iPreallocatedFile->UpdateLogServerIdL(index, aLogServerId);
	}

/*
It sets the message passed to client or not. The message, which match the 
passed entry information, its passed to client value is set.

@param aEntry reference to entry information.
@param aBool boolean value indicating whether message is passed or not.

@internalComponent
*/
void CClass0SmsReassemblyStore::SetPassedToClientL(const TReassemblyEntry& aEntry, TBool aBool)
	{
	TInt index;
	iPreallocatedFile->MatchEntryToExistingMessage(aEntry, index);
	if (index == KErrNotFound)
		{
		//This condition should never arise
		User::Leave(KErrNotFound);
		}
	iPreallocatedFile->SetPassedToClientL(index, aBool);
	}

/**
It forwards the complete class 0 messages to client.
It checks whether the received object is complete or not.
If it is complete, then it forwards the complete message to client.
Otherwise it forms the message & forward the message to client.
In this it might be possible to forward multiple incomplete message.

@param aSmsComm  a reference to aSmsComm object which implemented the events.

@param aSmsMessage	a reference to sms message object. This sms message must be class 0 messages.

@param aOriginalSmsAddr pointer to the address of the sender of a previously sent

@param aOriginalSmsMessage pointer to a message previously sent matched to the received 
							one (e.g. status report).	Null if not matched.

@param aDes user data for the deliver report acknowledging this message to the SC.
			Filled in by the observer.

@internalComponent
*/
void CClass0SmsReassemblyStore::ForwardCompleteClass0SmsMessagesL(MSmsComm& aSmsComm, const CSmsMessage& aSmsMessage,const TSmsAddr* aOriginalSmsAddr,const CSmsMessage* aOriginalSmsMessage,TDes& aDes)
	{
	TBool passedToClient=ETrue;

	if (aSmsMessage.IsComplete())
		{
		//Message is complete so forward it.
		TInt ret = aSmsComm.ProcessMessageL(aSmsMessage, aOriginalSmsAddr, aOriginalSmsMessage, aDes);
		if (ret!=KErrNone)
			{
			passedToClient = EFalse;
			}
		}
	else
		{
		/*
		Message is not complete, but re-assembly store contains other constituent PDU
		of the message which makes the message complete.

		In case of class 0 messages, it is possible to forward the incomplete messages. So after forwarding
		the incomplete message, if we receive other constituent PDU of that message then in that case we 
		might receive all the constituent PDU of that message but aSmsMesssage will contain partial complete message.
		*/
		TReassemblyEntry entry;
		CReassemblyStoreUtility::PopulateEntry(entry, aSmsMessage, 1);

		CSmsBuffer* buffer = CSmsBuffer::NewL();
		CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer );
		CleanupStack::PushL( smsMessage );

		CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
		CleanupStack::PushL(indexArray);
		CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
		CleanupStack::PushL(smsArray);

		GetSmsEntriesL(entry, *smsMessage, *indexArray, *smsArray);
		SortPDUsL(*indexArray, *smsArray);

		CArrayFixFlat<TGsmSms>*  tmpSmsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
		CleanupStack::PushL(tmpSmsArray);

		//Form the sequence of incomplete message & forward it to client.
		TInt startSeqIndex(0), endSeqIndex(0);
		TInt i;
		do
			{
			i = startSeqIndex;
			for (; i < indexArray->Count() - 1; i++)
				{
				if (indexArray->At(i) + 1 != indexArray->At(i+1))
					{
					endSeqIndex = i;
					break;
					}
				}

			if (i == (indexArray->Count() - 1))
				{
				endSeqIndex = i;
				}

			tmpSmsArray->Reset();
			for (TInt j = startSeqIndex; j <= endSeqIndex; j++)
				{
				tmpSmsArray->AppendL(smsArray->At(j));
				}

			if (tmpSmsArray->Count() == entry.Total())
				{
				smsMessage->DecodeMessagePDUsL(*tmpSmsArray);
				}
			else
				{
				//Build the partial complete message.
				if (endSeqIndex == (indexArray->Count() - 1))
					{
					smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue);
					}
				else
					{
					smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse);
					}
				}

			//Forward the partial complete message to client.
			TInt ret=KErrNone;
			TRAP(ret, aSmsComm.ProcessMessageL(*smsMessage, aOriginalSmsAddr, aOriginalSmsMessage, aDes));
			if (ret != KErrNone)
				{
				passedToClient = EFalse;
				}
			startSeqIndex = endSeqIndex + 1;
			endSeqIndex = startSeqIndex;
			}
		while (startSeqIndex < indexArray->Count());

		CleanupStack::PopAndDestroy(4);	//tmpSmsArray, indexArray, smsArray, smsMessage
		}

	if(passedToClient)
		{
		SetMessagePassedToClientL(aSmsMessage);
		}

	//If the latest segement exceeds the limitation, then we can't wait till ack.
	//So call ProcessMessageIfExceedLimitationL() function & delete the oldest message.
	if (IsExceedLimitation())
		{
		ProcessMessageIfExceedLimitationL(aSmsComm);
		}
	}

/**
It frees the space by forwarding the class 0 message if class 0 re-assembly store 
exceeds limitation (max class 0 message, max reserved pdu segment).

@param aSmsComm  a reference to aSmsComm object which implemented the events.

@internalComponent
*/
void CClass0SmsReassemblyStore::ProcessMessageIfExceedLimitationL(MSmsComm& aSmsComm)
	{
	//Add Comment
	TInt class0MsgCount = iEntryArray.Count();
	TInt noOfMsgStoredInPreAllocatedFile = iPreallocatedFile->NumberOfPDUStored();
	if ((class0MsgCount > iMaxClass0Msg)
			|| (noOfMsgStoredInPreAllocatedFile >= iMaxPDUSeg))
		{
		CSmsBuffer* buffer = CSmsBuffer::NewL();
		CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer );
		CleanupStack::PushL( smsMessage );

		CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
		CleanupStack::PushL(indexArray);
		CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
		CleanupStack::PushL(smsArray);

		//Add Comment
		TInt index;
		if (class0MsgCount > iMaxClass0Msg)
			{
			index = GetIndexOfOldestMessage();
			}
		else
			{
			index = GetIndexOfOldestMessageFromReservedFileL();
			}

		TReassemblyEntry entry = iEntryArray[index];

		GetSmsEntriesL(entry, *smsMessage, *indexArray, *smsArray);

		SortPDUsL(*indexArray, *smsArray);

		CArrayFixFlat<TGsmSms>*  tmpSmsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
		CleanupStack::PushL(tmpSmsArray);

		//Form the message & forward it to client.
		TInt startSeqIndex(0), endSeqIndex(0);
		TInt i;
		do
			{
			i = startSeqIndex;
			for (; i < indexArray->Count() - 1; i++)
				{
				if (indexArray->At(i) + 1 != indexArray->At(i+1))
					{
					endSeqIndex = i;
					break;
					}
				}
			if (i == (indexArray->Count() - 1))
				{
				endSeqIndex = i;
				}
			tmpSmsArray->Reset();

			for (TInt j = startSeqIndex; j <= endSeqIndex; j++)
				{
				tmpSmsArray->AppendL(smsArray->At(j));
				}

			if (entry.IsComplete())
				{
				if (tmpSmsArray->Count() == entry.Total())
					{
					smsMessage->DecodeMessagePDUsL(*tmpSmsArray);
					}
				else
					{
					//Build the partial complete message.
					if (endSeqIndex == (indexArray->Count() - 1))
						{
						smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue);
						}
					else
						{
						smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse);
						}
					}
				}
			else
				{
				//Build the partial complete message.
				smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse);
				}
			//Forward the partial complete message to client.
			TBuf16<CSmsPDUProcessor::ESmsMaxDeliverReportBufferSize> buffer;
			TRAP_IGNORE(aSmsComm.ProcessMessageL(*smsMessage, NULL, NULL, buffer));

			/*
			Can't do much. So free the reserved space & also stored the information 
			related to forwarded message. If the message is complete then delete
			the complete message (see the condition after the while loop)rather 
			than freeing the memory. No need to store the complete messages forwaded 
			info.
			*/
			if ((noOfMsgStoredInPreAllocatedFile > iMaxPDUSeg) && (!entry.IsComplete()))
				{
				SetIncompleteMessageForwardedToClientL(*smsMessage);
				}
			startSeqIndex = endSeqIndex + 1;
			endSeqIndex = startSeqIndex;
			}
		while (startSeqIndex < indexArray->Count());
		//Can't do much. So removed the oldest message.
		if ((class0MsgCount > iMaxClass0Msg) ||
			((noOfMsgStoredInPreAllocatedFile > iMaxPDUSeg) && (entry.IsComplete())))
			{
			DeleteMessageL(*smsMessage, EFalse);
			}
		CleanupStack::PopAndDestroy(4);	//tmpSmsArray, indexArray, smsArray, smsMessage
		}
	}

/**
Guard timer calls this function when timer expires.
It goes through all the messages stored in re-assembly store & forwards those message
whose time has expired.
It is also called when a new observer is added or a PDU has been received and successfully 
processed. It is also called from ProcessCompleteClass0SmsMessagesL.

@internalComponent
*/
void CClass0SmsReassemblyStore::ProcessTimeoutMessageL()
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::ProcessTimeoutMessageL()");
	TBool passedToClient=ETrue;
	TInt count=iEntryArray.Count();

	CSmsBuffer* buffer = CSmsBuffer::NewL();
	CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer );
	CleanupStack::PushL( smsMessage );

	CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(KFlatArrayGranularity);
	CleanupStack::PushL(indexArray);
	CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
	CleanupStack::PushL(smsArray);

	CArrayFixFlat<TGsmSms>*  tmpSmsArray = new(ELeave) CArrayFixFlat<TGsmSms>(KFlatArrayGranularity);
	CleanupStack::PushL(tmpSmsArray);

	TTime time;
	time.UniversalTime();

	TTimeIntervalSeconds timeOutInSecs = iGuardTimeOut*3600;

	for (TInt k=count-1; k>=0; k--)
		{
		if (!iEntryArray[k].PassedToClient())
			{
			TTimeIntervalSeconds diffInSecs;
			time.SecondsFrom(iEntryArray[k].Time(), diffInSecs);
			indexArray->Reset();
			smsArray->Reset();
			if (diffInSecs >= timeOutInSecs)
				{
				GetSmsEntriesL(iEntryArray[k], *smsMessage, *indexArray, *smsArray);
				SortPDUsL(*indexArray, *smsArray);

				//Form the message & forward it to client.
				TInt startSeqIndex(0), endSeqIndex(0);
				TInt i;
				do
					{
					i = startSeqIndex;
					for (; i < indexArray->Count() - 1; i++)
						{
						if (indexArray->At(i) + 1 != indexArray->At(i+1))
							{
							endSeqIndex = i;
							break;
							}
						}
					if (i == (indexArray->Count() - 1))
						{
						endSeqIndex = i;
						}
					tmpSmsArray->Reset();
					for (TInt j = startSeqIndex; j <= endSeqIndex; j++)
						{
						tmpSmsArray->AppendL(smsArray->At(j));
						}

					if (iEntryArray[k].IsComplete())
						{
						if (tmpSmsArray->Count() == iEntryArray[k].Total())
							{
							smsMessage->DecodeMessagePDUsL(*tmpSmsArray);
							}
						else
							{
							//Build the partial complete message.
							if (endSeqIndex == (indexArray->Count() - 1))
								{
								smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue);
								}
							else
								{
								smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse);
								}
							}				
						}
					else
						{
						//Build the partial complete message.
						if (endSeqIndex == (indexArray->Count() - 1))
							{
							smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue);
							}
						else
							{
							smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse);
							}
						}

					//Forward the partial complete message to client.
					TInt ret=KErrNone;
					TBuf16<CSmsPDUProcessor::ESmsMaxDeliverReportBufferSize> buffer;
					ret = iSmsComm.ProcessMessageL(*smsMessage, NULL, NULL, buffer);
					if (ret != KErrNone)
						{
						passedToClient = EFalse;
						}

					startSeqIndex = endSeqIndex + 1;
					endSeqIndex = startSeqIndex;
					}
				while (startSeqIndex < indexArray->Count());

				if(passedToClient)
					{
					SetMessagePassedToClientL(*smsMessage);
					}
				}	//diffInSecs >= timeOutInSecs
			}	//!iEntryArray[k].PassedToClient()
		}	//For k loop
	CleanupStack::PopAndDestroy(4);	//tmpSmsArray, indexArray, smsArray, smsMessage
	}

/**
It sets the bit-map of forwarded incomplete message to client.
It frees up the memory by removing the forwarded PDUs.

@param aSmsMessage	a reference to forwaded incomplete class 0 sms message object.

@internalComponent
*/
void CClass0SmsReassemblyStore::SetIncompleteMessageForwardedToClientL(const CSmsMessage& aSmsMessage)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::SetIncompleteMessageForwardedToClientL()");
	TInt index = KErrNotFound;

	if (aSmsMessage.IsComplete())
		{
		LOGSMSPROT1("This function must be called when message is incomplete");
		User::Leave(KErrArgument);
		}

	if (FindMessageL(aSmsMessage, EFalse, index))
		{
		CIncompleteClass0MessageInfo& incompleteClass0MsgInfo = (CIncompleteClass0MessageInfo &) aSmsMessage.GetOperationsForNonIEL(ESmsIncompleteClass0MessageParameter);
		// Retrieve incomplete class 0 message information & process
		TInt startPos, endPos;
		TBool isLastMessage;
		incompleteClass0MsgInfo.GetIncompleteMessageInfoL(startPos, endPos, isLastMessage);

		const TReassemblyEntry&  entry = iEntryArray[index];

		//Remove the forwarded PDU entries from pre-allocated file
		TInt preAllocatedFileIndex=KErrNotFound;
		iPreallocatedFile->MatchEntryToExistingMessage(entry, preAllocatedFileIndex);
		if (preAllocatedFileIndex!=KErrNotFound)
			{
			BeginTransactionLC();
			iPreallocatedFile->RemovePDUsL(preAllocatedFileIndex, startPos, endPos);
			iPreallocatedFile->StoreForwardedPDUsInfoL(preAllocatedFileIndex, startPos, endPos);
			CommitTransactionL();
			}
		else
			{
			//This situation should never arise
			User::Leave(KErrNotFound);
			}

		//Remove the forwarded PDU entries from permanent store file
		TInt permanentStoreIndex=KErrNotFound;
		iPermanentFileStore->MatchEntryToExistingMessage(entry, permanentStoreIndex);
		if (permanentStoreIndex!=KErrNotFound)
			{
			TRAP_IGNORE(BeginTransactionLC();
						iPermanentFileStore->RemovePDUsL(permanentStoreIndex, startPos, endPos);
						CommitTransactionL(););
			}
		}
	}

/**
This function is needed to ensure that permanent file
clean itself with pre-allocated file.
There migth be a scenario where user has deleted a message
but the corresponding message has not been deleted from permanent store file
due to out-of-disk condition. But the entry is invalid because
it is no more in the pre-allocated file which contains master header info.

@return the error code.

@internalComponent
*/
TInt CClass0SmsReassemblyStore::CleanReassemblyEntries()
	{
	LOGSMSPROT1("CleanReassemblyEntries");
	const CArrayFix<TSmsPreAllocatedFileStoreReassemblyEntry>& preAllocatedFileEntryArray = iPreallocatedFile->Entries();
	TInt ret=KErrNone;
	TRAP(ret,	BeginTransactionLC();
				iPermanentFileStore->CleanupEntriesL(preAllocatedFileEntryArray);
				CommitTransactionL(););

	if (ret == KErrDiskFull)
		{
		LOGSMSPROT1("CleanupEntriesL returns KErrDiskFull");
		/*
		Get access to reserve memory, call again to clean the entries with compact.
		Compact needs to be called at this instance because permanent store
		will use reserve disk space to delete, but after deletion reserve
		disk space will be freed. So compact will make sure that reserve disk
		space is not used after compaction.
		*/
		iFs.GetReserveAccess(KStoreDrive);
		TRAP(ret,	BeginTransactionLC();
					iPermanentFileStore->CleanupEntriesWithCompactL(preAllocatedFileEntryArray);
					CommitTransactionL(););
		LOGSMSPROT2("CleanupEntriesWithCompactL returns %d", ret);
		iFs.ReleaseReserveAccess(KStoreDrive);
		}
	return ret;
	}

/**
It returns the SMS entries (sms messages, sms pdu arrays & the corresponding index array)
from its permanent store & pre-allocated file.

@param aEntry reference to entry information. The message which match this entry information
				is returned to caller of the function.
@param aSmsMessage	a reference to class 0 sms message object.
@param aIndexArray	a reference to the array of pdu index.
@param aSmsArray	a reference to the array of pdu.

@internalComponent
*/
void CClass0SmsReassemblyStore::GetSmsEntriesL(const TReassemblyEntry& aEntry, CSmsMessage& aSmsMessage, CArrayFix<TInt>& aIndexArray, CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGSMSPROT1("CClass0SmsReassemblyStore::GetSmsEntriesL()");
	TInt permanentStoreIndex=KErrNotFound;
	iPermanentFileStore->MatchEntryToExistingMessage(aEntry, permanentStoreIndex);
	if (permanentStoreIndex!=KErrNotFound)
		{
		iPermanentFileStore->InternalizeEntryL(permanentStoreIndex, aSmsMessage, aIndexArray, aSmsArray);
		}
	else
		{
		LOGSMSPROT1("No PDUs in Permanent store file");
		}

	TInt preAllocatedFileIndex=KErrNotFound;
	iPreallocatedFile->MatchEntryToExistingMessage(aEntry, preAllocatedFileIndex);
	if (preAllocatedFileIndex!=KErrNotFound)
		{
		iPreallocatedFile->InternalizeEntryL(preAllocatedFileIndex, aSmsMessage, aIndexArray, aSmsArray);

		//Filter forwarde PDU here..this condition might arise if the PDU
		//could not be deleted at the time of OOD condition from permanent store file.
		TSmsPreAllocatedFileStoreReassemblyEntry preAllocatedFileEntry = iPreallocatedFile->Entries()[preAllocatedFileIndex];
		if (preAllocatedFileEntry.NumberOfPDUsForwardToClient() > 0)
			{
			for (TInt j=aIndexArray.Count(); j>0 ;j--)
				{
				TUint8 bitMapIndex = (aIndexArray.At(j-1)-1)/8;
				TUint8 bitPos = (aIndexArray.At(j-1)-1)%8;
				TUint8 bitMap;
				preAllocatedFileEntry.GetBitMap(bitMapIndex, bitMap);
				TUint8 tmpBitMap = 1;
				tmpBitMap <<= bitPos;
				if (tmpBitMap == (bitMap & tmpBitMap))
					{
					aIndexArray.Delete(j-1);
					aSmsArray.Delete(j-1);
					}
				}
			}

		if (permanentStoreIndex==KErrNotFound)
			{
			//In this scenario a CSmsMessage object has to be created from the existing PDU in
			//pre-allocated file & then serialized into aSmsMessage.
			LOGSMSPROT2("Number of PDUs in Pre-allocated file %d", aIndexArray.Count());
			if (aIndexArray.Count() > 0)
				{
				CSmsBuffer* smsBuffer = CSmsBuffer::NewL();
				CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, aSmsArray.At(0), smsBuffer, EFalse, ETrue);
				CleanupStack::PushL(smsMessage);

				//Update sms messages's properties.
				smsMessage->SetStorage(aSmsMessage.Storage());
				smsMessage->SetStatus((NMobileSmsStore::TMobileSmsStoreStatus)aSmsMessage.Status());
				smsMessage->SetLogServerId(aSmsMessage.LogServerId());
				smsMessage->SetTime(aSmsMessage.Time());
				smsMessage->SetUTCOffset(aSmsMessage.UTCOffset());
				smsMessage->SetDecodedOnSIM(aSmsMessage.DecodedOnSim());
				smsMessage->SetForwardToClient(aSmsMessage.ForwardToClient());
				smsMessage->SetToFromAddressL(aSmsMessage.ToFromAddress());

				CBufSeg* recvbufseg = CBufSeg::NewL(KSmsMaxSegmentLength);
				CleanupStack::PushL(recvbufseg);
				// Attempt to serialize this message into the buffer
				RBufWriteStream writestream(*recvbufseg);
				writestream.Open(*recvbufseg);
				CleanupClosePushL(writestream);
				writestream << *smsMessage;

				// Initialize the read stream with the buffer
				RBufReadStream readstream(*recvbufseg);
				readstream.Open(*recvbufseg,0);
				CleanupClosePushL(readstream);
				// De-serialize the message from using the read stream
				readstream >> aSmsMessage;
				CleanupStack::PopAndDestroy(4);		//readstream, writestream, recvbufseg, smsMessage
				}
			}
		}
	}

/*
It returns when the next timeout should occur.
*/
void CClass0SmsReassemblyStore::GetNextGuardTimeout(TTime& aNextTimeout)
	{
	TTime curTime;
	curTime.UniversalTime();
	TTimeIntervalSeconds timeOutInSecs = iGuardTimeOut*3600;
	aNextTimeout = curTime + timeOutInSecs;

	TInt class0ReassemblyStoreCount = iEntryArray.Count();

	if (class0ReassemblyStoreCount > 0)
		{
		for (TInt i=0; i<class0ReassemblyStoreCount;i++)
			{
			TTime guradTimeout = iEntryArray[i].Time() + timeOutInSecs;
			if ((guradTimeout > curTime) &&
				(guradTimeout < aNextTimeout))
				{
				aNextTimeout = guradTimeout;
				}
			}
		}
	}

/*
It returns the index of oldest message in class 0 re-assembly store.
*/
TInt CClass0SmsReassemblyStore::GetIndexOfOldestMessage()
	{
	TInt index = 0;
	TTime time = iEntryArray[index].Time();
	for (TInt index2=1; index2 < iEntryArray.Count(); index2++)
		{
		if (iEntryArray[index2].Time() < time)
			{
			index = index2;
			}
		}
	return index;
	}

/*
It returns the index of oldest message in class 0 re-assembly store.
*/
TInt CClass0SmsReassemblyStore::GetIndexOfOldestMessageFromReservedFileL()
	{
	TInt index = KErrNotFound;
	//Find the index of oldest message in pre-allocated file.
	index = iPreallocatedFile->GetOldestMessageEntryIndex();
	if (index == KErrNotFound)
		{
		//This condition should never arise
		User::Leave(KErrNotFound);
		}

	//Compare the oldest pre-allocated message entry against class 0 reassembly store entry
	//and return the index of class 0 reassembly store entry.
	const TSmsPreAllocatedFileStoreReassemblyEntry& preAllocatedFileEntry = iPreallocatedFile->Entries()[index];

	TInt reassemblyCount = iEntryArray.Count();
	TInt count;
	for (count = 0;  count < reassemblyCount;  count++)
		{
		TReassemblyEntry&  entry = iEntryArray[count];

		if (entry.Reference() == preAllocatedFileEntry.Reference()  &&
			entry.Total() == preAllocatedFileEntry.Total()  &&
			entry.PduType() == preAllocatedFileEntry.PduType()  &&
			entry.Storage() == preAllocatedFileEntry.Storage()  &&
			entry.Description2() == preAllocatedFileEntry.Description2())
			{
			//
			// Found it!
			//
			index = count;
			break;
			}
		}
	if (count == reassemblyCount)
		{
		//This condition should never arise
		User::Leave(KErrNotFound);
		}
	return index;
	}

/*
It returns a boolean value indicating whether the limitation (max class 0 message,
max pdu stored in pre-allocated file) imposed on class 0 reassembly store exceeds or not.
*/
TBool CClass0SmsReassemblyStore::IsExceedLimitation()
	{
	TInt class0MsgCount = iEntryArray.Count();
	TInt noOfMsgStoredInPreAllocatedFile = iPreallocatedFile->NumberOfPDUStored();
	if ((class0MsgCount > iMaxClass0Msg)
			|| (noOfMsgStoredInPreAllocatedFile > iMaxPDUSeg))
		{
		return ETrue;
		}

	return EFalse;
	}

/*
This sorting algorithm is based on bubble sort
*/
void CClass0SmsReassemblyStore::SortPDUsL(CArrayFix<TInt>& aIndexArray, CArrayFix<TGsmSms>& aSmsArray)
	{
	//Test---Index array count must be equal to sms array count
	TBool swapped;

	do
		{
		swapped = EFalse;
		for (TInt i=0; i<aIndexArray.Count()-1 ;i++)
			{
			if (aIndexArray[i] > aIndexArray[i+1])
				{
				aSmsArray.InsertL(i, aSmsArray[i+1]);
				aSmsArray.Delete(i+2);
				aIndexArray.InsertL(i, aIndexArray[i+1]);
				aIndexArray.Delete(i+2);
				swapped = ETrue;
				}
			}
		}
	while (swapped);
	}

/**
 *  Utility func for cleanup stack
 */
void CClass0StoreCloseObject(TAny* aObj)
	{
	LOGGSMU2("WARNING! Hey, CClass0StoreCloseObject called by Untrapper! [0x%08x]", aObj);
	((CClass0SmsReassemblyStore*)aObj)->Revert();
	}

/**
 *  Sets the class 0 re-assembly store as in-transaction.
 *  
 *  The function checks the validity of the call and leaves KErrAccessDenied if
 *  invalid.
 *  @capability None
 */
void CClass0SmsReassemblyStore::BeginTransactionLC()
	{
	LOGSMSPROT3("CClass0SmsReassemblyStore::BeginTransactionLC [this=0x%08X iInTransaction=%d]", this, iInTransaction);

	if (iInTransaction)
		{
	    LOGGSMU1("WARNING CClass0SmsReassemblyStore::BeginTransactionLC leaving with KErrAccessDenied");
		User::Leave(KErrAccessDenied);
		}

	TCleanupItem class0StoreClose(CClass0StoreCloseObject, this);
	CleanupStack::PushL(class0StoreClose);
	iPreallocatedFile->BeginTransactionL();
	iPermanentFileStore->BeginTransactionL();
	iInTransaction = ETrue;
	}

/**
 *  It commits the transaction.
 */
void CClass0SmsReassemblyStore::CommitTransactionL()
	{
	LOGSMSPROT3("CClass0SmsReassemblyStore::CommitTransactionL(): this=0x%08X iInTransaction=%d",
				this, iInTransaction);

	//Commit permanent store file
	iPermanentFileStore->CommitTransactionL();
	//Commit pre-allocated file
	iPreallocatedFile->CommitTransactionL();
	CleanupStack::Pop(this);	//CClass0StoreCloseObject
	iInTransaction = EFalse;
	}

/**
 *  It reverts the transaction.
 */
void CClass0SmsReassemblyStore::Revert()
	{
	LOGSMSPROT3("CClass0SmsReassemblyStore::Revert(): this=0x%08X, iInTransaction=%d",
    		 this, iInTransaction);

	iPreallocatedFile->Revert();
	iPermanentFileStore->Revert();
	iInTransaction = EFalse;
	}