diff -r 000000000000 -r f979ecb2b13e calendarengines/agnversit2/src/AgnVersit2Importer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calendarengines/agnversit2/src/AgnVersit2Importer.cpp Tue Feb 02 10:12:19 2010 +0200 @@ -0,0 +1,1900 @@ +/* +* Copyright (c) 2002-2004 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 contains iCal importer implementation. +* Converts iCal data to symbian agenda entry. +* +*/ + + + +// Class include. +#include "AgnVersit2Importer.h" + +//debug +#include "calendarengines_debug.h" + +// 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 +#include + +// Versit includes. +#include "ICal.h" // For CICal +#include "ICalComponent.h" // For CICalComponent +#include "ICalKeyWords.h" // For CICalKeyWords +#include "ICalParser.h" // For CICalParser +#include "ICalProperty.h" // For CICalProperty +#include "ICalPropertyParam.h" // For CICalPropertyParam +#include "ICalRuleSegment.h" // For CICalRuleSegment +#include "ICalValue.h" // For CICalValue + +// AgnVersit2 includes. +#include "agnimportobserver.h" // For MAgnImportObserver +#include "AgnVersit2StringProvider.h" // For CAgnVersit2StringProvider +#include "CleanupPointerArray.h" // For TCleanupPointerArray + +#include "AgnRRuleImporter.h" // For CAgnRRuleImporter +// Constants. + + +/** A semi-colon character. */ +const TUint KAgnVersitTokenSemiColonVal =';'; + +// +// CTzNamedRules +// ============= +// + +/** +Constructor. +@internalTechnology +*/ +CAgnVersit2Importer::CTzNamedRules::CTzNamedRules() + { + TRACE_ENTRY_POINT; + TRACE_EXIT_POINT; + } + +/** +Destructor. +@internalTechnology +*/ +CAgnVersit2Importer::CTzNamedRules::~CTzNamedRules() + { + TRACE_ENTRY_POINT; + + delete iRules; + delete iName; + + TRACE_EXIT_POINT; + } + +// +// CAgnVersit2Importer +// =================== +// + +/** +Constructs a new CAgnVersit2Importer and returns it. +@param aStringProvider A string provider for obtaining resource strings from. +@return a new CAgnVersit2Importer. +@internalTechnology +*/ +CAgnVersit2Importer* CAgnVersit2Importer::NewL(CAgnVersit2StringProvider& aStringProvider) + { + TRACE_ENTRY_POINT; + + CAgnVersit2Importer* self = CAgnVersit2Importer::NewLC(aStringProvider); + CleanupStack::Pop(self); + + TRACE_EXIT_POINT; + return self; + } + +/** +Constructs a new CAgnVersit2Importer and returns it. The object is left on the +cleanup stack. +@param aStringProvider A string provider for obtaining resource strings from. +@return a new CAgnVersit2Importer. +@internalTechnology +*/ +CAgnVersit2Importer* CAgnVersit2Importer::NewLC(CAgnVersit2StringProvider& aStringProvider) + { + TRACE_ENTRY_POINT; + + CAgnVersit2Importer* self = new (ELeave) CAgnVersit2Importer(aStringProvider); + CleanupStack::PushL(self); + self->ConstructL(); + + TRACE_EXIT_POINT; + return self; + } + +/** +Destructor. +@internalTechnology +*/ +CAgnVersit2Importer::~CAgnVersit2Importer() + { + TRACE_ENTRY_POINT; + + iTzRules.ResetAndDestroy(); + + TRACE_EXIT_POINT; + } + +/** +Uses Versit2 to parse an input stream, and then converts the result into +CCalEntry objects. +@param aEntries The array to append entries to. +@param aReadStream The stream to read iCalendar data from. +@param aObserver Application interface for error reporting. +@internalTechnology +*/ +void CAgnVersit2Importer::ImportL( RPointerArray& aEntries, + RReadStream& aReadStream, + MAgnImportObserver& aObserver ) + { + TRACE_ENTRY_POINT; + + iImportObserver = &aObserver; + + CICalParser* parser = CICalParser::NewLC(); + parser->InternalizeL(aReadStream); + + const TInt count = parser->CalCount(); + + for (TInt cal = 0; cal < count; ++cal) + { + CICal& calendar = parser->Cal(cal); + TRAPD(err, ImportICalL(aEntries, calendar)); + iTzRules.ResetAndDestroy(); + User::LeaveIfError(err); + } + + CleanupStack::PopAndDestroy(parser); + TRACE_EXIT_POINT; + } + +/** +Constructor. +@internalTechnology +*/ +CAgnVersit2Importer::CAgnVersit2Importer(CAgnVersit2StringProvider& aStringProvider) + : iStringProvider(aStringProvider) + { + TRACE_ENTRY_POINT; + TRACE_EXIT_POINT; + } + +/** +Internal construction. +@internalTechnology +*/ +void CAgnVersit2Importer::ConstructL() + { + TRACE_ENTRY_POINT; + TRACE_EXIT_POINT; + } + +/** +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 CICal from which to obtain data. +@leave KErrAbort, or any other system wide error code. +@internalTechnology +*/ +void CAgnVersit2Importer::ImportICalL(RPointerArray& aEntries, CICal& aCal) + { + TRACE_ENTRY_POINT; + + 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 == MAgnImportObserver::EImpResponseContinue; ++com) + { + CICalComponent* component = components[com]; + + // We are only interested in timezones: + if (component->Type() == CICalBase::EICalTimeZone) + { + TRAPD(err, ImportTimezoneL(*component)); + if (err) + { + // If there isn't a response, then it isn't a recoverable leave: + if (iResponse == MAgnImportObserver::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 == MAgnImportObserver::EImpResponseSkip) + { + iResponse = MAgnImportObserver::EImpResponseContinue; + } + } + } + } + + // Run through the other components: + for (com = 0; com < comCount && iResponse == MAgnImportObserver::EImpResponseContinue; ++com) + { + CICalComponent* component = components[com]; + + // We aren't interested in timezones: + if (component->Type() != CICalBase::EICalTimeZone) + { + TRAPD(err, ImportComponentL(aCal, *component, aEntries)); + if (err) + { + // If there isn't a response, then it isn't a recoverable leave: + if (iResponse == MAgnImportObserver::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 == MAgnImportObserver::EImpResponseSkip) + { + iResponse = MAgnImportObserver::EImpResponseContinue; + } + } + } + } + + // If we have been asked to leave after an error, do so: + if (iResponse == MAgnImportObserver::EImpResponseLeave) + { + User::Leave(KErrAbort); + } + + TRACE_EXIT_POINT; + } + +/** +* 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 CAgnVersit2Importer::ImportComponentL( const CICal& aCal, + const CICalComponent& aComponent, + RPointerArray& aEntries ) + { + TRACE_ENTRY_POINT; + + // Get the UID: + HBufC8* entryUid = NULL; + const CICalProperty* propUid = aComponent.FindProperty(KICalUid()); + + if (!propUid)//(propUid && propUid->Values().Count() < 1) + { + // if UID is not present,generate using caleninterimutils api + CCalenInterimUtils2* interimUtils = CCalenInterimUtils2::NewL(); + CleanupStack::PushL(interimUtils); + // get the global uid + entryUid = interimUtils->GlobalUidL(); + CleanupStack::PopAndDestroy(interimUtils); + CleanupStack::PushL(entryUid); + } + else + { + entryUid = propUid->Values()[0]->BinaryLC(); + } + + if(entryUid) + { + // Import entry-type specific data: + switch (aComponent.Type()) + { + case CICalBase::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 CICalProperty* rrule = aComponent.FindProperty(KICalRRule()); + const RPointerArray& rulevalues = rrule->Values(); + + if (rulevalues.Count() >= 1) + { + CICalRuleSegment::TFreq freq = CICalRuleSegment::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, CICalRuleSegment::ESegFreq); + if (pos != KErrNotFound) + { + ASSERT(recRules.Count() >= pos + 1); + freq = recRules[pos]->FreqL(); + } + + // Find the interval. + pos = FindRuleSegment(recRules, CICalRuleSegment::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 == CICalRuleSegment::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 CICalBase::EICalTodo : + CleanupStack::Pop(entryUid); + // Passing ownership of entryUid + ImportEntryL(aCal, aComponent, aEntries, entryUid, CCalEntry::ETodo); + break; + case CICalBase::EICalJournal : + ReportErrorL(MAgnImportObserver::EImpErrorNotSupported, *entryUid, KICalJournal, EFalse); + // Continue equates to skip in this instance. + break; + case CICalBase::EICalAlarm : + ReportErrorL(MAgnImportObserver::EImpErrorInvalidData, *entryUid, KICalAlarm, EFalse); + // Continue equates to skip in this instance. + break; + case CICalBase::EICalFreeBusy : + ReportErrorL(MAgnImportObserver::EImpErrorNotSupported, *entryUid, KICalFreeBusy, EFalse); + // Continue equates to skip in this instance. + break; + case CICalBase::EICalInvalid : // not valid here, fall through + case CICalBase::EICalCalendar : // not valid here, fall through + case CICalBase::EICalTimeZone : // not valid here, fall through + case CICalBase::EICalStandard : // not valid here, fall through + case CICalBase::EICalDaylight : // not valid here, fall through + default : + ReportErrorL( MAgnImportObserver::EImpErrorInvalidData, + *entryUid, + aComponent.TypeStringL(), + EFalse ); + // Continue equates to skip in this instance. + break; + } + } + TRACE_EXIT_POINT; + } + +/** +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 MAgnImportObserver::TImpResponse indicating application response to errors, if any. +@internalTechnology +*/ +void CAgnVersit2Importer::ImportAlarmL( const CICalComponent& aComponent, + CCalEntry& aEntry, + const HBufC8& aUid, + const TCalTime* aStartTime ) + { + TRACE_ENTRY_POINT; + + if (aComponent.ComponentExists(CICalBase::EICalAlarm)) + { + // Check the outer component is a VEVENT or a VTODO + if (aComponent.Type() == CICalBase::EICalEvent || aComponent.Type() == CICalBase::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() == CICalBase::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 CICalPropertyParam* valueType = nestedProperties[j]->FindParam(KICalValue); + TTimeIntervalMinutes duration; + if (valueType) // Only DATE-TIME is legal here - assume it's that one. + { + if (!aStartTime) + { + ReportErrorL( MAgnImportObserver::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( MAgnImportObserver::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( MAgnImportObserver::EImpErrorMissingData, + aUid, + iStringProvider.StringL(EICalErrValarmNoTrigger), + EFalse ); + // On continue, the alarm will be skipped. + } + CleanupStack::PopAndDestroy(alarm); + } + } + } + else + { + ReportErrorL( MAgnImportObserver::EImpErrorInvalidData, + aUid, + iStringProvider.StringL(EICalErrValarmNotAllowed), + EFalse ); + // On continue, the alarm will be skipped. + } + } + + TRACE_EXIT_POINT; + } + + +/** +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 MAgnImportObserver::TImpResponse indicating application response to errors, if any. +@internalTechnology +*/ +void CAgnVersit2Importer::ImportEntryL( const CICal& aCal, + const CICalComponent& aComponent, + RPointerArray& aEntries, + HBufC8* aUid, + CCalEntry::TType aType ) + { + TRACE_ENTRY_POINT; + + // 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: + CICalProperty* rruleproperty = NULL; + CICalProperty* startproperty = NULL; + CICalProperty* endproperty = NULL; + CICalProperty* 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) + { + CICalProperty* 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(MAgnImportObserver::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 == MAgnImportObserver::EImpResponseSkip) + { + iResponse = MAgnImportObserver::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(MAgnImportObserver::EImpErrorNotSupported, *aUid, KICalExdate); + } + } + else + { + // Cannot process an RRULE without a DTSTART + ReportErrorL(MAgnImportObserver::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); + + TRACE_EXIT_POINT; + } + +/** +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 * CAgnVersit2Importer::CreateEntryLC( const CICal& aCal, + const CICalComponent& aComponent, + HBufC8* aUid, + CCalEntry::TType aType ) + { + TRACE_ENTRY_POINT; + + CleanupStack::PushL(aUid); + + const CICalProperty* 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 CICalProperty* 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(MAgnImportObserver::EImpErrorInvalidData, *aUid, val); + // On continue, use EMethodNone. + } + } + + CCalEntry* entry = NULL; + // Find the recurrence information, if there is any: + const CICalProperty* propRecurrenceId = aComponent.FindProperty(KICalRecurrenceId); + if (propRecurrenceId) + { + TCalTime entryRecId; + GetCalTimeL(*propRecurrenceId, entryRecId, EDateTime); + + const CICalPropertyParam* 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(MAgnImportObserver::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); + } + + TRACE_EXIT_POINT; + 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 CAgnVersit2Importer::ImportPropertyL(CCalEntry& aEntry, const CICalProperty& aProperty) + { + TRACE_ENTRY_POINT; + + if (aProperty.Type().CompareF(KICalAttendee) == 0) + { + // Find the paramaters we need: + CICalValue* address = aProperty.Values()[0]; + const CICalPropertyParam* sentBy = aProperty.FindParam(KICalSentBy); + const CICalPropertyParam* role = aProperty.FindParam(KICalRole); + const CICalPropertyParam* status = aProperty.FindParam(KICalPartStat); + const CICalPropertyParam* rsvp = aProperty.FindParam(KICalRsvp); + const CICalPropertyParam* cn = aProperty.FindParam(KICalCn); + // The role: + CCalAttendee::TCalRole roleVal = CCalAttendee::EReqParticipant; + if (role && role->Values().Count() > 0) + { + CICalValue* 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) + { + CICalValue* 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 + { + attendee = CCalAttendee::NewL(address->TextL()); + } + 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(MAgnImportObserver::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(MAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalOrganizer); + // On continue ignore the organizer. + } + else + { + CICalValue* address = aProperty.Values()[0]; + const CICalPropertyParam* sentBy = aProperty.FindParam(KICalSentBy); + const CICalPropertyParam* cn = aProperty.FindParam(KICalCn); + CCalUser* user = NULL; + if (sentBy && sentBy->Values().Count() > 0) + { + user = CCalAttendee::NewL(address->TextL(), sentBy->Values()[0]->TextL()); + } + else + { + user = CCalAttendee::NewL(address->TextL()); + } + 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(MAgnImportObserver::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(MAgnImportObserver::EImpErrorNotSupported, aEntry.UidL(), statstr); + // On continue ignore the status. + } + } + } + else if (aProperty.Type().CompareF(KICalSummary) == 0) + { + if (aProperty.Values().Count() < 1) + { + ReportErrorL(MAgnImportObserver::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(MAgnImportObserver::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 + 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 if (aProperty.Type().Find(KICalGeo) == 0) + { + //Extract Geo co-ordinates latitude and longitude values from property + + TInt valCount = aProperty.Values().Count(); + if(valCount > 0 && valCount < 2) + { + HBufC* geoBuffLatLong (aProperty.Values()[0]->TextL().AllocLC()); + // Determine the position of the delimiter for extraction of the geo latitude and longitude values + TInt delimiterPos = geoBuffLatLong->Locate(KAgnVersitTokenSemiColonVal); + + if(delimiterPos!=KErrNotFound) + { + // Extract the latitude + TLex geoLatitudeLex(geoBuffLatLong->Left(delimiterPos)); + // Extract the longitude by excluding the delimiter + TLex geoLongitudeLex(geoBuffLatLong->Right(geoBuffLatLong->Length()-(delimiterPos+1))); + + TReal geoLatitude; + TReal geoLongitude; + + if((geoLatitudeLex.Val(geoLatitude)==KErrNone) && (geoLongitudeLex.Val(geoLongitude)==KErrNone)) + { + CCalGeoValue* importedGeoValue=CCalGeoValue::NewL(); + CleanupStack::PushL(importedGeoValue); + TRAPD(err, importedGeoValue->SetLatLongL(geoLatitude,geoLongitude)); + if(err==KErrNone) + { + aEntry.SetGeoValueL(*importedGeoValue); + } + CleanupStack::PopAndDestroy(importedGeoValue); + } + } + CleanupStack::PopAndDestroy(geoBuffLatLong); + } + } + else + { + ReportErrorL(MAgnImportObserver::EImpErrorNotSupported, aEntry.UidL(), aProperty.Type()); + } + + TRACE_EXIT_POINT; + } + +/** +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 MAgnImportObserver::TImpResponse indicating application response to errors, if any. +@internalTechnology +*/ +void CAgnVersit2Importer::ImportTimezoneL(const CICalComponent& aTimezone) + { + TRACE_ENTRY_POINT; + + CTzRules* rules = NULL; + // Get the TZID: + TPtrC tzid; + const CICalProperty* propUid = aTimezone.FindProperty(KICalTzid()); + if (propUid && propUid->Values().Count() > 0) + { + tzid.Set(propUid->Values()[0]->TextL()); + } + else + { + ReportErrorL(MAgnImportObserver::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(MAgnImportObserver::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: + CICalComponent* 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); + + TRACE_EXIT_POINT; + } + +/** +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 CAgnVersit2Importer::ImportTimezoneIntervalL( const CICalComponent& aInterval, + CTzRules& aRules ) + { + TRACE_ENTRY_POINT; + + // Find the properties: + const CICalProperty* from = aInterval.FindProperty(KICalTzoffsetfrom); + const CICalProperty* to = aInterval.FindProperty(KICalTzoffsetto); + const CICalProperty* start = aInterval.FindProperty(KICalDtstart); + const CICalProperty* rdate = aInterval.FindProperty(KICalRdate); + const CICalProperty* 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(MAgnImportObserver::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; + CICalValue::TTimeZoneType type; + start->Values()[0]->GetDateTimeL(startTime, type); + dateTime = startTime.DateTime(); + aRules.SetStartYear(dateTime.Year()); + tzrule.iFrom = dateTime; + // 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(MAgnImportObserver::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(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, + iStringProvider.StringL(EICalErrRruleNumRules)); + // On continue ignore all but the first RRule. + } + CICalRuleSegment::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, CICalRuleSegment::ESegFreq); + if (pos != KErrNotFound) + { + ASSERT(recRules.Count() >= pos + 1); + freq = recRules[pos]->FreqL(); + if (freq != CICalRuleSegment::EFreqYearly) + { + ReportErrorL(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, + iStringProvider.StringL(EICalErrRruleNotYearly)); + // On continue assume yearly. + freq = CICalRuleSegment::EFreqYearly; + } + } + else + { + ReportErrorL(MAgnImportObserver::EImpErrorMissingData, KNullDesC8, + rrule->Values()[0]->TextL()); + // On continue assume yearly. + freq = CICalRuleSegment::EFreqYearly; + } + + // Find the interval. We can only handle an interval of 1. + TInt thePos = FindRuleSegment(recRules, CICalRuleSegment::ESegInterval); + if (thePos != KErrNotFound) + { + ASSERT(recRules.Count() >= thePos + 1); + ASSERT(recRules[thePos]->Values().Count() >= 0); + if (recRules[thePos]->Values()[0]->IntegerL() != 1) + { + ReportErrorL(MAgnImportObserver::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, CICalRuleSegment::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, CICalRuleSegment::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, CICalRuleSegment::ESegUntil); + if (thePos != KErrNotFound) + { + ReportErrorL(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalWkSt); + // On continue ignore the WKST. + } + + // BySecond, ByMinute, ByHour, ByYearDay, ByWeekNo are not + // supported by the agenda timezones. + thePos = FindRuleSegment(recRules, CICalRuleSegment::ESegBySecond); + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CICalRuleSegment::ESegByMinute); + } + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CICalRuleSegment::ESegByHour); + } + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CICalRuleSegment::ESegByYearDay); + } + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CICalRuleSegment::ESegByWeekNo); + } + if (thePos != KErrNotFound) + { + ReportErrorL(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalRRule); + // On continue ignore the unsupported field. + } + + // ByMonth + TBool haveMonth = EFalse; + thePos = FindRuleSegment(recRules, CICalRuleSegment::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(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalByMonth); + // On continue ignore the additional months. + } + } + } + + if (!haveMonth) + { + tzrule.iMonth = dateTime.Month(); + } + + // ByDay + TBool haveDay = EFalse; + TInt daypos; + thePos = FindRuleSegment(recRules, CICalRuleSegment::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(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalByDay); + // On continue ignore extra days. + } + } + } + + if (haveDay) + { + if (daypos == 0) + { + ReportErrorL(MAgnImportObserver::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(MAgnImportObserver::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, CICalRuleSegment::ESegByMonthDay); + if (thePos == KErrNotFound) + { + thePos = FindRuleSegment(recRules, CICalRuleSegment::ESegByPos); + } + if (thePos != KErrNotFound) + { + ReportErrorL(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalRRule); + // On continue ignore both fields. + } + + tzrule.iTimeOfChange = dateTime.Hour() * KMinutesInOneHour + dateTime.Minute(); + tzrule.iTo = TCalTime::MaxTime(); + 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); + + TRACE_EXIT_POINT; + } + +/** +* 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 CAgnVersit2Importer::ImportCategoriesL( const CICalProperty& aProperty, + CCalEntry& aEntry ) const + { + TRACE_ENTRY_POINT; + + 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); + } + TRACE_EXIT_POINT; + } + +/** +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 CAgnVersit2Importer::ImportClassL(const CICalProperty& aProperty, CCalEntry& aEntry) + { + TRACE_ENTRY_POINT; + + 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(MAgnImportObserver::EImpErrorNotSupported, aEntry.UidL(), name); + // On continue ignore the class. + } + } + else + { + ReportErrorL(MAgnImportObserver::EImpErrorMissingData, aEntry.UidL(), KICalRRule); + // On continue ignore the class. + } + TRACE_EXIT_POINT; + } + +/** +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 CAgnVersit2Importer::ImportRRuleL( const CICalProperty& aProperty, + CCalEntry& aEntry, + const TCalTime& aStartTime ) + { + TRACE_ENTRY_POINT; + + //Create a new recurrence rule importer + CAgnRRuleImporter *importer = CAgnRRuleImporter::NewLC( *this ); + + //import the recurrence rule + TBool importRRule = importer->ImportL( aProperty, aEntry, aStartTime ); + + if( importRRule ) + { + iNeedsTzRules = ETrue; + } + + CleanupStack::PopAndDestroy( importer ); + + TRACE_EXIT_POINT; + + //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 CAgnVersit2Importer::GetCalTimeL( const CICalProperty& aProperty, + TCalTime& aTime, + TValueType aValueType, + TInt aValue ) + { + TRACE_ENTRY_POINT; + + // Find the property value, value parameter and tzid parameter: + if (aProperty.Values().Count() < aValue) + { + ReportErrorL(MAgnImportObserver::EImpErrorMissingData, KNullDesC8, aProperty.Type(), EFalse); + // We cannot continue - we interpret continue as skip. + } + + const CICalPropertyParam* valuetype = aProperty.FindParam(KICalValue); + if (valuetype) + { + if (valuetype->Values().Count() < 1) + { + ReportErrorL(MAgnImportObserver::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(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, vt, EFalse); + // We can't continue - skip instead. + } + } + } + + TTime dateAndTime; + CICalValue::TTimeZoneType tzType = CICalValue::EUtcTime; + + CICalValue* 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(MAgnImportObserver::EImpErrorNotSupported, KNullDesC8, KICalPeriod, EFalse); + // We can't continue - skip instead. + break; + } + } + + const CICalPropertyParam* tzid = aProperty.FindParam(KICalTzid); + + // Handle timezone information: + if (tzType == CICalValue::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(MAgnImportObserver::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); + } + TRACE_EXIT_POINT; + } + +/** +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 CICalRuleSegment::TSegmentType. +@see CICalRuleSegment::TSegmentType +@return The index of the rule within the array, or KErrNotFound +@internalTechnology +*/ +TInt CAgnVersit2Importer::FindRuleSegment( const RPointerArray& aRules, + CICalRuleSegment::TSegmentType aType ) const + { + TRACE_ENTRY_POINT; + + for (TInt r = aRules.Count() - 1; r > -1; --r) + { + if (aRules[r]->Type() == aType) + { + TRACE_EXIT_POINT; + return r; + } + } + TRACE_EXIT_POINT; + 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* CAgnVersit2Importer::FindTzRule(const TDesC& aName) const + { + TRACE_ENTRY_POINT; + + TInt tzCount = iTzRules.Count(); + for (TInt r = 0; r < tzCount; ++r) + { + if (iTzRules[r]->iName->CompareF(aName) == 0) + { + TRACE_EXIT_POINT; + return iTzRules[r]->iRules; + } + } + + TRACE_EXIT_POINT; + return NULL; + } + +/** +Reports an error through the MAgnImportObserver interface, and then leaves if +the return value is anything other than +MAgnImportObserver::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 CAgnVersit2Importer::ReportErrorL( MAgnImportObserver::TImpError aType, + const TDesC8& aUid, + const TDesC& aContext, + TBool aCanContinue ) + { + TRACE_ENTRY_POINT; + + iResponse = iImportObserver->AgnImportErrorL(aType, aUid, aContext); + if (iResponse != MAgnImportObserver::EImpResponseContinue) + { + TRACE_EXIT_POINT; + User::Leave(KErrAbort); + } + else if (!aCanContinue) + { + iResponse = MAgnImportObserver::EImpResponseSkip; + + TRACE_EXIT_POINT; + User::Leave(KErrAbort); + } + + TRACE_EXIT_POINT; + } + +// End of file.