pimappservices/calendarvcalplugin/src/agmvcalx.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 14 Sep 2010 21:17:03 +0300
branchRCL_3
changeset 31 97232defd20e
parent 30 bd7edf625bdd
permissions -rw-r--r--
Revision: 201033 Kit: 201035

// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <s32mem.h>
#include <vtzrules.h>
#include <utf.h>
#include <tz.h>

#include <calentry.h>
#include <calcontent.h>
#include <calrrule.h>
#include <caluser.h>
#include <calalarm.h>
#include <calattachment.h>

#include "agmvcal.h"
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <vpropbinaryfile.h>
#endif


/** Specifies number of years backward from current year for which DAYLIGHT properties are exported.*/
const TInt KDaylightPropertyYearsBackwards(2);

/** Specifies number of years forward from current year, or the repeat start year, for which DAYLIGHT properties are exported.*/
const TInt KDaylightPropertyYearsForwards(5);


// Utility method to ensure proper cleanup in OOM 
//
void CAgendaEntryToVCalConverter::ResetAndDestroyArrayOfParams(TAny* aObject)
	{
	CArrayPtr<CParserParam>* array=reinterpret_cast<CArrayPtr<CParserParam>*>(aObject);
	if (array)
		{
		array->ResetAndDestroy();
		}
	delete array;
	}

void CAgendaEntryToVCalConverter::ResetAndDestroyArrayOfVersitDateTime(TAny* aObject)
	{
	CArrayPtr<TVersitDateTime>* array=reinterpret_cast<CArrayPtr<TVersitDateTime>*>(aObject);
	if (array)
		{
		array->ResetAndDestroy();
		}
	delete array;
	}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *	Public
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

// Export agenda entry aEntry to the given parser
//
void CAgendaEntryToVCalConverter::ExportEntryL(CCalEntry* aEntry, CVersitParser& aParser)
	{
	CParserVCalEntity* entity = CParserVCalEntity::NewL();
	CleanupStack::PushL(entity);

	if (aEntry->EntryTypeL() == CCalEntry::ETodo)
		{
		entity->SetEntityNameL(KVersitVarTokenVTODO);
		}
	else
		{
		entity->SetEntityNameL(KVersitVarTokenVEVENT);
		}

	if ((aEntry->StartTimeL()).TimeMode() == TCalTime::EFloating)
		{
		iTimeType = TVersitDateTime::EIsMachineLocal;
		iTimeFlag = TVersitDateTime::EExportLeaveAsLocalTime;
		}
	else 
		{
		iTimeType=TVersitDateTime::EIsUTC;
		iTimeFlag=TVersitDateTime::EExportTimeZoneDesignator;
		}

	AddEntryPropertiesL(aEntry, *entity);
	CleanupStack::Pop(); // entity
	aParser.AddEntityL(entity); // takes ownership
	}

CAgendaEntryToVCalConverter::~CAgendaEntryToVCalConverter()
	{
	}
	

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *	Private
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	
/** AddTzPropertyL()
 *
 *	Adds TZ property to aParser.
 *
 *	@param aParser		- a vCal entity or sub-entity (VEVENT or VTODO);
 *	@param aStdOffset	- standard time zone offset from UTC in seconds.
 */
void CAgendaEntryToVCalConverter::AddTzPropertyL(CVersitParser& aParser, 
												 TTimeIntervalSeconds aStdOffsetInSeconds)
	{
	if (!iAddedTzProperty)
		{
		CParserPropertyValue* vValue = new (ELeave) CParserPropertyValueTimeZone(aStdOffsetInSeconds);
		CleanupStack::PushL(vValue);

		CParserProperty* vProp = CParserProperty::NewL(*vValue, KVersitTokenTZ, NULL); 
		CleanupStack::Pop(vValue);

		aParser.AddPropertyL(vProp);
		}
	iAddedTzProperty = ETrue;
	}


/** MapToRelativeTime()
 *
 *	Provides mapping between iTimeReference of TVTzActualisedRule and iRelativeTime of TVersitDateTime. 
 *
 *	CTzRules::AddActualisedRulesL() guarantees that TVTzActualisedRule is always UTC or 
 *	local(ETzWallTimeReference), and is never STD. The func maps everything to 
 *	TVersitDateTime::EIsMachineLocal, unless iTimeReference is ETzUtcTimeReference, which is mapped
 *  to TVersitDateTime::EIsUTC.
 *
 *	This is local helper function.
 *	Used exclusively by CAgendaEntryToVCalConverter::AddTimeZonePropertiesL() to export DAYLIGHT properties.
 */
static inline TVersitDateTime::TRelativeTime MapToRelativeTime(TTzTimeReference& aTzTimeRef)
	{
	return (aTzTimeRef==ETzUtcTimeReference) ? (TVersitDateTime::EIsUTC) : (TVersitDateTime::EIsMachineLocal);
	}

/*	AddDaylightPropertyL()
 *
 *	Export a DAYLIGHT property to describe the daylight saving period and its offset.
 *	There can be more than one DAYLIGHT property for a year (depenging on number of DST rules for the year).  
 *	Time designators (i.e EST, EDT) are not exported.
 *
 *	@param aParser		- a vCal entity or sub-entity (VEVENT or VTODO);
 *	@param aOffsetInSeconds	 - offset in seconds from UTC (i.e. STD + DST) for the period when the 
 					 	  daylight saving rule is in effect, 
 						  for instance, for time zone "Vancouver/America (PST)" aOffsetInSeconds is
 						  -480*60=-28800 secs in winter time, and -420*60=-25200 secs when DST is observed; 
 *	@param aStartTime	- exact time when a rule starts (usually 02:00:00 local time);
 *	@param aEndTime		- time when rule ends (usually 03:00:00 local time).
 */
void CAgendaEntryToVCalConverter::AddDaylightPropertyL(CVersitParser& aParser, 
													   TTimeIntervalSeconds aOffsetInSeconds,
													   TTimeWithReference aStartTime,
													   TTimeWithReference aEndTime)
	{
	TVersitDateTime* vStartTime = new(ELeave) TVersitDateTime(aStartTime.iTime.DateTime(),
															  MapToRelativeTime(aStartTime.iTimeReference));
	CleanupStack::PushL(vStartTime);

	TVersitDateTime* vEndTime = new (ELeave) TVersitDateTime(aEndTime.iTime.DateTime(),
															  MapToRelativeTime(aEndTime.iTimeReference));
	CleanupStack::PushL(vEndTime);

	CVersitDaylight* vDaylight = CVersitDaylight::NewL(ETrue, 
													  aOffsetInSeconds, 
													  vStartTime, 
													  vEndTime, 
													  KNullDesC(), 
													  KNullDesC());
	
	CleanupStack::Pop(vEndTime);   // CVersitDaylight takes ownership.
	CleanupStack::Pop(vStartTime); // CVersitDaylight takes ownership.

	CleanupStack::PushL(vDaylight);
	CParserPropertyValue* vValue = new (ELeave) CParserPropertyValueDaylight(vDaylight);
	CleanupStack::Pop(vDaylight);

	CleanupStack::PushL(vValue);
	CParserProperty* vProp = CParserProperty::NewL(*vValue, KVersitTokenDAYLIGHT, NULL); 
	CleanupStack::Pop(vValue);

	aParser.AddPropertyL(vProp);
	}


/**	Adds DAYLIGHT properties for a range of years from aStartYear to aEndYear inclusive to 
 *	a vCalendar entity (aParser).
 *	
 *	@param aParser		- a vCalendar object where properties added
 *	@param aRules		- time zone rules used to retrieve DST information
 *	@param aRepeatStartTime		- start of entry range, including the repeat rule
 *	@param aRepeatEndTime		- end of entry range, including the repeat rule
 *
 */
