--- /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