diff -r 000000000000 -r 8466d47a6819 meetingrequest/mragnversit2/src/cesmragnversit2importer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/meetingrequest/mragnversit2/src/cesmragnversit2importer.cpp Thu Dec 17 08:39:21 2009 +0200 @@ -0,0 +1,1860 @@ +/* +* Copyright (c) 2002-2009 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 file implements classes CTzNamedRules, CESMRAgnVersit2Importer. +* +*/ + + +// Class include. +#include "emailtrace.h" +#include "cesmragnversit2importer.h" +#include "cesmragnversit2exporter.h" + +//debug +// + +// Agenda includes. +#include // For CCalAlarm +#include // For CCalCategory +#include // For CCalEntry +#include // For CCalRRule +#include // For CCalTime +#include // For CCalUser +#include // For TTzRuleDay +#include // For CTzRules and TTzRule + +// Versit includes. +#include "cesmrical.h" // for cical +#include "cesmricalcomponent.h" // for cicalcomponent +#include "esmricalkeywords.h" // for cicalkeywords +#include "cesmricalparser.h" // for cicalparser +#include "cesmricalproperty.h" // for cicalproperty +#include "cesmricalpropertyparam.h" // for cicalpropertyparam +#include "cesmricalrulesegment.h" // for cicalrulesegment +#include "cesmricalvalue.h" // for cicalvalue + +// ESMRAgnVersit2 includes. +#include "mesmragnimportobserver.h" // For MESMRAgnImportObserver +#include "cesmragnversit2stringprovider.h" // For CESMRAgnVersit2StringProvider +#include "esmrcleanuppointerarray.h" // For TCleanupPointerArray + +#include "cesmragnrruleimporter.h" // For CESMRAgnRRuleImporter +#include "esmrhelper.h" + +// MRUI includes +#include "esmrdef.h" +// + +// Constants. + +// +// CTzNamedRules +// ============= +// + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::CTzNamedRules::CTzNamedRules +// --------------------------------------------------------------------------- +// +CESMRAgnVersit2Importer::CTzNamedRules::CTzNamedRules() + { + FUNC_LOG; + //do nothing + } + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::CTzNamedRules::~CTzNamedRules +// --------------------------------------------------------------------------- +// +CESMRAgnVersit2Importer::CTzNamedRules::~CTzNamedRules() + { + FUNC_LOG; + + delete iRules; + delete iName; + + } + +// +// CESMRAgnVersit2Importer +// =================== +// + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::NewL +// --------------------------------------------------------------------------- +// +CESMRAgnVersit2Importer* CESMRAgnVersit2Importer::NewL(CESMRAgnVersit2StringProvider& aStringProvider) + { + FUNC_LOG; + + CESMRAgnVersit2Importer* self = CESMRAgnVersit2Importer::NewLC(aStringProvider); + CleanupStack::Pop(self); + + return self; + } + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::NewLC +// --------------------------------------------------------------------------- +// +CESMRAgnVersit2Importer* CESMRAgnVersit2Importer::NewLC(CESMRAgnVersit2StringProvider& aStringProvider) + { + FUNC_LOG; + + CESMRAgnVersit2Importer* self = new (ELeave) CESMRAgnVersit2Importer(aStringProvider); + CleanupStack::PushL(self); + self->ConstructL(); + + return self; + } + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::NewL +// --------------------------------------------------------------------------- +// +CESMRAgnVersit2Importer::~CESMRAgnVersit2Importer() + { + FUNC_LOG; + + iTzRules.ResetAndDestroy(); + + } + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::ImportL +// --------------------------------------------------------------------------- +// +void CESMRAgnVersit2Importer::ImportL( RPointerArray& aEntries, + RReadStream& aReadStream, + MESMRAgnImportObserver& aObserver ) + { + FUNC_LOG; + + iImportObserver = &aObserver; + + CESMRICalParser* parser = CESMRICalParser::NewLC(); + parser->InternalizeL(aReadStream); + + const TInt count = parser->CalCount(); + + for (TInt cal = 0; cal < count; ++cal) + { + CESMRICal& calendar = parser->Cal(cal); + TRAPD(err, ImportICalL(aEntries, calendar)); + iTzRules.ResetAndDestroy(); + User::LeaveIfError(err); + } + + CleanupStack::PopAndDestroy(parser); + } + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::CESMRAgnVersit2Importer +// --------------------------------------------------------------------------- +// +CESMRAgnVersit2Importer::CESMRAgnVersit2Importer(CESMRAgnVersit2StringProvider& aStringProvider) + : iStringProvider(aStringProvider) + { + FUNC_LOG; + //do nothing + } + +// --------------------------------------------------------------------------- +// CESMRAgnVersit2Importer::ConstructL +// --------------------------------------------------------------------------- +// +void CESMRAgnVersit2Importer::ConstructL() + { + FUNC_LOG; + //do nothing + } + +/** + * Takes a calendar object from Versit2 and converts it into a set of CCalEntry + * objects. + * @param aEntries The array to append entries to. + * @param aCal An object of type CESMRICal from which to obtain data. + * @leave KErrAbort, or any other system wide error code. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportICalL(RPointerArray& aEntries, CESMRICal& aCal) + { + FUNC_LOG; + + const RPointerArray& components = aCal.Components(); + + // Run through the timezones so that we know what other components are referring to: + iTzRules.ResetAndDestroy(); + TInt com; // current component number + TInt comCount = components.Count(); + for (com = 0; com < comCount && iResponse == MESMRAgnImportObserver::EImpResponseContinue; ++com) + { + CESMRICalComponent* component = components[com]; + + // We are only interested in timezones: + if (component->Type() == CESMRICalBase::EICalTimeZone) + { + TRAPD(err, ImportTimezoneL(*component)); + if (err) + { + // If there isn't a response, then it isn't a recoverable leave: + if (iResponse == MESMRAgnImportObserver::EImpResponseContinue) + { + User::Leave(err); + } + // If there is a response, then we threw this after reporting an + // error to the observer, and we may be able to continue: + else if(iResponse == MESMRAgnImportObserver::EImpResponseSkip) + { + iResponse = MESMRAgnImportObserver::EImpResponseContinue; + } + } + } + } + + // Run through the other components: + for (com = 0; com < comCount && iResponse == MESMRAgnImportObserver::EImpResponseContinue; ++com) + { + CESMRICalComponent* component = components[com]; + + // We aren't interested in timezones: + if (component->Type() != CESMRICalBase::EICalTimeZone) + { + TRAPD(err, ImportComponentL(aCal, *component, aEntries)); + if (err) + { + // If there isn't a response, then it isn't a recoverable leave: + if (iResponse == MESMRAgnImportObserver::EImpResponseContinue) + { + User::Leave(err); + } + // If there is a response, then we threw this after reporting an + // error to the observer, and we may be able to continue: + else if(iResponse == MESMRAgnImportObserver::EImpResponseSkip) + { + iResponse = MESMRAgnImportObserver::EImpResponseContinue; + } + } + } + } + + // If we have been asked to leave after an error, do so: + if (iResponse == MESMRAgnImportObserver::EImpResponseLeave) + { + User::Leave(KErrAbort); + } + + } + +/** +* IMPORT COMPONENT FUNCTIONS +* ========================== +*/ + +/** + * Handles the importing of a single component, other than a timezone. + * @param aCal The calendar object from which we are importing + * @param aComponent The specific component we are importing + * @param aEntries The agenda entries onto which this component is to be appended. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportComponentL( const CESMRICal& aCal, + const CESMRICalComponent& aComponent, + RPointerArray& aEntries ) + { + FUNC_LOG; + + // Get the UID: + HBufC8* entryUid = NULL; + const CESMRICalProperty* propUid = aComponent.FindProperty(KICalUid()); + + + if (!propUid)//(propUid && propUid->Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorInvalidData, KNullDesC8, KICalUid, EFalse); + // We do not continue without a UID. + } + + //if (propUid) + else + { + entryUid = propUid->Values()[0]->BinaryLC(); + + // Import entry-type specific data: + switch (aComponent.Type()) + { + case CESMRICalBase::EICalEvent : + { + CCalEntry::TType eventType = CCalEntry::EEvent; + + //Check if we have a DTEND or DURATION + if (aComponent.FindProperty(KICalDtend()) || aComponent.FindProperty(KICalDuration())) + { + eventType = CCalEntry::EAppt; + } + + else + { + //Check if we have a DTSTART + if (aComponent.FindProperty(KICalDtstart())) + { + eventType = CCalEntry::EReminder; + } + + //Check if this is a yearly repeat + if (aComponent.FindProperty(KICalRRule())) + { + const CESMRICalProperty* rrule = aComponent.FindProperty(KICalRRule()); + const RPointerArray& rulevalues = rrule->Values(); + + if (rulevalues.Count() >= 1) + { + CESMRICalRuleSegment::TFreq freq = CESMRICalRuleSegment::EFreqDaily; + RPointerArray recRules; + CleanupPointerArrayPushL(recRules); + + // At this point we take ownership of the things which rules contains: + rulevalues[0]->GetRecurrenceRuleL(recRules); + + // An RRule must have a frequency. + TInt pos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegFreq); + if (pos != KErrNotFound) + { + ASSERT(recRules.Count() >= pos + 1); + freq = recRules[pos]->FreqL(); + } + + // Find the interval. + pos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegInterval); + TInt interval = 1; //default value if no INTERVAL specified + if (pos != KErrNotFound) + { + ASSERT(recRules[pos]->Values().Count() >= 0); + interval = recRules[pos]->Values()[0]->IntegerL(); + } + + if ((freq == CESMRICalRuleSegment::EFreqYearly) && (interval == 1)) + { + //Anniversary repeats once a year + eventType = CCalEntry::EAnniv; + } + + CleanupStack::PopAndDestroy(&recRules); + } + } + } + CleanupStack::Pop(entryUid); + // Passing ownership of entryUid + ImportEntryL(aCal, aComponent, aEntries, entryUid, eventType); + + break; + } + case CESMRICalBase::EICalTodo : + CleanupStack::Pop(entryUid); + // Passing ownership of entryUid + ImportEntryL(aCal, aComponent, aEntries, entryUid, CCalEntry::ETodo); + break; + case CESMRICalBase::EICalJournal : + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, *entryUid, KICalJournal, EFalse); + // Continue equates to skip in this instance. + break; + case CESMRICalBase::EICalAlarm : + ReportErrorL(MESMRAgnImportObserver::EImpErrorInvalidData, *entryUid, KICalAlarm, EFalse); + // Continue equates to skip in this instance. + break; + case CESMRICalBase::EICalFreeBusy : + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, *entryUid, KICalFreeBusy, EFalse); + // Continue equates to skip in this instance. + break; + case CESMRICalBase::EICalInvalid : // not valid here, fall through + case CESMRICalBase::EICalCalendar : // not valid here, fall through + case CESMRICalBase::EICalTimeZone : // not valid here, fall through + case CESMRICalBase::EICalStandard : // not valid here, fall through + case CESMRICalBase::EICalDaylight : // not valid here, fall through + default : + ReportErrorL( MESMRAgnImportObserver::EImpErrorInvalidData, + *entryUid, + aComponent.TypeStringL(), + EFalse ); + // Continue equates to skip in this instance. + break; + } + } + } + +/** + * Checks for a VALARM in a component. If a valid one is found, it is added to the + * specified entry. + * @param aComponent Component to search for a VALARM in. + * @param aEntry Alarm is added to this calendar entry. + * @param aUid UID of the component. + * @param aStartTime A pointer to the DTSTART of the parent component, if it has + * one, or NULL if it does not. This does not take ownership. + * @return MESMRAgnImportObserver::TImpResponse indicating application response to errors, if any. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportAlarmL( const CESMRICalComponent& aComponent, + CCalEntry& aEntry, + const HBufC8& aUid, + const TCalTime* aStartTime ) + { + FUNC_LOG; + + if (aComponent.ComponentExists(CESMRICalBase::EICalAlarm)) + { + // Check the outer component is a VEVENT or a VTODO + if (aComponent.Type() == CESMRICalBase::EICalEvent || aComponent.Type() == CESMRICalBase::EICalTodo) + { + const RPointerArray& nestedComponents = aComponent.Components(); + // Iterate through all the nested components and create a new alarm for each VALARM component + TInt nestedComCount = nestedComponents.Count(); + for (TInt i = 0; i < nestedComCount; i++) + { + if (nestedComponents[i]->Type() == CESMRICalBase::EICalAlarm) + { + CCalAlarm* alarm = CCalAlarm::NewL(); + CleanupStack::PushL(alarm); + const RPointerArray& nestedProperties = nestedComponents[i]->Properties(); + // Go through all the nested properties until we find the TRIGGER + // Ignore REPEAT and DURATION + any others + TBool foundTrigger=EFalse; + TInt nestedPropCount = nestedProperties.Count(); + for (TInt j = 0; j < nestedPropCount; j++) + { + if (nestedProperties[j]->Type() == KICalTrigger) + { + const RPointerArray& values = nestedProperties[j]->Values(); + // There should only be one value, don't try to add the alarm if there are more + if (values.Count() == 1) + { + const CESMRICalPropertyParam* valueType = nestedProperties[j]->FindParam(KICalValue); + TTimeIntervalMinutes duration; + if (valueType) // Only DATE-TIME is legal here - assume it's that one. + { + if (!aStartTime) + { + ReportErrorL( MESMRAgnImportObserver::EImpErrorMissingData, + aUid, + iStringProvider.StringL(EICalErrValarmNoStart), + EFalse ); + // On continue, we skip the alarm. + } + else + { + TCalTime absTime; + GetCalTimeL(*nestedProperties[j], absTime, EDateTime); + aStartTime->TimeUtcL().MinutesFrom(absTime.TimeUtcL(), duration); + } + } + else + { + duration = values[0]->DurationL().Int() / KSecondsInOneMinute; + } + + // Only negative durations are allowed (the alarm must be *before* the event). + if (duration.Int() > 0) + { + ReportErrorL( MESMRAgnImportObserver::EImpErrorNotSupported, + aUid, + iStringProvider.StringL(EICalErrAlarmAfterEvent) ); + // On continue, the duration will be made "negative". + } + + // Agenda expects a positive duration - convert the sign. + if (duration.Int() < 0) + { + duration = -(duration.Int()); + } + + alarm->SetTimeOffset(duration); + foundTrigger=ETrue; + } + } + } + if (foundTrigger) + { + aEntry.SetAlarmL(alarm);// Doesn't take ownership + } + else + { + ReportErrorL( MESMRAgnImportObserver::EImpErrorMissingData, + aUid, + iStringProvider.StringL(EICalErrValarmNoTrigger), + EFalse ); + // On continue, the alarm will be skipped. + } + CleanupStack::PopAndDestroy(alarm); + } + } + } + else + { + ReportErrorL( MESMRAgnImportObserver::EImpErrorInvalidData, + aUid, + iStringProvider.StringL(EICalErrValarmNotAllowed), + EFalse ); + // On continue, the alarm will be skipped. + } + } + + } + + +/** + * Performs most of the work of importing a component from an iCalendar, provided + * that the component is one which can be handled as a CCalEntry. Timezone and + * Alarm components are not imported in this manner. + * @param aCal The calendar object containing the component. + * @param aComponent The component to be imported. + * @param aEntries The array onto which to append the new entry. + * @param aUid The UID of the component (takes ownership). + * @param aType The type of entry being imoported + * @return MESMRAgnImportObserver::TImpResponse indicating application response to errors, if any. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportEntryL( const CESMRICal& aCal, + const CESMRICalComponent& aComponent, + RPointerArray& aEntries, + HBufC8* aUid, + CCalEntry::TType aType ) + { + FUNC_LOG; + + // Create a basic entry to add properties to. + CCalEntry* entry = CreateEntryLC(aCal, aComponent, aUid, aType); // takes ownership of aUid + + iNeedsTzRules = (EFalse); + iCurrentTzRules = NULL; + + // set the entry properties: + CESMRICalProperty* rruleproperty = NULL; + CESMRICalProperty* startproperty = NULL; + CESMRICalProperty* endproperty = NULL; + CESMRICalProperty* durationproperty = NULL; + RPointerArray exDateProperties; + CleanupClosePushL(exDateProperties); + RPointerArray rDateProperties; + CleanupClosePushL(rDateProperties); + + const RPointerArray& properties = aComponent.Properties(); + TInt propCount = properties.Count(); + for (TInt pnum = 0; pnum < propCount; ++pnum) + { + CESMRICalProperty* property = properties[pnum]; + + // DTSTART, DTEND, DURATION, EXDATE, RDATE and RRULE need special handling, + //as they affect one another. + if (property->Type().CompareF(KICalRRule) == 0) + { + rruleproperty = property; + } + else if (property->Type().CompareF(KICalDtstart) == 0) + { + startproperty = property; + } + else if (property->Type().CompareF(KICalDtend) == 0) + { + endproperty = property; + } + else if (property->Type().CompareF(KICalDuration) == 0) + { + durationproperty = property; + } + else if (property->Type().CompareF(KICalExdate) == 0) + { + // It is valid to have more than one EXDATE property. We store them all in an array. + User::LeaveIfError(exDateProperties.Append(property)); + } + else if (property->Type().CompareF(KICalRdate) == 0) + { + // It is valid to have more than one RDATE property. We store them all in an array. + User::LeaveIfError(rDateProperties.Append(property)); + } + else + { + ImportPropertyL(*entry, *property); + } + } + + // We cannot handle a DTSTART, DTEND or DURATION field on its own, so we have saved them up + // and will sort them out here. + // In theory, some methods allow an event to specify an end time but no start time. + // We have no way of dealing with this so we discard it. When we get a start time + // with no end time, we assume a duration of 0 seconds. + TCalTime start; + TCalTime end; + if (startproperty) + { + GetCalTimeL(*startproperty, start, EDateTime); + end = start; + if (endproperty) + { + GetCalTimeL(*endproperty, end, EDateTime); + } + else if (durationproperty && startproperty) + { + if (durationproperty->Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, *aUid, KICalDuration); + // On continue use the start time as the end time (a duration of 0 seconds) + } + else + { + end.SetTimeUtcL(start.TimeUtcL() + durationproperty->Values()[0]->DurationL()); + } + } + entry->SetStartAndEndTimeL(start, end); + } + + // We must have a start time before we can interpret some alarms: + TRAPD(alarmerr, ImportAlarmL(aComponent, *entry, *aUid, startproperty?&start:0)); + // If we fail and elect to skip whilst importing the alarm, continue + // with the current component. + if (alarmerr && iResponse == MESMRAgnImportObserver::EImpResponseSkip) + { + iResponse = MESMRAgnImportObserver::EImpResponseContinue; + } + else + { + User::LeaveIfError(alarmerr); + } + + // We cannot put an rrule property into the agenda model format until we + // have properties such as dtstart already stored, so we do this last: + if (rruleproperty) + { + if (startproperty) + { + if (ImportRRuleL(*rruleproperty, *entry, start)) + { + // APINOTE: In the Cal Interim API EXDATE is only valid with an RRULE. + // This is not necessarily so for iTIP updates. + for (TInt i = 0; i < exDateProperties.Count(); i++) + { + ImportPropertyL(*entry, *(exDateProperties[i])); + } + } + else if (exDateProperties.Count() > 0) + { + // Cannot process an EXDATE without an RRULE. + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, *aUid, KICalExdate); + } + } + else + { + // Cannot process an RRULE without a DTSTART + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, *aUid, KICalDtstart); + } + } + + + // RDATE does not neccessary need an RRULE. + for (TInt i = 0; i < rDateProperties.Count(); i++) + { + ImportPropertyL(*entry, *(rDateProperties[i])); + } + + CleanupStack::PopAndDestroy(&rDateProperties); + CleanupStack::PopAndDestroy(&exDateProperties); + + // APINOTE: When setting the RRULE, the API overwrites the DTEND field. + // This block sets it back to what it should be. + if (startproperty) + { + // We don't need to check for an end time as it gets initialised when the + // start time gets set. + entry->SetStartAndEndTimeL(start, end); + } + + // If we have set up a timezone, add this to the entry. + // Only entries with RDATES, EXDATES, RRULES or EXRULES will have this. + if (iNeedsTzRules) + { + if (iCurrentTzRules) + { + entry->SetTzRulesL(*iCurrentTzRules); + } + } + + //Set the last modified date to current time + entry->SetLastModifiedDateL(); + // Store the entry: + User::LeaveIfError(aEntries.Append(entry)); // Takes ownership if successful + CleanupStack::Pop(entry); + + } + +/** + * Creates a new entry and pushes it onto the cleanup stack. + * @param aCal The calendar object containing the component. + * @param aComponent The component to be imported. + * @param aUid The UID of the component (takes ownership). + * @param aType The type of entry being imoported + * @return A new CCalEntry, or zero if a recoverable error occurs and the observer + * elects not to continue from it. + * @internalTechnology + */ +CCalEntry * CESMRAgnVersit2Importer::CreateEntryLC( const CESMRICal& aCal, + const CESMRICalComponent& aComponent, + HBufC8* aUid, + CCalEntry::TType aType ) + { + + CleanupStack::PushL(aUid); + + const CESMRICalProperty* propSeq = aComponent.FindProperty(KICalSequence); + + // Find the sequence number, but accept a default of 0 if there isn't one: + TUint entrySeq = 0; + if (propSeq && propSeq->Values().Count() > 0) + { + entrySeq = propSeq->Values()[0]->IntegerL(); + } + + // Find the method, but accept a default of EMethodNone if there isn't one: + CCalEntry::TMethod method = CCalEntry::EMethodNone; + const CESMRICalProperty* prop = aCal.FindProperty(KICalMethod); + if (prop && prop->Values().Count() > 0) + { + TPtrC val; + val.Set(prop->Values()[0]->TextL()); + if (val.CompareF(KICalPublish) == 0) + { + method = CCalEntry::EMethodPublish; + } + else if (val.CompareF(KICalRequest) == 0) + { + method = CCalEntry::EMethodRequest; + } + else if (val.CompareF(KICalReply) == 0) + { + method = CCalEntry::EMethodReply; + } + else if (val.CompareF(KICalAdd) == 0) + { + method = CCalEntry::EMethodAdd; + } + else if (val.CompareF(KICalCancel) == 0) + { + method = CCalEntry::EMethodCancel; + } + else if (val.CompareF(KICalRefresh) == 0) + { + method = CCalEntry::EMethodRefresh; + } + else if (val.CompareF(KICalCounter) == 0) + { + method = CCalEntry::EMethodCounter; + } + else if (val.CompareF(KICalDeclineCounter) == 0) + { + method = CCalEntry::EMethodDeclineCounter; + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorInvalidData, *aUid, val); + // On continue, use EMethodNone. + } + } + + CCalEntry* entry = NULL; + // Find the recurrence information, if there is any: + const CESMRICalProperty* propRecurrenceId = aComponent.FindProperty(KICalRecurrenceId); + if (propRecurrenceId) + { + TCalTime entryRecId; + GetCalTimeL(*propRecurrenceId, entryRecId, EDateTime); + + const CESMRICalPropertyParam* range = propRecurrenceId->FindParam(KICalRange); + CalCommon::TRecurrenceRange entryRange = CalCommon::EThisOnly; + if (range && range->Values().Count() > 0) + { + if (range->Values()[0]->TextL().CompareF(KICalThisAndPrior) == 0) + { + entryRange = CalCommon::EThisAndPrior; + } + else if (range->Values()[0]->TextL().CompareF(KICalThisAndFuture) == 0) + { + entryRange = CalCommon::EThisAndFuture; + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorInvalidData, *aUid, range->Values()[0]->TextL()); + // On continue, assume this instance only. + } + } + // Create an entry with recurrence data: + // Passing ownership of aUid + CleanupStack::Pop(aUid); + entry = CCalEntry::NewL(aType, aUid, method, entrySeq, entryRecId, entryRange); + CleanupStack::PushL(entry); + } + else + { + // Create an entry without recurrence data: + // Passing ownership of aUid + CleanupStack::Pop(aUid); + entry = CCalEntry::NewL(aType, aUid, method, entrySeq); + CleanupStack::PushL(entry); + } + + return entry; + } + +/** + * Imports an individual property and adds it to an entry. + * @param aEntry The entry to which the property is to be added. + * @param aProperty The property to add. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportPropertyL(CCalEntry& aEntry, const CESMRICalProperty& aProperty) + { + FUNC_LOG; + + if (aProperty.Type().CompareF(KICalAttendee) == 0) + { + // Find the paramaters we need: + CESMRICalValue* address = aProperty.Values()[0]; + const CESMRICalPropertyParam* sentBy = aProperty.FindParam(KICalSentBy); + const CESMRICalPropertyParam* role = aProperty.FindParam(KICalRole); + const CESMRICalPropertyParam* status = aProperty.FindParam(KICalPartStat); + const CESMRICalPropertyParam* rsvp = aProperty.FindParam(KICalRsvp); + const CESMRICalPropertyParam* cn = aProperty.FindParam(KICalCn); + // The role: + CCalAttendee::TCalRole roleVal = CCalAttendee::EReqParticipant; + if (role && role->Values().Count() > 0) + { + CESMRICalValue* theVal = role->Values()[0]; + if (theVal->TextL().CompareF(KICalChair) == 0) + { + roleVal = CCalAttendee::EChair; + } + else if (theVal->TextL().CompareF(KICalOptParticipant) == 0) + { + roleVal = CCalAttendee::EOptParticipant; + } + else if (theVal->TextL().CompareF(KICalNonParticipant) == 0) + { + roleVal = CCalAttendee::ENonParticipant; + } + + } + // The status: + CCalAttendee::TCalStatus statusVal = CCalAttendee::ENeedsAction; + if (status && status->Values().Count() > 0) + { + CESMRICalValue* theVal = status->Values()[0]; + if (theVal->TextL().CompareF(KICalAccepted) == 0) + { + statusVal = CCalAttendee::EAccepted; + } + else if (theVal->TextL().CompareF(KICalDeclined) == 0) + { + statusVal = CCalAttendee::EDeclined; + } + else if (theVal->TextL().CompareF(KICalTentative) == 0) + { + statusVal = CCalAttendee::ETentative; + } + else if (theVal->TextL().CompareF(KICalDelegated) == 0) + { + statusVal = CCalAttendee::EDelegated; + } + else if (theVal->TextL().CompareF(KICalCompleted) == 0) + { + statusVal = CCalAttendee::ECompleted; + } + else if (theVal->TextL().CompareF(KICalInProcess) == 0) + { + statusVal = CCalAttendee::EInProcess; + } + } + // The RSVP: + TBool rsvpVal = EFalse; + if (rsvp && rsvp->Values().Count() > 0) + { + rsvpVal = rsvp->Values()[0]->BooleanL(); + } + + // The complete attendee: + CCalAttendee* attendee; + if (sentBy && sentBy->Values().Count() > 0) + { + attendee = CCalAttendee::NewL( + address->TextL(), + sentBy->Values()[0]->TextL()); + } + else + { + HBufC* buf = + ESMRHelper::AddressWithoutMailtoPrefix( + address->TextL() ).AllocLC(); + + attendee = CCalAttendee::NewL( buf->Des() ); + CleanupStack::PopAndDestroy( buf ); + buf = NULL; + } + CleanupStack::PushL(attendee); + if (cn && cn->Values().Count() > 0) + { + attendee->SetCommonNameL(cn->Values()[0]->TextL()); + } + attendee->SetRoleL(roleVal); + attendee->SetStatusL(statusVal); + attendee->SetResponseRequested(rsvpVal); + // Add the attendee: + CleanupStack::Pop(attendee); + // Passing ownership of attendee + aEntry.AddAttendeeL(attendee); + } + else if (aProperty.Type().CompareF(KICalCategories) == 0) + { + ImportCategoriesL(aProperty, aEntry); + } + else if (aProperty.Type().CompareF(KICalClass) == 0) + { + ImportClassL(aProperty, aEntry); + } + else if (aProperty.Type().CompareF(KICalDescription) == 0) + { + if (aProperty.Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalDescription); + // On continue ignore the description. + } + else + { + aEntry.SetDescriptionL(aProperty.Values()[0]->TextL()); + } + } + else if (aProperty.Type().CompareF(KICalLocation) == 0) + { + // CCalEntry only supports one location, so bundle them all + // together into one descriptor. + TInt numLocations = aProperty.Values().Count(); + + if (numLocations > 0) + { + HBufC* loc (aProperty.Values()[0]->TextL().AllocLC()); + + for (TInt i = 1; i < numLocations; i++) + { + HBufC* oldLoc = loc; + loc = loc->ReAllocL(loc->Length() + aProperty.Values()[i]->TextL().Length() + 1); + CleanupStack::Pop(oldLoc); // location of loc has changed, cleanupstack needs updating + CleanupStack::PushL(loc); + loc->Des().Append(KICalComma); + loc->Des().Append(aProperty.Values()[i]->TextL()); + } + + aEntry.SetLocationL(*loc); + CleanupStack::PopAndDestroy(loc); + } + } + else if (aProperty.Type().CompareF(KICalOrganizer) == 0) + { + if (aProperty.Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalOrganizer); + // On continue ignore the organizer. + } + else + { + CESMRICalValue* address = aProperty.Values()[0]; + const CESMRICalPropertyParam* sentBy = aProperty.FindParam(KICalSentBy); + const CESMRICalPropertyParam* cn = aProperty.FindParam(KICalCn); + CCalUser* user = NULL; + if (sentBy && sentBy->Values().Count() > 0) + { + user = CCalAttendee::NewL(address->TextL(), sentBy->Values()[0]->TextL()); + } + else + { + HBufC* buf = ESMRHelper::AddressWithoutMailtoPrefix( + address->TextL() ).AllocLC(); + + user = CCalAttendee::NewL( buf->Des() ); + CleanupStack::PopAndDestroy( buf ); + buf = NULL; + } + CleanupStack::PushL(user); + if (cn && cn->Values().Count() > 0) + { + user->SetCommonNameL(cn->Values()[0]->TextL()); + } + CleanupStack::Pop(user); + // Passing ownership of user + aEntry.SetOrganizerL(user); + } + } + else if (aProperty.Type().CompareF(KICalRdate) == 0) + { + iNeedsTzRules = ETrue; + RArray dates; + CleanupClosePushL(dates); + aEntry.GetRDatesL(dates); + TInt valCount = aProperty.Values().Count(); + for (TInt dnum = 0; dnum < valCount; ++dnum) + { + TCalTime time; + GetCalTimeL(aProperty, time, EDateTime, dnum); + User::LeaveIfError(dates.Append(time)); + } + aEntry.SetRDatesL(dates); + CleanupStack::PopAndDestroy(&dates); + } + else if (aProperty.Type().CompareF(KICalExdate) == 0) + { + iNeedsTzRules = ETrue; + RArray dates; + CleanupClosePushL(dates); + aEntry.GetExceptionDatesL(dates); + TInt valCount = aProperty.Values().Count(); + for (TInt dnum = 0; dnum < valCount; ++dnum) + { + TCalTime time; + GetCalTimeL(aProperty, time, EDateTime, dnum); + User::LeaveIfError(dates.Append(time)); + } + aEntry.SetExceptionDatesL(dates); + CleanupStack::PopAndDestroy(&dates); + } + else if (aProperty.Type().CompareF(KICalStatus) == 0) + { + if (aProperty.Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalDescription); + // On continue ignore the status. + } + else + { + TPtrC statstr(aProperty.Values()[0]->TextL()); + if (statstr.CompareF(KICalTentative) == 0) + { + aEntry.SetStatusL(CCalEntry::ETentative); + } + else if (statstr.CompareF(KICalConfirmed) == 0) + { + aEntry.SetStatusL(CCalEntry::EConfirmed); + } + else if (statstr.CompareF(KICalCancelled) == 0) + { + aEntry.SetStatusL(CCalEntry::ECancelled); + } + else if (statstr.CompareF(KICalNeedsAction) == 0) + { + aEntry.SetStatusL(CCalEntry::ETodoNeedsAction); + } + else if (statstr.CompareF(KICalCompleted) == 0) + { + aEntry.SetStatusL(CCalEntry::ETodoCompleted); + } + else if (statstr.CompareF(KICalInProcess) == 0) + { + aEntry.SetStatusL(CCalEntry::ETodoInProcess); + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, aEntry.UidL(), statstr); + // On continue ignore the status. + } + } + } + else if (aProperty.Type().CompareF(KICalSummary) == 0) + { + if (aProperty.Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalDescription); + // On continue ignore the summary. + } + else + { + aEntry.SetSummaryL(aProperty.Values()[0]->TextL()); + } + } + else if (aProperty.Type().CompareF(KICalPriority) == 0) + { + if (aProperty.Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalDescription); + // On continue ignore the priority. + } + else + { + TInt priority = aProperty.Values()[0]->IntegerL(); + + //Pirorites are saved always in vcal format + //so they are mapped the following way + //iCal 1 - 4 = vCal 1 + //iCal 5 = vCal 2 + //iCal 6 - 9 = vCal 3 + + // MRUI always uses iCal format + //Pirorites are saved always in vcal format + //so they are mapped the following way + //vCal 1 = iCal 1 + //vCal 2 = iCal 5 + //vCal 3 = iCal 9 + + if ( priority != EFSCalenMRPriorityLow && + priority != EFSCalenMRPriorityNormal && + priority != EFSCalenMRPriorityHigh ) + { + priority = EFSCalenMRPriorityNormal; + } + /*if( priority < 5 ) + { + priority = 1; + } + + else if( priority == 5 ) + { + priority = 2; + } + + else + { + priority = 3; + }*/ + + aEntry.SetPriorityL( priority ); + } + } + + else if (aProperty.Type().CompareF(KICalDtstamp) == 0) + { + TCalTime timeStamp; + GetCalTimeL(aProperty, timeStamp, EDateTime); + aEntry.SetDTStampL(timeStamp); + } + else if ((aProperty.Type().CompareF(KICalUid) == 0) || + (aProperty.Type().CompareF(KICalSequence) == 0) || + (aProperty.Type().CompareF(KICalRecurrenceId) == 0) || + (aProperty.Type().CompareF(KICalLastmodified) == 0)) + { + //Already been handled when constructing the entry :- do nothing + } + else if (aProperty.Type().Find(KICalXProperty) == 0) + { + // Quietly swallow X- properties, instead of reporting "NotSupported" + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, aEntry.UidL(), aProperty.Type()); + } + + } + +/** + * Performs most of the work of importing a timezone component. Timezones are not + * stored as separate entities within Agenda, but as CTzRules objects within + * individual entries, and so this maintains an array of all timezone components + * for any given calendar, to be accessed later as required. + * @param aTimezone The timezone to convert into CTzRules format. + * @return MESMRAgnImportObserver::TImpResponse indicating application response to errors, if any. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportTimezoneL(const CESMRICalComponent& aTimezone) + { + FUNC_LOG; + + CTzRules* rules = NULL; + // Get the TZID: + TPtrC tzid; + const CESMRICalProperty* propUid = aTimezone.FindProperty(KICalTzid()); + if (propUid && propUid->Values().Count() > 0) + { + tzid.Set(propUid->Values()[0]->TextL()); + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorInvalidData, KNullDesC8, KICalUid, EFalse); + // A Timezone with no ID is completely pointless, so on continue skip. + } + + // Create somewhere to store the data we are parsing: + rules = CTzRules::NewL(); + CleanupStack::PushL(rules); + + // We don't care about any of the timezones properties except the tzid - we go straight + // onto the standard and daylight values. + const RPointerArray& tzComponents = aTimezone.Components(); + if (tzComponents.Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, KNullDesC8, KICalTimeZone, EFalse); + // A Timezone without atleast 1 value is meaningless so on continue skip. + } + else + { + // Agenda expects a set of month+day definitions, which repeat every year between start and end years. + // iCalendar provides a set of year+month+day definitions, which cannot always be represented. + // We will try, and hope for the best, using the first rules start and end to define the overall duration, + // effectively overwriting all the other rules. + rules->SetInitialStdTimeOffset(0); + + TInt tzCount = tzComponents.Count(); + for (TInt tznum = 0; tznum < tzCount; ++tznum) + { + // Find the properties for this component: + CESMRICalComponent* tzinterval = tzComponents[tznum]; + ImportTimezoneIntervalL(*tzinterval, *rules); + } + } + + CTzNamedRules* namedRules = new (ELeave) CTzNamedRules; + CleanupStack::Pop(rules); + namedRules->iRules = rules; + CleanupStack::PushL(namedRules); + namedRules->iName = tzid.AllocL(); + User::LeaveIfError(iTzRules.Append(namedRules)); + CleanupStack::Pop(namedRules); + + } + +/** + * Performs most of the work of translating a DAYLIGHT or STANDARD section of a + * VTIMEZONE into the equivalent TTzRule for Agenda. + * @param aInterval The DAYLIGHT or STANDARD component. + * @param aRules The CTzRules into which the TTzRule will be placed. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportTimezoneIntervalL( const CESMRICalComponent& aInterval, + CTzRules& aRules ) + { + FUNC_LOG; + + // Find the properties: + const CESMRICalProperty* from = aInterval.FindProperty(KICalTzoffsetfrom); + const CESMRICalProperty* to = aInterval.FindProperty(KICalTzoffsetto); + const CESMRICalProperty* start = aInterval.FindProperty(KICalDtstart); + const CESMRICalProperty* rdate = aInterval.FindProperty(KICalRdate); + const CESMRICalProperty* rrule = aInterval.FindProperty(KICalRRule); + + // Set the time offsets it implies: + TTzRule tzrule; + tzrule.iOldLocalTimeOffset = from->Values()[0]->UtcOffsetL().Int() / KSecondsInOneMinute; + tzrule.iNewLocalTimeOffset = to->Values()[0]->UtcOffsetL().Int() / KSecondsInOneMinute; + tzrule.iTimeReference = ETzUtcTimeReference; + + RPointerArray recRules; + CleanupPointerArrayPushL(recRules); + + // If this has an rdate with more than one value we can only represent it by + // creating a seperate tzrule for each date. Not only that, but because the rdate + // specifies years, we need separate CTzRules for each year, which we can't actually + // do in Agenda. This is unsupported. + if (rdate) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalRdate); + // On continue we ignore the rdate. + } + + // Start date + // Agenda has a start year and end year for the entire timezone. + // iCalendar has a start date for each interval and an end date for each rrule within the interval. + // This means we are trying to map N starts and M ends to 1 start and 1 end. + // We will just use whatever we find - it's not going to work and it can't be made to work. + TDateTime dateTime; + if (start) + { + TTime startTime; + CESMRICalValue::TTimeZoneType type; + start->Values()[0]->GetDateTimeL(startTime, type); + dateTime = startTime.DateTime(); + aRules.SetStartYear(dateTime.Year()); + tzrule.iFrom = TTimeWithReference( dateTime ); + // Put the max TTimeWithReference value to iTo as default. + // Normally it is not set in the VCALENDAR object, + // but it is required for CTzRules API. + tzrule.iTo = TTimeWithReference::Max(); + // We cannot set the day of the month on which this timezone rule starts being applied + // because Agenda does not support it. We just have to make do without it; the field + // gets set to the first day of the month by default. + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, KNullDesC8, KICalDtstart, EFalse); + // We cannot continue without a start, so skip. + } + + // If this has an rrule we may be able to represent it. + if (rrule && rrule->Values().Count() > 0) + { + // One thing we cannot do is handle multiple RRules. + if (rrule->Values().Count() > 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, + iStringProvider.StringL(EICalErrRruleNumRules)); + // On continue ignore all but the first RRule. + } + CESMRICalRuleSegment::TFreq freq; + rrule->Values()[0]->GetRecurrenceRuleL(recRules); + + // Find the frequency first - if there is no frequency it is an error, and if the + // frequency is anything other than yearly we can't handle it anyway. + TInt pos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegFreq); + if (pos != KErrNotFound) + { + ASSERT(recRules.Count() >= pos + 1); + freq = recRules[pos]->FreqL(); + if (freq != CESMRICalRuleSegment::EFreqYearly) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, + iStringProvider.StringL(EICalErrRruleNotYearly)); + // On continue assume yearly. + freq = CESMRICalRuleSegment::EFreqYearly; + } + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, KNullDesC8, + rrule->Values()[0]->TextL()); + // On continue assume yearly. + freq = CESMRICalRuleSegment::EFreqYearly; + } + + // Find the interval. We can only handle an interval of 1. + TInt thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegInterval); + if (thePos != KErrNotFound) + { + ASSERT(recRules.Count() >= thePos + 1); + ASSERT(recRules[thePos]->Values().Count() >= 0); + if (recRules[thePos]->Values()[0]->IntegerL() != 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalInterval); + // On continue assume the interval is 1. + } + } + + // Find the count, and interpret it as an end year. + // See the comment above about start date for the problems with this. + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegCount); + if (thePos != KErrNotFound) + { + ASSERT(recRules.Count() >= thePos + 1); + ASSERT(recRules[thePos]->Values().Count() >= 0); + TInt count = recRules[thePos]->Values()[0]->IntegerL(); + aRules.SetEndYear(aRules.StartYear() + count); + } + + // Find the until, and interpret it as an end year. + // See the comment above about start date for the problems with this. + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegUntil); + if (thePos != KErrNotFound) + { + ASSERT(recRules.Count() >= thePos + 1); + ASSERT(recRules[thePos]->Values().Count() >= 0); + TTime endTime; + recRules[thePos]->Values()[0]->GetDateL(endTime); + aRules.SetEndYear(endTime.DateTime().Year()); + } + + // WKST (which defines the day on which a week starts) is not + // supported by the agenda timezones. + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegUntil); + if (thePos != KErrNotFound) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalWkSt); + // On continue ignore the WKST. + } + + // BySecond, ByMinute, ByHour, ByYearDay, ByWeekNo are not + // supported by the agenda timezones. + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegBySecond); + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByMinute); + } + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByHour); + } + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByYearDay); + } + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByWeekNo); + } + if (thePos != KErrNotFound) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalRRule); + // On continue ignore the unsupported field. + } + + // ByMonth + TBool haveMonth = EFalse; + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByMonth); + if (thePos != KErrNotFound) + { + if (recRules[thePos]->Values().Count() > 0) + { + TMonth month = recRules[thePos]->Values()[0]->MonthL(); + haveMonth = ETrue; + tzrule.iMonth = month; + if (recRules[thePos]->Values().Count() > 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalByMonth); + // On continue ignore the additional months. + } + } + } + + if (!haveMonth) + { + tzrule.iMonth = dateTime.Month(); + } + + // ByDay + TBool haveDay = EFalse; + TInt daypos; + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByDay); + if (thePos != KErrNotFound) + { + if (recRules[thePos]->Values().Count() > 0) + { + TDay day; + recRules[thePos]->Values()[0]->GetDayL(day, daypos); + haveDay = ETrue; + tzrule.iDayOfWeek = day; + if (recRules[thePos]->Values().Count() > 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalByDay); + // On continue ignore extra days. + } + } + } + + if (haveDay) + { + if (daypos == 0) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalByDay, EFalse); + // We cannot handle this - on continue skip. + } + else if (daypos < 0) + { + // We cant handle -2,-3 etc, so we assume -1 + tzrule.iDayRule = ETzDayInLastWeekOfMonth; + tzrule.iDayOfMonth = 0; + } + else + { + if (daypos > KMaxWeekDayNum) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorInvalidData, KNullDesC8, KICalByDay); + // On continue, assume the KMaxWeekDayNum. + daypos = KMaxWeekDayNum; + } + tzrule.iDayRule = ETzDayAfterDate; + tzrule.iDayOfMonth = (daypos - 1) * KDaysPerWeek; + } + } + else + { + tzrule.iDayRule = ETzFixedDate; + tzrule.iDayOfMonth = dateTime.Day(); + } + + // ByMonthDay and BySetPos can in theory be supported under + // some circumstances, but right now they aren't. + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByMonthDay); + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CESMRICalRuleSegment::ESegByPos); + } + if (thePos != KErrNotFound) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalRRule); + // On continue ignore both fields. + } + + tzrule.iTimeOfChange = dateTime.Hour() * KMinutesInOneHour + dateTime.Minute(); + aRules.AddRuleL(tzrule); + } + else // no rrule + { + tzrule.iDayRule = ETzFixedDate; + tzrule.iMonth = dateTime.Month(); + tzrule.iDayOfMonth = dateTime.Day(); + tzrule.iTimeOfChange = dateTime.Hour() * KMinutesInOneHour + dateTime.Minute(); + aRules.AddRuleL(tzrule); + } + CleanupStack::PopAndDestroy(&recRules); + + } + +/** +* IMPORT PROPERTY FUNCTIONS +* ========================= +*/ + +/** + * Helper function for translating categories from the pure text format of + * iCalendar to the enumerated type of Agenda. + * @param aProperty A category property. + * @param aEntry An entry into which to put the categories. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportCategoriesL( const CESMRICalProperty& aProperty, + CCalEntry& aEntry ) const + { + FUNC_LOG; + + TInt valCount = aProperty.Values().Count(); + for (TInt pnum = 0; pnum < valCount; ++pnum) + { + TPtrC name(aProperty.Values()[pnum]->TextL()); + CCalCategory* cat = NULL; + if (name.CompareF(iStringProvider.StringL(EICalAppointment)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalAppointment); + } + else if (name.CompareF(iStringProvider.StringL(EICalBusiness)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalBusiness); + } + else if (name.CompareF(iStringProvider.StringL(EICalEducation)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalEducation); + } + else if (name.CompareF(iStringProvider.StringL(EICalHoliday)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalHoliday); + } + else if (name.CompareF(iStringProvider.StringL(EICalMeeting)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalMeeting); + } + else if (name.CompareF(iStringProvider.StringL(EICalMisc)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalMiscellaneous); + } + else if (name.CompareF(iStringProvider.StringL(EICalPersonal)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalPersonal); + } + else if (name.CompareF(iStringProvider.StringL(EICalPhoneCall)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalPhoneCall); + } + else if (name.CompareF(iStringProvider.StringL(EICalSick)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalSickDay); + } + else if (name.CompareF(iStringProvider.StringL(EICalSpecial)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalSpecialOccasion); + } + else if (name.CompareF(iStringProvider.StringL(EICalTravel)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalTravel); + } + else if (name.CompareF(iStringProvider.StringL(EICalVacation)) == 0) + { + cat = CCalCategory::NewL(CCalCategory::ECalVacation); + } + else + { + cat = CCalCategory::NewL(name); + } + aEntry.AddCategoryL(cat); + } + } + +/** + * A function to import a CLASS property into a format suitable for Agenda. + * @param aProperty The CLASS property to translate. + * @param aEntry The entry to add the rules to. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ImportClassL(const CESMRICalProperty& aProperty, CCalEntry& aEntry) + { + FUNC_LOG; + + if (aProperty.Values().Count() > 0) + { + // Ignore any values after the first one + TPtrC name(aProperty.Values()[0]->TextL()); + if (name.CompareF(KICalPublic) == 0) + { + aEntry.SetReplicationStatusL(CCalEntry::EOpen); + } + else if (name.CompareF(KICalPrivate) == 0) + { + aEntry.SetReplicationStatusL(CCalEntry::EPrivate); + } + else if (name.CompareF(KICalConfidential) == 0) + { + aEntry.SetReplicationStatusL(CCalEntry::ERestricted); + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, aEntry.UidL(), name); + // On continue ignore the class. + } + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalRRule); + // On continue ignore the class. + } + } + +/** + * A function to translate an RRULE property into a TCalRRule for Agenda. + * @param aProperty The RRULE property to translate. + * @param aEntry The entry to add the rules to. + * @param aStartTime The DTSTART of the parent component. + * @return ETrue if the rule is imported, else EFalse. + * @internalTechnology + */ +TBool CESMRAgnVersit2Importer::ImportRRuleL( const CESMRICalProperty& aProperty, + CCalEntry& aEntry, + const TCalTime& aStartTime ) + { + FUNC_LOG; + + //Create a new recurrence rule importer + CESMRAgnRRuleImporter *importer = CESMRAgnRRuleImporter::NewLC( *this ); + + //import the recurrence rule + TBool importRRule = importer->ImportL( aProperty, aEntry, aStartTime ); + + if( importRRule ) + { + iNeedsTzRules = ETrue; + } + + CleanupStack::PopAndDestroy( importer ); + + + //return true if the rule is imported + return importRRule; + } + + + +// +// HELPER FUNCTIONS +// ================ +// + + +/** + * Takes a property, a TCalTime, and an index into the set of values of the + * property, and populates the TCalTime from the value, performing any required + * timezone conversions first. + * @param aProperty A property which has a date or date-time type. + * @param aTime The time to put the resulting UTC time into. + * @param aDefaultType Enumeration of the type of value being passed. + * @param aValue The index of the property value to use, for properties which have + * more than one. + * @internalTechnology + */ +void CESMRAgnVersit2Importer::GetCalTimeL( const CESMRICalProperty& aProperty, + TCalTime& aTime, + TValueType aValueType, + TInt aValue ) + { + FUNC_LOG; + + // Find the property value, value parameter and tzid parameter: + if (aProperty.Values().Count() < aValue) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, KNullDesC8, aProperty.Type(), EFalse); + // We cannot continue - we interpret continue as skip. + } + + const CESMRICalPropertyParam* valuetype = aProperty.FindParam(KICalValue); + if (valuetype) + { + if (valuetype->Values().Count() < 1) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData, KNullDesC8, KICalValue); + // On continue, use the default type passed in the function arguments. + } + else + { + TPtrC vt(valuetype->Values()[0]->TextL()); + if (vt.CompareF(KICalDate) == 0) + { + aValueType = EDate; + } + else if (vt.CompareF(KICalDateTime) == 0) + { + aValueType = EDateTime; + } + else + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, vt, EFalse); + // We can't continue - skip instead. + } + } + } + + TTime dateAndTime; + CESMRICalValue::TTimeZoneType tzType = CESMRICalValue::EUtcTime; + + CESMRICalValue* value = aProperty.Values()[aValue]; + + switch (aValueType) + { + case EDate : + value->GetDateL(dateAndTime); + break; + case EDateTime : + value->GetDateTimeL(dateAndTime, tzType); + break; + case ETime : + value->GetTimeL(dateAndTime, tzType); + break; + case EDefault : // fall through.. + case EDuration : // not supported, fall through + case EPeriod : // not supported, fall through + default : + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalPeriod, EFalse); + // We can't continue - skip instead. + break; + } + } + + const CESMRICalPropertyParam* tzid = aProperty.FindParam(KICalTzid); + + // Handle timezone information: + if (tzType == CESMRICalValue::EUtcTime) + { + // A trailing Z means UTC: + aTime.SetTimeUtcL(dateAndTime); + } + else if (!tzid || tzid->Values().Count() < 1) + { + // No trailing Z and no timezone ID. + // This is a floating time. + aTime.SetTimeLocalFloatingL(dateAndTime); + } + else + { + // Find the name of the timezone, and the timezone with that name: + const CTzRules* rules = FindTzRule(tzid->Values()[0]->TextL()); + + if (!rules) + { + ReportErrorL(MESMRAgnImportObserver::EImpErrorMissingData,KNullDesC8, aProperty.Type()); + // On continue, assume UTC. + } + else + { + // Remember the timezone: + iCurrentTzRules = rules; + // All times are stored in Agenda in UTC, and the timezone + // applied by Agenda where appropriate: + rules->ConvertToUtcL(dateAndTime); + } + + // Set the time: + aTime.SetTimeUtcL(dateAndTime); + } + } + +/** + * Searches through a set of rule segments and finds the segment corresponding to + * a particular type. + * @param aRules The array of rules to search. + * @param aType The type of segment to search for from CESMRICalRuleSegment::TSegmentType. + * @see CESMRICalRuleSegment::TSegmentType + * @return The index of the rule within the array, or KErrNotFound + * @internalTechnology + */ +TInt CESMRAgnVersit2Importer::FindRuleSegment( const RPointerArray& aRules, + CESMRICalRuleSegment::TSegmentType aType ) const + { + FUNC_LOG; + + for (TInt r = aRules.Count() - 1; r > -1; --r) + { + if (aRules[r]->Type() == aType) + { + return r; + } + } + return KErrNotFound; + } + +/** + * Searches through the internally stored array of time zone rules looking for one + * with a particular name, and returns its address. This does not transfer + * ownership of the rule. + * @param aName The name of the timezone rule to find (corresponding to TZID) + * @return A pointer to the rule, or zero if no matching rule is found. + * @internalTechnology + */ +const CTzRules* CESMRAgnVersit2Importer::FindTzRule(const TDesC& aName) const + { + FUNC_LOG; + + TInt tzCount = iTzRules.Count(); + for (TInt r = 0; r < tzCount; ++r) + { + if (iTzRules[r]->iName->CompareF(aName) == 0) + { + return iTzRules[r]->iRules; + } + } + + return NULL; + } + +/** + * Reports an error through the MESMRAgnImportObserver interface, and then leaves if + * the return value is anything other than + * MESMRAgnImportObserver::EImpResponseContinue. The return value is stored in + * iResponse. + * @param aType The type of error which occurred + * @param aUid The Uid of the entry in which it occurred, if known, or KNullDesC + * if not. + * @param aContext A textual indication of where the error occurred. + * @param aCanContinue If this is not ETrue (default = ETrue) then a response of + * EImpResponseContinue will be handled as an EImpResponseSkip + * @leave KErrAbort + * @internalTechnology + */ +void CESMRAgnVersit2Importer::ReportErrorL( MESMRAgnImportObserver::TImpError aType, + const TDesC8& aUid, + const TDesC& aContext, + TBool aCanContinue ) + { + FUNC_LOG; + + iResponse = iImportObserver->AgnImportErrorL(aType, aUid, aContext); + if (iResponse != MESMRAgnImportObserver::EImpResponseContinue) + { + User::Leave(KErrAbort); + } + else if (!aCanContinue) + { + iResponse = MESMRAgnImportObserver::EImpResponseSkip; + + User::Leave(KErrAbort); + } + + } + +// End of file. +