// Copyright (c) 1999-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 CSmsReassemblyStore and CSmsSegmentationStore.
// 
//

/**
 @file
*/

#include <e32svr.h>
#include <es_ini.h>
#include "smspstor.h"
#include "smspmain.h"
#include "smsuaddr.H"
#include "Gsmumsg.h"
#include "gsmubuf.h"
#include <logwrap.h>
#include <logwraplimits.h>
#include "Gsmuelem.h"
#include "gsmuieoperations.h"
#include "gsmunonieoperations.h"

LOCAL_C TPtrC TrimLeadingZeros(const TDesC& aString)
	{
	LOGSMSPROT1("CSARStore::ExternalizeEntryArrayL()");

	const TInt len = aString.Length();

	if (len == 0)
		return aString;

	const TUint16* startChar = &aString[0];
	const TUint16* endChar = &aString[len-1];

	while (startChar <= endChar && *startChar == '0')
		{
		++startChar;
		}
	return TPtrC(startChar, endChar - startChar + 1);
	} // TrimLeadingZeros


/**
 *  Creates new CSmsReassemblyStore instance
 *  
 *  @param aFs  File Server handle.
 */
CSmsReassemblyStore* CSmsReassemblyStore::NewL(RFs& aFs)
	{
	LOGSMSPROT1("CSmsReassemblyStore::NewL()");

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

	return self;
	} // CSmsReassemblyStore::NewL


/**
 *  Creates and starts timer
 */
void CSmsReassemblyStore::ConstructL()
	{
	LOGSMSPROT1("CSmsReassemblyStore::ConstructL()");

	//
	// Generate the full path to the reassembly store.
	//
	PrivatePath(iFullPathBuf);
	iFullPathBuf.Append(KReassemblyStoreName);

 	iLastReceivedTime.UniversalTime();
 	iLastRealTime = iLastReceivedTime;
	} // CSmsReassemblyStore::ConstructL


/**
 *  Destructor
 */
CSmsReassemblyStore::~CSmsReassemblyStore()
	{
	// NOP
	} // CSmsReassemblyStore::~CSmsReassemblyStore


void CSmsReassemblyStore::UpdateLogServerIdL(TInt aIndex, TLogId aLogServerId)
	{
	LOGSMSPROT1("CSmsReassemblyStore::UpdateLogServerIdL()");

	TSmsReassemblyEntry entry(reinterpret_cast<const TSmsReassemblyEntry&>(Entries()[aIndex]));

	if (entry.LogServerId() != aLogServerId)
		{
		entry.SetLogServerId(aLogServerId);
		BeginTransactionLC();
		ChangeEntryL(aIndex, entry);
		CommitTransactionL();
		}
	} // CSmsReassemblyStore::UpdateLogServerIdL


/**
 *  Searches the reassembly store for a CSmsMessage and returns its index.
 *
 *  @param aSmsMessage  Message to search for.
 *  @param aPassed      Determines if we are searching for a message already
 *                      passed to the client.
 *  @param aIndex       Return index value.
 *
 *  @return True and an index if aSmsMessage is found in this reassembly store
 */
TBool CSmsReassemblyStore::FindMessageL(const CSmsMessage& aSmsMessage,
										TBool aPassed,
										TInt& aIndex)
 	{
 	LOGSMSPROT1("CSmsReassemblyStore::FindMessageL()");

	//
	// Parse the GSM data from the SMS message...
	//
	TGsmSmsTelNumber  parsedAddress;

	aSmsMessage.ParsedToFromAddress(parsedAddress);

	//
	// Search the store for a matching message...
	//
 	for (TInt index = Entries().Count() - 1;  index >= 0;  index--)
 		{
		const TSmsReassemblyEntry&  entry = reinterpret_cast<const TSmsReassemblyEntry&>(Entries()[index]);

		// Always search the basic types first and strings last!
		if (entry.PduType() == aSmsMessage.Type()  &&
			entry.PassedToClient() == aPassed  &&
			entry.Storage() == aSmsMessage.Storage()  &&
			entry.Time() == aSmsMessage.Time()  &&
			entry.Description2().Right(8) == parsedAddress.iTelNumber.Right(8))
 			{
			//
			// Found!
			//
			LOGSMSPROT2("CSmsReassemblyStore::FindMessage(): Found! index=%d", index);

			aIndex = index;

			return ETrue;
			}
 		}

	//
	// Not found...
	//
	LOGSMSPROT1("CSmsReassemblyStore::FindMessage(): Not found!");

 	return EFalse;
 	} // CSmsReassemblyStore::FindMessageL


/**
 *  Adds Pdu to reassembly store
 *  
 *  @param aSmsMessage  PDU to 
 *  @param aGsmSms Used to
 *  @param aIndex Used to
 *  @param aComplete Used to
 *  @param aServiceCenterAddressPresent Used to
 */
void CSmsReassemblyStore::MatchPDUToExistingMessage(const CSmsMessage& aSmsMessage,
													TInt& aIndex)
	{
	LOGSMSPROT1("CSmsReassemblyStore::MatchPDUToExistingMessage()");

	__ASSERT_ALWAYS(!aSmsMessage.IsDecoded(), SmspPanic(KSmspPanicMessageConcatenated));

	aIndex = KErrNotFound;

	TGsmSmsTelNumber  parsedAddress;
	aSmsMessage.ParsedToFromAddress(parsedAddress);

	//
	// Search the reassembly store for a matching entry (start from the
	// end as the most recent PDUs appear at the end)...
	//
	TInt reassemblyCount = Entries().Count();

	for (TInt index = reassemblyCount - 1;  index >= 0;  index--)
		{
		TSmsReassemblyEntry&  entry = (TSmsReassemblyEntry&) Entries()[index];

		// Always check the fields in order of the quickest to check...
		if (entry.IsComplete() == EFalse  &&
		    entry.PduType() == aSmsMessage.Type()  &&
			entry.Storage() == aSmsMessage.Storage())
			{
			TInt  telLen = Min(entry.Description2().Length(),
					           parsedAddress.iTelNumber.Length());
			
			if (entry.Description2().Right(telLen) == parsedAddress.iTelNumber.Right(telLen)  &&
		        entry.Total() == aSmsMessage.SmsPDU().NumConcatenatedMessagePDUs()  &&
			    entry.Reference() == aSmsMessage.SmsPDU().ConcatenatedMessageReference())
				{
				//
				// Found it!
				//
				aIndex = index;
				break;
				}
			}
		}

	LOGSMSPROT3("CSmsReassemblyStore::MatchPDUToExistingMessage(): reassemblyCount=%d, aIndex=%d", reassemblyCount, aIndex);
	} // CSmsReassemblyStore::MatchPDUToExistingMessage


