diff -r 6385c4c93049 -r 8e6fa1719340 pushmtm/Plugins/PushContentHandler/CSIContentHandler.cpp --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +// 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( 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 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 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 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 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; + } +