pushmtm/Plugins/PushContentHandler/CSIContentHandler.cpp
branchRCL_3
changeset 65 8e6fa1719340
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pushmtm/Plugins/PushContentHandler/CSIContentHandler.cpp	Wed Sep 01 12:31:04 2010 +0100
@@ -0,0 +1,1835 @@
+/*
+* 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 CSIContentHandler.
+*
+*/
+
+
+
+// INCLUDE FILES
+
+#include "CSIContentHandler.h"
+#include "PushMtmUtil.h"
+#include "PushMtmSettings.h"
+#include "PushMtmLog.h"
+#include "PushMtmUiDef.h"
+#include "StringResourceReader.h"
+#include "PushContentHandlerPanic.h"
+#include "si_dict.h"
+#include "PushContentHandlerUtils.h"
+#include <push/CSIPushMsgEntry.h>
+#include <msvids.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 <THttpFields.h>
+
+// CONSTANTS
+
+// si attributes / elements
+_LIT8( KSi,          "si" );
+_LIT8( KIndication,  "indication" );
+_LIT8( KHrefAttrib,  "href" );
+_LIT8( KSiIdAttrib,  "si-id" );
+_LIT8( KCreatedAttrib,   "created" );
+_LIT8( KExpiresAttrib,   "si-expires" );
+_LIT8( KActionAttrib,    "action" );
+
+// action attribute literals
+_LIT8( KDeleteAction,    "delete" );
+_LIT8( KSignalNone,      "signal-none" );
+_LIT8( KSignalLow,       "signal-low" );
+_LIT8( KSignalMedium,    "signal-medium" );
+_LIT8( KSignalHigh,      "signal-high" );
+
+_LIT( KSiTextContentType, "text/vnd.wap.si" );
+
+const TInt KValidMaxEncodedDateTimeSize = 7;
+const TInt KValidUTCLength = 20; // YYYY-MM-DDTHH:MM:SSZ
+const TInt KValidUTCNumericals = 14;
+const TInt KValidUTCYearBlockLength = 4;
+const TInt KValidUTCOtherBlockLength = 2;
+const TUint8 KAsciiZeroCharCode = 0x30;
+
+const TInt KValidTTimeMonthStart = 4;
+const TInt KValidTTimeDayStart = 6;
+const TInt KValidTTimeHourStart = 8;
+const TInt KValidTTimeMinuteStart = 10;
+const TInt KValidTTimeSecondStart = 12;
+const TInt KValidTTimeBlockLength = 2;
+
+const TInt KValidTTimeLength = 14; // YYYYMMDDHHMMSS
+
+const TInt KNoOfDictArrays = 1;
+
+_LIT( KCharMinus, "-" );
+_LIT( KCharT, "T" );
+_LIT( KCharColon, ":" );
+_LIT( KCharZ, "Z" );
+
+/// Conversion buffer size.
+LOCAL_D const TInt KPushConversionBufferSize = 256;
+/// Zero width non-breaking space character.
+LOCAL_D const TUint16 KPushZeroWidthNbsp = 0xfeff;
+
+// ================= MEMBER FUNCTIONS =======================
+
+// ---------------------------------------------------------
+// CSIContentHandler::NewL
+// ---------------------------------------------------------
+//
+CSIContentHandler* CSIContentHandler::NewL()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::NewL")
+
+	CSIContentHandler* self = new (ELeave) CSIContentHandler;
+	CleanupStack::PushL( self );
+	self->ConstructL();
+	CleanupStack::Pop( self );
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::NewL")
+	return self;
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::~CSIContentHandler
+// ---------------------------------------------------------
+//
+CSIContentHandler::~CSIContentHandler()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::~CSIContentHandler")
+
+    Cancel();
+	delete iHrefBuf;
+	delete iSiIdBuf;
+	delete iData;
+    delete iCharacterSetConverter;
+    iCharacterSetConverter = NULL;
+    delete iCharacterSetsAvailable;
+    iCharacterSetsAvailable = NULL;
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::~CSIContentHandler")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::CSIContentHandler
+// ---------------------------------------------------------
+//
+CSIContentHandler::CSIContentHandler()
+:   CPushContentHandlerBase(),
+    iSavedMsgId( KMsvNullIndexEntryId ),
+    iPushMsgAction( KErrNotFound ),
+    iExpiresTime( Time::NullTTime() ),
+    iCreatedTime( Time::NullTTime() )
+	{
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::ConstructL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::ConstructL()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ConstructL")
+
+    CPushContentHandlerBase::ConstructL();
+    // Added to Active Scheduler.
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ConstructL")
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::CollectGarbageL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::CollectGarbageL()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::CollectGarbageL")
+
+    DoCollectGarbageL();
+
+    //Ready.
+    iState = EFilteringAndParsing;
+	IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::CollectGarbageL")
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::ParsePushMsgL
+// Note that cXML parser dosn't do any validation!
+// ---------------------------------------------------------
+//
+void CSIContentHandler::ParsePushMsgL()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ParsePushMsgL")
+
+    TPtrC8 bodyPtr;
+    iMessage->GetMessageBody( bodyPtr );
+    // If there is no body in the message leave with an error
+    if ( bodyPtr.Size() == 0 )
+        {
+        PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: Empty body")
+        User::Leave( KErrCorrupt );
+        }
+
+    // Get content type. It will tell us wheather the msg body is encoded or
+    // textual.
+    TPtrC contentType;
+	iMessage->GetContentType( contentType );
+    PUSHLOG_WRITE_FORMAT("CSIContentHandler::ParsePushMsgL: HTTP header - Content type <%S>",&contentType);
+
+    /*
+    TPtrC8 encodingPtr;
+    TBool encodingFound = iMessage->GetHeaderField
+                          ( EHttpContentEncoding, encodingPtr );
+    #ifdef __TEST_LOG__
+    TBuf<64> encodingBuf;
+    encodingBuf.Copy( encodingPtr );
+    PUSHLOG_WRITE_FORMAT(" HTTP header - Content encoding <%S>",&encodingBuf);
+    #endif // __TEST_LOG__
+    */
+
+    // Add SI dictionary.
+    NW_WBXML_Dictionary_t* dictArray[ KNoOfDictArrays ] =
+        { (NW_WBXML_Dictionary_t*)&NW_SI_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();
+    // Let's use the content type now.
+    NW_Bool encoded = ( contentType.CompareF( KSiTextContentType ) == 0 ) ?
+                                                         NW_FALSE : NW_TRUE;
+    // SI public identifier.
+    NW_Uint32 publicID = NW_SI_PublicId;
+    NW_Bool extTNotStringTable = NW_FALSE;
+    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("CSIContentHandler::ParsePushMsgL: domNode is Null")
+		}
+    User::LeaveIfNull( domNode );
+	PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: domNode is not Null") // to be deleted
+    // Let domNode be on the Cleanup Stack.
+    docTreeOwner->SetDocTree( domNode );
+
+    // It must be a document node.
+    type = NW_DOM_Node_getNodeType( domNode );
+    if ( type != NW_DOM_DOCUMENT_NODE )
+        {
+        PUSHLOG_WRITE_FORMAT("CSIContentHandler::ParsePushMsgL: Not Document node <%d>",type)
+        User::Leave( KErrArgument );
+        }
+
+    // Get character encoding (NW_Uint32)
+    iCharEncoding = NW_DOM_DocumentNode_getCharacterEncoding( domNode );
+    PUSHLOG_WRITE_FORMAT("CSIContentHandler::ParsePushMsgL: Doc char encoding <%x>",iCharEncoding)
+
+    /**********************************
+    *   ELEMENT si
+    ***********************************/
+    // Get the first element of the document that must be an si.
+	// 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("CSIContentHandler::ParsePushMsgL: check if Dom tree has <SI> node <%d>", domNodeHasChildNodes)
+	if (!domNodeHasChildNodes)
+        {
+        PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: No SI element present in the dom tree. Message corrupted.")
+        User::Leave( KErrCorrupt );
+        }
+
+	PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: before calling getDocumentElement")
+    NW_DOM_ElementNode_t* siElement =
+        NW_DOM_DocumentNode_getDocumentElement( domNode );
+	PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: after calling getDocumentElement")
+	if (!siElement)
+		{
+		PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: siElement is Null")
+		}
+	PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: siElement is not Null, before leaving")
+    User::LeaveIfNull( siElement );
+	PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: siElement is not Null, after leaving if siElement is null")
+
+    type = NW_DOM_Node_getNodeType( siElement );
+
+    CStringOwner* stringOwner = new (ELeave) CStringOwner;
+    CleanupStack::PushL( stringOwner );
+
+    NW_String_t* name = NW_String_new();
+    User::LeaveIfNull( name );
+    // Let name be on the Cleanup Stack.
+    stringOwner->SetString( name );
+    stat = NW_DOM_Node_getNodeName( siElement, name );
+    User::LeaveIfError( NwxStatusToErrCode( stat ) );
+    NW_Byte*  nameBuf = NW_String_getStorage( name );
+    NW_Uint16 nameLen = NW_String_getCharCount( name, iCharEncoding );
+    TPtrC8 namePtr( nameBuf, nameLen );
+
+    // Now comes the validity check.
+    if ( type != NW_DOM_ELEMENT_NODE || namePtr.CompareF( KSi ) != 0 )
+        {
+        PUSHLOG_WRITE_FORMAT("CSIContentHandler::ParsePushMsgL: Not si element node <%d>",type)
+        User::Leave( KErrArgument );
+        }
+
+    CleanupStack::PopAndDestroy( stringOwner ); // stringOwner
+
+    /**********************************
+    *   ELEMENT indication
+    ***********************************/
+    if ( NW_DOM_Node_hasChildNodes( siElement ) )
+        {
+        NW_DOM_Node_t* node = NW_DOM_Node_getFirstChild( siElement );
+		if (!node)
+			{
+			PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: no si child nodes!")
+			}
+        User::LeaveIfNull( node );
+
+        // Find the indication element.
+        TBool indicationFound = EFalse;
+        do {
+            type = NW_DOM_Node_getNodeType( node );
+
+            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( node, name );
+			PUSHLOG_WRITE_FORMAT("CSIContentHandler::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( KIndication ) == 0 )
+                {
+                // We found the indication element. Parse it.
+                PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: indication under si found.")
+                indicationFound = ETrue;
+                NW_DOM_ElementNode_t* indicationElement =
+                    REINTERPRET_CAST( NW_DOM_ElementNode_t*, node );
+                ParseIndicationL( *indicationElement );
+                }
+
+            CleanupStack::PopAndDestroy( stringOwner ); // stringOwner
+
+            if ( !indicationFound )
+                {
+                // Iterate next.
+                node = NW_DOM_Node_getNextSibling( node );
+                if ( !node )
+                    {
+                    PUSHLOG_WRITE("CSIContentHandler::ParsePushMsgL: No more sibling.")
+                    break;
+                    }
+                }
+
+            } while ( !indicationFound );
+        }
+
+    // Cleanup.
+    CleanupStack::PopAndDestroy( 2, &wbxmlDict ); // docTreeOwner, wbxmlDict
+
+    if ( !ActionFlag() )
+        {
+        // default if no action explicitly stated
+        iPushMsgAction = CSIPushMsgEntry::ESIPushMsgSignalMedium;
+        SetActionFlag( ETrue );
+        PUSHLOG_WRITE_FORMAT("CSIContentHandler::ParsePushMsgL: Defaulting to ActionFlag: %d",iPushMsgAction)
+        }
+
+    iState = EProcessing;
+	IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ParsePushMsgL")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::ParseIndicationL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::ParseIndicationL( NW_DOM_ElementNode_t& aIndication )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ParseIndicationL")
+
+    NW_Status_t stat = NW_STAT_SUCCESS;
+    NW_DOM_NodeType_t type = 0;
+
+    if ( NW_DOM_ElementNode_hasAttributes( &aIndication ) )
+        {
+        NW_DOM_AttributeListIterator_t attrIter;
+        stat = NW_DOM_ElementNode_getAttributeListIterator
+                             ( &aIndication, &attrIter );
+		PUSHLOG_WRITE_FORMAT("CSIContentHandler::ParseIndicationL: 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 )
+            {
+            ParseIndAttributeL( attrHandle );
+            }
+        }
+
+    /**********************************
+    *   PCDATA of ELEMENT indication
+    ***********************************/
+    if ( NW_DOM_Node_hasChildNodes( &aIndication ) )
+        {
+        NW_DOM_TextNode_t* textNode =
+            NW_DOM_Node_getFirstChild( &aIndication );
+        User::LeaveIfNull( textNode );
+
+        type = NW_DOM_Node_getNodeType( textNode );
+        if ( type != NW_DOM_TEXT_NODE )
+            {
+            PUSHLOG_WRITE_FORMAT("CSIContentHandler::ParseIndicationL: Not text node <%d>",type)
+            User::Leave( KErrArgument );
+            }
+
+        ParseTextL( *textNode );
+        }
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ParseIndicationL")
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::ParseIndAttributeL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::ParseIndAttributeL( NW_DOM_AttributeHandle_t&
+                                            aAttrHandle )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ParseIndAttributeL")
+
+    NW_Status_t stat = NW_STAT_SUCCESS;
+
+    CStringOwner* stringOwner = new (ELeave) CStringOwner;
+    CleanupStack::PushL( stringOwner );
+
+    NW_String_t* attrName = NW_String_new();
+    User::LeaveIfNull( attrName );
+    stringOwner->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( KCreatedAttrib ) == 0 )
+        {
+        if ( CreatedFlag() )
+            {
+            PUSHLOG_WRITE(" created redefinition")
+            User::Leave( KErrCorrupt );
+            }
+        else
+            {
+            TBool gotDate = AttributeToTTimeL( aAttrHandle, iCreatedTime );
+            SetCreatedFlag( gotDate );
+            PUSHLOG_WRITE_FORMAT(" iCreatedTime set %d",gotDate?1:0)
+            }
+        }
+    else if ( attrNamePtr.CompareF( KHrefAttrib ) == 0 )
+        {
+        if ( HrefFlag() )
+            {
+            PUSHLOG_WRITE(" href redefinition")
+            User::Leave( KErrCorrupt );
+            }
+        else
+            {
+            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 );
+            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 );
+                if ( length == 0 )
+                    {
+                    // Zero length href attribute is considered as missing.
+                    PUSHLOG_WRITE(" Zero length HrefFlag");
+                    }
+                else
+                    {
+                    TPtrC8 hrefPtr( storage, length );
+                    HBufC* tempHrefBuf = HBufC::NewMaxL( length );
+                    // No leavable after it!!! until...
+                    tempHrefBuf->Des().Copy( hrefPtr );
+                    iHrefBuf = tempHrefBuf; // ...until here.
+                    SetHrefFlag( ETrue );
+                    PUSHLOG_WRITE_FORMAT(" HrefFlag set <%S>",iHrefBuf);
+                    }
+                }
+
+            CleanupStack::PopAndDestroy( stringOwner ); // stringOwner
+            }
+        }
+    else if ( attrNamePtr.CompareF( KExpiresAttrib ) == 0 )
+        {
+        if ( ExpiresFlag() )
+            {
+            PUSHLOG_WRITE(" expires redefinition")
+            User::Leave( KErrCorrupt );
+            }
+        else
+            {
+            TBool gotDate = AttributeToTTimeL( aAttrHandle, iExpiresTime );
+            SetExpiresFlag( gotDate );
+            PUSHLOG_WRITE_FORMAT(" iExpiresTime set %d",gotDate?1:0)
+            }
+        }
+    else if ( attrNamePtr.CompareF( KSiIdAttrib ) == 0 )
+        {
+        if ( SiIdFlag() )
+            {
+            PUSHLOG_WRITE(" si-id redefinition")
+            User::Leave( KErrCorrupt );
+            }
+        else
+            {
+            // It is expected to be String.
+            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 siidPtr( storage, length );
+
+            iSiIdBuf = HBufC::NewMaxL( siidPtr.Length() );
+            iSiIdBuf->Des().Copy( siidPtr );
+            SetSiIdFlag( ETrue );
+            PUSHLOG_WRITE_FORMAT(" SiIdFlag set <%S>",iSiIdBuf)
+
+            CleanupStack::PopAndDestroy( stringOwner ); // stringOwner
+            }
+        }
+    else if ( attrNamePtr.CompareF( KActionAttrib ) == 0 )
+        {
+        if ( ActionFlag() )
+            {
+            PUSHLOG_WRITE(" action redefinition")
+            User::Leave( KErrCorrupt );
+            }
+        else
+            {
+            // It is expected to be String.
+            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 ); // stringOwner
+            }
+        }
+    else
+        {
+        __ASSERT_DEBUG( EFalse,
+            ContHandPanic( EPushContHandPanUnexpSiToken ) );
+        }
+
+    CleanupStack::PopAndDestroy( stringOwner ); // stringOwner
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ParseIndAttributeL")
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::ParseTextL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::ParseTextL( NW_DOM_TextNode_t& aTextNode )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ParseTextL")
+
+    if ( DataFlag() )
+        {
+        PUSHLOG_WRITE(" Data flag already set.")
+        }
+    else
+        {
+        CStringOwner* stringOwner = new (ELeave) CStringOwner;
+        CleanupStack::PushL( stringOwner );
+
+        NW_String_t* data = NW_String_new();
+        User::LeaveIfNull( data );
+        stringOwner->SetString( data );
+        NW_Status_t stat = NW_STAT_SUCCESS;
+        stat = NW_DOM_TextNode_getData( &aTextNode, data );
+        User::LeaveIfError( NwxStatusToErrCode( stat ) );
+
+        HBufC16* ucs2buffer = ConvertToUnicodeL( *data, iCharEncoding );
+        // Be careful: ucs2buffer is not on the CleanupStack!
+        __ASSERT_DEBUG( ucs2buffer != 0, ContHandPanic( EPushContHandPanNullUcs2Buf ) );
+
+        TPtrC16 ucs2ptrC( *ucs2buffer );
+        if ( ucs2ptrC.Length() == 0 )
+            {
+            // Zero length data is considered as nothing.
+            PUSHLOG_WRITE(" Zero length Data");
+            }
+        else
+            {
+            PUSHLOG_WRITE_FORMAT(" Data: <%S>",&ucs2ptrC);
+
+            #ifdef __TEST_LOG__
+            // Write out each unicode character identifier
+            TInt length = ucs2ptrC.Length();
+            for (TInt logI=0;logI<length;logI++)
+                {
+                TBuf16<1> currChar;
+                currChar.Copy( ucs2ptrC.Mid( logI, /*aLength*/1 ) );
+                PUSHLOG_WRITE_FORMAT2(" 0x%x %S",currChar[0],&currChar);
+                }
+            #endif // __TEST_LOG__
+
+            iData = ucs2buffer; // Ownership transferred.
+            ucs2buffer = NULL;
+            SetDataFlag( ETrue );
+            }
+
+        CleanupStack::PopAndDestroy( stringOwner );
+        }
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ParseTextL")
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::ConvertToUnicodeL
+// ---------------------------------------------------------
+//
+HBufC16* CSIContentHandler::ConvertToUnicodeL( const TDesC8& aSrc, TUint aCharSetId )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ConvertToUnicodeL");
+
+    __ASSERT_DEBUG( aCharSetId != 0, ContHandPanic( EPushContHandPanNullCharSetId ) );
+
+    InitialiseCharacterSetConverterL();
+
+    HBufC16* ucs2buffer = NULL; // The return value.
+    TBool resultOnStack = EFalse;
+
+    #ifdef __TEST_LOG__
+    // Write out the original 8-bit buffer
+    TInt origiLength = aSrc.Length();
+    for (TInt origiLogI=0;origiLogI<origiLength;origiLogI++)
+        {
+        TBuf16<1> currChar; // Only 16-bit buffer can be written out.
+        currChar.Copy( aSrc.Mid( origiLogI, /*aLength*/1 ) );
+        PUSHLOG_WRITE_FORMAT2(" 0x%x %S",currChar[0],&currChar);
+        }
+    #endif // __TEST_LOG__
+
+    // Result
+    HBufC16* buffer = HBufC16::NewLC( KPushConversionBufferSize );
+    PUSHLOG_WRITE(" buffer allocated");
+    TPtr16 ptr( buffer->Des() );
+
+    // Prepare conversion for the given charset ID.
+    RFs& fs = iMsvSession->FileSession();
+    iCharacterSetConverter->PrepareToConvertToOrFromL
+        ( aCharSetId, *iCharacterSetsAvailable, fs );
+    PUSHLOG_WRITE(" PrepareToConvertToOrFromL OK");
+
+    TInt state = 0;
+    TInt remaining = iCharacterSetConverter->ConvertToUnicode( ptr, aSrc, state );
+    PUSHLOG_WRITE_FORMAT(" remaining: %d",remaining);
+    while ( remaining >= 0 )
+        {
+        if ( ucs2buffer == NULL )
+            {
+            ucs2buffer = HBufC::NewLC( ptr.Length() );
+            resultOnStack = ETrue;
+            }
+        else
+            {
+            __ASSERT_DEBUG( resultOnStack,
+                ContHandPanic( EPushContHandPanSiResNotOnStack ) );
+            // This may change the address of ucs2buffer so we need to put
+            // it on the cleanup stack again!!
+            ucs2buffer = ucs2buffer->ReAllocL
+                ( ucs2buffer->Length() + ptr.Length() );
+            CleanupStack::Pop();    // old ucs2buffer
+            CleanupStack::PushL( ucs2buffer );  // possibly new copy
+            PUSHLOG_WRITE(" ucs2buffer reallocated");
+            }
+        TPtr16 ucs2ptr( ucs2buffer->Des() );
+        ucs2ptr.Append( ptr );
+        if ( remaining > 0 )
+            {
+            // Try to convert all remaining characters.
+            ptr.Zero();
+            remaining = iCharacterSetConverter->ConvertToUnicode
+                ( ptr, aSrc.Right( remaining ), state );
+            PUSHLOG_WRITE_FORMAT(" remaining: %d",remaining);
+            }
+        else
+            {
+            PUSHLOG_WRITE(" break");
+            break;
+            }
+        }
+
+    if ( resultOnStack )
+        {
+        CleanupStack::Pop();    // ucs2buffer
+        resultOnStack = EFalse;
+        }
+
+    // ucs2buffer is not on the CleanupStack!
+
+    CleanupStack::PopAndDestroy( buffer ); // buffer
+
+    if ( ucs2buffer == NULL )
+        {
+        PUSHLOG_WRITE(" NULL ucs2buffer - allocating an empty buf");
+        ucs2buffer = KNullDesC().AllocL();
+        }
+    else
+        {
+        // Check if first character is a Zero-width nbsp.
+        TPtrC16 ucs2ptrC( *ucs2buffer );
+        if ( ucs2ptrC.Length() >= 1 && ucs2ptrC[0] == KPushZeroWidthNbsp )
+            {
+            // First character is a Zero-width NBSP. This character is used as
+            // BOM in some encodings and should not be present at this point.
+            // But we are tolerant and remove it.
+            // (Not expecting big-endianness here.)
+            HBufC16* temp = ucs2buffer;
+            CleanupStack::PushL( temp );
+            ucs2buffer = ucs2ptrC.Mid( 1 ).AllocL();
+            CleanupStack::PopAndDestroy( temp ); // temp
+            PUSHLOG_WRITE(" BOM removed");
+            }
+        else
+            {
+            PUSHLOG_WRITE(" No BOM");
+            }
+        }
+
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ConvertToUnicodeL");
+    return ucs2buffer;
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::ConvertToUnicodeL
+// ---------------------------------------------------------
+//
+HBufC16* CSIContentHandler::ConvertToUnicodeL
+    ( NW_String_t& aString, NW_Uint32 aCharEncoding )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ConvertToUnicodeL");
+
+    /* As cXmlLibrary does, we support only the following charsets:
+    #define HTTP_iso_10646_ucs_2        0x03E8
+    #define HTTP_iso_8859_1             0x04
+    #define HTTP_us_ascii               0x03
+    #define HTTP_utf_8                  0x6A
+    #define HTTP_utf_16                 1015
+    */
+    TUint id = 0;
+    if ( aCharEncoding == HTTP_iso_10646_ucs_2 )
+        {
+        id = KCharacterSetIdentifierUcs2;
+        PUSHLOG_WRITE(" KCharacterSetIdentifierUcs2")
+        }
+    else if ( aCharEncoding == HTTP_iso_8859_1 )
+        {
+        id = KCharacterSetIdentifierIso88591;
+        PUSHLOG_WRITE(" KCharacterSetIdentifierIso88591")
+        }
+    else if ( aCharEncoding == HTTP_us_ascii )
+        {
+        id = KCharacterSetIdentifierAscii;
+        PUSHLOG_WRITE(" KCharacterSetIdentifierAscii")
+        }
+    else if ( aCharEncoding == HTTP_utf_8 )
+        {
+        id = KCharacterSetIdentifierUtf8;
+        PUSHLOG_WRITE(" KCharacterSetIdentifierUtf8")
+        }
+    else if ( aCharEncoding == HTTP_utf_16 ) // No such in CharConv.h
+        {
+        id = KCharacterSetIdentifierUcs2;
+        PUSHLOG_WRITE(" KCharacterSetIdentifierUcs2")
+        }
+    else
+        {
+        id = KCharacterSetIdentifierUtf8; // Defaulting to UTF-8
+        PUSHLOG_WRITE(" DEFAULTING to KCharacterSetIdentifierUtf8");
+        }
+
+    PUSHLOG_WRITE_FORMAT(" id: 0x%x",id);
+    __ASSERT_DEBUG( id != 0, ContHandPanic( EPushContHandPanNullCharSetId ) );
+
+    // Source
+    PUSHLOG_WRITE_FORMAT(" Storage: 0x%x",NW_String_getStorage(&aString));
+    PUSHLOG_WRITE_FORMAT(" Byte count: %d",NW_String_getByteCount(&aString)-1);
+
+    // We will use NW_String_getByteCount(&aString)-1 as size, because
+    // NW_String_getByteCount(&aString) includes NULL terminator.
+    const TPtrC8 src( NW_String_getStorage(&aString),
+                      NW_String_getByteCount(&aString)-1 );
+    HBufC16* ucs2buffer = ConvertToUnicodeL( src, id );
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ConvertToUnicodeL");
+    return ucs2buffer;
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::InitialiseCharacterSetConverterL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::InitialiseCharacterSetConverterL()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::InitialiseCharacterSetConverterL")
+
+    iCharacterSetConverter = CCnvCharacterSetConverter::NewL();
+
+    RFs& fs = iMsvSession->FileSession();
+    iCharacterSetsAvailable =
+        CCnvCharacterSetConverter::CreateArrayOfCharacterSetsAvailableL( fs );
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::InitialiseCharacterSetConverterL")
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::ConvertActionString
+// ---------------------------------------------------------
+//
+TUint CSIContentHandler::ConvertActionString
+                         ( const TDesC8& aActionString ) const
+	{
+	const TInt KMatchFound = 0;
+
+	// set to default signal value (to rid ourselves of build warning)
+	TUint actionValue = CSIPushMsgEntry::ESIPushMsgSignalMedium;
+
+	if ( aActionString.Compare( KDeleteAction ) == KMatchFound )
+        {
+		actionValue = CSIPushMsgEntry::ESIPushMsgDelete;
+        }
+	else if ( aActionString.Compare( KSignalNone ) == KMatchFound )
+        {
+		actionValue = CSIPushMsgEntry::ESIPushMsgSignalNone;
+        }
+	else if ( aActionString.Compare( KSignalLow ) == KMatchFound )
+        {
+		actionValue = CSIPushMsgEntry::ESIPushMsgSignalLow;
+        }
+	else if ( aActionString.Compare( KSignalMedium ) == KMatchFound )
+        {
+		actionValue = CSIPushMsgEntry::ESIPushMsgSignalMedium;
+        }
+	else if ( aActionString.Compare( KSignalHigh ) == KMatchFound )
+        {
+		actionValue = CSIPushMsgEntry::ESIPushMsgSignalHigh;
+        }
+
+	return actionValue;
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::SetSIPushMsgEntryFieldsL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::SetSIPushMsgEntryFieldsL( CSIPushMsgEntry&
+                                                  aSIPushMsgEntry )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::SetSIPushMsgEntryFieldsL")
+
+	if ( SiIdFlag() || HrefFlag() )
+		{
+		if ( SiIdFlag() && ( HrefFlag() == EFalse ) )
+			{
+            // Message has only si-id but no href.
+            aSIPushMsgEntry.SetIdL( *iSiIdBuf );
+			}
+		else if ( HrefFlag() && ( SiIdFlag() == EFalse ) )
+			{
+            // If message has no si-id but does have a href, use href as si-id.
+            aSIPushMsgEntry.SetIdL( *iHrefBuf );
+            aSIPushMsgEntry.SetUrlL( *iHrefBuf );
+			}
+		else
+            {
+            // Use si-id and href as is.
+            aSIPushMsgEntry.SetIdL( *iSiIdBuf );
+            aSIPushMsgEntry.SetUrlL( *iHrefBuf );
+            }
+		}
+
+    __ASSERT_DEBUG( ActionFlag(),
+                    ContHandPanic( EPushContHandPanUnspecSiAction ) );
+	if ( ActionFlag() )
+        {
+		aSIPushMsgEntry.SetAction( iPushMsgAction );
+        }
+	else // default if no action explicitly stated
+        {
+		aSIPushMsgEntry.SetAction( CSIPushMsgEntry::ESIPushMsgSignalMedium );
+        }
+
+	// uses default null time value if no explicit date set in message
+	aSIPushMsgEntry.SetCreated( iCreatedTime );
+	aSIPushMsgEntry.SetExpires( iExpiresTime );
+
+	// PCDATA (text) from message
+	if ( DataFlag() )
+        {
+		aSIPushMsgEntry.SetTextL( *iData );
+        }
+
+	TPtrC8 msgHeaderPtr;
+	iMessage->GetHeader( msgHeaderPtr );
+	aSIPushMsgEntry.SetHeaderL( msgHeaderPtr );
+
+    // Get server address.
+    TPtrC8 srvAddress;
+    if ( iMessage->GetServerAddress( srvAddress ) )
+        {
+	    aSIPushMsgEntry.SetFromL( srvAddress );
+        }
+
+    // First line in Inbox: TMsvEntry::iDetails.
+    if ( srvAddress.Length() == 0 )
+        {
+        // Read from resource.
+        HBufC* details =
+            iStrRscReader->AllocReadResourceLC( R_PUSHMISC_UNK_SENDER );
+        aSIPushMsgEntry.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 );
+        //
+        aSIPushMsgEntry.SetMsgDetailsL( *convertedFrom );
+        //
+        CleanupStack::PopAndDestroy( 2, details ); // convertedFrom, details
+        }
+
+    // Second line in Inbox: TMsvEntry::iDescription.
+	if ( DataFlag() )
+        {
+        // Display SI message.
+		aSIPushMsgEntry.SetMsgDescriptionL( *iData );
+        }
+    else
+        {
+        // Display URL.
+        __ASSERT_DEBUG( HrefFlag(),
+                        ContHandPanic( EPushContHandPanUnspecSiHref ) );
+        const TPtrC url = aSIPushMsgEntry.Url();
+        HBufC* convertedUrl = CPushMtmUtil::ConvertUriToDisplayFormL( url );
+        CleanupStack::PushL( convertedUrl );
+        //
+        aSIPushMsgEntry.SetMsgDescriptionL( *convertedUrl );
+        //
+        CleanupStack::PopAndDestroy( convertedUrl ); // convertedUrl
+        }
+
+    // ******** 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 CSIPushMsgEntry. The latter
+    * solution is more expensive so we choose the first.
+    */
+    TMsvEntry& tEntry = CONST_CAST( TMsvEntry&, aSIPushMsgEntry.Entry() );
+    if ( HrefFlag() )
+        {
+        CPushMtmUtil::SetAttrs( tEntry, EPushMtmAttrHasHref );
+        }
+    else
+        {
+        CPushMtmUtil::ResetAttrs( tEntry, EPushMtmAttrHasHref );
+        }
+
+    // *** Set the entry to unread and new state. ***
+
+    tEntry.SetNew( ETrue );
+    tEntry.SetUnread( ETrue );
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::SetSIPushMsgEntryFieldsL")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::ProcessingPushMsgEntryL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::ProcessingPushMsgEntryL()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ProcessingPushMsgEntryL")
+
+	TBool deletePushMsg( EFalse );
+
+    __ASSERT_DEBUG( ActionFlag(),
+                    ContHandPanic( EPushContHandPanUnspecSiAction ) );
+
+    // S60 requirement: if both the href and the message is empty then
+    // delete the msg.
+    if ( HrefFlag() == EFalse && DataFlag() == EFalse )
+        {
+        deletePushMsg = ETrue;
+        }
+
+    // Expiration.
+    if ( !deletePushMsg && ExpiresFlag() )
+        {
+	    TTime today;
+	    today.UniversalTime();
+#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;
+        today.FormatL( dateHolder, KDateFormat );
+        today.FormatL( timeHolder, KTimeFormat );
+        PUSHLOG_WRITE_FORMAT(" now date: <%S>",&dateHolder)
+        PUSHLOG_WRITE_FORMAT(" now time: <%S>",&timeHolder)
+        iExpiresTime.FormatL( dateHolder, KDateFormat );
+        iExpiresTime.FormatL( timeHolder, KTimeFormat );
+        PUSHLOG_WRITE_FORMAT(" exp date: <%S>",&dateHolder)
+        PUSHLOG_WRITE_FORMAT(" exp time: <%S>",&timeHolder)
+#endif // __TEST_LOG__
+	    // check if message has expiry date before today's date
+	    if ( iExpiresTime < today )
+		    {
+            PUSHLOG_WRITE("CSIContentHandler already expired")
+		    deletePushMsg = ETrue;
+		    }
+        }
+
+	// An SI with the action attribute set to “delete” MUST have an
+    // explicitly assigned value for si-id.
+	if ( !deletePushMsg && ActionFlag() )
+		{
+		if ( iPushMsgAction == CSIPushMsgEntry::ESIPushMsgDelete )
+            {
+            if ( !SiIdFlag() || ( SiIdFlag() && iSiIdBuf->Length() == 0 ) )
+                {
+                deletePushMsg = ETrue;
+                }
+            }
+        }
+
+    // Handling out of order delivery & Replacement.
+    TMsvId matchingEntryId = KMsvNullIndexEntryId;
+
+    if ( !deletePushMsg && ( SiIdFlag() || HrefFlag() ) && CreatedFlag() )
+        {
+        deletePushMsg = HandleMsgOrderReceptionL( matchingEntryId );
+        }
+
+    if ( !deletePushMsg && ActionFlag() )
+        {
+        // SI with action=signal-none must not be presented to the end-user.
+        // Note. In S60 signal-none behaves the same as delete: the
+        // message is discarded after processing it!
+        if ( iPushMsgAction == CSIPushMsgEntry::ESIPushMsgSignalNone )
+            {
+            deletePushMsg = ETrue;
+            }
+        // SI with action=delete must also be discarded.
+        else if ( iPushMsgAction == CSIPushMsgEntry::ESIPushMsgDelete )
+            {
+            deletePushMsg = ETrue;
+            }
+        }
+
+	// Store message if not marked for deletion.
+	if ( !deletePushMsg )
+        {
+		StoreSIMessageL( matchingEntryId );
+        }
+    else
+        {
+        // The new entry must be discarded.
+        // Delete the corresponding matching entry, too.
+        if ( matchingEntryId != KMsvNullIndexEntryId )
+            {
+            iWapPushUtils->DeleteEntryL( matchingEntryId );
+            }
+        }
+
+	iState = EDone;
+	IdleComplete();
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ProcessingPushMsgEntryL")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::StoreSIMessageL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::StoreSIMessageL( TMsvId aMatchingEntryId )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::StoreSIMessageL")
+
+	CSIPushMsgEntry* siEntry = CSIPushMsgEntry::NewL();
+	CleanupStack::PushL( siEntry );
+
+    if ( aMatchingEntryId != KMsvNullIndexEntryId )
+    {
+       PUSHLOG_WRITE("Matching SI found");
+       //Delete this old entry
+       iWapPushUtils->DeleteEntryL( aMatchingEntryId );
+    }
+
+    SetSIPushMsgEntryFieldsL( *siEntry );
+    iSavedMsgId = siEntry->SaveL( *iMsvSession, KMsvGlobalInBoxIndexEntryId );
+
+#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 = siEntry->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( siEntry ); // siEntry
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::StoreSIMessageL")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::HandleMsgOrderReceptionL
+// ---------------------------------------------------------
+//
+TBool CSIContentHandler::HandleMsgOrderReceptionL( TMsvId& aMatchingEntryId )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::HandleMsgOrderReceptionL")
+
+    __ASSERT_DEBUG( ( SiIdFlag() || HrefFlag() ),
+            ContHandPanic( EPushContHandPanNoSiIdOrHrefAttr ) );
+    __ASSERT_DEBUG( CreatedFlag(),
+            ContHandPanic( EPushContHandPanNoCreatedAttr ) );
+
+    CMsvEntrySelection* matchingIdList = NULL;
+	TBool discardPushMsg( EFalse );
+
+	// Get list of matching stored SI messages.
+	if ( SiIdFlag() && iSiIdBuf->Length() != 0 )
+        {
+		matchingIdList = iWapPushUtils->FindSiIdLC( *iSiIdBuf );
+        }
+	else // HrefFlag()
+        {
+        // Use href as si-id.
+		matchingIdList = iWapPushUtils->FindSiIdLC( *iHrefBuf );
+        }
+    const TInt matchingListCount( matchingIdList->Count() );
+    // Note that the count can be greater than 1.
+
+    PUSHLOG_WRITE_FORMAT("CSIContentHandler Count: %d",matchingListCount)
+
+	if ( 0 < matchingListCount && CreatedFlag() )
+		{
+		CSIPushMsgEntry* siEntry = CSIPushMsgEntry::NewL();
+		CleanupStack::PushL( siEntry );
+
+		// Delete older stored messages and/or mark current message for
+        // deletion if same date or older than stored messages
+        TBool foundOneToBeReplaced = EFalse;
+		for ( TInt count = 0; count < matchingListCount; ++count )
+			{
+			TMsvId matchingSiMsgEntryId( matchingIdList->At(count) );
+
+            siEntry->RetrieveL( *iMsvSession, matchingSiMsgEntryId );
+
+			// Skip date comparisons if creation date not valid -
+            // SI without created attribute never gets replaced.
+			TTime existingSiCreatedTime( siEntry->Created() );
+
+			if ( existingSiCreatedTime == Time::NullTTime() )
+                {
+				// continue;
+                }
+            else
+                {
+                __ASSERT_DEBUG( !foundOneToBeReplaced,
+                                ContHandPanic( EPushContHandPanTooManySi ) );
+                if ( foundOneToBeReplaced )
+                    {
+                    PUSHLOG_WRITE(" Already found one")
+                    // Only one SI has to be found.
+                    // If the program runs into it, then make a
+                    // garbage collection to ensure consistency and
+                    // remove other messages found.
+                    iWapPushUtils->DeleteEntryL( matchingSiMsgEntryId );
+                    // After the 'for' only one SI is allowed that has created
+                    // attribute.
+                    }
+                else
+                    {
+                    foundOneToBeReplaced = ETrue; // A match was found.
+                    // Check if received SI is newer than existing stored Si
+                    // (out of order).
+                    if ( iCreatedTime > existingSiCreatedTime )
+	                    {
+                        PUSHLOG_WRITE(" Replacing...")
+                        // The new SI replaces the existing.
+                        aMatchingEntryId = matchingSiMsgEntryId;
+                        discardPushMsg = EFalse;
+	                    }
+                    else if ( iCreatedTime <= existingSiCreatedTime )
+                        {
+                        PUSHLOG_WRITE(" Discarding...")
+                        // Received SI is older than existing stored Si.
+                        discardPushMsg = ETrue;
+                        }
+                    }
+                }
+			}
+
+		CleanupStack::PopAndDestroy( siEntry ); // siEntry
+		}
+
+	CleanupStack::PopAndDestroy( matchingIdList ); // matchingIdList
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::HandleMsgOrderReceptionL")
+    return discardPushMsg;
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::ConvertDateTimeL
+// ---------------------------------------------------------
+//
+TBool CSIContentHandler::ConvertDateTimeL( const TDesC& aDateTime,
+                                           TTime& aConvertedDate ) const
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ConvertDateTimeL")
+
+	TTime convertedTime = Time::NullTTime();
+	TBool convertedOK = EFalse;
+
+    // check supplied descriptor is the correct length
+	if ( aDateTime.Length() != KValidUTCLength )
+        {
+        PUSHLOG_WRITE_FORMAT(" invalid UTC length <%d>",aDateTime.Length())
+        User::Leave( KErrCorrupt );
+        }
+    else
+		{
+		TBuf<KValidUTCLength> str = aDateTime;
+        PUSHLOG_WRITE_FORMAT(" datetime str: <%S>",&str)
+		if ( !IsValidUTCTime( str ) )
+            {
+            // The UTC time is invalid.
+            PUSHLOG_WRITE(" invalid UTC time")
+            User::Leave( KErrCorrupt );
+            }
+        else
+			{
+            // Now 'str' is in format YYYYMMDD:HHMMSS
+			// Adjust UTC time to zero offset TTime. Only month and day
+            // is effected.
+			const TInt KFirstMonthChar = KValidTTimeMonthStart;
+			const TInt KSecondMonthChar = KFirstMonthChar + 1;
+			const TInt KFirstDayChar = KValidTTimeDayStart;
+			const TInt KSecondDayChar = KFirstDayChar + 1;
+            // Month.
+			// check for special case of month = 10 which becomes 09
+			if ( str[KFirstMonthChar] == '1' && str[KSecondMonthChar] == '0' )
+				{
+				str[KFirstMonthChar] = '0';
+				str[KSecondMonthChar] = '9';
+				}
+			else
+                {
+				// month value is either 11, 12 or less than 10, ie 1 - 9.
+				// reduce day by one, eg 11 becomes 10, 12 becomes 11, 09 becomes 08
+				str[KSecondMonthChar]--;
+                }
+
+            // Day.
+			// check for special cases 10, 20, 30
+			if ( str[KSecondDayChar] == '0' )
+				{
+				// reduce day by 1, ie 10 becomes 09, 20 becomes 19 ...
+				str[KSecondDayChar] = '9';
+				str[KFirstDayChar]--;
+				}
+			else
+                {
+				// day value is between 1 and 9 so reduce day by one
+				// eg 29 becomes 28, 11 bcomes 10, 31 becomes 30
+				str[KSecondDayChar]--;
+                }
+
+			// string is now syntaxically correct, but Set() will return an
+            // error if it's semantically incorrect.
+            User::LeaveIfError( convertedTime.Set( str ) );
+			convertedOK = ETrue;
+			}
+		}
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::ConvertDateTimeL")
+	aConvertedDate = convertedTime;
+	return convertedOK;
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::ConvertOpaqueToUtcL
+// ---------------------------------------------------------
+//
+HBufC* CSIContentHandler::ConvertOpaqueToUtcL( const TDesC8& aOpaque ) const
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::ConvertOpaqueToUtcL")
+
+    const TInt opaqueSize = aOpaque.Size();
+    if ( KValidMaxEncodedDateTimeSize < opaqueSize )
+        {
+        PUSHLOG_WRITE_FORMAT(" Bad OPAQUE size: <%d>",opaqueSize)
+        User::Leave( KErrCorrupt );
+        }
+
+    HBufC* converted = HBufC::NewMaxLC( KValidUTCLength );
+    TPtr convertedPtr = converted->Des();
+    convertedPtr.SetLength( 0 ); // Reset.
+
+    // Split up each opaque byte to two bytes.
+    TUint8 byte( 0x00 );
+    TUint8 high( 0x00 );
+    TUint8 low( 0x00 );
+    TInt i = 0;
+    for ( i = 0; i < opaqueSize; ++i )
+        {
+        byte = aOpaque[i];
+        high = (TUint8)( (byte & 0xF0) >> 4 );
+        low  = (TUint8)(  byte & 0x0F );
+        // Check high and low if they are in the range [0-9].
+        if ( 9 < high || 9 < low )
+            {
+            PUSHLOG_WRITE_FORMAT2(" Overflow: <%d, %d>",high,low)
+            User::Leave( KErrOverflow );
+            }
+        convertedPtr.Append( TChar(KAsciiZeroCharCode + high) );
+        convertedPtr.Append( TChar(KAsciiZeroCharCode + low) );
+        }
+
+    // A valid UTC %Datetime contains 14 numerical characters and 6
+    // non-numerical: “1999-04-30T06:40:00Z”.
+    // So fill the remaining bytes with zeros.
+    for ( i = convertedPtr.Length(); i < KValidUTCNumericals; ++i )
+        {
+        convertedPtr.Append( TChar('0') );
+        }
+
+    // Insert the necessary non-numerical boundary characters.
+    i = 0;
+    // Skip year and insert '-'. (Don't forget to increase i with 1 each time!)
+    i += KValidUTCYearBlockLength;
+    convertedPtr.Insert( i++, KCharMinus );
+    // Skip month and insert '-'.
+    i += KValidUTCOtherBlockLength;
+    convertedPtr.Insert( i++, KCharMinus );
+    // Skip day and insert 'T'.
+    i += KValidUTCOtherBlockLength;
+    convertedPtr.Insert( i++, KCharT );
+    // Skip hour and insert ':'.
+    i += KValidUTCOtherBlockLength;
+    convertedPtr.Insert( i++, KCharColon );
+    // Skip minute and insert ':'.
+    i += KValidUTCOtherBlockLength;
+    convertedPtr.Insert( i++, KCharColon );
+    // Skip second and insert 'Z'.
+    i += KValidUTCOtherBlockLength;
+    convertedPtr.Insert( i++, KCharZ );
+
+    CleanupStack::Pop( converted ); // converted
+    PUSHLOG_LEAVEFN("CSIContentHandler::ConvertOpaqueToUtcL")
+	return converted;
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::IsValidUTCTime
+// ---------------------------------------------------------
+//
+TBool CSIContentHandler::IsValidUTCTime( TDes& aDateTime ) const
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::IsValidUTCTime")
+
+    TBool isValid( ETrue ); // Return value.
+
+    // Now aDateTime has to be in format YYYY-MM-DDTHH:MM:SSZ
+
+    // check supplied descriptor is the correct length
+	if ( aDateTime.Length() != KValidUTCLength )
+        {
+        PUSHLOG_WRITE_FORMAT(" invalid UTC length <%d>",aDateTime.Length())
+        isValid = EFalse;
+        }
+    else
+        {
+	    // strip out formatting characters
+	    TInt formatCharPos = 4;
+	    aDateTime.Delete( formatCharPos, 1 );
+	    // now move through two characters at a time and remove other chars
+	    // to just leave digits
+	    const TInt KRemainingFormatChars = 5;
+        TInt i( 0 );
+	    for ( i = 0; i < KRemainingFormatChars; ++i )
+		    {
+		    formatCharPos += 2;
+		    aDateTime.Delete( formatCharPos, 1 );
+		    }
+
+        // Now aDateTime has to be in format YYYYMMDDHHMMSS
+
+        __ASSERT_DEBUG( aDateTime.Length() == KValidTTimeLength,
+                        ContHandPanic( EPushContHandPanBadTTimeLength ) );
+
+        // now have UTC string stripped of format characters - check remaining
+        // characters are all digits - YYYYMMDDHHMMSS
+        TChar ch;
+        for ( i = 0; i < KValidTTimeLength; ++i )
+		    {
+		    ch = aDateTime[i];
+		    if ( ch.IsDigit() == EFalse )
+                {
+                PUSHLOG_WRITE_FORMAT(" not digit <%d>",i)
+                isValid = EFalse;
+                }
+		    }
+
+        if ( isValid )
+            {
+            /*
+            In YYYYMMDDHHMMSS
+            YYYY = 4 digit year ("0000" ... "9999")
+            MM = 2 digit month ("01"=January, "02"=February ... "12"=December)
+            DD = 2 digit day ("01", "02" ... "31")
+            HH = 2 digit hour, 24-hour timekeeping system ("00" ... "23")
+            MM = 2 digit minute ("00" ... "59")
+            SS = 2 digit second ("00" ... "59")
+            */
+            TInt err;
+            TUint val;
+            // Do not check year. There are no restrictions.
+            // Check month.
+            TLex parser( aDateTime.Mid( KValidTTimeMonthStart,
+                                        KValidTTimeBlockLength ) );
+            err = parser.Val( val, EDecimal );
+            if ( err )
+                {
+                isValid = EFalse;
+                PUSHLOG_WRITE_FORMAT(" parser err: <%d>",err)
+                }
+            else
+                {
+                PUSHLOG_WRITE_FORMAT(" month: <%d>",val)
+                if ( val < 1 || 12 < val )
+                    {
+                    isValid = EFalse;
+                    }
+                }
+            // Check day.
+            if ( isValid )
+                {
+                parser = aDateTime.Mid( KValidTTimeDayStart,
+                                        KValidTTimeBlockLength );
+                err = parser.Val( val, EDecimal );
+                if ( err )
+                    {
+                    isValid = EFalse;
+                    PUSHLOG_WRITE_FORMAT(" parser err: <%d>",err)
+                    }
+                else
+                    {
+                    PUSHLOG_WRITE_FORMAT(" day: <%d>",val)
+                    if ( val < 1 || 31 < val )
+                        {
+                        isValid = EFalse;
+                        }
+                    }
+                }
+            // Check hour.
+            if ( isValid )
+                {
+                parser = aDateTime.Mid( KValidTTimeHourStart,
+                                        KValidTTimeBlockLength );
+                err = parser.Val( val, EDecimal );
+                if ( err )
+                    {
+                    isValid = EFalse;
+                    PUSHLOG_WRITE_FORMAT(" parser err: <%d>",err)
+                    }
+                else
+                    {
+                    PUSHLOG_WRITE_FORMAT(" hour: <%d>",val)
+                    if ( 23 < val )
+                        {
+                        isValid = EFalse;
+                        }
+                    }
+                }
+            // Check minute.
+            if ( isValid )
+                {
+                parser = aDateTime.Mid( KValidTTimeMinuteStart,
+                                        KValidTTimeBlockLength );
+                err = parser.Val( val, EDecimal );
+                if ( err )
+                    {
+                    isValid = EFalse;
+                    PUSHLOG_WRITE_FORMAT(" parser err: <%d>",err)
+                    }
+                else
+                    {
+                    PUSHLOG_WRITE_FORMAT(" min: <%d>",val)
+                    if ( 59 < val )
+                        {
+                        isValid = EFalse;
+                        }
+                    }
+                }
+            // Check second.
+            if ( isValid )
+                {
+                parser = aDateTime.Mid( KValidTTimeSecondStart,
+                                        KValidTTimeBlockLength );
+                err = parser.Val( val, EDecimal );
+                if ( err )
+                    {
+                    isValid = EFalse;
+                    PUSHLOG_WRITE_FORMAT(" parser err: <%d>",err)
+                    }
+                else
+                    {
+                    PUSHLOG_WRITE_FORMAT(" sec: <%d>",val)
+                    if ( 59 < val )
+                        {
+                        isValid = EFalse;
+                        }
+                    }
+                }
+
+	        // insert colon seperating date from time
+	        const TInt KColonPosition = 8;
+	        aDateTime.Insert( KColonPosition, KCharColon );
+
+            // Now aDateTime has to be in format YYYYMMDD:HHMMSS
+            }
+        }
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::IsValidUTCTime")
+	return isValid; // aDateTime contains a modified buffer.
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::AttributeToTTimeL
+// ---------------------------------------------------------
+//
+TBool CSIContentHandler::AttributeToTTimeL
+                        ( NW_DOM_AttributeHandle_t& aAttrHandle,
+                          TTime& aConvertedDate ) const
+    {
+    PUSHLOG_ENTERFN("CSIContentHandler::AttributeToTTimeL")
+
+    TBool gotDate = EFalse;
+    NW_Status_t stat = NW_STAT_SUCCESS;
+    NW_DOM_AttrVal_t attrVal;
+
+    // It is expected to be String or Opaque.
+    // It may be Opaque, to which we will need a NW_DOM_AttrVal_t structure.
+    stat = NW_DOM_AttributeHandle_getNextVal( &aAttrHandle, &attrVal );
+
+    if ( stat != NW_STAT_WBXML_ITERATE_MORE )
+        {
+        User::LeaveIfError( NwxStatusToErrCode( stat ) );
+        }
+    else
+        {
+        NW_Uint16 valType = NW_DOM_AttrVal_getType( &attrVal );
+
+        if ( valType == NW_DOM_ATTR_VAL_STRING )
+            {
+            CStringOwner* stringOwner = new (ELeave) CStringOwner;
+            CleanupStack::PushL( stringOwner );
+
+            NW_String_t* val = NW_String_new();
+            User::LeaveIfNull( val );
+            stringOwner->SetString( val );
+            //
+            stat = NW_DOM_AttrVal_toString( &attrVal, val, iCharEncoding );
+            User::LeaveIfError( NwxStatusToErrCode( stat ) );
+            NW_Byte* storage = NW_String_getStorage( val );
+            NW_Uint16 length = NW_String_getCharCount( val, iCharEncoding );
+            TPtrC8 dataPtr( storage, length );
+            HBufC* dataBuf = HBufC::NewMaxLC( dataPtr.Length() );
+            dataBuf->Des().Copy( dataPtr );
+            gotDate = ConvertDateTimeL( *dataBuf, aConvertedDate );
+
+            CleanupStack::PopAndDestroy( 2, stringOwner ); // dataBuf,
+                                                           // stringOwner
+            }
+        else if ( valType == NW_DOM_ATTR_VAL_OPAQUE )
+            {
+            NW_Uint32 len = 0;
+            NW_Byte* data = NW_DOM_AttrVal_getOpaque( &attrVal, &len );
+            User::LeaveIfNull( data );
+            TPtrC8 dataPtr( data, len );
+
+            HBufC* dateTime = ConvertOpaqueToUtcL( dataPtr );
+            CleanupStack::PushL( dateTime );
+            gotDate = ConvertDateTimeL( *dateTime, aConvertedDate );
+            CleanupStack::PopAndDestroy( dateTime ); // dateTime
+            }
+        else
+            {
+            User::Leave( KErrNotSupported );
+            }
+        }
+
+    PUSHLOG_LEAVEFN("CSIContentHandler::AttributeToTTimeL")
+    return gotDate; // aConvertedDate contains the result.
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::HandleMessageL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::HandleMessageL( CPushMessage* aPushMsg,
+                                        TRequestStatus& aStatus )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::HandleMessageL")
+
+    __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("CSIContentHandler::HandleMessageL")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::HandleMessageL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::HandleMessageL( CPushMessage* aPushMsg )
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::HandleMessageL")
+
+    __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("CSIContentHandler::HandleMessageL")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::CancelHandleMessage
+// ---------------------------------------------------------
+//
+void CSIContentHandler::CancelHandleMessage()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::CancelHandleMessage")
+    Cancel();
+    PUSHLOG_LEAVEFN("CSIContentHandler::CancelHandleMessage")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::CPushHandlerBase_Reserved1
+// ---------------------------------------------------------
+//
+void CSIContentHandler::CPushHandlerBase_Reserved1()
+    {
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::CPushHandlerBase_Reserved2
+// ---------------------------------------------------------
+//
+void CSIContentHandler::CPushHandlerBase_Reserved2()
+    {
+    }
+
+// ---------------------------------------------------------
+// CSIContentHandler::DoCancel
+// ---------------------------------------------------------
+//
+void CSIContentHandler::DoCancel()
+	{
+    PUSHLOG_ENTERFN("CSIContentHandler::DoCancel")
+	Complete( KErrCancel );
+    PUSHLOG_LEAVEFN("CSIContentHandler::DoCancel")
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::RunL
+// ---------------------------------------------------------
+//
+void CSIContentHandler::RunL()
+	{
+    // Handle errors in RunError().
+    PUSHLOG_WRITE_FORMAT("iStatus.Int(): %d",iStatus.Int())
+    User::LeaveIfError( iStatus.Int() );
+
+	// use active state machine routine to manage activites:
+	switch ( iState )
+		{
+	    case EGarbageCollecting:
+            {
+		    CollectGarbageL();
+		    break;
+            }
+	    case EFilteringAndParsing:
+            {
+            if ( !FilterPushMsgL() )
+                {
+                // It did not pass the filter. Done.
+	            iState = EDone;
+	            IdleComplete();
+                }
+            else
+                {
+                // Continue.
+		        TInt ret = KErrNone;
+				PUSHLOG_WRITE("CSIContentHandler::RunL : before trapping parsing.")
+				TRAP(ret, ParsePushMsgL());
+				PUSHLOG_WRITE_FORMAT("CSIContentHandler::RunL : after trapping parsing. ret = %d", ret)
+				if ( ret != KErrNone)
+					{
+					PUSHLOG_WRITE("CSIContentHandler::RunL : Parsing failed. discarding message.")
+					iState = EDone;
+					IdleComplete();
+					}
+                }
+		    break;
+            }
+	    case EProcessing:
+            {
+		    ProcessingPushMsgEntryL();
+		    break;
+            }
+	    case EDone:
+            {
+            PUSHLOG_WRITE("CSIContentHandler EDone")
+		    Complete( KErrNone );
+		    break;
+            }
+	    default:
+            {
+            // JIC.
+            PUSHLOG_WRITE("CSIContentHandler default Done")
+		    Complete( KErrNone );
+		    break;
+            }
+		}
+	}
+
+// ---------------------------------------------------------
+// CSIContentHandler::RunError
+// ---------------------------------------------------------
+//
+TInt CSIContentHandler::RunError( TInt aError )
+	{
+    PUSHLOG_WRITE_FORMAT("CSIContentHandler::RunError: %d",aError)
+
+	iState = EDone;
+	Complete( aError );
+	return KErrNone;
+	}
+