calendarengines/caleninterimutils/src/CalenInterimUtils2Impl.cpp
changeset 0 f979ecb2b13e
child 25 bf573002ff72
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/calendarengines/caleninterimutils/src/CalenInterimUtils2Impl.cpp	Tue Feb 02 10:12:19 2010 +0200
@@ -0,0 +1,1236 @@
+/*
+* 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:  This class contains utility method implementation 
+*               related to usage of Calendar Interim API.
+*
+*/
+
+
+// ----------------------------------------------------------------------------
+// INCLUDE FILES
+// ----------------------------------------------------------------------------
+//
+#include "CalenInterimUtils2Impl.h"
+
+//debug
+#include "calendarengines_debug.h"
+
+#include <calentry.h>
+#include <caluser.h>
+#include <calentryview.h>
+#include <etelmm.h>
+#include <e32math.h>
+#include <calrrule.h>
+#include <cmrmailboxutils.h>
+#include <featmgr.h>
+#include <MeetingRequestUids.hrh>
+#include <ecom/ecom.h>
+#include <utf.h>
+#include <openssl/md5.h>
+#include <string.h>
+
+#include "CleanupResetAndDestroy.h"
+#include "RImplInfoPtrArrayOwn.inl"
+#include "CalenEcomWatcher.h"       // Watches for ECOM registry changes
+
+// CONSTANTS
+/// Unnamed namespace for local definitions
+namespace {
+
+#ifdef __WINSCW__
+    _LIT( KEmulatorImei, "123456789012345" );
+#endif
+
+    const TInt KImeiLength = 15; // 15 chars from left of imei
+
+    const TInt KAsciiFirstNumber = 48;             // ASCII character 48 = '0'.
+    const TInt KAsciiFirstLowercaseLetter = 97;    // ASCII character 97 = 'a'.
+
+}  // namespace
+
+// ----------------------------------------------------------------------------
+// MEMBER FUNCTIONS
+// ----------------------------------------------------------------------------
+//
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::NewL()
+// Creates and returns a new CCalenInterimUtils2Impl object.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+CCalenInterimUtils2Impl* CCalenInterimUtils2Impl::NewL()
+    {
+    TRACE_ENTRY_POINT;
+    
+    CCalenInterimUtils2Impl* self = new(ELeave) CCalenInterimUtils2Impl();
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    
+    TRACE_EXIT_POINT;
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::CCalenInterimUtils2Impl()
+// Default constructor
+// -----------------------------------------------------------------------------
+CCalenInterimUtils2Impl::CCalenInterimUtils2Impl()
+    {
+    TRACE_ENTRY_POINT;
+
+    iMrEnabledCheck = ETrue;
+    iMrEnabled = EFalse;
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::ConstructL()
+// -----------------------------------------------------------------------------
+void CCalenInterimUtils2Impl::ConstructL()
+    {
+    TRACE_ENTRY_POINT;
+
+    FeatureManager::InitializeLibL();
+    // Only start the ecom watcher if the feature flags are on.
+    if ((FeatureManager::FeatureSupported(KFeatureIdMeetingRequestEnabler)) || 
+        (FeatureManager::FeatureSupported(KFeatureIdMeetingRequestSupport)) )
+        {
+        iEcomWatcher  = CCalenEComWatcher::NewL(*this);
+        }
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::~CCalenInterimUtils2Impl()
+// -----------------------------------------------------------------------------
+CCalenInterimUtils2Impl::~CCalenInterimUtils2Impl()
+    {
+    TRACE_ENTRY_POINT;
+    
+    delete iEcomWatcher;
+	FeatureManager::UnInitializeLib();
+	
+	TRACE_EXIT_POINT;
+    }
+
+// ----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::PopulateChildFromParentL
+// ----------------------------------------------------------------------------
+void CCalenInterimUtils2Impl::PopulateChildFromParentL(CCalEntry& aChild, const CCalEntry& aParent)
+    {
+    TRACE_ENTRY_POINT;
+
+    if ( aChild.DescriptionL() == KNullDesC )
+        {
+        aChild.SetDescriptionL( aParent.DescriptionL() );
+        }
+    if ( aChild.LocationL() == KNullDesC )
+        {
+        aChild.SetLocationL( aParent.LocationL() );
+        }
+    if ( aChild.PriorityL() == 0 )
+        { // zero is undefined priority according to iCal spec.
+        aChild.SetPriorityL( aParent.PriorityL() );
+        }
+    if ( aChild.SummaryL() == KNullDesC )
+        {
+        aChild.SetSummaryL( aParent.SummaryL() );
+        }
+    if ( aChild.StatusL() == CCalEntry::ENullStatus )
+        {
+        aChild.SetStatusL( aParent.StatusL() );
+        }
+    CCalAlarm* childAlarm = aChild.AlarmL();
+    CCalAlarm* parentAlarm = aParent.AlarmL();
+    
+    CleanupStack::PushL(childAlarm);
+    CleanupStack::PushL(parentAlarm);
+    
+    if( !childAlarm && parentAlarm )
+        {
+        aChild.SetAlarmL( parentAlarm);
+        }
+    
+    CleanupStack::PopAndDestroy(childAlarm);
+    CleanupStack::PopAndDestroy(parentAlarm);
+		
+    if (aChild.ReplicationStatusL() != aParent.ReplicationStatusL())
+        {
+        aChild.SetReplicationStatusL(aParent.ReplicationStatusL());
+        }
+    if (aChild.MethodL() == CCalEntry::EMethodNone)
+        {
+        aChild.SetMethodL(aParent.MethodL());
+        }
+
+    if ( !aChild.OrganizerL() && aParent.OrganizerL() )
+        {
+        CCalUser* owner = CopyUserLC( *( aParent.OrganizerL() ) );
+        aChild.SetOrganizerL( owner );
+        CleanupStack::Pop(owner); // ownership transferred
+        }
+    
+    PopulateAttendeeListL( aChild, aParent );
+    
+    TRACE_EXIT_POINT;
+    // "Categories" property is omitted
+    }
+
+// ----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::PopulateAttendeeListL
+// ----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::PopulateAttendeeListL(
+    CCalEntry& aChild,
+    const CCalEntry& aParent )
+    {
+    TRACE_ENTRY_POINT;
+    
+    RPointerArray<CCalAttendee>& childAtt = aChild.AttendeesL();
+    RPointerArray<CCalAttendee>& parentAtt = aParent.AttendeesL();
+ 
+    CCalUser *po = NULL;
+    if ( !aChild.PhoneOwnerL() )
+        { // Phone owner is internal data, not part of ical protocol, it's
+          // benefit is to avoid heavy "who am I?" resolving operation
+        po = aParent.PhoneOwnerL();
+        }
+    TInt parentAttCount( parentAtt.Count() );
+    if ( childAtt.Count() == 0 && parentAttCount > 0 )
+        {
+        for ( TInt i( 0 ); i < parentAttCount; ++i )
+            {
+            CCalAttendee* copy = CopyAttendeeLC( *( parentAtt[i] ) );
+            aChild.AddAttendeeL( copy );
+            CleanupStack::Pop(copy); // ownership transferred
+            if(po == parentAtt[i])
+                {
+                aChild.SetPhoneOwnerL(copy);
+
+                }
+            }
+        }
+    
+    TRACE_EXIT_POINT;
+    }
+
+// ----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::CopyAttendeeLC
+// ----------------------------------------------------------------------------
+//
+CCalAttendee* CCalenInterimUtils2Impl::CopyAttendeeLC(
+    const CCalAttendee& aSource )
+    {
+    TRACE_ENTRY_POINT;
+    
+    CCalAttendee* copy = CCalAttendee::NewL( aSource.Address(),
+                                             aSource.SentBy() );
+    CleanupStack::PushL( copy );
+    copy->SetCommonNameL( aSource.CommonName() );
+    copy->SetRoleL( aSource.RoleL() );
+    copy->SetStatusL( aSource.StatusL() );
+    copy->SetResponseRequested( aSource.ResponseRequested() );
+    
+    TRACE_EXIT_POINT;
+    return copy;
+    }
+
+// ----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::CopyUserLC
+// ----------------------------------------------------------------------------
+//
+CCalUser* CCalenInterimUtils2Impl::CopyUserLC( const CCalUser& aSource )
+    {
+    TRACE_ENTRY_POINT;
+    
+    CCalUser* copy = CCalUser::NewL( aSource.Address(),
+                                     aSource.SentBy() );
+    CleanupStack::PushL( copy );
+    copy->SetCommonNameL( aSource.CommonName() );
+    
+    TRACE_EXIT_POINT;
+    return copy;
+    }
+
+
+// ----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::GlobalUidL
+// ----------------------------------------------------------------------------
+//
+HBufC8* CCalenInterimUtils2Impl::GlobalUidL()
+    {
+    TRACE_ENTRY_POINT;
+    
+    // Loosely using UID generation algorithm from
+    // http://www.webdav.org/specs/draft-leach-uuids-guids-01.txt
+
+    // Number of 100ns ticks since Oct 15 1582.
+    TInt64 timeStamp = GetTicksFromGregorianCalendarStartL();
+
+    // This differs slightly from the spec in that the clock sequence is just a pseudo-random number.
+       TUint32 clockSeq = Math::Random();
+       // IMEI is read the first time this is called, and stored for subsequent calls.
+       if(!iImeiNode)
+           {
+           iImeiNode = GetImeiAsNodeValueL();
+           }
+
+    HBufC8* resultBuf = DoCreateUidLC(clockSeq, timeStamp, iImeiNode);
+    CleanupStack::Pop(resultBuf);
+    
+    TRACE_EXIT_POINT;
+    return resultBuf;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::GetTicksFromGregorianCalendarStartL()
+// This function returns the number of 100ns ticks since 0:00 15th October 1582.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TInt64 CCalenInterimUtils2Impl::GetTicksFromGregorianCalendarStartL()
+    {
+    TRACE_ENTRY_POINT;
+    
+    TTime timeNow;
+    timeNow.HomeTime();
+    TDateTime gregorianStartDT(1582, EOctober, 15, 0, 0, 0, 0);
+    TTime gregorianStart(gregorianStartDT);
+    TTimeIntervalMicroSeconds msDifference = timeNow.MicroSecondsFrom(gregorianStart);
+    
+    TRACE_EXIT_POINT;
+    return msDifference.Int64() * 10; // * 10 to convert from micro sec (==1000 ns) count to 100ns count.
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::GetImeiAsNodeValueL()
+// Formats the IMEI returned from GetImeiL() into a 64-bit integer.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TInt64 CCalenInterimUtils2Impl::GetImeiAsNodeValueL()
+    {
+    TRACE_ENTRY_POINT;
+    
+    TBuf<RMobilePhone::KPhoneSerialNumberSize> serialNumber;
+    GetImeiL( serialNumber );
+
+    // Trim off all but the first KImeiLength digits.
+    TLex lex( serialNumber.Left(KImeiLength) );
+
+    TInt64 val;
+    User::LeaveIfError(lex.Val(val));
+    
+    TRACE_EXIT_POINT;
+    return val;
+    }
+
+inline TUint8 LastFourBits(TUint8& aNum)
+    {
+    TRACE_ENTRY_POINT;
+    
+    // 00001111 b
+    TRACE_EXIT_POINT;
+    return aNum & 0x0F;
+    }
+
+inline TUint8 FirstFourBits(TUint8& aNum)
+    {
+    TRACE_ENTRY_POINT;
+    
+    // 11110000 b
+    TRACE_EXIT_POINT;
+    return (aNum & 0xF0) >> 4;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::DoCreateUidLC()
+// Performs the work in creating a GUID.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+HBufC8* CCalenInterimUtils2Impl::DoCreateUidLC( const TUint32& aClockSeq,
+                                               const TUint64& aTimeStamp,
+                                               const TUint64& aNodeValue )
+    {
+    TRACE_ENTRY_POINT;
+    
+    // The roast beef of the algorithm. Does all the shifting about as described in the web draft.
+    TUint32 time_low = aTimeStamp & 0xFFFFFFFF;
+    TUint16 time_mid = (aTimeStamp >> 32) & 0xFFFF;
+    TUint16 time_high = (aTimeStamp >> 48) & 0x0FFF;
+    time_high |= (1 << 12);
+    TUint8 clock_seq_low = aClockSeq & 0xFF;
+    TUint8 clock_seq_high = (aClockSeq & 0x3F00) >> 8;
+    clock_seq_high |= 0x80;
+
+    // Can't use RArray as that's set up for minimum 4 bytes per item.
+    CArrayFixFlat<TUint8> *node = new (ELeave) CArrayFixFlat<TUint8>(6);
+    CleanupStack::PushL(node);
+
+    // The rest of the function is mapping the 64, 32 and 16 bit numbers to 8 bit numbers
+    // while losing as little data as possible.
+
+    TUint64 mask = 0xFF0000000000;
+    for(TInt i=0; i<=6; ++i)
+        {
+        TInt64 temp = aNodeValue & mask;
+        temp >>= ((5-i)*8);
+        node->AppendL(temp);
+        mask = mask >> 8;
+        }
+
+    TBuf8<16> rawOutput;
+
+    rawOutput.Append( (time_low  & 0xFF000000) >> 24 );
+    rawOutput.Append( (time_low  & 0x00FF0000) >> 16 );
+    rawOutput.Append( (time_low  & 0x0000FF00) >> 8 );
+    rawOutput.Append( (time_low  & 0x000000FF) );
+
+    rawOutput.Append( (time_mid  & 0xFF00) >> 8 );
+    rawOutput.Append( (time_mid  & 0x00FF) );
+
+    rawOutput.Append( (time_high & 0xFF00) >> 8 );
+    rawOutput.Append( (time_high & 0x00FF) );
+
+    rawOutput.Append( clock_seq_low );
+    rawOutput.Append( clock_seq_high );
+
+    for(TInt i=0; i<6; ++i)
+        {
+        rawOutput.Append( node->At(i) );
+        }
+    CleanupStack::PopAndDestroy(); // node
+    
+    TUint8 digest[16];
+    HBufC8* resultBuf = rawOutput.AllocLC();
+    TPtr8 resultBufPtr = resultBuf->Des();
+    TUint length = resultBufPtr.Length();
+    
+    // Create a new buffer to provide space for '\0'
+    HBufC8* newBuf = HBufC8::NewLC( length + 1 );//+1 space for '\0'
+    TPtr8 newBufPtr = newBuf->Des();
+    newBufPtr.Copy(resultBufPtr);
+    
+    // Appends a zero terminator onto the end of this descriptor's data
+    // and returns a pointer to the data.
+    char* chPtrTemp = ( char*)newBufPtr.PtrZ();
+    char* chPtr = ( char*) User::AllocL( length + 1 );
+    strcpy( chPtr , chPtrTemp );
+    
+    //md5 context
+    MD5_CTX* context = new MD5_CTX();
+    //initialize the context
+    MD5_Init(context);
+    //Append a string to the message
+    MD5_Update(context, chPtr, length );
+    //Finish the message and return the digest.
+    MD5_Final(digest, context );
+    
+    // Add the version field in the msb 4 bits. The value of version is 3.
+    digest[6] = digest[6] & 0x0F;
+    digest[6] |= (3 << 4);
+    
+    //Add the variant field in the msb 2 bits. The value of variant is 2.
+    digest[9] = digest[9] & 0x3F;
+    digest[9] |= 0x80;
+    
+    delete chPtr;
+    delete context;
+    CleanupStack::PopAndDestroy( newBuf );
+    CleanupStack::PopAndDestroy( resultBuf );
+    TBuf8<36> output;
+    TInt i;
+    for(i=0; i<16; ++i)
+        {
+        output.Append( ConvertToCharacterL( FirstFourBits( digest[i] ) ) ); 
+        output.Append( ConvertToCharacterL( LastFourBits( digest[i] ) ) );
+        if(i == 3 || i == 5 || i == 7 ||i == 9)
+            {
+            output.Append( '-' );
+            }
+        }
+    HBufC8* md5ResultBuf = output.AllocLC();
+    
+    TRACE_EXIT_POINT;
+    return md5ResultBuf;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::ConvertToCharacterL()
+// Converts from a number between 0-15 to a hexadecimal character representation.
+// 0-9, a-f
+// -----------------------------------------------------------------------------
+//
+TChar CCalenInterimUtils2Impl::ConvertToCharacterL(TUint8 aChar)
+    {
+    TRACE_ENTRY_POINT;
+    
+    if(aChar < 10)
+        {
+        // Convert to a digit.
+        TRACE_EXIT_POINT;
+        return TChar(aChar + KAsciiFirstNumber);
+        }
+    else if(aChar < 16)
+        {
+        // Convert to lower case character.
+        TRACE_EXIT_POINT;
+        return TChar(aChar - 10 + KAsciiFirstLowercaseLetter);
+        }
+
+    // This function should only be passed numbers between 0-15.
+    User::Leave(KErrArgument);
+    
+    TRACE_EXIT_POINT;
+    return TChar(KAsciiFirstLowercaseLetter); // Stops compiler warning.
+    }
+
+// ----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::GetImeiL
+// Gets the IMEI of the device. On the emulator, this will return a fake IMEI.
+// (other items were commented in a header).
+// ----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::GetImeiL( TDes& aImei )
+    {
+    TRACE_ENTRY_POINT;
+    
+    #ifndef __WINSCW__
+
+    RTelServer telServer;
+    User::LeaveIfError( telServer.Connect() );
+    CleanupClosePushL(telServer);
+
+    TInt numPhones = 0;
+    User::LeaveIfError( telServer.EnumeratePhones(numPhones) );
+    if(numPhones < 1)
+        {
+        User::Leave(KErrNotFound);
+        }
+    RTelServer::TPhoneInfo info;
+    User::LeaveIfError( telServer.GetPhoneInfo(0, info) );
+    RMobilePhone mobilePhone;
+    User::LeaveIfError( mobilePhone.Open(telServer, info.iName) );
+    CleanupClosePushL(mobilePhone);
+
+    TUint32 identityCaps;
+    User::LeaveIfError( mobilePhone.GetIdentityCaps( identityCaps ) );
+
+    if ( identityCaps & RMobilePhone::KCapsGetSerialNumber )
+        {
+        TRequestStatus status;
+        RMobilePhone::TMobilePhoneIdentityV1 mobilePhoneIdentity;
+        mobilePhone.GetPhoneId( status, mobilePhoneIdentity );
+        // Needs testing on target - May lock device
+        User::WaitForRequest( status );
+        User::LeaveIfError( status.Int() );
+        aImei = mobilePhoneIdentity.iSerialNumber;
+        }
+    else
+        {
+        User::Leave(KErrNotSupported);
+        }
+
+    CleanupStack::PopAndDestroy(); // mobilePhone;
+    CleanupStack::PopAndDestroy(); // telServer;
+
+    #else
+
+      // Return a fake IMEI when working on emulator
+      aImei = KEmulatorImei;
+
+    #endif // __WINSCW__
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::MRViewersEnabledL()
+// Checks to see if Meeting Request Viewer functionality
+// is enabled and an implementation is available to use
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCalenInterimUtils2Impl::MRViewersEnabledL(TBool aForceCheck)
+    {
+    TRACE_ENTRY_POINT;
+    if( aForceCheck || iMrEnabledCheck )
+        {
+		iMrEnabled = EFalse;
+		iMrEnabledCheck = EFalse;
+
+        PIM_TRAPD_HANDLE( DoMRViewersEnabledL() );
+        }
+        
+    TRACE_EXIT_POINT;
+    return iMrEnabled;    
+    }
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::DoMRViewersEnabledL()
+// Checks to see if Meeting Request Viewer functionality
+// is enabled and an implementation is available to use
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::DoMRViewersEnabledL()
+    {
+    TRACE_ENTRY_POINT;
+
+    // We ignore any leaves because iMrEnabled and iMrEnabledCheck are already set to
+    // EFalse, so if the function leaves it will return EFalse this time and any future
+    // calls will not force a check (unless aForceCheck is ETrue).
+    if (FeatureManager::FeatureSupported(KFeatureIdMeetingRequestSupport))
+        {
+        // Full meeting request solution
+        // As long as we have at least one mailbox we can return true
+        CMRMailboxUtils *mbUtils = CMRMailboxUtils::NewL();
+        CleanupStack::PushL( mbUtils );
+
+                RArray<CMRMailboxUtils::TMailboxInfo> mailboxes;
+                CleanupClosePushL(mailboxes);
+                mbUtils->ListMailBoxesL(mailboxes);
+                if(mailboxes.Count() > 0)
+                    {
+                    iMrEnabled = ETrue; // && 1 mailbox
+                    }
+
+        CleanupStack::PopAndDestroy(); // mailboxes
+        CleanupStack::PopAndDestroy(); // mbUtils
+        }
+     else
+        {
+        if (FeatureManager::FeatureSupported(KFeatureIdMeetingRequestEnabler))
+            {
+    		// Meeting request enablers solution
+    		//Check for:
+    		//	At least one mailbox exists on the terminal
+    		//	At least one MRViewer implementation, with an id matching a mailbox,
+    		    //	exists on the terminal
+    		    //	At least one MRUtils implementation exists on the terminal
+    		    CMRMailboxUtils *mbUtils = CMRMailboxUtils::NewL();
+    		    CleanupStack::PushL( mbUtils );
+
+                RArray<CMRMailboxUtils::TMailboxInfo> mailboxes;
+                CleanupClosePushL(mailboxes);
+
+                mbUtils->ListMailBoxesL(mailboxes);
+                if(mailboxes.Count() > 0)
+                    {
+                    RImplInfoPtrArrayOwn implArray;
+                     CleanupClosePushL( implArray );
+
+                     //Check for a MRViewers Implementation
+                     const TUid mrViewersIface = {KMRViewersInterfaceUID};
+                     REComSession::ListImplementationsL(mrViewersIface, implArray );
+                     if ( implArray.Count() > 0 )
+                         {
+                         // MRViewers implementations exist.  We need to see if any are
+                         // associated with an existing mailbox
+                         TBool mrImplMatchesMailbox = EFalse;
+                         for (TInt i=0; i<implArray.Count(); ++i)
+                             {
+                             for(TInt j=0; j<mailboxes.Count(); ++j)
+                                 {
+                                 TBuf16<KMaxUidName> mbName;
+                                 CnvUtfConverter::ConvertToUnicodeFromUtf8( mbName, implArray[i]->DataType() );
+                                 if(mailboxes[j].iMtmUid.Name().CompareF(mbName) == 0)
+                                     {
+                                    mrImplMatchesMailbox = ETrue;
+                                    //One match is enough for this decision to be true
+                                    break;
+                                    }
+                                 }
+                             if (mrImplMatchesMailbox)
+                                 {
+                                 break;
+                                 }
+                             }
+
+                         // Reset the viewer implementation array.  This will be reused
+                         // if we need to check for mr utils implementations
+                         implArray.ResetAndDestroy();
+
+                         if (mrImplMatchesMailbox)
+                             {
+                             // We have at least one MRViewer implementation with an associated
+                             // mailbox.  Check for a matching MR utils implementation
+                             const TUid mrUtilsIface = {KMRUtilsInterfaceUID};
+                             REComSession::ListImplementationsL(mrUtilsIface, implArray );
+                             if (implArray.Count() > 0)
+                                 {
+                                 // Meeting request functionality is available on this device
+                                 iMrEnabled = ETrue;
+                                 }
+                             }
+
+                         }
+                      CleanupStack::PopAndDestroy(); // implArray
+                      }
+
+                CleanupStack::PopAndDestroy(); // mailboxes
+                CleanupStack::PopAndDestroy(); // mbUtils
+                }            
+            }
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::HasTimeOrDateChangedL()
+// Checks to see if the date or time has changed from the given entry to the
+// entry with the same UID stored in the database. This function should only be
+// called on parent entries with a repeat.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCalenInterimUtils2Impl::HasTimeOrDateChangedL(const CCalEntry& aNewEntry, const CCalEntry& aOldEntry)
+    {
+    TRACE_ENTRY_POINT;
+    
+    TBool hasChanged = EFalse;
+
+    if( aNewEntry.StartTimeL().TimeUtcL() != aOldEntry.StartTimeL().TimeUtcL() ||
+        aNewEntry.EndTimeL().TimeUtcL()   != aOldEntry.EndTimeL().TimeUtcL())
+        {
+        hasChanged = ETrue;
+        }
+
+    TRACE_EXIT_POINT;
+    return hasChanged;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::PrepareForStorageL()
+// Sets the last modified date of an entry, and if aEntry is a meeting request,
+// sets the phone owner if not previously set.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::PrepareForStorageL( CCalEntry& aEntry )
+    {
+    TRACE_ENTRY_POINT;
+
+    FeatureManager::InitializeLibL();
+
+    aEntry.SetLastModifiedDateL();
+
+    if (FeatureManager::FeatureSupported(KFeatureIdMeetingRequestEnabler) ||
+        FeatureManager::FeatureSupported(KFeatureIdMeetingRequestSupport))
+        {
+        if( IsMeetingRequestL(aEntry) && aEntry.PhoneOwnerL() == NULL )
+            {
+            CMRMailboxUtils *mbUtils = CMRMailboxUtils::NewL();
+            CleanupStack::PushL( mbUtils );
+            mbUtils->SetPhoneOwnerL( aEntry );
+            CleanupStack::PopAndDestroy(); // mbUtils
+            }
+        }
+    FeatureManager::UnInitializeLib();
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::SingleStoreL()
+// Calls CCalEntryView::StoreL() on the given entry. Puts the entry into an
+// RPointerArray, as CalInterimApi expects.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::SingleStoreL( CCalEntryView& aEntryView,
+                                      CCalEntry& aEntry )
+    {
+    TRACE_ENTRY_POINT;
+
+    PrepareForStorageL( aEntry );
+    TInt successCount=0;
+    RPointerArray<CCalEntry> entries;
+    CleanupClosePushL( entries );
+    entries.Append( &aEntry );
+    aEntryView.StoreL( entries, successCount );
+    CleanupStack::PopAndDestroy( &entries );
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::SingleUpdateL()
+// Calls CCalEntryView::UpdateL() on the given entry. Puts the entry into an
+// RPointerArray, as CalInterimApi expects.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::SingleUpdateL( CCalEntryView& aEntryView,
+                                       CCalEntry& aEntry )
+    {
+    TRACE_ENTRY_POINT;
+    
+    PrepareForStorageL( aEntry );
+    TInt successCount=0;
+    RPointerArray<CCalEntry> entries;
+    CleanupClosePushL( entries );
+    entries.Append( &aEntry );
+    aEntryView.UpdateL( entries, successCount );
+    CleanupStack::PopAndDestroy( &entries );
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::FieldIsTheSameL()
+// Checks to see if the ONE given field is the same on one entry as another.
+// To check if more than one field is the same, call the function more than once
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCalenInterimUtils2Impl::FieldIsTheSameL( CCalEntry& aEntryOne,
+                                          CCalEntry& aEntryTwo,
+                                          TDifferenceFlag aFlag)
+    {
+    TRACE_ENTRY_POINT;
+    
+    switch( aFlag )
+        {
+        case EEntryDifferentStartTimeAndEndTime:
+            TRACE_EXIT_POINT;
+            return      ( TimeOfDay( aEntryOne.StartTimeL().TimeUtcL() )
+                        == TimeOfDay( aEntryTwo.StartTimeL().TimeUtcL() ))
+                    &&  ( TimeOfDay( aEntryOne.EndTimeL().TimeUtcL() )
+                        == TimeOfDay( aEntryTwo.EndTimeL().TimeUtcL() ) );
+        case EEntryDifferentSummary:
+            TRACE_EXIT_POINT;
+            return aEntryOne.SummaryL() == aEntryTwo.SummaryL();
+        case EEntryDifferentDescription:
+            TRACE_EXIT_POINT;
+            return aEntryOne.DescriptionL() == aEntryTwo.DescriptionL();
+        case EEntryDifferentLocation:
+            TRACE_EXIT_POINT;
+            return aEntryOne.LocationL() == aEntryTwo.LocationL();
+        default:
+            _LIT(KCalenInterimUtils2Panic, "CCalenInterimUtils2");
+            User::Panic(KCalenInterimUtils2Panic, KErrArgument);
+        }
+    TRACE_EXIT_POINT;
+    return EFalse; // Never hit.
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::CopyFieldL()
+// Copies ONE given field from aSrc to aDst. To copy more than one field,
+// call the function more than one time.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::CopyFieldL( const CCalEntry& aSrc,
+                                    CCalEntry& aDst,
+                                    TDifferenceFlag aField )
+    {
+    TRACE_ENTRY_POINT;
+
+    switch( aField )
+        {
+        case EEntryDifferentStartTimeAndEndTime:
+            {
+            // START TIME
+            // Keep aDst's start date, but copy the start time (h/m/s) from aSrc to aDst.
+            TTimeIntervalMinutes dtStartTimeOfDay = TimeOfDay( aSrc.StartTimeL().TimeUtcL() );
+            TTime dtStartDay = BeginningOfDay( aDst.StartTimeL().TimeUtcL() );
+
+            TCalTime startTime;
+            startTime.SetTimeUtcL( dtStartDay + (TTimeIntervalMinutes)dtStartTimeOfDay );
+
+
+            TTimeIntervalMinutes duration;
+            aSrc.EndTimeL().TimeUtcL().MinutesFrom(aSrc.StartTimeL().TimeUtcL(), duration);
+
+            // END TIME
+            // Calculate the duration of aSrc, and make aDst endtime equal aDst startTime
+            // + duration.  This will allow for events spanning multiple days.
+            TCalTime endTime;
+            endTime.SetTimeUtcL(startTime.TimeUtcL() + duration);
+
+            aDst.SetStartAndEndTimeL(startTime, endTime);
+
+            break;
+            }
+        case EEntryDifferentSummary:
+            aDst.SetSummaryL(aSrc.SummaryL());
+            break;
+        case EEntryDifferentDescription:
+            aDst.SetDescriptionL(aSrc.DescriptionL());
+            break;
+        case EEntryDifferentLocation:
+            aDst.SetLocationL(aSrc.LocationL());
+            break;
+        default:
+            _LIT(KCalenInterimUtils2Panic, "CCalenInterimUtils2");
+            User::Panic(KCalenInterimUtils2Panic, KErrArgument);
+        }
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::CopyChildrenExceptionDataL()
+// For each exception, for each field, if the field is NOT the reason for the
+// entry being an exception, copy the new parent's field across to the exception
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::CopyChildrenExceptionDataL( CCalEntry& aEditedEntry,
+                                                    RPointerArray<CCalEntry>& aOldEntries )
+    {
+    TRACE_ENTRY_POINT;
+
+    // For each oldChild...
+    for(TInt i=1; i<aOldEntries.Count(); ++i)
+        {
+        // For each field...
+        for(TDifferenceFlag j=(TDifferenceFlag)1; j<EEntryDifferenceCount; j=(TDifferenceFlag)(j<<1))
+            {
+            // Where oldChild field == oldParent Field
+            // and newParent field != oldParent Field...
+            if( FieldIsTheSameL(*aOldEntries[i], *aOldEntries[0], j ) &&
+                !FieldIsTheSameL(aEditedEntry,  *aOldEntries[0], j ) )
+                {
+                // ...copy newParent field to oldChild.
+                CopyFieldL(aEditedEntry, *aOldEntries[i], j);
+                }
+            }
+        }
+    
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::StoreL()
+// Public exported function. Checks to see if the given entry should be entered
+// using StoreL() or UpdateL().
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::StoreL( CCalEntryView& aEntryView,
+                                CCalEntry& aEntry,
+                                TBool aCopyToChildren )
+    {
+    TRACE_ENTRY_POINT;
+    
+    if (!IsEntryRepeatingL(aEntry))
+        {
+        // This is not a repeating entry.  Technically we should make a
+        // decision to see if we should be calling StoreL or UpdateL, but
+        // the cost of all the checking required to determine this would 
+        // be greater than the benefit of calling UpdateL if possible.
+        // Just call StoreL, as ultimately it is a lighter call.
+        SingleStoreL(aEntryView, aEntry);
+        return;
+        }
+
+
+    // This entry is repeating. Does it have EXDATEs which could be due to children?
+    RArray<TCalTime> exceptionDates;
+    CleanupClosePushL( exceptionDates );
+    aEntry.GetExceptionDatesL( exceptionDates );
+    TInt exceptionCount = exceptionDates.Count();
+    CleanupStack::PopAndDestroy( &exceptionDates );
+
+    if( exceptionCount == 0 )
+        {
+        // No exception dates so do a StoreL().
+        // We have no exceptions, so there are no children to re-store
+        // Same logic as above applies, we call StoreL rather than check to 
+        // see if we could have called UpdateL
+        SingleStoreL( aEntryView, aEntry );
+        return;
+        } 
+
+    //Is this a child entry?
+    if(aEntry.RecurrenceIdL().TimeUtcL() != Time::NullTTime())
+        {
+        SingleStoreL( aEntryView, aEntry );
+        return;
+        }
+
+    //Entry is not a child, but does it have any children?
+
+    // Fetch array of entries associated with this UID.
+    RPointerArray<CCalEntry> oldEntries;
+    CleanupResetAndDestroyPushL(oldEntries);
+    aEntryView.FetchL(aEntry.UidL(), oldEntries);
+    TBool hasChildren = oldEntries.Count() > 0;
+       
+    if (oldEntries.Count() == 0)
+        {
+        //This is a new repeating entry, with exceptions
+        //This must have come from an external application, as the 
+        //calendar UI does not allow creation of this type of entry
+        SingleStoreL(aEntryView, aEntry);
+        }
+        
+    //Have the RRule or time fields changed
+    else if(aCopyToChildren || HasTimeOrDateChangedL(*oldEntries[0], aEntry) 
+        || HaveRepeatPropertiesChangedL(*oldEntries[0], aEntry))
+        {
+        if (hasChildren && aCopyToChildren)
+            {
+            CopyChildrenExceptionDataL( aEntry, oldEntries );
+            }
+        SingleStoreL(aEntryView, aEntry);
+        
+        if(hasChildren)
+            {
+            StoreEachChildEntryL( aEntryView, aEntry, oldEntries, !aCopyToChildren );
+            }
+        }
+    else
+        {
+        SingleUpdateL(aEntryView, aEntry);
+        }
+
+    CleanupStack::PopAndDestroy( &oldEntries );
+    
+    TRACE_EXIT_POINT;
+    }
+
+// ---------------------------------------------------------
+// GenerateRecurrenceIdFromEntryL
+// Static helper function for StoreEachChildEntryL. Generates a TCalTime which is at the
+// same TIME as aEntry but at the same DATE as aInstanceDate
+// ---------------------------------------------------------
+TCalTime GenerateRecurrenceIdFromEntryL( CCalEntry& aEntry, TCalTime aInstanceDate )
+    {
+    TRACE_ENTRY_POINT;
+    
+    TDateTime theTime = aEntry.StartTimeL().TimeUtcL().DateTime();
+    TDateTime theDate = aInstanceDate.TimeUtcL().DateTime();
+
+    theTime.SetYear(theDate.Year());
+    theTime.SetMonth(theDate.Month());
+    theTime.SetDay(theDate.Day());
+
+    TCalTime toRet;
+    toRet.SetTimeUtcL(theTime);
+    
+    TRACE_EXIT_POINT;
+    return toRet;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::StoreEachChildEntryL()
+// Run through each of the given CCalEntries and call CCalEntryView::StoreL()
+// on them. Any Leaves in StoreL are ignored to make sure all items get entered.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::StoreEachChildEntryL( CCalEntryView &aEntryView,
+                                              CCalEntry &aEntry,
+                                              RPointerArray<CCalEntry> &aOldEntries,
+                                              TBool aResetLocalUid)
+    {
+    TRACE_ENTRY_POINT;
+    
+    // Start from 1 as we don't want to copy the old parent entry.
+    for(TInt i=1; i<aOldEntries.Count(); ++i)
+        {
+        if (aResetLocalUid)
+            {
+            // Reset the local UID of the exception.  When we store the exception, it will
+            // be added as a new entry rather than an update.
+            aOldEntries[i]->SetLocalUidL( TCalLocalUid( 0 ) );
+            }
+
+        // The RecurrenceId of child (exception) entries should never be a null time by definition.
+        // The code below will attempt to generate a RecurrenceId from the start time of the
+        // exception if no RecurrenceId is found.  This should never actually happen, and
+        // will not work if the start time/start date is changed.  The if case below should remain
+        // until the Symbian defect fix for NULL RecurrenceIds is verified.
+
+        if(aOldEntries[i]->RecurrenceIdL().TimeUtcL() == Time::NullTTime())
+            {
+            // This is being hit, but shouldn't be. Hence we create a new Recurrence ID.
+            // Without doing this, the SingleStoreL below fails with Agenda Model -35: No agenda server.
+            TCalTime recId = GenerateRecurrenceIdFromEntryL( aEntry, aOldEntries[i]->StartTimeL() );
+            CCalEntry *exception = CCalEntry::NewL( aOldEntries[i]->EntryTypeL(),
+                                                aEntry.UidL().AllocL(),
+                                                aOldEntries[i]->MethodL(),
+                                                aOldEntries[i]->SequenceNumberL(),
+                                                recId,
+                                                aOldEntries[i]->RecurrenceRangeL() );
+            CleanupStack::PushL(exception);
+            exception->CopyFromL(*aOldEntries[i]);
+
+            PIM_TRAPD_HANDLE( SingleStoreL(aEntryView, *exception) );
+            CleanupStack::PopAndDestroy(exception);
+            }
+        else
+            {
+            // If the start time of the series has been changed, the call below will
+            // leave with -1, and the child entries will be lost.  To prevent this
+            // we need to regenerate a new recurrence id for each child, create a copy
+            // of the child with the new recurrence id, and store that instead.
+            // Fixing this may cause issues with sync though, as some servers delete the
+            // children when changing the start time of the series anyway.
+            PIM_TRAPD_HANDLE( SingleStoreL(aEntryView, *aOldEntries[i]) );
+            }
+        }
+        
+    TRACE_EXIT_POINT;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::IsMeetingRequestL()
+// Check to see if the given entry is a meeting request.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCalenInterimUtils2Impl::IsMeetingRequestL( CCalEntry& aEntry )
+    {
+    TRACE_ENTRY_POINT;
+    TRACE_EXIT_POINT;
+    return aEntry.EntryTypeL() == CCalEntry::EAppt
+        && aEntry.OrganizerL();
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::IsEntryRepeatingL()
+// Check to see if the given entry is a repeating
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+TBool CCalenInterimUtils2Impl::IsEntryRepeatingL(const CCalEntry& aEntry)
+    {
+    TRACE_ENTRY_POINT;
+    
+    TBool isRepeating = EFalse;
+    RArray<TCalTime> rdates;
+    CleanupClosePushL(rdates);
+    aEntry.GetRDatesL(rdates);
+    TBool isRDate = rdates.Count() > 0;
+    CleanupStack::PopAndDestroy(); // rdates
+
+    TCalRRule newRule;
+
+    if ((isRDate) || (aEntry.GetRRuleL(newRule) && newRule.Type() != TCalRRule::EInvalid ))
+        {
+        isRepeating = ETrue;
+        }
+    
+    TRACE_EXIT_POINT;
+    return isRepeating;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::HaveRepeatPropertiesChangedL()
+// Check to see if the RRule or RDates have been modified.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+TBool CCalenInterimUtils2Impl::HaveRepeatPropertiesChangedL(const CCalEntry& aNewEntry, const CCalEntry& aOldEntry)
+    {
+    TRACE_ENTRY_POINT;
+    
+    //Have the RRules Changed?
+    TCalRRule newEntryRule;
+    aNewEntry.GetRRuleL(newEntryRule);
+
+    TCalRRule oldEntryRule;
+    aOldEntry.GetRRuleL(oldEntryRule);
+
+    if ((newEntryRule.Type() != oldEntryRule.Type()) ||
+    (newEntryRule.DtStart().TimeUtcL() != oldEntryRule.DtStart().TimeUtcL()) ||
+    (newEntryRule.Until().TimeUtcL() != oldEntryRule.Until().TimeUtcL()) ||
+    (newEntryRule.Count() != oldEntryRule.Count()))
+        {
+        TRACE_EXIT_POINT;
+        return ETrue;
+        }
+
+    // Did the RDates change?
+    TBool rDatesChanged = EFalse;
+    RArray<TCalTime> newRDates;
+    RArray<TCalTime> oldRDates;
+    CleanupClosePushL(newRDates);
+    CleanupClosePushL(oldRDates);
+    aNewEntry.GetRDatesL(newRDates);
+    aOldEntry.GetRDatesL(oldRDates);
+
+    if (newRDates.Count() != oldRDates.Count())
+        {
+        rDatesChanged = ETrue;
+        }
+    else
+        {
+        for (TInt x = 0; x < newRDates.Count(); ++x)
+            {
+            if (newRDates[x].TimeUtcL() != oldRDates[x].TimeUtcL())
+                {
+                rDatesChanged = ETrue;
+                break;
+                }
+            }
+        }
+
+    CleanupStack::PopAndDestroy(&oldRDates);
+    CleanupStack::PopAndDestroy(&newRDates);
+
+    TRACE_EXIT_POINT;
+    return rDatesChanged;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::TimeOfDay
+// Returns minimum time allowed, 1.1.1900 0:00 is min
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+TTimeIntervalMinutes CCalenInterimUtils2Impl::TimeOfDay( const TTime& aDateTime )
+    {
+    TRACE_ENTRY_POINT;
+    
+    TTime midnight = BeginningOfDay( aDateTime );
+    TTimeIntervalMinutes result;
+    aDateTime.MinutesFrom( midnight, result );
+    return result;
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::BeginningOfDay
+// ?implementation_description
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+TTime CCalenInterimUtils2Impl::BeginningOfDay(const TTime& aStartTime)
+    {
+    TRACE_ENTRY_POINT;
+
+    TTime zero(TInt64(0));
+    
+    TRACE_EXIT_POINT;
+    return zero + aStartTime.DaysFrom(zero);
+    }
+
+// -----------------------------------------------------------------------------
+// CCalenInterimUtils2Impl::EComChanged
+// From MCalenEComChangeObserver, called by when the ecom registry is changed
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCalenInterimUtils2Impl::EComChanged()
+    {
+    TRACE_ENTRY_POINT;
+
+    TRAP_IGNORE(MRViewersEnabledL(ETrue));
+
+    TRACE_EXIT_POINT;
+    }
+
+// End of file