From 3c82d9b1245400208b2d167683a82a39cdbc2689 Mon Sep 17 00:00:00 2001
From: Steve Brokenshire <sbrokenshire@xestia.co.uk>
Date: Sat, 30 Apr 2016 21:43:49 +0100
Subject: [PATCH] Added code and unit tests for deleting entries in the CalDAV
 object

Added the following subroutines to the CalDAV object:

GetEntryETag(*string);

Unit tests setup for deleting calendars under CalDAV/GetEntryETag
testing the above function.
---
 .../objects/CalDAV/CalDAV-XMLProcessing.cpp   | 70 ++++++++++++++
 source/objects/CalDAV/CalDAV.cpp              | 95 +++++++++++++++++++
 source/objects/CalDAV/CalDAV.h                |  3 +
 source/tests/xestiacalendar_caldav.h          | 72 ++++++++++++++
 4 files changed, 240 insertions(+)

diff --git a/source/objects/CalDAV/CalDAV-XMLProcessing.cpp b/source/objects/CalDAV/CalDAV-XMLProcessing.cpp
index 38e26c7..f84daed 100644
--- a/source/objects/CalDAV/CalDAV-XMLProcessing.cpp
+++ b/source/objects/CalDAV/CalDAV-XMLProcessing.cpp
@@ -355,6 +355,76 @@ CalDAVCalendarList CalDAV::ProcessXMLCalendarList(){
 	
 }
 
