smsprotocols/smsstack/smsprot/Src/smspstor.cpp
changeset 0 3553901f7fa8
child 5 7ef16719d8cb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/smsprotocols/smsstack/smsprot/Src/smspstor.cpp	Tue Feb 02 01:41:59 2010 +0200
@@ -0,0 +1,1609 @@
+// 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)
+//TODO CommentThisFunction
+	{
+	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();
+
+	//TODO AA: What is it doing here? Please comment
+	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);
+				}
+
+			//TODO What is happening here? Seems strange
+			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)
+	{
+	TSmsSegmentationEntry entry; // TODO const and inside loop
+	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++)
+			{
+			entry = (TSmsSegmentationEntry&)Entries()[i];
+			if (logserverid==entry.LogServerId())
+				break;
+			}
+		}
+	else
+		{
+		TGsmSmsTelNumber parsedaddress;
+		aSmsMessage.ParsedToFromAddress(parsedaddress);
+		TInt telLen;
+		for (i=0; i<count; i++)
+			{
+			entry = (TSmsSegmentationEntry&)Entries()[i];
+			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;
+			}
+		}
+//	__ASSERT_DEBUG(i<count,SmspPanic(KSmspPanicEntryWithLogServerIdNotFound)); TODO
+	if(i>=count)
+		{
+		LOGSMSPROT3("WARNING! KSmspPanicEntryWithLogServerIdNotFound [i=%d, count=%d]", i, count);
+		}
+
+	RSmsSegmentationStoreRefStatusArray refStatusArray;
+	CleanupClosePushL(refStatusArray);
+
+	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)
+ 	{
+	TSmsSegmentationEntry entry; // TODO const and inside loop
+	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++)
+			{
+			entry = (TSmsSegmentationEntry&)Entries()[i];
+			if (logserverid==entry.LogServerId())
+				break;
+			}
+		}
+	else
+		{
+		TGsmSmsTelNumber parsedaddress;
+		aSmsMessage.ParsedToFromAddress(parsedaddress);
+		TInt telLen;
+		for (i=0; i<count; i++)
+			{
+			entry = (TSmsSegmentationEntry&)Entries()[i];
+			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;
+			}
+		}
+//	__ASSERT_DEBUG(i<count,SmspPanic(KSmspPanicEntryWithLogServerIdNotFound)); TODO
+	if(i>=count)
+		{
+		LOGSMSPROT3("WARNING! KSmspPanicEntryWithLogServerIdNotFound [i=%d, count=%d]", i, count);
+		}
+
+	RSmsSegmentationStoreRefStatusArray refStatusArray;
+	CleanupClosePushL(refStatusArray);
+
+	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);
+	TSmsSegmentationEntry entry; // TODO const ref and inside loop
+
+	BeginTransactionLC();
+
+	aIndex = count1;
+
+	TInt telLen;
+	while (!found && aIndex--)
+		{
+		entry = (TSmsSegmentationEntry&)Entries()[aIndex];
+
+		// 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++)
+			{
+			entry = (TSmsSegmentationEntry&)Entries()[aIndex];
+			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++)
+					{
+					//TODO What is this doing?
+					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()); // TODO use val per type
+		
+		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++;
+		}
+	/*
+	 *  
+	 *  TODO ahe - for release
+	 *  tested hack: the messagereceived function will be called right
+	 *  I did a lot of testing with multipart messages, the sms are
+	 *  almost always received and sent now, there might be only problems
+	 *  with the SR now - to wait for more logs to see what happens in this
+	 *  special cases - and the device crashes and is too slow of course
+	 *  
+	 */
+	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