void CSmsReassemblyStore::UpdateExistingMessageL(const CSmsMessage& aSmsMessage,
												 const TGsmSms& aGsmSms, TInt aIndex,
												 TBool& aComplete,
												 TBool& aDuplicateMsgRef,
												 TBool& aDuplicateSlot)
	{
	LOGSMSPROT1("CSmsReassemblyStore::UpdateExistingMessageL()");

	aComplete        = EFalse;
	aDuplicateMsgRef = EFalse;
	aDuplicateSlot   = EFalse;

	//
	// Read the segment from the store into a CSmsMessage buffer...
	//
	TSmsReassemblyEntry  entry = (TSmsReassemblyEntry&) Entries()[aIndex];

	CArrayFix<TInt>*  indexArray = new(ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indexArray);
	CArrayFixFlat<TGsmSms>*  smsArray = new(ELeave) CArrayFixFlat<TGsmSms>(8);
	CleanupStack::PushL(smsArray);
	CSmsBuffer*  buffer = CSmsBuffer::NewL();
	CSmsMessage*  smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer);
	CleanupStack::PushL(smsMessage);

	InternalizeEntryL(entry.DataStreamId(), *smsMessage, *indexArray, *smsArray);
	
	//
	// 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;
			}
		}

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

	//
	// If the PDU is stored then add the slot information...
	//
	if (smsMessage->Storage() == CSmsMessage::ESmsSIMStorage  ||
		smsMessage->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);
		}

	//
	// Does this PDU mean the message is complete? If so then decode the message,
	// reset the index and sms arrays (to save space for completed SMSs).
	//
	indexArray->AppendL(concatPDUIndex);
	smsArray->AppendL(aGsmSms);

	if (smsArray->Count() == smsMessage->SmsPDU().NumConcatenatedMessagePDUs())
		{
		smsMessage->DecodeMessagePDUsL(*smsArray);

		indexArray->Reset();
		smsArray->Reset();

		aComplete = ETrue;
		}

	//
	// Write the entry back into the store...
	//
	TStreamId  streamid = entry.DataStreamId();
	smsMessage->SetLogServerId(entry.LogServerId());

	BeginTransactionLC();
	ExternalizeEntryL(streamid, *smsMessage, *indexArray, *smsArray);
	PopulateEntry(entry, *smsMessage, smsArray->Count());
	ChangeEntryL(aIndex, entry);
	CommitTransactionL();

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


void CSmsReassemblyStore::NewMessagePDUL(TInt& aIndex,CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms)
	{
	LOGSMSPROT1("CSmsReassemblyStore::NewMessagePDUL");

	CArrayFix<TInt>* indexarray=new(ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indexarray);
	CArrayFixFlat<TGsmSms>* smsarray=new(ELeave) CArrayFixFlat<TGsmSms>(8);
	CleanupStack::PushL(smsarray);
	TInt index=aSmsMessage.IsDecoded()? 0: aSmsMessage.SmsPDU().ConcatenatedMessagePDUIndex();
	indexarray->AppendL(index);
	smsarray->AppendL(aGsmSms);
	aIndex=Entries().Count();
	CreateEntryL(aSmsMessage,*indexarray,*smsarray);
	CleanupStack::PopAndDestroy(2);
	} // CSmsReassemblyStore::NewMessagePDUL


void CSmsReassemblyStore::GetMessageL(TInt aIndex,CSmsMessage& aSmsMessage)
	{
	LOGSMSPROT2("CSmsReassemblyStore::GetMessageL [aIndex=%d]", aIndex);

	CArrayFix<TInt>* indexarray=new(ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indexarray);
	CArrayFixFlat<TGsmSms>* smsarray=new(ELeave) CArrayFixFlat<TGsmSms>(8);
	CleanupStack::PushL(smsarray);
	InternalizeEntryL(Entries()[aIndex].DataStreamId(),aSmsMessage,*indexarray,*smsarray);
	TInt logid=Entries()[aIndex].LogServerId();
	if(aSmsMessage.LogServerId() == KLogNullId &&  logid != KLogNullId)
		aSmsMessage.SetLogServerId(logid);
	CleanupStack::PopAndDestroy(2); // smsarray, indexarray
	} // CSmsReassemblyStore::GetMessageL


/**
 *  internalize all the entries from the permanent file store to internal memory
 *  
 *  NOTE! You have to call CSARStore::OpenFileLC() before calling this function
 */
void CSmsReassemblyStore::InternalizeEntryL(const TStreamId& aStreamId,CSmsMessage& aSmsMessage,CArrayFix<TInt>& aIndexArray,CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGSMSPROT2("CSmsReassemblyStore::InternalizeEntryL Start [sid=%d]", aStreamId.Value());
	RStoreReadStream readstream;
	readstream.OpenLC(FileStore(),aStreamId);
	readstream >> aSmsMessage;
	TInt count=readstream.ReadInt32L();
	aIndexArray.Reset();
	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);
	aSmsArray.Reset();
	for (i=0; i<count; i++)
		{
		RMobileSmsMessaging::TMobileSmsGsmTpdu pdu;
		readstream >> pdu;
		TGsmSms sms;
		sms.SetPdu(pdu);
		aSmsArray.AppendL(sms);
		}
	CleanupStack::PopAndDestroy();
	LOGSMSPROT2("CSmsReassemblyStore::InternalizeEntryL End [count=%d]", count);
	} // CSARStore::OpenFileLC


/**
 *  externalizes all the entries from the internal memory to the permanent file store
 *  
 *  NOTE! You have to call CSARStore::OpenFileLC() before calling this function
 */
void CSmsReassemblyStore::ExternalizeEntryL(TStreamId& aStreamId,const CSmsMessage& aSmsMessage,const CArrayFix<TInt>& aIndexArray,const CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGSMSPROT2("CSmsReassemblyStore::ExternalizeEntryL Start [sid=%d]", aStreamId.Value());

	RStoreWriteStream writestream;
	if (aStreamId==KNullStreamId)
		aStreamId=writestream.CreateLC(FileStore());
	else
		writestream.ReplaceLC(FileStore(),aStreamId);
	writestream << aSmsMessage;
	TInt count=aIndexArray.Count();
	__ASSERT_ALWAYS(count==aIndexArray.Count(),SmspPanic(KSmspPanicBadIndexArray));
	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("CSmsReassemblyStore::ExternalizeEntryL End [count=%d]", count);
	} // CSARStore::OpenFileLC


void CSmsReassemblyStore::PopulateEntry(TSmsReassemblyEntry& aEntry,const CSmsMessage& aSmsMessage,TInt aNumSmss)
	{
	LOGSMSPROT1("CSmsReassemblyStore::PopulateEntry");
	aEntry.SetReference(0);
	aEntry.SetTotal(1);
	aEntry.SetCount(1);

	if (aSmsMessage.TextPresent())
		{
		if (aSmsMessage.SmsPDU().TextConcatenated())
			{
			aEntry.SetReference(aSmsMessage.SmsPDU().ConcatenatedMessageReference());
			aEntry.SetTotal(aSmsMessage.SmsPDU().NumConcatenatedMessagePDUs());
			aEntry.SetCount(aSmsMessage.IsComplete()? aEntry.Total(): aNumSmss);
			}
		TInt bits7to4=aSmsMessage.SmsPDU().Bits7To4();
		TInt count=aSmsMessage.SmsPDU().UserData().NumInformationElements();
		TInt identifier1=0xFF;
		TInt identifier2=0x00;
		for (TInt i=0; i<count; i++)
			{
			TInt identifier=aSmsMessage.SmsPDU().UserData().InformationElement(i).Identifier();
			if ((identifier!=CSmsInformationElement::ESmsIEIConcatenatedShortMessages8BitReference) && (identifier!=CSmsInformationElement::ESmsIEIConcatenatedShortMessages16BitReference))
				{
				if (identifier<identifier1)
					identifier1=identifier;
				if (identifier>identifier2)
					identifier2=identifier;
				}
			}

		if ((bits7to4>=TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationDiscardMessage) && (bits7to4<=TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationUCS2))
			aEntry.SetBits7to4andIdentifiers(bits7to4, identifier1, identifier2);
		else
			aEntry.SetBits7to4andIdentifiers(0, identifier1, identifier2);
		}

	//Set the logServerId to aSmsMessage.LogServerId()

	aEntry.SetLogServerId(aSmsMessage.LogServerId());

	const CSmsPDU::TSmsPDUType type(aSmsMessage.Type());
	aEntry.SetPduType(type);
	aEntry.SetPassedToClient(EFalse);

	aEntry.SetStorage(aSmsMessage.Storage());
	if ((type!=CSmsPDU::ESmsSubmitReport) && (type!=CSmsPDU::ESmsDeliverReport))
		{
		//  Strip out spaces etc from address
		TGsmSmsTelNumber parsedaddress;
		aSmsMessage.ParsedToFromAddress(parsedaddress);
		aEntry.SetDescription2(parsedaddress.iTelNumber);
		}

	aEntry.SetTime(aSmsMessage.Time());
	} // CSmsReassemblyStore::PopulateEntry


