calendarengines/caldav/src/caldavengine.cpp
author Pat Downey <patd@symbian.org>
Wed, 27 Oct 2010 15:48:34 +0100
branchRCL_3
changeset 87 6d3f83773253
parent 74 97232defd20e
permissions -rw-r--r--
Restore caldav module from 356f28cd5ca0 (Revision 201035,Kit 201036). Bug 3883.

/*
 * Copyright (c) 2010 Sun Microsystems, Inc. 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 Contributor:
 * Maximilian Odendahl
 *
 * Contributors:
 * 
 * Description:  main Caldav class, all magic happens here 
 *				one engine for each configured Caldav calendar
 */
#include "caldavengine.h"
#include <f32file.h> 
#include <S32FILE.H>
#include <S32MEM.H> 
#include <avkon.rsg>
#include <UTF.H>
#include <agnexportobserver.h>
#include <calsession.h>
#include <caliterator.h>
#include <calentry.h>
#include <calentryview.h>
#include <caltime.h>
#include <calalarm.h>
#include <calcategory.h>
#include <caluser.h>
#include <calrrule.h>
#include <calinstance.h>
#include <calinstanceview.h>
#include <caleninterimutils2.h> 
#include <calenexporter.h>
#include <calenimporter.h>
#include <calcalendarinfo.h>
#include "calenglobaldata.h"
#include <xmlengdocument.h>
#include <xmlengnode.h>
#include <xmlengelement.h>
#include <xmlengnodelist.h>
#include <xmlengtext.h>
#include "httpclient.h"
#include <e32math.h>	// divmod
#define MULTIGETSPLIT 			100
#define ARRAYEXPAND				4
#define	URLMAX					500
#define EXPANDSIZE_SMALL		512
#define EXPANDSIZE_BIG			1024
_LIT(KFormatString,"\"%F%Y%M%DT000000Z\"");

/**
 * SyncTickL
 * timer for basic sync interval
 */
TInt SyncTickL(TAny *aObject)
	{
	CCalDavEngine* engine = ((CCalDavEngine*) aObject);
	CPeriodic* timer = engine->Timer();
	if (engine && timer)
		{
		timer->Cancel();
		TRAP_IGNORE(engine->SyncL());
		TInt ticktime = engine->SyncInterval().Int() == 0 ? 1
				: engine->SyncInterval().Int();
		const TUint64 tickInterval = 1000000 * 60 * ticktime;
		timer->Start(tickInterval, tickInterval, TCallBack(SyncTickL, engine));
		}
	return 0;
	}

#ifdef _DEBUG
void ExportToFileNameL(const TDesC8& aDes, const TDesC &aFilename)
	{
	RFs fsSession;
	User::LeaveIfError(fsSession.Connect());
	CleanupClosePushL(fsSession);

	TInt err = fsSession.MkDirAll(aFilename);

	RFile file;
	User::LeaveIfError(file.Replace(fsSession, aFilename, EFileWrite));
	CleanupClosePushL(file);

	RFileWriteStream writeStream2;
	writeStream2.Attach(file);
	CleanupClosePushL(writeStream2);

	writeStream2 << aDes;
	writeStream2.CommitL();
	CleanupStack::PopAndDestroy(&writeStream2);
	CleanupStack::PopAndDestroy(&file);
	CleanupStack::PopAndDestroy(&fsSession);
	}

void ExportToFileL(CCalEntry* aEntry, CCalenExporter *iCalExporter)
	{
	if (aEntry)
		{
		_LIT(KFileName, "C:\\logs\\caldav\\testing_export.txt");

		RFs fsSession;
		User::LeaveIfError(fsSession.Connect());
		CleanupClosePushL(fsSession);

		TInt err = fsSession.MkDirAll(KFileName);

		RFile file;
		User::LeaveIfError(file.Replace(fsSession, KFileName, EFileWrite));
		CleanupClosePushL(file);

		RFileWriteStream writeStream2;
		writeStream2.Attach(file);
		CleanupClosePushL(writeStream2);

		iCalExporter->ExportICalL(*aEntry, writeStream2);
		writeStream2.CommitL();
		CleanupStack::PopAndDestroy(&writeStream2);
		CleanupStack::PopAndDestroy(&file);
		CleanupStack::PopAndDestroy(&fsSession);
		}
	}
#endif

/**
 * SearchL
 * search inside xml tree for a specific node
 */
void SearchL(TXmlEngNode &aTopNode, const TDesC8 &aName,
		const TDesC8 &aNamespace, TXmlEngNode & aReturnNode)
	{
	RXmlEngNodeList<TXmlEngNode> List;
	aTopNode.GetChildNodes(List);
	while (List.HasNext() && aReturnNode.IsNull())
		{
		TXmlEngNode Node = List.Next();
		TXmlEngNode::TXmlEngDOMNodeType Type = Node.NodeType();
		if (Type == TXmlEngNode::EElement)
			{
			//todo: compare namespace ?
			if (Node.Name() == aName)
				{
				aReturnNode = Node.CopyL();
				return;
				}
			else
				{
				SearchL(Node, aName, aNamespace, aReturnNode);
				if (!aReturnNode.IsNull())
					return;
				}
			}
		}
	}

/**
 * SearchL
 * search inside xml tree for a specific node
 */
void SearchL(TXmlEngNode &aTopNode, const TDesC8 &aName,
		const TDesC8 &aNamespace, RBuf8 &aBuf)
	{
	RXmlEngNodeList<TXmlEngNode> List;
	aTopNode.GetChildNodes(List);
	while (List.HasNext())
		{
		TXmlEngNode Node = List.Next();
		TXmlEngNode::TXmlEngDOMNodeType Type = Node.NodeType();
		if (Type == TXmlEngNode::EElement)
			{
			//todo: compare namespace ?
			if (Node.Name() == aName)
				{
				if (Node.IsSimpleTextContents())
					aBuf.Create(Node.Value());
				else
					Node.WholeTextContentsCopyL(aBuf);
				return;
				}
			else
				{
				SearchL(Node, aName, aNamespace, aBuf);
				if (aBuf.Length())
					return;
				}
			}
		}
	}

/**
 * SearchL
 * search inside xml tree for a specific node
 */
TPtrC8 SearchL(TXmlEngNode &aTopNode, const TDesC8 &aName,
		const TDesC8 &aNamespace)
	{
	RXmlEngNodeList<TXmlEngNode> List;
	aTopNode.GetChildNodes(List);
	while (List.HasNext())
		{
		TXmlEngNode Node = List.Next();
		TXmlEngNode::TXmlEngDOMNodeType Type = Node.NodeType();
		if (Type == TXmlEngNode::EElement)
			{
			//todo: compare namespace ?
			if (Node.Name() == aName)
				return Node.Value();
			else
				{
				TPtrC8 Return = SearchL(Node, aName, aNamespace);
				if (Return != KNullDesC8)
					return Return;
				}
			}
		}
	return KNullDesC8();
	}

/**
 * GetBoolFromPropertiesL
 * get a boolean value from CCalCalendarInfo
 */
TBool GetBoolFromPropertiesL(CCalCalendarInfo* info, const TDesC8 &aKey)
	{
	TBool boolean;
	TPckgC<TBool> pckgboolean(boolean);
	pckgboolean.Set(info->PropertyValueL(aKey));
	return pckgboolean();
	}

/**
 * GetTimeFromPropertiesL
 * get a time value from CCalCalendarInfo
 */
TCalTime GetTimeFromPropertiesL(CCalCalendarInfo* info, const TDesC8 &aKey)
	{
	TCalTime time;
	TPckgC<TCalTime> pckgtime(time);
	pckgtime.Set(info->PropertyValueL(aKey));
	return pckgtime();
	}

/**
 * PropertyExists
 * finds a property from an array
 */
TBool PropertyExists(CDesC8Array* aArray, const TDesC8 &aKey)
	{
	TInt pos = 0;
	return aArray->Find(aKey, pos, ECmpNormal) == KErrNone;
	}

/**
 * CCalDavEngine::SetCalendarInfoL
 * set key and value pair at calendar session info
 */
void CCalDavEngine::SetCalendarInfoL(const TDesC8 &aKey, const TDesC8 &aValue)
	{
	TBool createdelete = !iCalSession;
	if (createdelete)
		{
		iCalSession = CCalSession::NewL();
		TRAP_IGNORE(iCalSession->OpenL(*iCalendar));
		}
	CCalCalendarInfo* calendarInfo = iCalSession->CalendarInfoL();
	CleanupStack::PushL(calendarInfo);
	TPckgC<TBool> enabled(iEnabled);
	calendarInfo->SetPropertyL(KCaldavEnabled, enabled);
	if (aValue != KNullDesC8)
		calendarInfo->SetPropertyL(aKey, aValue);
	iCalSession->SetCalendarInfoL(*calendarInfo);
	CleanupStack::PopAndDestroy(calendarInfo);

	if (createdelete)
		{
		delete iCalSession;
		iCalSession = NULL;
		}
	}

/**
 * CCalDavEngine::CCalDavEngine
 * default constructor
 */
CCalDavEngine::CCalDavEngine() :
	iCalSession(0), iCalIter(0), iCalEntryView(0), iCalExporter(0),
			iCalImporter(0), iCalIntermimUtils2(0), iCalendar(0), iUrl(0),
			iBaseUrl(0), iHome(0), iHttp(0), iSynctoken(0), iCTag(0),
			iSyncInterval(DEFAULT_SYNC_MINUTES), iPastDays(DEFAULT_PAST_DAYS),
			iImmediateSync(DEFAULT_IMMEDIATE_SYNC), iKeepServerEntry(
					DEFAULT_KEEP_SERVER_ENTRY), iEnabled(EFalse), iTimer(0)
	{
	}

/**
 * CCalDavEngine::~CCalDavEngine
 * default destructor
 */
CCalDavEngine::~CCalDavEngine()
	{
	iLocalUidArray.Close();
	iGlobalUidArray.Close();
	iDeletedEntries.Close();

	iDomParser.Close();
	iDomImpl.Close();

	if (iCalendar)
		delete iCalendar;

	if (iUrl)
		delete iUrl;

	if (iBaseUrl)
		delete iBaseUrl;

	if (iHttp)
		delete iHttp;

	DeleteCalObjects();

	if (iCalIntermimUtils2)
		delete iCalIntermimUtils2;

	if (iSynctoken)
		delete iSynctoken;

	if (iCTag)
		delete iCTag;

	if (iHome)
		delete iHome;

	if (iTimer)
		delete iTimer;
	}

