calendarengines/caleninterimutils/src/CalenInterimUtils2Impl.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 21 Jun 2010 15:38:59 +0300
branchRCL_3
changeset 48 bf573002ff72
parent 0 f979ecb2b13e
child 65 12af337248b1
permissions -rw-r--r--
Revision: 201023 Kit: 2010125

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

/* set the following to the number of 100ns ticks of the actual
   resolution of your system's clock.
   800MHZ means 1s->800*(10^6) clock cycles
   100ns->80 clock cycles*/
const TUint16 UUIDS_PER_TICK = 80;

/// 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;    
    iInited = 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;
    GetSystemTime(timeStamp);
    
    // 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 ( timeNow.Int64() + msDifference.Int64() ) * 10; // * 10 to convert from micro sec (==1000 ns) count to 100ns count.
    }

// -----------------------------------------------------------------------------
// CCalenInterimUtils2Impl::GetSystemTime()
// This function returns the system time.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CCalenInterimUtils2Impl::GetSystemTime(TInt64& aTimeStamp)
    {  
    TRACE_ENTRY_POINT;
    
    if (!iInited)
        {
        aTimeStamp = GetTicksFromGregorianCalendarStartL();
        iThisTick = UUIDS_PER_TICK; 
        iInited = ETrue;
        }
    for( ; ; )
        {
        aTimeStamp = GetTicksFromGregorianCalendarStartL();
        //if clock reading changed since last UUID generated...
        if( iTimeLast != aTimeStamp )
            {
            //reset count of uuids gen'd with this clock reading             
            iThisTick = 0;
            iTimeLast = aTimeStamp;
            break;
            }
        if( iThisTick < UUIDS_PER_TICK )
            {
            iThisTick++;
            break;
            }        
        }
        //add the count of uuids to low order bits of the clock reading 
        aTimeStamp += iThisTick;
        
    TRACE_EXIT_POINT;
    }

// -----------------------------------------------------------------------------
// 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.
    SUuid uuid;   
    TUint8 hash[16];
    
    uuid.time_low = aTimeStamp & 0xFFFFFFFF;
    uuid.time_mid = (aTimeStamp >> 32) & 0xFFFF;
    uuid.time_high_and_version = (aTimeStamp >> 48) & 0x0FFF;
    uuid.time_high_and_version |= (1 << 12);  
    uuid.clock_seq_low = aClockSeq & 0xFF;
    uuid.clock_seq_hi_and_reserved = (aClockSeq & 0x3F00) >> 8;
    uuid.clock_seq_hi_and_reserved |= 0x80;
   
    TUint64 mask = 0xFF0000000000;
    for(TInt i=0; i<=6; ++i)
        {
        TInt64 temp = aNodeValue & mask;
        temp >>= ((5-i)*8);
        uuid.node[i] = temp;
        mask = mask >> 8;
        }

    //md5 context   
    MD5_CTX context;
    //initialize the context
    MD5_Init(&context);
    //Append a string to the message
    MD5_Update(&context, &uuid, sizeof(uuid) );
    //Finish the message and return the digest.
    MD5_Final(hash, &context );
    // Add the version field in the msb 4 bits. The value of version is 3.
    hash[6] = hash[6] & 0x0F;
    hash[6] |= (3 << 4);
    
    //Add the variant field in the msb 2 bits. The value of variant is 2.
    hash[8] = hash[8] & 0x3F;
    hash[8] |= 0x80;
           
    TBuf8<36> output;
    TInt i;
    for(i=0; i<16; ++i)
      {
      output.Append( ConvertToCharacterL( FirstFourBits( hash[i] ) ) ); 
      output.Append( ConvertToCharacterL( LastFourBits( hash[i] ) ) );
      if(i == 3 || i == 5 || i == 7 ||i == 9)
          {
          output.Append( '-' );
          }
      }
    HBufC8* retBuf = output.AllocLC();
    
    TRACE_EXIT_POINT;
    return retBuf;
    
    }

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