void CSmsReassemblyStore::CreateEntryL(CSmsMessage& aSmsMessage,const CArrayFix<TInt>& aIndexArray,const CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGSMSPROT1("CSmsReassemblyStore::CreateEntryL");
 	TStreamId streamid=KNullStreamId;
 	if (aSmsMessage.Time() >= iLastRealTime)
 		{
 		iLastRealTime=aSmsMessage.Time();
 		if(iLastReceivedTime >= aSmsMessage.Time())
 			{
 			aSmsMessage.SetTime(iLastReceivedTime+(TTimeIntervalMicroSeconds32)1);
 			}
 		iLastReceivedTime=aSmsMessage.Time(); //provide uniqueness of time
 		}
 	else  // clock turned back
 		{
 		iLastReceivedTime=aSmsMessage.Time();
 		}
	BeginTransactionLC();
	ExternalizeEntryL(streamid,aSmsMessage,aIndexArray,aSmsArray);
	TSmsReassemblyEntry entry;
	entry.SetDataStreamId(streamid);

	PopulateEntry(entry,aSmsMessage,aSmsArray.Count());
	AddEntryL(entry);
	CommitTransactionL();
	} // CSmsReassemblyStore::CreateEntryL


TBool CSmsReassemblyStore::PassedToClient( TInt aIndex ) const
	{
	LOGSMSPROT1("CSmsReassemblyStore::PassedToClient()");

	const TSmsReassemblyEntry& entry = reinterpret_cast<const TSmsReassemblyEntry&>(Entries()[ aIndex ]);
	return entry.PassedToClient();
	} // CSmsReassemblyStore::PassedToClient


void CSmsReassemblyStore::SetPassedToClientL(TInt aIndex, TBool aPassed)
	{
	LOGSMSPROT1("CSmsReassemblyStore::SetPassedToClientL()");

	TSmsReassemblyEntry entry(reinterpret_cast<const TSmsReassemblyEntry&>(Entries()[aIndex]));

	const TBool alreadyPassed = entry.PassedToClient();

	if ((!aPassed && alreadyPassed) || (aPassed && !alreadyPassed))
		{
		entry.SetPassedToClient(aPassed);
		ChangeEntryL(aIndex, entry);
		}
	} // CSmsReassemblyStore::SetPassedToClientL


/**
 *  Open the sms reassembly store.
 */
void CSmsReassemblyStore::OpenStoreL()
	{
	LOGSMSPROT1("CSmsReassemblyStore::OpenStoreL()");

	this->OpenL(iFullPathBuf,KReassemblyStoreUid);
	} // CSmsReassemblyStore::OpenStoreL


/**
 *  Constructor
 *  
 *  @param aFs Used to set CSARStore object
 */
CSmsReassemblyStore::CSmsReassemblyStore(RFs& aFs)
	:CSARStore(aFs)
	{
	// NOP
	} // CSmsReassemblyStore::CSmsReassemblyStore


CSmsSegmentationStore* CSmsSegmentationStore::NewL(RFs& aFs)
	{
	LOGSMSPROT1("CSmsSegmentationStore::NewL()");

	CSmsSegmentationStore* segmentationStore = new(ELeave) CSmsSegmentationStore(aFs);
	CleanupStack::PushL( segmentationStore );
	segmentationStore->ConstructL();
	CleanupStack::Pop( segmentationStore );
	return segmentationStore;
	} // CSmsSegmentationStore::NewL


void CSmsSegmentationStore::ConstructL()
	{
	LOGSMSPROT1("CSmsSegmentationStore::ConstructL()");

	//generate fullpath of segmentation store.
	PrivatePath(iFullPathBuf);
	//append store name
	iFullPathBuf.Append(KSegmentationStoreName);
	
	// Set the default value for the maxmum number messages in the segmentation store.
	iMaxmumNumberOfMessagesInSegmentationStore = KDefaultMaxmumNumberOfMessagesInSegmentationStore;

	CESockIniData*  ini = NULL;
	_LIT(KSmseskfile, "smswap.sms.esk");
	TRAPD(ret, ini=CESockIniData::NewL(KSmseskfile));
	if(ret == KErrNone)
	    {
	    // Get the maximum number of messages allowed in the segmentation store from .ESK file
        CleanupStack::PushL(ini);
        
        TPtrC value;
        if((ini->FindVar(_L("SegmentationStoreOptions"),_L("MaxNumOfMessInSegStore"),value)))
            {
            TLex16 valueconv(value);
            valueconv.Val(iMaxmumNumberOfMessagesInSegmentationStore); 
            }
        CleanupStack::PopAndDestroy(ini);    
	    }
	else if (ret != KErrNotFound && ret != KErrPathNotFound)
	    {
	    User::Leave(ret);
	    }
	} // CSmsSegmentationStore::ConstructL


CSmsSegmentationStore::~CSmsSegmentationStore()
    {
    // NOP
    } // CSmsSegmentationStore::~CSmsSegmentationStore


TInt CSmsSegmentationStore::Next8BitReferenceL()
	{
	LOGSMSPROT1("CSmsSegmentationStore::Next8BitReferenceL");

	TInt reference8bit=0;
	TInt reference16bit=0;
	TStreamId streamid=ExtraStreamId();

	//
	// access file store
	//
	BeginTransactionLC();
	if (streamid!=KNullStreamId)
		{
		TRAPD(ret,InternalizeConcatenationReferencesL(streamid,reference8bit,reference16bit));
		if(ret != KErrNone)
			{
			// We have to leave on any error; otherwise a duplicate reference number will be generated
			// The transaction will revert
			LOGSMSPROT2("WARNING! CSmsSegmentationStore::InternalizeConcatenationReferencesL left with %d", ret);
			User::Leave(ret);  //  stream not corrupted
			}
		reference8bit=(reference8bit+1)%0x100;
		}
	TRAPD(ret, ExternalizeConcatenationReferencesL(streamid,reference8bit,reference16bit));
	if(ret != KErrNone)
		{
		// We have to leave on any error; otherwise a duplicate reference number will be generated
		// The transaction will revert
		LOGSMSPROT2("WARNING! CSmsSegmentationStore::ExternalizeConcatenationReferencesL left with %d", ret);
		User::Leave(ret);  //  stream not corrupted
		}
	SetExtraStreamIdL(streamid);
	CommitTransactionL();
	return reference8bit;
	} // CSmsSegmentationStore::Next8BitReferenceL