/**
 * CCalDavEngine::NewLC
 * first phase construction
 */
CCalDavEngine* CCalDavEngine::NewLC(const TDesC& aCalendar)
	{
	CCalDavEngine* self = new (ELeave) CCalDavEngine();
	CleanupStack::PushL(self);
	self->ConstructL(aCalendar);
	return self;
	}

/**
 * CCalDavEngine::NewL
 * first phase construction
 */
CCalDavEngine* CCalDavEngine::NewL(const TDesC& aCalendar)
	{
	CCalDavEngine* self = CCalDavEngine::NewLC(aCalendar);
	CleanupStack::Pop(self); // self;
	return self;
	}

/**
 * CCalDavEngine::
 * second phase construction
 */
void CCalDavEngine::ConstructL(const TDesC& aCalendar)
	{
	iManualSync = EFalse;
	iFirstInit = ETrue;
	iCalendar = aCalendar.AllocL();

	iHttp = CHttpClient::NewL();

	iDomImpl.OpenL();
	TInt err = iDomParser.Open(iDomImpl);
	if (KErrNone != err)
		User::Leave(err);

	iTimer = CPeriodic::NewL(EPriorityNormal);
	iCalIntermimUtils2 = CCalenInterimUtils2::NewL();
	iCalSession = CCalSession::NewL();
	TRAP_IGNORE(iCalSession->OpenL(aCalendar));
	CalendarInfoL();
	// we can't close the file, so delete it completly
	delete iCalSession;
	iCalSession = NULL;
	}

/**
 * CCalDavEngine::Progress
 * Progress callback
 */
void CCalDavEngine::Progress(TInt /*aPercentageCompleted*/)
	{
	}

/**
 * CCalDavEngine::RegisterL
 * register all neccessary notification callback
 */
void CCalDavEngine::RegisterL()
	{
	TDateTime Start;
	TDateTime End;
	End.SetYear(2200);

	TCalTime StartCal;
	TCalTime EndCal;
	StartCal.SetTimeLocalL(Start);
	EndCal.SetTimeLocalL(End);
	CalCommon::TCalTimeRange Range(StartCal, EndCal);
	CCalChangeNotificationFilter *Filter = CCalChangeNotificationFilter::NewL(
			MCalChangeCallBack2::EChangeEntryAll, true, Range);
	iCalSession->StartChangeNotification(*this, *Filter);
	iCalSession->StartFileChangeNotificationL(*this);
	delete Filter;
	}

/**
 * CCalDavEngine::CalendarInfoL
 * load all properties from CalendarInfo
 */
void CCalDavEngine::CalendarInfoL()
	{
	CCalCalendarInfo* calendarInfo = iCalSession->CalendarInfoL();
	if (calendarInfo->IsValid())
		{
		CleanupStack::PushL(calendarInfo);
		CDesC8Array* propertyKeys = calendarInfo->PropertyKeysL();
		CleanupStack::PushL(propertyKeys);
		TInt pos = 0;
		if (propertyKeys->Find(KCaldavEnabled, pos, ECmpNormal) == KErrNone)
			{
			iEnabled = GetBoolFromPropertiesL(calendarInfo, KCaldavEnabled);
			if (PropertyExists(propertyKeys, KCaldavFirstInit))
				iFirstInit = GetBoolFromPropertiesL(calendarInfo,
						KCaldavFirstInit);
			if (PropertyExists(propertyKeys, KCaldavSynctoken))
				iSynctoken
						= calendarInfo->PropertyValueL(KCaldavSynctoken).AllocL();
			if (PropertyExists(propertyKeys, KCaldavCtag))
				iCTag = calendarInfo->PropertyValueL(KCaldavCtag).AllocL();
			if (PropertyExists(propertyKeys, KCaldavManualSync))
				iManualSync = GetBoolFromPropertiesL(calendarInfo,
						KCaldavManualSync);
			if (PropertyExists(propertyKeys, KCaldavTime))
				iLastSyncTime = GetTimeFromPropertiesL(calendarInfo,
						KCaldavTime);
			if (PropertyExists(propertyKeys, KCaldavUrl))
				iUrl = calendarInfo->PropertyValueL(KCaldavUrl).AllocL();
			if (PropertyExists(propertyKeys, KCaldavUser))
				iHttp->SetUserL(calendarInfo->PropertyValueL(KCaldavUser));
			if (PropertyExists(propertyKeys, KCaldavPassword))
				iHttp->SetPasswordL(calendarInfo->PropertyValueL(
						KCaldavPassword));
			if (PropertyExists(propertyKeys, KCaldavKeepServer))
				iKeepServerEntry = GetBoolFromPropertiesL(calendarInfo,
						KCaldavKeepServer);
			if (PropertyExists(propertyKeys, KCaldavImmediateSync))
				iImmediateSync = GetBoolFromPropertiesL(calendarInfo,
						KCaldavImmediateSync);
			if (PropertyExists(propertyKeys, KCaldavPastDays))
				{
				TPckgC<TTimeIntervalDays> pastdays(iPastDays);
				pastdays.Set(calendarInfo->PropertyValueL(KCaldavPastDays));
				iPastDays = pastdays();
				}
			if (PropertyExists(propertyKeys, KCaldavSyncInterval))
				{
				TPckgC<TTimeIntervalMinutes> syncinterval(iSyncInterval);
				syncinterval.Set(calendarInfo->PropertyValueL(
						KCaldavSyncInterval));
				iSyncInterval = syncinterval();
				}
			// access point	    
			}

		CleanupStack::PopAndDestroy(propertyKeys);
		CleanupStack::Pop(calendarInfo);
		}
	delete calendarInfo;
	}

/**
 * CCalDavEngine::InitL
 * check for correct url
 * load most Caldav url properties
 * do inital sync
 */
TInt CCalDavEngine::InitL()
	{
	// this is only needed to find a GlobalUID from a LocalUID, used after an event was deleted
	// also used now for uploading of local events when only GlobalUID is know 
	LocalLoopL(ELoopActionFillArray);

	if (iFirstInit)
		{
		TInt err = GetCalendarUrlsL(NULL);
		if (err == KErrArgument)
			return KErrArgument;
		GetOptionsL();
		SetLastSyncTimeL();

		TBool success;
		// get all server items
		if (iOptions.sync_collection)
			success = WebDavSyncL();
		else
			success = ListL() == KErrNone;

		if (!success)
			return KErrGeneral;

		// upload all local entries of this calendar to server
		LocalLoopL(ELoopActionUpload);

		if (iOptions.sync_collection)
			SetSyncTokenL(GetSyncTokenL());
		else
			SetCTagL(GetCTagL());

		iFirstInit = EFalse;
		iEnabled = ETrue;
		TPckgC<TBool> firstInit(iFirstInit);
		SetCalendarInfoL(KCaldavFirstInit, firstInit); // this will set iEnabled as well
		}
	else
		{
		TInt err = GetCalendarUrlsL(NULL);
		if (err == KErrArgument)
			return KErrArgument;
		GetOptionsL();
		SetLastSyncTimeL();
		iEnabled = ETrue;
		SetCalendarInfoL(KCaldavEnabled, KNullDesC8);
		SyncL();
		}

	return KErrNone;
	}

/**
 * CCalDavEngine::Completed
 * Completed callback
 */
void CCalDavEngine::Completed(TInt aError)
	{
	if (aError == KErrNone)
		{
		CActiveScheduler::Stop();
		}
	else
		iManualSync = true;
	}

/**
 * CCalDavEngine::NotifyProgress
 * NotifyProgress callback
 */
TBool CCalDavEngine::NotifyProgress()
	{
	return EFalse;
	}

/**
 * CCalDavEngine::CalChangeNotification
 * change item callback, sync to server
 */
void CCalDavEngine::CalChangeNotification(RArray<TCalChangeEntry> &aChangeItems)
	{
	for (TInt i = 0; i < aChangeItems.Count(); i++)
		{
		TRAP_IGNORE(HandleChangeL(aChangeItems[i].iChangeType, aChangeItems[i].iEntryType, aChangeItems[i].iEntryId));
		}
	}

/**
 * CCalDavEngine::CalendarInfoChangeNotificationL
 * change callback, sync changed color or name to server
 */
void CCalDavEngine::CalendarInfoChangeNotificationL(RPointerArray<
		CCalFileChangeInfo>& aCalendarInfoChangeEntries)
	{
	for (TInt i = 0; i < aCalendarInfoChangeEntries.Count(); i++)
		{
		if ((aCalendarInfoChangeEntries[i]->FileNameL() == *iCalendar)
				&& (aCalendarInfoChangeEntries[i]->ChangeType()
						== MCalFileChangeObserver::ECalendarInfoUpdated))
			{
			TRAP_IGNORE(HandleCalendarInfoChangeL());
			}
		}
	}

/**
 * CCalDavEngine::HandleCalendarInfoChangeL
 * sync changed color or name to server
 */
void CCalDavEngine::HandleCalendarInfoChangeL()
	{
	if (iHttp)
		{
		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		CCalCalendarInfo* info = iCalSession->CalendarInfoL();
		CleanupStack::PushL(info);

		HBufC8* name =
				CnvUtfConverter::ConvertFromUnicodeToUtf8L(info->NameL());
		CleanupStack::PushL(name);

		TRgb color = info->Color();
		_LIT8(KColorformat,"#%02x%02x%02xFF");
		TBuf8<9> colorbuf;
		colorbuf.Format(KColorformat, color.Red(), color.Green(), color.Blue());

		HBufC8* patch = HBufC8::NewLC(KColorDisplayStart().Length()
				+ colorbuf.Length() + KColorDisplayMiddle().Length()
				+ name->Length() + KColorDisplayEnd().Length());
		patch->Des().Append(KColorDisplayStart);
		patch->Des().Append(colorbuf);
		patch->Des().Append(KColorDisplayMiddle);
		patch->Des().Append(*name);
		patch->Des().Append(KColorDisplayEnd);
		iHttp->ProppatchL(*iUrl, *patch, response);
		CleanupStack::PopAndDestroy(4); // response, info, name, patch
		}
	}