void CAgendaEntryToVCalConverter::AddDaylightPropertiesL(CVersitParser& aParser, const CTzRules* aRules, TInt aStdOffset, TTime aRepeatStartTime, TTime aRepeatEndTime)
	{
	const TInt KRepeatStartYear = aRepeatStartTime.DateTime().Year();
	const TInt KRepeatEndYear = aRepeatEndTime.DateTime().Year();
	
 	// actualise the rules starting a year before our period of interest
	CVTzActualisedRules* actRules = CVTzActualisedRules::NewL(KRepeatStartYear - 1, KRepeatEndYear + 1);
	
	if (actRules)
		{
		CleanupStack::PushL(actRules);
		aRules->GetActualisedRulesL(*actRules);
		
		TTime now;
		now.HomeTime();
		const TInt KThisYear = now.DateTime().Year();
		
		// Set up the time range for exported DAYLIGHT properties
		const TInt KRangeBeginYear(Max(KThisYear - KDaylightPropertyYearsBackwards, KRepeatStartYear));
		const TInt KRangeEndYear(Min(Max(KThisYear + KDaylightPropertyYearsForwards, KRepeatStartYear + KDaylightPropertyYearsForwards), KRepeatEndYear + 1));
		const TTime KRangeBeginTime(TDateTime(KRangeBeginYear, EJanuary, 0, 0, 0, 0, 0));
		const TTime KRangeEndTime(TDateTime(KRangeEndYear, EJanuary, 0, 0, 0, 0, 0));
		
		const TInt KRulesCount(actRules->Count());
		for (TInt i(0) ; i < KRulesCount - 1 ; ++i)
			{
			TVTzActualisedRule& actRule = (*actRules)[i];
			
			if (aStdOffset < actRule.iNewOffset)		// work with DST rules only
				{
				// End time of a rule is start time of the next rule
				TVTzActualisedRule& actRuleNext = (*actRules)[i+1];
				
				TTimeWithReference ruleStart(TTimeWithReference(actRule.iTimeOfChange, actRule.iTimeReference));
				TTimeWithReference ruleEnd(TTimeWithReference(actRuleNext.iTimeOfChange, actRuleNext.iTimeReference));
				
				TTime ruleStartTime = ruleStart.iTime;
				TTime ruleEndTime = ruleEnd.iTime;
				
				if (actRule.iTimeReference == ETzStdTimeReference)
					{
					ruleStartTime -= TTimeIntervalMinutes(aRules->InitialStdTimeOffset());
					}
				else if (actRule.iTimeReference == ETzWallTimeReference)
					{
					ruleStartTime -= TTimeIntervalMinutes(actRule.iNewOffset);
					}						

				if (actRule.iTimeReference == ETzStdTimeReference)
					{
					ruleEndTime -= TTimeIntervalMinutes(aRules->InitialStdTimeOffset());
					}
				else if (actRule.iTimeReference == ETzWallTimeReference)
					{
					ruleEndTime -= TTimeIntervalMinutes(actRule.iNewOffset);
					}	
				
				// Export rule if the first instance occours when dst is on
				// or if the rule covers the range defined above
				if ( (aRepeatStartTime >= ruleStartTime) && (aRepeatStartTime <= ruleEndTime) || (ruleEndTime >= KRangeBeginTime) && (ruleStartTime <= KRangeEndTime) )
					{
					// Export DAYLIGHT property for concrete DST rule
					AddDaylightPropertyL(aParser, actRule.iNewOffset * 60, ruleStart, ruleEnd);
					}
				}
			}
		CleanupStack::PopAndDestroy(actRules);
		}
	}

void CAgendaEntryToVCalConverter::NextVCalendarL()
	{
	iAddedTzProperty = EFalse;
	}

/**	CAgendaEntryToVCalConverter::AddTimeZonePropertiesL()
 *	 	
 *	Adds TZ and DAYLIGHT properties to a vCalendar entity. 
 *
 *	@param aEntry 	- an agenda entry which supplies timezone and tz rules information 
 *						(has to have a repeat definition, otherwise function will do nothing);
 *	@param aParser	- a vCalendar object to which the properties are added. 
 *	
 *	aParser can be 1) a vCalendar object (also referrred to as "vCalendar entity"), 
 *		as well as 2) a vEvent or vTodo objects (also referred to as "vCalendar sub-entities").
 *	
 *	TZ and DAYLIGHT properties (as per vCalendar v1.0 spec) apply to the vCalendar object as a whole; UNLESS 
 *	overriden by a property within the scope of an event or todo entity.
 *	
 *	@internalComponent
 */
void CAgendaEntryToVCalConverter::AddTimeZonePropertiesL(CVersitParser& aParser, CCalEntry* aEntry)
	{
	TCalRRule rrule;
	
  	if (aEntry->GetRRuleL(rrule) && aEntry->StartTimeL().TimeMode() != TCalTime::EFloating)
		{
		// Only export the TZ property when the entry is repeating.
		// Do not export TZ rules for floating entry.
		CTzRules* rules = aEntry->TzRulesL();
		
		if (rules)
			{
    		CleanupStack::PushL( rules );

            // find the standard offset for this time zone
            // which is the lowest offset from the rules for the entry's repeat range
            CVTzActualisedRules* actRules = CVTzActualisedRules::NewL(rrule.DtStart().TimeUtcL().DateTime().Year() - 1, rrule.Until().TimeUtcL().DateTime().Year() + 1);
        	CleanupStack::PushL(actRules);
        	rules->GetActualisedRulesL(*actRules);
        	
        	TInt stdOffset(KMaxTInt);
        	
        	const TInt KRulesCount = actRules->Count();
        	for (TInt i(0) ; i < KRulesCount ; ++i)
        		{
        		if ((*actRules)[i].iNewOffset < stdOffset)
        			{
        			stdOffset = (*actRules)[i].iNewOffset;
        			}
        		}
        	
        	if (stdOffset == KMaxTInt)
        		{
        		// there were no offsets found so use the current system utc offset
        		stdOffset = (User::UTCOffset().Int() / 60);
        		}
        	
        	CleanupStack::PopAndDestroy(actRules);
			AddTzPropertyL(aParser, stdOffset * 60);
			AddDaylightPropertiesL(aParser, rules, stdOffset, rrule.DtStart().TimeUtcL(), rrule.Until().TimeUtcL());
            
			CleanupStack::PopAndDestroy(rules);	
			}
		}
	}

