From: Steve Brokenshire <sbrokenshire@xestia.co.uk>
Date: Fri, 19 Aug 2016 23:13:49 +0000 (+0100)
Subject: Initial version of CardDAV2 class
X-Git-Tag: release-0.15~23
X-Git-Url: http://Server1/repobrowser/?a=commitdiff_plain;h=daa33d641181d15710614cad752187a87263bcc9;p=xestiaab%2F.git

Initial version of CardDAV2 class

Inherits from the ConnectionObject interface.
---

diff --git a/source/carddav2/carddav2.cpp b/source/carddav2/carddav2.cpp
new file mode 100644
index 0000000..f5bc2a3
--- /dev/null
+++ b/source/carddav2/carddav2.cpp
@@ -0,0 +1,980 @@
+// CardDAV2.cpp - CardDAV v2 class
+//
+// (c) 2012-2016 Xestia Software Development.
+//
+// This file is part of Xestia Address Book.
+//
+// Xestia Address Book is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by the
+// Free Software Foundation, version 3 of the license.
+//
+// Xestia Address Book is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with Xestia Address Book. If not, see <http://www.gnu.org/licenses/> 
+
+#include "carddav2.h"
+
+#include <iostream>
+
+using namespace std;
+
+size_t CardDAV2::WritebackFunc(char *ptr, size_t size, size_t nmemb, void *stream){
+	
+	return static_cast<CardDAV2*>(stream)->WritebackFuncImplementation(ptr, size, nmemb, stream);
+	
+}
+	
+size_t CardDAV2::WritebackFuncImplementation(char *ptr, size_t size, size_t nmemb, void *stream){
+	
+	// Writeback function for the CardDAV object.
+		
+	string *data = static_cast<string*>(stream);
+	data->append(ptr);
+	
+	// Get the SSL engine pointer and trust if required on certain operating systems.
+	
+	if (ServerSSL){
+	
+#if defined(__APPLE__)
+	
+		const struct curl_tlssessioninfo *TLSInfo;
+		CURLcode TLSCode;
+		CURL *Connection = GetConnectionObject();
+		TLSCode = curl_easy_getinfo(Connection, CURLINFO_TLS_SSL_PTR, &TLSInfo);
+	
+		if (TLSInfo->internals != nullptr && TLSCode == CURLE_OK){
+			SSLCopyPeerTrust((SSLContext*)TLSInfo->internals, &SecTrustObject);
+		}
+	
+#elif defined(__WIN32__)
+
+		const struct curl_tlssessioninfo *TLSInfo;
+		CURLcode TLSCode;
+		CURL *Connection = GetConnectionObject();
+		TLSCode = curl_easy_getinfo(Connection, CURLINFO_TLS_SSL_PTR, &TLSInfo);
+
+		if (TLSInfo->internals != nullptr && TLSCode == CURLE_OK){
+
+			// Free the previous certificate data.
+
+			CertFreeCertificateContext(CertificateData);
+
+			PCtxtHandle SSLHandle = (PCtxtHandle)TLSInfo->internals;
+			SECURITY_STATUS GetData = QueryContextAttributes(SSLHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &CertificateData);
+
+		}
+
+#endif
+
+	}
+	
+	return size * nmemb;
+
+}
+
+CardDAV2::~CardDAV2(){
+	curl_easy_cleanup(ConnectionSession);
+	ConnectionSession = nullptr;
+}
+
+#if defined(__APPLE__)
+
+#elif defined(__WIN32__)
+
+#else
+
+SSLCertCollectionString CardDAV2::BuildSSLCollection(){
+
+	// Build and return the SSL collection.
+	
+	SSLCertCollectionString SSLCertInfo;
+
+	// Grab the certificate data.
+
+	union {
+		struct curl_slist *certdata;
+		struct curl_certinfo *certinfo;
+	} certptr;
+
+	certptr.certdata = NULL;
+	
+	curl_easy_getinfo(ConnectionSession, CURLINFO_CERTINFO, &certptr.certinfo);
+
+	std::string CertPropName;
+	std::string CertPropValue;
+
+	for (int i = 0; i < certptr.certinfo->num_of_certs; i++){
+
+		struct curl_slist *slist;
+		SSLCertDataString SSLCertDataInc;
+		
+		for (slist = certptr.certinfo->certinfo[i]; slist; slist = slist->next){
+			
+			// Using wxStringTokenizer from wxWidgets.
+			
+			wxStringTokenizer CertDataInc(wxString::FromUTF8(slist->data), ":");
+			
+			// Get first token as the property name.
+			
+			CertPropName = CertDataInc.GetNextToken().ToStdString();
+			
+			// Get remaining tokens as the property value.
+			
+			while(CertDataInc.HasMoreTokens()){
+			
+				CertPropValue.append(CertDataInc.GetNextToken());
+			
+			}
+			
+			SSLCertDataInc.CertData.insert(std::make_pair(CertPropName, CertPropValue));
+			CertPropName.clear();
+			CertPropValue.clear();
+			
+		}
+	
+		SSLCertInfo.SSLCollection.insert(std::make_pair(i, SSLCertDataInc));
+	
+	}
+	
+	return SSLCertInfo;
+
+}
+
+void CardDAV2::BypassSSLVerification(bool EnableBypass){
+	EnableSSLBypass = EnableBypass;
+	SSLSelfSigned = EnableBypass;
+}
+
+#endif
+
+void CardDAV2::SetupConnectionObject(){
+	ConnectionSession = curl_easy_init();
+}
+
+bool CardDAV2::IsTaskCompleted(){
+	return false;
+}
+
+COConnectResult CardDAV2::Connect(bool DoAuthentication){
+	
+	ServerSSL ? SetupDefaultParametersSSL(DoAuthentication) : SetupDefaultParametersNonSSL(DoAuthentication);
+	ResetResults();
+	
+	COConnectResult ConnectResult = COCONNECT_UNITTESTFAIL;
+	string ServerAddressURL = BuildURL("/principals/");
+
+	curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
+	
+	if (TestMode == true){
+		SessionResult = curl_easy_perform(ConnectionSession);
+	} else {
+		
+	}
+	
+	switch(SessionResult){
+		case CURLE_OK:
+			SSLStatus = true;
+			SSLVerified = COSSL_VERIFIED;
+			ConnectResult = COCONNECT_OK;
+			break;
+		case CURLE_SSL_CACERT:
+		case CURLE_SSL_CONNECT_ERROR:
+			SSLStatus = true;
+			ConnectResult = COCONNECT_OK;
+			SSLVerified = COSSL_UNABLETOVERIFY;
+			break;
+		default:
+			ConnectResult = COCONNECT_INVALID;
+			break;
+	};
+	
+	// Check if an error occured before continuing.
+	
+	// Check if authentication was successful.
+	
+	long SessionResponseCode = 0;
+	
+	curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
+	
+	if (DoAuthentication == true){
+		
+		// Get the HTTP status code (Should be 200 and not 403).
+		// Return error otherwise.
+		
+		if (SessionResponseCode == 200){
+			ConnectResult = COCONNECT_OK;
+			AuthPassed = true;
+			ValidResponse = true;
+		} else if (SessionResponseCode == 403){
+			ConnectResult = COCONNECT_AUTHFAIL;
+			AuthPassed = false;
+			ValidResponse = true;
+		} else if (SessionResponseCode >= 200) {
+			ConnectResult = COCONNECT_INVALID;
+			AuthPassed = false;
+			ValidResponse = true;
+		} else {
+			ConnectResult = COCONNECT_INVALID;
+			AuthPassed = false;
+			ValidResponse = false;			
+		}
+		
+	} else {
+		
+		ValidResponse = true;
+		
+	}
+	
+	// Check the header to see if CardDAV is supported.
+	
+	vector<string> DAVHeaderValues = GetDAVHeader();
+	
+	for (vector<string>::iterator DAVHeaderValuesIter = DAVHeaderValues.begin();
+		DAVHeaderValuesIter != DAVHeaderValues.end(); DAVHeaderValuesIter++){
+		
+		if ((*DAVHeaderValuesIter) == "addressbook"){
+			CanProcess = true;
+			break;
+		}
+			
+	}
+	
+	return ConnectResult;
+	
+}
+
+COServerResponse CardDAV2::GetDefaultPrefix(string *ServerPrefix){
+
+	// Check if authentication was successful, otherwise don't do anything.
+
+	COServerResponse ServerResponse;
+	
+	if (AuthPassed == false){
+		ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
+		ServerResponse.EntityTag = "";
+		ServerResponse.SessionCode = 0;
+		ServerResponse.ResultCode = 0;
+		ServerResponse.ResultMessage = "";
+		return ServerResponse;
+	}
+
+	ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
+	ResetResults();
+	
+	// Need to do three requests:
+	
+	// 1. Get the current user principal URI.
+	// 2. Get the address book home URI.
+	// 3. Get the default address book URI.
+	
+	// Setup the first query finding out where the principal URL is.
+	
+	const char* CurrentUserPrincipalXMLQuery = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+		"<D:propfind xmlns:D=\"DAV:\">\n"
+		" <D:prop>"
+		"  <D:current-user-principal/>\n"
+		" </D:prop>"
+		"</D:propfind>";
+
+	// Setup the second query finding out where the address book home URL is.
+	
+	const char* AddressBookHomeXMLQuery = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+	"<D:propfind xmlns:D=\"DAV:\""
+	"  xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
+	"  <D:prop>\n"
+	"    <C:addressbook-home-set/>\n"
+	"  </D:prop>\n"
+	"</D:propfind>";
+	
+	// Setup the third query finding out where the default address book URL is.
+	
+	const char* DefaultAddressBookXMLQuery = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+	"<D:propfind xmlns:D=\"DAV:\""
+	"  xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
+	"  <D:prop>\n"
+	"    <C:default-addressbook-URL/>\n"	
+	"  </D:prop>\n"
+	"</D:propfind>";
+	
+	string ServerAddressURL = BuildURL("/principals/");
+	curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
+	curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PROPFIND");
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, CurrentUserPrincipalXMLQuery);
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(CurrentUserPrincipalXMLQuery));
+	
+	if (TestMode == true){
+		SessionResult = curl_easy_perform(ConnectionSession);
+	} else {
+		
+	}
+	
+	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 == 200 || SessionResponseCode == 207){
+		AuthPassed = true;
+		ValidResponse = true;
+	} else if (SessionResponseCode == 403){
+		AuthPassed = false;
+		ValidResponse = true;
+	} else if (SessionResponseCode >= 200) {
+		AuthPassed = false;
+		ValidResponse = true;
+	} else {
+		AuthPassed = false;
+		ValidResponse = false;			
+	}
+	
+	if (ValidResponse == false && AuthPassed == false){
+		ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
+		ServerResponse.EntityTag = "";
+		ServerResponse.SessionCode = SessionResult;
+		ServerResponse.ResultCode = SessionResponseCode;
+		ServerResponse.ResultMessage = "";
+		return ServerResponse;
+	}
+	
+	// Process the first response.
+	
+	string UserPrincipalURI = GetUserPrincipalURI();
+	
+	// Cleanup and reset for the second connection.
+	
+	ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
+	ResetResults();
+
+	ServerAddressURL = BuildURL(UserPrincipalURI);
+	curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
+	curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PROPFIND");
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, AddressBookHomeXMLQuery);
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(AddressBookHomeXMLQuery));
+	
+	if (TestMode == true){
+		SessionResult = curl_easy_perform(ConnectionSession);
+	} else {
+		
+	}
+	
+	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;
+	};
+	
+	SessionResponseCode = 0;
+	
+	curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
+	
+	if (SessionResponseCode == 200 || SessionResponseCode == 207){
+		//ConnectResult = COCONNECT_OK;
+		AuthPassed = true;
+		ValidResponse = true;
+	} else if (SessionResponseCode == 403){
+		//ConnectResult = COCONNECT_AUTHFAIL;
+		AuthPassed = false;
+		ValidResponse = true;
+	} else if (SessionResponseCode >= 200) {
+		//ConnectResult = COCONNECT_INVALID;
+		AuthPassed = false;
+		ValidResponse = true;
+	} else {
+		//ConnectResult = COCONNECT_INVALID;
+		AuthPassed = false;
+		ValidResponse = false;			
+	}
+	
+	if (ValidResponse == false && AuthPassed == false){
+		ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
+		ServerResponse.EntityTag = "";
+		ServerResponse.SessionCode = SessionResult;
+		ServerResponse.ResultCode = SessionResponseCode;
+		ServerResponse.ResultMessage = "";
+		return ServerResponse;
+	}
+	
+	// Process the second response.
+	
+	string AddressBookHomeURI = GetAddressBookHomeURI();
+	
+	// Cleanup and reset for the second connection.
+	
+	ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
+	ResetResults();
+	
+	ServerAddressURL = BuildURL(AddressBookHomeURI);
+	curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
+	curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PROPFIND");
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, DefaultAddressBookXMLQuery);
+	curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(DefaultAddressBookXMLQuery));
+	
+	if (TestMode == true){
+		SessionResult = curl_easy_perform(ConnectionSession);
+	} else {
+		
+	}
+	
+	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;
+	};
+	
+	SessionResponseCode = 0;
+	
+	curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
+	
+	if (SessionResponseCode == 200 || SessionResponseCode == 207){
+		AuthPassed = true;
+		ValidResponse = true;
+	} else if (SessionResponseCode == 403){
+		AuthPassed = false;
+		ValidResponse = true;
+	} else if (SessionResponseCode >= 200) {
+		AuthPassed = false;
+		ValidResponse = true;
+	} else {
+		AuthPassed = false;
+		ValidResponse = false;			
+	}
+	
+	if (ValidResponse == false && AuthPassed == false){
+		ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
+		ServerResponse.EntityTag = "";
+		ServerResponse.SessionCode = SessionResult;
+		ServerResponse.ResultCode = SessionResponseCode;
+		ServerResponse.ResultMessage = "";
+		return ServerResponse;
+	}
+	
+	// Process the second response.
+	
+	(*ServerPrefix) = GetDefaultAddressBookURI();
+	
+	CanProcess = true;
+	
+	ServerResponse.RequestResult = COREQUEST_OK;
+	ServerResponse.EntityTag = "";
+	ServerResponse.SessionCode = SessionResult;
+	ServerResponse.ResultCode = SessionResponseCode;
+	ServerResponse.ResultMessage = SessionErrorBuffer;
+	return ServerResponse;
+	
+}
+
+std::string CardDAV2::GetUserPrincipalURI(){
+	
+	xmlDocPtr xmlCardDAVDoc;
+	xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
+	string UserPrincipalURI = "";
+	
+	xmlNodePtr nodeLevel1;
+	xmlNodePtr nodeLevel2;
+	xmlNodePtr nodeLevel3;
+	xmlNodePtr nodeLevel4;
+	xmlNodePtr nodeLevel5;
+	xmlNodePtr nodeLevel6;
+	xmlNodePtr nodeLevel7;
+		
+	for (nodeLevel1 = xmlCardDAVDoc->children;
+		nodeLevel1 != NULL;
+		nodeLevel1 = nodeLevel1->next)
+	{
+
+		for (nodeLevel2 = nodeLevel1->children;
+			nodeLevel2 != NULL;
+			nodeLevel2 = nodeLevel2->next)
+		{
+
+
+			for (nodeLevel3 = nodeLevel2->children;
+			nodeLevel3 != NULL;
+			nodeLevel3 = nodeLevel3->next)
+			{
+			
+				for (nodeLevel4 = nodeLevel3->children;
+				nodeLevel4 != NULL;
+				nodeLevel4 = nodeLevel4->next)
+				{
+			
+					for (nodeLevel5 = nodeLevel4->children;
+					nodeLevel5 != NULL;
+					nodeLevel5 = nodeLevel5->next)
+					{
+			
+						for (nodeLevel6 = nodeLevel5->children;
+						nodeLevel6 != NULL;
+						nodeLevel6 = nodeLevel6->next)
+						{
+			
+							if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
+							!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
+							!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
+							){
+			
+								// Found the <href> part so extract the principal URL address.
+								
+								for (nodeLevel7 = nodeLevel6->children;
+								nodeLevel7 != NULL;
+								nodeLevel7 = nodeLevel7->next)
+								{
+								
+									UserPrincipalURI = ((const char*)nodeLevel7->content);
+
+								}
+			
+							}
+			
+						}
+			
+					}
+			
+				}
+			
+			}
+		
+		}
+		
+	}
+	
+	xmlFreeDoc(xmlCardDAVDoc);
+	
+	return UserPrincipalURI;
+	
+}
+
+std::string CardDAV2::GetAddressBookHomeURI(){
+	
+	xmlDocPtr xmlCardDAVDoc;
+	xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
+	string AddressBookHomeURI = "";
+	
+	xmlNodePtr nodeLevel1;
+	xmlNodePtr nodeLevel2;
+	xmlNodePtr nodeLevel3;
+	xmlNodePtr nodeLevel4;
+	xmlNodePtr nodeLevel5;
+	xmlNodePtr nodeLevel6;
+	xmlNodePtr nodeLevel7;
+		
+	for (nodeLevel1 = xmlCardDAVDoc->children;
+		nodeLevel1 != NULL;
+		nodeLevel1 = nodeLevel1->next)
+	{
+
+		for (nodeLevel2 = nodeLevel1->children;
+			nodeLevel2 != NULL;
+			nodeLevel2 = nodeLevel2->next)
+		{
+
+
+			for (nodeLevel3 = nodeLevel2->children;
+			nodeLevel3 != NULL;
+			nodeLevel3 = nodeLevel3->next)
+			{
+			
+				for (nodeLevel4 = nodeLevel3->children;
+				nodeLevel4 != NULL;
+				nodeLevel4 = nodeLevel4->next)
+				{
+			
+					for (nodeLevel5 = nodeLevel4->children;
+					nodeLevel5 != NULL;
+					nodeLevel5 = nodeLevel5->next)
+					{
+			
+						for (nodeLevel6 = nodeLevel5->children;
+						nodeLevel6 != NULL;
+						nodeLevel6 = nodeLevel6->next)
+						{
+			
+							if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
+							!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
+							!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
+							){
+			
+								// Found the <href> part so extract the principal URL address.
+								
+								for (nodeLevel7 = nodeLevel6->children;
+								nodeLevel7 != NULL;
+								nodeLevel7 = nodeLevel7->next)
+								{
+								
+									AddressBookHomeURI = ((const char*)nodeLevel7->content);
+
+								}
+			
+							}
+			
+						}
+			
+					}
+			
+				}
+			
+			}
+		
+		}
+		
+	}
+	
+	xmlFreeDoc(xmlCardDAVDoc);
+	
+	return AddressBookHomeURI;
+	
+}
+
+std::string CardDAV2::GetDefaultAddressBookURI(){
+	
+	xmlDocPtr xmlCardDAVDoc;
+	xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
+	string DefaultAddressBookURI = "";
+	
+	xmlNodePtr nodeLevel1;
+	xmlNodePtr nodeLevel2;
+	xmlNodePtr nodeLevel3;
+	xmlNodePtr nodeLevel4;
+	xmlNodePtr nodeLevel5;
+	xmlNodePtr nodeLevel6;
+	xmlNodePtr nodeLevel7;
+		
+	for (nodeLevel1 = xmlCardDAVDoc->children;
+		nodeLevel1 != NULL;
+		nodeLevel1 = nodeLevel1->next)
+	{
+
+		for (nodeLevel2 = nodeLevel1->children;
+			nodeLevel2 != NULL;
+			nodeLevel2 = nodeLevel2->next)
+		{
+
+
+			for (nodeLevel3 = nodeLevel2->children;
+			nodeLevel3 != NULL;
+			nodeLevel3 = nodeLevel3->next)
+			{
+			
+				for (nodeLevel4 = nodeLevel3->children;
+				nodeLevel4 != NULL;
+				nodeLevel4 = nodeLevel4->next)
+				{
+			
+					for (nodeLevel5 = nodeLevel4->children;
+					nodeLevel5 != NULL;
+					nodeLevel5 = nodeLevel5->next)
+					{
+			
+						for (nodeLevel6 = nodeLevel5->children;
+						nodeLevel6 != NULL;
+						nodeLevel6 = nodeLevel6->next)
+						{
+			
+							if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
+							!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
+							!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
+							){
+			
+								// Found the <href> part so extract the principal URL address.
+								
+								for (nodeLevel7 = nodeLevel6->children;
+								nodeLevel7 != NULL;
+								nodeLevel7 = nodeLevel7->next)
+								{
+								
+									DefaultAddressBookURI = ((const char*)nodeLevel7->content);
+
+								}
+			
+							}
+			
+						}
+			
+					}
+			
+				}
+			
+			}
+		
+		}
+		
+	}
+	
+	xmlFreeDoc(xmlCardDAVDoc);
+	
+	return DefaultAddressBookURI;
+	
+}
+
+COServerResponse CardDAV2::AddContact(std::string Location, std::string Data){
+	
+}
+
+COServerResponse CardDAV2::EditContact(std::string Location, std::string Data){
+	
+}
+
+COServerResponse CardDAV2::DeleteContact(std::string Location, std::string EntityTag){
+	
+}
+
+COServerResponse CardDAV2::GetServerEntityTagValue(std::string Location){
+	
+}
+
+COServerResponse CardDAV2::GetContact(std::string Location){
+	
+}
+
+COContactList CardDAV2::GetContactList(std::string SyncToken){
+	
+}
+	
+bool CardDAV2::CanDoProcessing(){
+	return CanProcess;
+}
+
+bool CardDAV2::CanDoSSL(){
+	return SSLStatus;
+}
+
+COSSLVerified CardDAV2::SSLVerify(){
+	return SSLVerified;
+}
+
+bool CardDAV2::AbleToLogin(){
+	return AuthPassed;
+}
+
+bool CardDAV2::HasValidResponse(){
+	return ValidResponse;
+}
+
+bool CardDAV2::IsSelfSigned(){
+	return SSLSelfSigned;
+}
+
+void CardDAV2::SetupDefaultParametersNonSSL(bool DoAuthentication){
+	
+	std::string ServerAddress = "";
+
+	string ServerAddressURL = "http://" + ServerAddress + ":" + to_string(ServerPort) + "/";
+	string UsernamePassword = ServerUser + ":" + ServerPass;
+	
+	curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddress.c_str());
+	curl_easy_setopt(ConnectionSession, CURLOPT_NOPROGRESS, 1L);
+	curl_easy_setopt(ConnectionSession, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+	curl_easy_setopt(ConnectionSession, CURLOPT_TIMEOUT, 60);
+	curl_easy_setopt(ConnectionSession, CURLOPT_FAILONERROR, true);
+	curl_easy_setopt(ConnectionSession, CURLOPT_USERAGENT, XSDAB_USERAGENT);
+	curl_easy_setopt(ConnectionSession, CURLOPT_WRITEFUNCTION, CardDAV2::WritebackFunc);
+	curl_easy_setopt(ConnectionSession, CURLOPT_WRITEDATA, &PageData);
+	curl_easy_setopt(ConnectionSession, CURLOPT_WRITEHEADER, &PageHeader);
+	curl_easy_setopt(ConnectionSession, CURLOPT_NOSIGNAL, 1);
+	curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "GET");
+	
+	if (DoAuthentication == true){
+		curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, UsernamePassword.c_str());
+	} else {
+		curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, ":");		
+	}
+	
+}
+
+void CardDAV2::SetupDefaultParametersSSL(bool DoAuthentication){
+	
+	// Setup the default parameters.
+	
+	string ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + "/";
+	string UsernamePassword = ServerUser + ":" + ServerPass;
+	
+	curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
+	curl_easy_setopt(ConnectionSession, CURLOPT_NOPROGRESS, 1L);
+	curl_easy_setopt(ConnectionSession, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+	curl_easy_setopt(ConnectionSession, CURLOPT_TIMEOUT, 60);
+	curl_easy_setopt(ConnectionSession, CURLOPT_FAILONERROR, true);
+	curl_easy_setopt(ConnectionSession, CURLOPT_USERAGENT, XSDAB_USERAGENT);
+	curl_easy_setopt(ConnectionSession, CURLOPT_WRITEFUNCTION, CardDAV2::WritebackFunc);
+	curl_easy_setopt(ConnectionSession, CURLOPT_WRITEDATA, &PageData);
+	curl_easy_setopt(ConnectionSession, CURLOPT_WRITEHEADER, &PageHeader);
+	curl_easy_setopt(ConnectionSession, CURLOPT_ERRORBUFFER, SessionErrorBuffer);
+	curl_easy_setopt(ConnectionSession, CURLOPT_NOSIGNAL, 1);
+	curl_easy_setopt(ConnectionSession, CURLOPT_CERTINFO, 1);
+	curl_easy_setopt(ConnectionSession, CURLOPT_VERBOSE, 1);
+	curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "GET");
+	
+	if (DoAuthentication == true){
+		curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, UsernamePassword.c_str());
+	} else {
+		curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, ":");		
+	}
+	
+	if (EnableSSLBypass == true){
+		curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYHOST, 0);
+		curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYPEER, 0);
+	} else {
+		curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYHOST, 2);
+		curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYPEER, 1);		
+	}
+	
+}
+
+string CardDAV2::BuildURL(string URI){
+	
+	string ServerAddressURL;
+	
+	if (SSLStatus == true){
+		ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + URI;	
+	} else {
+		ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + URI;
+	}
+	
+	return ServerAddressURL;
+	
+}
+
+string CardDAV2::GetErrorMessage(){
+	
+	ErrorMessage = SessionErrorBuffer;	
+	return ErrorMessage;
+	
+}
+
+void CardDAV2::ResetResults(){
+	
+	SSLStatus = false;
+	COSSLVerified SSLVerified = COSSL_NORESULT;
+	ValidResponse = false;
+	AuthPassed = false;
+	CanProcess = false;
+	SSLSelfSigned = false;
+	TaskCompleted = false;
+	ErrorMessage = "";
+	SessionErrorBuffer[0] = '\0';
+	PageData = "";
+	PageHeader = "";
+	
+}
+
+vector<string> CardDAV2::GetDAVHeader(){
+	
+	// Go through each of the lines looking for the
+	// 'DAV:' section.
+	
+	
+	string HeaderName;
+	string HeaderValue;
+	bool DAVFound = false;
+	bool FastForward = false;
+	vector<string> DAVHeaderList;
+	
+	for (int HeaderSeek = 0; HeaderSeek < PageHeader.size(); HeaderSeek++){
+		
+		if (FastForward == true){
+			
+			if (PageHeader[HeaderSeek] == '\n'){
+				FastForward = false;
+			}
+			
+			continue;
+			
+		}
+		
+		try {
+			PageHeader.substr(HeaderSeek, 4) == "DAV:";
+		}
+		
+		catch (const out_of_range &oor){
+			break;
+		}
+		
+		if (PageHeader.substr(HeaderSeek, 4) == "DAV:"){
+			
+			int CharacterSeek = 5;
+			
+			while ((HeaderSeek + CharacterSeek) < PageHeader.size()){
+				
+				if (PageHeader.substr((HeaderSeek + CharacterSeek), 2) == "\r\n"){
+					break;
+				}
+				
+				HeaderValue += PageHeader.substr((HeaderSeek + CharacterSeek), 1);
+				CharacterSeek++;
+			}
+			
+			break;
+			
+		} else {
+			
+			FastForward = true;
+			continue;
+			
+		}
+		
+		if (PageHeader[HeaderSeek] == '\n'){
+			HeaderName = "";
+		}
+		
+		//HeaderName += PageHeader.substr(HeaderSeek, 1);
+		
+	}
+	
+	// Split the header data.
+	
+	std::string DAVHeaderValue;
+	
+	for (int HeaderSeek = 0; HeaderSeek < HeaderValue.size(); HeaderSeek++){
+		
+		if (HeaderValue.substr(HeaderSeek, 1) == ","){
+			DAVHeaderList.push_back(DAVHeaderValue);
+			DAVHeaderValue = "";
+			HeaderSeek++;
+			continue;
+		}
+		
+		DAVHeaderValue += HeaderValue[HeaderSeek];
+		
+	}
+	
+	if (DAVHeaderValue.size() > 0){
+		
+		DAVHeaderList.push_back(DAVHeaderValue);
+		
+	}
+	
+	return DAVHeaderList;
+	
+}
\ No newline at end of file
diff --git a/source/carddav2/carddav2.h b/source/carddav2/carddav2.h
new file mode 100644
index 0000000..0c41fdb
--- /dev/null
+++ b/source/carddav2/carddav2.h
@@ -0,0 +1,111 @@
+// CardDAV2.h - CardDAV v2 class header
+//
+// (c) 2012-2016 Xestia Software Development.
+//
+// This file is part of Xestia Address Book.
+//
+// Xestia Address Book is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by the
+// Free Software Foundation, version 3 of the license.
+//
+// Xestia Address Book is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with Xestia Address Book. If not, see <http://www.gnu.org/licenses/> 
+
+#ifndef __CARDDAV2_CARDDAV2_H__
+#define __CARDDAV2_CARDDAV2_H__
+
+#include "../connobject/ConnectionObject.h"
+#include "../version.h"
+#include "../common/sslcertstructs.h"
+
+#include <curl/curl.h>
+#include <wx/tokenzr.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <string>
+#include <vector>
+
+class CardDAV2 : public ConnectionObject {
+	
+	public:
+	
+		using ConnectionObject::ConnectionObject;
+	
+		// Destructor.
+	
+		~CardDAV2();
+	
+		// Functions from the ConnectionObject interface.
+	
+		void SetupConnectionObject();
+		
+		bool IsTaskCompleted();
+		
+		COConnectResult Connect(bool DoAuthentication);
+		
+		COServerResponse GetDefaultPrefix(std::string *ServerPrefix);
+		COServerResponse AddContact(std::string Location, std::string Data);
+		COServerResponse EditContact(std::string Location, std::string Data);
+		COServerResponse DeleteContact(std::string Location, std::string EntityTag);
+		COServerResponse GetServerEntityTagValue(std::string Location);
+		COServerResponse GetContact(std::string Location);
+		COContactList GetContactList(std::string SyncToken);
+	
+		bool CanDoProcessing();
+		bool CanDoSSL();
+		COSSLVerified SSLVerify();
+		bool AbleToLogin();
+		bool HasValidResponse();
+		bool IsSelfSigned();
+		std::string GetErrorMessage();
+
+		void BypassSSLVerification(bool EnableBypass);
+
+#if defined(__APPLE__)
+#elif defined(__WIN32__)
+#else
+		SSLCertCollectionString BuildSSLCollection();
+
+#endif
+	
+	protected:
+	private:
+		
+		// Variables to set for the CardDAV2 class.
+		
+		CURL *ConnectionSession = nullptr;
+		CURLcode SessionResult = CURLE_OK;
+	
+		void SetupDefaultParametersNonSSL(bool DoAuthentication);
+		void SetupDefaultParametersSSL(bool DoAuthentication);
+	
+		std::string PageData;
+		std::string PageHeader;
+		char SessionErrorBuffer[CURL_ERROR_SIZE];
+	
+		static size_t WritebackFunc(char *ptr, size_t size, size_t nmemb, void *stream);
+		size_t WritebackFuncImplementation(char *ptr, size_t size, size_t nmemb, void *stream);
+	
+		std::string BuildURL(std::string URI);
+		void ResetResults();
+		std::vector<std::string> GetDAVHeader();
+	
+		std::string GetUserPrincipalURI();
+		std::string GetAddressBookHomeURI();
+		std::string GetDefaultAddressBookURI();
+	
+#if defined(__APPLE__)
+#elif defined(__WIN32__)
+#else
+		bool EnableSSLBypass = false;
+#endif
+	
+};
+
+#endif
\ No newline at end of file