/**
 * CCalDavEngine::HandleChangeL
 * change item callback, sync to server
 */
void CCalDavEngine::HandleChangeL(
		MCalChangeCallBack2::TChangeType &aChangeType,
		MCalChangeCallBack2::TChangeEntryType &aEntryType, TCalLocalUid &aUid)
	{
	switch (aChangeType)
		{
		case MCalChangeCallBack2::EChangeAdd:
		case MCalChangeCallBack2::EChangeModify:
			{
			if (iImmediateSync)
				UploadEntryL(aUid, aChangeType, aEntryType);
			else
				// enable manual sync for the next sync interval
				SyncFailedL();
			break;
			}
		case MCalChangeCallBack2::EChangeDelete:
			{
			if (iImmediateSync)
				DeleteEntryL(aUid);
			else
				{
				iDeletedEntries.Append(aUid);
				SyncFailedL();
				}
			break;
			}
		case MCalChangeCallBack2::EChangeUndefined:
			{
			// upload new and modified entries to server
			UploadModifiedSinceDateL();

			// Find locally deleted ones and delete on server
			DeleteRemovedEntriesOnServerL();

			break;
			}
		}
	}

/**
 * CCalDavEngine::EnableL
 * enable Caldav sync
 */
TInt CCalDavEngine::EnableL()
	{
	if (!iCalEntryView) //do not use iEnabled here,might be set already in ConstructL()
		{
		TInt aErr = CreateCalObjectsL();
		if ((aErr == KErrNone) && (InitL() == KErrNone))
			{
			iTimer->Cancel();
			TInt ticktime = iSyncInterval.Int() == 0 ? 1 : iSyncInterval.Int();
			const TUint64 tickInterval = 1000000 * 60 * ticktime;
			iTimer->Start(tickInterval, tickInterval,
					TCallBack(SyncTickL, this));
			// register change notification
			RegisterL();
			return KErrNone;
			}
		else
			{
			iEnabled = EFalse;
			SetCalendarInfoL(KCaldavEnabled, KNullDesC8);
			DeleteCalObjects();
			return KErrArgument;
			}
		}
	return KErrNone;
	}

/**
 * CCalDavEngine::DeleteCalObjects
 * delete all calendar objects
 */
void CCalDavEngine::DeleteCalObjects()
	{
	delete iCalIter;
	iCalIter = NULL;
	delete iCalEntryView;
	iCalEntryView = NULL;
	delete iCalImporter;
	iCalImporter = NULL;
	delete iCalExporter;
	iCalExporter = NULL;
	if (iCalSession)
		{
		iCalSession->StopChangeNotification();
		iCalSession->StopFileChangeNotification();
		delete iCalSession;
		}
	iCalSession = NULL;
	}

/**
 * CCalDavEngine::CreateCalObjectsL
 * create all calendar objects
 */
TInt CCalDavEngine::CreateCalObjectsL()
	{
	iCalSession = CCalSession::NewL();
	TRAPD(aErr,iCalSession->OpenL(*iCalendar));
	iCalExporter = CCalenExporter::NewL(*iCalSession);
	iCalImporter = CCalenImporter::NewL(*iCalSession);
	iCalEntryView = CCalEntryView::NewL(*iCalSession);
	iCalIter = CCalIter::NewL(*iCalSession);
	return aErr;
	}

/**
 * CCalDavEngine::DisableL
 * disable sync
 */
void CCalDavEngine::DisableL()
	{
	if (iEnabled)
		{
		iTimer->Cancel();
		iEnabled = EFalse;
		SetCalendarInfoL(KCaldavEnabled, KNullDesC8);
		DeleteCalObjects();
		}
	}

/**
 * CCalDavEngine::EnabledSync
 * check for enabled sync
 */
TBool CCalDavEngine::EnabledSync()
	{
	return iEnabled;
	}

/**
 * CCalDavEngine::TimeReportL
 * do a CalDav time report
 */
TInt CCalDavEngine::TimeReportL(TBool VEVENT, const TDesC8 &aStart,
		TBool aDelete)
	{
	CBufFlat* body = CBufFlat::NewL(EXPANDSIZE_BIG);
	CleanupStack::PushL(body);

	body->InsertL(body->Size(), VEVENT ? KTimeStartEVENT() : KTimeStartTODO());
	body->InsertL(body->Size(), aStart); // "20090509T220000Z"/>
	body->InsertL(body->Size(), KTimeEnd);

	CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_BIG);
	CleanupStack::PushL(response);
	TInt ret = iHttp->ReportL(*iUrl, body->Ptr(0), response);

	if (ret == MULTISTATUS)
		ret = aDelete ? ParseResponsesDeleteL(response->Ptr(0))
				: ParseResponsesL(response->Ptr(0));
	else
		ret = KErrGeneral;

	CleanupStack::PopAndDestroy(response);
	CleanupStack::PopAndDestroy(body);
	return ret;
	}

/**
 * CCalDavEngine::ListL
 * get events either using time report or basic propfind
 */
TInt CCalDavEngine::ListL()
	{
	if (iOptions.calendar_access)
		{
		TTime syncstart;
		syncstart.HomeTime();
		syncstart = syncstart - iPastDays;
		TBuf<100> nowStr;
		syncstart.FormatL(nowStr, KFormatString);
		TBuf8<100> nowStrAdd;
		nowStrAdd.Append(nowStr);

		TInt eventreturn = KErrNone;
		if (iOptions.VEVENT)
			eventreturn = TimeReportL(ETrue, nowStrAdd);

		TInt todoreturn = KErrNone;
		if (iOptions.VTODO)
			todoreturn = TimeReportL(EFalse, nowStrAdd);

		return (eventreturn == KErrNone) && (todoreturn == KErrNone) ? KErrNone
				: KErrGeneral;
		}
	else
		{
		// use PROPFIND report
		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		TInt ret = iHttp->PropfindL(*iUrl, KPropList, response, EFalse);
		if (ret == MULTISTATUS)
			ret = ParseResponsesL(response->Ptr(0));
		else
			ret = KErrGeneral;
		CleanupStack::PopAndDestroy(response);
		return ret;
		}
	}

/**
 * CCalDavEngine::SyncL
 * sync a calendar
 */
TInt CCalDavEngine::SyncL()
	{
	if (iUrl)
		{
		if (iOptions.sync_collection)
			return WebDavSyncL();
		else
			return ManualSyncL();
		}
	return KErrGeneral;
	}

/**
 * CCalDavEngine::ManualSyncL
 * if sync failed previously, try again manually
 */
TBool CCalDavEngine::ManualSyncL()
	{
	TBool client = ClientChangesL();
	if (iOptions.sync_ctag)
		{
		HBufC8 *newCTag = GetCTagL();
		TBool server = ETrue;
		if ((CTag() != *newCTag) && (*newCTag != KNullDesC8))
			{
			server = ServerChangesL();
			if (server)
				SetCTagL(newCTag);
			else
				delete newCTag;
			}
		else
			delete newCTag;

		return client || server;
		}
	else
		{
		TBool server = ServerChangesL();
		return client || server;
		}
	}

/**
 * CCalDavEngine::ServerChangesL
 * sync server changes
 */
TBool CCalDavEngine::ServerChangesL()
	{
	// loop over all server items to find new and modified entries
	// uses either propfind or calendar-query
	TInt ret = ListL();

	// loop over all local items to find deleted ones on the server
	LocalLoopL(ELoopActionDeleteLocal);

	return ret == KErrNone;
	}

/**
 * CCalDavEngine::LocalLoopL
 * loop over local calendar store
 */
TInt CCalDavEngine::LocalLoopL(TLocalLoopAction aAction)
	{
	TBuf8<URLMAX> iter = iCalIter->FirstL();
	TBuf8<URLMAX> url;
	while (iter != KNullDesC8)
		{
		url.Append(*iUrl);
		url.Append(iter);
		url.Append(KIcs);
		switch (aAction)
			{
			case ELoopActionDeleteLocal:
				{
				if (HeadL(iter) == NOTFOUND)
					DeleteLocalEntryL(url);
				break;
				}
			case ELoopActionFillArray:
				{
				RPointerArray<CCalEntry> entryArray;
				CleanupClosePushL(entryArray);
				iCalEntryView->FetchL(iter, entryArray);
				if (entryArray.Count())
					{
					iLocalUidArray.Append(entryArray[0]->LocalUidL());
					iGlobalUidArray.Append(entryArray[0]->UidL());
					}
				entryArray.ResetAndDestroy();
				CleanupStack::PopAndDestroy(&entryArray);

				break;
				}
			case ELoopActionUpload:
				{
				TInt pos = iGlobalUidArray.Find(iter);
				if ((pos != KErrNotFound) && (iLocalUidArray.Count() > pos))
					UploadEntryL(iLocalUidArray[pos],
							MCalChangeCallBack2::EChangeAdd,
							MCalChangeCallBack2::EChangeEntryAll);
				break;
				}
			}
		url.Delete(0, url.Length());
		iter = iCalIter->NextL();
		}
	return KErrNone;
	}

/**
 * CCalDavEngine::ParseResponsesDeleteL
 * process a recieved multistatus response
 */
TInt CCalDavEngine::ParseResponsesDeleteL(const TDesC8 &aDocument)
	{
	TInt ret = KErrNone;
	RXmlEngDocument document = iDomParser.ParseL(aDocument);
	CleanupClosePushL(document);
	if (document.NotNull())
		{
		// this method works for response as well as sync-response
		// do not use GetElementsByTagNameL for one specific responses directly	
		TXmlEngElement ResponseListTop;
		SearchL(document, KMultistatus, KNullDesC8, ResponseListTop);

		RXmlEngNodeList<TXmlEngNode> ResponseList;
		CleanupClosePushL(ResponseList);
		ResponseListTop.GetChildNodes(ResponseList);

		while (ResponseList.HasNext())
			{
			TXmlEngNode node = ResponseList.Next();
			if (node.NodeType() == TXmlEngNode::EElement)
				{
				TPtrC8 href = SearchL(node, KHref, KNullDesC8);
				// don't do anything with home itself
				if ((href.Right(KIcs().Length()) == KIcs))
					{
					if (!DoesEntryExistL(href))
						DeleteEntryL(href);
					}
				}
			}
		CleanupStack::PopAndDestroy(&ResponseList);

		}
	else
		ret = KErrArgument;
	CleanupStack::PopAndDestroy(&document);
	return ret;
	}

