pushmtm/Plugins/PushContentHandler/CSLContentHandler.cpp
branchRCL_3
changeset 48 8e6fa1719340
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pushmtm/Plugins/PushContentHandler/CSLContentHandler.cpp	Wed Sep 01 12:31:04 2010 +0100
@@ -0,0 +1,1221 @@
+/*
+* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "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:  Implementation of CSLContentHandler.
+*
+*/
+
+
+
+// INCLUDE FILES
+
+#include "CSLContentHandler.h"
+#include "PushMtmUtil.h"
+#include "PushMtmSettings.h"
+#include "PushAuthenticationUtilities.h"
+#include "PushMtmLog.h"
+#include "PushContentHandlerPanic.h"
+#include "PushMtmAutoFetchOperation.h"
+#include "PushMtmUiDef.h"
+#include "StringResourceReader.h"
+#include "sl_dict.h"
+#include "PushContentHandlerUtils.h"
+#include <push/cslpushmsgentry.h>
+#include <msvids.h>
+#include <apgtask.h>
+#include <apgcli.h>
+#include <w32std.h>
+#include <PushMtmUi.rsg>
+#include <nw_dom_node.h>
+#include <nw_dom_document.h>
+#include <nw_dom_element.h>
+#include <nw_dom_text.h>
+#include <nw_wbxml_dictionary.h>
+#include <nw_string_char.h>
+#include "PushMtmPrivateCRKeys.h"
+#include <centralrepository.h> 
+
+// CONSTANTS
+
+// sl attributes / elements
+_LIT8( KSl,      "sl" );
+_LIT8( KHref,    "href" );
+_LIT8( KAction,  "action" );
+
+// action attribute literals
+_LIT8( KExecHigh,"execute-high" );  
+_LIT8( KExecLow, "execute-low" );
+_LIT8( KCache,   "cache" );
+
+// Text SL MIME type
+_LIT( KSlTextContentType, "text/vnd.wap.sl" );
+
+// Browser command to fetch an URL. See Browser API Specification!
+_LIT( KBrowserCmdFetchUrl, "4 " );
+const TUid KBrowserAppUid = { 0x10008D39 };
+
+const TInt KNoOfDictArrays = 1;
+
+/// Autofetch time delay in seconds.
+const TInt KAutofetchDelayInSec = 5;
+
+// file monitored by browser
+_LIT( KPushMtmUrl, "c:\\system\\temp\\PushMtmUrl.txt" );
+
+// ================= MEMBER FUNCTIONS =======================
+
+// ---------------------------------------------------------
+// CSLContentHandler::NewL
+// ---------------------------------------------------------
+//
+CSLContentHandler* CSLContentHandler::NewL()
+	{
+	CSLContentHandler* self = new (ELeave) CSLContentHandler;
+	CleanupStack::PushL( self );
+	self->ConstructL();
+	CleanupStack::Pop( self );
+	return self;
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::~CSLContentHandler
+// ---------------------------------------------------------
+//
+CSLContentHandler::~CSLContentHandler()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::~CSLContentHandler")
+
+    Cancel();
+    delete iFetchOp;
+    delete iHrefBuf;
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::~CSLContentHandler")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::CSLContentHandler
+// ---------------------------------------------------------
+//
+CSLContentHandler::CSLContentHandler()
+:   CPushContentHandlerBase(), 
+    iSavedMsgId( KMsvNullIndexEntryId ), 
+    iPushMsgAction( KErrNotFound ), 
+    iSaveAsRead( EFalse )
+	{
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::ConstructL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::ConstructL()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::ConstructL")
+    
+    CRepository* PushSL = CRepository::NewL( KCRUidPushMtm );
+    CleanupStack::PushL( PushSL );
+    User::LeaveIfError( PushSL->Get( KPushMtmServiceEnabled , iPushSLEnabled ) );
+    CleanupStack::PopAndDestroy( PushSL ); 
+    
+    CPushContentHandlerBase::ConstructL();
+    // Added to Active Scheduler.
+    PUSHLOG_LEAVEFN("CSLContentHandler::ConstructL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::CollectGarbageL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::CollectGarbageL()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::CollectGarbageL")
+
+    DoCollectGarbageL();
+
+	if(iPushSLEnabled)
+	    iState = EFilteringAndParsing;
+	else
+	    iState = EDone;
+	
+	IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::CollectGarbageL")
+    }
+
+
+// ---------------------------------------------------------
+// CSLContentHandler::ParsePushMsgL
+// Note that cXML parser dosn't do any validation!
+// ---------------------------------------------------------
+//
+void CSLContentHandler::ParsePushMsgL()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::ParsePushMsgL")
+
+    TPtrC8 bodyPtr;
+    iMessage->GetMessageBody( bodyPtr );
+    // If there is no body in the message leave with an error
+    if ( bodyPtr.Size() == 0 )
+        {
+        PUSHLOG_WRITE("CSLContentHandler::ParsePushMsgL: Empty body")
+        User::Leave( KErrCorrupt );
+        }
+
+    TPtrC contentType;
+    iMessage->GetContentType( contentType );
+    PUSHLOG_WRITE_FORMAT(" Content type <%S>",&contentType)
+
+    // Add SL dictionary.
+    NW_WBXML_Dictionary_t* dictArray[ KNoOfDictArrays ] = 
+        { (NW_WBXML_Dictionary_t*)&NW_SL_WBXMLDictionary };
+
+    NW_Status_t stat = NW_STAT_SUCCESS;
+
+    RWbxmlDictionary wbxmlDict;
+    wbxmlDict.InitializeL( KNoOfDictArrays, dictArray );
+    CleanupClosePushL<RWbxmlDictionary>( wbxmlDict );
+
+    NW_TinyDom_Handle_t domHandle;
+    NW_Byte* buffer = (NW_Byte*)bodyPtr.Ptr();
+    NW_Int32 length = (NW_Int32)bodyPtr.Size();
+    NW_Bool encoded = ( contentType.CompareF( KSlTextContentType ) == 0 ) ? 
+                                                         NW_FALSE : NW_TRUE;
+    NW_Uint32 publicID = NW_SL_PublicId;
+    NW_Bool extTNotStringTable = NW_FALSE; // What is this?
+    NW_DOM_NodeType_t type = 0;
+    /**********************************
+    *   Root of DOM
+    ***********************************/
+    CDocumentTreeOwner* docTreeOwner = new (ELeave) CDocumentTreeOwner;
+    CleanupStack::PushL( docTreeOwner );
+    NW_DOM_DocumentNode_t* domNode = NW_DOM_DocumentNode_BuildTree
+        ( 
+                            &domHandle, 
+                            buffer, 
+                            length, 
+                            encoded, 
+                            publicID, 
+                            extTNotStringTable 
+        );
+	if (!domNode)
+		{
+		PUSHLOG_WRITE("CSLContentHandler::ParsePushMsgL: domNode is Null")
+		}
+    User::LeaveIfNull( domNode );
+    docTreeOwner->SetDocTree( domNode );
+
+    type = NW_DOM_Node_getNodeType( domNode );
+    if ( type != NW_DOM_DOCUMENT_NODE )
+        {
+        PUSHLOG_WRITE_FORMAT("CSLContentHandler::ParsePushMsgL: Not Document node <%d>",type)
+        User::Leave( KErrArgument );
+        }
+
+    iCharEncoding = NW_DOM_DocumentNode_getCharacterEncoding( domNode );
+    PUSHLOG_WRITE_FORMAT("CSLContentHandler::ParsePushMsgL: Doc char encoding <%x>",iCharEncoding)
+
+    /**********************************
+    *   ELEMENT sl
+    ***********************************/
+	// first make sure if there any children in the dom tree, otherwise we will PANIC(in NW_DOM_DocumentNode_getDocumentElement) and crash WatcherMainThread.
+	TBool domNodeHasChildNodes = EFalse;
+	domNodeHasChildNodes = NW_DOM_Node_hasChildNodes( domNode );
+	PUSHLOG_WRITE_FORMAT("CSLContentHandler::ParsePushMsgL: check if Dom tree has <SI> node <%d>", domNodeHasChildNodes)
+	if (!domNodeHasChildNodes)
+        {
+        PUSHLOG_WRITE("CSLContentHandler::ParsePushMsgL: No SL element present in the dom tree. Message corrupted.")
+        User::Leave( KErrCorrupt );
+        }
+
+	PUSHLOG_WRITE("CSLContentHandler::ParsePushMsgL: before calling getDocumentElement")
+    NW_DOM_ElementNode_t* slElement = 
+        NW_DOM_DocumentNode_getDocumentElement( domNode );
+	if (!slElement)
+		{
+		PUSHLOG_WRITE("CSLContentHandler::ParsePushMsgL: slElement is Null")
+		}
+    User::LeaveIfNull( slElement );
+
+    type = NW_DOM_Node_getNodeType( slElement );
+
+    CStringOwner* stringOwner = new (ELeave) CStringOwner;
+    CleanupStack::PushL( stringOwner );
+
+    NW_String_t* name = NW_String_new();
+    User::LeaveIfNull( name );
+    stringOwner->SetString( name );
+    stat = NW_DOM_Node_getNodeName( slElement, name );
+	PUSHLOG_WRITE_FORMAT("CSLContentHandler::ParsePushMsgL: getNodeName ErrCode: %d", NwxStatusToErrCode( stat ))
+    User::LeaveIfError( NwxStatusToErrCode( stat ) );
+    NW_Byte*  nameBuf = NW_String_getStorage( name );
+    NW_Uint16 nameLen = NW_String_getCharCount( name, iCharEncoding );
+    TPtrC8 namePtr( nameBuf, nameLen );
+
+    if ( type != NW_DOM_ELEMENT_NODE || namePtr.CompareF( KSl ) != 0 )
+        {
+        PUSHLOG_WRITE_FORMAT("CSLContentHandler::ParsePushMsgL: Not sl element node <%d>",type)
+        User::Leave( KErrArgument );
+        }
+
+    CleanupStack::PopAndDestroy( stringOwner ); // stringOwner
+
+    /**********************************
+    *   Attributes of ELEMENT sl
+    ***********************************/
+    if ( NW_DOM_ElementNode_hasAttributes( slElement ) )
+        {
+        NW_DOM_AttributeListIterator_t attrIter;
+        stat = NW_DOM_ElementNode_getAttributeListIterator
+                             ( slElement, &attrIter );
+		PUSHLOG_WRITE_FORMAT("CSLContentHandler::ParsePushMsgL: getAttribListIter ErrCode: %d", NwxStatusToErrCode( stat ))
+        User::LeaveIfError( NwxStatusToErrCode( stat ) );
+
+        NW_DOM_AttributeHandle_t attrHandle;
+        while( NW_DOM_AttributeListIterator_getNextAttribute
+               ( &attrIter, &attrHandle ) == NW_STAT_WBXML_ITERATE_MORE )
+            {
+            ParseSlAttributeL( attrHandle );
+            }
+        }
+
+    // Cleanup.
+    CleanupStack::PopAndDestroy( 2, &wbxmlDict ); // docTreeOwner, wbxmlDict
+
+    // if 'action' attribute not specified, the value 'execute-low' is used.
+	if ( !ActionFlag() )
+        {
+		iPushMsgAction = CSLPushMsgEntry::ESLPushMsgExecuteLow;
+        SetActionFlag( ETrue );
+        PUSHLOG_WRITE_FORMAT(" Defaulting to ActionFlag: %d",iPushMsgAction)
+        }
+
+    iState = EProcessing;
+    IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::ParsePushMsgL")
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::ParseSlAttributeL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::ParseSlAttributeL
+                        ( NW_DOM_AttributeHandle_t& aAttrHandle )
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::ParseSlAttributeL")
+
+    NW_Status_t stat = NW_STAT_SUCCESS;
+
+    CStringOwner* attrNameOwner = new (ELeave) CStringOwner;
+    CleanupStack::PushL( attrNameOwner );
+
+    NW_String_t* attrName = NW_String_new();
+    User::LeaveIfNull( attrName );
+    attrNameOwner->SetString( attrName );
+
+    // Get the name of the attribute.
+    stat = NW_DOM_AttributeHandle_getName( &aAttrHandle, attrName );
+    User::LeaveIfError( NwxStatusToErrCode( stat ) );
+    NW_Byte*  attrNameBuf = NW_String_getStorage( attrName );
+    NW_Uint16 attrNameLen = NW_String_getCharCount( attrName, iCharEncoding );
+    TPtrC8 attrNamePtr( attrNameBuf, attrNameLen );
+
+    if ( attrNamePtr.CompareF( KHref ) == 0 )
+        {
+        if ( !HrefFlag() )
+            {
+            HBufC* tempHrefBuf = NULL;
+
+            CStringOwner* valOwner = new (ELeave) CStringOwner;
+            CleanupStack::PushL( valOwner );
+
+            NW_String_t* val = NW_String_new();
+            User::LeaveIfNull( val );
+            valOwner->SetString( val );
+            stat = NW_DOM_AttributeHandle_getValue( &aAttrHandle, val );
+            if ( stat != NW_STAT_DOM_NO_STRING_RETURNED )
+                {
+                User::LeaveIfError( NwxStatusToErrCode( stat ) );
+                NW_Byte* storage = NW_String_getStorage( val );
+                NW_Uint16 length = NW_String_getCharCount( val, iCharEncoding );
+                TPtrC8 prefixPtr( storage, length );
+                tempHrefBuf = HBufC::NewMaxL( length );
+                // No leavable after it!!! until...
+                tempHrefBuf->Des().Copy( prefixPtr );
+                }
+
+            CleanupStack::PopAndDestroy( valOwner ); // valOwner
+
+            if ( tempHrefBuf )
+                {
+                if ( tempHrefBuf->Length() == 0 )
+                    {
+                    // Zero length Href is considered as nothing.
+                    PUSHLOG_WRITE(" Zero length HrefFlag");
+                    }
+                else
+                    {
+                    iHrefBuf = tempHrefBuf; // ...until here.
+                    SetHrefFlag( ETrue );
+                    PUSHLOG_WRITE_FORMAT(" HrefFlag set <%S>",iHrefBuf);
+                    }
+                }
+            }
+        }
+    else if ( attrNamePtr.CompareF( KAction ) == 0 )
+        {
+        if ( !ActionFlag() )
+            {
+            CStringOwner* stringOwner = new (ELeave) CStringOwner;
+            CleanupStack::PushL( stringOwner );
+
+            NW_String_t* val = NW_String_new();
+            User::LeaveIfNull( val );
+            stringOwner->SetString( val );
+            stat = NW_DOM_AttributeHandle_getValue( &aAttrHandle, val ); 
+            User::LeaveIfError( NwxStatusToErrCode( stat ) );
+            NW_Byte* storage = NW_String_getStorage( val );
+            NW_Uint16 length = NW_String_getCharCount( val, iCharEncoding );
+            TPtrC8 actionPtr( storage, length );
+
+            iPushMsgAction = ConvertActionString( actionPtr );
+            SetActionFlag( ETrue );
+            PUSHLOG_WRITE_FORMAT(" ActionFlag: %d",iPushMsgAction)
+
+            CleanupStack::PopAndDestroy( stringOwner );
+            }
+        }
+    else
+        {
+        __ASSERT_DEBUG( EFalse, 
+            ContHandPanic( EPushContHandPanUnexpSlToken ) );
+        }
+
+    CleanupStack::PopAndDestroy( attrNameOwner ); // attrNameOwner
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::ParseSlAttributeL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::ConvertActionString
+// ---------------------------------------------------------
+//
+TUint CSLContentHandler::ConvertActionString
+                         ( const TDesC8& aActionString ) const
+	{
+	const TInt KMatchFound = 0;
+
+	// if 'action' attribute not specified, the value 'execute-low' is used.
+	TUint actionValue = CSLPushMsgEntry::ESLPushMsgExecuteLow;
+
+	if ( aActionString.Compare( KExecHigh ) == KMatchFound )
+        {
+		actionValue = CSLPushMsgEntry::ESLPushMsgExecuteHigh;
+        }
+	else if ( aActionString.Compare( KExecLow ) == KMatchFound )
+        {
+		actionValue = CSLPushMsgEntry::ESLPushMsgExecuteLow;
+        }
+	else if ( aActionString.Compare( KCache ) == KMatchFound )
+        {
+		actionValue = CSLPushMsgEntry::ESLPushMsgExecuteCache;
+        }
+
+	return actionValue;
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::SetSlPushMsgEntryFieldsL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::SetSlPushMsgEntryFieldsL( CSLPushMsgEntry& 
+                                                  aSlPushMsgEntry ) const
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::SetSlPushMsgEntryFieldsL")
+
+	// Set URL and Action fields.
+    if ( HrefFlag() )
+        {
+	    aSlPushMsgEntry.SetUrlL( *iHrefBuf );
+        }
+
+    __ASSERT_DEBUG( ActionFlag(), 
+                    ContHandPanic( EPushContHandPanUnspecSlAction ) );
+    if ( ActionFlag() )
+        {
+	    aSlPushMsgEntry.SetAction( iPushMsgAction );
+        }
+    else // if not specified, the value 'execute-low' is used.
+        {
+        aSlPushMsgEntry.SetAction( CSLPushMsgEntry::ESLPushMsgExecuteLow );
+        }
+
+	// Set all the relevant header fields.
+	TPtrC8 msgHeaderPtr;
+	iMessage->GetHeader( msgHeaderPtr );
+	aSlPushMsgEntry.SetHeaderL( msgHeaderPtr );
+
+    // Get server address.
+    TPtrC8 srvAddress;
+    if ( iMessage->GetServerAddress( srvAddress ) )
+        {
+	    aSlPushMsgEntry.SetFromL( srvAddress );
+        }
+
+    // First line in Inbox: TMsvEntry::iDetails.
+    if ( srvAddress.Length() == 0 )
+        {
+        // Read from resource.
+        HBufC* details = 
+            iStrRscReader->AllocReadResourceLC( R_PUSHMISC_UNK_SENDER );
+        aSlPushMsgEntry.SetMsgDetailsL( *details );
+        CleanupStack::PopAndDestroy( details );
+        }
+    else
+        {
+        // Convert the "From" information to the format required by the UI 
+        // spec and then decode it.
+        HBufC* details = iWapPushUtils->ConvertDetailsL( srvAddress );
+        CleanupStack::PushL( details );
+        HBufC* convertedFrom = 
+            CPushMtmUtil::ConvertUriToDisplayFormL( *details );
+        CleanupStack::PushL( convertedFrom );
+        //
+        aSlPushMsgEntry.SetMsgDetailsL( *convertedFrom );
+        //
+        CleanupStack::PopAndDestroy( 2, details ); // convertedFrom, details
+        }
+
+    // Second line in Inbox: TMsvEntry::iDescription.
+    // Read from resource.
+    HBufC* description = 
+        iStrRscReader->AllocReadResourceLC( R_PUSHMISC_INBOX_SERV_MSG );
+    aSlPushMsgEntry.SetMsgDescriptionL( *description );
+    CleanupStack::PopAndDestroy( description );
+
+    // ******** Push MTM specific processing *********
+
+    /*
+    * Unfortunately in CPushMsgEntryBase there is no such functionality 
+    * with which we can reach TMsvEntry as non-const, but we have to 
+    * modify the entry's iMtmData2 member somehow. We can do it 
+    * with either casting or with modifying and saving the entry 
+    * manually after it has been saved by CSLPushMsgEntry. The latter 
+    * solution is more expensive so we choose the first.
+    */
+    TMsvEntry& tEntry = CONST_CAST( TMsvEntry&, aSlPushMsgEntry.Entry() );
+    if ( HrefFlag() )
+        {
+        CPushMtmUtil::SetAttrs( tEntry, EPushMtmAttrHasHref );
+        }
+    else
+        {
+        CPushMtmUtil::ResetAttrs( tEntry, EPushMtmAttrHasHref );
+        }
+
+    // Indication is required if the entry is saved as 'read' (delete & 
+    // replacement notification). It can happen only in case of SL message.
+    // Otherwise the flag has to be cleared!
+    if ( !iSaveAsRead )
+        {
+        // Saving as unread & new.
+        tEntry.SetNew( ETrue );
+        tEntry.SetUnread( ETrue );
+        CPushMtmUtil::ResetAttrs( tEntry, EPushMtmReadButContentChanged );
+        }
+    else
+        {
+        // Saving as read.
+        tEntry.SetNew( EFalse );
+        tEntry.SetUnread( EFalse );
+        CPushMtmUtil::SetAttrs( tEntry, EPushMtmReadButContentChanged );
+        }
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::SetSlPushMsgEntryFieldsL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::ProcessingPushMsgEntryL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::ProcessingPushMsgEntryL()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::ProcessingPushMsgEntryL")
+
+    TBool discardPushMsg( EFalse );
+
+    __ASSERT_DEBUG( ActionFlag(), 
+                    ContHandPanic( EPushContHandPanUnspecSlAction ) );
+
+    // S60 requirement: if the href is empty then delete (discard) the msg.
+    if ( HrefFlag() == EFalse )
+        {
+        PUSHLOG_WRITE(" No SL Href.")
+        discardPushMsg = ETrue;
+        }
+    else
+        {
+        __ASSERT_DEBUG( HrefFlag() && iHrefBuf, 
+                        ContHandPanic( EPushContHandPanUnspecSlHref ) );
+
+        // The message will not be discarded
+        discardPushMsg = EFalse;
+
+        CMsvEntrySelection* matchingUrlList = iWapPushUtils->FindUrlLC
+                                              ( *iHrefBuf, KUidWapPushMsgSL );
+        TInt matchingListCount = matchingUrlList->Count();
+        PUSHLOG_WRITE_FORMAT(" matchingListCount: %d",matchingListCount)
+
+        // Only one SL is allowed with the same Url, so leave the first and 
+        // delete the others.
+        if ( 1 < matchingListCount )
+            {
+            for ( TInt count = 1; count < matchingListCount; ++count )
+                {
+                iWapPushUtils->DeleteEntryL( matchingUrlList->At(count) );
+                }
+            matchingListCount = 1; // Only one remains.
+            }
+
+	    if ( 0 < matchingListCount )
+		    {
+		    // Find msg of the same href and discard it if it has a lower or 
+            // the same action value.
+            CSLPushMsgEntry* matchingSl = CSLPushMsgEntry::NewL();
+	        CleanupStack::PushL( matchingSl );
+
+            const TMsvId matchingId = matchingUrlList->At(0);
+            matchingSl->RetrieveL( *iMsvSession, matchingId );
+
+            if ( iPushMsgAction <= matchingSl->Action() ) 
+			    {
+                PUSHLOG_WRITE(" SL: not higher action")
+                discardPushMsg = ETrue;
+                }
+
+            CleanupStack::PopAndDestroy( matchingSl ); // matchingSl, 
+            }
+
+	    CleanupStack::PopAndDestroy( matchingUrlList ); // matchingUrlList
+        }
+
+    if ( discardPushMsg )
+        {
+        // Nothing to do.
+        PUSHLOG_WRITE(" SL discarded.")
+        iState = EDone;
+        IdleComplete();
+        }
+    else
+        {
+        iState = HandleServiceInvocationL();
+        IdleComplete();
+        }
+
+    __ASSERT_DEBUG( iSavedMsgId == KMsvNullIndexEntryId, 
+                    ContHandPanic( EPushContHandPanSlMsgIdSet ) );
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::ProcessingPushMsgEntryL")
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::HandleServiceInvocationL
+// ---------------------------------------------------------
+//
+TInt CSLContentHandler::HandleServiceInvocationL() const
+    {
+    PUSHLOG_ENTERFN("CSLContentHandler::HandleServiceInvocationL")
+
+    TInt nextState = ESavePushMsgEntry;
+
+    if ( iPushMsgAction == CSLPushMsgEntry::ESLPushMsgExecuteCache )
+        {
+        PUSHLOG_WRITE(" SL cache");
+        TBool isAuthenticated = TPushAuthenticationUtil::
+            AuthenticateMsgL( *iMtmSettings, *iMessage );
+		if ( !isAuthenticated )
+            {
+            PUSHLOG_WRITE(" Not authenticated");
+            // The message is placed to Inbox.
+            nextState = ESavePushMsgEntry;
+            }
+        else
+            {
+            // Authenticated. Fetch SL-cache.
+            nextState = EFetching;
+            }
+        }
+
+    else if ( iPushMsgAction == CSLPushMsgEntry::ESLPushMsgExecuteLow )
+        {
+        PUSHLOG_WRITE(" SL execute-low")
+        // It is independent from Automatic/Manual setting and WL 
+        // authentication is not applied. The message is placed to Inbox 
+        // for manual downloading.
+        nextState = ESavePushMsgEntry;
+        }
+
+    else // ESLPushMsgExecuteHigh
+        {
+        PUSHLOG_WRITE(" SL execute-high");
+        // If the settings is Manual or it does not pass the WL authentication 
+		// then it is placed to Inbox for manual downloading.
+        // If the setting is Automatic and it passes the WL authentication, 
+        // the Browser is started standalone to download the URL without any 
+		// user interaction.
+        if ( iMtmSettings->ServiceLoadingType() == 
+			               CPushMtmSettings::EManual )
+            {
+            PUSHLOG_WRITE(" Manual setting")
+            // The message is placed to Inbox.
+            nextState = ESavePushMsgEntry;
+            }
+        else // Automatic loading
+            {
+            PUSHLOG_WRITE(" Automatic setting");
+            // Authenticate the message.
+            TBool isAuthenticated = TPushAuthenticationUtil::
+                AuthenticateMsgL( *iMtmSettings, *iMessage );
+            if ( !isAuthenticated )
+	            {
+                PUSHLOG_WRITE(" Not authenticated");
+	            // The message is placed to Inbox.
+	            nextState = ESavePushMsgEntry;
+	            }
+            else
+	            {
+	            // Authenticated - start downloading.
+	            nextState = EFetching;
+	            }
+            }
+        }
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::HandleServiceInvocationL")
+    return nextState;
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::FetchPushMsgEntryL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::FetchPushMsgEntryL()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::FetchPushMsgEntryL")
+
+    __ASSERT_DEBUG( iSavedMsgId == KMsvNullIndexEntryId, 
+                    ContHandPanic( EPushContHandPanAlreadyInitialized ) );
+    __ASSERT_DEBUG( HrefFlag() && iHrefBuf, 
+                    ContHandPanic( EPushContHandPanUnspecSlHref ) );
+
+    /* 
+    * In case of execute-high use the Browser to download the service.
+    * In case of cache use the fetch operation to download the service 
+    * silently. 
+    */
+
+    if ( iPushMsgAction == CSLPushMsgEntry::ESLPushMsgExecuteHigh )
+        {
+        PUSHLOG_WRITE(" Start Browser")
+        // Launch the Browser with the URI, then save the message.
+        // Trap errors. If Browser's launching fails, then save the 
+        // message as 'unread'. In case of an error, it is not forwarded.
+        TRAPD( err, StartBrowserL() );
+        iState = ESavePushMsgEntry;
+        // Mark it 'read' after succesfull Browser startup.
+        iSaveAsRead = err ? EFalse : ETrue;
+        IdleComplete();
+        }
+    else if ( iPushMsgAction == CSLPushMsgEntry::ESLPushMsgExecuteCache )
+        {
+        // Fetch the service inside the content handler.
+        iStatus = KRequestPending;
+        SetActive();
+        __ASSERT_DEBUG( !iFetchOp, 
+                        ContHandPanic( EPushContHandPanFetchAlreadyInit ) );
+
+        iFetchOp = CPushMtmAutoFetchOperation::NewL( *iHrefBuf, 
+                                                     KAutofetchDelayInSec, 
+                                                     iStatus );
+        iFetchOp->StartL();
+        PUSHLOG_WRITE(" Fetch op started")
+        iState = EFetchCompleted; // Next state.
+        // Fetch completes it.
+        }
+    else
+        {
+        __ASSERT_DEBUG( EFalse, 
+                        ContHandPanic( EPushContHandPanBadActionValue ) );
+        User::Leave( KErrNotSupported );
+        }
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::FetchPushMsgEntryL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::StartBrowserL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::StartBrowserL()
+    {
+    PUSHLOG_ENTERFN("CSLContentHandler::StartBrowserL")
+
+    // Parameters are separated by space
+    // 1st parameter: type of the further parameters
+    // 2nd parameter: URL
+    //
+    HBufC* param = HBufC::NewLC( KBrowserCmdFetchUrl().Length() + 
+                                 iHrefBuf->Length() );
+    TPtr paramPtr = param->Des();
+    paramPtr.Copy( KBrowserCmdFetchUrl );
+    paramPtr.Append( *iHrefBuf );
+
+    RWsSession wsSession;
+    User::LeaveIfError( wsSession.Connect() );
+    CleanupClosePushL<RWsSession>( wsSession );
+    TApaTaskList taskList( wsSession );
+    TApaTask task = taskList.FindApp( KBrowserAppUid );
+
+    if ( task.Exists() )
+        {
+        PUSHLOG_WRITE("CSLContentHandler Browser::SendMessage")
+
+        RFs             rfs;
+        RFile           file;
+        TPtrC8          param8Ptr;
+        // 8-bit buffer is required.
+        HBufC8* param8 = HBufC8::NewLC( param->Length() );
+        param8->Des().Copy( *param );
+        param8Ptr.Set(param8->Des());
+
+        // Open the file.
+        User::LeaveIfError(rfs.Connect());
+        CleanupClosePushL(rfs);
+
+        // Replace file if exists or Create file if not exist yet
+        User::LeaveIfError( file.Replace( rfs, KPushMtmUrl, EFileWrite | EFileShareExclusive ) );
+        CleanupClosePushL(file);
+
+        // Write to file      
+        User::LeaveIfError( file.Write( param8Ptr ) );
+        
+        // Clean up.
+        CleanupStack::PopAndDestroy(/*file*/);
+        CleanupStack::PopAndDestroy(/*rfs*/);
+        CleanupStack::PopAndDestroy( /*param8*/ );
+        }
+    else 
+        {
+        PUSHLOG_WRITE("CSLContentHandler Browser::StartDocument")
+        RApaLsSession appArcSession;
+        User::LeaveIfError( appArcSession.Connect() );
+        CleanupClosePushL<RApaLsSession>( appArcSession );
+        TThreadId id;
+        User::LeaveIfError
+            (
+                appArcSession.StartDocument( *param, KBrowserAppUid, id )
+            );
+        CleanupStack::PopAndDestroy( &appArcSession );
+        }
+
+    CleanupStack::PopAndDestroy( &wsSession );
+    CleanupStack::PopAndDestroy( param );
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::StartBrowserL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::FetchCompletedL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::FetchCompletedL()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::FetchCompletedL")
+
+    __ASSERT_DEBUG( iPushMsgAction == CSLPushMsgEntry::ESLPushMsgExecuteCache, 
+                    ContHandPanic( EPushContHandPanBadActionValue ) );
+    __ASSERT_DEBUG( iSavedMsgId == KMsvNullIndexEntryId, 
+                    ContHandPanic( EPushContHandPanAlreadyInitialized ) );
+    __ASSERT_DEBUG( iFetchOp, ContHandPanic( EPushContHandPanNoFetchOp ) );
+
+    const TInt fetchRes = iStatus.Int();
+    PUSHLOG_WRITE_FORMAT(" fetchRes <%d>",fetchRes)
+
+    if ( fetchRes != KErrNone )
+        {
+        // Downloading failed. Save the message.
+        iState = ESavePushMsgEntry;
+        }
+    else
+        {
+        // Silent fetching has completed successfully.
+        // The message should not be saved.
+        iState = EDone;
+        }
+
+    // Next state set. Complete.
+    IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::FetchCompletedL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::SavePushMsgEntryL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::SavePushMsgEntryL()
+    {
+    PUSHLOG_ENTERFN("CSLContentHandler::SavePushMsgEntryL")
+
+    __ASSERT_DEBUG( ActionFlag(), 
+                    ContHandPanic( EPushContHandPanUnspecSlAction ) );
+    __ASSERT_DEBUG( HrefFlag() && iHrefBuf, 
+                    ContHandPanic( EPushContHandPanUnspecSlHref ) );
+    __ASSERT_DEBUG( iSavedMsgId == KMsvNullIndexEntryId, 
+                    ContHandPanic( EPushContHandPanAlreadyInitialized ) );
+
+    CMsvEntrySelection* matchingUrlList = iWapPushUtils->FindUrlLC
+                                          ( *iHrefBuf, KUidWapPushMsgSL );
+    TInt matchingListCount = matchingUrlList->Count();
+    PUSHLOG_WRITE_FORMAT(" matchingListCount: %d",matchingListCount)
+
+    // Only one SL is allowed with the same Url, so leave the first and 
+    // delete the others.
+    __ASSERT_DEBUG( matchingListCount <= 1, 
+                    ContHandPanic( EPushContHandPanTooManySl ) );
+    if ( 1 < matchingListCount )
+        {
+        for ( TInt count = 1; count < matchingListCount; ++count )
+            {
+            iWapPushUtils->DeleteEntryL( matchingUrlList->At(count) );
+            }
+        matchingListCount = 1; // Only one remains.
+        }
+
+    TBool saveNewMsg = ETrue; // Save by default.
+    TMsvId matchingEntryId = KMsvNullIndexEntryId;
+
+    // Apply reception rules.
+    if ( matchingListCount == 0 )
+        {
+        // Nothing to do.
+        saveNewMsg = ETrue;
+        }
+    else
+        {
+        CSLPushMsgEntry* matchingSl = CSLPushMsgEntry::NewL();
+        CleanupStack::PushL( matchingSl );
+
+        matchingEntryId = matchingUrlList->At(0);
+        matchingSl->RetrieveL( *iMsvSession, matchingEntryId );
+
+        if ( iPushMsgAction <= matchingSl->Action() ) 
+            {
+            // Discard the new SL: it does not have higher 
+            // action value as the existing.
+            PUSHLOG_WRITE(" SL not higher action - discarded")
+            saveNewMsg = EFalse;
+            }
+        else
+            {
+            // The new has greater action attribute. 
+            // Update the old SL with the new data.
+            saveNewMsg = ETrue;
+            }
+
+        CleanupStack::PopAndDestroy( matchingSl ); // matchingSl
+        }
+
+    CleanupStack::PopAndDestroy( matchingUrlList ); // matchingUrlList
+
+    // Store message if not marked for deletion.
+    if ( saveNewMsg )
+        {
+		StoreSLMessageL( matchingEntryId );
+		}
+
+    iState = EDone;
+    IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::SavePushMsgEntryL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::StoreSLMessageL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::StoreSLMessageL( TMsvId aMatchingEntryId )
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::StoreSLMessageL")
+
+	CSLPushMsgEntry* slEntry = CSLPushMsgEntry::NewL();
+	CleanupStack::PushL( slEntry );
+
+    if ( aMatchingEntryId == KMsvNullIndexEntryId )
+        {
+        PUSHLOG_WRITE(" No matching SL")
+        // Save new to Inbox.
+        SetSlPushMsgEntryFieldsL( *slEntry );
+	    iSavedMsgId = 
+            slEntry->SaveL( *iMsvSession, KMsvGlobalInBoxIndexEntryId );
+        // Set the entry to read and *not* new state depending on iSaveAsRead.
+        if ( !iSaveAsRead )
+            {
+            // Do nothing SaveL saves it as unread.
+            }
+        else
+            {
+            // SaveL owerrides the read settings (iEntry.SetUnread(ETrue);) 
+            // that we set in SetSlPushMsgEntryFieldsL, thus the read status 
+            // has to be reset manually here:
+            iWapPushUtils->MarkServiceUnreadL( iSavedMsgId, EFalse );
+            }
+        }
+    else
+        {
+        PUSHLOG_WRITE(" Matching SL")
+        slEntry->RetrieveL( *iMsvSession, aMatchingEntryId );
+        SetSlPushMsgEntryFieldsL( *slEntry );
+
+        slEntry->UpdateL( *iMsvSession );
+        iSavedMsgId = aMatchingEntryId;
+        // Note that UpdateL does not change the read/unread status.
+
+        // Move the updated msg back to Inbox.
+        TMsvId parentId = slEntry->Entry().Parent();
+        if ( parentId != KMsvGlobalInBoxIndexEntryId )
+	        {
+            PUSHLOG_WRITE(" Moving back to Inbox")
+            CMsvEntry* cParent = iMsvSession->GetEntryL( parentId );
+            CleanupStack::PushL( cParent );
+	        cParent->MoveL( iSavedMsgId, KMsvGlobalInBoxIndexEntryId );
+            CleanupStack::PopAndDestroy( cParent ); // cParent
+	        }
+        }
+
+#ifdef __TEST_LOG__
+        _LIT( KDateFormat, "%E%D%X%N%Y %1 %2 %3" );
+        _LIT( KTimeFormat, "%-B%:0%J%:1%T%:2%S%:3%+B" );
+        TBuf<32> dateHolder;
+        TBuf<32> timeHolder;
+        TTime recDateTime = slEntry->ReceivedDate();
+        recDateTime.FormatL( dateHolder, KDateFormat );
+        recDateTime.FormatL( timeHolder, KTimeFormat );
+        PUSHLOG_WRITE_FORMAT(" rec date: <%S>",&dateHolder)
+        PUSHLOG_WRITE_FORMAT(" rec time: <%S>",&timeHolder)
+#endif // __TEST_LOG__
+
+	CleanupStack::PopAndDestroy( slEntry ); // slEntry
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::StoreSLMessageL")
+	}
+
+
+// ---------------------------------------------------------
+// CSLContentHandler::HandleMessageL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::HandleMessageL( CPushMessage* aPushMsg, 
+                                        TRequestStatus& aStatus )
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::HandleMessageL 2")
+
+    __ASSERT_DEBUG( aPushMsg != NULL, 
+                    ContHandPanic( EPushContHandPanMsgNull ) );
+
+#ifdef __TEST_LOG__
+    TPtrC8 bodyPtr;
+    aPushMsg->GetMessageBody( bodyPtr );
+    PUSHLOG_HEXDUMP( bodyPtr )
+#endif // __TEST_LOG__
+
+	iMessage = aPushMsg;
+	iAcknowledge = ETrue;
+	SetConfirmationStatus( aStatus );
+
+	iState = EGarbageCollecting;
+	IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::HandleMessageL 2")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::HandleMessageL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::HandleMessageL( CPushMessage* aPushMsg )
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::HandleMessageL 1")
+
+    __ASSERT_DEBUG( aPushMsg != NULL, 
+                    ContHandPanic( EPushContHandPanMsgNull ) );
+	
+#ifdef __TEST_LOG__
+    TPtrC8 bodyPtr;
+    aPushMsg->GetMessageBody( bodyPtr );
+    PUSHLOG_HEXDUMP( bodyPtr )
+#endif // __TEST_LOG__
+
+	iAcknowledge = EFalse;
+	iMessage = aPushMsg;
+
+	iState = EGarbageCollecting;
+	IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::HandleMessageL 1")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::CancelHandleMessage
+// ---------------------------------------------------------
+//
+void CSLContentHandler::CancelHandleMessage()
+	{
+    Cancel();
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::CPushHandlerBase_Reserved1
+// ---------------------------------------------------------
+//
+void CSLContentHandler::CPushHandlerBase_Reserved1()
+	{
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::CPushHandlerBase_Reserved1
+// ---------------------------------------------------------
+//
+void CSLContentHandler::CPushHandlerBase_Reserved2()
+	{
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::DoCancel
+// ---------------------------------------------------------
+//
+void CSLContentHandler::DoCancel()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::DoCancel")
+    // TODO Cancel outstanding requests!
+	Complete( KErrCancel );
+    PUSHLOG_LEAVEFN("CSLContentHandler::DoCancel")
+	}
+
+// ---------------------------------------------------------
+// CSLContentHandler::RunL
+// ---------------------------------------------------------
+//
+void CSLContentHandler::RunL()
+	{
+    PUSHLOG_ENTERFN("CSLContentHandler::RunL")
+
+    // Handle errors in RunError().
+    PUSHLOG_WRITE_FORMAT(" iStatus.Int(): %d",iStatus.Int())
+
+	switch ( iState )
+		{
+	    case EGarbageCollecting:
+            {
+		    CollectGarbageL();
+		    break;
+            }
+
+
+        case EFilteringAndParsing:
+            {
+            if(iPushSLEnabled)
+                {
+                if ( !FilterPushMsgL() )
+                    {
+                    // It did not pass the filter. Done.
+                    iState = EDone;
+                    IdleComplete();
+                    }
+                else
+                    {
+                    // Continue.
+                    TInt ret = KErrNone;
+                    PUSHLOG_WRITE("CSLContentHandler::RunL : before trapping parsing.")
+                    TRAP(ret, ParsePushMsgL());
+                    PUSHLOG_WRITE_FORMAT("CSLContentHandler::RunL : after trapping parsing. ret = %d", ret)
+                    if ( ret != KErrNone)
+                        {
+                        PUSHLOG_WRITE("CSLContentHandler::RunL : Parsing failed. discarding message.")
+                        iState = EDone;
+                        IdleComplete();
+                        }
+                    }
+                }
+			break;
+            }
+
+        case EProcessing:
+            {
+            if(iPushSLEnabled)
+			ProcessingPushMsgEntryL();
+			break;
+            }
+
+		case EFetching:
+            {
+            if(iPushSLEnabled)
+			FetchPushMsgEntryL();
+			break;
+            }
+
+		case EFetchCompleted:
+            {
+            if(iPushSLEnabled)
+			FetchCompletedL();
+			break;
+            }
+
+		case ESavePushMsgEntry:
+            {
+            if(iPushSLEnabled)
+			SavePushMsgEntryL();
+			break;
+            }
+
+
+        case EDone:
+            {
+            PUSHLOG_WRITE("CSLContentHandler EDone")
+			Complete( KErrNone );
+			break;
+            }
+		default:
+            {
+            // JIC.
+            PUSHLOG_WRITE("CSLContentHandler default EDone")
+			Complete( KErrNone );
+			break;
+            }
+		}
+
+    PUSHLOG_LEAVEFN("CSLContentHandler::RunL")
+    }
+
+// ---------------------------------------------------------
+// CSLContentHandler::RunError
+// ---------------------------------------------------------
+//
+TInt CSLContentHandler::RunError( TInt aError )
+	{
+    PUSHLOG_WRITE_FORMAT("CSLContentHandler::RunError: %d",aError)
+
+	iState = EDone;
+	Complete( aError );
+	return KErrNone;
+	}
+