email/emailnotificationhandler/src/EMNXMLContentHandler.cpp
changeset 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/emailnotificationhandler/src/EMNXMLContentHandler.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -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<TMonth>( 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("</%s>", 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;
+	}
+