// This function specifies the ordering of the vCalendar properties as exported by Versit.
//
// The UID property should be the first property. 
//
// The following three properties of a vCalendar should be DESCRIPTION, DTSTART and DTEND for appointments, 
// anniversaries & events. To-do entries should have DESCRIPTION and DUE as the following two properties.
// This is to assist processing by Connectivity (as requested by Time Technology)
//
void CAgendaEntryToVCalConverter::AddEntryPropertiesL(CCalEntry* aEntry, CVersitParser& aParser)
	{
	// UID 

	HBufC* guid16 = HBufC::NewLC(aEntry->UidL().Length());
	TPtr ptr16 = guid16->Des();
	CnvUtfConverter::ConvertToUnicodeFromUtf8(ptr16, aEntry->UidL());
	AddDesPropertyL(aParser, KVersitTokenUID, guid16->Des());
	CleanupStack::PopAndDestroy(guid16);
		
	// CAgnEntry::RichTextL()	=> SUMMARY 
	// CAgnEntry::NotesTextL()	=> DESCRIPTION 

	if (aEntry->DescriptionL().Length() > 0)
		{
		AddDesPropertyL(aParser, KVersitTokenDESCRIPTION, aEntry->DescriptionL());					
		}
		
	if (aEntry->SummaryL().Length() > 0)
		{
		AddDesPropertyL(aParser, KVersitTokenSUMMARY, aEntry->SummaryL());
		}

	// Add DTSTART and DTEND (or DUE, PRIORITY and todo list information for to-do items), then add X-EPOCAGENDAENTRYTYPE
	switch (aEntry->EntryTypeL())
		{
		case CCalEntry::EAppt:
			{
			AddEventPropertiesL(aParser, aEntry);
			AddDesPropertyL(aParser, KVCalToken8ENTRYTYPE, KVCalTokenTypeAPPT);	
			break;
			}
		case CCalEntry::EReminder:
			{
			AddEventPropertiesL(aParser, aEntry);
			AddDesPropertyL(aParser, KVCalToken8ENTRYTYPE, KVCalTokenTypeREMINDER);	
			break;
			}
		case CCalEntry::EAnniv:
			{
			AddEventPropertiesL(aParser, aEntry);
			AddDesPropertyL(aParser, KVCalToken8ENTRYTYPE, KVCalTokenTypeANNIV);
			break;
			}
		case CCalEntry::EEvent:
			{
			AddEventPropertiesL(aParser, aEntry);
			AddDesPropertyL(aParser, KVCalToken8ENTRYTYPE, KVCalTokenTypeEVENT);
			break;
			}
		case CCalEntry::ETodo:
			{
			AddTodoPropertiesL(aParser, aEntry);
			AddDesPropertyL(aParser, KVCalToken8ENTRYTYPE, KVCalTokenTypeTODO);
			break;
			}
		default: break;
		}

	// Class
	CCalEntry::TReplicationStatus status = aEntry->ReplicationStatusL();
	switch(status)
		{
		case CCalEntry::EOpen:
			AddDesPropertyL(aParser, KVersitTokenCLASS, KVCalTokenPUBLIC);
			break;
		case CCalEntry::EPrivate:
			AddDesPropertyL(aParser, KVersitTokenCLASS, KVCalTokenPRIVATE);
			break;
		case CCalEntry::ERestricted:
			AddDesPropertyL(aParser, KVersitTokenCLASS, KVCalTokenCONFIDENTIAL);
			break;
		}

	// Location
	const TDesC& location = aEntry->LocationL();
	if (location.Length() != 0)
		{
		AddDesPropertyL(aParser, KVersitTokenLOCATION, location);
		}

	// DTSTAMP
	TCalTime dTStamp = aEntry->DTStampL();
		
   	if ( dTStamp.TimeUtcL() != Time::NullTTime() )
      	{	
		AddDateTimePropertyL(aParser, KVersitTokenXDTSTAMP, dTStamp.TimeUtcL(), TVersitDateTime::EIsUTC, iTimeFlag);
      	}
	
	// Sequence Number
	AddIntegerPropertyL(aParser, KVersitTokenSEQUENCE, aEntry->SequenceNumberL());

	//X-Method property
	CCalEntry::TMethod methodStatus = aEntry->MethodL();
	TPtrC methodStr;
	switch(methodStatus)
		{
		case  CCalEntry::EMethodNone:
			methodStr.Set(KVCalTokenMethodStatusENone);
			break;
		case CCalEntry::EMethodPublish:
			methodStr.Set(KVCalTokenMethodStatusEPublish);
			break;
		case CCalEntry::EMethodRequest:
			methodStr.Set(KVCalTokenMethodStatusERequest);
			break;
		case CCalEntry::EMethodReply:
			methodStr.Set(KVCalTokenMethodStatusEReply);
			break;
		case CCalEntry::EMethodAdd:
			methodStr.Set(KVCalTokenMethodStatusEAdd);
			break;
		case CCalEntry::EMethodCancel:
			methodStr.Set(KVCalTokenMethodStatusECancel);
			break;
		case CCalEntry::EMethodRefresh:
			methodStr.Set(KVCalTokenMethodStatusERefresh);
			break;
		case CCalEntry::EMethodCounter:
			methodStr.Set(KVCalTokenMethodStatusECounter);
			break;
		case CCalEntry::EMethodDeclineCounter:
			methodStr.Set(KVCalTokenMethodStatusEDeclineCounter);
			break;	
		default:
			User::Leave(KErrArgument);	
		}

	AddDesPropertyL(aParser, KVersitTokenXMETHOD, methodStr);
	// X-Recurrence-ID
	TCalTime recurrenceDate = aEntry->RecurrenceIdL();
	
	if (recurrenceDate.TimeUtcL() != Time::NullTTime())
		{
		TTime recId;
		if(iTimeType==TVersitDateTime::EIsUTC)
			{
			recId = recurrenceDate.TimeUtcL();
			}
		else
			{
			recId = recurrenceDate.TimeLocalL();
			}
		AddDateTimePropertyL(aParser, KVersitTokenXRECURRENCEID, recId, iTimeType, iTimeFlag);	
		}
		
	// Attendees
	RPointerArray<CCalAttendee> attendees = aEntry->AttendeesL();

	TInt count=attendees.Count();
	CCalUser* phoneowner=aEntry->PhoneOwnerL();
	for (TInt ii=0; ii<count; ii++)
		{
		if(phoneowner==attendees[ii])
			{
			AddAttendeePropertyL(aParser, attendees[ii],ETrue, EFalse);
			}
		else
			{
			AddAttendeePropertyL(aParser, attendees[ii],EFalse, EFalse);
			}
		
		}

	CCalUser* organizer=aEntry->OrganizerL();
	if(organizer)
		{
		if(phoneowner==organizer)
			{
			AddAttendeePropertyL(aParser, organizer,ETrue, ETrue);
			}
		else
			{
			AddAttendeePropertyL(aParser, organizer,EFalse, ETrue);
			}
		}

	// Recurrence details
	TCalRRule rule;
	TBool isrepeat = aEntry->GetRRuleL(rule);
	if (isrepeat)
		{
		CTzRules* repeatRule = aEntry->TzRulesL();
		CleanupStack::PushL(repeatRule);
		AddRepeatPropertiesL(aParser, rule, repeatRule);
		CleanupStack::PopAndDestroy(repeatRule);
		}
		
	// RDates
	RArray<TCalTime>rdates;
	CleanupClosePushL(rdates);
	aEntry->GetRDatesL(rdates);

	if (rdates.Count()>0)
		{
		AddRDatePropertyL(aParser, rdates);
		isrepeat = ETrue;
		}
		
	CleanupStack::PopAndDestroy(&rdates);

	if (isrepeat)
		{
		// Recurrence Exception details
		RArray<TCalTime> exceptions;
		CleanupClosePushL(exceptions);
		aEntry->GetExceptionDatesL(exceptions);

		if (exceptions.Count()>0)
			{
			AddRepeatExceptionPropertiesL(aParser, exceptions);
			}
		CleanupStack::PopAndDestroy(&exceptions);
		}
		
	// Alarm
	CCalAlarm* alarm = aEntry->AlarmL();
	if (alarm)
		{
		TCalTime alarmOrigin;
		CleanupStack::PushL(alarm);
		if(aEntry->EntryTypeL()==CCalEntry::ETodo && (aEntry->EndTimeL()).TimeUtcL()!=Time::NullTTime())
			{
			alarmOrigin = aEntry->EndTimeL();
			}
		else
			{
			alarmOrigin = aEntry->StartTimeL();
			}
		
		TTime alarmtime;
		if (aEntry->StartTimeL().TimeMode() != TCalTime::EFloating)
			{
			alarmtime = alarmOrigin.TimeUtcL() - TTimeIntervalMinutes(alarm->TimeOffset());
			}
		else
			{
			alarmtime = alarmOrigin.TimeLocalL() - TTimeIntervalMinutes(alarm->TimeOffset());
			}
		AddAlarmPropertyL(aParser, KVersitTokenAALARM, alarmtime, alarm->AlarmSoundNameL());
		

		CCalContent* alarmAction = alarm->AlarmAction();
		if (alarmAction != NULL)
			{
			AddExtendedAlarmPropertyL(aParser, KVersitTokenXALARM, *alarmAction);
			}
		CleanupStack::PopAndDestroy(alarm);
		}

	// Add Last Changed Date
	AddDateTimePropertyL(aParser, KVersitTokenLASTMODIFIED, (aEntry->LastModifiedDateL()).TimeUtcL(), TVersitDateTime::EIsUTC, iTimeFlag);

	// Category Support
	const RPointerArray<CCalCategory>& categories = aEntry->CategoryListL();

	if (categories.Count()>0)
		{
		AddCategoryPropertyL(aParser, categories);
		}

	// ADDITION FOR VERSIT COMPLIANCE 
	// Merged priorities into one place - ToDo priority was previously in AddTodoPropertiesL()
	TInt priority = aEntry->PriorityL();
	AddIntegerPropertyL(aParser, KVersitTokenPRIORITY, priority);
	if (aEntry->EntryTypeL() != CCalEntry::ETodo)
		{
		AddStatusPropertyL(aParser, aEntry);
		}
	AddIntegerPropertyL(aParser, KVersitTokenXLOCALUID, aEntry->LocalUidL());
	AddIntegerPropertyL(aParser, KVersitTokenTRANSP, aEntry->TimeTransparencyL());
	
	// 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,KVersitTokenCommaVal,&geoLongString);
			
			// Add the GEO property
			AddDesPropertyL(aParser,KVersitTokenGEO,geoString);
			}
		
		CleanupStack::PopAndDestroy(geoValue);
		}
		
	TUint attachCount = aEntry->AttachmentCountL();
	for(TInt ii = 0; ii<attachCount; ++ii)
		{
		CCalAttachment* attach = aEntry->AttachmentL(ii);
		AddAttachmentPropertyL(aParser, *attach);
		}
	
    TInt userDataInt = aEntry->UserInt32L();       
    AddIntegerPropertyL( aParser, KVersitExtUserInt, userDataInt );       
	}


