messagingfw/biomsgfw/BioWatchers/Src/NBSSocketWatcher.cpp
changeset 0 8e480a14352b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/biomsgfw/BioWatchers/Src/NBSSocketWatcher.cpp	Mon Jan 18 20:36:02 2010 +0200
@@ -0,0 +1,812 @@
+// 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:
+// NBSocketWatcher.cpp
+// 
+//
+
+#include "NBSSocketWatcher.h"
+#include "SmsSocketWatcher.h"
+
+#include <smuthdr.h>
+#include <msventry.h>
+#include <msvstd.h>
+#include <msvuids.h>
+#include <smut.h>
+#include <watcher.h>
+#include <smsuaddr.h>
+#include <gsmuelem.h>
+#include <gsmunonieoperations.h>
+#include <s32strm.h>
+#include <s32mem.h>
+#include <gsmubuf.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS  
+#include <tmsvsmsentry.h>
+#include "cwatcher.h"			
+#endif
+
+const TInt KSmsWRetryTimer = 1000000 * 15; // 15 Secs
+
+/*
+ * CNbsSocketWatcher
+ */
+
+CNbsSmsSocketWatcher::CNbsSmsSocketWatcher(CWatcherLog& aWatcherLog, TInt aPriority, TUid aBioID, RFs& aFs, const TDesC& aNbsPattern, TSmsAddrFamily aAddrFamily)
+:	CBaseSmsActiveSocketWatcher(aWatcherLog, aPriority, aBioID, aFs),
+	iAddrFamily(aAddrFamily),
+	iNbsPattern(aNbsPattern)
+	{
+	}
+
+CNbsSmsSocketWatcher::~CNbsSmsSocketWatcher()
+	{
+	Cancel();
+
+	if (iSocket.SubSessionHandle())
+		iSocket.Close();
+
+	if (iSocketServer.Handle())
+		iSocketServer.Close();
+	
+	//destroying class 0 SMS E-Com pluin implementation 
+	if (iSmsClass0 != NULL)
+		{
+		REComSession::DestroyedImplementation(iDtor_ID_Key);
+		REComSession::FinalClose();
+		delete iSmsClass0;
+		}
+	RProperty::Delete(KUidSystemCategory, KUidPSDiskSpaceMonitorKeyType);
+	}
+
+CNbsSmsSocketWatcher* CNbsSmsSocketWatcher::NewLC(TMsvId aBioServiceId, 
+									   			  TMsvId aSmsServiceId, 
+									   			  CBIODatabase& aBioDb, 
+									   			  CWatcherLog& aWatcherLog, 
+									   			  TInt aPriority, 
+									   			  TUid aBioID, 
+									   			  RFs& aFs, 
+									   			  const TDesC& aNbsPattern, 
+									   			  TSmsAddrFamily aAddrFamily /*= ESmsAddrMatchText*/)
+	{
+	CNbsSmsSocketWatcher* self = new(ELeave) CNbsSmsSocketWatcher(aWatcherLog, aPriority, aBioID, aFs, aNbsPattern, aAddrFamily);
+	CleanupStack::PushL(self);
+	self->ConstructL(aBioServiceId, aSmsServiceId, aBioDb);
+	return self;	
+	}
+
+
+void CNbsSmsSocketWatcher::ConstructL(TMsvId aBioServiceId, TMsvId aSmsServiceId, CBIODatabase& aBioDb)
+	{
+	CBaseSmsActiveSocketWatcher::ConstructL(aBioDb, aBioServiceId, aSmsServiceId);
+	User::LeaveIfError(iSocketServer.Connect());
+	TSecurityPolicy  readPolicy(ECapabilityReadDeviceData);
+	TSecurityPolicy  writePolicy(ECapabilityWriteDeviceData);
+
+ 
+	TInt ret = RProperty::Define(KUidSystemCategory, KUidPSDiskSpaceMonitorKeyType,
+  	  	 				  RProperty::EInt, readPolicy, writePolicy);
+ 
+	BIOWATCHERLOG(iWatcherLog.Printf(_L(" Define return value %d"), ret));
+ 
+	if (ret != KErrNone  &&  ret != KErrAlreadyExists)
+  		{
+  		User::Leave(ret);
+  		}   
+  		 
+	TInt setRet = RProperty::Set(KUidSystemCategory, KUidPSDiskSpaceMonitorKeyType, ESmsDiskSpaceAvailable);
+ 
+	BIOWATCHERLOG(iWatcherLog.Printf(_L(" Set value %d"), setRet));
+ 
+	if (setRet != KErrNone)
+  		{
+  		User::Leave(setRet);
+  		}
+	}
+
+void CNbsSmsSocketWatcher::DoCancel()
+	{
+	BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: DoCancel")));
+	if (iIoCtlActive)
+		{
+		iSocket.CancelIoctl();
+		}
+
+	if(iTimer.Handle() != NULL)
+		iTimer.Cancel();
+
+	Complete(KErrCancel);
+	}
+
+void CNbsSmsSocketWatcher::DoRunL()
+	{
+	iIoCtlActive = EFalse;
+	switch(iState)
+		{
+		case ESmsWWaitForMsg:
+			iState = ESmsWReadMsg;
+			WaitForMessageL();
+			break;
+		
+		case ESmsWReadMsg:
+			iState = ESmsWWaitForMsg;
+			ReceiveL();
+			break;
+
+		case ESmsWRetryError:
+			if (!iTimer.Handle())
+				{
+				User::LeaveIfError(iTimer.CreateLocal());
+				}
+				
+			iState=ESmsWWaitForMsg;
+			iTimer.After(iStatus, KSmsWRetryTimer);
+			break;
+			
+		case ESmsIsClass0Msg:
+			iState = ESmsSupportClass0Msg;
+			CheckForClass0SmsSupport();
+			break;
+		
+		case ESmsSupportClass0Msg:
+			iState = ESmsWWaitForMsg;
+			SupportForClass0SmsL();
+			break;
+		}
+
+	SetActive();
+
+	}
+
+void CNbsSmsSocketWatcher::DoSetupL()
+	{
+	BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: DoSetupL: %S"), &iBioMsgText));
+
+	// Set up the Socket to watch the port
+
+	// test if it's there... 
+	TProtocolDesc protoInfo;
+	TProtocolName protocolname;
+
+	BIOWATCHERLOG(iWatcherLog.Printf(_L8("BioNbs: DoSetupL: Finding protocol")));
+
+	protocolname.Copy(KSmsDatagram);
+	User::LeaveIfError(iSocketServer.FindProtocol(protocolname,protoInfo));
+
+	BIOWATCHERLOG(iWatcherLog.Printf(_L8("BioNbs: DoSetupL: Opening socket")));
+
+	// Create the RSocket
+	User::LeaveIfError(iSocket.Open(iSocketServer,
+										protoInfo.iAddrFamily,
+										protoInfo.iSockType, 
+										protoInfo.iProtocol));
+	
+	// Set up the Socket to watch the pattern
+	if (iBioMsgUID != KNullUid && iAddrFamily == ESmsAddrMatchText)
+	{
+		// need an 8 bit pattern
+		TBuf8<KMaxBioIdText> pattern; 
+		pattern.Copy(iNbsPattern);
+
+		iSmsAddr.SetSmsAddrFamily(ESmsAddrMatchText);
+		iSmsAddr.SetTextMatch(pattern);
+	}
+	else
+	{
+		// Binding to a non-patter address family
+		// like StatusReports, Special Messages, and RecieveAny
+		iSmsAddr.SetSmsAddrFamily(iAddrFamily);
+	}
+	
+	BIOWATCHERLOG(iWatcherLog.Printf(_L8("BioNbs: DoSetupL: Binding socket")));
+
+	User::LeaveIfError(iSocket.Bind(iSmsAddr));
+	}
+
+void CNbsSmsSocketWatcher::DoComplete(TInt& rStatus)
+	{	
+	BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: DoComplete: %S, status %d"), &iBioMsgText, rStatus));
+
+	if (iSocket.SubSessionHandle())
+		{
+		iSocket.Close();
+		}
+	}
+
+void CNbsSmsSocketWatcher::WaitForMessageL()
+	{
+	BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: WaitForMessageL: %S"), &iBioMsgText));
+	iOctlRes()= KSockSelectRead;
+	iIoCtlActive = ETrue;
+	iSocket.Ioctl(KIOctlSelect, iStatus, &iOctlRes, KSOLSocket);
+	}
+	
+// CNbsSmsSocketWatcher::CheckForClass0SmsSupport to find out whether SMS stack is 
+// configured for this new behaviour of class 0 message or not.
+void CNbsSmsSocketWatcher::CheckForClass0SmsSupport()
+	{
+	// Need verify SMS stack configuration, if type of socket address is ESmsAddrRecvAny only
+	if (iAddrFamily == ESmsAddrRecvAny)
+		{
+		BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: CheckForClass0SmsSupport")));
+		iOctlRes()= KSockSelectRead;
+		iIoCtlActive = ETrue;
+		//iStatus will return KErrNone if stack supports new way of handling class 0 messages. 
+		//Otherwise it will return KErrNotSupported.
+		iSocket.Ioctl(KIoctlSupportOODClass0SmsMessages, iStatus, NULL, KSolSmsProv);
+		}
+	else
+		{
+		//Change state to Wait For Message
+		iState = ESmsWWaitForMsg;
+		//Complete self to enter state machine
+		TRequestStatus* status = &iStatus;
+		iStatus=KRequestPending;
+		User::RequestComplete(status, KErrNone);
+		}
+	}
+
+void CNbsSmsSocketWatcher::DoReceiveL()
+ 	{
+ 	// Get the Message
+ 	CSmsMessage* smsmessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, CSmsBuffer::NewL());
+ 	CleanupStack::PushL(smsmessage);
+ 			
+ 	RSmsSocketReadStream readstream(iSocket);
+ 	readstream >> *smsmessage; //can leave
+ 			
+ 	CleanupStack::Pop(smsmessage);
+ 	
+ 	//get the class type here
+	TSmsDataCodingScheme::TSmsClass classType(TSmsDataCodingScheme::ESmsClass2);
+	const CSmsDeliver& deliver = static_cast<const CSmsDeliver&>(smsmessage->SmsPDU());
+    TBool classDefined = deliver.Class(classType);
+    
+    //handling class 0 sms message 
+ 	if(iSmsClass0 != NULL && classType == TSmsDataCodingScheme::ESmsClass0)
+ 		{
+ 		//destroy the CSmsMessage, once passed succesfully to Class0SMS plugin
+ 		CleanupStack::PushL(smsmessage);
+ 		
+		//Externalize CSmsMessage into a TDesC8
+		CSmsBufferBase& convSmsMsg = smsmessage->Buffer();
+		TInt len = convSmsMsg.Length() ;
+	
+		//for sending Incomplete MessageInfo to UI.
+		TInt startPos = 0;
+		TInt endPos = 0;
+		TBool isLastMessage = EFalse;
+		
+		HBufC *buffer = HBufC::NewLC(len);
+		TPtr temp = buffer->Des();
+		convSmsMsg.Extract(temp, 0, len);
+		
+		//allocate memory for CBufFlat, length sholud be: length of Class0SMS data + 2*(size of TInt)+(size of TBool)
+		CBufFlat* flat = CBufFlat::NewL(len + (3 * sizeof(TInt)));
+		CleanupStack::PushL(flat);
+		flat->ExpandL(0, len);
+		
+		RBufWriteStream writeStream(*flat);
+		CleanupClosePushL (writeStream);
+			
+		if (smsmessage->IsComplete())
+			{
+			//if it's complete class 0 SMS message
+			//passing startPos, endPos and isLastMessage value to 0
+			ExternalizeL(writeStream, startPos, endPos, isLastMessage, temp);
+			TPtr8 smsMsg(flat->Ptr(0));
+			iSmsClass0->DisplayMessageHandler(smsMsg, ETrue);
+			}
+		else
+			{
+			//handling incomplete SMS Message
+			CIncompleteClass0MessageInfo  &incompleteMessageInfo = (CIncompleteClass0MessageInfo&)smsmessage->GetOperationsForNonIEL(ESmsIncompleteClass0MessageParameter);
+			TInt msgRefNo = smsmessage->SmsPDU().ConcatenatedMessageReference();
+			
+			//get the information about the messsage & externalize it..	
+			incompleteMessageInfo.GetIncompleteMessageInfoL(startPos, endPos, isLastMessage);
+			ExternalizeL(writeStream, startPos, endPos, isLastMessage, temp);
+			TPtr8 smsMsg(flat->Ptr(0));
+					
+			if (iMsgRefNumber == -1 || iMsgRefNumber == msgRefNo)
+				{
+				//if it's 1st part of the Message, assign msgRefNo to iMsgRefNumber  
+				if (iMsgRefNumber == -1)
+					{
+					iMsgRefNumber = msgRefNo;
+					}
+						
+				iSmsClass0->DisplayMessageHandler(smsMsg, EFalse);
+							
+				//if it's last part of the Message, assign iMsgRefNumber to -1  							
+				if (isLastMessage)
+					{
+					iMsgRefNumber = -1;	
+					}
+				}
+			}
+			//delete smsmessage, as owned by header
+			CleanupStack::PopAndDestroy(4, smsmessage);
+ 		} //handling class 0 sms message
+ 	
+ 	// If the message is delivered to an unknown port (as opposed to no port at all)
+ 	// then we should only store the message if it is a "TextMessage".
+ 	// Otherwise the message should be silently consumed without being delivered.
+ 	// This is a requirement of the Java "Mobile Service Architecture", JSR 248, section 6.11.3.2 	
+	
+ 	else if (	iAddrFamily == ESmsAddrRecvAny 			// << This watcher is listening on an unknown port
+ 			&& MessageHasPort(*smsmessage)			// << The message is addressed to a specific port
+ 			&& ! IsJavaTextMessage(*smsmessage) )	// << The message is defined by JSR 205 as NOT a TextMessage object
+ 		{
+ 		BIOWATCHERLOG(iWatcherLog.Printf(_L8("BioNbs: Non-text message delivered to unknown port - deleting message")));
+ 		}
+ 	else
+ 		{
+ 		StoreMsgL(smsmessage, ETrue); // Store the messaage and check for SID entry for ownership
+ 		}
+ 	}
+
+void CNbsSmsSocketWatcher::ReceiveL()
+	{
+	BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: ReceiveL: %S"), &iBioMsgText));
+	
+	TInt error;
+	
+	switch (iStatus.Int())
+		{
+		case KErrNone:
+			{
+			TRAP(error, DoReceiveL());
+			
+			// Tell the socket if we handled the message or not
+			if (error == KErrNone)
+				{
+				iIoCtlActive = ETrue;
+				iSocket.Ioctl(KIoctlReadMessageSucceeded, iStatus, NULL, KSolSmsProv);
+				}
+			else
+				{
+				BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: Recv fail %d: %S"), error, &iBioMsgText));
+ 				iState = ESmsWRetryError;
+				iIoCtlActive = ETrue;
+				iSocket.Ioctl(KIoctlReadMessageFailed, iStatus, NULL, KSolSmsProv);
+				}
+			}
+			break;
+			
+		case KIoctlSelectModemPresent:
+			// Enumerate to see it there are any messages when the phone's hooked up
+			// Only enumerate with default recievAny watcher
+			// this pattern watcher should do nothing
+			// SMS Prot should get the phone memory's SMS messages here
+		case KIoctlSelectModemNotPresent:
+			{
+			// Phone's disconnected ... wait in Ioctl again
+			// base class does this ... we're okay
+			TRequestStatus* pS=&iStatus;
+			User::RequestComplete(pS,0);
+			}			
+			break;
+
+		default:
+			User::Leave(iStatus.Int());
+		}
+	}
+
+/**
+Returns whether the message contains optional port addressing information.
+@return ETrue if message contains port addressing information, otherwise EFalse.
+*/
+TBool CNbsSmsSocketWatcher::MessageHasPort(const CSmsMessage& aSmsMessage)
+	{
+	// Declare variables that will receive their values in ApplicationPortAddressing()
+ 	TInt destinationPort=0;
+ 	TInt originatorPort=0;
+ 	TBool is16bit=EFalse;
+ 	TBool hasPortAddressing = aSmsMessage.SmsPDU().ApplicationPortAddressing(destinationPort, originatorPort, &is16bit);
+ 	
+ 	BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: MessageHasPort()"))); 	
+ 	BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs:  > has:%d, dest:%d, orig:%d, 16bit:%d)"), hasPortAddressing, destinationPort, originatorPort, is16bit));
+ 	
+ 	return hasPortAddressing;
+	}
+
+/**
+Check whether Discard Unknown Port message flag is set, if not check for Java wireless messaging.
+
+Returns whether the message is considered a TextMessage, according to Java wireless messaging
+specifications - JSR 205 "Wireless Messaging API" Appendix A.5.0 - which associates the TextMessage
+object with the 7-bit and 16-bit UCS-2 alphabets, and BinaryMessage with the 8-bit alphabet.
+@param aSmsMessage the message whose alphabet we are testing.
+@return ETrue if the alphabet is 7-bit or 16-bit UCS-2, otherwise EFalse.
+*/
+TBool CNbsSmsSocketWatcher::IsJavaTextMessage(const CSmsMessage& aSmsMessage)
+	{
+	TBool isTextMessage = EFalse;
+	if(!iSettings->DiscardUnknownPortMessage())
+		{
+		TSmsDataCodingScheme::TSmsAlphabet alphabet = aSmsMessage.SmsPDU().Alphabet();
+ 	
+		if (alphabet == TSmsDataCodingScheme::ESmsAlphabet7Bit || alphabet == TSmsDataCodingScheme::ESmsAlphabetUCS2)
+			{
+			isTextMessage = ETrue;
+			}
+
+			BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs: IsJavaTextMessage()")));
+	 		BIOWATCHERLOG(iWatcherLog.Printf(_L("BioNbs:  > isTextMessage:%d, alphabet:%d)"), isTextMessage, alphabet));
+		}
+	
+	return isTextMessage;
+	}	
+	
+/*
+ *	CSpecialNbsSmsSocketWatcher Implementation
+ */
+ 
+CSpecialNbsSmsSocketWatcher* CSpecialNbsSmsSocketWatcher::NewLC(
+															TMsvId 			aBioServiceId, 
+															TMsvId 			aSmsServiceId, 
+															CBIODatabase&	aBioDb, 
+															CWatcherLog&	aWatcherLog,
+															TInt			aPriority,
+															TUid			aBioID,
+															RFs&			aFs,
+															const TDesC&	aNbsPattern, 
+															TSmsAddrFamily	aAddrFamily)
+	{
+	CSpecialNbsSmsSocketWatcher* self = new (ELeave) CSpecialNbsSmsSocketWatcher(aWatcherLog, aPriority, aBioID, aFs, aNbsPattern, aAddrFamily);
+	CleanupStack::PushL(self);
+	self->ConstructL(aBioServiceId, aSmsServiceId, aBioDb);
+	return self;
+	}
+
+CSpecialNbsSmsSocketWatcher* CSpecialNbsSmsSocketWatcher::NewL(
+															TMsvId 			aBioServiceId, 
+															TMsvId 			aSmsServiceId, 
+															CBIODatabase&	aBioDb, 
+															CWatcherLog&	aWatcherLog, 
+															TInt			aPriority, 
+															TUid			aBioID, 
+															RFs&			aFs,
+															const TDesC&	aNbsPattern, 
+															TSmsAddrFamily	aAddrFamily)
+	{
+	CSpecialNbsSmsSocketWatcher* self = NewLC(aBioServiceId, aSmsServiceId, aBioDb, aWatcherLog, aPriority, aBioID, aFs, aNbsPattern, aAddrFamily);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CSpecialNbsSmsSocketWatcher::CSpecialNbsSmsSocketWatcher(CWatcherLog& aWatcherLog, TInt aPriority, TUid aBioID, RFs& aFs, const TBioMsgIdText& aNbsPattern, TSmsAddrFamily aAddrFamily)
+: CNbsSmsSocketWatcher(aWatcherLog, aPriority, aBioID, aFs, aNbsPattern, aAddrFamily)
+	{
+	}
+
+void CSpecialNbsSmsSocketWatcher::RestoreSettingsL(CMsvSession& aSession)
+	{
+	CBaseSmsActiveSocketWatcher::RestoreSettingsL(aSession);
+
+	switch(iAddrFamily)
+		{
+	case ESmsAddrMessageIndication:
+		iReportHandling = iSettings->SpecialMessageHandling();
+		break;
+	case ESmsAddrStatusReport:
+		iReportHandling = iSettings->StatusReportHandling();
+		break;
+	default:
+		PanicWatcher(EAddrFamilyNotSupported1);
+		break;
+		}
+	}
+	
+void CSpecialNbsSmsSocketWatcher::RestoreSettingsL(TMsvId aBioServiceId, TMsvId aSmsServiceId)
+	{
+	CBaseSmsActiveSocketWatcher::RestoreSettingsL(aBioServiceId, aSmsServiceId);
+	
+	switch(iAddrFamily)
+		{
+	case ESmsAddrMessageIndication:
+		iReportHandling = iSettings->SpecialMessageHandling();
+		break;
+	case ESmsAddrStatusReport:
+		iReportHandling = iSettings->StatusReportHandling();
+		break;
+	default:
+		PanicWatcher(EAddrFamilyNotSupported2);
+		break;
+		}
+	}
+
+void CSpecialNbsSmsSocketWatcher::ConstructL(TMsvId aBioServiceId, TMsvId aSmsServiceId, CBIODatabase& aBioDb)
+	{
+	CNbsSmsSocketWatcher::ConstructL(aBioServiceId, aSmsServiceId, aBioDb);
+
+	if (iReportHandling == CSmsSettings::EDoNotWatchForReport)
+		{
+		User::Leave(KErrNotSupported);
+		}
+	}
+	
+
+CSpecialNbsSmsSocketWatcher::~CSpecialNbsSmsSocketWatcher()
+	{
+	Cancel();
+	}
+
+void CSpecialNbsSmsSocketWatcher::SetBioMsgText(CBIODatabase& /*aBioDb*/)
+	{
+	switch(iAddrFamily)
+		{
+	case ESmsAddrMessageIndication:
+		iBioMsgText = _L("Special Messages");
+		break;
+	case ESmsAddrStatusReport:
+		iBioMsgText = _L("Status Report");
+		break;
+	default:
+		PanicWatcher(EAddrFamilyNotSupported3);
+		break;
+		}
+	}
+
+void CSpecialNbsSmsSocketWatcher::PreStoreActionL(CMsvSession& aSession, CSmsMessage& aSmsMessage)
+	{
+	if( iAddrFamily == ESmsAddrStatusReport && 
+		(iReportHandling == CSmsSettings::EMoveReportToInboxInvisibleAndMatch ||
+		iReportHandling == CSmsSettings::EMoveReportToInboxVisibleAndMatch ||
+		iReportHandling == CSmsSettings::EDiscardReportAndMatch) )
+		{
+		// This is a Status Report and the matching has been requested - so
+		// do the matching!
+		TLogId logId = aSmsMessage.LogServerId();
+		CSmsStatusReport& report = static_cast<CSmsStatusReport&>(aSmsMessage.SmsPDU());
+		
+		MatchStatusReportL(aSession, logId, report.Status());
+		}	
+	}
+	
+void CSpecialNbsSmsSocketWatcher::MatchStatusReportL(CMsvSession& aSession, TLogId aLogId, TSmsStatus::TSmsStatusValue aDeliveryStatus)
+	{
+	// Need to check all SMS messages in the sent folder.
+	CMsvEntry* msvEntry = aSession.GetEntryL(KMsvSentEntryId);
+	CleanupStack::PushL(msvEntry);
+	
+	CMsvEntrySelection* selSent = msvEntry->ChildrenWithMtmL(KUidMsgTypeSMS);
+	CleanupStack::PushL(selSent);
+	
+	// Also need to check the Outbox - if the message failed to send at least
+	// one of its recipients then it will still be in the Outbox.
+	msvEntry->SetEntryL(KMsvGlobalOutBoxIndexEntryId);
+
+	CMsvEntrySelection* selOut = msvEntry->ChildrenWithMtmL(KUidMsgTypeSMS);
+	CleanupStack::PushL(selOut);
+	
+	// Create a header object here for efficiency
+	CParaFormatLayer* paraFormatLayer = CParaFormatLayer::NewL();
+	CleanupStack::PushL(paraFormatLayer);
+	CCharFormatLayer* charFormatLayer = CCharFormatLayer::NewL();
+	CleanupStack::PushL(charFormatLayer);
+	CRichText* richText = CRichText::NewL(paraFormatLayer,charFormatLayer);
+	CleanupStack::PushL(richText);
+
+	CSmsHeader* header = CSmsHeader::NewL(CSmsPDU::ESmsSubmit, *richText);
+	CleanupStack::PushL(header);
+	
+	TMsvId messageId;
+	TBool found = SearchForMessageL(*msvEntry, *header, *selSent, aLogId, messageId);
+	
+	if( !found )
+		{
+		// Search the outbox just in case it is still there.
+		found = SearchForMessageL(*msvEntry, *header, *selOut, aLogId, messageId);
+		}
+	
+	if( found )
+		{
+		UpdateMessageEntryL(*msvEntry, *header, messageId, aLogId, aDeliveryStatus);
+		}
+
+	CleanupStack::PopAndDestroy(7, msvEntry);
+	}
+
+TBool CSpecialNbsSmsSocketWatcher::SearchForMessageL(CMsvEntry& aMsvEntry, CSmsHeader& aHeader, const CMsvEntrySelection& aSelection, TLogId aLogId, TMsvId& aMessageId)
+	{
+	TInt count = aSelection.Count();
+
+	if( count == 0 )
+		{
+		// Empty selection - cannot do search.
+		return EFalse;;
+		}
+		
+	TBool found = EFalse;
+	while( !found && count-- > 0 )
+		{
+		aMsvEntry.SetEntryL(aSelection.At(count));
+		TMsvSmsEntry entry = aMsvEntry.Entry();
+		
+		if( entry.AckSummary(ESmsAckTypeDelivery) == TMsvSmsEntry::EPendingAcks )
+			{
+			// This message is waiting for delivery report - is this it?
+			TLogId logId = 0;	
+			TBool validId = entry.MessageId(logId);
+			
+			if( validId )
+				{
+				// Check the logId is the one
+				if( aLogId == logId )
+					{
+					found = ETrue;
+					aMessageId = entry.Id();
+					}
+				}
+			else
+				{
+				// The long way round - restore the entry and search the list of 
+				// recipients.
+				CMsvStore* store = aMsvEntry.ReadStoreL();
+				CleanupStack::PushL(store);
+				aHeader.RestoreL(*store);	// no need to restore message body
+				CleanupStack::PopAndDestroy(store);
+				
+				TInt rcptCount = aHeader.Recipients().Count();
+				
+				while( !found && rcptCount-- > 0 )
+					{
+					CSmsNumber& rcpt = *aHeader.Recipients()[rcptCount];
+					
+					if( rcpt.AckStatus(ESmsAckTypeDelivery) == CSmsNumber::EPendingAck &&
+						rcpt.LogId() == aLogId )
+						{
+						found = ETrue;
+						aMessageId = entry.Id();
+						}					
+					}				
+				}
+			}
+		}
+	return found;
+	}
+	
+void CSpecialNbsSmsSocketWatcher::UpdateMessageEntryL(CMsvEntry& aMsvEntry, CSmsHeader& aHeader, TMsvId aMessageId, TLogId aLogId, TSmsStatus::TSmsStatusValue aDeliveryStatus)
+	{
+	// Set the entry - probably not necessary...
+	aMsvEntry.SetEntryL(aMessageId);
+	
+	// Update the message entry.
+	CMsvStore* store = aMsvEntry.EditStoreL();
+	CleanupStack::PushL(store);
+	aHeader.RestoreL(*store);	// no need to restore message body
+	
+	const TInt rcptCount = aHeader.Recipients().Count();
+	TInt pending = 0;
+	TInt fail = 0;
+	TInt last = 0;
+	TInt sent = 0;
+	
+	for( TInt count = 0; count < rcptCount; ++count )
+		{
+		CSmsNumber& rcpt = *aHeader.Recipients()[count];
+		
+		if( rcpt.Status() == CMsvRecipient::ESentSuccessfully )
+			{
+			++sent;
+		
+			CSmsNumber::TSmsAckStatus status = rcpt.AckStatus(ESmsAckTypeDelivery);
+			if( status == CSmsNumber::EPendingAck )
+				{
+				if(	rcpt.LogId() == aLogId )
+					{
+					// Update the recipient with the delivery status.
+					if( aDeliveryStatus == TSmsStatus::ESmsShortMessageReceivedBySME )
+						rcpt.SetAckStatus(ESmsAckTypeDelivery, CSmsNumber::EAckSuccessful);
+					else
+						{
+						// Recipient failed to get the message :(							
+						rcpt.SetAckStatus(ESmsAckTypeDelivery, CSmsNumber::EAckError);
+						rcpt.SetStatus(CMsvRecipient::EFailedToSend);
+						rcpt.SetError(aDeliveryStatus);
+						++fail;
+						}
+					}
+				else
+					{
+					// This message has recipients waiting for status reports...
+					++pending;
+					last = count;	// index to last pending recipient.
+					}
+				}
+			else if( status == CSmsNumber::EAckError )
+				{
+				// There is a recipient that failed to get the message
+				++fail;				
+				}
+			}
+		}
+		
+	// Store the updated recipient info
+	aHeader.StoreL(*store);
+	store->CommitL();
+	CleanupStack::PopAndDestroy(store);
+	
+	// Update the delivery summary info in the index...
+	// 1. EPendingAcks - there are still recipients waiting for status reports.
+	// 2. EAllSuccessful - all recipients were delivered to successfully.
+	// 3. EAllFailed - all recipients failed to be delivered to.
+	// 4. EMixed - a mixture of successful and failed deliveries to the recipients.
+	TMsvSmsEntry entry = aMsvEntry.Entry();
+	if( pending == 0 )
+		{
+		// Cases 2, 3 or 4 - check how many failed.
+		if( fail == 0 )
+			{
+			// Case 2 - EAllSuccessful.
+			entry.SetAckSummary(ESmsAckTypeDelivery, TMsvSmsEntry::EAllSuccessful);
+			}
+		else if( fail < sent )
+			{
+			// Case 4 - EMixed.
+			entry.SetAckSummary(ESmsAckTypeDelivery, TMsvSmsEntry::EMixed);
+			}
+		else
+			{
+			// Case 3 - EAllFailed.
+			entry.SetAckSummary(ESmsAckTypeDelivery, TMsvSmsEntry::EAllFailed);
+			}
+		}
+	else
+		{
+		// Case 1 - EPendingAcks.
+		entry.SetAckSummary(ESmsAckTypeDelivery, TMsvSmsEntry::EPendingAcks);
+		
+		if( (entry.iBioType == 0) && (pending == 1) )
+			{
+			// This message has only one pending recipient - update the entry to
+			// reflect this.
+			TLogId logId = aHeader.Recipients()[last]->LogId();
+			entry.SetMessageId(logId, ETrue);
+			}
+		}
+	aMsvEntry.ChangeL(entry);
+	}
+
+//SupportForClass0SmsL for launching the Class 0 SMS plugin, If Class 0 SMS support enabled from SMS Stack.
+void CNbsSmsSocketWatcher::SupportForClass0SmsL()
+	{
+	//if iStatus is KErrNotSupported, class 0 messages will be handled as normal Class2 messages
+	if( iStatus.Int() == KErrNone )
+		{
+		iMsgRefNumber = -1;
+		
+		//launch class 0 sms plugin 
+		iSmsClass0 = reinterpret_cast<CSmsClass0Base*>	
+								(REComSession::CreateImplementationL(
+														KUidClass0SmsHandler,
+														_FOFF(CNbsSmsSocketWatcher ,iDtor_ID_Key))); 
+		
+		}
+		
+		// Complete self to enter state machine
+		TRequestStatus* status = &iStatus;
+		iStatus=KRequestPending;
+		User::RequestComplete(status, KErrNone);
+	}	
+
+
+//externalize  start position, end position, isLastMessage flag and Class 0 SMS data,
+void CNbsSmsSocketWatcher::ExternalizeL(RBufWriteStream& aWriteStream, TInt aStartPos, TInt endPos, TBool aIsLastMessage, TPtr aData)
+	{
+	aWriteStream.WriteInt32L(aStartPos);
+	aWriteStream.WriteInt32L(endPos);
+	aWriteStream.WriteInt32L(aIsLastMessage);
+	aWriteStream << aData;
+	}