--- /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;
+ }
+