/**
 * CCalDavEngine::ParseResponsesL
 * process a recieved multistatus response
 */
TInt CCalDavEngine::ParseResponsesL(RXmlEngDocument &aDocument, TBool aMultiget)
	{
	TInt ret = KErrNone;
	if (aDocument.NotNull())
		{
		CDesC8ArrayFlat *multiget = NULL;
		if (iOptions.MULTIGET)
			{
			multiget = new (ELeave) CDesC8ArrayFlat(ARRAYEXPAND);
			CleanupStack::PushL(multiget);
			}

		// this method works for response as well as sync-response
		// do not use GetElementsByTagNameL for one specific responses directly	
		TXmlEngElement ResponseListTop;
		SearchL(aDocument, KMultistatus, KNullDesC8, ResponseListTop);

		RXmlEngNodeList<TXmlEngNode> ResponseList;
		CleanupClosePushL(ResponseList);
		ResponseListTop.GetChildNodes(ResponseList);

		while (ResponseList.HasNext())
			{
			TXmlEngNode node = ResponseList.Next();
			if (node.NodeType() == TXmlEngNode::EElement)
				{
				TPtrC8 href = SearchL(node, KHref, KNullDesC8);
				//do not use Search, only looking for first childs,
				//as D:propstat has D:status as well
				RXmlEngNodeList<TXmlEngElement> statuslist;
				CleanupClosePushL(statuslist);
				node.AsElement().GetElementsByTagNameL(statuslist, KStatus,
						KDav);
				//only one or zero item
				HBufC8* status =
						statuslist.Count() ? statuslist.Next().Value().AllocL()
								: KNullDesC8().AllocL();
				CleanupStack::PopAndDestroy(&statuslist);
				CleanupStack::PushL(status);
				status->Des().LowerCase();
				TPtrC8 etag = SearchL(node, KEtag, KNullDesC8);
				RBuf8 calendardata;
				SearchL(node, KCalendarData, KNullDesC8, calendardata);
				calendardata.CleanupClosePushL();

				// don't do anything with home itself
				if (href.Right(KIcs().Length()) == KIcs)
					{
					if ((*status == KHTTP200) || (*status == KHTTP201) || (*status == KNullDesC8))
						{
						if ((calendardata == KNullDesC8))
							{
							if (aMultiget)
								{
								// ATTENTION: an empty response to a multiget should never happen
								// data wrapped inside CDATA, e.g. bedework ??
								}
							else
								//TOOD: if this is a webdav sync response, we should skip the etag check
								AddModifyLocalEntryL(href, etag, multiget);
							}
						else
							{
							// response to a multiget or time-range report, we now already have everything we need
							StoreEntryL(calendardata, etag);
							}
						}
					else if (*status == KHTTP404)
						{
						if (iOptions.sync_collection)
							{
							// if this is an initial sync without token, 
							// this should be ignored, Sun Server bug!!!
							if (SyncToken() != KNullDesC8)
								DeleteLocalEntryL(href);
							}
						else
							{
							//multiget answer, but deleted in the meantime, should delete locally as well
							DeleteLocalEntryL(href);
							}
						}
					}
				CleanupStack::PopAndDestroy(&calendardata);
				CleanupStack::PopAndDestroy(status);
				}
			}
		CleanupStack::PopAndDestroy(&ResponseList);

		if (iOptions.MULTIGET)
			{
			if (multiget->Count())
				{
				DownloadEntryL(multiget);
				multiget->Reset();
				}
			CleanupStack::PopAndDestroy(multiget);
			}
		}
	else
		ret = KErrArgument;

	return ret;
	}

/**
 * CCalDavEngine::ParseResponsesL
 * process a recieved multistatus response
 */
TInt CCalDavEngine::ParseResponsesL(const TDesC8 &aDocument, TBool aMultiget)
	{
#ifdef _DEBUG
	_LIT(KFilename,"c:\\logs\\caldav\\parseresonseslatest.txt");
	ExportToFileNameL(aDocument, KFilename);
#endif

	RXmlEngDocument document = iDomParser.ParseL(aDocument);
	CleanupClosePushL(document);
	TInt ret = ParseResponsesL(document, aMultiget);
	CleanupStack::PopAndDestroy(&document);
	return ret;
	}

/**
 * CCalDavEngine::StoreEntryL
 * store event in local store
 */
#ifdef ETAG
TInt CCalDavEngine::StoreEntryL(const TDesC8 &aBuf, const TDesC8 &aEtag)
#else
TInt CCalDavEngine::StoreEntryL(const TDesC8 &aBuf, const TDesC8 &/*aEtag*/)
#endif
	{
#ifdef _DEBUG
	_LIT(KFileName2, "C:\\logs\\caldav\\testing_import.txt");
	ExportToFileNameL(aBuf, KFileName2);
#endif

	HBufC8* buffer = HBufC8::NewL(aBuf.Length() + 500);
	buffer->Des().Append(aBuf);
	TPtr8 ptr = buffer->Des();
	CalDavUtils::FixImportIssues(ptr);

	RPointerArray<CCalEntry> Array;
	CleanupClosePushL(Array);
	RDesReadStream ReadStream;
	ReadStream.Open(ptr);
	CleanupClosePushL(ReadStream);
#ifdef _DEBUG
	_LIT(KFileName, "C:\\logs\\caldav\\testing_import_fixed.txt");
	ExportToFileNameL(ptr, KFileName);
#endif
	TRAPD(error, iCalImporter->ImportICalendarL(ReadStream,Array));
	CleanupStack::PopAndDestroy(&ReadStream); // calls close on rSteam												
	if ((error == KErrNone) && (Array.Count()))
		{
		iCalIntermimUtils2->StoreL(*iCalEntryView, *Array[0], ETrue); // or should last one be EFalse??
		TInt pos = iLocalUidArray.Find(Array[0]->LocalUidL());
		if (pos == KErrNotFound)
			{
			iLocalUidArray.Append(Array[0]->LocalUidL());
			iGlobalUidArray.Append(Array[0]->UidL());
			}
#ifdef ETAG
		Array[0]->SetETag(aEtag);
#endif
		}
	Array.ResetAndDestroy();
	CleanupStack::PopAndDestroy(&Array);

	delete buffer;
	return error;
	}

/**
 * CCalDavEngine::WebDavSyncReportL
 * webdav sync report
 * http://tools.ietf.org/html/draft-daboo-webdav-sync-02
 */
TInt CCalDavEngine::WebDavSyncReportL(TBool aSynctoken)
	{
	HBufC8 *Buf = HBufC8::NewL(KSync().Length() + SyncToken().Length());
	TPtrC8 token = SyncToken();
	if (aSynctoken)
		Buf->Des().Format(KSync, &token);
	else
		Buf->Des().Format(KSync, &KNullDesC8());
	CleanupStack::PushL(Buf);

	CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_BIG);
	CleanupStack::PushL(response);
	TInt ret = iHttp->ReportL(*iUrl, *Buf, response);

	if (ret == MULTISTATUS)
		{
		RXmlEngDocument document = iDomParser.ParseL(response->Ptr(0));
		CleanupClosePushL(document);
		if (document.NotNull())
			{
			TPtrC8 Token = SearchL(document, KSynctoken, KNullDesC8);
			if ((Token != KNullDesC8) && (Token != SyncToken()))
				{
				ret = ParseResponsesL(document);
				//store newest token
				if (ret == KErrNone)
					SetSyncTokenL(Token.AllocL());
				}
			else
				ret = KErrNone;
			}
		else
			ret = KErrGeneral;
		CleanupStack::PopAndDestroy(&document);
		}
	CleanupStack::PopAndDestroy(response);
	CleanupStack::PopAndDestroy(Buf);
	return ret;
	}

/**
 * CCalDavEngine::WebDavSyncL
 * sync using webdav sync
 * http://tools.ietf.org/html/draft-daboo-webdav-sync-02
 */
TBool CCalDavEngine::WebDavSyncL()
	{
	if (iHttp)
		{
		// commit any left over client changes
		TBool RetClient = ClientChangesL();
		// get all changes from server
		TInt ret = WebDavSyncReportL(ETrue);
		if (ret == CONFLICT)
			ret = WebDavSyncReportL(EFalse);
		return (ret == KErrNone) && RetClient;
		}
	return EFalse;
	}

/**
 * CCalDavEngine::GetUIDByUrl
 * parse url to find UID
 */
TPtrC8 CCalDavEngine::GetUIDByUrl(const TDesC8 &aUrl)
	{
	TPtrC8 UID;
	TInt Pos = aUrl.LocateReverse('/');
	TInt Pos2 = aUrl.Find(KIcs);

	if ((Pos != KErrNotFound) && (Pos2 != KErrNotFound))
		UID.Set(aUrl.Mid(Pos + 1, Pos2 - Pos - 1));
	else
		{
		if (Pos != KErrNotFound)
			UID.Set(aUrl.Mid(Pos + 1, aUrl.Length() - Pos - 1));
		else if (Pos2 != KErrNotFound)
			UID.Set(aUrl.Left(aUrl.Length() - KIcs().Length()));
		else
			UID.Set(aUrl);
		}

	return UID;
	}

/**
 * CCalDavEngine::DoesEntryExistL
 * check if entry exists in local store
 */
unsigned long CCalDavEngine::DoesEntryExistL(const TDesC8 &aUrl)
	{
	// check if we already have it locally by uid
	RPointerArray<CCalEntry> entryArray;
	CleanupClosePushL(entryArray);
	iCalEntryView->FetchL(GetUIDByUrl(aUrl), entryArray);
	// get parent 
	CCalEntry *entry = entryArray.Count() ? entryArray[0] : NULL;
	TInt ret = entry ? entry->LocalUidL() : 0;
	entryArray.ResetAndDestroy();
	CleanupStack::PopAndDestroy(&entryArray);
	return ret;
	}