TInt CSmsSegmentationStore::Next16BitReferenceL()
	{
	LOGSMSPROT1("CSmsSegmentationStore::Next16BitReferenceL");
	TInt reference8bit=0;
	TInt reference16bit=0x100;
	TStreamId streamid=ExtraStreamId();
	//
	// access file store
	//
	BeginTransactionLC();
	if (streamid!=KNullStreamId)
		{
		TRAPD(ret,InternalizeConcatenationReferencesL(streamid,reference8bit,reference16bit));
		if(ret != KErrNone)
			{
			// We have to leave on any error; otherwise a duplicate reference number will be generated
			// The transaction will revert
			LOGSMSPROT2("WARNING! CSmsSegmentationStore::InternalizeConcatenationReferencesL left with %d", ret);
			User::Leave(ret);  //  stream not corrupted
			}
		reference16bit=((reference16bit+1)%0xFF00)+0x100;
		}
	TRAPD(ret, ExternalizeConcatenationReferencesL(streamid,reference8bit,reference16bit));
	if(ret != KErrNone)
		{
		// We have to leave on any error; otherwise a duplicate reference number will be generated
		// The transaction will revert
		LOGSMSPROT2("WARNING! CSmsSegmentationStore::ExternalizeConcatenationReferencesL left with %d", ret);
		User::Leave(ret);  //  stream not corrupted
		}
	SetExtraStreamIdL(streamid);
	CommitTransactionL();
	return reference16bit;
	} // CSmsSegmentationStore::Next16BitReferenceL


/**
 *  Adds a CSmsMessage to the segmentation store
 *  
 *  @note aSumbit.Buffer() may be shortened to TSAREntry::ESmsSAREntryDescriptionLength
 *  
 *  @param aSubmit Message to add to the segmentation store
 *  @pre aSubmit.Type() is ESmsSubmit
 *  @pre aSubmit.EncodeMessagePdusL() has been called. This is so PopulateEntry sets the correct total on the TSAREntry
 */
void CSmsSegmentationStore::AddSubmitL(const TSmsAddr& aSmsAddr,CSmsMessage& aSubmit)
	{
	LOGSMSPROT1("CSmsSegmentationStore::AddSubmitL");

	__ASSERT_ALWAYS(aSubmit.Type()==CSmsPDU::ESmsSubmit,SmspPanic(KSmspPanicNotSubmit));

	BeginTransactionLC();


	RSmsSegmentationStoreRefStatusArray refStatus;
	CleanupClosePushL(refStatus);

	TStreamId streamid=KNullStreamId;
	CSmsBufferBase& buffer=aSubmit.Buffer();
	TInt length=buffer.Length();
	if (length>TSAREntry::ESmsSAREntryDescriptionLength)
		buffer.DeleteL(TSAREntry::ESmsSAREntryDescriptionLength,length-TSAREntry::ESmsSAREntryDescriptionLength);

	ExternalizeEntryL(streamid,aSmsAddr,aSubmit, refStatus);

	TSmsSegmentationEntry entry;
	entry.SetDataStreamId(streamid);
	PopulateEntry(entry, aSubmit, refStatus);

	CleanupStack::PopAndDestroy(&refStatus);

	AddEntryL(entry);
	CommitTransactionL();
	} // CSmsSegmentationStore::AddSubmitL


TBool CSmsSegmentationStore::AddCommandL(const TSmsAddr& aSmsAddr,const CSmsMessage& aCommand, CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray& aRefStatus)
	{
	LOGSMSPROT1("CSmsSegmentationStore::AddCommandL");
	__ASSERT_ALWAYS(aCommand.Type()==CSmsPDU::ESmsCommand,SmspPanic(KSmspPanicNotCommand));
	const TInt count=Entries().Count();
	const TLogId logid=(TLogId) aCommand.LogServerId();

	BeginTransactionLC();

	for (TInt i=count-1; i>=0; --i)
		{
		if ((logid!=KLogNullId) && (logid==Entries()[i].LogServerId()))
			{
			DeleteEntryL(i);
			break;
			}
		}
	TBool found=EFalse;

	CSmsBuffer* buffer=CSmsBuffer::NewL();
	CSmsMessage* smsmessage=CSmsMessage::NewL(iFs, CSmsPDU::ESmsSubmit,buffer);
	CleanupStack::PushL(smsmessage);
	TGsmSmsTelNumber parsedaddress;
	aCommand.ParsedToFromAddress(parsedaddress);
	TInt telLen;

	for (TInt j=0; j<count; j++)
		{
		TSmsSegmentationEntry entry = (TSmsSegmentationEntry&)Entries()[j];
		const CSmsPDU::TSmsPDUType type = entry.PduType();
		telLen=Min(entry.Description2().Length(),parsedaddress.iTelNumber.Length());
		if ((type==CSmsPDU::ESmsSubmit) &&
			 entry.IsComplete() &&
		    (entry.Description2().Right(telLen)==parsedaddress.iTelNumber.Right(telLen)) &&
		    (entry.Time()==aCommand.Time()))
			{
			found=ETrue;
			TSmsAddr smsaddr;

			InternalizeEntryL(entry.DataStreamId(),smsaddr,*smsmessage, aRefStatus);
			TStreamId streamid;
			CSmsSubmit& submit=(CSmsSubmit&) smsmessage->SmsPDU();
			if ((((CSmsCommand&) aCommand.SmsPDU()).CommandType()==TSmsCommandType::ESmsCommandTypeEnableStatusReportRequest) &&
			     (!submit.StatusReportRequest()))
				{
				submit.SetStatusReportRequest(ETrue);
				streamid=entry.DataStreamId();
				ExternalizeEntryL(streamid,smsaddr,*smsmessage, aRefStatus);
				PopulateEntry(entry,*smsmessage, aRefStatus);
				ChangeEntryL(j,entry);
				}

			RSmsSegmentationStoreRefStatusArray refStatusTemp;
			CleanupClosePushL(refStatusTemp);

			streamid=KNullStreamId;

			ExternalizeEntryL(streamid,aSmsAddr,aCommand, refStatusTemp);
			entry.SetDataStreamId(streamid);
			PopulateEntry(entry,aCommand, refStatusTemp);

			CleanupStack::PopAndDestroy(&refStatusTemp);

			AddEntryL(entry);

			break;
			}
		}
	CleanupStack::PopAndDestroy(smsmessage);  // smsmessage
	CommitTransactionL();

	return found;
	} // CSmsSegmentationStore::AddCommandL