// Add appointment properties to parser
//
void CAgendaEntryToVCalConverter::AddEventPropertiesL(CVersitParser& aParser, CCalEntry* aEntry)
	{
	//If this is an untimed appt the start time will be the default start time
 	//If the alarm time is *after* this time we need to move the start time to the alarm time
	TTime startTime; 
	TTime endTime; 
	if((aEntry->StartTimeL()).TimeMode() == TCalTime::EFloating )
		{
		startTime = (aEntry->StartTimeL()).TimeLocalL();
		if(aEntry->EntryTypeL()==CCalEntry::EReminder)
			{
			endTime = startTime;
			}
		else
			{
			endTime =(aEntry->EndTimeL()).TimeLocalL();
			}

		}
	else 	
		{
		startTime = (aEntry->StartTimeL()).TimeUtcL();
		if(aEntry->EntryTypeL()==CCalEntry::EReminder)
			{
			endTime = startTime;
			}
		else
			{
			endTime = (aEntry->EndTimeL()).TimeUtcL();
			}

		}
	
	iStartTime=startTime.DateTime();


	AddDateTimePropertyL(aParser, KVersitTokenDTSTART, startTime, iTimeType, iTimeFlag);
	AddDateTimePropertyL(aParser, KVersitTokenDTEND, endTime, iTimeType, iTimeFlag);
	}

// Add todo properties
//
void CAgendaEntryToVCalConverter::AddTodoPropertiesL(CVersitParser& aParser, CCalEntry* aEntry)
	{
	TBool isUtc = ETrue;
	if ((aEntry->EndTimeL()).TimeUtcL() != Time::NullTTime())
		{
		if ((aEntry->StartTimeL()).TimeMode() == TCalTime::EFloating)
			{
			isUtc = EFalse;
			AddDateTimePropertyL(aParser, KVersitTokenDUE, (aEntry->EndTimeL()).TimeLocalL(), iTimeType, iTimeFlag);
			}
		else
			{
			AddDateTimePropertyL(aParser, KVersitTokenDUE, (aEntry->EndTimeL()).TimeUtcL(), iTimeType, iTimeFlag);
			}
		}

	// Add todo name list is now done with rest of category implementation	
	TTime startDate; 
	
	if (isUtc)
		{
		startDate = aEntry->StartTimeL().TimeUtcL();
		}
	else
		{
		startDate = aEntry->StartTimeL().TimeLocalL();
		}
	iStartTime=startDate.DateTime();
	
	// We have already checked the start and end times so we do not need 
	// further checks here. After discussion with MV.
	if (startDate != Time::NullTTime())
		{
		AddDateTimePropertyL(aParser, KVersitTokenDTSTART, startDate, iTimeType, iTimeFlag);
		}

	// Is the entry completed - if so export completed date/time and COMPLETED status
	if ((aEntry->CompletedTimeL()).TimeUtcL() != Time::NullTTime())
		{
		AddDateTimePropertyL(aParser,KVCalTokenCOMPLETED, (aEntry->CompletedTimeL()).TimeUtcL(), TVersitDateTime::EIsUTC, iTimeFlag);
		AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusCOMPLETED);
		}
	else
		{
		if (aEntry->StatusL()==CCalEntry::ETodoCompleted)
			{
			// If entry status is COMPLETED, ensure COMPLETED date is exported too
			if ((aEntry->EndTimeL()).TimeUtcL()!= Time::NullTTime())
				{
				aEntry->SetCompletedL(ETrue,aEntry->EndTimeL());
				}
			else
				{
				//No valid due date, set the completed date to the today's date
				TTime today;
				today.UniversalTime();
				TCalTime caltime;
				caltime.SetTimeUtcL(today);
				aEntry->SetCompletedL(ETrue, caltime);
				}
			AddDateTimePropertyL(aParser,KVCalTokenCOMPLETED, (aEntry->CompletedTimeL()).TimeUtcL(), TVersitDateTime::EIsUTC, iTimeFlag);
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusCOMPLETED);
			}
		else
			{
			// Entry is not completed and doesn't have status==Completed
			// So, add status property as normal
			AddStatusPropertyL(aParser,aEntry);
			}

		}
		
	}

// Construct an integer property
//
void CAgendaEntryToVCalConverter::AddIntegerPropertyL(CVersitParser& aParser, const TDesC8& aToken, TCalLocalUid aInt)
	{
	CParserPropertyValue* value = new(ELeave)CParserPropertyValueInt(aInt);
	CleanupStack::PushL(value);
	CParserProperty* prop = CParserProperty::NewL(*value, aToken, NULL); 
	CleanupStack::Pop(value);	
	// Add to the parser
	aParser.AddPropertyL(prop);
	}

void CAgendaEntryToVCalConverter::AddDateTimePropertyL(CVersitParser& aParser, const TDesC8& aToken, const TTime& aTime, const TVersitDateTime::TRelativeTime aRelativeTime, TVersitDateTime::TVersitDateTimeFlags aTimeFlag)
	{
	// Construct a date-time property
	TVersitDateTime* versitDateTime = new(ELeave)TVersitDateTime(aTime.DateTime(), aRelativeTime);
	CleanupStack::PushL(versitDateTime);
	CParserPropertyValue* value = new(ELeave)CParserPropertyValueDateTime(versitDateTime);
	CleanupStack::Pop(versitDateTime);
	versitDateTime->SetFlag(aTimeFlag);
	
	CleanupStack::PushL(value);
	CParserProperty* prop = CParserProperty::NewL(*value, aToken, NULL);
	CleanupStack::Pop(value);
	// Add to the parser
	aParser.AddPropertyL(prop);

	}

void CAgendaEntryToVCalConverter::AddDesPropertyL(CVersitParser& aParser, const TDesC8& aToken, const TDesC& aDes)
	{
	HBufC* buf=AgnConvertUtil::EncodeL(aDes,KUidEtextToText);
	CleanupStack::PushL(buf);
	CParserPropertyValue* value = CParserPropertyValueHBufC::NewL(buf->Des());
	CleanupStack::PopAndDestroy(buf);
	CleanupStack::PushL(value);
	CParserProperty* prop = CParserProperty::NewL(*value, aToken, NULL);
	CleanupStack::Pop(value);
	// Add to the parser
	aParser.AddPropertyL(prop);
	}

void CAgendaEntryToVCalConverter::AddAlarmPropertyL(CVersitParser& aParser, const TDesC8& aToken, const TTime& aAlarmTime, const TDesC& aAlarmName)
	{
	// Create an array of paramaters for this property
	CArrayPtr<CParserParam>* arrayOfParams = new(ELeave)CArrayPtrFlat<CParserParam>(4);
	CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfParams,arrayOfParams));

	CParserParam* typeParam = CParserParam::NewL(KVCalToken8TYPE, KNullDesC8);
	CleanupStack::PushL(typeParam);
	typeParam->SetValueL(KVCalValue8EPOCSOUND);
	arrayOfParams->AppendL(typeParam); // takes ownership
	CleanupStack::Pop(typeParam);

	// Construct a date-time property
	
	TVersitDateTime* versitDateTime = new(ELeave)TVersitDateTime(aAlarmTime.DateTime(), iTimeType);
	CleanupStack::PushL(versitDateTime);

	CVersitAlarm* alarm = CVersitAlarm::NewL(versitDateTime, NULL, 0, aAlarmName, TPtrC());
	CleanupStack::Pop(versitDateTime);
	versitDateTime->SetFlag(iTimeFlag);
	CleanupStack::PushL(alarm);
	CParserPropertyValue* value = new(ELeave)CParserPropertyValueAlarm(alarm);
	CleanupStack::Pop(alarm);
	CleanupStack::PushL(value);

	CParserProperty* prop = CParserProperty::NewL(*value, aToken, arrayOfParams);
	CleanupStack::Pop(2,arrayOfParams); // value, arrayOfParams

	// Add to the parser
	aParser.AddPropertyL(prop);
	}

