messagingfw/sendas/server/src/csendassession.cpp
changeset 0 8e480a14352b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/sendas/server/src/csendassession.cpp	Mon Jan 18 20:36:02 2010 +0200
@@ -0,0 +1,527 @@
+// Copyright (c) 2004-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:
+//
+
+#include "csendassession.h"
+
+#include <msvids.h>
+#include <s32mem.h> 
+#include <csendasaccounts.h>
+#include <csendasmessagetypes.h>
+#include <mtmuids.h>
+#include <mtclreg.h>
+#include <mtclbase.h>
+
+#include "csendasserver.h"
+#include "csendasmtmmanager.h"
+#include "csendasmessage.h"
+#include "sendasserverdefs.h"
+#include "tsendasserverpanic.h"
+
+CSendAsSession* CSendAsSession::NewL(const TVersion& aVersion, CSendAsServer& aServer)
+	{
+	TVersion version(KSendAsServerVersionMajor, KSendAsServerVersionMinor, KSendAsServerVersionBuild);
+	if( !User::QueryVersionSupported(version, aVersion) )
+		{
+		User::Leave(KErrNotSupported);
+		}
+	
+	CSendAsSession* self = new(ELeave) CSendAsSession(aServer);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+void CSendAsSession::ConstructL()
+	{
+	iMessages = CObjectIx::NewL();
+	iContainer = iServer.NewContainerL();
+	TMsvSelectionOrdering order(KMsvNoGrouping, EMsvSortByDetails, ETrue);
+	iMsvEntry = CMsvEntry::NewL(GetMsvSessionL(), KMsvRootIndexEntryId, order);
+	iSendAsAccounts = CSendAsAccounts::NewL();
+	
+	// Prepare available message type list
+	iAvailableMessageTypes = CSendAsMessageTypes::NewL();
+	ResetTypeFilterL();
+	}
+	
+CSendAsSession::CSendAsSession(CSendAsServer& aServer)
+: iServer(aServer)
+	{
+	}
+
+CSendAsSession::~CSendAsSession()
+	{
+	delete iMessages;
+	// iContainer will be deleted by the server
+	iServer.SessionClosed(iContainer);
+	delete iSendAsAccounts;
+	delete iAvailableMessageTypes;
+	delete iMsvEntry;
+	}
+	
+/** Service the incoming message.
+
+If the message is for the session object, it will be dispatched to
+the correct handler.  If the message is for the subsession object,
+it will be dispatched to the handler located in the subsession.
+
+This allows all of the subsession logic to be internal to itself.
+
+If a handler requires an asynchronous response, then this method
+should return ETrue.  If this method leaves, the trap harness in
+the ServiceL method will catch the error and complete the message
+with the error code.
+
+@param	aMessage
+The IPC message object.
+*/
+TBool CSendAsSession::DoServiceL(const RMessage2& aMessage)
+	{
+	// session methods
+	switch (aMessage.Function())
+		{
+		case ESASSetFilter:
+			DoSetMessageTypeFilterL(aMessage);
+			break;
+		case ESASClearFilter:
+			DoClearMessageTypeFilterL();
+			break;
+		case ESAMDestroySubSession:
+			// this is a sub session message, which means that the handle is transmitted
+			// automatically, but we will handle it at the session level so we can
+			// not only close the subsession, but also delete the object properly.
+			RemoveMessage(aMessage.Int3());
+			break;
+		case ESASGetMessageTypeListLength:
+			DoGetMessageTypeListLengthL(aMessage);
+			break;
+		case ESASGetMessageTypes:
+			DoGetMessageTypeListL(aMessage);
+			break;
+		case ESASGetAccountListLength:
+			DoGetAccountListLengthL(aMessage);
+			break;
+		case ESASGetAccountList:
+			DoGetAccountListL(aMessage);
+			break;
+		case ESASCreateSubSession:
+			CreateSubsessionL(aMessage);
+			break;
+		default:
+			{
+			// must be a subsession method
+			CSendAsMessage* msg = MessageFromHandle(aMessage.Int3());
+			if( msg == NULL )
+				{
+				PanicClient(aMessage, ESendAsClientPanicBadSubSessionHandle);
+				User::Leave(KErrBadHandle);
+				}
+			return msg->DoSubSessionServiceL(aMessage);
+			}
+		}
+	return EFalse;	
+	}
+
+/** Create a new subsession object to represent a message.
+
+Each message is represented by a CSendAsMessage object.  These objects
+are derived from CContainer.  This allows us to use the object container
+framework to keep track of allocated objects and to ensure that the
+object handles are unique.
+
+The RMessage is required so that the session can pass the new
+subsession handle to the client-side RSubSession object.
+
+@param	aMessage
+The IPC message object.
+*/
+void CSendAsSession::CreateSubsessionL(const RMessage2& aMessage)
+	{
+	// new message (subsession)
+	CSendAsMessage* message = CSendAsMessage::NewL(*this);
+	CleanupClosePushL(*message);
+
+	// add message class into the container and obtain a handle for it.
+	iContainer->AddL(message);
+	TInt id = iMessages->AddL(message);
+	CleanupStack::Pop(message);
+	
+	// write the handle back to the client
+	TPckg<TInt> pckg(id);
+	TInt err = aMessage.Write(3, pckg);
+
+	if( err != KErrNone )
+		{
+		iMessages->Remove(id);
+		message->Close();
+
+		User::Leave(err);
+		}
+	++iMessageCount;
+	}
+
+/** Find a subsession pointer from its handle.
+
+A message sent from an RSubSession-derived object will always contain
+a handle to the server-side subsession object in parameter 3.
+
+This method takes that handle and looks it up in the object index to
+find the pointer to the correct subsession instance.
+
+@param	aHandle
+The handle for the subsession object
+
+@return
+The pointer to the subsesion object referred to by aHandle if it exists,
+otherwise NULL is returned.
+*/
+CSendAsMessage* CSendAsSession::MessageFromHandle(TUint aHandle)
+	{
+	return static_cast<CSendAsMessage*>(iMessages->At(aHandle));
+	}
+	
+void CSendAsSession::RemoveMessage(TUint aHandle)
+	{
+	// this operation will delete the subsession.
+	iMessages->Remove(aHandle);
+	--iMessageCount;
+	}
+	
+void CSendAsSession::PanicClient(const RMessage2& aMessage, TSendAsClientPanic aPanic) const
+	{
+	_LIT(KSendAsServerSession, "SendAsSession");
+	aMessage.Panic(KSendAsServerSession, aPanic);
+	}
+
+/** Add a message type filter to the list.
+
+This removes any non-compliant MTMs from the list of available MTMs.
+
+This is a cumulative operation, filters are applied as they are received
+and the list is shortened each time.
+
+@param	aMessage
+The IPC message object.
+*/
+void CSendAsSession::DoSetMessageTypeFilterL(const RMessage2& aMessage)
+	{
+	// extract filter
+	TPckgBuf<TSendAsMessageTypeFilter> filterBuf;
+	aMessage.ReadL(0, filterBuf);
+	
+	// apply to available mtm list
+	AddTypeFilterL(filterBuf());
+	}
+
+/** Remove the message type filter.
+
+In reality, this means that we repopulate the entire list.
+*/
+void CSendAsSession::DoClearMessageTypeFilterL()
+	{
+	ResetTypeFilterL();
+	}
+
+void CSendAsSession::DoGetMessageTypeListLengthL(const RMessage2& aMessage)
+	{
+	// return size of message types data
+	TPckgBuf<TUint32> buf(iAvailableMessageTypes->Size());
+	aMessage.WriteL(0, buf);
+	}
+	
+void CSendAsSession::DoGetMessageTypeListL(const RMessage2& aMessage)
+	{
+	// buffer to hold message types data
+	HBufC8* buffer = HBufC8::NewLC(iAvailableMessageTypes->Size());
+	
+	// stream 
+	RDesWriteStream strm;
+	CleanupClosePushL(strm);
+	TPtr8 ptr(buffer->Des());
+	strm.Open(ptr);
+
+	// write to stream
+	iAvailableMessageTypes->ExternalizeL(strm);
+	strm.CommitL();
+	aMessage.WriteL(0, *buffer);
+
+	// clean up
+	CleanupStack::PopAndDestroy(2, buffer); // buffer, strm
+	}
+
+void CSendAsSession::DoGetAccountListLengthL(const RMessage2& aMessage)
+	{
+	// message slot 1 contains the mtm uid 
+	TPckgBuf<TUid> uidBuf;
+	aMessage.ReadL(1, uidBuf);
+
+	// reset the account list
+	iSendAsAccounts->Reset();
+	
+	// get account list for this mtm
+	TUid acctUid = uidBuf();
+	iMsvEntry->SetEntryL(KMsvRootIndexEntryId);
+	CMsvEntrySelection* selection = iMsvEntry->ChildrenWithMtmL(acctUid);
+	CleanupStack::PushL(selection);
+	
+	// populate account list
+	TInt count = selection->Count();
+	iSendAsAccounts->SetMessageType(acctUid);
+	for (TInt i=0; i<count; ++i)
+		{
+		TSendAsAccount id = selection->At(i);
+		// set context
+		iMsvEntry->SetEntryL(id);
+		// add account		
+		iSendAsAccounts->AppendAccountL(iMsvEntry->Entry().iDetails, id);
+		}
+	CleanupStack::PopAndDestroy(selection);
+
+	// get size
+	TPckgBuf<TInt> buf(iSendAsAccounts->Size());
+	aMessage.WriteL(0, buf);
+	}
+	
+void CSendAsSession::DoGetAccountListL(const RMessage2& aMessage)
+	{
+	// buffer to hold accounts data
+	HBufC8* buffer = HBufC8::NewLC(iSendAsAccounts->Size());
+	
+	// buffer write stream
+	RDesWriteStream strm;
+	CleanupClosePushL(strm);
+	TPtr8 ptr(buffer->Des());
+	strm.Open(ptr);
+
+	// write to buffer
+	iSendAsAccounts->ExternalizeL(strm);
+	
+	// commit and return buffer
+	strm.CommitL();
+	aMessage.WriteL(0, *buffer);
+
+	// clean up
+	CleanupStack::PopAndDestroy(2, buffer);	// buffer, strm
+	}
+	
+CMsvSession& CSendAsSession::GetMsvSessionL()
+	{ 
+	return iServer.GetMsvSessionL(); 
+	}
+
+CSendAsActiveContainer& CSendAsSession::ActiveContainer()
+	{
+	return iServer.ActiveContainer();
+	}
+
+const TUid& CSendAsSession::NotifierUid() const
+	{
+	return iServer.NotifierUid();
+	}
+
+const TUid& CSendAsSession::EditUtilsPluginUid() const
+	{
+	return iServer.EditUtilsPluginUid();
+	}
+
+/*
+ *	methods from CSession2
+ */
+ 
+/** Session ServiceL method.
+
+This method traps the real serviceL method so that leaves can
+be trapped and returned as error completion codes on the message.
+
+This prevents the session from leaving during the server AO's
+RunL and causing a panic.
+
+@param	aMessage
+The IPC message object.
+*/
+void CSendAsSession::ServiceL(const RMessage2& aMessage)
+	{
+	TBool async = EFalse;
+	TRAPD(err, async = DoServiceL(aMessage));
+	
+	// complete the message if necessary.
+	if (!async || err != KErrNone)
+		{
+		aMessage.Complete(err);
+		}
+	}
+
+
+/** Handles session disconnect.
+
+@param	aMessage
+The IPC message object.
+*/
+void CSendAsSession::Disconnect(const RMessage2& aMessage)
+	{
+	// Cancel all sub-session objects.
+	TInt count = iMessages->Count();
+	for( TInt i=0; i<count; ++i)
+		{
+		CSendAsMessage* message = static_cast<CSendAsMessage*>((*iMessages)[i]);
+		message->CancelMessage();
+		}
+	
+	// Must call the base class implementation - this will delete the object.
+	CSession2::Disconnect(aMessage);
+	}
+	
+
+/*
+ *	available Client MTM array management and access methods.
+ */
+	
+/** Resets the available message type list
+
+*/
+void CSendAsSession::ResetTypeFilterL()
+	{
+	// get unfiltered MTM array
+	RArray<TUid>& aMtmUidArray = iServer.GetMtmManager()->GetMtmUidArray();
+	
+	// get client registry
+	CClientMtmRegistry* aClientRegistry = iServer.GetMtmManager()->GetClientMtmRegistry();
+
+	// reset filtered MTM array
+	iAvailableMessageTypes->Reset();
+	
+	// populate filtered MTM array
+	TInt count = aMtmUidArray.Count();
+	for( TInt i=0; i<count; ++i )
+		{
+		// mtm uid
+		TUid mtmUid = aMtmUidArray[i];
+		// mtm name
+		TPtrC name = aClientRegistry->RegisteredMtmDllInfo(mtmUid).HumanReadableName();
+		iAvailableMessageTypes->AppendMessageTypeL(name, mtmUid);
+		}
+	}
+
+/** Reduces available message type list according to filter 
+
+@param	aFilter
+The filter to be applied
+*/
+void CSendAsSession::AddTypeFilterL(const TSendAsMessageTypeFilter& aFilter)
+	{
+	if( aFilter.iMessageCapability == KUidMtmQueryCanSendMsg )
+		{
+		// the list of mtms available to sendas sessions is created with this 
+		// capability by default by the sendas MTM manager, so no action needed
+		return;
+		}
+
+	// locals used in scan loop
+	CBaseMtm* mtm = NULL;
+	TInt      response;
+	TInt      error;
+	TBool     capable;
+	
+	// scan list of mtms and filter out those without the required capabilities
+	TInt count = iAvailableMessageTypes->Count();
+	while( count-- > 0 )
+		{
+		// get mtm by uid		
+		mtm = iServer.GetMtmManager()->FindStoredMtmL(iAvailableMessageTypes->MessageTypeUid(count));
+
+		// check if capability supported
+		response = 0;
+		error = mtm->QueryCapability(aFilter.iMessageCapability, response);
+		
+		// if capability supported, check conditions 
+		capable = EFalse;
+		if( error == KErrNone )
+			{
+			switch (aFilter.iCondition)
+				{
+			case RSendAs::ESendAsNoCondition:
+				capable = ETrue;
+				break;
+			case RSendAs::ESendAsEquals:
+				capable = (response == aFilter.iValue);
+				break;
+			case RSendAs::ESendAsNotEquals:
+				capable = (response != aFilter.iValue);
+				break;
+			case RSendAs::ESendAsGreaterThan:
+				capable = (response > aFilter.iValue);
+				break;
+			case RSendAs::ESendAsLessThan:
+				capable = (response < aFilter.iValue);
+				break;
+			case RSendAs::ESendAsBitwiseAnd:
+				capable = (response & aFilter.iValue);
+				break;
+			case RSendAs::ESendAsBitwiseOr:
+				capable = (response | aFilter.iValue);
+				break;
+			case RSendAs::ESendAsBitwiseNand:
+				capable = !(response & aFilter.iValue);
+				break;
+			case RSendAs::ESendAsBitwiseNor:
+				capable = !(response | aFilter.iValue);
+				break;
+			default:
+				break;
+				}
+			}
+		else if( error != KErrNotSupported )
+			{
+			User::Leave(error); 
+			}
+
+		if( !capable )
+			{
+			// mtm is not capable, so remove it from the available list
+			iAvailableMessageTypes->RemoveMessageType(count);
+			}
+		}
+	}
+
+/** Updates the array of available Client MTMs
+
+*/
+void CSendAsSession::HandleMtmChange()
+	{
+	// get unfiltered MTM UID array
+	RArray<TUid>& aMtmUidArray = iServer.GetMtmManager()->GetMtmUidArray();
+	
+	// check for removed mtm from filtered lists
+	TInt count = iAvailableMessageTypes->Count();
+	for( TInt i=0; i<count; ++i )
+		{
+		if (aMtmUidArray.Find(iAvailableMessageTypes->MessageTypeUid(i)) == KErrNotFound)
+			{
+			iAvailableMessageTypes->RemoveMessageType(i);
+			break;
+			}
+		}
+	}
+
+/** Returns the client MTM specified by UID 
+
+@param	aMtmUid
+The UID of the Client MTM
+*/
+CBaseMtm* CSendAsSession::GetClientMtmL(TUid aMtmUid)
+	{
+	return ( iServer.GetMtmManager()->GetClientMtmL(aMtmUid) );
+	}