diff -r 000000000000 -r 3553901f7fa8 smsprotocols/smsstack/smsprot/Src/smspstor.cpp --- /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 +#include +#include "smspstor.h" +#include "smspmain.h" +#include "smsuaddr.H" +#include "Gsmumsg.h" +#include "gsmubuf.h" +#include +#include +#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(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(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* indexArray = new(ELeave) CArrayFixFlat(8); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(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* indexarray=new(ELeave) CArrayFixFlat(8); + CleanupStack::PushL(indexarray); + CArrayFixFlat* smsarray=new(ELeave) CArrayFixFlat(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* indexarray=new(ELeave) CArrayFixFlat(8); + CleanupStack::PushL(indexarray); + CArrayFixFlat* smsarray=new(ELeave) CArrayFixFlat(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& aIndexArray,CArrayFix& 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> 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& aIndexArray,const CArrayFix& 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 (; iidentifier2) + 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& aIndexArray,const CArrayFix& 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(Entries()[ aIndex ]); + return entry.PassedToClient(); + } // CSmsReassemblyStore::PassedToClient + + +void CSmsReassemblyStore::SetPassedToClientL(TInt aIndex, TBool aPassed) +//TODO CommentThisFunction + { + LOGSMSPROT1("CSmsReassemblyStore::SetPassedToClientL()"); + + TSmsReassemblyEntry entry(reinterpret_cast(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; jSmsPDU(); + 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) + { + 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) + { + 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<=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 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> 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 order(TSmsSegmentationStoreRefStatus::Compare); + User::LeaveIfError(InsertInOrderAllowRepeats(aRefStatus, order)); + } // CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::InsertL + + +TInt CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::Find(const TSmsSegmentationStoreRefStatus& aRefStatus) const + { + LOGSMSPROT1("CSmsSegmentationStore::RSmsSegmentationStoreRefStatusArray::Find()"); + + TLinearOrder 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