/**
 * CCalDavEngine::ETagMatchL
 * checks for equal ETag
 */
TBool CCalDavEngine::ETagMatchL(const TDesC8& /*aUrl*/, const TDesC8& /*aETag*/)
	{
#ifdef ETAG
	// check if we already have it locally by uid
	RPointerArray<CCalEntry> entryArray;
	CleanupClosePushL(entryArray);
	iCalEntryView->FetchL(GetUIDByUrl(aUrl), entryArray);
	// get parent 
	CCalEntry *entry = entryArray.Count() ? entryArray[0] : NULL;
	TBool ret = entry ? entry->ETag() == aETag : EFalse;
	entryArray.ResetAndDestroy();
	CleanupStack::PopAndDestroy(&entryArray);
	return ret;
#else
	return EFalse;
#endif
	}

/**
 * CCalDavEngine::AddModifyLocalEntryL
 * add or modify existing event
 */
TInt CCalDavEngine::AddModifyLocalEntryL(const TDesC8 &aUrl,
		const TDesC8 &aETag, CDesC8ArrayFlat* aArray)
	{
	// check if we have the entry locally
	// check for etag if we have the latest version, if not, download and import or add to multiget request
	if (!ETagMatchL(aUrl, aETag))
		{
		if (aArray)
			aArray->AppendL(aUrl);
		else
			DownloadEntryL(aUrl);
		}
	return KErrNone;
	}

/**
 * CCalDavEngine::DownloadEntryL
 * download entries using multiget from server
 */
TInt CCalDavEngine::DownloadEntryL(CDesC8Array* aArray)
	{
	TInt ret = KErrNone;
	TInt64 remainder;
	TInt64 result = Math::DivMod64(aArray->Count(), MULTIGETSPLIT, remainder);

	// split large multigets request into several smaller ones
	for (TInt64 l = 0; l <= result; l++)
		{
		// do the multiget request and pass it to parserepsonses again to read in the data
		CBufFlat* body = CBufFlat::NewL(EXPANDSIZE_BIG);
		CleanupStack::PushL(body);

		body->InsertL(body->Size(), KMultistart);
		for (TInt64 i = 0; i <= ((l == result) ? remainder - 1 : MULTIGETSPLIT
				- 1); i++)
			{
			body->InsertL(body->Size(), KHrefstart);
			body->InsertL(body->Size(), (*aArray)[MULTIGETSPLIT * l + i]);
			body->InsertL(body->Size(), KHrefend);
			}
		body->InsertL(body->Size(), KMultiend);

		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_BIG);
		CleanupStack::PushL(response);
		TInt Return = iHttp->ReportL(*iUrl, body->Ptr(0), response);

		if (Return == MULTISTATUS)
			{
			TInt parsereturn = ParseResponsesL(response->Ptr(0), ETrue);
			// if it failed before, we do not want to override this error
			ret = (ret == KErrNone) ? parsereturn : ret;
			}
		else
			ret = KErrGeneral;
		CleanupStack::PopAndDestroy(response);
		CleanupStack::PopAndDestroy(body);

		}
	return ret;
	}

/**
 * CCalDavEngine::DownloadEntryL
 * download entry from server
 */
TInt CCalDavEngine::DownloadEntryL(const TDesC8 &aUrl)
	{
	TBuf8<URLMAX> url;
	url.Append(*iUrl);
	url.Append(GetUIDByUrl(aUrl));
	url.Append(KIcs);

	CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
	CleanupStack::PushL(response);
	TInt ret = iHttp->GetL(url, response);
	if (ret == OK)
		ret = StoreEntryL(response->Ptr(0), iHttp->ETag());
	else
		ret = KErrGeneral;
	CleanupStack::PopAndDestroy(response);
	return ret;
	}

/**
 * CCalDavEngine::DeleteLocalEntryL
 * delete an event from local store
 */
TInt CCalDavEngine::DeleteLocalEntryL(const TDesC8 &aUID)
	{
	CDesC8ArrayFlat * Array = new (ELeave) CDesC8ArrayFlat(ARRAYEXPAND);
	CleanupStack::PushL(Array);
	Array->AppendL(GetUIDByUrl(aUID));
	// we could have delete it ourselves, so it is already gone
	TRAPD(error, iCalEntryView->DeleteL(*Array));
	Array->Reset();
	CleanupStack::PopAndDestroy(Array);
	return error;
	}

/**
 * CCalDavEngine::DeleteRemovedEntriesOnServerL
 * check for removed entries on server
 */
TInt CCalDavEngine::DeleteRemovedEntriesOnServerL()
	{
	if (iOptions.calendar_access)
		{
		TTime syncstart;
		syncstart.HomeTime();
		syncstart = syncstart - iPastDays;
		TBuf<100> nowStr;
		syncstart.FormatL(nowStr, KFormatString);
		TBuf8<100> nowStrAdd;
		nowStrAdd.Append(nowStr);

		TInt eventreturn = KErrNone;
		if (iOptions.VEVENT)
			eventreturn = TimeReportL(ETrue, nowStrAdd, ETrue);

		TInt todoreturn = KErrNone;
		if (iOptions.VTODO)
			todoreturn = TimeReportL(EFalse, nowStrAdd, ETrue);

		return (eventreturn == KErrNone) && (todoreturn == KErrNone) ? KErrNone
				: KErrGeneral;
		}
	else
		{
		// use PROPFIND report
		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		TInt ret = iHttp->PropfindL(*iUrl, KPropList, response, EFalse);
		if (ret == MULTISTATUS)
			ret = ParseResponsesDeleteL(response->Ptr(0));
		else
			ret = KErrGeneral;
		CleanupStack::PopAndDestroy(response);
		return ret;
		}

	}

/**
 * CCalDavEngine::UploadModifiedSinceDateL
 * check for any modified data after last sync time
 */
TBool CCalDavEngine::UploadModifiedSinceDateL()
	{
	TBool manualsync = EFalse;
	// upload modified and newly create ones 
	RArray<TCalLocalUid> *Ids = new (ELeave) RArray<TCalLocalUid> (ARRAYEXPAND);
	iCalEntryView->GetIdsModifiedSinceDateL(iLastSyncTime, *Ids);
	for (TInt i = 0; i < Ids->Count(); i++)
		{
		TCalLocalUid id = (*Ids)[i];
		TInt ret = UploadEntryL(id, MCalChangeCallBack2::EChangeUndefined,
				MCalChangeCallBack2::EChangeEntryAll);
		// TOOD: if it fails during upload, ignore
		// if it fails due to internet connection, try again
		if (ret != KErrNone)
			manualsync = ETrue;
		}
	Ids->Reset();
	delete Ids;
	return manualsync;
	}

/**
 * CCalDavEngine::ClientChangesL
 * check for left over local client changes
 */
TBool CCalDavEngine::ClientChangesL()
	{
	if (iCalEntryView && iManualSync)
		{
		iManualSync = EFalse;

		// upload modified and newly create ones 
		iManualSync = UploadModifiedSinceDateL();

		// delete locally deleted entries on server 
		for (TInt i = iDeletedEntries.Count() - 1; i >= 0; --i)
			{
			TInt ret = DeleteEntryL(iDeletedEntries[i]);
			if (ret == KErrNone)
				iDeletedEntries.Remove(i);

			}

		iManualSync = iDeletedEntries.Count() ? ETrue : EFalse;

		TPckgC<TBool> manualSync(iManualSync);
		SetCalendarInfoL(KCaldavManualSync, manualSync);
		}

	return ETrue;
	}

/**
 * CCalDavEngine::MkcalendarL
 * create a new calendar on the server
 */
TInt CCalDavEngine::MkcalendarL(const TDesC8 &aName)
	{
	if (iOptions.MKCALENDAR)
		{
		TBuf8<URLMAX> url;
		url.Append(*iHome);
		url.Append(aName);
		url.Append(KSlash);

		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);

		// TOOD: initialize with name, body etc.
		TInt ret = iHttp->MkCalendarL(url, KNullDesC8, response);

		if ((ret == CREATED) || (ret == OK))
			ret = KErrNone;
		else if ((ret == NOTALLOWED) || (ret == FORBIDDEN))
			ret = KErrArgument;
		else
			ret = KErrGeneral;
		CleanupStack::PopAndDestroy(response);
		return ret;
		}
	else
		return KErrNotSupported;
	}

/**
 * CCalDavEngine::DeleteCalendarL
 * delete a calendar on the server
 */
TInt CCalDavEngine::DeleteCalendarL(const TDesC8 &aName)
	{
	if (iOptions.MKCALENDAR)
		{
		TBuf8<URLMAX> url;
		url.Append(*iHome);
		url.Append(aName);
		url.Append(KSlash);

		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		TInt ret = iHttp->DeleteL(url);
		if ((ret == NOCONTENT) || (ret == OK))
			ret = KErrNone;
		else
			ret = KErrGeneral;
		CleanupStack::PopAndDestroy(response);
		return ret;
		}
	else
		return KErrNotSupported;
	}

/**
 * CCalDavEngine::HeadL
 * check for existence of an entry on server
 */
TInt CCalDavEngine::HeadL(const TDesC8 &aUID)
	{
	// special handing for yahoo neccessary
	// after deleting an event, it is still there and findable with HEAD
	_LIT8(KYahoo,"yahoo");
	_LIT8(KTrash,"trash");

	TBuf8<URLMAX> url;
	url.Append(*iUrl);
	url.Append(aUID);
	url.Append(KIcs);
	if (iUrl->Find(KYahoo) == KErrNotFound)
		{
		TInt head = iHttp->HeadL(url);
		return (head == NOCONTENT) || (head == OK) ? OK : head;
		}
	else
		{
		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		TInt ret = iHttp->PropfindL(url, KNullDesC8, response);

		if (ret == MULTISTATUS)
			{
			RXmlEngDocument document = iDomParser.ParseL(response->Ptr(0));
			CleanupClosePushL(document);
			if (document.NotNull())
				{
				HBufC8* href = SearchL(document, KHref, KNullDesC8).AllocL();
				href->Des().LowerCase();
				ret = href->Find(KTrash) == KErrNotFound ? OK : NOTFOUND;
				delete href;
				}
			CleanupStack::PopAndDestroy(&document);
			CleanupStack::PopAndDestroy(response);
			return ret;
			}
		else
			{
			CleanupStack::PopAndDestroy(response);
			return NOTFOUND;
			}
		}
	}

