diff -r 000000000000 -r f979ecb2b13e calendarengines/caleninterimutils/src/CalenInterimUtils2Impl.cpp --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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& childAtt = aChild.AttendeesL(); + RPointerArray& 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 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 *node = new (ELeave) CArrayFixFlat(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 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 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 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 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 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& aOldEntries ) + { + TRACE_ENTRY_POINT; + + // For each oldChild... + for(TInt i=1; i 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 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 &aOldEntries, + TBool aResetLocalUid) + { + TRACE_ENTRY_POINT; + + // Start from 1 as we don't want to copy the old parent entry. + for(TInt i=1; iSetLocalUidL( 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 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 newRDates; + RArray 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