messagingfw/wappushfw/plugins/PushContentHandler/CSIAContentHandler.cpp
changeset 62 db3f5fa34ec7
parent 0 8e480a14352b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/wappushfw/plugins/PushContentHandler/CSIAContentHandler.cpp	Wed Nov 03 22:41:46 2010 +0530
@@ -0,0 +1,483 @@
+// Copyright (c) 2001-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 <wapmessage.h>
+// Local includes
+//
+#include "csiacontenthandler.h"
+#include "clwatcher.h"
+
+// System includes
+//
+#include <commdb.h>
+#include "wapdecoder.h"
+#include <push/cwappushmsgutils.h>
+
+const TUid KSIADialogNotifierUid={0x100096f6}; //from WapPushSIADialogNotifier.h
+_LIT8(KWapPushCmdConnect,"Connect"); // from WapPushSIADialogNotifier.h
+
+#if defined(_DEBUG)
+_LIT(KErrPushMsgNull,	"NULL CPushMessage");
+#endif
+
+//comment out this line if testing without notifier
+#define _ASK_USER_TO_CONNECT
+
+//supported bearers for connection-oriented push
+const TUint8 KCSDBearer = 0x0A;
+const TUint8 KGPRSBearer = 0x0B;
+
+//bitmasks
+const TUint8 KPortFlagBitMask = 0x40;
+const TUint8 KBearerFlagBitMask = 0x80;
+const TUint8 KAddressLengthBitMask = 0x3F;
+
+// Constants
+_LIT(KReserved, "Reserved");
+
+void CSIAContentHandler::CPushHandlerBase_Reserved1()
+	{
+	User::Panic(KReserved, KErrNotSupported);
+	}
+
+void CSIAContentHandler::CPushHandlerBase_Reserved2()
+	{
+	User::Panic(KReserved, KErrNotSupported);
+	}
+
+/**
+ * The SIA App handler private constructor
+ * Index number : ESIAContentHandlerIndex 
+ */ 
+CSIAContentHandler::CSIAContentHandler()
+: CContentHandlerBase()
+	{
+	}
+
+/**
+ *  This will complete initialization of the object
+ */
+void CSIAContentHandler::ConstructL()
+	{
+	iWapPushUtils = CWapPushMsgUtils::NewL();
+	iCommDb = CCommsDatabase::NewL(EDatabaseTypeIAP);
+	CActiveScheduler::Add(this);
+	}
+
+/**
+ * Static Factory Construction
+ *
+ * Version of NewL which leaves nothing
+ * on the cleanup stack
+ */
+CSIAContentHandler* CSIAContentHandler::NewL()
+	{
+	CSIAContentHandler* self = new(ELeave) CSIAContentHandler;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+ * Default d'tor
+ */
+CSIAContentHandler::~CSIAContentHandler()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: Destructor Called");
+	delete iWapPushUtils;
+	delete iCommDb;
+	}
+
+/**
+ * HandleMessage Async. Version
+ *	Takes ownership of Push Message and sets self active to continue
+ *	processing message.
+ *	@param aPushMsg
+ *		CPushMessage to process
+ *	@param aStatus
+ *		request status variable for use in asynchronous operations
+ */
+void CSIAContentHandler::HandleMessageL(CPushMessage* aPushMsg, TRequestStatus& aStatus)
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: HandleMessage Async Func. Called");
+	__ASSERT_DEBUG( aPushMsg != NULL , User::Panic(KErrPushMsgNull, KErrNone));
+
+	iMessage = aPushMsg;
+	iAcknowledge = ETrue;
+	SetConfirmationStatus(aStatus);
+
+	iState=EParsing;
+	IdleComplete();
+	}
+
+/**
+ *	Abort handling  
+ */
+void CSIAContentHandler::CancelHandleMessage()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: CancelHandleMessage Called");
+	Complete(KErrCancel);
+	}
+
+/**
+ * HandleMessage Sync. Version
+ *	Takes ownership of Push Message and sets self active to continue
+ *	processing message.
+ *	@param aPushMsg
+ *		CPushMessage to process
+ */
+void CSIAContentHandler::HandleMessageL(CPushMessage* aPushMsg)
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: HandleMessage Sync Func. Called");
+	__ASSERT_DEBUG( aPushMsg != NULL , User::Panic(KErrPushMsgNull, KErrNone));
+	
+	iAcknowledge = EFalse;
+	iMessage = aPushMsg;
+	
+	iState = EParsing;
+	IdleComplete();
+	}
+
+/**
+ * Stop all actions and quit
+ */
+void CSIAContentHandler::DoCancel()
+	{
+	 __LOG_PTR_DEBUG("CSIAContentHandler:: DoCancel Called");
+	
+#ifdef _ASK_USER_TO_CONNECT
+	//cancel notifier if appropriate
+	if (iState == EOpeningCOconn && iStatus.Int() == KErrNone)
+		 iNotifier.CancelNotifier(KSIADialogNotifierUid);
+#endif
+	
+	Complete(KErrCancel);
+	}
+
+/**	
+ * Step through the various representative states for handling a message 
+ * 1. Initial State - Parsing SIA Push Msg
+ * 2. Make a CO connection
+ * 3. Delete Content Handler Plugin Owner (i.e self)
+ */
+void CSIAContentHandler::RunL()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: RunL Called");
+	// use active state machine routine to manage activites:
+	switch(iState)
+ 		{
+	case EParsing:
+		ParsePushMsgL();
+		break;
+	case ERequestingConn:
+		RequestConnectionL();
+		break;
+	case EOpeningCOconn:
+		OpenConnectionL();
+		break;
+	case EDone:
+ 		Complete(KErrNone);
+		break;
+	default:
+		break;
+		}
+	}
+
+/**
+ *	This is invoked when RunL Leaves with an error.
+ *	Cleans up and returns.
+ *	@param aError Error passed into this function
+ */
+TInt CSIAContentHandler::RunError(TInt aError)
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: RunError Called"); 
+	iState=EDone;
+	Complete(aError);
+	return KErrNone;
+	}
+
+/**
+ *	Retrieve SIA body, which is in octets, from within CPushMessage,
+ *	parse it for a valid connection point.
+ *	@leave KErrNotFound no valid connection point located
+ *	@leave KErrCorrupt Will leave if the message body contains corrupted data
+ */
+void CSIAContentHandler::ParsePushMsgL()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: ParsePushMsgL Called");
+	
+	// get contact points list from message
+	ObtainContactPointsListL();
+	
+	// got a ContactList so now need to parse this and obtain a TPushConnPoint
+	TBool foundValidConnectionDetails = EFalse;
+	TBool moreContactPointsAvailable = ETrue;
+	// search iContactList for valid connection Point
+	while (!foundValidConnectionDetails && moreContactPointsAvailable)
+		{
+		if (RetrieveContactPoint())
+			foundValidConnectionDetails = ValidContactPointL();
+		if (foundValidConnectionDetails == EFalse)
+			moreContactPointsAvailable = iContactList.Length() > 1;
+		}
+	if (!foundValidConnectionDetails)
+		{
+		__LOG_PTR_DEBUG("CSIAContentHandler: No valid connection point located.");
+		User::Leave(KErrNotFound);
+		}
+	
+	// will leave if no connection possible
+	iState = ERequestingConn;
+	IdleComplete();
+	}
+
+/** 
+ *	Retrieve contact point details from within SIA content body. This 
+ *	method looks up the owned CPushMessage message body and gets a 
+ *	pointer to it which it then parses to obtain the contact Points
+ *	string only which it sets the iContactList pointer to.
+ *	@leave KErrNotFound	Will leave if the contacts list can not be obtained
+ *	@leave KErrCorrupt Will leave if the message body contains corrupted data
+ */
+void CSIAContentHandler::ObtainContactPointsListL()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler::ObtainContactPointsListL Called");
+	TPtrC8 rFieldValue;
+	TBool gotList = EFalse;
+	if (iMessage->GetMessageBody(rFieldValue))
+		{
+		TPtrC8 messageBodyPtr = rFieldValue;
+		TWapBinCodex::TUintvar rMultiByte;
+		TUint startpos = 1;
+		TWapBinCodex::ExtractUIntvarL(messageBodyPtr, startpos, rMultiByte);
+		TUint fieldSize = rMultiByte.iOctetSize;
+		TUint appIdFieldLength = rMultiByte.iValue;
+		TUint contactPointOffset = fieldSize + appIdFieldLength + startpos;
+		// now get Uintvar of ContactPointLen
+		TWapBinCodex::ExtractUIntvarL(messageBodyPtr, contactPointOffset, rMultiByte);
+		//actual Contact Points field starts after ContactPointLen uintvar
+		TUint contactPointLenSize = rMultiByte.iOctetSize;
+		TUint contactPointLen = rMultiByte.iValue;
+		TUint index = contactPointOffset + contactPointLenSize;
+		iContactList.Set(messageBodyPtr.Mid(index, contactPointLen));
+		if (iContactList.Length())
+			gotList = ETrue;
+		}
+	
+	// failed to parse the contact points list
+	if (!gotList)
+		{
+		__LOG_PTR_DEBUG("CSIAContentHandler: Invalid Message Body - unable to parse.");
+		User::Leave(KErrNotFound);
+		}
+	}
+
+/**
+ *	Parse iContactList string for the first Contact Point.
+ *	If found sets iContactPoint to retrieved point and
+ *	removes this point from iContactList so that any further
+ *	calls will start with the next unused Contact Point in the
+ *	iContactList.
+ *	@return TBool
+ *		returns True if found a contact point
+ *		returns False if no contact point found
+ */
+TBool CSIAContentHandler::RetrieveContactPoint()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: RetrieveContactPoint Called");
+	// ensure have the first byte at least which indicates bearer, port and address
+	// length of ContactPoint within list
+	if (iContactList.Length() > 1)
+		{
+		const TInt KFirstByte = 0;
+		TUint8 byte = iContactList[KFirstByte];
+		
+		// check for bit flags settings
+		TUint8 bearerFlagPresent = STATIC_CAST(TUint8, byte & KBearerFlagBitMask);
+		TUint8 portFlagPresent = STATIC_CAST(TUint8, byte & KPortFlagBitMask);
+
+		// get address length to calculate length of ContactPoint
+		TUint8 addressLength = STATIC_CAST(TUint8, byte & KAddressLengthBitMask);
+
+		TInt length = 1;
+		if (bearerFlagPresent)
+			length++;
+		if (portFlagPresent)
+			length += 2;
+		length += addressLength;
+
+		// check not going to access beyond List
+		if (length > iContactList.Length())
+			{
+			// avoid retrying same retrieval and exiting at this point
+			// set list length to zero as no contact points left in it
+			const TInt KZero = 0;
+			iContactList.Set(iContactList.Left(KZero));
+			return EFalse;
+			}
+		iContactPoint.Set(iContactList.Left(length));
+		// if contact list has no further data will be set to length of zero
+		TInt pos = length;
+		TInt count = iContactList.Length() - pos;
+		iContactList.Set(iContactList.Mid(pos, count));
+		return ETrue;
+		}
+	
+	return EFalse;
+	}
+
+/**
+ *	Check the current retrieved Contact Point, contained in
+ *	iContactPoint, to check that the connection details are
+ *	valid for a connection-oriented Push session. If the
+ *	iContactPoint passes the tests iConnPoint is set and 
+ *	this value will be used for opening a connection.
+ *	@return TBool
+ *		Returns True if ContactPoint is valid
+ *		retruns False if invalid contact point
+ *	@leave CCommsDatabase::OpenViewMatchingTextLC
+ *		CommDb methods can Leave - so just propogate
+ *		as can't verfiy address and security.
+ *	@leave CCommsDbTableView::ReadBoolL
+ */
+TBool CSIAContentHandler::ValidContactPointL()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: ValidContactPointL Called");
+
+	TBool valid = ETrue;
+	TUint index = 0;
+
+	// check first bit of first byte to see if bearer Type present 
+	TUint8 byte = iContactPoint[index];
+	index++;
+	// will be zero if flag not set - non-zero overwise
+	TUint8 bearerFlagPresent = STATIC_CAST(TUint8, byte & KBearerFlagBitMask);
+	TUint8 portFlagPresent = STATIC_CAST(TUint8, byte & KPortFlagBitMask);
+	// mask off 1st two bits to get the 6 bits representing address length
+	TUint8 addressLength = STATIC_CAST(TUint8, byte & KAddressLengthBitMask);
+
+	//check bearer type
+	if (bearerFlagPresent)
+		{
+		byte = iContactPoint[index];
+		index++;
+		if ((byte != KCSDBearer) && (byte != KGPRSBearer))
+			{
+			// unsupported bearer
+			__LOG_PTR_DEBUG("CSIAContentHandler: unsupported Bearer Type");
+			valid = EFalse;
+			return valid;
+			}
+		}
+
+	//set postion of the first byte of the port
+	TUint portStartByte = index; 
+	
+	// get address
+	if (portFlagPresent)
+		index += 2;
+	TPtrC8 address;
+	address.Set(iContactPoint.Mid(index, addressLength));
+
+	//Look up supplied address in commsdb. Leave possible from here on in... 
+	TPtrC KTableName(WAP_IP_BEARER);
+	TPtrC KColumnName(WAP_GATEWAY_ADDRESS);
+	TPtrC KSecurityCol(WAP_SECURITY);
+	CCommsDbTableView* table = iCommDb->OpenViewMatchingTextLC(KTableName, KColumnName, address); 
+	
+	TInt ret = table->GotoFirstRecord();
+	if (ret == KErrNone)	//address found
+		{
+		__LOG_PTR_DEBUG("CSIAContentHandler:: Commdb lookup");
+
+		// set port number, using the following logic:
+		// 1. if port number supplied, use it with an unsecure security setting
+		// 2. if no port supplied, use the default port for the security setting 
+		//    returned by the commsdb address lookup
+		TUint16 port;
+		if (portFlagPresent)
+			{
+			TUint8 portNumHighByte = iContactPoint[portStartByte];
+			TUint8 portNumlowByte = iContactPoint[portStartByte + 1];
+			port = STATIC_CAST(TUint16, ((portNumHighByte<<8)|portNumlowByte));
+			}
+		else
+			{
+			TBool secure;
+			table->ReadBoolL(KSecurityCol, secure);
+			 if (secure)
+				port = KPushPortSecure;
+			 else
+				port = KPushPortUnsecure;
+			}
+
+		//set connection point
+		iConnPoint.iHost = address;
+		iConnPoint.iPort = port;
+		}
+	else	//address lookup failed
+		{
+		valid = EFalse;
+		__LOG_PTR_DEBUG("CSIAContentHandler:: Commdb lookup failed");
+		}
+	CleanupStack::PopAndDestroy(); // table
+
+	// will only return true if not SMS and address in CommDB
+	return valid;
+	}
+
+/**
+ *	Start Notifier to inform the user that an SIA request
+ *  has been received and a connection-oriented session is
+ *	required to be initiated.
+ */
+void CSIAContentHandler::RequestConnectionL()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: RequestConnectionL Called"); 
+	
+	iNotifierResponse.SetMax();
+
+#ifdef _ASK_USER_TO_CONNECT
+	User::LeaveIfError(iNotifier.Connect());
+	iNotifier.StartNotifierAndGetResponse(iStatus, KSIADialogNotifierUid, iConnPoint.iHost, iNotifierResponse);
+	iState=EOpeningCOconn;
+	SetActive();
+#else	//always connect case (notifier not started)
+	iNotifierResponse = KWapPushCmdConnect;
+	iState=EOpeningCOconn;
+	IdleComplete();
+#endif
+
+
+	}
+
+/**
+ *	If user accepts connection request, call Connection Manager 
+ *  with valid Connection Point to initiate connection-oriented 
+ *	session. Handler has now finished and closes down leaving 
+ *	connection manager to carry on.
+ */
+void CSIAContentHandler::OpenConnectionL()
+	{
+	__LOG_PTR_DEBUG("CSIAContentHandler:: OpenConnectionL Called"); 
+	
+	//open connection if user accepted	
+	if ((iStatus.Int() == KErrNone) && (iNotifierResponse == KWapPushCmdConnect))
+		iManager->CMOpenConnectionL(iConnPoint);
+	
+	iState=EDone;
+	IdleComplete();
+	}