calendarengines/agnversit2/src/AgnVersit2Importer.cpp
changeset 0 f979ecb2b13e
child 29 12af337248b1
--- /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 <calalarm.h>			// For CCalAlarm
+#include <calcategory.h>		// For CCalCategory
+#include <calentry.h>			// For CCalEntry
+#include <calrrule.h>			// For CCalRRule
+#include <caltime.h>			// For CCalTime
+#include <caluser.h>			// For CCalUser
+#include <tzdefines.h>			// For TTzRuleDay
+#include <vtzrules.h>			// For CTzRules and TTzRule
+#include <CalenInterimUtils2.h>
+
+// 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<CCalEntry>& 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<CCalEntry>& aEntries, CICal& aCal)
+	{
+	TRACE_ENTRY_POINT;
+	
+	const RPointerArray<CICalComponent>& 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<CCalEntry>& 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<CICalValue>& rulevalues = rrule->Values();
+	
+						if (rulevalues.Count() >= 1)
+							{
+							CICalRuleSegment::TFreq freq = CICalRuleSegment::EFreqDaily;
+							RPointerArray<CICalRuleSegment> 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<CICalComponent>& 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<CICalProperty>& 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<CICalValue>& 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<CCalEntry>& 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<CICalProperty> exDateProperties;
+	CleanupClosePushL(exDateProperties);
+	RPointerArray<CICalProperty> rDateProperties;
+	CleanupClosePushL(rDateProperties);
+
+	const RPointerArray<CICalProperty>& 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<TCalTime> 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<TCalTime> 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<CICalComponent>& 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<CICalRuleSegment> 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<CICalRuleSegment>& 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.