/**
 * CCalDavEngine::DeleteEntryL
 * delete entry on server
 */
TInt CCalDavEngine::DeleteEntryL(const TDesC8 &aUid)
	{
	TBuf8<URLMAX> url;
	url.Append(*iBaseUrl);
	url.Append(aUid);
	return iHttp->DeleteL(url);
	}

/**
 * CCalDavEngine::DeleteEntryL
 * delete entry on server
 */
TInt CCalDavEngine::DeleteEntryL(const TCalLocalUid &aUid)
	{
	TInt Ret = KErrNone;
	// find the filename for a given local UID
	TInt aPos = iLocalUidArray.Find(aUid);
	if (aPos != KErrNotFound)
		{
		TBuf8<URLMAX> url;
		url.Append(*iUrl);
		url.Append(iGlobalUidArray[aPos]);
		url.Append(KIcs);

#ifdef ETAG
		CCalEntry* entry = iCalEntryView->FetchL(aUid);
		CleanupStack::PushL(entry);
		TInt Return = entry ? iHttp->DeleteL(url, entry->ETag())
		: iHttp->DeleteL(url);
		CleanupStack::PopAndDestroy(entry);
#else
		TInt Return = iHttp->DeleteL(url);
#endif

		if ((Return == NOCONTENT) || (Return == OK))
			{
			SetLastSyncTimeL();
			}
		else if (Return == PRECONDFAILED)
			{
			// someone modified this in the meantime
			// ask user if he wants the new event or still delete it
			TBool modify = EFalse;
			if (modify)
				DownloadEntryL(url);
			else
				iHttp->DeleteL(url);
			}
		else if (Return == NOTFOUND)
			{
			// someone deleted this already
			Ret = KErrGeneral;
			}
		else if (Return == FORBIDDEN)
			{
			// event read-only
			Ret = KErrGeneral;
			}
		else
			{
			Ret = KErrGeneral;
			SyncFailedL();
			TInt pos = iDeletedEntries.Find(aUid);
			if (pos == KErrNotFound)
				iDeletedEntries.Append(aUid);
			}
		}
	else
		Ret = KErrGeneral;
	return Ret;
	}

/**
 * CCalDavEngine::UploadEntryL
 * upload entry to server
 */
TInt CCalDavEngine::UploadEntryL(CCalEntry* aEntry,
		MCalChangeCallBack2::TChangeType aChangeType,
		MCalChangeCallBack2::TChangeEntryType aEntryType)
	{
	if (aEntry)
		{
		TInt ret = KErrNone;
		TBool upload = EFalse;
		switch (aEntryType)
			{
			case MCalChangeCallBack2::EChangeEntryEvent:
			case MCalChangeCallBack2::EChangeEntryTodo:
				{
				upload = aEntry && ((MCalChangeCallBack2::EChangeEntryEvent
						&& iOptions.VEVENT)
						|| (MCalChangeCallBack2::EChangeEntryTodo
								&& iOptions.VTODO));
				break;
				}
			case MCalChangeCallBack2::EChangeEntryAll:
				{
				if (aEntry)
					{
					switch (aEntry->EntryTypeL())
						{
						case CCalEntry::EAppt:
						case CCalEntry::EAnniv:
						case CCalEntry::EEvent:
						case CCalEntry::EReminder:
							{
							upload = iOptions.VEVENT;
							break;
							}
						case CCalEntry::ETodo:
							{
							upload = iOptions.VTODO;
							break;
							}
						}
					}

				}
			}
		if (upload)
			{
			CBufFlat* BufFlat = CBufFlat::NewL(EXPANDSIZE_SMALL);
			CleanupStack::PushL(BufFlat);
			RBufWriteStream writeStream(*BufFlat);
			CleanupClosePushL(writeStream);
			iCalExporter->ExportICalL(*aEntry, writeStream);
			writeStream.CommitL();
			CleanupStack::PopAndDestroy(&writeStream);

			HBufC8* buffer = BufFlat->Ptr(0).AllocL();
			CleanupStack::PopAndDestroy(BufFlat);
			CleanupStack::PushL(buffer);
			TPtr8 ptr = buffer->Des();
			CalDavUtils::FixExportIssues(ptr);

#ifdef _DEBUG
			ExportToFileL(aEntry, iCalExporter);
			_LIT(KFileName, "C:\\logs\\caldav\\testing_export_fixed.txt");
			ExportToFileNameL(ptr, KFileName);
#endif

			TBuf8<URLMAX> url;
			url.Append(*iUrl);
			url.Append(aEntry->UidL());
			url.Append(KIcs);

			CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
			CleanupStack::PushL(response);

#ifdef ETAG
			TPtrC8 etag = aEntry->GetETag();
			TBool newentry = (aChangeType == MCalChangeCallBack2::EChangeAdd)
			|| ((aChangeType == MCalChangeCallBack2::EChangeUndefined)
					&& (etag == KNullDesC8));
			TInt Ret = newentry ? iHttp->PutL(url, *buffer, response)
			: iHttp->PutL(url, *buffer, response, etag);
#else
			TBool newentry = (aChangeType == MCalChangeCallBack2::EChangeAdd)
					|| (aChangeType == MCalChangeCallBack2::EChangeUndefined);
			TInt Ret = newentry ? iHttp->PutL(url, *buffer, response)
					: iHttp->PutL(url, *buffer, response, _L8("ETAG"));
#endif
			if ((Ret == CREATED) || (Ret == NOCONTENT) || (Ret == OK))
				{
				if (newentry)
					{
					iLocalUidArray.Append(aEntry->LocalUidL());
					iGlobalUidArray.Append(aEntry->UidL());
					}
#ifdef ETAG
				aEntry->SetETag(iHttp->ETag());
#endif
				SetLastSyncTimeL();
				}
			else if (Ret == PRECONDFAILED)
				{
				if (newentry)// same filename already exists, use a different one and upload again
					{
					TBuf8<URLMAX> nextUrl;
					nextUrl.Append(*iUrl);
					nextUrl.Append(aEntry->UidL());
					TTime time;
					time.HomeTime();
					_LIT(KTimeFormat,"%H%T%S");
					TBuf<20> StringTime;
					time.FormatL(StringTime, KTimeFormat);
					nextUrl.Append(StringTime);
					nextUrl.Append(_L8(".ics"));
					response->Reset();
					TInt Ret = iHttp->PutL(nextUrl, *buffer, response);
					if ((Ret == CREATED) || (Ret == OK))
						{
						iLocalUidArray.Append(aEntry->LocalUidL());
						iGlobalUidArray.Append(aEntry->UidL());
#ifdef ETAG
						aEntry->SetETag(iHttp->ETag());
#endif
						SetLastSyncTimeL();
						}
					else
						{
						SyncFailedL();
						ret = KErrAbort;
						}
					}
				else
					{
					if (!iKeepServerEntry)
						{
						response->Reset();
						// upload again without ETAG to overwrite server entry
						TInt Ret = iHttp->PutL(url, *buffer, response);
						if ((Ret == CREATED) || (Ret == OK))
							{
#ifdef ETAG
							aEntry->SetETag(iHttp->ETag());
#endif
							SetLastSyncTimeL();
							}
						else
							{
							SyncFailedL();
							ret = KErrAbort;
							}
						}
					else
						{
						// download the server event and update local store
						ret = DownloadEntryL(url);
						if (ret == KErrNone)
							SetLastSyncTimeL();
						else
							{
							SyncFailedL();
							ret = KErrAbort;
							}
						}
					}
				}
			else
				{
				SyncFailedL();
				ret = KErrAbort;
				}
			CleanupStack::PopAndDestroy(response);
			CleanupStack::PopAndDestroy(buffer);
			}
		return ret;
		}
	return KErrArgument;
	}

/**
 * CCalDavEngine::UploadEntryL
 * upload entry to server
 */
TInt CCalDavEngine::UploadEntryL(const TCalLocalUid &aUid,
		MCalChangeCallBack2::TChangeType aChangeType,
		MCalChangeCallBack2::TChangeEntryType aEntryType)
	{
	CCalEntry * aEntry = iCalEntryView->FetchL(aUid);
	CleanupStack::PushL(aEntry);
	TInt ret = UploadEntryL(aEntry, aChangeType, aEntryType);
	CleanupStack::PopAndDestroy(aEntry);
	return ret;
	}

/**
 * CCalDavEngine::GetSyncTokenL
 * get latest Webdav Sync token
 */
HBufC8* CCalDavEngine::GetSyncTokenL()
	{
	HBufC8 *aBuf = HBufC8::NewL(KSync().Length());
	aBuf->Des().Format(KSync, &KNullDesC8());
	CleanupStack::PushL(aBuf);

	CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
	CleanupStack::PushL(response);
	TInt RetServer = iHttp->ReportL(*iUrl, *aBuf, response);

	if (RetServer)
		{
		RXmlEngDocument document = iDomParser.ParseL(response->Ptr(0));
		CleanupClosePushL(document);
		if (document.NotNull())
			{
			HBufC8* ret = SearchL(document, KSynctoken, KNullDesC8).AllocL();
			CleanupStack::PopAndDestroy(&document);
			CleanupStack::PopAndDestroy(response);
			CleanupStack::PopAndDestroy(aBuf);
			return ret;
			}
		CleanupStack::PopAndDestroy(&document);
		}
	CleanupStack::PopAndDestroy(response);
	CleanupStack::PopAndDestroy(aBuf);
	return KNullDesC8().AllocL();
	}

/**
 * CCalDavEngine::CheckCalendarInfoL
 * check for new calendar displayname and color
 */