TBool CSmsSegmentationStore::AddReferenceL(const CSmsMessage& aSmsMessage,TInt aReference)
	{
	const TInt count=Entries().Count();
	LOGSMSPROT3("CSmsSegmentationStore::AddReferenceL [count=%d, ref=%d]", count, aReference);
	TInt i=0;
	TInt logserverid=aSmsMessage.LogServerId();
	if (logserverid!=KLogNullId)
		{
		for (i=0; i<count; i++)
			{
            if (logserverid==((TSmsSegmentationEntry&)Entries()[i]).LogServerId())
				break;
			}
		}
	else
		{
		TGsmSmsTelNumber parsedaddress;
		aSmsMessage.ParsedToFromAddress(parsedaddress);
		TInt telLen;
		for (i=0; i<count; i++)
			{
            TSAREntry tsareenty = Entries()[i];      
            TSmsSegmentationEntry& entry = static_cast<TSmsSegmentationEntry&>(tsareenty);        
            telLen=Min(entry.Description2().Length(),parsedaddress.iTelNumber.Length());
            const CSmsPDU::TSmsPDUType type=entry.PduType();
            if ((type==aSmsMessage.Type()) && (!entry.IsComplete()) && (aSmsMessage.Time()==entry.Time()) && (entry.Description2().Right(telLen)==parsedaddress.iTelNumber.Right(telLen)))
                    break;
                }
            }
	if(i>=count)
		{
		LOGSMSPROT3("WARNING! KSmspPanicEntryWithLogServerIdNotFound [i=%d, count=%d]", i, count);
		return EFalse;
		}

	RSmsSegmentationStoreRefStatusArray refStatusArray;
	CleanupClosePushL(refStatusArray);

	TSAREntry tsareenty = Entries()[i];      
	TSmsSegmentationEntry& entry = static_cast<TSmsSegmentationEntry&>(tsareenty);        
	TStreamId streamid=entry.DataStreamId();
	TSmsAddr smsaddr;
	CSmsBuffer* buffer=CSmsBuffer::NewL();
	CSmsMessage* smsmessage=CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver,buffer);
	CleanupStack::PushL(smsmessage);

	//
	// access the file store
	//
	InternalizeEntryL(streamid,smsaddr,*smsmessage, refStatusArray);
	refStatusArray.InsertL(aReference);

	BeginTransactionLC();
	ExternalizeEntryL(streamid,smsaddr,*smsmessage, refStatusArray);

	PopulateEntry(entry,/*smsaddr,*/*smsmessage, refStatusArray);
	ChangeEntryL(i,entry);
	CommitTransactionL();

	//
	// AEH: moved here because if is done before calling ChangeEntryL
	//      it will pop and destroy the filestore which is also on
	//      the cleanup stack
	//
	CleanupStack::PopAndDestroy(2);  //  smsmessage, refStatus

	return entry.Total()==entry.Count();
	}

	
/**
 *  Does exactly the same thing as AddReferenceL() i.e. adds the the segment refernce to a list. But 
 *  to support the new status report schemes a slight change has been made. Instead of inserting
 *  just a reference now its status is inserted as well. This is provided one of the two new schemes
 *  is being used. If the status is required then we do exactly the same as AddReferenceL(), but if
 *  it's not then we just call the InsertL() method with an extra parameter: EStatusComplete.
 *  
 *  @param aSmsMessage Reference to CSmsMessage.
 *  @param aReference  The PDU reference.
 */
TBool CSmsSegmentationStore::AddReferenceStatusPairL(const CSmsMessage& aSmsMessage,TInt aReference, TUint aSegmentSequenceNumber)
 	{
	const TInt count=Entries().Count();
	LOGSMSPROT3("CSmsSegmentationStore::AddReferenceStatusPairL [count=%d, ref=%d]", count, aReference);
	TInt i=0;
	TInt logserverid=aSmsMessage.LogServerId();
	if (logserverid!=KLogNullId)
		{
		for (i=0; i<count; i++)
			{
            if (logserverid==((TSmsSegmentationEntry&)Entries()[i]).LogServerId())
				break;
			}
		}
	else
		{
		TGsmSmsTelNumber parsedaddress;
		aSmsMessage.ParsedToFromAddress(parsedaddress);
		TInt telLen;
		for (i=0; i<count; i++)
			{
            TSAREntry tsareenty = Entries()[i];  
            TSmsSegmentationEntry& entry = static_cast<TSmsSegmentationEntry&>(tsareenty);
			telLen=Min(entry.Description2().Length(),parsedaddress.iTelNumber.Length());
			const CSmsPDU::TSmsPDUType type=entry.PduType();
			if ((type==aSmsMessage.Type()) && (!entry.IsComplete()) && (aSmsMessage.Time()==entry.Time()) && (entry.Description2().Right(telLen)==parsedaddress.iTelNumber.Right(telLen)))
				break;
			}
		}
	if(i>=count)
		{
		LOGSMSPROT3("WARNING! KSmspPanicEntryWithLogServerIdNotFound [i=%d, count=%d]", i, count);
		return EFalse;
		}

	RSmsSegmentationStoreRefStatusArray refStatusArray;
	CleanupClosePushL(refStatusArray);
    TSAREntry tsareenty = Entries()[i];  
    TSmsSegmentationEntry& entry = static_cast<TSmsSegmentationEntry&>(tsareenty);

	TStreamId streamid=entry.DataStreamId();
	TSmsAddr smsaddr;
	CSmsBuffer* buffer=CSmsBuffer::NewL();
	CSmsMessage* smsmessage=CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver,buffer);
	CleanupStack::PushL(smsmessage);

	//
	// access the file store
	//
	InternalizeEntryL(streamid,smsaddr,*smsmessage, refStatusArray);
	
	if (aSmsMessage.Scheme() == EControlParametersScheme)
		{
		TUint8 octet(0);
		TInt  ret;
		
		ret = ((CSmsSMSCCtrlParameterOperations&)aSmsMessage.GetOperationsForIEL(CSmsInformationElement::ESmsIEISMSCControlParameters)).GetStatusReport(aSegmentSequenceNumber, octet);
		if (ret == KErrNone)
			{
			if (octet & ESmsSMSCControlParametersMask)
				{
				refStatusArray.InsertL(aReference);	
				}
			else
				{
				refStatusArray.InsertL(TSmsSegmentationStoreRefStatus(aReference, EStatusComplete));	
				}
			}
		}
	else if(aSmsMessage.Scheme() == ETPSRRScheme)
		{
		TInt tpsrr;
		tpsrr =  ((CSmsTPSRROperations&)aSmsMessage.GetOperationsForNonIEL(ESmsTPSRRParameter)).GetStatusReport(aSegmentSequenceNumber);
		
		if(tpsrr == TSmsFirstOctet::ESmsStatusReportNotRequested)
			{
			refStatusArray.InsertL(TSmsSegmentationStoreRefStatus(aReference, EStatusComplete));
			}
		else if(tpsrr == TSmsFirstOctet::ESmsStatusReportRequested)
			{
			refStatusArray.InsertL(aReference);	
			}	
		}
	else
		{
		User::Leave(KErrArgument);
		}
	

	BeginTransactionLC();
	ExternalizeEntryL(streamid,smsaddr,*smsmessage, refStatusArray);

	PopulateEntry(entry,/*smsaddr,*/*smsmessage, refStatusArray);
	ChangeEntryL(i,entry);
	CommitTransactionL();

	//
	// AEH: moved here because if is done before calling ChangeEntryL
	//      it will pop and destroy the filestore which is also on
	//      the cleanup stack
	//
	CleanupStack::PopAndDestroy(2);  //  smsmessage, refStatus

	return entry.Total()==entry.Count();
	} // CSmsSegmentationStore::AddReferenceStatusPairL