void CAgendaEntryToVCalConverter::AddExtendedAlarmPropertyL(CVersitParser& aParser, const TDesC8& aToken, const CCalContent& aAlarmAction)
	{
	// Create an array of parameters for this property
	CArrayPtr<CParserParam>* arrayOfParams = new(ELeave)CArrayPtrFlat<CParserParam>(4);
	CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfParams,arrayOfParams));
	
	// Create content disposition parameter
	CParserParam* dispositionParam = CParserParam::NewL(KVersitTokenVALUE, KNullDesC8);
	CleanupStack::PushL(dispositionParam);
	CCalContent::TDisposition disposition = aAlarmAction.Disposition();
	switch (disposition)
		{
		case CCalContent::EDispositionUrl:
			{
			dispositionParam->SetValueL(KVCalContentValueUrl);
			break;
			}
		case CCalContent::EDispositionInline:
			{
			dispositionParam->SetValueL(KVCalContentValueInline);
			break;
			}
		default:
			{
			// In the default case, the property is unsupported. So we'll clean up
			// and then return
			CleanupStack::PopAndDestroy(2); //arrayOfParams, dispositionParam
			return;
			}
		}
	arrayOfParams->AppendL(dispositionParam); // takes ownership
	CleanupStack::Pop(dispositionParam);

	// Create content MIME type parameter
	CParserParam* mimeTypeParam = CParserParam::NewL(KVersitTokenCONTENTTYPE, KNullDesC8);
	CleanupStack::PushL(mimeTypeParam);
	TPtrC8 mimeType(aAlarmAction.MimeType());
	mimeTypeParam->SetValueL(mimeType);
  	arrayOfParams->AppendL(mimeTypeParam);
  	CleanupStack::Pop(mimeTypeParam);
  	
  	
	// Construct X-EPOC-ALARM property and initialise with parameters and content
	CVersitExtendedAlarm* alarm = CVersitExtendedAlarm::NewL(aAlarmAction.Content(), aAlarmAction.MimeType(), static_cast<CVersitExtendedAlarm::TDisposition>(aAlarmAction.Disposition()));
	CleanupStack::PushL(alarm);
	CParserPropertyValue* value = new(ELeave)CParserPropertyValueExtendedAlarm(alarm);
	CleanupStack::Pop(alarm);
	CleanupStack::PushL(value);
	CParserProperty* prop = CParserProperty::NewL(*value, aToken, arrayOfParams);
	CleanupStack::Pop(2,arrayOfParams); // value, arrayOfParams

	// Add to the parser
	aParser.AddPropertyL(prop); // takes ownership of prop
	}

void CAgendaEntryToVCalConverter::AddAttachmentPropertyL(CVersitParser& aParser, CCalAttachment& aAttachment)
	{
	// Create an array of parameters for this property
	CArrayPtr<CParserParam>* arrayOfParams = new(ELeave)CArrayPtrFlat<CParserParam>(4);
	CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfParams,arrayOfParams));
	
	// Create value parameter
	CParserParam* valueType = CParserParam::NewL(KVersitTokenVALUE, KNullDesC8);
	CleanupStack::PushL(valueType);
	CCalAttachment::TType type = aAttachment.Type();
	TBool exportBinaryData = EFalse;
	switch (type)
		{
		case CCalAttachment::EUri:
			{
			valueType->SetValueL(KVCalContentValueUrl);
			break;
			}
		case CCalAttachment::EFile:
			{
			if (aAttachment.FileAttachment()->ContentId().Length() > 0)
				{
				valueType->SetValueL(KVCalContentValueContentId);
				}
			else if (aAttachment.IsAttributeSet(CCalAttachment::EExportInline))
				{
				valueType->SetValueL(KVCalContentValueInline);
				exportBinaryData = ETrue;
				}
			break;
			}
		default:
			// Do nothing. In this case the value will not be set
			break;
		}
	if (valueType->Value().Length() == 0)
		{
		// The property is unsupported. So we'll clean up and then return
		CleanupStack::PopAndDestroy(2, arrayOfParams); //arrayOfParams, valueType
		return;
		}
	arrayOfParams->AppendL(valueType); // takes ownership
	CleanupStack::Pop(valueType);

	// Create content MIME type parameter
	TPtrC8 mimeType(aAttachment.MimeType());
	if(mimeType != KNullDesC8)
		{
		CParserParam* mimeTypeParam = CParserParam::NewL(KVersitAttachMimeType, mimeType);
		CleanupStack::PushL(mimeTypeParam);
	  	arrayOfParams->AppendL(mimeTypeParam);
	  	CleanupStack::Pop(mimeTypeParam);
		}
  
  	const TPtrC fileName(aAttachment.Label());
  	if(fileName != KNullDesC)
  		{
		HBufC8* filenameUtf8 = HBufC8::NewLC(2 * fileName.Length()); // need room to convert from 16 bit to 8 bit (x2)
		TPtr8 pfileName(filenameUtf8->Des());
		User::LeaveIfError(CnvUtfConverter::ConvertFromUnicodeToUtf8(pfileName, fileName));					 
		CParserParam* fileNameParameter = CParserParam::NewL(KVersitAttachLabel, pfileName);
		CleanupStack::PopAndDestroy(filenameUtf8);  
		CleanupStack::PushL(fileNameParameter);
	  	arrayOfParams->AppendL(fileNameParameter);
	  	CleanupStack::Pop(fileNameParameter);
  		}
	 	// Create File Date parameter
	TTime lastModifiedFileTime = Time::NullTTime();
	if (aAttachment.FileAttachment())
		{
		lastModifiedFileTime = aAttachment.FileAttachment()->LastModifiedTimeUtc();
		}
	if(lastModifiedFileTime != Time::NullTTime())
		{
		_LIT(KStringFormat, "%F%Y%M%DT%H%T%SZ"); // locale-independent formatting YYMMDD"T"HHMMSS"Z" (day,month,year,hour,minute,second)
		TBuf<KMaxTimeStringSize> dateString;
		lastModifiedFileTime.FormatL(dateString,KStringFormat);
		TBuf8<KMaxTimeStringSize> dateString8;
		dateString8.Copy(dateString);
		CParserParam* fileDate = CParserParam::NewL(KVCalAttachFileDate, dateString8);
		CleanupStack::PushL(fileDate);
	  	arrayOfParams->AppendL(fileDate);
	  	CleanupStack::Pop(fileDate);
		}
	  	
  	CParserPropertyValue* propertyValue = NULL;

 	if (exportBinaryData)
 		{
 		RFile file;
	 	TRAPD(err,aAttachment.FileAttachment()->FetchFileHandleL(file));
		if(err == KErrNone)
			{
			CleanupClosePushL(file);
			propertyValue = CParserPropertyValueBinaryFile::NewL(file);
			CleanupStack::PopAndDestroy(&file);
			}
		else if(err == KErrArgument)
			{//FetchFileHandleL leaves with KErrArgument if there is no file handle has been set, eg. the entry hasn't been saved to Calendar file which isn't common user case
			TRAP(err, aAttachment.FileAttachment()->LoadBinaryDataL());

			if(err != KErrNone)
				{//The user can keep synching if, e.g. it is out of memory, or the attachment file is on a drive where the media has been removed, or the attachment has been deleted from the calendar store by other clients.
				CleanupStack::PopAndDestroy(arrayOfParams);	
				return;
				}

			propertyValue = CParserPropertyValueBinary::NewL(aAttachment.Value());
			}
		else
			{
			User::Leave(err);
			}
		}
 	else 
 		{
 		TPtrC8 attach8;
 		if (type == CCalAttachment::EFile)
 			{
 			attach8.Set(aAttachment.FileAttachment()->ContentId());	
 			}
 		else
 			{
 			attach8.Set(aAttachment.Value());	
 			}
 		
 		HBufC* attach16 = HBufC::NewLC(attach8.Length());
		attach16->Des().Copy(attach8);
 		propertyValue = CParserPropertyValueHBufC::NewL(attach16->Des());
		CleanupStack::PopAndDestroy(attach16);		
  		}
	CleanupStack::PushL(propertyValue);
	CParserProperty* property = CParserProperty::NewL(*propertyValue, KVersitTokenATTACH, arrayOfParams);
	aParser.AddPropertyL(property);
	CleanupStack::Pop(2,arrayOfParams);		// propertyValue, arrayOfParams
	}

