diff -r ebe688cedc25 -r 7fdbb852d323 email/emailnotificationhandler/src/EMNXMLContentHandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/emailnotificationhandler/src/EMNXMLContentHandler.cpp Wed Sep 01 12:31:54 2010 +0100 @@ -0,0 +1,572 @@ +/* +* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Extracts needed attributes from received EMN message. +* +* +*/ + + +#include "EMNXMLContentHandler.h" +#include "EMNLogging.h" + +// Used in compares +_LIT8( KEmnElement, "emn" ); +_LIT8( KMailboxAttribute, "mailbox"); +_LIT8( KTimestampAttribute, "timestamp"); + +// Used for parsing timestamp attribute's value from ASCII message. +_LIT( KColonChar, ":" ); +_LIT( KDashChar, "-" ); +_LIT( KDateTimeSeparator, "T" ); +_LIT( KTimezoneSeparator, "Z" ); + +// Received timestamp string length +const TInt KEMNLengthOfRecvTimeStamp = 20; + +// Index of hour or minute in timestamp string +const TInt KEMNTimestampIncHour = 5; +const TInt KEMNTimestampIncHourMinute = 6; + +// Constants for getting first or last four bits from byte +const TInt KEMNUpperBits = 4; +const TInt KEMNLowerBits = 0xf; + +// Constants for date handling +const TInt KEMNAtLeastDate = 4; +const TInt KEMNIndexOfMilleniumAndCentury = 0; +const TInt KEMNIndexOfDecadeAndYear = 1; +const TInt KEMNIndexOfMonth = 2; +const TInt KEMNIndexOfDay = 3; + +// Constants for time handling +const TInt KEMNIndexOfHour = 4; +const TInt KEMNIndexOfMinute = 5; + +using namespace Xml; + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::CEMNXMLContentHandler +//----------------------------------------------------------------------------- +CEMNXMLContentHandler::CEMNXMLContentHandler( + TEMNElement& aElement, TBool aIsAscii ) : + iElement( aElement ), + iIsAscii( aIsAscii ), + iFoundMailboxAttribute( EFalse ) + { + KEMNLOGGER_WRITE("CEMNXMLContentHandler::CEMNXMLContentHandler()"); + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::NewL +//----------------------------------------------------------------------------- +CEMNXMLContentHandler* CEMNXMLContentHandler::NewL( + TEMNElement& aElement, + TBool aIsAscii ) + { + CEMNXMLContentHandler* self = NewLC( aElement, aIsAscii ); + CleanupStack::Pop( self ); + return self; + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::NewLC +//----------------------------------------------------------------------------- +CEMNXMLContentHandler* CEMNXMLContentHandler::NewLC( + TEMNElement& aElement, + TBool aIsAscii ) + { + CEMNXMLContentHandler* self = + new (ELeave) CEMNXMLContentHandler( aElement, aIsAscii ); + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::ConstructL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::ConstructL() + { + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::~CEMNXMLContentHandler +//----------------------------------------------------------------------------- +CEMNXMLContentHandler::~CEMNXMLContentHandler() + { + KEMNLOGGER_WRITE("CEMNXMLContentHandler::~CEMNXMLContentHandler()"); + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnStartDocumentL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnStartDocumentL( + const RDocumentParameters& /*aDocParam*/, + TInt /*aErrorCode*/ ) + { + KEMNLOGGER_WRITE("CEMNXMLContentHandler::OnStartDocumentL()"); + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnEndDocumentL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnEndDocumentL( TInt /*aErrorCode*/ ) + { + KEMNLOGGER_WRITE("<- EMN message data"); + + // This EMN message will be discarded if no mailbox attribute found. + if ( !iFoundMailboxAttribute ) + { + KEMNLOGGER_WRITE("CEMNXMLContentHandler::OnEndDocumentL(): No mailbox attribute found, leaving."); + User::Leave( EEMNMissingMailboxAttribute ); + } + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnStartElementL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnStartElementL( + const RTagInfo& aElement, + const RAttributeArray& aAttributes, + TInt aErrorCode ) + { + KEMNLOGGER_WRITE("CEMNXMLContentHandler::OnStartElementL()"); + KEMNLOGGER_WRITE("EMN message data ->"); + + // Element name + const TDesC8& localPart8 = aElement.LocalName().DesC(); + KEMNLOGGER_WRITE_FORMAT8("<%s>", localPart8.Ptr() ); + + // Attribute(s) of emn element + if ( localPart8.Compare( KEmnElement ) == 0 ) // Is emn element + { + TInt attributeCount = aAttributes.Count(); + + for ( TInt n = 0; n < attributeCount; n++ ) + { + const RAttribute& attribute = aAttributes[n]; + const RTagInfo& nameInfo = attribute.Attribute(); + + const TDesC8& localPart8 = nameInfo.LocalName().DesC(); + const TDesC8& prefix8 = nameInfo.Prefix().DesC(); + const TDesC8& value8 = attribute.Value().DesC(); + + // Mailbox attribute is similar to both ascii and binary + if ( localPart8.Compare( KMailboxAttribute ) == 0) + { + KEMNLOGGER_WRITE_FORMAT8("mailbox=%s", value8.Ptr() ); + // Read the whole value of mailbox attribute and copy that + // to iElement. Conversion happens from 8-bit to 16 bit + // unicode. + iElement.mailbox.Copy( value8.Left( KAOMailboxAttributeLength ) ); + iFoundMailboxAttribute = ETrue; + } + // Timestamp attribute is in different form in ascii than in binary + // so we need to extract it with different methods. + else if ( localPart8.Compare( KTimestampAttribute ) == 0 ) + { + KEMNLOGGER_WRITE_FORMAT8("timestamp=%s", value8.Ptr() ); + // NOTE: Currently timestamp attribute is omitted. + /* This code branch will be commented out when there is some nice + use case for timestamp. + if ( iIsAscii ) + { + HandleXMLAttributesL( localPart8, value8 ); + } + else + { + HandleWBXMLAttributesL( localPart8, value8 ); + } + */ + } + // Something else than mailbox or timestamp attribute + else + { + KEMNLOGGER_WRITE_FORMAT28("Unknown attribute: %s=%s", localPart8.Ptr(), value8.Ptr() ); + } + } + } + else // No emn element found + { + KEMNLOGGER_WRITE_FORMAT8("%s\">", localPart8.Ptr() ); + User::Leave( EEMNMissingEMNElement ); + } + + User::LeaveIfError( aErrorCode ); + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::HandleXMLAttributes +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::HandleXMLAttributesL( + const TDesC8& aAttributeName8, + const TDesC8& aAttributeValue8 ) + { + // Currently only timestamp attribute is handled here. Mailbox attribute + // is same for both XML and WBXML. + if ( aAttributeName8.Compare( KTimestampAttribute ) == 0 ) + { + iElement.timestamp = NULL; + + TPtrC8 p8( aAttributeValue8.Ptr(), aAttributeValue8.Length() ); + HBufC* tmpBuf = HBufC::NewLC( aAttributeValue8.Length() ); + // Copies 8 bit to 16 bit and makes a new copy from heap + tmpBuf->Des().Copy( p8 ); + TPtr ptr = tmpBuf->Des(); + + if ( ptr.Length() == KEMNLengthOfRecvTimeStamp ) + { + // Parse received timestamp to be in a form, which can be set to TTime + // "2010-12-31T00:01:00Z" != YYYYMMDD:HHMMSS + TInt pos = ptr.Find( KColonChar ); + while ( pos != KErrNotFound ) + { + ptr.Delete( pos, 1 ); + pos = ptr.Find( KColonChar ); + } + + // "2010-12-31T000100Z" != YYYYMMDD:HHMMSS + pos = ptr.Find( KDashChar ); + while ( pos != KErrNotFound ) + { + ptr.Delete( pos, 1 ); + pos = ptr.Find( KDashChar ); + } + + // "20101231T000100Z" != YYYYMMDD:HHMMSS + pos = ptr.Find( KDateTimeSeparator ); + while ( pos != KErrNotFound ) + { + ptr.Replace( pos, 1, KColonChar ); + pos = ptr.Find( KDateTimeSeparator ); + } + + // "20101231:000100Z" != YYYYMMDD:HHMMSS + pos = ptr.Find( KTimezoneSeparator ); + while ( pos != KErrNotFound ) + { + ptr.Delete( pos, 1 ); + pos = ptr.Find( KTimezoneSeparator ); + } + + // "20101231:000100" == YYYYMMDD:HHMMSS, but day and month must be + // decreased by one, because those are offset from zero and we + // received them in offset from one. + + /* + * if MM == 09 -> MM = 08 + * if MM == 10 -> MM = 09 + * if MM == 11 -> MM = 10 + */ + if ( ( ptr[4] == '1' ) && ( ptr[5] == '0' ) ) // CSI: 47 # see comments + { + ptr[4] = ( ptr[4] - 0x1 ); // 1 -> 0 + ptr[5] = ( ptr[5] + 0x9 ); // 0 -> 9 + } + else + { + ptr[5] = ( ptr[5] - 0x1 ); // n -> n-1 + } + + /* + * if DD == 09 -> DD = 08 + * if DD == 13 -> DD = 12 + * if DD == 28 -> DD = 27 + * if DD == 31 -> DD = 30 + * if DD == 10 -> DD = 09 + * if DD == 20 -> DD = 19 + * if DD == 30 -> DD = 29 + */ + if ( ( ( ptr[6] == '1' ) || // CSI: 47 # see comments + ( ptr[6] == '2' ) || // CSI: 47 # see comments + ( ptr[6] == '3' ) ) && ( ptr[7] ) == '0' ) // CSI: 47 # see comments + { + ptr[6] = ( ptr[6] - 0x1 ); // n -> n-1 + ptr[7] = ( ptr[7] + 0x9 ); // 0 -> 9 + } + else + { + ptr[7] = ( ptr[7] - 0x1 ); // n -> n-1 + } + + // Now the "20101231:000100" should be "20101130:000100" + // Make a TTime object from it. + TTime time; + TInt err = time.Set( ptr ); + if ( err == KErrNone ) + { + iElement.timestamp = time; + } + else + { + iElement.timestamp = NULL; + } + } + + CleanupStack::PopAndDestroy( tmpBuf ); + } + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::HandleWBXMLAttributes +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::HandleWBXMLAttributesL( + const TDesC8& aAttributeName8, + const TDesC8& aAttributeValue8 ) + { + // Currently only timestamp attribute is handled here. Mailbox attribute + // is same for both XML and WBXML. + if ( aAttributeName8.Compare( KTimestampAttribute ) == 0 ) + { + iElement.timestamp = NULL; + + // Here is an example of received timestamp string: 20 05 11 12 13 14 01 + // It is 12th of November 2005, 01:14:01 PM + + // Timestamp length must be atleast 4. It means that at least year, month + // and day must be given, because those can't be zeros. Hour, minute and + // second can be zeros. But not all the Push Content GWs doesn't send those, + // so we must handle both situations. + TInt timestampLength = aAttributeValue8.Length(); + if ( timestampLength >= KEMNAtLeastDate ) + { + TDateTime datetime; + TInt err; + TInt temp1; + TInt temp2; + + temp1 = aAttributeValue8[ KEMNIndexOfMilleniumAndCentury ]; + temp2 = aAttributeValue8[ KEMNIndexOfDecadeAndYear ]; + + // Separately parse millenium, century, decade and year + TInt year = + ( ( temp1 >> KEMNUpperBits ) * 1000 ) + // CSI: 47 # see comments + ( ( temp1 & KEMNLowerBits ) * 100 ) + // CSI: 47 # see comments + ( ( temp2 >> KEMNUpperBits ) * 10 ) + // CSI: 47 # see comments + ( temp2 & KEMNLowerBits ); + + temp1 = aAttributeValue8[ KEMNIndexOfMonth ]; + TInt month = + ( ( temp1 >> KEMNUpperBits ) * 10 ) + // CSI: 47 # see comments + ( temp1 & KEMNLowerBits ); + + temp1 = aAttributeValue8[ KEMNIndexOfDay ]; + TInt day = + ( ( temp1 >> KEMNUpperBits ) * 10 ) + // CSI: 47 # see comments + ( temp1 & KEMNLowerBits ); + + err = datetime.SetYear( year ); + if ( err != KErrNone ) + { + User::Leave( EEMNInvalidYear ); + } + + err = datetime.SetMonth( static_cast( month - 1 ) ); + if ( err != KErrNone ) + { + User::Leave( EEMNInvalidMonth ); + } + + err = datetime.SetDay( day - 1 ); + if ( err != KErrNone ) + { + User::Leave( EEMNInvalidDay ); + } + + // There might not be hour, minute and second in EMN message + if ( timestampLength > KEMNAtLeastDate ) + { + // At least hour + if ( timestampLength >= KEMNTimestampIncHour ) + { + temp1 = aAttributeValue8[ KEMNIndexOfHour ]; + TInt hour = ( ( temp1 >> KEMNUpperBits ) * 10 ) + // CSI: 47 # see comments + ( temp1 & KEMNLowerBits ); + + err = datetime.SetHour( hour ); + if ( err != KErrNone ) + { + User::Leave( EEMNInvalidHour ); + } + } + + // At least hour and minute + if ( timestampLength >= KEMNTimestampIncHourMinute ) + { + temp1 = aAttributeValue8[ KEMNIndexOfMinute ]; + TInt minute = + ( ( temp1 >> KEMNUpperBits ) * 10 ) + // CSI: 47 # see comments + ( temp1 & KEMNLowerBits ); + + err = datetime.SetMinute( minute ); + if ( err != KErrNone ) + { + User::Leave( EEMNInvalidMinute ); + } + } + } + else + { + datetime.SetHour( 0 ); + datetime.SetMinute( 0 ); + } + + // At this point, datetime should be well formed, so no need to do extra + // checks. + TTime time( datetime ); + + iElement.timestamp = time; + KEMNLOGGER_WRITE_DATETIME("timestamp=", time ); + } + else + { + KEMNLOGGER_WRITE("Timestamp provided, but not valid ==> EMN discarded!" ); + KEMNLOGGER_WRITE_FORMAT("Timestamp length was %d", aAttributeValue8.Length() ); + + // Timestamp attribute was found, but its format was not valid. + User::Leave( EEMNInvalidTimestampAttribute ); + } + } + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::CEMNXMLContentHandler +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnEndElementL( + const RTagInfo& aElement, + TInt /*aErrorCode*/ ) + { + const TDesC8& localPart8 = aElement.LocalName().DesC(); + KEMNLOGGER_WRITE_FORMAT8("", localPart8.Ptr() ); + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnContentL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnContentL( + const TDesC8& /*aData8*/, + TInt /*aErrorCode*/ ) + { + // Content of an EMN message should always be empty. + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnStartPrefixMappingL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnStartPrefixMappingL( + const RString& /*aPrefix*/, + const RString& /*aUri*/, + TInt /*aErrorCode*/ ) + { + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnEndPrefixMappingL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnEndPrefixMappingL( + const RString& /*aPrefix*/, + TInt /*aErrorCode*/ ) + { + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnIgnorableWhiteSpaceL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnIgnorableWhiteSpaceL( + const TDesC8& /*aBytes*/, + TInt /*aErrorCode*/ ) + { + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnSkippedEntityL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnSkippedEntityL( + const RString& /*aName*/, + TInt /*aErrorCode*/ ) + { + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnProcessingInstructionL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnProcessingInstructionL( + const TDesC8& /* aTarget8*/, + const TDesC8& /*aData8*/, + TInt /*aErrorCode*/ ) + { + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnExtensionL +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnExtensionL( + const RString& /*aData*/, + TInt /*aToken*/, + TInt /*aErrorCode*/ ) + { + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::OnError +//----------------------------------------------------------------------------- +void CEMNXMLContentHandler::OnError( TInt __DEBUG_ONLY( aError ) ) + { + KEMNLOGGER_WRITE_FORMAT("CEMNXMLContentHandler::OnError() aError = %d", aError ); + +#ifdef _DEBUG + // No actions taken, if there is something wrong with elements or attributes + // in received EMN message. This is because OMA EMN spesification says that + // received message can be discarded if it is not valid or well-formed. This + // error handling is just for debugging purposes. + switch ( aError ) + { + case EEMNMissingEMNElement: + KEMNLOGGER_WRITE("No EMN element found."); + break; + + case EEMNMissingMailboxAttribute: + KEMNLOGGER_WRITE("Mailbox attribute not found."); + break; + + case EEMNMissingTimestampAttribute: + KEMNLOGGER_WRITE("Timestamp attribute not found."); + break; + + // Received timestamp attribute did not contain proper date and time. + case EEMNInvalidTimestampAttribute: + + // Following errors indicates that values were not in acceptable ranges + case EEMNInvalidYear: + case EEMNInvalidMonth: + case EEMNInvalidDay: + case EEMNInvalidMinute: + case EEMNInvalidHour: + KEMNLOGGER_WRITE("Timestamp attribute found, but with invalid value."); + break; + default: + KEMNLOGGER_WRITE("Invalid error code, where we got it?."); + } +#endif + } + +//----------------------------------------------------------------------------- +// CEMNXMLContentHandler::GetExtendedInterface +//----------------------------------------------------------------------------- +TAny* CEMNXMLContentHandler::GetExtendedInterface( const TInt32 ) + { + KEMNLOGGER_WRITE("CEMNXMLContentHandler::GetExtendedInterface()"); + return NULL; + } +