TBool CSmsSegmentationStore::AddStatusReportL(TInt& aIndex,TBool& aComplete,const CSmsMessage& aStatusReport)
	{
	LOGSMSPROT1("CSmsSegmentationStore::AddStatusReportL");

	__ASSERT_DEBUG(aStatusReport.Type()==CSmsPDU::ESmsStatusReport,SmspPanic(KSmspPanicNotStatusReport));

	const CSmsStatusReport& statusreport=(CSmsStatusReport&) aStatusReport.SmsPDU();
	const TInt reference=statusreport.MessageReference();
	const TInt status=statusreport.Status();
	const TInt isPerm = IsPermanentStatus(status);
	const TSmsFirstOctet::TSmsStatusReportQualifier qualifier=statusreport.StatusReportQualifier();
	TBool found=EFalse;
	aComplete=EFalse;

	LOGSMSPROT4("CSmsSegmentationStore::AddStatusReportL [ref=%d status=%d IsPerm=%d]", reference, status, isPerm);

	if(!isPerm)
		{
		return EFalse;
		}

	RSmsSegmentationStoreRefStatusArray refStatusArray;
	CleanupClosePushL(refStatusArray);

	const TInt count1=Entries().Count();

	TSmsAddr smsaddr;
	CSmsBuffer* buffer=CSmsBuffer::NewL();
	CSmsMessage* smsmessage=CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver,buffer);
	CleanupStack::PushL(smsmessage);
	TGsmSmsTelNumber parsedaddress;
	aStatusReport.ParsedToFromAddress(parsedaddress);

	BeginTransactionLC();

	aIndex = count1;

	TInt telLen;
	while (!found && aIndex--)
		{
        TSAREntry tsareenty = Entries()[aIndex]; 
        TSmsSegmentationEntry& entry = static_cast<TSmsSegmentationEntry&>(tsareenty);  

		// Remove leading zeros of national numbers
		TPtrC trimmedTelNumber(TrimLeadingZeros(entry.Description2()));
		TPtrC trimmedParsedTelNumber(TrimLeadingZeros(parsedaddress.iTelNumber));

		telLen=Min(trimmedTelNumber.Length(),trimmedParsedTelNumber.Length());

		const CSmsPDU::TSmsPDUType type = entry.PduType();
		const TInt startref = entry.Reference1();
		const TInt stopref = entry.Reference2();

		TBool sameTelNumbers = entry.Description2().Right(telLen) == parsedaddress.iTelNumber.Right(telLen);
		
		if (sameTelNumbers)
			{
			LOGSMSPROT1("CSmsSegmentationStore::AddStatusReportL telNumber from submit report matches that from SMS message");
			}
		else
			{
			LOGSMSPROT1("CSmsSegmentationStore::AddStatusReportL telNumber from submit report does NOT match that from SMS message");
			}

		if (sameTelNumbers &&
		  (((qualifier==TSmsFirstOctet::ESmsStatusReportResultOfCommand) && (type==CSmsPDU::ESmsCommand)) ||
		   ((qualifier==TSmsFirstOctet::ESmsStatusReportResultOfSubmit) &&  (type==CSmsPDU::ESmsSubmit))) &&
		   (((stopref>=startref) &&(reference>=startref) && (reference<=stopref))||((stopref<startref) &&((reference>=startref) || (reference<=stopref))))
		   )
			{
			InternalizeEntryL(entry.DataStreamId(),smsaddr,*smsmessage, refStatusArray);
			TInt refStatusPos = refStatusArray.Find(reference); //assumes Find returns the first matching reference in the array
			TInt numMessagePDUs=entry.Total();

			if (refStatusPos != KErrNotFound)
				{
				const TInt refStatusArrayCount = refStatusArray.Count();

				//Find an element in refStatusArray where Reference() == reference and Status() is not permanent
				while (!found && refStatusPos < refStatusArrayCount && refStatusArray[refStatusPos].Reference() == reference)
					{
					//@note This loop assumes refStatusArray is sorted iReference
					if (!IsPermanentStatus(refStatusArray[refStatusPos].Status())&&(refStatusArrayCount <= numMessagePDUs))
						{
						found = ETrue;
						}
					else
						{
						LOGSMSPROT4("CSmsSegmentationStore::AddStatusReportL WARNING: Status already perm [status=%d refStatusPos=%d count=%d]", refStatusArray[refStatusPos].Status(), refStatusPos, refStatusArrayCount);
						refStatusPos++;
						}
					}

				if (found)
					{
					LOGSMSPROT2("CSmsSegmentationStore::AddStatusReportL Found [refStatusPos=%d]", refStatusPos);
					refStatusArray[refStatusPos].SetStatus(status);
					TStreamId streamid=entry.DataStreamId();
					ExternalizeEntryL(streamid,smsaddr,*smsmessage, refStatusArray);
					PopulateEntry(entry,*smsmessage, refStatusArray);
					ChangeEntryL(aIndex,entry);
					aComplete=StatusArrayComplete(refStatusArray, entry);
				    LOGSMSPROT2("CSmsSegmentationStore::AddStatusReportL StatusArrayComplete %d", aComplete);
					}
				}
			}
		}

	if (found && (smsmessage->Type()==CSmsPDU::ESmsCommand))  // look for original submit
		{
		TTime time=smsmessage->Time();
		found=EFalse;

		RSmsSegmentationStoreRefStatusArray refStatusArray2;
		CleanupClosePushL(refStatusArray2);
		refStatusArray2.CopyL(refStatusArray);
		refStatusArray2.ResetAllStatus();

		aComplete=EFalse;
		TInt telLen;
		for (aIndex=0; aIndex<count1; aIndex++)
			{
            TSAREntry tsareenty = Entries()[aIndex]; 
	        TSmsSegmentationEntry& entry = static_cast<TSmsSegmentationEntry&>(tsareenty);  
			telLen=Min(entry.Description2().Length(),parsedaddress.iTelNumber.Length());
			const CSmsPDU::TSmsPDUType type = entry.PduType();
			if ((type==CSmsPDU::ESmsSubmit) &&
				 entry.IsComplete() &&
			    (entry.Time()==time) &&
			    (entry.Description2().Right(telLen)==parsedaddress.iTelNumber.Right(telLen)))
				{

				/*
				TODO ahe - this should take out of the for loop
				only set/delete flags in the for loop and let the methods:
				InternalizeXXX
				ExternalizeXXX
				PopulateEntry
				ChangeEntry
				... all ... other big CSarStore methods
				have only to run once -> internal loop in this function through
				the array of flags
				*/

				found=ETrue;
				InternalizeEntryL(entry.DataStreamId(),smsaddr,*smsmessage, refStatusArray2);
				const TInt count2 = refStatusArray.Count();
				__ASSERT_DEBUG(count2 == refStatusArray2.Count(),SmspPanic(KSmspPanicBadReferenceArray));
				for (TInt i=0; i<count2; i++)
					{
					TSmsSegmentationStoreRefStatus& refStatus2 = refStatusArray2[i];
					if (!IsPermanentStatus(refStatus2.Status()))
						{
						refStatus2.SetStatus(refStatusArray[i].Status());
						}
					}

				TStreamId streamid=entry.DataStreamId();
				ExternalizeEntryL(streamid,smsaddr,*smsmessage, refStatusArray2);
				PopulateEntry(entry,/*smsaddr,*/*smsmessage, refStatusArray2);
				ChangeEntryL(aIndex,entry);
				aComplete=StatusArrayComplete(refStatusArray2, entry);
			    LOGSMSPROT3("CSmsSegmentationStore::StatusArrayComplete [aStatus=%d, ret=%d]", status, aComplete);
				break;
				}
			}

		CleanupStack::PopAndDestroy(&refStatusArray2);
		}

	CommitTransactionL();
	CleanupStack::PopAndDestroy(2); // smsmessage, refStatusArray

	LOGSMSPROT2("CSmsSegmentationStore::AddStatusReportL Exit [found=%d]", found);
	return found;
	} // CSmsSegmentationStore::AddStatusReportL


