diff -r 000000000000 -r 8e480a14352b messagingfw/wappushfw/plugins/PushContentHandler/CSIAContentHandler.cpp --- /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 +// Local includes +// +#include "csiacontenthandler.h" +#include "clwatcher.h" + +// System includes +// +#include +#include "wapdecoder.h" +#include + +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(); + }