void CAgendaEntryToVCalConverter::AddAttendeeParameterL(CArrayPtr<CParserParam>* aArrayOfParams, CCalAttendee* aAttendee)
	{
	// Create an array of paramaters for this property
	CParserParam* expectParam = CParserParam::NewL(KVCalAttendee8EXPECT, KNullDesC8);
	CleanupStack::PushL(expectParam);
	switch (aAttendee->VCalExpect())
		{
		case CCalAttendee::EVCalFyi:
			expectParam->SetValueL(KVCalAttendeeExpect8FYI);
			break;
		case CCalAttendee::EVCalRequire:
			expectParam->SetValueL(KVCalAttendeeExpect8REQUIRE);
			break;
		case CCalAttendee::EVCalRequest:
			expectParam->SetValueL(KVCalAttendeeExpect8REQUEST);
			break;
		case CCalAttendee::EVCalImmediate:
			expectParam->SetValueL(KVCalAttendeeExpect8IMMEDIATE);
			break;
		default:
			break;
		}
	aArrayOfParams->AppendL(expectParam); // takes ownership
	CleanupStack::Pop(expectParam); 
	
	CParserParam* rsvpParam = CParserParam::NewL(KVCalAttendee8RSVP, KNullDesC8);
	CleanupStack::PushL(rsvpParam);
	switch (aAttendee->ResponseRequested())
		{
		case ETrue:
			rsvpParam->SetValueL(KVCalAttendeeRsvp8YES);
			break;
		case EFalse:
			rsvpParam->SetValueL(KVCalAttendeeRsvp8NO);
			break;
		default:
			break;
		}
	aArrayOfParams->AppendL(rsvpParam); // takes ownership
	CleanupStack::Pop(rsvpParam); 
	
	CParserParam* calroleParam = CParserParam::NewL(KICalAttendeeCalRole8, KNullDesC8);
	CleanupStack::PushL(calroleParam);
	
	CParserParam* vcalroleParam = CParserParam::NewL(KICalAttendeeCalRole8, KNullDesC8);
	CleanupStack::PushL(vcalroleParam);
	
	switch (aAttendee->RoleL())
		{
		case CCalAttendee::EChair:
			calroleParam->SetValueL(KICalAttendeeCalRole8CHAIR);
			break;
		case CCalAttendee::EReqParticipant:
			calroleParam->SetValueL(KICalAttendeeCalRole8REQUIRED);
			break;
		case CCalAttendee::EOptParticipant: 
			calroleParam->SetValueL(KICalAttendeeCalRole8OPTIONAL);
			break;
		case CCalAttendee::ENonParticipant:
			calroleParam->SetValueL(KICalAttendeeCalRole8NONPARTICIPANT);
			break;
			
		case CCalAttendee::EVCalAttendee:
			vcalroleParam->SetValueL(KVCalAttendeeRole8ATTENDEE);
			break;
		case CCalAttendee::EVCalDelegate:
			vcalroleParam->SetValueL(KVCalAttendeeRole8DELEGATE);
			break;
		case CCalAttendee::EVCalOwner:
			vcalroleParam->SetValueL(KVCalAttendeeRole8OWNER);
			break;
		default:
			break;
		}
	if (calroleParam->Value().Length() > 0)
		{
		aArrayOfParams->AppendL(calroleParam); // takes ownership
		CleanupStack::PopAndDestroy(vcalroleParam); // delete the vCal role as it is not used
		CleanupStack::Pop(calroleParam);
		}
	else
		{
		aArrayOfParams->AppendL(vcalroleParam); // takes ownership
		CleanupStack::Pop(vcalroleParam);
		CleanupStack::PopAndDestroy(calroleParam); // delete the iCal role as it is not used
		}
	
	CParserParam* calStatusParam = CParserParam::NewL(KICalAttendeeCalStatus8, KNullDesC8);
	CleanupStack::PushL(calStatusParam);
	
	CParserParam* vcalStatusParam = CParserParam::NewL(KVCalAttendee8STATUS, KNullDesC8);
	CleanupStack::PushL(vcalStatusParam);
	
	switch (aAttendee->StatusL())
		{
		case CCalAttendee::EAccepted:
			calStatusParam->SetValueL(KVCalAttendeeStatus8ACCEPTED);
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8ACCEPTED);
			break;
		case CCalAttendee::ENeedsAction:
			calStatusParam->SetValueL(KICalAttendeeCalStatus8NEEDSACTION);
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8NEEDSACTION);
			break;
		case CCalAttendee::ETentative:
			calStatusParam->SetValueL(KVCalAttendeeStatus8TENTATIVE);
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8TENTATIVE);
			break;
		case CCalAttendee::EConfirmed:
			calStatusParam->SetValueL(KVCalAttendeeStatus8CONFIRMED);
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8CONFIRMED);
			break;
		case CCalAttendee::EDeclined:
			calStatusParam->SetValueL(KVCalAttendeeStatus8DECLINED);
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8DECLINED);
			break;
		case CCalAttendee::ECompleted:
			calStatusParam->SetValueL(KVCalAttendeeStatus8COMPLETED);
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8COMPLETED);
			break;
		case CCalAttendee::EDelegated:
			calStatusParam->SetValueL(KVCalAttendeeStatus8DELEGATED);
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8DELEGATED);
			break;
		case CCalAttendee::EInProcess:
			calStatusParam->SetValueL(KICalAttendeeCalStatus8INPROCESS);
			break;
		case CCalAttendee::EVCalSent:
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8SENT);
			break;
		case CCalAttendee::EVCalXReceived:
			vcalStatusParam->SetValueL(KVCalAttendeeStatus8XDASHRECEIVED);
			break;
		default:
			break;
		}
	
	if (vcalStatusParam->Value().Length() > 0)
		{
		aArrayOfParams->AppendL(vcalStatusParam); // takes ownership
		CleanupStack::Pop(vcalStatusParam);
		}
	else
		{
		CleanupStack::PopAndDestroy(vcalStatusParam); 
		}
		
	if (calStatusParam->Value().Length() > 0)
		{
		aArrayOfParams->AppendL(calStatusParam); // takes ownership
		CleanupStack::Pop(calStatusParam); 
		}
	else
		{
		CleanupStack::PopAndDestroy(calStatusParam); 
		}
	}

void CAgendaEntryToVCalConverter::AddAttendeePropertyL(CVersitParser& aParser, CCalUser* aAttendee, TBool isPhoneOwner, TBool isOrganizer)
	{
	CArrayPtr<CParserParam>* arrayOfParams = new(ELeave)CArrayPtrFlat<CParserParam>(9);
	CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfParams,arrayOfParams));

	if( aAttendee->SentBy() != KNullDesC)
		{
		CParserParam* sentByParam = CParserParam::NewL(KICalAttendeeSentBy8, aAttendee->SentBy());
		CleanupStack::PushL(sentByParam);
		arrayOfParams->AppendL(sentByParam);
		CleanupStack::Pop(sentByParam);	
		}	

	if( aAttendee->CommonName() != KNullDesC)
		{
		CParserParam* commonNameParam = CParserParam::NewL(KICalAttendeeCommonName8, aAttendee->CommonName());
		CleanupStack::PushL(commonNameParam);
		arrayOfParams->AppendL(commonNameParam);	// takes ownership
		CleanupStack::Pop(commonNameParam);	
		}
	
	if(isPhoneOwner)
		{
		CParserParam* phoneOwnerParam = CParserParam::NewL(KICalAttendee8XDASHPHONEOWNER, KNullDesC8);
		CleanupStack::PushL(phoneOwnerParam);
		arrayOfParams->AppendL(phoneOwnerParam);	// takes ownership
		CleanupStack::Pop(phoneOwnerParam);			
		}
		
	if (isOrganizer)
		{
		CParserParam* organizerParam = CParserParam::NewL(KVCalAttendee8ROLE, KVCalAttendeeRole8ORGANIZER);
		CleanupStack::PushL(organizerParam);
		arrayOfParams->AppendL(organizerParam);	// takes ownership
		CleanupStack::Pop(organizerParam);
		}
	else
		{
		// these properties are not supported in CCalUser so don't need to be exported for an organizer
		AddAttendeeParameterL(arrayOfParams,static_cast<CCalAttendee*>(aAttendee));
		}
				
	CParserPropertyValue* value = CParserPropertyValueHBufC::NewL(aAttendee->Address());
	CleanupStack::PushL(value);
	CParserProperty* prop = CParserProperty::NewL(*value, KVersitTokenATTENDEE, arrayOfParams);
	// Add to the parser
	aParser.AddPropertyL(prop);
	CleanupStack::Pop(2,arrayOfParams);		// value, arrayOfParams
	}