void CSmsSegmentationStore::GetMessageL(TInt aIndex,TSmsAddr& aSmsAddr,CSmsMessage& aSmsMessage, RSmsSegmentationStoreRefStatusArray& aRefStatusArray)
	{
	LOGSMSPROT2("CSmsSegmentationStore::GetMessageL [aIndex=%d]", aIndex);

	InternalizeEntryL(Entries()[aIndex].DataStreamId(),aSmsAddr,aSmsMessage, aRefStatusArray);
	} // CSmsSegmentationStore::GetMessageL


/**
 *  internalize the concat refs from the permanent file store to internal memory
 *  
 *  @note You have to call CSARStore::OpenFileLC() before calling this function
 *  and CSARStore::CloseFile() after.
 */
void CSmsSegmentationStore::InternalizeConcatenationReferencesL(const TStreamId& aStreamId,TInt& aReference8bit,TInt& aReference16bit)
	{
	LOGSMSPROT1("CSmsSegmentationStore::InternalizeConcatenationReferencesL Start");
	RStoreReadStream readstream;
	readstream.OpenLC(FileStore(),aStreamId);
	aReference8bit=readstream.ReadInt32L();
	aReference16bit=readstream.ReadInt32L();
	CleanupStack::PopAndDestroy();
	LOGSMSPROT1("CSmsSegmentationStore::InternalizeConcatenationReferencesL End");
	} // CSmsSegmentationStore::InternalizeConcatenationReferencesL


/**
 *  externalize the concat refs from the permanent file store to internal memory
 *  
 *  @note You have to call CSARStore::OpenFileLC() before calling this function
 *  and CSARStore::CloseFile() after.
 */
void CSmsSegmentationStore::ExternalizeConcatenationReferencesL(TStreamId& aStreamId,TInt aReference8bit,TInt aReference16bit)
	{
	LOGSMSPROT1("CSmsSegmentationStore::ExternalizeConcatenationReferencesL Start");
	RStoreWriteStream writestream;
	if (aStreamId==KNullStreamId)
		aStreamId=writestream.CreateLC(FileStore());
	else
		writestream.ReplaceLC(FileStore(),aStreamId);
	writestream.WriteInt32L(aReference8bit);
	writestream.WriteInt32L(aReference16bit);
	writestream.CommitL();
	CleanupStack::PopAndDestroy();
	LOGSMSPROT1("CSmsSegmentationStore::ExternalizeConcatenationReferencesL End");
	} // CSmsSegmentationStore::ExternalizeConcatenationReferencesL


/**
 *  internalize all the entries from the permanent file store to internal memory
 *  
 *  @note You have to call CSARStore::OpenFileLC() before calling this function
 *  and CSARStore::CloseFile() after.
 */
void CSmsSegmentationStore::InternalizeEntryL(const TStreamId& aStreamId,TSmsAddr& aSmsAddr,CSmsMessage& aSmsMessage, RSmsSegmentationStoreRefStatusArray& aRefStatusArray)
	{
	LOGSMSPROT1("CSmsSegmentationStore::InternalizeEntryL Start");

	aRefStatusArray.Reset();

	RStoreReadStream readstream;
	readstream.OpenLC(FileStore(),aStreamId);
	readstream >> aSmsAddr;
	readstream >> aSmsMessage;
	readstream >> aRefStatusArray;
	CleanupStack::PopAndDestroy(&readstream);

	LOGSMSPROT2("CSmsSegmentationStore::InternalizeEntryL End [count=%d]", aRefStatusArray.Count());
	} // CSmsSegmentationStore::InternalizeEntryL


/**
 *  externalizes all the entries from the internal memory to the permanent file store
 */
void CSmsSegmentationStore::ExternalizeEntryL(TStreamId& aStreamId,const TSmsAddr& aSmsAddr,const CSmsMessage& aSmsMessage, const RSmsSegmentationStoreRefStatusArray& aRefStatusArray)
	{
	LOGSMSPROT1("CSmsSegmentationStore::ExternalizeEntryL Start");

	RStoreWriteStream writestream;

	if (aStreamId==KNullStreamId)
		aStreamId=writestream.CreateLC(FileStore());
	else
		writestream.ReplaceLC(FileStore(),aStreamId);

	writestream << aSmsAddr;
	writestream << aSmsMessage;
	writestream << aRefStatusArray;
	writestream.CommitL();
	CleanupStack::PopAndDestroy(&writestream);

	LOGSMSPROT2("CSmsSegmentationStore::ExternalizeEntryL End [count=%d]", aRefStatusArray.Count());
	} // CSmsSegmentationStore::ExternalizeEntryL


/**
 *  Populates an SMS message into SAR store entry
 *  
 *  @pre aSmsMessage.EncodeMessagePdusL() has been called
 *  
 *  @param aEntry          Entry to be populated to
 *  @param aSmsMessage     SMS message to be populated from
 *  @param aReferenceArray Array containing references
 *  @param aStatusArray    Array containing status
 */
void CSmsSegmentationStore::PopulateEntry(TSmsSegmentationEntry& aEntry,
					  const CSmsMessage& aSmsMessage,
					  const RSmsSegmentationStoreRefStatusArray& aRefStatusArray)
	{
	LOGSMSPROT1("CSmsSegmentationStore::PopulateEntry");
	TBool statusreportrequest=EFalse;
	if (aSmsMessage.Type()==CSmsPDU::ESmsSubmit)
		{
		aEntry.SetReference(0);
		aEntry.SetTotal(1);
		CSmsSubmit& submit=(CSmsSubmit&) aSmsMessage.SmsPDU();
		aEntry.SetValidityPeriod(submit.ValidityPeriod().Int());
		
		if (aSmsMessage.Scheme() == EDefaultScheme)
		    {
	    	statusreportrequest=((CSmsSubmit&) aSmsMessage.SmsPDU()).StatusReportRequest();		    
		    }
		else
		    {
		    statusreportrequest = ETrue;    
		    }
		}
	else
		{
		statusreportrequest=((CSmsCommand&) aSmsMessage.SmsPDU()).StatusReportRequest();
		}

	if (aSmsMessage.TextPresent())
		{
		if (aSmsMessage.SmsPDU().TextConcatenated())
			{
			aEntry.SetReference(aSmsMessage.SmsPDU().ConcatenatedMessageReference());

			//
			// aSmsMessage.EncodeMessagePdusL() must have been called before this point,
			// otherwise aSmsMessage.SmsPDU().NumConcatenatedMessagePDUs() will return 1.
			//
			aEntry.SetTotal(aSmsMessage.SmsPDU().NumConcatenatedMessagePDUs());
			}
		}

	aEntry.SetLogServerId(aSmsMessage.LogServerId());
	//  Strip out spaces etc from address
	TGsmSmsTelNumber parsedaddress;
	aSmsMessage.ParsedToFromAddress(parsedaddress);
	aEntry.SetDescription2(parsedaddress.iTelNumber);
	aEntry.SetTime(aSmsMessage.Time());

	const TInt count= aRefStatusArray.Count();
	__ASSERT_DEBUG((count>=0) && (count<=aEntry.Total()),SmspPanic(KSmspPanicBadReferenceArray));
	aEntry.SetCount(count);
	TInt reference1=0xFF;
	TInt reference2=0x00;
	TInt startref=reference1;
	TInt stopref=reference2;

	if(count>0 && statusreportrequest)
	{
		startref=aRefStatusArray[0].Reference();
		stopref=aRefStatusArray[count-1].Reference();
	}

	TInt delivered=0;
	TInt failed=0;
	for (TInt i=0; i<count; i++)
		{
		const TSmsSegmentationStoreRefStatus& refStatus = aRefStatusArray[i];

		if (refStatus.Status() == TSmsStatus::ESmsShortMessageReceivedBySME)
			delivered++;
		else if (IsPermanentStatus(refStatus.Status()))
			failed++;
		}

	//
	// AEH: Defect fix for EDNPAHN-4WADW3 'Unreliable logging'
	//
	//      a little hack here to store information about whether
	//      we need Status Report or not, in the TSAREntry. This
	//      is because we want to retrieve it later in PurgeL.
	//      An extra bit is added iData4, LSB of byte 3. See gsmustor.h
	//      for more documentation.
	//
	aEntry.SetDeliveredAndFailed(delivered, failed);	
	TBool have_sr = EFalse;

	if (aSmsMessage.Scheme() == EDefaultScheme)
		    {
	    	have_sr=((CSmsSubmit&) aSmsMessage.SmsPDU()).StatusReportRequest();		    
		    }
	else
		    {
		    have_sr = ETrue;    
		    }

	aEntry.SetPduTypeAndRefs(have_sr, aSmsMessage.Type(), startref, stopref);
	} // CSmsSegmentationStore::PopulateEntry


