From f318d43d685eda47b653a28c82a5baffbfc7edd4 Mon Sep 17 00:00:00 2001
From: Steve Brokenshire <sbrokenshire@xestia.co.uk>
Date: Sun, 4 Sep 2016 20:36:23 +0100
Subject: [PATCH] Implemented GetContactList in CardDAV2 class

---
 source/carddav2/carddav2.cpp | 279 +++++++++++++++++++++++++++++++++++
 source/carddav2/carddav2.h   |   1 +
 2 files changed, 280 insertions(+)

diff --git a/source/carddav2/carddav2.cpp b/source/carddav2/carddav2.cpp
index 1d8f632..536afac 100644
--- a/source/carddav2/carddav2.cpp
+++ b/source/carddav2/carddav2.cpp
@@ -1172,6 +1172,126 @@ COServerResponse CardDAV2::GetContact(std::string Location, std::string *Contact
 
 COContactList CardDAV2::GetContactList(std::string SyncToken){
 	
+	COContactList ServerContactList;
+	
+	// Check if authentication was successful, otherwise don't do anything.
+	
+	if (AuthPassed == false){
+		ServerContactList.ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
+		ServerContactList.ServerResponse.EntityTag = "";
+		ServerContactList.ServerResponse.SessionCode = 0;
+		ServerContactList.ServerResponse.ResultCode = 0;
+		ServerContactList.ServerResponse.ResultMessage = "";
+		return ServerContactList;
+	}
+
+	ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
+	ResetResults();
+	
+	std::string SyncData;
+	
+	// TODO: Copy old code from CardDAV class as needed.
+	
+	if (SyncToken.size() > 0){
+		
+		SyncData = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+		"<D:sync-collection xmlns:D=\"DAV:\"\n"
+		" xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
+		"<D:sync-token>";
+		SyncData.append(SyncToken);
+		SyncData.append("</D:sync-token>\n"
+		"<D:sync-level>1</D:sync-level>\n"
+		"<D:prop>\n"
+		"	<D:getetag/>\n"
+		"</D:prop>\n"
+		"</D:sync-collection>");
+	
+	} else {
+		
+		SyncData = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+		"<D:sync-collection xmlns:D=\"DAV:\"\n"
+		" xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
+		"<D:sync-level>1</D:sync-level>\n"
+		"<D:prop>\n"
+		"	<D:getetag/>\n"
+		"</D:prop>\n"
+		"</D:sync-collection>";
+
+	}
+	
+	string ServerAddressURL = BuildURL(ServerPrefix);
+	
+	std::cout << SyncData << std::endl;
+	
+	curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, SyncData.c_str());
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(SyncData.c_str()));
+	curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "REPORT");
+	
+	HeaderList = curl_slist_append(HeaderList, "Content-Type: application/xml; charset=utf-8");
+	HeaderList = curl_slist_append(HeaderList, "Depth: 1");
+
+	curl_easy_setopt(ConnectionSession, CURLOPT_HTTPHEADER, HeaderList);
+	
+	if (TestMode == true){
+		SessionResult = curl_easy_perform(ConnectionSession);
+	} else {
+		SessionResult = curl_easy_perform(ConnectionSession);
+	}
+	
+	switch(SessionResult){
+		case CURLE_OK:
+			SSLStatus = true;
+			SSLVerified = COSSL_VERIFIED;
+			break;
+		case CURLE_SSL_CACERT:
+		case CURLE_SSL_CONNECT_ERROR:
+			SSLStatus = true;
+			SSLVerified = COSSL_UNABLETOVERIFY;
+			break;
+		default:
+			break;
+	};
+	
+	long SessionResponseCode = 0;
+	
+	curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
+	
+	if (SessionResponseCode == 207){
+		AuthPassed = true;
+		ValidResponse = true;
+	} else if (SessionResponseCode == 403){
+		AuthPassed = false;
+		ValidResponse = true;
+	} else if (SessionResponseCode >= 400){
+		AuthPassed = false;
+		ValidResponse = true;
+	} else {
+		AuthPassed = false;
+		ValidResponse = false;			
+	}
+	
+	if (ValidResponse == false || AuthPassed == false){
+		ServerContactList.ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
+		ServerContactList.ServerResponse.EntityTag = "";
+		ServerContactList.ServerResponse.SessionCode = SessionResult;
+		ServerContactList.ServerResponse.ResultCode = SessionResponseCode;
+		ServerContactList.ServerResponse.ResultMessage = "";
+		return ServerContactList;
+	}
+	
+	CanProcess = true;
+	
+	ProcessContactData(&ServerContactList);
+	
+	ServerContactList.ServerResponse.RequestResult = COREQUEST_OK;
+	ServerContactList.ServerResponse.EntityTag = "";
+	ServerContactList.ServerResponse.SessionCode = SessionResult;
+	ServerContactList.ServerResponse.ResultCode = SessionResponseCode;
+	ServerContactList.ServerResponse.ResultMessage = SessionErrorBuffer;
+	
+	return ServerContactList;
+	
 }
 	
 bool CardDAV2::CanDoProcessing(){
@@ -1630,4 +1750,163 @@ vector<string> CardDAV2::GetDAVHeader(){
 	
 	return DAVHeaderList;
 	
+}
+
+void CardDAV2::ProcessContactData(COContactList *ContactList){
+	
+	xmlDocPtr xmlCardDAVDoc;
+	xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
+
+	xmlNodePtr MultiStatusNode;
+	xmlNodePtr ResponseNode;
+	xmlNodePtr ResponseDataNode;
+	xmlNodePtr PropStatNode;
+	xmlNodePtr ValueNode;
+	xmlNodePtr ETagNode;
+	xmlNodePtr StatusNode;
+
+	std::string HREFValue;
+	std::string ETagValue;
+	std::string StatusValue;
+	std::string SyncValue;
+
+	// Go through the document!
+
+	MultiStatusNode = xmlCardDAVDoc->children;
+
+	if (MultiStatusNode == nullptr){
+		return;
+	}
+
+	bool SyncTokenFound = false;
+
+	// Tranverse through the catacombs of the response to get our ETag for the file and
+	// the server syncronisation token.
+	
+	for (ResponseNode = MultiStatusNode->children;
+		ResponseNode != nullptr;
+		ResponseNode = ResponseNode->next){
+
+		// Check if tag is response or sync-token.
+
+		if (!xmlStrcmp(ResponseNode->name, (const xmlChar *)"response") ||
+		!xmlStrcmp(ResponseNode->name, (const xmlChar *)"d:response") ||
+		!xmlStrcmp(ResponseNode->name, (const xmlChar *)"D:response")){
+
+			COContactStatus ContactStatus = COCS_UNKNOWN;
+			
+			for (ResponseDataNode = ResponseNode->children;
+				ResponseDataNode != nullptr;
+				ResponseDataNode = ResponseDataNode->next){
+
+				if (!xmlStrcmp(ResponseDataNode->name, (const xmlChar *)"href") ||
+				!xmlStrcmp(ResponseDataNode->name, (const xmlChar *)"d:href") ||
+				!xmlStrcmp(ResponseDataNode->name, (const xmlChar *)"D:href")){
+				
+					HREFValue = (const char*)ResponseDataNode->children->content;
+
+					// Get the filename after the last forward slash.
+					
+					int LastSlash = 0;
+					
+					for (int HREFValueSeek = 0; HREFValueSeek < HREFValue.size(); HREFValueSeek++){
+						
+						if (HREFValue[HREFValueSeek] == '/'){
+						
+							LastSlash = HREFValueSeek;
+							
+						}
+						
+					}
+					
+					HREFValue = HREFValue.substr((LastSlash + 1));
+					
+				} else if (!xmlStrcmp(ResponseDataNode->name, (const xmlChar *)"propstat") ||
+				!xmlStrcmp(ResponseDataNode->name, (const xmlChar *)"d:propstat") ||
+				!xmlStrcmp(ResponseDataNode->name, (const xmlChar *)"D:propstat")){
+
+					for (PropStatNode = ResponseDataNode->children;
+						PropStatNode != nullptr;
+						PropStatNode = PropStatNode->next){
+
+						if (!xmlStrcmp(PropStatNode->name, (const xmlChar *)"prop") ||
+							!xmlStrcmp(PropStatNode->name, (const xmlChar *)"d:prop") ||
+							!xmlStrcmp(PropStatNode->name, (const xmlChar *)"D:prop")){
+
+							for (ETagNode = PropStatNode->children;
+								ETagNode != nullptr;
+								ETagNode = ETagNode->next){
+
+									if (!xmlStrcmp(ETagNode->name, (const xmlChar *)"getetag") ||
+									!xmlStrcmp(ETagNode->name, (const xmlChar *)"getetag") ||
+									!xmlStrcmp(ETagNode->name, (const xmlChar *)"getetag")){
+
+										ETagValue = (const char*)ETagNode->children->content;
+
+										if (ETagValue.size() > 2 && ETagValue.substr(0,1) == "\""){
+											ETagValue.erase((ETagValue.size() - 1),1);
+											ETagValue.erase(0,1);
+										}
+
+									}
+							
+
+							}
+
+						} else if (!xmlStrcmp(PropStatNode->name, (const xmlChar *)"status") ||
+							!xmlStrcmp(PropStatNode->name, (const xmlChar *)"d:status") ||
+							!xmlStrcmp(PropStatNode->name, (const xmlChar *)"D:status")){
+
+							StatusValue = (const char*)PropStatNode->children->content;
+
+							if (StatusValue == "HTTP/1.1 200 OK"){
+
+								ContactStatus = COCS_UPDATED;
+
+							} else if (StatusValue == "HTTP/1.1 404 Not Found"){
+
+								ContactStatus = COCS_DELETED;
+
+							} else {
+
+								ContactStatus = COCS_UNKNOWN;
+
+							}
+
+						}
+
+					}
+
+				}
+
+			}
+
+			COContactData ContactInformation;
+			
+			ContactInformation.Location = HREFValue;
+			ContactInformation.Data = ETagValue;
+			ContactInformation.Status = ContactStatus;
+
+			HREFValue.clear();
+			ETagValue.clear();
+			StatusValue.clear();
+			
+			ContactList->ListData.push_back(ContactInformation);
+
+		} else if (!xmlStrcmp(ResponseNode->name, (const xmlChar *)"sync-token") ||
+			!xmlStrcmp(ResponseNode->name, (const xmlChar *)"d:sync-token") ||
+			!xmlStrcmp(ResponseNode->name, (const xmlChar *)"D:sync-token")){
+
+			SyncValue = (const char*)ResponseNode->children->content;
+
+		}
+
+	}
+
+	ContactList->SyncToken = SyncValue;
+
+	xmlFreeDoc(xmlCardDAVDoc);
+
+	return;
+	
 }
\ No newline at end of file
diff --git a/source/carddav2/carddav2.h b/source/carddav2/carddav2.h
index ceca5c2..2698d5a 100644
--- a/source/carddav2/carddav2.h
+++ b/source/carddav2/carddav2.h
@@ -104,6 +104,7 @@ class CardDAV2 : public ConnectionObject {
 		std::string GetUserPrincipalURI();
 		std::string GetAddressBookHomeURI();
 		std::string GetDefaultAddressBookURI();
+		void ProcessContactData(COContactList *ContactList);
 	
 #if defined(__APPLE__)
 #elif defined(__WIN32__)
-- 
2.39.5