void CCalDavEngine::CheckCalendarInfoL(RXmlEngDocument &aDocument)
	{
	TBool change = EFalse;
	CCalCalendarInfo* info = iCalSession->CalendarInfoL();
	CleanupStack::PushL(info);

	HBufC8* color = SearchL(aDocument, KCalendar_Color, KNullDesC8).AllocLC();
	if ((*color != KNullDesC8) && (color->Length() > 6))
		{
		TLex8 lexred(color->Des().Mid(1, 2));
		TInt red;
		lexred.Val(red);
		TLex8 lexgreen(color->Des().Mid(3, 2));
		TInt green;
		lexgreen.Val(green);
		TLex8 lexblue(color->Des().Mid(5, 2));
		TInt blue;
		lexblue.Val(blue);
		TRgb newcolor(red, green, blue);
		if (info->Color() != newcolor)
			{
			info->SetColor(newcolor);
			change = ETrue;
			}
		}
	CleanupStack::PopAndDestroy(color);

	HBufC8* displayname =
			SearchL(aDocument, KDisplayname, KNullDesC8).AllocLC();
	if (*displayname != KNullDesC8)
		{
		HBufC16* name =
				CnvUtfConverter::ConvertToUnicodeFromUtf8L(*displayname);
		CleanupStack::PushL(name);
		if (info->NameL() != *name)
			{
			info->SetNameL(*name);
			change = ETrue;
			}
		CleanupStack::PopAndDestroy(name);
		change = ETrue;
		}
	CleanupStack::PopAndDestroy(displayname);

	if (change)
		iCalSession->SetCalendarInfoL(*info);
	CleanupStack::PopAndDestroy(info);

	}

/**
 * CCalDavEngine::GetCTagL
 * get latest CTag
 * https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-ctag.txt
 */
HBufC8* CCalDavEngine::GetCTagL()
	{
	if (iHttp)
		{
		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		TInt ret = iHttp->PropfindL(*iUrl, KCtag, response);

		if (ret == MULTISTATUS)
			{
			RXmlEngDocument document = iDomParser.ParseL(response->Ptr(0));
			CleanupClosePushL(document);
			if (document.NotNull())
				{
				HBufC8* status =
						SearchL(document, KStatus, KNullDesC8).AllocLC();
				HBufC8* ctag =
						SearchL(document, KGetctag, KNullDesC8).AllocLC();
				status->Des().LowerCase();
				if ((*ctag != KNullDesC8) && (*status == KHTTP200))
					{
					CleanupStack::Pop(ctag);
					CleanupStack::PopAndDestroy(status);
					CleanupStack::PopAndDestroy(&document);
					CleanupStack::PopAndDestroy(response);
					return ctag;
					}
				else
					CleanupStack::PopAndDestroy(ctag);
				CleanupStack::PopAndDestroy(status);
				}
			CleanupStack::PopAndDestroy(&document);

			}
		CleanupStack::PopAndDestroy(response);
		}
	return KNullDesC8().AllocL();
	}

/**
 * CCalDavEngine::GetOptionsL
 * get OPTIONS from server
 */
TBool CCalDavEngine::GetOptionsL()
	{
	if (iHttp)
		{
		// check DAV and allow headers
		iHttp->GetServerOptionsL(*iUrl, iOptions);

		// check ctag extension
		HBufC8* ctag = GetCTagL();
		if (*ctag != KNullDesC8)
			iOptions.sync_ctag = true;
		delete ctag;

		// check supported elements
		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		TInt Ret = iHttp->PropfindL(*iUrl, KSupportedSet, response);

		if (Ret == MULTISTATUS)
			{
			RXmlEngDocument document = iDomParser.ParseL(response->Ptr(0));
			CleanupClosePushL(document);
			if (document.NotNull())
				{
				CheckCalendarInfoL(document);

				//<C:supported-calendar-component-set/>
				TXmlEngElement supportedelement;
				SearchL(document, KSupportedCalendarComponentSet, KNullDesC8,
						supportedelement);
				if (supportedelement.NotNull())
					{
					RXmlEngNodeList<TXmlEngElement> supportedelements;
					CleanupClosePushL(supportedelements);
					supportedelement.GetChildElements(supportedelements);
					while (supportedelements.HasNext())
						{
						TXmlEngElement element = supportedelements.Next();
						TPtrC8 value = element.AttributeValueL(KName);
						if (value == KNullDesC8)
							value.Set(element.AttributeValueL(KName, KCalDav));
						if (value == KVEVENT)
							iOptions.VEVENT = ETrue;
						else if (value == KVTODO)
							iOptions.VTODO = ETrue;
						else if (value == KVFREBUSY)
							iOptions.VFREEBUSY = ETrue;
						else if (value == KVJOURNAL)
							iOptions.VJOURNAL = ETrue;
						}
					CleanupStack::PopAndDestroy(&supportedelements);
					}
				}
			CleanupStack::PopAndDestroy(&document);
			}
		CleanupStack::PopAndDestroy(response);
		}
	return ETrue;
	}

/**
 * CCalDavEngine::SetSyncTokenL
 * set sync token
 */
void CCalDavEngine::SetSyncTokenL(HBufC8* aToken)
	{
	if (iSynctoken)
		{
		delete iSynctoken;
		iSynctoken = NULL;
		}
	iSynctoken = aToken;
	SetCalendarInfoL(KCaldavSynctoken, *iSynctoken);
	}

/**
 * CCalDavEngine::SyncToken
 * get synctoken
 */
TPtrC8 CCalDavEngine::SyncToken()
	{
	return iSynctoken ? *iSynctoken : KNullDesC8();
	}

/**
 * CCalDavEngine::SetCTagL
 * set ctag
 */
void CCalDavEngine::SetCTagL(HBufC8* aToken)
	{
	if (iCTag)
		{
		delete iCTag;
		iCTag = NULL;
		}
	iCTag = aToken;
	SetCalendarInfoL(KCaldavCtag, *iCTag);
	}

/**
 * CCalDavEngine::CTag
 * get ctag
 */
TPtrC8 CCalDavEngine::CTag()
	{
	return iCTag ? *iCTag : KNullDesC8();
	}

/**
 * CCalDavEngine::SetLastSyncTimeL
 * set last sync time
 */
void CCalDavEngine::SetLastSyncTimeL()
	{
	// only set a new last sync time, if we did not have a failed one before
	// otherwise, the old one would be lost
	if (!iManualSync)
		{
		TTime time;
		time.UniversalTime();
		iLastSyncTime.SetTimeUtcL(time);
		TPckgC<TCalTime> lasttime(iLastSyncTime);
		SetCalendarInfoL(KCaldavTime, lasttime);
		}
	}

/**
 * CCalDavEngine::SyncFailedL
 * sync failed, enable manual sync
 */
void CCalDavEngine::SyncFailedL()
	{
	if (!iManualSync)
		{
		iManualSync = ETrue;
		TPckgC<TBool> manualSync(iManualSync);
		SetCalendarInfoL(KCaldavManualSync, manualSync);
		}
	}

/**
 * CCalDavEngine::GetBaseUrl
 * get base domain url
 */
void CCalDavEngine::GetBaseUrl(const TDesC8 &aUrl)
	{
	_LIT8(http,"http://");
	_LIT8(https,"https://");

	if (iBaseUrl)
		{
		delete iBaseUrl;
		iBaseUrl = NULL;
		}

	if (aUrl.Length() > http().Length())
		{
		TInt length = aUrl.Find(https) != KErrNotFound ? https().Length()
				: http().Length();
		TInt pos = aUrl.Mid(length).Locate('/');
		iBaseUrl = aUrl.Left(pos + length).Alloc();
		}
	}

/**
 * CCalDavEngine::FindUrlsL
 * find home, inbox and outbox property
 */
void CCalDavEngine::FindUrlsL(const TDesC8 &aDes, HBufC8 *&home,
		HBufC8 *&inbox, HBufC8 *&outbox)
	{
	RXmlEngDocument document = iDomParser.ParseL(aDes);
	CleanupClosePushL(document);
	if (document.NotNull())
		{
		HBufC8* status = SearchL(document, KStatus, KNullDesC8).AllocLC();
		status->Des().LowerCase();
		if (*status == KHTTP200)
			{
			TXmlEngElement calendarhome, inboxhome, outboxhome;

			SearchL(document, KCalendarHomeSet, KNullDesC8, calendarhome);
			if (calendarhome.NotNull())
				{
				TPtrC8 homeend = SearchL(calendarhome, KHref, KNullDesC8);
				home = HBufC8::NewL(iBaseUrl->Length() + homeend.Length());
				home->Des().Append(*iBaseUrl);
				home->Des().Append(homeend);
				}

			SearchL(document, KInbox, KNullDesC8, inboxhome);
			if (inboxhome.NotNull())
				{
				TPtrC8 inboxend = SearchL(inboxhome, KHref, KNullDesC8);
				inbox = HBufC8::NewL(iBaseUrl->Length() + inboxend.Length());
				inbox->Des().Append(*iBaseUrl);
				inbox->Des().Append(inboxend);
				}

			SearchL(document, KOutbox, KNullDesC8, outboxhome);
			if (outboxhome.NotNull())
				{
				TPtrC8 outboxend = SearchL(outboxhome, KHref, KNullDesC8);
				outbox = HBufC8::NewL(iBaseUrl->Length() + outboxend.Length());
				outbox->Des().Append(*iBaseUrl);
				outbox->Des().Append(outboxend);
				}
			}
		CleanupStack::PopAndDestroy(status);
		}
	CleanupStack::PopAndDestroy(&document);
	}

/**
 * CCalDavEngine::FindCalendarCollectionL
 * find all calendar collections under home url
 */
