sdkcreationmw/sdkruntimes/msgsimulation/MsgRelay/Src/MsgRelayDll.cpp
changeset 0 b26acd06ea60
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sdkcreationmw/sdkruntimes/msgsimulation/MsgRelay/Src/MsgRelayDll.cpp	Mon Mar 08 12:09:11 2010 +0530
@@ -0,0 +1,714 @@
+/*
+* Copyright (c) 2004 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: 
+*
+*/
+
+
+
+//  Include Files  
+
+#include <e32std.h>         // GLDEF_C
+#include <s32mem.h>     // RDesReadStream
+#include <Gsmumsg.h>	// CSmsMessage
+#include <gsmubuf.h>	// CSmsBuffer
+#include <smut.h>		// KUidMsgTypeSMS
+#include <msvuids.h>	// KUidMsvMessageEntry
+#include <smsclnt.h>	// CSmsClientMtm
+#include <mtclreg.h>	// CClientMtmRegistry
+#include <mtmuibas.h>	// CBaseMtmUi
+#include <txtrich.h>	// CRichText
+#include <smutset.h>	// CSmsSettings
+#include <smuthdr.h>	// CSmsHeader
+#include <e32property.h> // RProperty 
+#include "MsgRelay.h"
+
+static const TInt KMaxSubjectLength = 30;
+
+const TUid KPSUidIdleInformation = {0x102071C0};
+const TUint32 KTelephonyIdleStatus = 0x00000001;
+enum TPSTelephonyIdleStatus { EPSTelephonyNotIdle, EPSTelephonyIdle };
+const TUid KPSUidAiInformation = {0x102750F0}; // ActiveIdle2 SID
+const TUint KActiveIdleState = 0x00000002; // Contains one value from following emuneration 
+enum EPSActiveIdleState { EPSAiBackground = 0,EPSAiForeground, EPSAiNumberEntry };
+
+TVersion MessageRelayVersion = TVersion(1, 0, 0);
+
+/****************************************************************************
+*   CRelaySession
+*****************************************************************************
+*
+*
+*
+****************************************************************************/
+/* Implements actual functionality for clients. */
+class CRelaySession : public CSession2
+{
+	CMessageRelay *iOwner;
+	
+	/* Is the session listening for messages. */
+	TBool iListening;
+
+	/* The port that is listened, if iListening is ETrue.
+	   Port 0 means that port is not specified. */
+	TUint iListenPort;
+
+	/* The message type that is listened. */
+	TMessageType iListenType;
+
+	/* Notifies new messages to client...*/
+	TRequestStatus *iListenStatus;
+
+	/* Used for message passing. */
+	RMessage2 iRcvMessage;
+
+	void ConstructL();
+
+	CRelaySession(CMessageRelay *aOwner);
+public:
+	
+	static CRelaySession *NewL(CMessageRelay *aOwner);
+	static CRelaySession *NewLC(CMessageRelay *aOwner);
+
+	~CRelaySession();
+
+	void ServiceL(const RMessage2& aMessage);
+
+	/* If message is accepted ETrue is returned, otherwise EFalse. 
+	   An accepted message is relayed to the client listening for the
+	   messages or to the system Inbox(if no port specified). */
+	TBool AcceptL(TPtr8 &aMessage, TUint aPort, TMessageType aType);
+
+	/* Returns ETrue, is the port is already listened for. */
+	TBool IsListening(TUint aPort, TMessageType aType);
+
+};
+
+CRelaySession::CRelaySession(CMessageRelay *aOwner) : 
+CSession2(),
+iOwner(aOwner),
+iListening(EFalse)
+	{
+
+	}
+	
+void CRelaySession::ConstructL()
+	{
+	
+	}
+		
+CRelaySession *CRelaySession::NewL(CMessageRelay *aOwner)
+	{
+	CRelaySession *self = CRelaySession::NewLC(aOwner);
+	CleanupStack::Pop();
+	return self;
+	}
+
+CRelaySession *CRelaySession::NewLC(CMessageRelay *aOwner)
+	{
+	CRelaySession *self = new (ELeave) CRelaySession(aOwner);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+CRelaySession::~CRelaySession()
+	{
+	for( int i = 0; i < iOwner->iSessions->Count(); i++)
+		{
+		if( iOwner->iSessions->At( i ) == this)
+			{
+			iOwner->iSessions->Delete( i );
+			break;
+			}
+		}
+	}
+
+/* Returns ETrue, is the port is already listened for. */
+TBool CRelaySession::IsListening(TUint aPort, TMessageType aType)
+	{
+	if (iListening == EFalse)
+		return EFalse;
+
+	if (aType != iListenType)
+		return EFalse;
+
+	if (aPort != iListenPort)
+		return EFalse;
+
+	return ETrue;
+	}
+
+void CRelaySession::ServiceL(const RMessage2& aMessage)
+	{
+	HBufC8 *newMessage = NULL;
+	TMessageType type;
+	TUint port;
+	
+	switch (aMessage.Function())
+		{
+		case KNewMessage:
+			{
+			if (iOwner == NULL)
+				User::Leave(KErrNotReady);
+
+			if (iOwner->iWriter == NULL)
+				User::Leave(KErrNotReady);
+
+			newMessage = HBufC8::NewL(aMessage.Int0());
+			TPtr8 ptr(newMessage->Des());
+			aMessage.ReadL( 1, ptr ); 
+
+			type = (TMessageType)aMessage.Int2();
+			iOwner->iWriter->WriteMessageL(ptr, type);
+			
+			aMessage.Complete(KErrNone);	
+
+			delete newMessage;
+
+			break;
+			}
+    case KReceive:
+      
+      if (iListening) {
+        if (!iRcvMessage.IsNull()) {
+          iRcvMessage.Complete(KErrCancel);
+        }
+        iRcvMessage = aMessage;
+			  iOwner->TraverseInboxL();
+      }
+      else {
+        aMessage.Complete(KErrNotReady);
+      }
+      break;
+    case KCancelReceive:
+      if (!iRcvMessage.IsNull()) {
+        iRcvMessage.Complete(KErrCancel);
+      }
+      aMessage.Complete(KErrNone);
+      break;
+		case KStartListening:
+
+			if (iOwner == NULL)
+				User::Leave(KErrNotReady);
+
+			type = (TMessageType)aMessage.Int1();
+			port = (TUint)aMessage.Int2();
+
+			if (iOwner->IsAnyBodyListening(port, type) != EFalse)
+				{
+				aMessage.Complete(KErrInUse);
+				return;
+				}
+
+			iListenType = type;
+			iListenPort = port;
+			iListening = ETrue;
+      aMessage.Complete(KErrNone);	    	
+			break;
+
+		case KStopListening:
+			iListening = EFalse;
+      if (!iRcvMessage.IsNull()) {
+        iRcvMessage.Complete(KErrCancel);
+      }
+			aMessage.Complete(KErrNone);	
+			break;
+
+		default:
+			User::Leave(KErrNotSupported);
+		}
+
+	}
+
+/* If message is accepted ETrue is returned, otherwise EFalse. 
+   An accepted message is relayed to the client listening for the
+   messages. */
+TBool CRelaySession::AcceptL(TPtr8 &aMessage, TUint aPort, TMessageType aType)
+	{
+	if (IsListening(aPort, aType) == EFalse)
+		return EFalse;
+
+	/* Write data to client */
+  if (!iRcvMessage.IsNull()) {
+    iRcvMessage.WriteL(0, aMessage);
+    iRcvMessage.Complete(KErrNone);
+  }
+  
+  return ETrue;
+	}
+
+/****************************************************************************
+*   CMessageRelay
+*****************************************************************************
+*
+*
+*
+****************************************************************************/
+EXPORT_C CMessageRelay::CMessageRelay() : CServer2(EPriorityNormal)
+	{
+
+	}
+
+EXPORT_C void CMessageRelay::ConstructL(CMessageWriter *aWriter)
+	{
+	iSessions = new (ELeave) CArrayFixFlat<CRelaySession *>(1);
+
+	SetWriter(aWriter);
+
+	}
+
+EXPORT_C CMessageRelay *CMessageRelay::NewL(CMessageWriter *aWriter /*= NULL*/)
+	{
+	CMessageRelay *self = CMessageRelay::NewLC(aWriter);
+	CleanupStack::Pop();
+	return self;
+	}
+
+EXPORT_C CMessageRelay *CMessageRelay::NewLC(CMessageWriter *aWriter /*= NULL*/)
+	{
+	CMessageRelay *self = new (ELeave) CMessageRelay();
+	CleanupStack::PushL(self);
+	self->ConstructL(aWriter);
+	return self;
+	}
+
+EXPORT_C CMessageRelay::~CMessageRelay()
+	{
+	delete iSessions;
+	}
+
+/* Goes through all sessions and returns ETrue, is someone
+   is listening the given port. */
+TBool CMessageRelay::IsAnyBodyListening(TUint aPort, TMessageType aType)
+	{
+	TInt i = 0;
+
+	for (i = 0; i < iSessions->Count(); i++)
+		{
+		if (iSessions->At(i)->IsListening(aPort, aType) != EFalse)
+			return ETrue;
+		}
+
+	return EFalse;
+	}
+
+CSession2* CMessageRelay::NewSessionL(const TVersion& aVersion) const
+	{
+	if (!User::QueryVersionSupported(MessageRelayVersion,
+                                     aVersion))
+        {
+		/* Wrong version. */
+		User::Leave(KErrNotSupported);
+        }
+
+	CRelaySession *session = CRelaySession::NewL( (CMessageRelay *)this );
+
+	iSessions->AppendL(session);
+
+	return session;
+	}
+
+CSession2* CMessageRelay::NewSessionL(const TVersion& aVersion, const RMessage2&) const
+	{
+	return NewSessionL( aVersion );
+	}
+
+/* Starts the server. */
+void CMessageRelay::StartRelayL()
+	{
+	CServer2::StartL(KRelayServiceName);
+	} 
+
+/* Call this method when a new message is received to relay it
+   to possible listeners. Returns ETrue, if the message was 
+   accepted. If the message was not accepted, it must not be
+   destroyed, but it must be offered again, as soon as there
+   is a listener for it. */
+EXPORT_C TBool CMessageRelay::NewMessageL(TPtr8 &aMessage, TMessageType aType)
+	{
+	TInt i = 0;
+
+	if (aType != ESmsMessage)
+		User::Leave(KErrNotSupported);
+    
+
+		/* Extract the port and check if it's listened to. */
+		RFs fs;
+		fs.Connect();
+
+		CSmsBuffer *smsBuf = CSmsBuffer::NewL();
+		CSmsMessage *smsMsg	= CSmsMessage::NewL(fs, CSmsPDU::ESmsSubmit, smsBuf);
+		
+		RDesReadStream rs(aMessage);
+		smsMsg->InternalizeL(rs);
+
+		rs.Close();
+		fs.Close();
+
+		CSmsPDU &pdu = smsMsg->SmsPDU(); 
+
+		TInt src, dst;
+
+		TBool isPorts = pdu.ApplicationPortAddressing(dst, src);
+
+		if (isPorts == EFalse)
+			{
+			/* Copy to inbox. */
+			CopyMessageToSmsInboxL(aMessage);
+			return ETrue;
+           	}
+
+		for (i = 0; i < iSessions->Count(); i++)
+			{
+			if (iSessions->At(i)->AcceptL(aMessage, dst, aType) != EFalse)
+				return ETrue;
+			}
+
+		/* No listeners. Cache the message to Inbox. Inbox is traversed,
+		   when new listener are created. */
+		TPtr8 &aMessage2 = aMessage;   
+		CopyMessageToSmsInboxL(aMessage2);
+		return ETrue;
+		}
+
+/* Sets the message writer. Writers WriteMessageL(...) method
+   is called when a client wants to send a message. */
+EXPORT_C void CMessageRelay::SetWriter(CMessageWriter *aWriter)
+	{
+	iWriter = aWriter;
+	}
+
+/* Copies the message to inbox. */
+void CMessageRelay::CopyMessageToSmsInboxL(TPtr8 &aMessage)
+	{
+	// Get the idle statuses from P&S
+	TInt aiStatus(0);    
+    TInt aiGetErr = RProperty::Get( KPSUidAiInformation,
+									KActiveIdleState,
+									aiStatus );
+	TInt tiStatus(0);
+	TInt tiGetErr = RProperty::Get( KPSUidIdleInformation,
+									KTelephonyIdleStatus,
+									tiStatus );
+	
+	_LIT(KIdleStatus,"MSGRelay -- aiState:%d aiGetErr:%d tiState:%d tiGetErr:%d");
+	RDebug::Print(KIdleStatus, aiStatus, aiGetErr, tiStatus, tiGetErr);
+
+	// Update telephony idle status to idle and active idle status 
+	// to foreground if needed. Without these "New message" notifications
+	// might not be visible in some cases.
+	if( aiStatus != EPSAiForeground )
+		{
+		TInt setAiErr = RProperty::Set( KPSUidAiInformation, 
+											KActiveIdleState, 
+											EPSAiForeground );
+		_LIT( KAISetErr,"MSGRelay -- setAiErr:%d" );
+		RDebug::Print( KAISetErr, setAiErr );
+		}
+
+	if( tiStatus != EPSTelephonyIdle )
+		{
+		TInt setTiErr = RProperty::Set( KPSUidIdleInformation, 
+											KTelephonyIdleStatus, 
+											EPSTelephonyIdle );
+		_LIT( KTISetErr,"MSGRelay -- setTiErr:%d" );
+		RDebug::Print( KTISetErr, setTiErr );
+		}
+
+	RFs fs;
+
+	User::LeaveIfError(fs.Connect());
+
+	CSmsBuffer *smsBuf = CSmsBuffer::NewL();
+	CSmsMessage *smsMessage = CSmsMessage::NewL(fs, CSmsPDU::ESmsSubmit, smsBuf);
+	CMsvSession *iMsvSession = CMsvSession::OpenSyncL(*this);
+	CClientMtmRegistry *registry = CClientMtmRegistry::NewL(*iMsvSession);
+
+	RDesReadStream rs(aMessage);
+	rs >> *smsMessage;
+	rs.Close();
+
+	TMsvEntry index;
+
+	index.iMtm = KUidMsgTypeSMS;
+	index.iType = KUidMsvMessageEntry;
+	index.iServiceId = KMsvLocalServiceIndexEntryId;
+	index.iDate.HomeTime();
+	index.SetInPreparation(ETrue);
+
+	TMsvSelectionOrdering ordering;
+	CMsvEntry *entry = CMsvEntry::NewL(*iMsvSession, KMsvGlobalInBoxIndexEntryIdValue,
+									   ordering);
+
+	entry->CreateL(index);
+
+	entry->SetEntryL(index.Id());
+
+	CSmsClientMtm* mtm = (CSmsClientMtm*)registry->NewMtmL(KUidMsgTypeSMS);
+
+	mtm->SetCurrentEntryL(entry);
+
+	index = mtm->Entry().Entry();
+	
+	index.iDetails.Set(smsMessage->ToFromAddress());
+
+	index.SetInPreparation(EFalse);
+
+	index.SetSendingState(KMsvSendStateNotApplicable);
+	index.iDate = smsMessage->Time();
+
+	// Set the new and unread flags. Enables "New message" notifications.
+	index.SetNew( ETrue );
+	index.SetUnread( ETrue );
+
+	CRichText &mtmBody = mtm->Body();
+	mtmBody.Reset();
+
+	HBufC *data = HBufC::NewLC(smsBuf->Length());
+	TPtr ptr(data->Des());
+	
+	smsBuf->Extract(ptr, 0, smsBuf->Length());
+	
+	mtmBody.InsertL(0, ptr);
+
+	if (ptr.Length() > KMaxSubjectLength)
+		index.iDescription.Set(ptr.Left(KMaxSubjectLength));
+	else
+		index.iDescription.Set(ptr);
+
+	mtm->RestoreServiceAndSettingsL();
+
+	CSmsHeader &hdr = mtm->SmsHeader();
+
+	CSmsSettings *sendOpt = CSmsSettings::NewL();
+	CleanupStack::PushL(sendOpt);
+	
+	sendOpt->CopyL(mtm->ServiceSettings());
+	sendOpt->SetDelivery(ESmsDeliveryImmediately);
+	
+	hdr.SetSmsSettingsL(*sendOpt);
+	hdr.SetFromAddressL(smsMessage->ToFromAddress());
+
+	CleanupStack::PopAndDestroy(sendOpt);
+
+	CSmsMessage &newMsg = hdr.Message();
+
+	newMsg.SetServiceCenterAddressL(smsMessage->ServiceCenterAddress());
+
+	CSmsPDU &pdu = newMsg.SmsPDU();
+	CSmsPDU &oldPdu = smsMessage->SmsPDU();
+
+	pdu.SetAlphabet(oldPdu.Alphabet());
+
+	TSmsDataCodingScheme::TSmsClass coding;
+	TBool b = oldPdu.Class(coding);
+	pdu.SetClass(b, coding);
+
+	TInt dst = -1;
+	TInt src = -1; 
+	TBool is16;
+	
+	b = oldPdu.ApplicationPortAddressing(dst, src, &is16);
+
+	if(b != EFalse)
+		{
+		pdu.SetApplicationPortAddressingL(b, dst, src, is16);
+		}
+
+	mtm->AddAddresseeL(smsMessage->ToFromAddress(), index.iDetails);
+
+    CMsvEntry &newEntry = mtm->Entry();    
+    newEntry.ChangeL(index);                
+
+	mtm->SaveMessageL();                 
+
+	CleanupStack::PopAndDestroy(1);
+
+	}
+
+static CSmsMessage *ConvertEntryToMessageL(TMsvEntry &aIndex, CMsvSession *aSession)
+	{
+	CClientMtmRegistry *registry = CClientMtmRegistry::NewL(*aSession);
+	CleanupStack::PushL(registry);
+
+	CSmsClientMtm* mtm = (CSmsClientMtm*)registry->NewMtmL(aIndex.iMtm);
+	CleanupStack::PushL(mtm);
+
+	mtm->SwitchCurrentEntryL(aIndex.Id());
+	mtm->LoadMessageL();
+
+	CSmsHeader &hdr = mtm->SmsHeader();
+
+	CSmsMessage &msg = hdr.Message();
+
+	HBufC8 *buf = HBufC8::NewLC(KMaxSmsBufferSize);
+	TPtr8 ptr(buf->Des());
+	RDesWriteStream ws(ptr);
+
+	ws << msg;
+
+	ws.Close();
+
+	RFs fs;
+
+	User::LeaveIfError(fs.Connect());
+
+	CSmsBuffer *smsBuf = CSmsBuffer::NewL();
+	CSmsMessage *newMsg = CSmsMessage::NewL(fs, CSmsPDU::ESmsSubmit, smsBuf);
+
+	RDesReadStream rs(ptr);
+	rs >> *newMsg;
+
+	rs.Close();
+	fs.Close();
+
+	CleanupStack::PopAndDestroy(3);
+
+	return newMsg;
+	}
+
+static void DeleteEntryFromInboxL(CMsvEntry *aEntry, CMsvSession *aSession)
+    {
+	TMsvEntry index = aEntry->Entry();
+	TMsvSelectionOrdering sort = aEntry->SortType();
+	sort.SetShowInvisibleEntries(ETrue);
+	CMsvEntry* parentEntry = CMsvEntry::NewL(*aSession, index.Parent(), sort);
+	CleanupStack::PushL(parentEntry);  
+    
+    TRAPD(err, parentEntry->DeleteL(index.Id()));
+
+    if(err != KErrNone)
+        {
+        aSession->RemoveEntry(index.Id());
+        }
+
+	CleanupStack::PopAndDestroy(parentEntry);
+    }
+
+/* Traverses the inbox and sends messages to listeners. */
+void CMessageRelay::TraverseInboxL()
+	{
+    TInt count = 0;
+    CMsvEntrySelection* childrenEntrySelection;
+    CMsvEntry* rootEntry;
+    CMsvEntry* child;
+
+	CMsvSession *iMsvSession = CMsvSession::OpenSyncL(*this);
+	CleanupStack::PushL(iMsvSession);
+
+    rootEntry = iMsvSession->GetEntryL(KMsvGlobalInBoxIndexEntryId);
+    count = rootEntry->Count();
+        
+    childrenEntrySelection = rootEntry->ChildrenL();
+
+    for(TInt i = 0; i < count; i++)
+        {
+        child = iMsvSession->GetEntryL((*childrenEntrySelection)[i]);
+        TMsvEntry msvEntry = child->Entry();
+        
+		if (msvEntry.iMtm != KUidMsgTypeSMS)
+			/* Not to us. */
+			continue;
+
+		CSmsMessage *smsMsg = ConvertEntryToMessageL(msvEntry, iMsvSession);
+		
+		HBufC8 *buf = HBufC8::NewLC(KMaxSmsBufferSize);
+		TPtr8 ptr(buf->Des());
+		RDesWriteStream ws(ptr);
+		
+		ws << *smsMsg;
+
+		ws.Close();
+        
+		CSmsPDU &pdu = smsMsg->SmsPDU(); 
+
+		TInt src, dst;
+
+		TBool isPorts = pdu.ApplicationPortAddressing(dst, src);
+
+		if (isPorts == EFalse)
+			{
+			/* No ports, no listeners, leave to inbox. */
+            CleanupStack::PopAndDestroy(1); //buf
+			continue;
+
+			}
+
+		for (i = 0; i < iSessions->Count(); i++)
+			{
+			if (iSessions->At(i)->AcceptL(ptr, dst, ESmsMessage) != EFalse)
+				{
+				/* Remove from inbox */
+				DeleteEntryFromInboxL(child, iMsvSession);
+				
+				/* Inbox has changed, start traversing again. */
+				rootEntry = iMsvSession->GetEntryL(KMsvGlobalInBoxIndexEntryId);
+				count = rootEntry->Count();
+				childrenEntrySelection = rootEntry->ChildrenL();
+				
+				i = -1; /* Will be incremented before testing... */
+				break;
+				}
+			}
+
+		CleanupStack::PopAndDestroy(1); //buf
+
+        }
+	CleanupStack::PopAndDestroy(1); // iMsvSesssion
+	}
+
+void CMessageRelay::HandleSessionEventL(TMsvSessionEvent, TAny*, 
+										TAny*, TAny*)
+	{
+	}
+/****************************************************************************
+*   CMessageWriter
+*****************************************************************************
+*
+*
+*
+****************************************************************************/
+EXPORT_C void CMessageWriter::ConstructL()
+	{
+
+	}
+
+EXPORT_C CMessageWriter *CMessageWriter::NewL()
+	{
+	CMessageWriter *self = CMessageWriter::NewLC();
+	CleanupStack::Pop();
+	return self;
+	}
+
+EXPORT_C CMessageWriter *CMessageWriter::NewLC()
+	{
+	CMessageWriter *self = new (ELeave) CMessageWriter;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+EXPORT_C CMessageWriter::~CMessageWriter()
+	{
+
+	}
+
+/* Called when a message is received from client for sending. */
+EXPORT_C void CMessageWriter::WriteMessageL(TPtr8 & /*aMessage*/, TMessageType /*aType*/)
+	{
+	/* Nothing to do, inherited class will override. */
+	}
+
+// End of file