void CAgendaEntryToVCalConverter::AddRepeatPropertiesL(CVersitParser& aParser, TCalRRule& aRpt, CTzRules* aEntryTzRule)
	{
	CVersitRecurrence* recurrence = NULL;
	TCalRRule::TType rType = aRpt.Type();

	TVersitDateTime* endTime = NULL;
	TUint duration = 0;
	if (aRpt.Count() != 0)
		{
		TTime untilTime;
		if(aEntryTzRule)
			{
			untilTime = aRpt.Until().TimeUtcL();
			//convert utc to repeat local
			aEntryTzRule->ConvertToLocalL(untilTime);
			}
		else
			{//use system local
			untilTime = aRpt.Until().TimeLocalL();
			}
		TDateTime end=untilTime.DateTime();
		endTime = new(ELeave) TVersitDateTime(end, TVersitDateTime::EIsVCardLocal);
		endTime->SetFlag(TVersitDateTime::EExportLeaveAsLocalTime);
 		duration = aRpt.Count();
		}
	CleanupStack::PushL(endTime);

	switch (aRpt.Type())
		{
		// Daily Repeat
		case TCalRRule::EDaily:
			recurrence = new(ELeave)CVersitRecurrenceDaily(aRpt.Interval(), duration, endTime);
			break;
		// Weekly Repeat
		case TCalRRule::EWeekly:
			recurrence = CreateWeeklyRepeatL(aRpt, duration, endTime);
			break;
		// Monthly By Days Repeat
		case TCalRRule::EMonthly:
			recurrence = CreateMonthlyRepeatL(aRpt, duration, endTime);
			break;
		// Yearly By Day Repeat
		case TCalRRule::EYearly:
			recurrence = CreateYearlyRepeatL(aRpt, duration, endTime);
			break;
		default:
			delete endTime;
			break;
		}
	CleanupStack::Pop();	// endTime (owned by recurrence)

	if (recurrence)
		{
		CleanupStack::PushL(recurrence);
		CParserPropertyValue* value = new(ELeave)CParserPropertyValueRecurrence(recurrence);	// takes ownership
		CleanupStack::Pop();		// recurrence
		CleanupStack::PushL(value);
		CParserProperty* prop = CParserProperty::NewL(*value, KVersitTokenRRULE, NULL);
		CleanupStack::Pop(value);

		// Add to the parser
		aParser.AddPropertyL(prop);
		}
	}

CVersitRecurrence* CAgendaEntryToVCalConverter::CreateWeeklyRepeatL(TCalRRule& aRpt,TInt aDuration, TVersitDateTime* aEndTime)
	{
	// Convert the agenda weekly repeat to a versit recurrence

	CWeekDayArray* dayArray = new(ELeave)CWeekDayArray();
	CleanupStack::PushL(dayArray);
	dayArray->iArray = new(ELeave)CArrayFixFlat<TDay>(1);

	// Iterate through the days to check which days are set
	RArray<TDay> daysinweek;
	CleanupClosePushL(daysinweek);
	aRpt.GetByDayL(daysinweek);
	TInt count=daysinweek.Count();
	for (TInt ii=0; ii<count; ++ii)
		{
		dayArray->iArray->AppendL(daysinweek[ii]);
		}
	CleanupStack::PopAndDestroy(&daysinweek);
	CVersitRecurrence* recurrence = new(ELeave)CVersitRecurrenceWeekly(aRpt.Interval(), aDuration, aEndTime, dayArray); // takes ownership
	CleanupStack::Pop(dayArray);

	return recurrence;
	}
	
CVersitRecurrence* CAgendaEntryToVCalConverter::CreateMonthlyRepeatL(TCalRRule& aRpt, TInt aDuration, TVersitDateTime* aEndTime)
	{
	// Convert Agenda Monthly By Days repeat to a versit recurrence
	RArray<TCalRRule::TDayOfMonth> days;
	aRpt.GetByDayL(days);
	TInt count=days.Count();
	if(count>0)	
		{//e.g. every Monday of first week and every Friday of the third week.
		CleanupClosePushL(days);
		CArrayPtrFlat<CVersitRecurrenceMonthlyByPos::CMonthPosition>* monthPositions = new(ELeave)CArrayPtrFlat<CVersitRecurrenceMonthlyByPos::CMonthPosition>(1);
		CleanupStack::PushL(monthPositions);

		// Iterate through the five weeks for the month
		// (First,Second,Third,Fourth,Last)
				
		for (TInt ii=0; ii<count; ++ii)
			{
			CVersitRecurrenceMonthlyByPos::CMonthPosition* monthPos = new(ELeave)CVersitRecurrenceMonthlyByPos::CMonthPosition();
			CleanupStack::PushL(monthPos);
			
			TInt8 weekNo = days[ii].WeekInMonth();
			if(weekNo>0)
				{
				monthPos->iSign = CVersitRecurrenceMonthlyByPos::CMonthPosition::EWeeksFromStartOfMonth;
				monthPos->iWeekNo = weekNo;	
				}
			else
				{
				monthPos->iSign = CVersitRecurrenceMonthlyByPos::CMonthPosition::EWeeksFromEndOfMonth;
				monthPos->iWeekNo = Abs(weekNo);
				}
			monthPos->iArrayOfWeekDays = new(ELeave)CWeekDayArray();
			monthPos->iArrayOfWeekDays->iArray = new(ELeave)CArrayFixFlat<TDay>(1);
			monthPos->iArrayOfWeekDays->iArray->AppendL(days[ii].Day());
			monthPositions->AppendL(monthPos);
			CleanupStack::Pop();		// monthPos;
			}

		CVersitRecurrence* recurrence = new(ELeave)CVersitRecurrenceMonthlyByPos(aRpt.Interval(), aDuration, aEndTime, monthPositions);
		CleanupStack::Pop();	// monthPositions (owned by recurrence)
		CleanupStack::PopAndDestroy(&days);
		return recurrence;
		}
	else
		{
		RArray<TInt> dayofmonth;
		aRpt.GetByMonthDayL(dayofmonth);
		TInt count=dayofmonth.Count();
		if(count>0)
			{
			CleanupClosePushL(dayofmonth);
			CArrayFixFlat<TInt>* dayList = new(ELeave)CArrayFixFlat<TInt>(1);
			CleanupStack::PushL(dayList);

			// Iterate through each day in the month

			for (TInt ii=0; ii<count; ++ii)
				{
				dayList->AppendL(dayofmonth[ii]+1);  // add 1, since 0 means 1st
				}

			CVersitRecurrence* recurrence = new(ELeave)CVersitRecurrenceMonthlyByDay(aRpt.Interval(), aDuration, aEndTime, dayList, NULL, EFalse);

			CleanupStack::Pop();		// dayList;
			
			CleanupStack::PopAndDestroy(&dayofmonth);
			return recurrence;
			}
		}
	return NULL;
	}

CVersitRecurrence* CAgendaEntryToVCalConverter::CreateYearlyRepeatL(TCalRRule& aRpt, TInt aDuration, TVersitDateTime* aEndTime)
	{
	// Convert Agenda Yearly By Days repeat to a versit recurrence

	// There is no vCal equivalent of a yearly 'by day' repeat,
	// e.g. repeat on the first monday of october,
	// so a monthly repeat with an interval of 12* the yearly repeat
	// is used instead
	RArray<TCalRRule::TDayOfMonth> days;
	aRpt.GetByDayL(days);
	TInt count=days.Count();
	if(count>0)
		{
		CleanupClosePushL(days);
		CArrayPtrFlat<CVersitRecurrenceMonthlyByPos::CMonthPosition>* monthPositions = new(ELeave)CArrayPtrFlat<CVersitRecurrenceMonthlyByPos::CMonthPosition>(1);
		CleanupStack::PushL(monthPositions);

		// Iterate through the five weeks for the month
		// (First,Second,Third,Fourth,Last)
				
		for (TInt ii=0; ii<count; ++ii)
			{
			CVersitRecurrenceMonthlyByPos::CMonthPosition* monthPos = new(ELeave)CVersitRecurrenceMonthlyByPos::CMonthPosition();
			CleanupStack::PushL(monthPos);
			monthPos->iSign = CVersitRecurrenceMonthlyByPos::CMonthPosition::EWeeksFromStartOfMonth;
			monthPos->iWeekNo = days[ii].WeekInMonth(); // iWeekNo goes from 1-5
		
			monthPos->iArrayOfWeekDays = new(ELeave)CWeekDayArray();
			monthPos->iArrayOfWeekDays->iArray = new(ELeave)CArrayFixFlat<TDay>(1);
			monthPos->iArrayOfWeekDays->iArray->AppendL(days[ii].Day());
			monthPositions->AppendL(monthPos);
			CleanupStack::Pop();		// monthPos;
			}
		// Interval * 12, since we really want is to repeat yearly
		CVersitRecurrence* recurrence = new(ELeave)CVersitRecurrenceMonthlyByPos(aRpt.Interval() * 12, aDuration, aEndTime, monthPositions);
		CleanupStack::Pop();	// monthPositions (owned by recurrence)
		CleanupStack::PopAndDestroy(&days);//close the array

		return recurrence;
		}
	else
		{
		CArrayFixFlat<TMonth>* monthList = new(ELeave)CArrayFixFlat<TMonth>(1);
		CleanupStack::PushL(monthList);
		monthList->AppendL(aRpt.DtStart().TimeLocalL().DateTime().Month());
		CVersitRecurrence* recurrence = new(ELeave)CVersitRecurrenceYearlyByMonth(aRpt.Interval(), aDuration, aEndTime, monthList);
		CleanupStack::Pop();	// monthList
		return recurrence;
		}
	}