HBufC8* CCalDavEngine::FindCalendarCollectionL(const TDesC8 &aUrl,
		CDesC8ArrayFlat *aArray)
	{
	HBufC8* homecalendar = 0;

	// do propfind depth:1 and find all calendar collections
	// right now, take the first one as default
	CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
	CleanupStack::PushL(response);
	TInt retcode = iHttp->PropfindL(aUrl, KCalendarurl, response, EFalse);
	if (retcode == MULTISTATUS)
		{
		RXmlEngDocument document = iDomParser.ParseL(response->Ptr(0));
		CleanupClosePushL(document);
		if (document.NotNull() && document.DocumentElement().NotNull())
			{
			RXmlEngNodeList<TXmlEngElement> ResponseList;
			CleanupClosePushL(ResponseList);
			document.DocumentElement().GetElementsByTagNameL(ResponseList,
					KResponse, KDav);
			TBool FirstOneDone = EFalse;
			while (ResponseList.HasNext())
				{
				TXmlEngElement node = ResponseList.Next();
				TPtrC8 href = SearchL(node, KHref, KNullDesC8);
				TPtrC8 status = SearchL(node, KStatus, KNullDesC8);

				TXmlEngElement calendar;
				TXmlEngElement vevent_collection;
				TXmlEngElement vtodo_collection;
				SearchL(node, KCalendar, KNullDesC8, calendar);
				SearchL(node, KVEventCollection, KNullDesC8, vevent_collection);
				SearchL(node, KVTodoCollection, KNullDesC8, vtodo_collection);

				if (calendar.NotNull() || vevent_collection.NotNull()
						|| vtodo_collection.NotNull())
					{
					if (!FirstOneDone)
						{
						homecalendar = HBufC8::NewL(iBaseUrl->Length()
								+ href.Length());
						homecalendar->Des().Append(*iBaseUrl);
						homecalendar->Des().Append(href);

						iOptions.VEVENT = vevent_collection.NotNull();
						iOptions.VTODO = vtodo_collection.NotNull();

						FirstOneDone = ETrue;
						}

					if (aArray)
						{
						TBuf8<URLMAX> url;
						url.Append(*iBaseUrl);
						url.Append(href);
						aArray->AppendL(url);
						}
					}
				}
			CleanupStack::PopAndDestroy(&ResponseList);
			}
		CleanupStack::PopAndDestroy(&document);
		}
	CleanupStack::PopAndDestroy(response);
	return homecalendar;
	}

/**
 * CCalDavEngine::GetCalendarUrlsL
 * find calendar url based on any url
 * could be principal url, home or direct calendar url
 */
TInt CCalDavEngine::GetCalendarUrlsL(CDesC8ArrayFlat *aArray)
	{
	if (iHttp && iUrl)
		{
		HBufC8 *principal = 0;
		HBufC8 *home = 0;
		HBufC8 *homecalendar = 0;
		HBufC8 *inbox = 0;
		HBufC8 *outbox = 0;

		GetBaseUrl(*iUrl);

		// TODO: does this really find groupdav collection?

		// find out if this is a caldav or groupdav calendar collection
		CBufFlat* response = CBufFlat::NewL(EXPANDSIZE_SMALL);
		CleanupStack::PushL(response);
		TInt retcode = iHttp->PropfindL(*iUrl, KCalendarurl, response);
		if (retcode == MULTISTATUS)
			{
			RXmlEngDocument document = iDomParser.ParseL(response->Ptr(0));
			CleanupClosePushL(document);
			if (document.NotNull())
				{
				HBufC8* status =
						SearchL(document, KStatus, KNullDesC8).AllocLC();
				status->Des().LowerCase();
				TXmlEngElement calendar;
				SearchL(document, KCalendar, KNullDesC8, calendar);
				// it should be <owner><href>value</href></owner>
				// but oracle beehive server does <owner>value</owner>
				TXmlEngElement owner;
				SearchL(document, KOwner, KNullDesC8, owner);
				TPtrC8 ownerref1 = SearchL(owner, KHref, KNullDesC8);
				TPtrC8 ownerref2 = owner.Value();
				TPtrC8 ownerref;
				ownerref.Set(ownerref1 != KNullDesC8 ? ownerref1 : ownerref2);

				if (calendar.NotNull() && (ownerref != KNullDesC8) && (*status
						== KHTTP200))
					{
					// this is a calendar collection and we know the principal as well now
					homecalendar = iUrl->AllocL();
					_LIT8(KHTTP,"http");
					HBufC8* http = ownerref.Left(KHTTP().Length()).AllocLC();
					if (*http == KHTTP)
						{
						// sogo server does not return relative, but principal url
						principal = ownerref.AllocL();
						}
					else
						{
						principal = HBufC8::NewL(iBaseUrl->Length()
								+ ownerref.Length());
						principal->Des().Append(*iBaseUrl);
						principal->Des().Append(ownerref);
						}
					CleanupStack::PopAndDestroy(http);
					}
				CleanupStack::PopAndDestroy(status);
				}
			CleanupStack::PopAndDestroy(&document);
			}

		// if we have principal, ask for home, otherwise see if principal was given in the first place
		if (principal)
			{
			response->Reset();
			TInt retcode =
					iHttp->PropfindL(*principal, KPrincipalurl, response);
			if (retcode == MULTISTATUS)
				{
				FindUrlsL(response->Ptr(0), home, inbox, outbox);
				}
			}
		else
			{
			response->Reset();
			TInt retcode = iHttp->PropfindL(*iUrl, KPrincipalurl, response);
			if (retcode == MULTISTATUS)
				{
				FindUrlsL(response->Ptr(0), home, inbox, outbox);
				}
			}

		home = CalDavUtils::EnsureSlashL(home);
		inbox = CalDavUtils::EnsureSlashL(inbox);
		outbox = CalDavUtils::EnsureSlashL(outbox);

		// find out all calendar collections under home
		if (home)
			{
			// TODO: temporary? we already have homecalendar...
			if (!homecalendar)
				{
				homecalendar = FindCalendarCollectionL(*home, aArray);
				}
			}
		else
			{
			// have not found out home nor a groupdav collection, maybe we are home ourselves
			homecalendar = FindCalendarCollectionL(*iUrl, aArray);
			if (homecalendar)
				home = iUrl->AllocL();
			}

		CleanupStack::PopAndDestroy(response);
		delete principal;
		delete inbox;
		delete outbox;

		if (home)
			iHome = CalDavUtils::EnsureSlashL(home);
		if (homecalendar)
			{
			delete iUrl;
			iUrl = NULL;
			iUrl = CalDavUtils::EnsureSlashL(homecalendar);
			return KErrNone;
			}
		else
			return KErrArgument;
		}
	return KErrArgument;
	}

/**
 * CCalDavEngine::CalendarName
 * get calendar name
 */
TPtrC CCalDavEngine::CalendarName() const
	{
	return iCalendar ? *iCalendar : KNullDesC();
	}

/**
 * CCalDavEngine::Home
 * get home
 */
TPtrC8 CCalDavEngine::Home() const
	{
	return iHome ? *iHome : KNullDesC8();
	}

/**
 * CCalDavEngine::Url
 * get url
 */
TPtrC8 CCalDavEngine::Url() const
	{
	return iUrl ? *iUrl : KNullDesC8();
	}

/**
 * CCalDavEngine::SetUrlL
 * set url
 */
void CCalDavEngine::SetUrlL(const TDesC8 &aUrl)
	{
	if (iUrl ? *iUrl != aUrl : ETrue)
		{
		DisableL();

		if (iUrl)
			{
			delete iUrl;
			iUrl = NULL;
			}

		iUrl = CalDavUtils::EnsureSlashL(aUrl);
		SetCalendarInfoL(KCaldavUrl, *iUrl);
		}
	}

/**
 * CCalDavEngine::User
 * get user
 */
TPtrC8 CCalDavEngine::User() const
	{
	return iHttp->User();
	}

/**
 * CCalDavEngine::SetUserL
 * set user
 */
void CCalDavEngine::SetUserL(const TDesC8 &aUser)
	{
	if (iHttp->User() != aUser)
		{
		DisableL();
		SetCalendarInfoL(KCaldavUser, aUser);
		iHttp->SetUserL(aUser);
		}
	}

/**
 * CCalDavEngine::Password
 * get password
 */
TPtrC8 CCalDavEngine::Password() const
	{
	return iHttp->Password();
	}

/**
 * CCalDavEngine::SetPasswordL
 * set password
 */
void CCalDavEngine::SetPasswordL(const TDesC8 &aPassword)
	{
	if (iHttp->Password() != aPassword)
		{
		DisableL();
		iHttp->SetPasswordL(aPassword);
		SetCalendarInfoL(KCaldavPassword, aPassword);
		}
	}

/**
 * CCalDavEngine::SyncInterval
 * get SyncInterval
 */
TTimeIntervalMinutes CCalDavEngine::SyncInterval() const
	{
	return iSyncInterval;
	}

/**
 * CCalDavEngine::SetSyncIntervalL
 * set SetSyncIntervalL
 */
void CCalDavEngine::SetSyncIntervalL(TTimeIntervalMinutes aSyncInterval)
	{
	iSyncInterval = aSyncInterval;
	TPckgC<TTimeIntervalMinutes> minutes(iSyncInterval);
	SetCalendarInfoL(KCaldavSyncInterval, minutes);
	}

/**
 * CCalDavEngine::PastDays
 * get past days
 */
TTimeIntervalDays CCalDavEngine::PastDays() const
	{
	return iPastDays;
	}

/**
 * CCalDavEngine::SetPastDaysL
 * Set PastDaysL
 */
void CCalDavEngine::SetPastDaysL(TTimeIntervalDays aDays)
	{
	iPastDays = aDays;
	TPckgC<TTimeIntervalDays> days(iPastDays);
	SetCalendarInfoL(KCaldavPastDays, days);
	}

/**
 * CCalDavEngine::ImmediateSync
 * get ImmediateSyncL
 */
TBool CCalDavEngine::ImmediateSync() const
	{
	return iImmediateSync;
	}

/**
 * CCalDavEngine::SetImmediateSyncL
 * Set ImmediateSyncL
 */
void CCalDavEngine::SetImmediateSyncL(TBool aImmediateSyc)
	{
	iImmediateSync = aImmediateSyc;
	TPckgC<TBool> immediatesync(iImmediateSync);
	SetCalendarInfoL(KCaldavImmediateSync, immediatesync);
	}

/**
 * CCalDavEngine::KeepServerEntry
 * get KeepServerEntryL
 */
TBool CCalDavEngine::KeepServerEntry() const
	{
	return iKeepServerEntry;
	}

/**
 * CCalDavEngine::SetKeepServerEntryL
 * Set KeepServerEntryL
 */
void CCalDavEngine::SetKeepServerEntryL(TBool aKeepServerEntry)
	{
	iKeepServerEntry = aKeepServerEntry;
	TPckgC<TBool> keepserver(iKeepServerEntry);
	SetCalendarInfoL(KCaldavKeepServer, keepserver);
	}

/**
 * CCalDavEngine::Timer
 * get timer
 */
CPeriodic* CCalDavEngine::Timer()
	{
	return iTimer;
	}