+string CalDAV::ProcessXMLEntryETag(){
+	
+	string EntryETag;
+	
+	xmlDocPtr xmlCalDAVDoc;
+	xmlCalDAVDoc = xmlReadMemory(ServerData.c_str(), (int)ServerData.size(), "noname.xml", NULL, 0);
+
+	xmlNodePtr NodeSeek;
+	bool NodeFound = false;
+	
+	// Start with the first node, look for multistatus.
+	
+	for (NodeSeek = xmlCalDAVDoc->children;
+		NodeSeek != NULL;
+		NodeSeek = NodeSeek->next)
+	{
+	
+		if (!xmlStrcmp(NodeSeek->name, (const xmlChar *)"multistatus") ||
+			!xmlStrcmp(NodeSeek->name, (const xmlChar *)"d:multistatus") ||
+			!xmlStrcmp(NodeSeek->name, (const xmlChar *)"D:multistatus")
+		){
+			
+			NodeFound = true;
+			break;
+			
+		}
+		
+	}
+	
+	// Look for response.
+	
+	if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
+	NodeFound = MatchXMLNameTransverse(&NodeSeek, "response");
+
+	// Look for propstat.
+	
+	if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
+	NodeFound = MatchXMLNameTransverse(&NodeSeek, "propstat");
+
+	// Look for prop.
+	
+	if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
+	NodeFound = MatchXMLNameTransverse(&NodeSeek, "prop");
+	
+	// Look for calendar-home-set.
+	
+	if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
+	NodeFound = MatchXMLNameTransverse(&NodeSeek, "getetag");
+
+	// Get the data from href.
+	
+	EntryETag = FetchXMLData(&NodeSeek);	
+	
+	xmlFreeDoc(xmlCalDAVDoc);	
+	
+	// Check if the entity tag contains quote marks
+	// at the start and end and remove them (if needed).
+	
+	if (EntryETag.substr(0,1) == "\"" && 
+		EntryETag.substr(EntryETag.size()-1, 1) == "\"" && EntryETag.size() > 2){
+		
+		EntryETag.erase(EntryETag.begin());
+		EntryETag.erase(EntryETag.end()-1);
+			
+	}
+	
+	return EntryETag;
+	
+}
+
 bool CalDAV::MatchXMLNameTransverse(xmlNodePtr *NodePtr, string NodeName){
 
 	string NodeNameSmallD = "d:" + NodeName;
diff --git a/source/objects/CalDAV/CalDAV.cpp b/source/objects/CalDAV/CalDAV.cpp
index bc62ff2..d103c81 100644
--- a/source/objects/CalDAV/CalDAV.cpp
+++ b/source/objects/CalDAV/CalDAV.cpp
@@ -908,6 +908,101 @@ CalDAVServerResult CalDAV::DeleteCalendar(string *CalendarHREF){
 	
 }
 
+CalDAVServerResult CalDAV::GetEntryETag(string *CalendarEntryHREF, string *ETagValue){
+	
+	CalDAVServerResult ServerResult;
+	CalDAVSendData EntryETagGetData;
+	
+	// Build the server address.
+	
+	string UserPrincipalURI = "";
+	UserPrincipalURI = GetUserPrincipal();
+	
+	if (UserPrincipalURI.size() == 0){
+		
+		return ServerResult;
+		
+	}
+	
+	string CalendarHomeURI = "";
+	CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
+
+	// Split the path and filename.
+	
+	string EntryURIPath;
+	string EntryFilename;
+	
+	SplitPathFilename(CalendarEntryHREF, &EntryURIPath, &EntryFilename);
+	
+	// Build the request for the server.
+
+	string EntryETagRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+	"<c:calendar-multiget xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
+	" <d:prop>\n"
+	"  <d:getetag />\n"
+	" </d:prop>\n"
+	" <d:href>";
+	EntryETagRequest += (*CalendarEntryHREF);
+	EntryETagRequest += "</d:href>\n"
+	"</c:calendar-multiget>";
+	
+	EntryETagGetData.readptr = &EntryETagRequest;
+	EntryETagGetData.sizeleft = EntryETagRequest.size();
+	
+	// Build the calendar list address.
+	
+	struct curl_slist *GetETagRequestHeader = NULL;
+
+	GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Depth: 1");	
+	GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Prefer: return-minimal");
+	GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Content-Type: application/xml; charset=utf-8");
+	
+	string GetETagURLAddress = BuildServerAddress(&ConnectionData, EntryURIPath);
+	
+	curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, GetETagRequestHeader);
+	curl_easy_setopt(ConnectionHandle, CURLOPT_URL, GetETagURLAddress.c_str());
+	curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT");
+	curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
+	curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &EntryETagGetData);
+	curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
+	
+	// Attempt to get the entity tag.
+	
+	ServerData.clear();
+	ServerHeader.clear();
+	
+	CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
+	
+	if (ServerConnectionResult == CURLE_OK){
+		ServerResult.Result = CALDAVQUERYRESULT_OK;
+	} else {
+		ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;		
+	}
+	ServerResult.Code = ServerConnectionResult;
+	curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
+	
+	if (ServerConnectionResult != CURLE_OK){
+		return ServerResult;
+	}
+	
+	// Get the entity tag from the result.
+	
+	*ETagValue = ProcessXMLEntryETag();
+	
+	// Restore the original settings.
+	
+	string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
+	curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
+	curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);	
+	curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
+	curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
+	curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
+	curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
+	
+	return ServerResult;
+	
+}
+
 CalDAVServerResult CalDAV::AddEntry(string *CalendarEntryHREF, string *EntryData){
 	
 	// Add an entry to the calendar collection.
diff --git a/source/objects/CalDAV/CalDAV.h b/source/objects/CalDAV/CalDAV.h
index f990b76..d632a60 100644
--- a/source/objects/CalDAV/CalDAV.h
+++ b/source/objects/CalDAV/CalDAV.h
@@ -123,6 +123,7 @@ class CalDAV{
 		string ProcessXMLUserPrincipal();
 		string ProcessXMLCalendarHome();
 		CalDAVCalendarList ProcessXMLCalendarList();
+		string ProcessXMLEntryETag();
 		bool MatchXMLNameTransverse(xmlNodePtr *NodePtr, string NodeName);
 		bool MatchXMLName(xmlNodePtr *NodePtrOriginal, string NodeName);
 		string FetchXMLData(xmlNodePtr *NodePtr);
@@ -172,6 +173,8 @@ class CalDAV{
 	
 		string GetUserPrincipal();
 		string GetCalendarHome(string UserPrincipalURI);
+		
+		CalDAVServerResult GetEntryETag(string *CalendarEntryHREF, string *ETagValue);
 	
 };
 
diff --git a/source/tests/xestiacalendar_caldav.h b/source/tests/xestiacalendar_caldav.h
index bbbef3d..e07fe2a 100644
--- a/source/tests/xestiacalendar_caldav.h
+++ b/source/tests/xestiacalendar_caldav.h
@@ -921,6 +921,78 @@ TEST(CalDAV, AddEntry){
 	EntryCalendarHREFProcessing = EntryAddHREF;
 	
 }
+
+TEST(CalDAV, GetEntryETag){
+	
+	CalDAVConnectionData ConnNormal;
+	string CurrentUserPrincipal;
+
+	bool ValidDataNormal = false;
+	
+	// Attempt to read the caldavtest.auth file.
+
+	ProcessConnectionDataFileResult	DataFileResult = ProcessConnectionDataFile("caldavtest.auth", &ConnNormal);
+	if (DataFileResult == PROCESSCONNECTIONDATAFILE_OK){
+		ValidDataNormal = true;
+	}
+
+	ASSERT_EQ(true, ValidDataNormal);
+	
+	// Setup the connection.
+	
+	CalDAV ServerConnection;
+	
+	ServerConnection.SetupConnectionData(&ConnNormal);
+	
+	// Verify the connection settings.
+	
+	CalDAVStatus CalDAVStatus = ServerConnection.GetConnectionData();
+	
+	ASSERT_EQ(CalDAVStatus.Hostname, ConnNormal.Hostname);
+	ASSERT_EQ(CalDAVStatus.Username, ConnNormal.Username);
+	ASSERT_EQ(CalDAVStatus.Port, ConnNormal.Port);
+	ASSERT_EQ(CalDAVStatus.Prefix, ConnNormal.Prefix);
+	ASSERT_EQ(CalDAVStatus.UseSSL, ConnNormal.UseSSL);
+	
+	// Connect to the server.
+	
+	CalDAVServerResult ConnResult = ServerConnection.Connect();
+	
+	EXPECT_EQ(CALDAVQUERYRESULT_OK, ConnResult.Result);
+	ASSERT_EQ(200, ConnResult.HTTPCode);
+	ASSERT_EQ(CURLE_OK, ConnResult.Code);
+	
+	// Check that the server supports CalDAV.
+	
+	CalDAVServerSupport ConnSupport = ServerConnection.GetServerSupport();
+	ConnResult = ServerConnection.GetServerResult();
+	
+	EXPECT_EQ(CALDAVQUERYRESULT_OK, ConnResult.Result);
+	ASSERT_EQ(200, ConnResult.HTTPCode);
+	ASSERT_EQ(CURLE_OK, ConnResult.Code);
+	ASSERT_EQ(true, ConnSupport.BasicSupport);
+	
+	// Get the list of calendars.
+	
+	CalDAVCalendarList CalendarList = ServerConnection.GetCalendars();
+	
+	// Check the response result from the server.
+	
+	ConnResult = ServerConnection.GetServerResult();
+	
+	EXPECT_EQ(CALDAVQUERYRESULT_OK, ConnResult.Result);
+	ASSERT_EQ(207, ConnResult.HTTPCode);
+	ASSERT_EQ(CURLE_OK, ConnResult.Code);
+	
+	string ETagValue;
+	
+	ConnResult = ServerConnection.GetEntryETag(&EntryCalendarHREFProcessing, &ETagValue);
+	
+	EXPECT_EQ(CALDAVQUERYRESULT_OK, ConnResult.Result);
+	ASSERT_EQ(207, ConnResult.HTTPCode);
+	ASSERT_EQ(CURLE_OK, ConnResult.Code);
+	
+}
 	
 }
 
-- 
2.39.5