void CAgendaEntryToVCalConverter::AddRDatePropertyL(CVersitParser& aParser, RArray<TCalTime>& aRdates)
	{
	CArrayPtrFlat<TVersitDateTime>* dateList = new (ELeave) CArrayPtrFlat<TVersitDateTime>(4);
	CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfVersitDateTime,dateList));
	
	const TInt KCount(aRdates.Count());

	for (TInt i = 0; i < KCount; ++i)
		{
		TCalTime sporadicDate = aRdates[i];
		TDateTime rDate;
		if (iTimeType == TVersitDateTime::EIsUTC)
			{
			rDate=sporadicDate.TimeUtcL().DateTime();
			}
		else
			{
			rDate=sporadicDate.TimeLocalL().DateTime();
			}
		
		// Construct a versit date-time property	
		TVersitDateTime* versitDateTime = new(ELeave) TVersitDateTime(rDate, iTimeType);
		CleanupStack::PushL(versitDateTime);
		versitDateTime->SetFlag(iTimeFlag);	
		dateList->AppendL(versitDateTime);
		CleanupStack::Pop(); // versitDateTime
		}
	
	CParserPropertyValue* value = new(ELeave)CParserPropertyValueMultiDateTime(dateList);
	CleanupStack::Pop(dateList);
	CleanupStack::PushL(value);
	
	CParserProperty* property = CParserProperty::NewL(*value, KVersitTokenRDATE, NULL);
	CleanupStack::Pop(value);
	
	// Add to the parser
	aParser.AddPropertyL(property);//aParser takes the ownership
	}

// Exception property
//
void CAgendaEntryToVCalConverter::AddRepeatExceptionPropertiesL(CVersitParser& aParser, RArray<TCalTime>& aExceptions)
	{
	CArrayPtrFlat<TVersitDateTime>* dateList = new(ELeave)CArrayPtrFlat<TVersitDateTime>(1);
	CleanupStack::PushL(TCleanupItem(ResetAndDestroyArrayOfVersitDateTime, dateList));
	
	TInt size = aExceptions.Count();

	for (TInt count=0; count<size; count++)
		{
		TCalTime exception = aExceptions[count];
		
		TDateTime date;
		if (iTimeType == TVersitDateTime::EIsUTC)
			{
			date = exception.TimeUtcL().DateTime();
			}
		else
			{
			date = exception.TimeLocalL().DateTime();
			}

		// Construct a date-time property

		TVersitDateTime* versitDateTime = new(ELeave) TVersitDateTime(date, iTimeType);
 		versitDateTime->SetFlag(iTimeFlag);
 		CleanupStack::PushL(versitDateTime);
		dateList->AppendL(versitDateTime);
		CleanupStack::Pop(); // versitDateTime 
		}

	CParserPropertyValue* value = new(ELeave)CParserPropertyValueMultiDateTime(dateList);
	CleanupStack::Pop();		// dateList

	CleanupStack::PushL(value);
	CParserProperty* prop = CParserProperty::NewL(*value, KVersitTokenEXDATE, NULL);
	CleanupStack::Pop(value);
	// Add to the parser
	aParser.AddPropertyL(prop);
	}

// Creates a stream dictionary, from an embedded store and a stream ID 
//
CStreamDictionary* CAgendaEntryToVCalConverter::CreateDictionaryLC(CEmbeddedStore& aEmbeddedStore, TStreamId& aId)
	{
	CStreamDictionary* dictionary = CStreamDictionary::NewLC();
	RStoreReadStream dicStream;
	dicStream.OpenLC(aEmbeddedStore, aId);
	dicStream >> *dictionary;
	CleanupStack::PopAndDestroy(); //dicStream
	return dictionary;
	}


// The vCalendar CATEGORIES property has multiple property values so the property values
// are stored in an array.
//
// The categories of the entry are iterated through, and each category is added as a 
// property value to the array. 
//
void CAgendaEntryToVCalConverter::AddCategoryPropertyL(CVersitParser& aParser, const RPointerArray<CCalCategory>& aCategories)
	{
	CDesCArrayFlat* desArray = new(ELeave) CDesCArrayFlat(4);
	CleanupStack::PushL(desArray);

	TInt categories = aCategories.Count();
	for (TInt count=0; count<categories; count++)
		{
		CCalCategory* category = aCategories[count];
		switch (category->Category())
			{
		case CCalCategory::ECalAppointment:
				desArray->AppendL(KVCalCategoriesAPPOINTMENT);
				break;
		case CCalCategory::ECalBusiness:
				desArray->AppendL(KVCalCategoriesBUSINESS);
				break;
		case CCalCategory::ECalEducation:
				desArray->AppendL(KVCalCategoriesEDUCATION);
				break;
		case CCalCategory::ECalHoliday:
				desArray->AppendL(KVCalCategoriesHOLIDAY);
				break;
		case CCalCategory::ECalMeeting:
				desArray->AppendL(KVCalCategoriesMEETING);
				break;
		case CCalCategory::ECalMiscellaneous:
				desArray->AppendL(KVCalCategoriesMISCELLANEOUS);
				break;
		case CCalCategory::ECalPersonal:
				desArray->AppendL(KVCalCategoriesPERSONAL);
				break;
		case CCalCategory::ECalPhoneCall:
				desArray->AppendL(KVCalCategoriesPHONECALL);
				break;
		case CCalCategory::ECalSickDay:
				desArray->AppendL(KVCalCategoriesSICKDAY);
				break;
		case CCalCategory::ECalSpecialOccasion:
				desArray->AppendL(KVCalCategoriesSPECIALOCCASION);
				break;
		case CCalCategory::ECalTravel:
				desArray->AppendL(KVCalCategoriesTRAVEL);
				break;
		case CCalCategory::ECalVacation:
				desArray->AppendL(KVCalCategoriesVACATION);
				break;
		case CCalCategory::ECalExtended:
				desArray->AppendL(category->ExtendedCategoryName());
				break;
		default:
				break;
			}
		}

    CParserPropertyValue* value = new (ELeave) CParserPropertyValueCDesCArray(desArray);
	CleanupStack::Pop(); //desArray
	
	CleanupStack::PushL(value); 
	CParserProperty* prop = CParserProperty::NewL(*value, KVersitTokenCATEGORIES, NULL);
	CleanupStack::Pop(value); 
	// Add to the parser
	aParser.AddPropertyL(prop);
	}

/**
 * Checks the status value and adds the appropriate status value to the export list 
 * held by the versit parser. 
 */
void CAgendaEntryToVCalConverter::AddStatusPropertyL(CVersitParser& aParser, CCalEntry* aEntry)
	{
	switch(aEntry->StatusL())
		{
		case CCalEntry::ETodoInProcess:
		case CCalEntry::EVCalAccepted:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusACCEPTED);			
			break;
		case CCalEntry::ETodoNeedsAction:
		case CCalEntry::EVCalNeedsAction:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusNEEDSACTION);			
			break;
		case CCalEntry::ETodoCompleted:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusCOMPLETED);			
			break;
			
		case CCalEntry::EConfirmed:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusCONFIRMED);			
			break;
		case CCalEntry::ECancelled:
		case CCalEntry::EVCalDeclined:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusDECLINED);			
			break;
		case CCalEntry::ETentative:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusTENTATIVE);			
			break;
		case CCalEntry::EVCalSent:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusSENT);			
			break;
		case CCalEntry::EVCalDelegated:
			AddDesPropertyL(aParser, KVCalTokenSTATUS, KVCalStatusDELEGATED);			
			break;
		}

	}