calendarengines/agnversit2/src/AgnVersit2Exporter.cpp
changeset 0 f979ecb2b13e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/calendarengines/agnversit2/src/AgnVersit2Exporter.cpp	Tue Feb 02 10:12:19 2010 +0200
@@ -0,0 +1,1987 @@
+/*
+* 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 exporter implementation.
+*                Converts symbian agenda entries to iCal data.
+*
+*/
+
+
+
+// Class include.
+#include "AgnVersit2Exporter.h"
+
+//debug
+#include "calendarengines_debug.h"
+
+// Agenda includes.
+#include <calalarm.h>					// CCalAlarm
+#include <calcategory.h>				// CCalCategory
+#include <calentry.h>					// CCalEntry
+#include <calrrule.h>					// TCalRRule
+#include <caluser.h>					// CCalUser
+#include <vtzrules.h>					// CVTzActualisedRules
+
+// Versit includes.
+#include "ICal.h"						// CICal
+#include "ICalComponent.h"				// CICalComponent
+#include "ICalKeyWords.h"				// Literals used during export
+#include "ICalProperty.h"				// CICalProperty
+#include "ICalValue.h"					// CICalValue
+#include "ICalBase.h"					// TICalMethod
+
+// AgnVersit2 includes.
+#include "AgnVersit2StringProvider.h"	// CAgnVersit2StringProvider
+#include "AgnExternalInterface.h"		// TAgnEntryExport
+
+// Constants for timezone RRULE
+_LIT(KRRuleFreqYearly, "FREQ=YEARLY;BYMONTH=");
+_LIT(KRRuleByMonthDay, ";BYMONTHDAY=");
+_LIT(KRRuleByDayPlus, ";BYDAY=+");
+_LIT(KRRuleByDayPlusOne, ";BYDAY=+1");
+_LIT(KRRuleByDayMinusOne, ";BYDAY=-1");
+
+//iCalendar version number
+_LIT(KICalVersionNumber,"2.0");
+
+
+// Maximum characters in a GEO value should be 11 = -NNN.NNNNNN
+const TUint KGEOMaxWidthOfGeoValue = 11;
+
+/** A semi-colon character. */
+const TUint KAgnVersitTokenSemiColonVal =';';
+
+// Constant integer values
+const TInt KMaxOffsetMinutes = 720; //12 hours * 60 minutes
+const TInt KMinOffsetMinutes = -720; //-12 hours * 60 minutes
+
+const TInt KSecondsInOneMinute = 60;
+const TInt KInitialRRuleLength = 150; //RRule line length
+const TInt KMaxBufLength = 80;	//Max time zone name length
+
+/**
+Constructs a new CAgnVersit2Exporter and returns it.
+@param aObserver Application interface for error reporting.
+@param aWriteStream Stream to write output to.
+@param aStringProvider localisable string resources
+@return The new CAgnVersit2Exporter
+@internalTechnology
+*/
+CAgnVersit2Exporter* CAgnVersit2Exporter::NewL(CAgnVersit2StringProvider& aStringProvider)
+	{
+	TRACE_ENTRY_POINT;
+		
+	CAgnVersit2Exporter* self = CAgnVersit2Exporter::NewLC(aStringProvider);
+	CleanupStack::Pop(self);
+	
+	TRACE_EXIT_POINT;
+	return self;
+	}
+
+/**
+Constructs a new CAgnVersit2Exporter and returns it.
+A pointer to the new object is left on the cleanup stack.
+@param aObserver Application interface for error reporting.
+@param aWriteStream Stream to write output to.
+@param aStringProvider localisable string resources
+@return The new CAgnVersit2Exporter
+@internalTechnology
+*/
+CAgnVersit2Exporter* CAgnVersit2Exporter::NewLC(CAgnVersit2StringProvider& aStringProvider)
+	{
+	TRACE_ENTRY_POINT;
+	
+	CAgnVersit2Exporter* self = new (ELeave) CAgnVersit2Exporter(aStringProvider);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	
+	TRACE_EXIT_POINT;
+	return self;
+	}
+
+/**
+Destructor.
+@internalTechnology
+*/
+CAgnVersit2Exporter::~CAgnVersit2Exporter()
+	{
+	TRACE_ENTRY_POINT;
+	
+	iTzNameArray.ResetAndDestroy();
+	iTimeZoneArray.ResetAndDestroy();
+	
+	TRACE_EXIT_POINT;
+	}
+/**
+Exports an array of calendar entries to a single iCalendar
+@param aEntries array of entries to export.
+@param aFlags not used.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::ExportL( RPointerArray<CCalEntry>& aEntries,
+                                   RWriteStream& aWriteStream,
+                                   TUint aExportFlags,
+                                   MAgnExportObserver& aObserver )
+	{
+	TRACE_ENTRY_POINT;
+	
+	//We are exporting more than one entry to a single iCalendar. 
+	//Clear the time zone arrays, otherwise if this function is called twice
+	//components in this iCalendar may reference timezone components in a preceding
+	//iCalendar.
+	iTzNameArray.ResetAndDestroy();
+	iTimeZoneArray.ResetAndDestroy();
+		
+	iExportProperties = aExportFlags;
+	iObserver = &aObserver;
+	ASSERT( aEntries.Count() > 0 );
+	CICal* cal = AddICalLC(*aEntries[0]);
+	
+	TInt entryCount = aEntries.Count();
+	
+	TRAPD(err,
+	
+		for (TInt x = 0; x < entryCount; ++x)
+			{
+			if (iExportProperties & KAgnExportTzRules)
+				{
+				AddTimezoneL(*cal, *aEntries[x]);
+				}
+			AddComponentL(*cal, *aEntries[x]);
+			}
+		); //End TRAP
+	
+	User::LeaveIfError(err);
+
+	cal->ExternalizeL(aWriteStream);
+	CleanupStack::PopAndDestroy(cal);
+	
+	TRACE_EXIT_POINT;
+	}
+	
+/**
+Exports a given calendar entry.
+@param aEntry entry to export.
+@param aFlags not used.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::ExportL( const CCalEntry& aEntry,
+                                   RWriteStream& aWriteStream,
+                                   TUint aExportFlags,
+                                   MAgnExportObserver& aObserver )
+	{
+	TRACE_ENTRY_POINT;
+	
+	//If we are exporting a single iCalendar per entry, we want to include the timezone
+	//even if we have previously encountered it.  We do this by clearing the time zone 
+	//name array
+	iTzNameArray.ResetAndDestroy();
+	iTimeZoneArray.ResetAndDestroy();
+			
+	iExportProperties = aExportFlags;
+	iObserver = &aObserver;
+	CICal* cal = AddICalLC(aEntry);
+
+	TRAPD(err,
+		if (iExportProperties & KAgnExportTzRules)
+			{
+			AddTimezoneL(*cal, aEntry);
+			}
+			AddComponentL(*cal, aEntry);
+		);
+
+
+	User::LeaveIfError(err);
+
+	cal->ExternalizeL(aWriteStream);
+	CleanupStack::PopAndDestroy(cal);
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Constructor.
+@param aObserver Application interface for error reporting.
+@param aWriteStream Stream to write output to.
+@param aStringProvider localisable string resources
+@internalTechnology
+*/
+CAgnVersit2Exporter::CAgnVersit2Exporter(CAgnVersit2StringProvider& aStringProvider) :
+	iStringProvider(aStringProvider)
+	{
+	TRACE_ENTRY_POINT;
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Internal construction.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::ConstructL()
+	{
+	TRACE_ENTRY_POINT;
+	iTimezoneIndex = KErrNotFound;
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Creates a new CICal*, adds any properties and returns it. The new object is
+left on the cleanup stack.
+@code
+ICalendar components can have the following properties:
+	PRODID: This is fixed for all exports from this device.
+	VERSION: Should	only ever be 2.0 for iCalendar v1 (vCalendar 2).
+	METHOD: Member data from aEntry.
+@endcode
+@param aEntry entry to export.
+@return newly created CICal*.
+@internalTechnology
+*/
+CICal* CAgnVersit2Exporter::AddICalLC(const CCalEntry& aEntry) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	CICal* cal = CICal::NewLC();
+	cal->AddPropertyL(KICalProdId, iStringProvider.StringL(EICalProdIdValue));
+	cal->AddPropertyL(KICalVersion,KICalVersionNumber);
+
+	switch (aEntry.MethodL())
+		{
+		case CCalEntry::EMethodPublish :
+			cal->AddPropertyL(KICalMethod, KICalPublish);
+			cal->SetMethodL(CICal::EMethodPublish);
+			break;
+		case CCalEntry::EMethodRequest :
+			cal->AddPropertyL(KICalMethod, KICalRequest);
+			cal->SetMethodL(CICal::EMethodRequest);
+			break;
+		case CCalEntry::EMethodReply :
+			cal->AddPropertyL(KICalMethod, KICalReply);
+			cal->SetMethodL(CICal::EMethodReply);
+			break;
+		case CCalEntry::EMethodAdd :
+			cal->AddPropertyL(KICalMethod, KICalAdd);
+			cal->SetMethodL(CICal::EMethodAdd);
+			break;
+		case CCalEntry::EMethodCancel :
+			cal->AddPropertyL(KICalMethod, KICalCancel);
+			cal->SetMethodL(CICal::EMethodCancel);
+			break;
+		case CCalEntry::EMethodRefresh :
+			cal->AddPropertyL(KICalMethod, KICalRefresh);
+			cal->SetMethodL(CICal::EMethodRefresh);
+			break;
+		case CCalEntry::EMethodCounter :
+			cal->AddPropertyL(KICalMethod, KICalCounter);
+			cal->SetMethodL(CICal::EMethodCounter);
+			break;
+		case CCalEntry::EMethodDeclineCounter :
+			cal->AddPropertyL(KICalMethod, KICalDeclineCounter);
+			cal->SetMethodL(CICal::EMethodDeclineCounter);
+			break;
+		case CCalEntry::EMethodNone :
+			// fall through...
+		default :
+			//Add MEHTOD:PUBLISH as default if no method exists
+			//needed by outlook
+			cal->AddPropertyL(KICalMethod, KICalPublish);
+			cal->SetMethodL(CICal::EMethodPublish);
+			break;
+		}
+	
+	TRACE_EXIT_POINT;
+	return cal;
+	}
+
+
+//
+//     ADD COMPONENT METHODS
+//     =====================
+//
+
+/**
+Exports an agenda Anniversary to an iCalendar component.
+@param aAnniv component to add to.
+@param aEntry Entry containing information about the anniversary.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddAnnivL(CICalComponent& aAnniv, const CCalEntry& /*aEntry*/) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	//Add 'X-PARAM' identifier
+	if (iExportProperties & KAgnExportXProp)
+		{
+		aAnniv.AddPropertyL( iStringProvider.StringL(EICalXParamType),
+		                     iStringProvider.StringL(EICalXParamAnniversary) );
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+
+/**
+Adds an alarm to a component.
+@code
+
+RFC 2445 states that an alarm MUST have an ACTION and TRIGGER property. We can
+retrieve the trigger property value from the alarm offset, but Agenda does not
+support the ACTION property.
+
+An alarm is defined as:
+
+	"BEGIN" ":" "VALARM" CRLF
+	(audioprop / dispprop / emailprop / procprop)
+	"END" ":" "VALARM" CRLF
+
+where audioprop / dispprop / emailprop and procprop are all values of the
+ACTION property.
+
+An ACTION property value of AUDIO only requires an additional TRIGGER property,
+all other properties are optional.  To comply with RFC 2445, all alarms will be
+exported with the following property:
+
+	ACTION:AUDIO
+
+as to set ACTION to any other value would require more properties, which we do
+not have values for.
+
+@endcode
+@param aComponent Component to add the VALARM component to
+@param aEntry Entry to extract ALARM information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddAlarmL(CICalComponent& aComponent, const CCalEntry& aEntry) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	CCalAlarm* alarm = aEntry.AlarmL();
+	if (alarm)
+		{
+		CleanupStack::PushL(alarm);
+		CICalComponent& component = aComponent.AddComponentL(CICalBase::EICalAlarm);
+		CICalValue* triggerVal = CICalValue::NewLC();
+		triggerVal->SetDurationL(TTimeIntervalSeconds(-alarm->TimeOffset().Int() * KSecondsInOneMinute));
+		CleanupStack::Pop(triggerVal);
+		component.AddPropertyL(KICalTrigger, triggerVal); //Pass ownership
+		component.AddPropertyL(KICalAction, KICalAudio);
+		CleanupStack::PopAndDestroy(alarm);
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds an appointment to a component.
+@param aAppt Component to add the appointment to.
+@param aEntry Entry to extract the appointment information from.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddApptL(CICalComponent& aAppt, const CCalEntry& aEntry) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	//Add 'DTEND' property
+	if (iExportProperties & KAgnExportDtEnd)
+		{
+		TCalTime time(aEntry.EndTimeL());
+		if (time.TimeUtcL() != Time::NullTTime())
+			{
+			AddDateTimePropertyL(aAppt, time, KICalDtend);
+			}
+		}
+		
+	if (iExportProperties & KAgnExportStatus)
+		{
+		//Add 'STATUS' property
+		switch (aEntry.StatusL())
+			{
+			//VEvent supports TENTATIVE, CONFIRMED, CANCELLED
+			case CCalEntry::ETentative:
+				aAppt.AddPropertyL(KICalStatus, KICalTentative);
+				break;
+			case CCalEntry::EConfirmed:
+				aAppt.AddPropertyL(KICalStatus, KICalConfirmed);
+				break;
+			case CCalEntry::ECancelled:
+				aAppt.AddPropertyL(KICalStatus, KICalCancelled);
+				break;
+			case CCalEntry::ETodoNeedsAction :	// not supported, fall through
+			case CCalEntry::ETodoCompleted :	// not supported, fall through
+			case CCalEntry::ETodoInProcess :	// not supported, fall through
+			default:
+				//Do not add a status property
+				break;
+			}
+		}
+			
+	//Add 'X-PARAM' identifier
+	if (iExportProperties & KAgnExportXProp)
+		{
+		aAppt.AddPropertyL( iStringProvider.StringL(EICalXParamType),
+		                    iStringProvider.StringL(EICalXParamAppointment) );
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds a component to aICal. The type of component added is determined by the
+value of aEntry.EntryTypeL(). Generic properties (valid in both VEVENT and
+VTODO components) are added first, followed by type specific properties.
+@param aICal CICal object to add the component to.
+@param aEntry CCalEntry containing information about the component.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddComponentL(CICal& aICal, const CCalEntry& aEntry) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	//Add the  component
+	CICalBase::TICalComponentType type(CICalBase::EICalEvent);
+	switch (aEntry.EntryTypeL())
+		{
+		//The agenda types all map to an iCalendar 'VEVENT' component
+		case CCalEntry::EAppt :
+			// fall through...
+		case CCalEntry::EEvent :
+			// fall through...
+		case CCalEntry::EReminder :
+			// fall through...
+		case CCalEntry::EAnniv :
+			{
+			type = CICalBase::EICalEvent;
+			break;
+			}
+		case CCalEntry::ETodo :
+			{
+			type = CICalBase::EICalTodo;
+			break;
+			}
+		default :
+			User::Leave(KErrNotSupported); //For now at least
+			break;
+		}
+	CICalComponent& comp = aICal.AddComponentL(type);
+
+	//Add 'ATTENDEE' properties
+	if (iExportProperties & KAgnExportAttendee)
+		{
+		AddAttendeePropertiesL(comp, aEntry);
+		}
+
+	//Add 'ORGANIZER' property
+	if (iExportProperties & KAgnExportOrganizer)
+		{
+		AddOrganizerPropertyL(comp, aEntry);
+		}
+
+	//Add 'LOCATION' property.
+	if (iExportProperties & KAgnExportLocation)
+		{
+		AddTextPropertyL(comp, aEntry.LocationL(), KICalLocation);
+		}
+
+	//Outlook puts TRANSP here. The default value is 'OPAQUE'.
+
+	//Add 'SEQUENCE' property.  The default value is '0', so
+	//the property is only added if there is a value.
+	if (iExportProperties & KAgnExportSequence)
+		{
+		TInt sequence = aEntry.SequenceNumberL();
+		if (sequence != 0)
+			{
+			AddIntegerPropertyL(comp, aEntry.SequenceNumberL(), KICalSequence);
+			}
+		}
+
+	//Add 'UID' property
+	if (iExportProperties & KAgnExportUID)
+		{
+		CICalValue* uidVal = CICalValue::NewLC();
+		uidVal->SetBinaryL(aEntry.UidL());
+		CleanupStack::Pop(uidVal);
+		comp.AddPropertyL(KICalUid, uidVal); //Pass ownership
+		}
+
+    //Time for DTSTAMP, CREATED and LAST-MODIFIED
+	TCalTime time;
+	
+	//Check if DTSTAMP exist
+	if( aEntry.DTStampL().TimeUtcL() != Time::NullTTime() )
+	    {
+	    time = aEntry.DTStampL();    
+	    }
+	else //If not use current time
+	    {
+	    TTime currentTime;
+	    currentTime.UniversalTime();
+	    time.SetTimeUtcL( currentTime );
+	    }
+	
+	//Add 'DTSTAMP' property
+	if (iExportProperties & KAgnExportDtStamp)
+		{
+		AddUtcDateTimePropertyL(comp, time.TimeUtcL(), KICalDtstamp);
+		}
+
+	//Add 'CATEGORIES' properties
+	if (iExportProperties & KAgnExportCategory)
+		{
+		AddCategoriesPropertyL(comp, aEntry);
+		}
+
+	//Add 'DESCRIPTION' property.
+	if (iExportProperties & KAgnExportDescription)
+		{
+		AddTextPropertyL(comp, aEntry.DescriptionL(), KICalDescription);
+		}
+
+	//Add 'SUMMARY' property
+	if (iExportProperties & KAgnExportSummary)
+		{
+		AddTextPropertyL(comp, aEntry.SummaryL(), KICalSummary);
+		}
+
+
+	//Add 'PRIORITY' property
+	if (iExportProperties & KAgnExportPriority)
+		{
+		TInt priority = aEntry.PriorityL();
+		
+		//Pirorites are saved always in vcal format
+		//so they are mapped the following way
+		//vCal 1 = iCal 1
+		//vCal 2 = iCal 5
+		//vCal 3 = iCal 9
+		if (priority != 0)	// Zero priority is default
+			{
+			if( priority == 1 )
+			    {
+			    }
+			
+			else if( priority == 2 )
+			    {
+			    priority = 5;
+			    }
+			
+			else
+			    {
+			    priority = 9;
+			    }
+			
+			AddIntegerPropertyL(comp, priority, KICalPriority);
+			}
+		}
+
+	//Add 'CLASS' property
+	if (iExportProperties & KAgnExportClass)
+		{
+		AddClassPropertyL(comp, aEntry);
+		}
+    
+    //Check if last modified date exist    
+    if( aEntry.LastModifiedDateL().TimeUtcL() != Time::NullTTime() )
+	    {
+	    //update time with it otherwise set CREATED and LAST-MODIFIED to
+	    //current time
+	    time = aEntry.LastModifiedDateL();    
+	    }
+
+	//Add 'CREATED' property with current system time.
+	//time should be set to 'now' in UTC if not already.
+	if (iExportProperties & KAgnExportCreated)
+		{
+		AddUtcDateTimePropertyL(comp, time.TimeUtcL(), KICalCreated);
+		}
+
+	//Add 'LAST-MODIFIED' property
+	if (iExportProperties & KAgnExportLastModified)
+		{
+		AddUtcDateTimePropertyL(comp, time.TimeUtcL(), KICalLastmodified);
+		}
+
+	//Add 'RECURRENCE-ID' property
+	if (iExportProperties & KAgnExportRecurrenceId)
+		{
+		time = aEntry.RecurrenceIdL();
+		if (time.TimeUtcL() != Time::NullTTime())
+			{
+			CICalProperty& recurProp = AddDateTimePropertyL(comp, time, KICalRecurrenceId);
+			switch (aEntry.RecurrenceRangeL())
+				{
+				case CalCommon::EThisAndFuture:
+					{
+					recurProp.AddPropertyParamL(KICalRange,KICalThisAndFuture);
+					}
+				break;
+				case CalCommon::EThisAndPrior:
+					{
+					recurProp.AddPropertyParamL(KICalRange,KICalThisAndPrior);
+					}
+				break;
+				default:
+				break;
+				}
+
+			}
+		}
+
+	//Add 'EXDATE' properties
+	if (iExportProperties & KAgnExportExDate)
+		{
+		AddExceptionDatePropertyL(comp, aEntry);
+		}
+
+	//Add 'RDATE' property
+	if (iExportProperties & KAgnExportRDate)
+		{
+		AddRDatePropertyL(comp, aEntry);
+		}
+
+	//Add 'RRULE' property
+	if (iExportProperties & KAgnExportRRule)
+		{
+		AddRRulePropertyL(comp, aEntry);
+		}
+
+	//Add 'VALARM' component
+	if (iExportProperties & KAgnExportAlarm)
+		{
+		AddAlarmL(comp, aEntry);
+		}
+
+	//Add 'DTSTART' property
+	if (iExportProperties & KAgnExportDtStart)
+		{
+		time = aEntry.StartTimeL();
+		if (time.TimeUtcL() != Time::NullTTime())
+			{
+			AddDateTimePropertyL(comp, time, KICalDtstart);
+			}
+		}
+
+	//Add type specific properties
+	switch (aEntry.EntryTypeL())
+		{
+		case CCalEntry::EAppt:
+			AddApptL(comp, aEntry);
+			break;
+		case CCalEntry::EEvent:
+			AddEventL(comp, aEntry);
+			break;
+		case CCalEntry::EReminder:
+			AddReminderL(comp, aEntry);
+			break;
+		case CCalEntry::EAnniv:
+			AddAnnivL(comp, aEntry);
+			break;
+		case CCalEntry::ETodo:
+			AddTodoL(comp, aEntry);
+			break;
+		default:
+			//Nothing more to add
+			break;
+		}
+	
+	// Add GEO
+    CCalGeoValue* geoValue = aEntry.GeoValueL();
+    
+    // Check that the GEO values are not NULL
+    if(geoValue)
+        {
+        CleanupStack::PushL(geoValue);
+        
+        TReal geoLatitude;
+        TReal geoLongitude;
+        
+        // Extract latitude and longitude values
+        // Check if it returns EFalse
+        geoValue->GetLatLong(geoLatitude,geoLongitude);
+        
+        // Convert the geo values from numbers to string
+        // Create GEO string buffer to be constructed from a latitude, delimiter and a longitude value
+        TBuf<KGEOMaxWidthOfGeoValue*2+1> geoString;
+        TBuf<KGEOMaxWidthOfGeoValue> geoLatString;
+        TBuf<KGEOMaxWidthOfGeoValue> geoLongString;
+        
+        // Maximum width of a GEO value and max number of decimal places
+        TRealFormat geoFormat(KGEOMaxWidthOfGeoValue,KCalGEOMaxDecimalPlaces);
+        
+        _LIT(KGeoStringFormat,"%S%c%S");
+        
+        // Ensure correct conversion from stored numeric values to descriptors
+        if((geoLatString.Num(geoLatitude,geoFormat)>0) && (geoLongString.Num(geoLongitude,geoFormat)>0))
+            {
+            geoString.AppendFormat(KGeoStringFormat,&geoLatString,KAgnVersitTokenSemiColonVal,&geoLongString);
+            
+            // Add the GEO property
+            AddTextPropertyL(comp,geoString,KICalGeo);
+            }
+        
+        CleanupStack::PopAndDestroy(geoValue);
+        }
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds the information stored in aEntry to aEvent
+@param aEvent CICalComponent& to add the agenda information to.
+@param aEntry CCalEntry& containing the agenda information to export.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddEventL(CICalComponent& aEvent, const CCalEntry& aEntry) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	//Add 'DTEND' property
+	if (iExportProperties & KAgnExportDtEnd)
+		{
+		TCalTime time(aEntry.EndTimeL());
+		if (time.TimeUtcL() != Time::NullTTime())
+			{
+			AddDateTimePropertyL(aEvent, time, KICalDtend);
+			}
+		}
+
+	if (iExportProperties & KAgnExportStatus)
+		{
+		//Add 'STATUS' property
+		switch (aEntry.StatusL())
+			{
+			//VEvent supports TENTATIVE, CONFIRMED, CANCELLED
+			case CCalEntry::ETentative:
+				aEvent.AddPropertyL(KICalStatus, KICalTentative);
+				break;
+			case CCalEntry::EConfirmed:
+				aEvent.AddPropertyL(KICalStatus, KICalConfirmed);
+				break;
+			case CCalEntry::ECancelled:
+				aEvent.AddPropertyL(KICalStatus, KICalCancelled);
+				break;
+			case CCalEntry::ETodoNeedsAction :	// not supported, fall through
+			case CCalEntry::ETodoCompleted :	// not supported, fall through
+			case CCalEntry::ETodoInProcess :	// not supported, fall through
+			default:
+				//Do not add a status property
+				break;
+			}
+		}
+	//Add 'X-PARAM' identifier
+	if (iExportProperties & KAgnExportXProp)
+		{
+		aEvent.AddPropertyL( iStringProvider.StringL(EICalXParamType),
+		                     iStringProvider.StringL(EICalXParamEvent) );
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds an agenda-style Reminder to the specified component.
+@param aReminder Component to add the reminder to.
+@param aEntry CCalEntry containing details of the reminder.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddReminderL(CICalComponent& aReminder, const CCalEntry& /*aEntry*/) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	//Add 'X-PARAM' identifier
+	if (iExportProperties & KAgnExportXProp)
+		{
+		aReminder.AddPropertyL( iStringProvider.StringL(EICalXParamType),
+		                        iStringProvider.StringL(EICalXParamReminder) );
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds a Timezone component to the specified CCalEntry.
+@param aICal CICal object to add the component to.
+@param aEntry CCalEntry containing information about the Timezone.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddTimezoneL(CICal& aICal, const CCalEntry& aEntry)
+	{
+	TRACE_ENTRY_POINT;
+	
+	iTimezoneIndex = KErrNotFound;
+	
+	CTzRules* tzRules = aEntry.TzRulesL();
+	CleanupStack::PushL(tzRules);
+
+	if (!tzRules || tzRules->Count() < 1)
+		{
+		CleanupStack::PopAndDestroy(tzRules);
+		return;
+		}
+
+	TInt minimumMinutes = KMaxOffsetMinutes;// TInts to store the maximum and minimum UTC offset
+	TInt maximumMinutes = KMinOffsetMinutes;// to get a human readable name for TZID
+	TInt count = tzRules->Count();
+	for (TInt i = 0; i < count; ++i)
+		{
+		TTzRule& singleRule = (*tzRules)[i];
+		if (singleRule.iNewLocalTimeOffset < minimumMinutes)
+			{
+			minimumMinutes = singleRule.iNewLocalTimeOffset;
+			}
+		if (singleRule.iNewLocalTimeOffset > maximumMinutes)
+			{
+			maximumMinutes = singleRule.iNewLocalTimeOffset;
+			}
+		if (singleRule.iOldLocalTimeOffset < minimumMinutes)
+			{
+			minimumMinutes = singleRule.iOldLocalTimeOffset;
+			}
+		if (singleRule.iOldLocalTimeOffset > maximumMinutes)
+			{
+			maximumMinutes = singleRule.iOldLocalTimeOffset;
+			}
+		}
+
+	// Now we've got the max and minimum values, store these in the TzId text field
+	// iTimeZoneName allocates a new HBufC, but either deletes it or transfers ownership
+	// to iTznamesArray
+	HBufC* timeZoneName = HBufC::NewLC(KMaxBufLength);
+	timeZoneName->Des().Copy(iStringProvider.StringL(EICalTzidUtc));
+
+	//Convert minutes to hours
+	const TInt KConversionMul = 10;
+	const TInt KConversionDiv = 6;
+	
+	// Number of digits to store time in
+	const TInt KTimeWidth = 4;
+
+	TInt minimumHours = minimumMinutes * KConversionMul / KConversionDiv; // convert to HHMM format
+	if (minimumHours < 0)
+		{
+		minimumHours =- minimumHours;
+		timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidMinus));
+		}
+	else
+		{
+		timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidPlus));
+		}
+	timeZoneName->Des().AppendNumFixedWidth(minimumHours, EDecimal, KTimeWidth);
+	timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidStandard));
+
+	//Add a slash seperator
+	timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidSlash));
+	timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidUtc));
+
+	TInt maximumHours = maximumMinutes * KConversionMul / KConversionDiv; // convert to HHMM format
+	if (maximumHours < 0)
+		{
+		maximumHours =- maximumHours;
+		timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidMinus));
+		}
+	else
+		{
+		timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidPlus));
+		}
+	timeZoneName->Des().AppendNumFixedWidth(maximumHours, EDecimal, KTimeWidth);
+	timeZoneName->Des().Append(iStringProvider.StringL(EICalTzidDaylight));
+
+	// The time zone name should now read: "UTC +HHMM (Standard) / UTC +HHMM (Daylight)".
+	
+	// Check if we have already used this timezone in this iCal.  If we have,
+	// we reuse this string but don't output the timezone again.
+	TInt tzCount = iTzNameArray.Count();
+	for (TInt x = 0; x < tzCount; ++x)
+		{
+		if ((iTzNameArray[x])->CompareC(*timeZoneName) == 0)
+			{
+			//Already used this timezone
+			iTimezoneIndex = x;
+			CleanupStack::PopAndDestroy(timeZoneName);
+			CleanupStack::PopAndDestroy(tzRules);
+			return;
+			}
+		}
+
+	//Add the 'VTIMEZONE' component
+	CICalComponent& timezone = aICal.AddComponentL(CICalBase::EICalTimeZone);
+
+	timezone.AddPropertyL(KICalTzid, *timeZoneName);
+
+
+	for (TInt ii = 0; ii < count; ++ii)
+		{
+		TTzRule& singleRule = (*tzRules)[ii];
+
+		// Set the DAYLIGHT/STANDARD field based on whether the
+		// time zone rule shifts time forwards or backwards
+		CICalComponent& tzProp = singleRule.iNewLocalTimeOffset < singleRule.iOldLocalTimeOffset ?
+			timezone.AddComponentL(CICalBase::EICalStandard) :
+			timezone.AddComponentL(CICalBase::EICalDaylight);
+
+		//  -DTSTART (Required)
+		CICalValue* dtstart = CICalValue::NewLC();
+
+
+		TTime startTime = singleRule.iFrom.iTime;
+        
+        //Check whether the year is 0, if so set it to 1970
+		if( !startTime.DateTime().Year() )//Year == 0000
+		    {
+		    TDateTime dateTime = startTime.DateTime();
+		    dateTime.SetYear( 1970 );
+		    startTime = dateTime;
+		    }
+
+		// DTSTART in a VTIMEZONE must always be in local time (with no TZ identifier).
+		dtstart->SetDateTimeL(startTime,CICalValue::ESpecifiedTimeZone);
+
+		CleanupStack::Pop(dtstart);
+		tzProp.AddPropertyL(KICalDtstart, dtstart); //Pass ownership
+
+		//  -RRULE (optional)
+		CICalValue* tzRrule = CICalValue::NewLC();
+		HBufC* ruleText = HBufC::NewLC(KMaxBufLength);
+		ruleText->Des().Copy(KRRuleFreqYearly);
+
+		TInt monthNum = singleRule.iMonth + 1;
+
+		switch (singleRule.iDayRule)
+			{
+			case ETzFixedDate :
+				{
+				// e.g. 11th October
+				// Equivalent to FREQ=YEARLY;BYMONTH=?;BYMONTHDAY=?
+				// This won't get imported by Outlook, but it's the only way of exporting it.
+				// It can't be converted to a week of the month.
+				ruleText->Des().AppendNum(monthNum);
+				ruleText->Des().Append(KRRuleByMonthDay);
+				ruleText->Des().AppendNum(singleRule.iDayOfMonth);
+				break;
+				}
+			case ETzDayAfterDate :
+				{
+				// e.g. first Sunday after 8th October
+				// To get this to a format understood by Outlook, we have to convert the day of
+				// month into a week number. If the day of the month is greater than 21, the week
+				// could cover multiple months (and possibly years), so we export using BYMONTHDAY.
+				const TInt KLastPossibleDayOfMonth = 21;
+				if (singleRule.iDayOfMonth > KLastPossibleDayOfMonth)
+					{
+					// Equivalent to FREQ=YEARLY;BYMONTH=?;BYMONTHDAY=?;BYDAY=+1?
+					ruleText->Des().AppendNum(monthNum);
+					ruleText->Des().Append(KRRuleByMonthDay);
+					ruleText->Des().AppendNum(singleRule.iDayOfMonth);
+					ruleText->Des().Append(KRRuleByDayPlusOne);
+					ruleText->Des().Append(DayFromInt(singleRule.iDayOfWeek));
+					}
+				else
+					{
+					// Equivalent to FREQ=YEARLY;BYMONTH=?;BYDAY=+n?
+					ruleText->Des().AppendNum(monthNum);
+					ruleText->Des().Append(KRRuleByDayPlus);
+					ruleText->Des().AppendNum(WeekNumFromDayOfMonth(singleRule.iDayOfMonth));
+					ruleText->Des().Append(DayFromInt(singleRule.iDayOfWeek));
+					}
+				break;
+				}
+			case ETzDayBeforeDate :
+				{
+				// e.g. Sunday before 8th October
+				// To get this to a format understood by Outlook, we have to convert the day of
+				// month into a week number. If the day of the month is less than 8, the week
+				// could cover multiple months (and possibly years), so we export using BYMONTHDAY.
+				const TInt KFirstPossibleDayOfMonth = 8;
+				if (singleRule.iDayOfMonth < KFirstPossibleDayOfMonth)
+					{
+					// Equivalent to FREQ=YEARLY;BYMONTH=?;BYMONTHDAY=?;BYDAY=-1?
+					ruleText->Des().AppendNum(monthNum);
+					ruleText->Des().Append(KRRuleByMonthDay);
+					ruleText->Des().AppendNum(singleRule.iDayOfMonth);
+					ruleText->Des().Append(KRRuleByDayMinusOne);
+					ruleText->Des().Append(DayFromInt(singleRule.iDayOfWeek));
+					}
+				else
+					{
+					// Equivalent to FREQ=YEARLY;BYMONTH=?;BYDAY=+n?
+					ruleText->Des().AppendNum(monthNum);
+					ruleText->Des().Append(KRRuleByDayPlus);
+					ruleText->Des().AppendNum(WeekNumFromDayOfMonth(singleRule.iDayOfMonth));
+					ruleText->Des().Append(DayFromInt(singleRule.iDayOfWeek));
+					}
+				break;
+				}
+			case ETzDayInLastWeekOfMonth :
+				{
+				// e.g. last Sunday in October
+				// Equivalent to FREQ=YEARLY;BYMONTH=?;BYDAY=-1?
+				ruleText->Des().AppendNum(monthNum);
+				ruleText->Des().Append(KRRuleByDayMinusOne);
+				ruleText->Des().Append(DayFromInt(singleRule.iDayOfWeek));
+				break;
+				}
+			default :
+				User::Leave(KErrCorrupt);
+				break;
+			}
+		tzRrule->SetRecurrenceRuleL(*ruleText);
+		CleanupStack::PopAndDestroy(ruleText);
+		CleanupStack::Pop(tzRrule);
+		tzProp.AddPropertyL(KICalRRule, tzRrule); //Pass ownership
+
+		//  -TZOFFSETFROM (Required)
+		CICalValue* tzOffsetFrom = CICalValue::NewLC();
+		tzOffsetFrom->SetUtcOffsetL(
+		            TTimeIntervalSeconds(singleRule.iOldLocalTimeOffset * KSecondsInOneMinute) );
+		CleanupStack::Pop(tzOffsetFrom);
+		tzProp.AddPropertyL(KICalTzoffsetfrom, tzOffsetFrom); //Pass ownership
+
+		//  -TZOFFSETTO (Required)
+		CICalValue* tzOffsetTo = CICalValue::NewLC();
+		tzOffsetTo->SetUtcOffsetL(
+		            TTimeIntervalSeconds(singleRule.iNewLocalTimeOffset * KSecondsInOneMinute) );
+		CleanupStack::Pop(tzOffsetTo);
+		tzProp.AddPropertyL(KICalTzoffsetto, tzOffsetTo); //Pass ownership
+		}
+		
+	iTzNameArray.AppendL(timeZoneName); //Transfer ownership
+	iTimeZoneArray.AppendL(tzRules);	//Transfer ownership
+	
+	iTimezoneIndex = iTimeZoneArray.Count() -1;
+	CleanupStack::Pop(timeZoneName);
+	CleanupStack::Pop(tzRules);
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds properties specific to VTODO to the specified component.
+@param aTodo VTODO component to add properties to.
+@param aEntry CCalEntry containing details of the TODO.
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddTodoL(CICalComponent& aTodo,const CCalEntry& aEntry) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	TCalTime time(aEntry.EndTimeL());
+
+	if (iExportProperties & KAgnExportDtEnd)
+		{
+		if (time.TimeUtcL() != Time::NullTTime())
+			{
+			//Add 'DUE' property
+			AddDateTimePropertyL(aTodo, time, KICalDue);
+			}
+		}
+
+	//Add 'COMPLETED' property
+	if (iExportProperties & KAgnExportCompleted)
+		{
+		time = aEntry.CompletedTimeL();
+		if (time.TimeUtcL() != Time::NullTTime())
+			{
+			//Add 'COMPLETED' property
+			AddDateTimePropertyL(aTodo, time, KICalCompleted);
+			}
+		}
+
+	//Add 'STATUS' property
+	if (iExportProperties & KAgnExportStatus)
+		{
+		switch (aEntry.StatusL())
+			{
+			//VTODO supports NEEDS_ACTION, COMPLETED, IN-PROCESS, CANCELLED
+			case CCalEntry::ECancelled :
+				aTodo.AddPropertyL(KICalStatus, KICalCancelled);
+				break;
+			case CCalEntry::ETodoNeedsAction :
+				aTodo.AddPropertyL(KICalStatus, KICalNeedsAction);
+				break;
+			case CCalEntry::ETodoCompleted :
+				aTodo.AddPropertyL(KICalStatus, KICalCompleted);
+				break;
+			case CCalEntry::ETodoInProcess :
+				aTodo.AddPropertyL(KICalStatus, KICalInProcess);
+				break;
+			case CCalEntry::ETentative :	// not supported, fall through
+			case CCalEntry::EConfirmed :	// not supported, fall through
+			default :
+				//Do not add a status property
+				break;
+			}
+		}
+	//Add 'X-PARAM' identifier
+	if (iExportProperties & KAgnExportXProp)
+		{
+		aTodo.AddPropertyL( iStringProvider.StringL(EICalXParamType),
+		                    iStringProvider.StringL(EICalXParamTodo) );
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+
+//
+//     ADD PROPERTY FUNCTIONS
+//     ======================
+//
+
+/**
+Adds 'ATTENDEE' properties to aComponent from aEntry
+@param aComponent component to add the ATTENDEE property to
+@param aEntry entry to extract ATTENDEE information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddAttendeePropertiesL( CICalComponent& aComponent,
+                                                  const CCalEntry& aEntry ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	const RPointerArray<CCalAttendee>& attendees = aEntry.AttendeesL();
+	const TInt count = attendees.Count();
+	CCalAttendee* attendee = NULL;
+	CICalValue* val = NULL;
+	for (TInt x = 0; x < count; ++x)
+		{
+		/** Can't add the following property parameters due to no equivalence:
+		*   (";" dirparam) / (";" languageparam) / (";" cutypeparam) /
+		*   (";"memberparam) / (";" deltoparam) / (";" delfromparam)
+		*/
+		attendee = attendees[x]; //Not taking ownership
+		val = CICalValue::NewLC();
+		val->SetTextL(attendee->Address());
+
+		//Add a property for each ATTENDEE
+		CleanupStack::Pop(val);
+		CICalProperty& attProp = aComponent.AddPropertyL(KICalAttendee, val); //Pass ownership
+
+		//Add 'CN' property parameter
+		TPtrC commonName(attendee->CommonName());
+		if (commonName.Length() > 0)
+			{
+			attProp.AddPropertyParamL(KICalCn, commonName);
+			}
+
+		//Add 'ROLE' property parameter
+		TPtrC roleName(RoleFromEnum(attendee->RoleL()));
+		if (roleName.Length() > 0)
+			{
+			attProp.AddPropertyParamL(KICalRole, roleName);
+			}
+
+		//Add 'RSVP' property parameter
+		// Default is FALSE so don't add a value if this is the case
+		if (attendee->ResponseRequested())
+			{
+			CICalValue* rsvpVal = CICalValue::NewLC();
+			rsvpVal->SetBooleanL(ETrue);
+			CleanupStack::Pop(rsvpVal);
+			attProp.AddPropertyParamL(KICalRsvp, rsvpVal);
+			}
+
+		//Add "SENT-BY" property parameter
+		TPtrC sentBy(attendee->SentBy());
+		if (sentBy.Length() > 0)
+			{
+			attProp.AddPropertyParamL(KICalSentBy, sentBy);
+			}
+
+		//Add PARTSTAT property parameter
+		TPtrC participationStatus(StatusFromEnum(attendee->StatusL()));
+		if (participationStatus.Length() > 0)
+			{
+			attProp.AddPropertyParamL(KICalPartStat, participationStatus);
+			}
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds 'CATEGORY' properties to aComponent from aEntry
+@param aComponent component to add the CATEGORY property to
+@param aEntry entry to extract CATEGORY information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddCategoriesPropertyL( CICalComponent& aComponent,
+                                                  const CCalEntry& aEntry ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	//CategoryListL is not const so we need a non-const CalEntry to call it
+	CCalEntry& nonConstEntry = const_cast<CCalEntry&>(aEntry);
+	const RPointerArray<CCalCategory>& categories = nonConstEntry.CategoryListL();
+	const TInt count = categories.Count();
+	if (count)
+		{
+		CICalProperty& catProp =
+                aComponent.AddPropertyL(KICalCategories, CategoryStringL(*categories[0]));
+		for (TInt x = 1; x < count; ++x)
+			{
+			catProp.AddValueL(CategoryStringL(*categories[x]));
+			}
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds a CLASS property to the specified component
+@param aComponent component to add the CLASS property to
+@param aEntry entry to extract CLASS information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddClassPropertyL( CICalComponent& aComponent,
+                                             const CCalEntry& aEntry ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	CCalEntry::TReplicationStatus repStatus = aEntry.ReplicationStatusL();
+	TPtrC status(ClassStringL(repStatus));
+	if (status.Length() > 0)
+		{
+		// Only add the CLASS property if it is not the default, 'PUBLIC'.
+		if (status.CompareF(KICalPublic) != 0)
+			{
+			aComponent.AddPropertyL(KICalClass, status);
+			}
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds 'EXDATE' property to aComponent from aEntry
+@param aComponent component to add the EXDATE property to
+@param aEntry entry to extract EXDATE information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddExceptionDatePropertyL( CICalComponent& aComponent,
+                                                     const CCalEntry& aEntry ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	RArray<TCalTime> exDates;
+	CleanupClosePushL(exDates);
+	aEntry.GetExceptionDatesL(exDates);
+	const TInt count = exDates.Count();
+	if (count)
+		{
+		CICalValue* val = CICalValue::NewLC();
+		val->SetDateTimeL(exDates[0].TimeUtcL(), CICalValue::EUtcTime);
+		CleanupStack::Pop(val);
+		CICalProperty& prop = aComponent.AddPropertyL(KICalExdate, val); //Pass ownership
+
+		for (TInt x = 1; x < count; ++x)
+			{
+			val = CICalValue::NewLC();
+			val->SetDateTimeL(exDates[x].TimeUtcL(), CICalValue::EUtcTime);
+			CleanupStack::Pop(val);
+			prop.AddValueL(val); //Pass ownership
+			}
+		}
+	CleanupStack::PopAndDestroy(&exDates);
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds an integer value to a given component
+@param aComponent component to add the integer value to
+@param aInt integer to be added
+@param aProperty current property
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddIntegerPropertyL( CICalComponent& aComponent,
+                                               TInt aInt,
+                                               const TDesC& aProperty ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	CICalValue* val = CICalValue::NewLC();
+	val->SetIntegerL(aInt);
+	CleanupStack::Pop(val);
+	aComponent.AddPropertyL(aProperty, val); //Pass ownership
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds an 'ORGANIZER' property to aComponent from aEntry
+@param aComponent component to add the ORGANIZER property to
+@param aEntry entry to extract ORGANIZER information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddOrganizerPropertyL( CICalComponent& aComponent,
+                                                 const CCalEntry& aEntry ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	CCalUser* organizer = aEntry.OrganizerL();
+
+	if (organizer)
+		{
+		/** Can't add the following property parameters due to no equivalence:
+		*	(";" dirparam) /  (";" languageparam)
+        */
+		CICalValue* orgVal = CICalValue::NewLC();
+		orgVal->SetTextL(organizer->Address());
+		CleanupStack::Pop(orgVal);
+		CICalProperty& orgProp = aComponent.AddPropertyL(KICalOrganizer, orgVal); //Pass ownership
+		//Add 'CN' property parameter
+		TPtrC commonName(organizer->CommonName());
+		if (commonName.Length() > 0)
+			{
+			orgProp.AddPropertyParamL(KICalCn, commonName);
+			}
+
+		//Add "SENT-BY" property parameter
+		TPtrC sentBy(organizer->SentBy());
+		if (sentBy.Length() > 0)
+			{
+			orgProp.AddPropertyParamL(KICalSentBy, commonName);
+			}
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds a text value to a given component
+@param aComponent component to add the text value to
+@param aText text to be added
+@param aProperty current property
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddTextPropertyL( CICalComponent& aComponent,
+                                            const TDesC& aText,
+                                            const TDesC& aProperty ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	if (aText.Length() > 0)
+		{
+		aComponent.AddPropertyL(aProperty, aText);
+		}
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds 'RDATE' property to aComponent from aEntry
+@param aComponent component to add the RDATE property to
+@param aEntry entry to extract RRULE information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddRDatePropertyL( CICalComponent& aComponent,
+                                             const CCalEntry& aEntry ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	RArray<TCalTime> rDates;
+	CleanupClosePushL(rDates);
+	aEntry.GetRDatesL(rDates);
+	const TInt count = rDates.Count();
+	if (count)
+		{
+		//Must create a property with a value
+		CICalValue* val = CICalValue::NewLC();
+		val->SetDateTimeL(rDates[0].TimeUtcL(), CICalValue::EUtcTime);
+		CleanupStack::Pop(val);
+		CICalProperty& prop = aComponent.AddPropertyL(KICalRdate, val); //Pass ownership
+
+		for (TInt x = 1; x < count; ++x)
+			{
+			val = CICalValue::NewLC();
+			val->SetDateTimeL(rDates[x].TimeUtcL(), CICalValue::EUtcTime);
+			CleanupStack::Pop(val);
+			prop.AddValueL(val);
+			}
+		}
+	CleanupStack::PopAndDestroy(&rDates);
+	
+	TRACE_EXIT_POINT;
+	}
+
+/**
+Adds 'EXDATE' property to aComponent from aEntry
+@param aComponent component to add the EXDATE property to
+@param aEntry entry to extract EXDATE information from
+@internalTechnology
+*/
+void CAgnVersit2Exporter::AddRRulePropertyL( CICalComponent& aComponent,
+                                             const CCalEntry& aEntry ) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	TCalRRule rule;
+	if (aEntry.GetRRuleL(rule))
+		{
+		HBufC* ruleVal = HBufC::NewLC(KInitialRRuleLength);
+		TPtr ruleValPtr(ruleVal->Des());
+		//Add 'FREQ'
+		ruleValPtr.Append(KICalFreq);
+		ruleValPtr.Append(KICalEquals);
+		switch (rule.Type())
+			{
+			case TCalRRule::EDaily:
+				ruleValPtr.Append(KICalDaily);
+				break;
+			case TCalRRule::EWeekly:
+				ruleValPtr.Append(KICalWeekly);
+				break;
+			case TCalRRule::EMonthly:
+				ruleValPtr.Append(KICalMonthly);
+				break;
+			case TCalRRule::EYearly:
+				ruleValPtr.Append(KICalYearly);
+				break;
+			default:
+				{
+				User::Leave(KErrCorrupt);
+				}
+			}
+
+		// Add 'UNTIL' or 'COUNT'.  Not both.
+		// Try UNTIL first as the COUNT implementation has issues.
+		TCalTime untilTime = rule.Until();
+
+		if (untilTime.TimeUtcL() == TCalTime::MaxTime())
+			{
+			// "UNTIL" returns the maximum date that Agenda can handle, so let's assume that it
+			// repeats "forever". In this case we do not export a value for either UNTIL or COUNT.
+			// Do nothing...
+			}
+		else if (untilTime.TimeUtcL() != Time::NullTTime())
+			{
+			ruleValPtr.Append(KICalSemiColon);
+			// Add the 'UNTIL'
+			ruleValPtr.Append(KICalUntil);
+			ruleValPtr.Append(KICalEquals);
+			CICalValue* val = CICalValue::NewLC();
+			// iCalendar spec states 'UNTIL' should be in UTC
+			val->SetDateTimeL(untilTime.TimeUtcL(), CICalValue::EUtcTime);
+			ruleValPtr.Append(val->TextL());
+			CleanupStack::PopAndDestroy(val);
+			}
+		else
+			{
+			TInt countNum = rule.Count();
+
+			if (countNum != 0)
+				{
+				ruleValPtr.Append(KICalSemiColon);
+				ruleValPtr.Append(KICalCount);
+				ruleValPtr.Append(KICalEquals);
+
+				// APINOTE: COUNT seems to be divided by interval during aEntry.GetRRuleL call.
+				// APINOTE: It also seems to be count left (from now) rather than count from DTSTART.
+				// APINOTE: In either case UNTIL will always be valid with the current API, so COUNT will never be used.
+				countNum *= rule.Interval();
+				ruleValPtr.AppendNum(countNum);
+				}
+
+			// Else neither is present event repeats forever.
+			}
+
+		//Add 'INTERVAL' if present
+		TInt interval = rule.Interval();
+
+		if (interval > 1)	// "1" is the default, so don't bother exporting it.
+			{
+			ruleValPtr.Append(KICalSemiColon);
+			ruleValPtr.Append(KICalInterval);
+			ruleValPtr.Append(KICalEquals);
+			ruleValPtr.AppendNum(rule.Interval());
+			}
+
+		//Add 'BYMONTHDAY' if present
+		if(rule.Type() == TCalRRule::EMonthly)
+			{
+			RArray<TInt> monthDays;
+			CleanupClosePushL(monthDays);
+			rule.GetByMonthDayL(monthDays);
+			TInt count = monthDays.Count();
+
+			if (count > 0)
+				{
+				ruleValPtr.Append(KICalSemiColon);
+				ruleValPtr.Append(KICalByMonthDay);
+				ruleValPtr.Append(KICalEquals);
+
+				for (TInt x = 0; x < count; ++x)
+					{
+					//Add 1 the month day ( 0 = 1).
+					ruleValPtr.AppendNum( monthDays[x]  + 1 );
+					if (x < count - 1)
+						{
+						ruleValPtr.Append(KICalComma);
+						}
+					}
+
+				}
+			CleanupStack::PopAndDestroy(&monthDays);
+			}
+
+		//Add 'WKST'.  Agenda only supports 'WKST' if 'FREQ=WEEKLY'
+		if (rule.Type() == TCalRRule::EWeekly)
+			{
+			ruleValPtr.Append(KICalSemiColon);
+			ruleValPtr.Append(KICalWkSt);
+			ruleValPtr.Append(KICalEquals);
+			ruleValPtr.Append(DayFromTDay(rule.WkSt()));
+			}
+
+
+		//Add 'BYDAY'
+		if ( rule.Type() == TCalRRule::EYearly  ||
+		     rule.Type() == TCalRRule::EMonthly ||
+		     rule.Type() == TCalRRule::EWeekly )
+			{
+			RArray<TDay> days;
+			CleanupClosePushL(days);
+			RArray<TCalRRule::TDayOfMonth> daysOfMonth;
+			CleanupClosePushL(daysOfMonth);
+			rule.GetByDayL(days);
+			rule.GetByDayL(daysOfMonth);
+			TInt daysCount = days.Count();
+			TInt daysOfMonthCount = daysOfMonth.Count();
+
+			if (daysCount > 0 || daysOfMonthCount > 0)
+				{
+				ruleValPtr.Append(KICalSemiColon);
+				ruleValPtr.Append(KICalByDay);
+				ruleValPtr.Append(KICalEquals);
+				}
+
+			if (daysCount > 0)
+				{
+				for (TInt x = 0; x < daysCount; ++x)
+					{
+					ruleValPtr.Append(DayFromTDay(days[x]));
+					if (x < daysCount - 1)
+						{
+						ruleValPtr.Append(KICalComma);
+						}
+					}
+				}
+			if (daysOfMonthCount > 0)
+				{
+				for (TInt x = 0; x < daysOfMonthCount; ++x)
+					{
+					ruleValPtr.AppendNum(daysOfMonth[x].WeekInMonth());
+					ruleValPtr.Append(DayFromTDay(daysOfMonth[x].Day()));
+					if (x < daysOfMonthCount - 1)
+						{
+						ruleValPtr.Append(KICalComma);
+						}
+					}
+				}
+			CleanupStack::PopAndDestroy(&daysOfMonth);
+			CleanupStack::PopAndDestroy(&days);
+			}
+
+
+		//Add 'BYMONTH'. Agenda only supports 'BYMONTH' if 'FREQ=YEARLY'
+		if (rule.Type() == TCalRRule::EYearly)
+			{
+			RArray<TMonth> theMonths;
+			CleanupClosePushL(theMonths);
+			rule.GetByMonthL(theMonths);
+
+			TInt monthsCount = theMonths.Count();
+
+			if (monthsCount > 0)
+				{
+				ruleValPtr.Append(KICalSemiColon);
+				ruleValPtr.Append(KICalByMonth);
+				ruleValPtr.Append(KICalEquals);
+
+				for (int i = 0; i < monthsCount; i++)
+					{
+					TInt monthNum = (TInt)(theMonths[i] + 1);	// TMonth is offset from 0
+					ruleValPtr.AppendNum(monthNum);
+					if (i < monthsCount - 1)
+						{
+						ruleValPtr.Append(KICalComma);
+						}
+					}
+				}
+			CleanupStack::PopAndDestroy(&theMonths);
+			}
+
+		CICalValue* val = CICalValue::NewLC();
+		val->SetTextL(ruleValPtr);
+		CleanupStack::Pop(val);
+		CICalProperty& prop = aComponent.AddPropertyL(KICalRRule,val); //Pass ownership
+
+		CleanupStack::PopAndDestroy(ruleVal);
+		
+		TRACE_EXIT_POINT;
+		}
+	}
+
+/**
+Adds a date-time property to the passed component. Outputs in Local time if
+possible, UTC otherwise.
+@param aComponent component to add the property to.
+@param aUtcTime time in UTC.
+@param aProperty property to add.
+@return property resulting from adding the date-time.
+@internalTechnology
+*/
+CICalProperty& CAgnVersit2Exporter::AddDateTimePropertyL(CICalComponent& aComponent,
+	const TCalTime& aTime, const TDesC& aProperty) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	// Check if we have a local time zone name - output in this timezone if we do
+	if ((iTimezoneIndex != KErrNotFound) && (iTimezoneIndex < iTzNameArray.Count()))
+		{
+		CICalValue* val = CICalValue::NewLC();
+		TTime timeCopy(aTime.TimeUtcL());
+		ASSERT(iTimeZoneArray.Count() >= iTimezoneIndex + 1);
+		iTimeZoneArray[iTimezoneIndex]->ConvertToLocalL(timeCopy);
+		val->SetDateTimeL(timeCopy, CICalValue::ESpecifiedTimeZone);
+		CleanupStack::Pop(val);
+		CICalProperty& prop = aComponent.AddPropertyL(aProperty, val); //pass ownership of val
+		prop.AddPropertyParamL(KICalTzid,*iTzNameArray[iTimezoneIndex]);
+		
+		TRACE_EXIT_POINT;
+		return prop;
+		}
+	else if(aTime.TimeMode() == TCalTime::EFloating)
+		{
+		TRACE_EXIT_POINT;
+		// If the CalTime was a floating time when it was created, output in floating time.
+		return AddFloatingDateTimePropertyL(aComponent, aTime.TimeLocalL(), aProperty);
+		}
+	else
+		{
+		TRACE_EXIT_POINT;
+		// Otherwise output in UTC
+		return AddUtcDateTimePropertyL(aComponent, aTime.TimeUtcL(), aProperty);
+		}
+	}
+
+/**
+Adds a date-time property to the passed component. Forces UTC.
+@param aComponent component to add the property to.
+@param aUtcTime time in UTC.
+@param aProperty property to add.
+@return property resulting from adding the date-time.
+@internalTechnology
+*/
+CICalProperty& CAgnVersit2Exporter::AddUtcDateTimePropertyL(CICalComponent& aComponent,
+	const TTime& aUtcTime, const TDesC& aProperty) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	CICalValue* val = CICalValue::NewLC();
+	val->SetDateTimeL(aUtcTime,CICalValue::EUtcTime);
+	CleanupStack::Pop(val);
+	CICalProperty& prop = aComponent.AddPropertyL(aProperty, val); //pass ownership of val
+	
+	TRACE_EXIT_POINT;
+	return prop;
+	}
+
+/**
+Adds a date-time property to the passed component. Forces floating.
+@param aComponent component to add the property to.
+@param aUtcTime time in floating time.
+@param aProperty property to add.
+@return property resulting from adding the date-time.
+@internalTechnology
+*/
+CICalProperty& CAgnVersit2Exporter::AddFloatingDateTimePropertyL(CICalComponent& aComponent,
+	const TTime& aFloatingTime, const TDesC& aProperty) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	CICalValue* val = CICalValue::NewLC();
+	val->SetDateTimeL(aFloatingTime,CICalValue::EFloatingTime);
+	CleanupStack::Pop(val);
+	CICalProperty& prop = aComponent.AddPropertyL(aProperty, val); //pass ownership of val
+	
+	TRACE_EXIT_POINT;
+	return prop;
+	}
+
+
+//
+//     HELPER FUNCTIONS
+//     ================
+//
+
+/**
+Helper function to convert between a CCalEntry::TReplicationStatus stored in
+agenda and a spec-compliant descriptor
+@param aStatus agenda representation of CLASS
+@return Descriptor representation of CLASS
+*/
+const TDesC& CAgnVersit2Exporter::ClassStringL(const CCalEntry::TReplicationStatus aStatus)
+	{
+	TRACE_ENTRY_POINT;
+	
+	switch (aStatus)
+		{
+		case CCalEntry::EOpen :
+		    TRACE_EXIT_POINT;
+			return KICalPublic;
+		case CCalEntry::EPrivate :
+		    TRACE_EXIT_POINT;
+			return KICalPrivate;
+		case CCalEntry::ERestricted :
+		    TRACE_EXIT_POINT;
+			return KICalConfidential;
+		default :
+			User::Leave(KErrCorrupt);
+			break;
+		}
+
+    TRACE_EXIT_POINT;	    
+	return KNullDesC;
+	}
+
+/**
+Helper function to convert between a category stored in agenda and a
+spec-compliant descriptor
+@param aCategory category to extract data from
+@return Descriptor representation of category
+@internalTechnology
+*/
+const TDesC& CAgnVersit2Exporter::CategoryStringL(const CCalCategory& aCategory) const
+	{
+	TRACE_ENTRY_POINT;
+	
+	switch (aCategory.Category())
+		{
+		case CCalCategory::ECalAppointment :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalAppointment);
+		case CCalCategory::ECalBusiness :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalBusiness);
+		case CCalCategory::ECalEducation :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalEducation);
+		case CCalCategory::ECalHoliday :
+		    TRACE_EXIT_POINT;   
+			return iStringProvider.StringL(EICalHoliday);
+		case CCalCategory::ECalMeeting :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalMeeting);
+		case CCalCategory::ECalMiscellaneous :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalMisc);
+		case CCalCategory::ECalPersonal :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalPersonal);
+		case CCalCategory::ECalPhoneCall :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalPhoneCall);
+		case CCalCategory::ECalSickDay :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalSick);
+		case CCalCategory::ECalSpecialOccasion :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalSpecial);
+		case CCalCategory::ECalTravel :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalTravel);
+		case CCalCategory::ECalVacation :
+		    TRACE_EXIT_POINT;
+			return iStringProvider.StringL(EICalVacation);
+		case CCalCategory::ECalExtended :
+		    TRACE_EXIT_POINT;
+			return aCategory.ExtendedCategoryName();
+		default :
+			User::Leave(KErrCorrupt);
+			break;
+		}
+    
+    TRACE_EXIT_POINT;
+	return KNullDesC;
+	}
+
+/**
+Converts from a TCalRole defined in CCalAttendee into a spec-compliant
+descriptor.
+@param aRole TCalRole enumeration to convert from.
+@return Descriptor containing the equivalent text.
+@internalTechnology
+*/
+const TDesC& CAgnVersit2Exporter::RoleFromEnum(CCalAttendee::TCalRole aRole)
+	{
+	TRACE_ENTRY_POINT;
+	
+	switch (aRole)
+		{
+		case CCalAttendee::EReqParticipant :
+		    TRACE_EXIT_POINT;
+			return KICalReqParticipant;
+		case CCalAttendee::EOptParticipant :
+		    TRACE_EXIT_POINT;
+			return KICalOptParticipant;
+		case CCalAttendee::ENonParticipant :
+		    TRACE_EXIT_POINT;
+			return KICalNonParticipant;
+		case CCalAttendee::EChair :
+		    TRACE_EXIT_POINT;
+			return KICalChair;
+		default :
+			break;
+		}
+    
+    TRACE_EXIT_POINT;
+	return KNullDesC;
+	}
+
+/**
+Converts from a TCalStatus defined in CCalAttendee into a spec-compliant descriptor.
+@param aStatus TCalStatus enumeration to convert from.
+@return Descriptor containing the equivalent text.
+@internalTechnology
+*/
+const TDesC& CAgnVersit2Exporter::StatusFromEnum(CCalAttendee::TCalStatus aStatus)
+	{
+	TRACE_ENTRY_POINT;
+	
+	switch (aStatus)
+		{
+		case CCalAttendee::EAccepted :
+		    TRACE_EXIT_POINT;
+			return KICalAccepted;
+		case CCalAttendee::ETentative :
+		    TRACE_EXIT_POINT;
+			return KICalTentative;
+		case CCalAttendee::EConfirmed :
+		    TRACE_EXIT_POINT;
+			return KICalConfirmed;
+		case CCalAttendee::EDeclined :
+		    TRACE_EXIT_POINT;
+			return KICalDeclined;
+		case CCalAttendee::ECompleted :
+		    TRACE_EXIT_POINT;
+			return KICalCompleted;
+		case CCalAttendee::EDelegated :
+		    TRACE_EXIT_POINT;
+			return KICalDelegated;
+		case CCalAttendee::EInProcess :
+		    TRACE_EXIT_POINT;
+			return KICalInProcess;
+		case CCalAttendee::ENeedsAction :
+		// Needs action is the default so fall through and set a null descriptor
+		default :
+			break;
+		}
+    
+    TRACE_EXIT_POINT;
+	return KNullDesC;
+	}
+
+/**
+Helper function to convert from a TUint8 as defined in vtzrules.h into a
+spec-compliant descriptor, such as MO, TU etc.
+@param aDayInt Day number to be converted.
+@return Descriptor containing the converted day.
+@internalTechnology
+*/
+const TDesC& CAgnVersit2Exporter::DayFromInt(TUint8 aDayInt)
+	{
+	TRACE_ENTRY_POINT;
+	
+	switch (aDayInt)
+		{
+		case 0 :
+		    TRACE_EXIT_POINT;
+			return KICalMonday;
+		case 1 :
+		    TRACE_EXIT_POINT;
+			return KICalTuesday;
+		case 2 :
+		    TRACE_EXIT_POINT;
+			return KICalWednesday;
+		case 3 :
+		    TRACE_EXIT_POINT;
+			return KICalThursday;
+		case 4 :
+		    TRACE_EXIT_POINT;
+			return KICalFriday;
+		case 5 :
+		    TRACE_EXIT_POINT;
+			return KICalSaturday;
+		case 6 :
+		    TRACE_EXIT_POINT;
+			return KICalSunday;
+		default :
+			break;
+		}
+    
+    TRACE_EXIT_POINT;
+	return KNullDesC;
+	}
+
+/**
+Helper function to convert from a TDay into a spec-compliant descriptor, such
+as MO, TU etc.
+@param aDay TDay to be converted.
+@return Descriptor containing the converted day.
+@internalTechnology
+*/
+const TDesC& CAgnVersit2Exporter::DayFromTDay(TDay aDay)
+	{
+	TRACE_ENTRY_POINT;
+	
+	switch (aDay)
+		{
+		case EMonday :
+		    TRACE_EXIT_POINT;
+			return KICalMonday;
+		case ETuesday :
+		    TRACE_EXIT_POINT;
+			return KICalTuesday;
+		case EWednesday :
+		    TRACE_EXIT_POINT;
+			return KICalWednesday;
+		case EThursday :
+		    TRACE_EXIT_POINT;
+			return KICalThursday;
+		case EFriday :
+		    TRACE_EXIT_POINT;
+			return KICalFriday;
+		case ESaturday :
+		    TRACE_EXIT_POINT;
+			return KICalSaturday;
+		case ESunday :
+		    TRACE_EXIT_POINT;
+			return KICalSunday;
+		default :
+			break;
+		}
+	
+	TRACE_EXIT_POINT;
+	return KNullDesC;
+	}
+
+TInt CAgnVersit2Exporter::WeekNumFromDayOfMonth(TInt aDayOfMonth)
+	{
+	TRACE_ENTRY_POINT;
+	
+	TRACE_EXIT_POINT;
+	return ((aDayOfMonth - 1) / 7) + 1;
+	}
+
+//End of file.