--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/wappushfw/plugins/PushContentHandler/CSIAContentHandler.cpp Mon Jan 18 20:36:02 2010 +0200
@@ -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();
+ }