/**
 *  Returns ETrue if the status array is complete
 *  
 *  @param aStatusArray    Array containing status
 *  @param aEntry          SAR Entry
 */
TBool CSmsSegmentationStore::StatusArrayComplete(const RSmsSegmentationStoreRefStatusArray& aRefStatusArray, TSAREntry& aEntry)
	{
	TInt permanent=0;
	const TInt count= aRefStatusArray.Count();
	for (TInt i=0; i<count; i++)
		{
		const TBool ret = IsPermanentStatus(aRefStatusArray[i].Status());
		LOGSMSPROT4("CSmsSegmentationStore::IsPermanentStatus [Status: %d, RetVal: %d, count=%d]", aRefStatusArray[i].Status(), ret, count);
		if (ret)
			permanent++;
		}
	return (permanent==count) && (permanent==aEntry.Total() );
	} // CSmsSegmentationStore::StatusArrayComplete


  /**
   *  C'tor
   */
CSmsSegmentationStore::CSmsSegmentationStore(RFs& aFs)
    :CSARStore(aFs)
    {
    LOGSMSPROT1("CSmsSegmentationStore::CSmsSegmentationStore()");

    } // CSmsSegmentationStore::CSmsSegmentationStore


TInt CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::Compare(const TSmsSegmentationStoreRefStatus& aLeft, const TSmsSegmentationStoreRefStatus& aRight)
	{
	LOGSMSPROT1("CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::Compare()");

	return aLeft.iReference - aRight.iReference;
	} // CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::Compare


void CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::InternalizeL(RReadStream& aStream)
	{
	LOGSMSPROT1("CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::InternalizeL()");

	iReference = aStream.ReadInt32L();
	iStatus = aStream.ReadInt32L();
	} // CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::InternalizeL


void CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::ExternalizeL(RWriteStream& aStream) const
	{
	LOGSMSPROT1("CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::ExternalizeL()");

	aStream.WriteInt32L(iReference);
	aStream.WriteInt32L(iStatus);
	} // CSmsSegmentationStore::TSmsSegmentationStoreRefStatus::ExternalizeL


void CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::InsertL(const TSmsSegmentationStoreRefStatus& aRefStatus)
	{
	LOGSMSPROT1("CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::InsertL()");

	TLinearOrder<TSmsSegmentationStoreRefStatus> order(TSmsSegmentationStoreRefStatus::Compare);
	User::LeaveIfError(InsertInOrderAllowRepeats(aRefStatus, order));
	} // CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::InsertL


TInt CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::Find(const TSmsSegmentationStoreRefStatus& aRefStatus) const
	{
	LOGSMSPROT1("CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::Find()");

	TLinearOrder<TSmsSegmentationStoreRefStatus> order(TSmsSegmentationStoreRefStatus::Compare);
	TInt index = FindInOrder(aRefStatus, order);
	if (index != KErrNotFound)
		{
        //The  function is to return  the first occurence.  However FindInOrder()
        //uses a binary search algorithm and does not guarantee to return the 1st item if there are duplicate items.
        //Therefore we manually check for duplicates to the left of the found item.
        while (index > 0 && (operator[](index-1).Reference() == aRefStatus.Reference()))
			{
			--index;
			}
		}
	return index;
	} // CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::Find


void CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::CopyL(const RSmsSegmentationStoreRefStatusArray& aOther)
	{
	LOGSMSPROT1("CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::CopyL()");

	Reset();

	TInt count = aOther.Count();
	while (count--)
		{
		InsertL(aOther[count]);
		}
	} // CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::CopyL


void CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::ResetAllStatus(TInt aStatus)
	{
	LOGSMSPROT1("CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::ResetAllStatus()");

	TInt count = Count();
	while (count--)
		{
		(*this)[count].SetStatus(aStatus);
		}
	} // CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::ResetAllStatus


void CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::InternalizeL(RReadStream& aStream)
	{
	LOGSMSPROT1("CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::InternalizeL()");

	TInt count = aStream.ReadInt32L();
	while (count--)
		{
		TSmsSegmentationStoreRefStatus refStatus;
		aStream >> refStatus;
		InsertL(refStatus); //maintain order
		}
	} // CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::InternalizeL


void CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::ExternalizeL(RWriteStream& aStream) const
	{
	LOGSMSPROT1("CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::ExternalizeL()");

	const TInt count = Count();
	aStream.WriteInt32L(count);

	for (TInt i = 0; i < count; i++)
		{
		aStream << (*this)[i];
		}
	} // CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::ExternalizeL


TBool CSmsSegmentationStore::HasEntryWithLogIdL(TLogId aLogID,TInt& aRefNo,TInt& aSent)
	{
	LOGSMSPROT1("CSmsSegmentationStore::HasEntryWithLogIdL()");

	TInt count=Entries().Count();
	TBool found=EFalse;
	if(aLogID != KLogNullId)
		{
		TInt total;
		TInt sent;
		BeginTransactionLC();
		for (TInt i=count-1; i>=0; --i)
			{
			if (aLogID==Entries()[i].LogServerId())
				{
				const TSAREntry& entry=Entries()[i];
				total=entry.Total();
				sent=entry.Count();
				if( sent < total)
					{
					aSent=sent;
					aRefNo=entry.Reference();
					found=ETrue;
					}
				else
					{
					DeleteEntryL(i);
					LOGSMSPROT3("CSmsSegmentationStore::HasEntryWithLogIdL [Entry: %d LogId %d - deleted]", i, aLogID );
					}
				break;
				}
			}
		CommitTransactionL();
		}
	return found;
} // CSmsSegmentationStore::HasEntryWithLogIdL


/**
 *  Open the sms segmentation store.
 */
void CSmsSegmentationStore::OpenStoreL()
	{
	LOGSMSPROT1("CSmsSegmentationStore::OpenStoreL()");

	this->OpenL(iFullPathBuf,KSegmentationStoreUid);
	} // CSmsSegmentationStore::OpenStoreL
