1 // CardDAV2.cpp - CardDAV v2 class
3 // (c) 2012-2016 Xestia Software Development.
5 // This file is part of Xestia Address Book.
7 // Xestia Address Book is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by the
9 // Free Software Foundation, version 3 of the license.
11 // Xestia Address Book is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with Xestia Address Book. If not, see <http://www.gnu.org/licenses/>
25 size_t CardDAV2::WritebackFunc(char *ptr, size_t size, size_t nmemb, void *stream){
27 return static_cast<CardDAV2*>(stream)->WritebackFuncImplementation(ptr, size, nmemb, stream);
31 size_t CardDAV2::WritebackFuncImplementation(char *ptr, size_t size, size_t nmemb, void *stream){
33 // Writeback function for the CardDAV object.
35 string *data = static_cast<string*>(stream);
38 // Get the SSL engine pointer and trust if required on certain operating systems.
42 #if defined(__APPLE__)
44 const struct curl_tlssessioninfo *TLSInfo;
46 CURL *Connection = GetConnectionObject();
47 TLSCode = curl_easy_getinfo(Connection, CURLINFO_TLS_SSL_PTR, &TLSInfo);
49 if (TLSInfo->internals != nullptr && TLSCode == CURLE_OK){
50 SSLCopyPeerTrust((SSLContext*)TLSInfo->internals, &SecTrustObject);
53 #elif defined(__WIN32__)
55 const struct curl_tlssessioninfo *TLSInfo;
57 CURL *Connection = GetConnectionObject();
58 TLSCode = curl_easy_getinfo(Connection, CURLINFO_TLS_SSL_PTR, &TLSInfo);
60 if (TLSInfo->internals != nullptr && TLSCode == CURLE_OK){
62 // Free the previous certificate data.
64 CertFreeCertificateContext(CertificateData);
66 PCtxtHandle SSLHandle = (PCtxtHandle)TLSInfo->internals;
67 SECURITY_STATUS GetData = QueryContextAttributes(SSLHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &CertificateData);
79 CardDAV2::~CardDAV2(){
81 curl_easy_cleanup(ConnectionSession);
82 ConnectionSession = nullptr;
84 if (HeaderList != nullptr){
85 curl_slist_free_all(HeaderList);
91 #if defined(__APPLE__)
93 #elif defined(__WIN32__)
97 SSLCertCollectionString CardDAV2::BuildSSLCollection(){
99 // Build and return the SSL collection.
101 SSLCertCollectionString SSLCertInfo;
103 // Grab the certificate data.
106 struct curl_slist *certdata;
107 struct curl_certinfo *certinfo;
110 certptr.certdata = NULL;
112 curl_easy_getinfo(ConnectionSession, CURLINFO_CERTINFO, &certptr.certinfo);
114 std::string CertPropName;
115 std::string CertPropValue;
117 for (int i = 0; i < certptr.certinfo->num_of_certs; i++){
119 struct curl_slist *slist;
120 SSLCertDataString SSLCertDataInc;
122 for (slist = certptr.certinfo->certinfo[i]; slist; slist = slist->next){
124 // Using wxStringTokenizer from wxWidgets.
126 wxStringTokenizer CertDataInc(wxString::FromUTF8(slist->data), ":");
128 // Get first token as the property name.
130 CertPropName = CertDataInc.GetNextToken().ToStdString();
132 // Get remaining tokens as the property value.
134 while(CertDataInc.HasMoreTokens()){
136 CertPropValue.append(CertDataInc.GetNextToken());
140 SSLCertDataInc.CertData.insert(std::make_pair(CertPropName, CertPropValue));
141 CertPropName.clear();
142 CertPropValue.clear();
146 SSLCertInfo.SSLCollection.insert(std::make_pair(i, SSLCertDataInc));
154 void CardDAV2::BypassSSLVerification(bool EnableBypass){
155 EnableSSLBypass = EnableBypass;
156 SSLSelfSigned = EnableBypass;
161 void CardDAV2::SetupConnectionObject(){
162 ConnectionSession = curl_easy_init();
165 bool CardDAV2::IsTaskCompleted(){
169 COConnectResult CardDAV2::Connect(bool DoAuthentication){
171 ServerSSL ? SetupDefaultParametersSSL(DoAuthentication) : SetupDefaultParametersNonSSL(DoAuthentication);
174 COConnectResult ConnectResult = COCONNECT_UNITTESTFAIL;
175 string ServerAddressURL = BuildURL("/principals/");
177 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
179 if (TestMode == true){
180 SessionResult = curl_easy_perform(ConnectionSession);
182 SessionResult = curl_easy_perform(ConnectionSession);
185 switch(SessionResult){
187 case CURLE_HTTP_RETURNED_ERROR:
189 SSLVerified = COSSL_VERIFIED;
190 ConnectResult = COCONNECT_OK;
192 case CURLE_SSL_CACERT:
193 case CURLE_SSL_CONNECT_ERROR:
195 ConnectResult = COCONNECT_OK;
196 SSLVerified = COSSL_UNABLETOVERIFY;
199 ConnectResult = COCONNECT_INVALID;
203 // Check if an error occured before continuing.
205 // Check if authentication was successful.
207 long SessionResponseCode = 0;
209 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
211 if (DoAuthentication == true){
213 // Get the HTTP status code (Should be 200 and not 403).
214 // Return error otherwise.
216 if (SessionResponseCode == 200){
217 ConnectResult = COCONNECT_OK;
219 ValidResponse = true;
220 } else if (SessionResponseCode == 401){
221 ConnectResult = COCONNECT_AUTHFAIL;
223 ValidResponse = true;
224 } else if (SessionResponseCode >= 200) {
225 ConnectResult = COCONNECT_INVALID;
227 ValidResponse = true;
229 ConnectResult = COCONNECT_INVALID;
231 ValidResponse = false;
236 ValidResponse = true;
240 // Check the header to see if CardDAV is supported.
242 vector<string> DAVHeaderValues = GetDAVHeader();
244 for (vector<string>::iterator DAVHeaderValuesIter = DAVHeaderValues.begin();
245 DAVHeaderValuesIter != DAVHeaderValues.end(); DAVHeaderValuesIter++){
247 if ((*DAVHeaderValuesIter) == "addressbook"){
254 return ConnectResult;
258 COServerResponse CardDAV2::GetDefaultPrefix(string *ServerPrefix){
260 // Check if authentication was successful, otherwise don't do anything.
262 COServerResponse ServerResponse;
264 if (AuthPassed == false){
265 ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
266 ServerResponse.EntityTag = "";
267 ServerResponse.SessionCode = 0;
268 ServerResponse.ResultCode = 0;
269 ServerResponse.ResultMessage = "";
270 return ServerResponse;
273 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
276 // Need to do three requests:
278 // 1. Get the current user principal URI.
279 // 2. Get the address book home URI.
280 // 3. Get the default address book URI.
282 // Setup the first query finding out where the principal URL is.
284 const char* CurrentUserPrincipalXMLQuery = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
285 "<D:propfind xmlns:D=\"DAV:\">\n"
287 " <D:current-user-principal/>\n"
291 // Setup the second query finding out where the address book home URL is.
293 const char* AddressBookHomeXMLQuery = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
294 "<D:propfind xmlns:D=\"DAV:\""
295 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
297 " <C:addressbook-home-set/>\n"
301 // Setup the third query finding out where the default address book URL is.
303 const char* DefaultAddressBookXMLQuery = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
304 "<D:propfind xmlns:D=\"DAV:\""
305 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
307 " <C:default-addressbook-URL/>\n"
311 string ServerAddressURL = BuildURL("/principals/");
312 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
313 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PROPFIND");
314 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, CurrentUserPrincipalXMLQuery);
315 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(CurrentUserPrincipalXMLQuery));
317 if (TestMode == true){
318 SessionResult = curl_easy_perform(ConnectionSession);
323 switch(SessionResult){
326 SSLVerified = COSSL_VERIFIED;
328 case CURLE_SSL_CACERT:
329 case CURLE_SSL_CONNECT_ERROR:
331 SSLVerified = COSSL_UNABLETOVERIFY;
337 long SessionResponseCode = 0;
339 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
341 if (SessionResponseCode == 200 || SessionResponseCode == 207){
343 ValidResponse = true;
344 } else if (SessionResponseCode == 403){
346 ValidResponse = true;
347 } else if (SessionResponseCode >= 400) {
349 ValidResponse = true;
352 ValidResponse = false;
355 if (ValidResponse == false && AuthPassed == false){
356 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
357 ServerResponse.EntityTag = "";
358 ServerResponse.SessionCode = SessionResult;
359 ServerResponse.ResultCode = SessionResponseCode;
360 ServerResponse.ResultMessage = "";
361 return ServerResponse;
364 // Process the first response.
366 string UserPrincipalURI = GetUserPrincipalURI();
368 // Cleanup and reset for the second connection.
370 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
373 ServerAddressURL = BuildURL(UserPrincipalURI);
374 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
375 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PROPFIND");
376 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, AddressBookHomeXMLQuery);
377 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(AddressBookHomeXMLQuery));
379 if (TestMode == true){
380 SessionResult = curl_easy_perform(ConnectionSession);
385 switch(SessionResult){
388 SSLVerified = COSSL_VERIFIED;
390 case CURLE_SSL_CACERT:
391 case CURLE_SSL_CONNECT_ERROR:
393 SSLVerified = COSSL_UNABLETOVERIFY;
399 SessionResponseCode = 0;
401 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
403 if (SessionResponseCode == 200 || SessionResponseCode == 207){
405 ValidResponse = true;
406 } else if (SessionResponseCode == 403){
408 ValidResponse = true;
409 } else if (SessionResponseCode >= 400) {
411 ValidResponse = true;
414 ValidResponse = false;
417 if (ValidResponse == false && AuthPassed == false){
418 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
419 ServerResponse.EntityTag = "";
420 ServerResponse.SessionCode = SessionResult;
421 ServerResponse.ResultCode = SessionResponseCode;
422 ServerResponse.ResultMessage = "";
423 return ServerResponse;
426 // Process the second response.
428 string AddressBookHomeURI = GetAddressBookHomeURI();
430 // Cleanup and reset for the second connection.
432 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
435 ServerAddressURL = BuildURL(AddressBookHomeURI);
436 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
437 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PROPFIND");
438 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, DefaultAddressBookXMLQuery);
439 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(DefaultAddressBookXMLQuery));
441 if (TestMode == true){
442 SessionResult = curl_easy_perform(ConnectionSession);
447 switch(SessionResult){
450 SSLVerified = COSSL_VERIFIED;
452 case CURLE_SSL_CACERT:
453 case CURLE_SSL_CONNECT_ERROR:
455 SSLVerified = COSSL_UNABLETOVERIFY;
461 SessionResponseCode = 0;
463 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
465 if (SessionResponseCode == 200 || SessionResponseCode == 207){
467 ValidResponse = true;
468 } else if (SessionResponseCode == 403){
470 ValidResponse = true;
471 } else if (SessionResponseCode >= 200) {
473 ValidResponse = true;
476 ValidResponse = false;
479 if (ValidResponse == false && AuthPassed == false){
480 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
481 ServerResponse.EntityTag = "";
482 ServerResponse.SessionCode = SessionResult;
483 ServerResponse.ResultCode = SessionResponseCode;
484 ServerResponse.ResultMessage = "";
485 return ServerResponse;
488 // Process the second response.
490 (*ServerPrefix) = GetDefaultAddressBookURI();
494 ServerResponse.RequestResult = COREQUEST_OK;
495 ServerResponse.EntityTag = "";
496 ServerResponse.SessionCode = SessionResult;
497 ServerResponse.ResultCode = SessionResponseCode;
498 ServerResponse.ResultMessage = SessionErrorBuffer;
499 return ServerResponse;
503 std::string CardDAV2::GetUserPrincipalURI(){
505 xmlDocPtr xmlCardDAVDoc;
506 xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
507 string UserPrincipalURI = "";
509 xmlNodePtr nodeLevel1;
510 xmlNodePtr nodeLevel2;
511 xmlNodePtr nodeLevel3;
512 xmlNodePtr nodeLevel4;
513 xmlNodePtr nodeLevel5;
514 xmlNodePtr nodeLevel6;
515 xmlNodePtr nodeLevel7;
517 for (nodeLevel1 = xmlCardDAVDoc->children;
519 nodeLevel1 = nodeLevel1->next)
522 for (nodeLevel2 = nodeLevel1->children;
524 nodeLevel2 = nodeLevel2->next)
528 for (nodeLevel3 = nodeLevel2->children;
530 nodeLevel3 = nodeLevel3->next)
533 for (nodeLevel4 = nodeLevel3->children;
535 nodeLevel4 = nodeLevel4->next)
538 for (nodeLevel5 = nodeLevel4->children;
540 nodeLevel5 = nodeLevel5->next)
543 for (nodeLevel6 = nodeLevel5->children;
545 nodeLevel6 = nodeLevel6->next)
548 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
549 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
550 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
553 // Found the <href> part so extract the principal URL address.
555 for (nodeLevel7 = nodeLevel6->children;
557 nodeLevel7 = nodeLevel7->next)
560 UserPrincipalURI = ((const char*)nodeLevel7->content);
578 xmlFreeDoc(xmlCardDAVDoc);
580 return UserPrincipalURI;
584 std::string CardDAV2::GetAddressBookHomeURI(){
586 xmlDocPtr xmlCardDAVDoc;
587 xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
588 string AddressBookHomeURI = "";
590 xmlNodePtr nodeLevel1;
591 xmlNodePtr nodeLevel2;
592 xmlNodePtr nodeLevel3;
593 xmlNodePtr nodeLevel4;
594 xmlNodePtr nodeLevel5;
595 xmlNodePtr nodeLevel6;
596 xmlNodePtr nodeLevel7;
598 for (nodeLevel1 = xmlCardDAVDoc->children;
600 nodeLevel1 = nodeLevel1->next)
603 for (nodeLevel2 = nodeLevel1->children;
605 nodeLevel2 = nodeLevel2->next)
609 for (nodeLevel3 = nodeLevel2->children;
611 nodeLevel3 = nodeLevel3->next)
614 for (nodeLevel4 = nodeLevel3->children;
616 nodeLevel4 = nodeLevel4->next)
619 for (nodeLevel5 = nodeLevel4->children;
621 nodeLevel5 = nodeLevel5->next)
624 for (nodeLevel6 = nodeLevel5->children;
626 nodeLevel6 = nodeLevel6->next)
629 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
630 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
631 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
634 // Found the <href> part so extract the principal URL address.
636 for (nodeLevel7 = nodeLevel6->children;
638 nodeLevel7 = nodeLevel7->next)
641 AddressBookHomeURI = ((const char*)nodeLevel7->content);
659 xmlFreeDoc(xmlCardDAVDoc);
661 return AddressBookHomeURI;
665 std::string CardDAV2::GetDefaultAddressBookURI(){
667 xmlDocPtr xmlCardDAVDoc;
668 xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
669 string DefaultAddressBookURI = "";
671 xmlNodePtr nodeLevel1;
672 xmlNodePtr nodeLevel2;
673 xmlNodePtr nodeLevel3;
674 xmlNodePtr nodeLevel4;
675 xmlNodePtr nodeLevel5;
676 xmlNodePtr nodeLevel6;
677 xmlNodePtr nodeLevel7;
679 for (nodeLevel1 = xmlCardDAVDoc->children;
681 nodeLevel1 = nodeLevel1->next)
684 for (nodeLevel2 = nodeLevel1->children;
686 nodeLevel2 = nodeLevel2->next)
690 for (nodeLevel3 = nodeLevel2->children;
692 nodeLevel3 = nodeLevel3->next)
695 for (nodeLevel4 = nodeLevel3->children;
697 nodeLevel4 = nodeLevel4->next)
700 for (nodeLevel5 = nodeLevel4->children;
702 nodeLevel5 = nodeLevel5->next)
705 for (nodeLevel6 = nodeLevel5->children;
707 nodeLevel6 = nodeLevel6->next)
710 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
711 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
712 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
715 // Found the <href> part so extract the principal URL address.
717 for (nodeLevel7 = nodeLevel6->children;
719 nodeLevel7 = nodeLevel7->next)
722 DefaultAddressBookURI = ((const char*)nodeLevel7->content);
740 xmlFreeDoc(xmlCardDAVDoc);
742 return DefaultAddressBookURI;
746 COServerResponse CardDAV2::AddContact(std::string Location, std::string Data){
748 // Check if authentication was successful, otherwise don't do anything.
750 COServerResponse ServerResponse;
752 if (AuthPassed == false){
753 ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
754 ServerResponse.EntityTag = "";
755 ServerResponse.SessionCode = 0;
756 ServerResponse.ResultCode = 0;
757 ServerResponse.ResultMessage = "";
758 return ServerResponse;
761 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
764 string ServerAddressURL = BuildURL(ServerPrefix + Location);
765 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
766 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PUT");
767 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, Data.c_str());
768 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(Data.c_str()));
770 HeaderList = curl_slist_append(HeaderList, "Content-Type: text/vcard; charset=utf-8");
772 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPHEADER, HeaderList);
774 if (TestMode == true){
775 SessionResult = curl_easy_perform(ConnectionSession);
777 SessionResult = curl_easy_perform(ConnectionSession);
780 switch(SessionResult){
783 SSLVerified = COSSL_VERIFIED;
785 case CURLE_SSL_CACERT:
786 case CURLE_SSL_CONNECT_ERROR:
788 SSLVerified = COSSL_UNABLETOVERIFY;
794 long SessionResponseCode = 0;
796 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
798 if (SessionResponseCode == 200 || SessionResponseCode == 201 || SessionResponseCode == 204){
800 ValidResponse = true;
801 } else if (SessionResponseCode == 403){
803 ValidResponse = true;
804 } else if (SessionResponseCode >= 400){
806 ValidResponse = true;
809 ValidResponse = false;
812 if (ValidResponse == false && AuthPassed == false){
813 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
814 ServerResponse.EntityTag = "";
815 ServerResponse.SessionCode = SessionResult;
816 ServerResponse.ResultCode = SessionResponseCode;
817 ServerResponse.ResultMessage = "";
818 return ServerResponse;
823 ServerResponse.RequestResult = COREQUEST_OK;
824 ServerResponse.EntityTag = "";
825 ServerResponse.SessionCode = SessionResult;
826 ServerResponse.ResultCode = SessionResponseCode;
827 ServerResponse.ResultMessage = SessionErrorBuffer;
828 return ServerResponse;
832 COServerResponse CardDAV2::EditContact(std::string Location, std::string Data){
836 COServerResponse CardDAV2::DeleteContact(std::string Location, std::string EntityTag){
840 COServerResponse CardDAV2::GetServerEntityTagValue(std::string Location){
842 // Check if authentication was successful, otherwise don't do anything.
844 COServerResponse ServerResponse;
846 if (AuthPassed == false){
847 ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
848 ServerResponse.EntityTag = "";
849 ServerResponse.SessionCode = 0;
850 ServerResponse.ResultCode = 0;
851 ServerResponse.ResultMessage = "";
852 return ServerResponse;
855 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
858 static const char* GetETagQuery =
859 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
860 "<C:addressbook-query xmlns:D=\"DAV:\""
861 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">"
862 "<D:prop><D:getetag/>"
865 "</C:addressbook-query>";
867 string ServerAddressURL = BuildURL(ServerPrefix + Location);
868 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
869 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "REPORT");
870 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, GetETagQuery);
871 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(GetETagQuery));
873 if (TestMode == true){
874 SessionResult = curl_easy_perform(ConnectionSession);
876 SessionResult = curl_easy_perform(ConnectionSession);
879 switch(SessionResult){
882 SSLVerified = COSSL_VERIFIED;
884 case CURLE_SSL_CACERT:
885 case CURLE_SSL_CONNECT_ERROR:
887 SSLVerified = COSSL_UNABLETOVERIFY;
893 long SessionResponseCode = 0;
895 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
897 if (SessionResponseCode == 207){
899 ValidResponse = true;
900 } else if (SessionResponseCode == 403){
902 ValidResponse = true;
903 } else if (SessionResponseCode >= 400){
905 ValidResponse = true;
908 ValidResponse = false;
911 if (ValidResponse == false && AuthPassed == false){
912 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
913 ServerResponse.EntityTag = "";
914 ServerResponse.SessionCode = SessionResult;
915 ServerResponse.ResultCode = SessionResponseCode;
916 ServerResponse.ResultMessage = "";
917 return ServerResponse;
922 ServerResponse.RequestResult = COREQUEST_OK;
923 ServerResponse.EntityTag = GetETagValue();
924 ServerResponse.SessionCode = SessionResult;
925 ServerResponse.ResultCode = SessionResponseCode;
926 ServerResponse.ResultMessage = SessionErrorBuffer;
928 return ServerResponse;
932 COServerResponse CardDAV2::GetContact(std::string Location){
936 COContactList CardDAV2::GetContactList(std::string SyncToken){
940 bool CardDAV2::CanDoProcessing(){
944 bool CardDAV2::CanDoSSL(){
948 COSSLVerified CardDAV2::SSLVerify(){
952 bool CardDAV2::AbleToLogin(){
956 bool CardDAV2::HasValidResponse(){
957 return ValidResponse;
960 bool CardDAV2::IsSelfSigned(){
961 return SSLSelfSigned;
964 void CardDAV2::SetupDefaultParametersNonSSL(bool DoAuthentication){
966 std::string ServerAddress = "";
968 string ServerAddressURL = "http://" + ServerAddress + ":" + to_string(ServerPort) + "/";
969 string UsernamePassword = ServerUser + ":" + ServerPass;
971 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddress.c_str());
972 curl_easy_setopt(ConnectionSession, CURLOPT_NOPROGRESS, 1L);
973 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
974 curl_easy_setopt(ConnectionSession, CURLOPT_TIMEOUT, 60);
975 curl_easy_setopt(ConnectionSession, CURLOPT_FAILONERROR, true);
976 curl_easy_setopt(ConnectionSession, CURLOPT_USERAGENT, XSDAB_USERAGENT);
977 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEFUNCTION, CardDAV2::WritebackFunc);
978 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEDATA, &PageData);
979 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEHEADER, &PageHeader);
980 curl_easy_setopt(ConnectionSession, CURLOPT_NOSIGNAL, 1);
981 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "GET");
982 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPHEADER, nullptr);
984 if (DoAuthentication == true){
985 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, UsernamePassword.c_str());
987 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, ":");
992 void CardDAV2::SetupDefaultParametersSSL(bool DoAuthentication){
994 // Setup the default parameters.
996 string ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + "/";
997 string UsernamePassword = ServerUser + ":" + ServerPass;
999 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
1000 curl_easy_setopt(ConnectionSession, CURLOPT_NOPROGRESS, 1L);
1001 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
1002 curl_easy_setopt(ConnectionSession, CURLOPT_TIMEOUT, 60);
1003 curl_easy_setopt(ConnectionSession, CURLOPT_FAILONERROR, true);
1004 curl_easy_setopt(ConnectionSession, CURLOPT_USERAGENT, XSDAB_USERAGENT);
1005 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEFUNCTION, CardDAV2::WritebackFunc);
1006 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEDATA, &PageData);
1007 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEHEADER, &PageHeader);
1008 curl_easy_setopt(ConnectionSession, CURLOPT_ERRORBUFFER, SessionErrorBuffer);
1009 curl_easy_setopt(ConnectionSession, CURLOPT_NOSIGNAL, 1);
1010 curl_easy_setopt(ConnectionSession, CURLOPT_CERTINFO, 1);
1011 curl_easy_setopt(ConnectionSession, CURLOPT_VERBOSE, 1);
1012 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "GET");
1013 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPHEADER, nullptr);
1015 if (DoAuthentication == true){
1016 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, UsernamePassword.c_str());
1018 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, ":");
1021 if (EnableSSLBypass == true){
1022 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYHOST, 0);
1023 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYPEER, 0);
1025 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYHOST, 2);
1026 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYPEER, 1);
1029 if (TestMode == false && ServerAccount.size() > 0){
1031 // Check if the server certificate file exists.
1033 string CertificateFilename = GetAccountDir(ServerAccount, true);
1035 if (wxFile::Exists(CertificateFilename)){
1037 curl_easy_setopt(ConnectionSession, CURLOPT_CAINFO, CertificateFilename.c_str());
1045 string CardDAV2::BuildURL(string URI){
1047 string ServerAddressURL;
1049 if (SSLStatus == true){
1050 ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + URI;
1052 ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + URI;
1055 return ServerAddressURL;
1059 string CardDAV2::GetErrorMessage(){
1061 ErrorMessage = SessionErrorBuffer;
1062 return ErrorMessage;
1066 void CardDAV2::ResetResults(){
1069 COSSLVerified SSLVerified = COSSL_NORESULT;
1070 ValidResponse = false;
1073 SSLSelfSigned = false;
1074 TaskCompleted = false;
1076 SessionErrorBuffer[0] = '\0';
1079 if (HeaderList != nullptr){
1080 curl_slist_free_all(HeaderList);
1081 HeaderList = nullptr;
1086 string CardDAV2::GetETagValue(){
1088 xmlDocPtr xmlCardDAVDoc;
1090 xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
1092 xmlNodePtr nodeLevel1;
1093 xmlNodePtr nodeLevel2;
1094 xmlNodePtr nodeLevel3;
1095 xmlNodePtr nodeLevel4;
1096 xmlNodePtr nodeLevel5;
1097 xmlNodePtr nodeLevel6;
1099 //std::map<wxString,wxString> xmlDataMap;
1101 std::string DataFilename;
1102 std::string ETagData;
1104 std::string xmlStringSafe;
1105 std::string ETagValue;
1107 // Tranverse through the catacombs of the response to get our ETag for the file.
1109 for (nodeLevel1 = xmlCardDAVDoc->children;
1111 nodeLevel1 = nodeLevel1->next)
1114 bool HREFFound = FALSE;
1115 bool ETagFound = FALSE;
1117 for (nodeLevel2 = nodeLevel1->children;
1119 nodeLevel2 = nodeLevel2->next)
1122 for (nodeLevel3 = nodeLevel2->children;
1124 nodeLevel3 = nodeLevel3->next)
1127 if (!xmlStrcmp(nodeLevel3->name, (const xmlChar *)"href") ||
1128 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"d:href") ||
1129 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"D:href")
1132 // Get the filename.
1134 for (nodeLevel4 = nodeLevel3->children;
1136 nodeLevel4 = nodeLevel4->next)
1139 if (!xmlStrcmp(nodeLevel4->name, (const xmlChar *)"text") ||
1140 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"d:text") ||
1141 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"D:text")
1144 DataFilename = wxString::FromUTF8((const char*)nodeLevel4->content);
1145 wxStringTokenizer wSTDFilename(DataFilename, wxT("/"));
1147 while (wSTDFilename.HasMoreTokens()){
1149 DataFilename = wSTDFilename.GetNextToken().ToStdString();
1163 for (nodeLevel4 = nodeLevel3->children;
1165 nodeLevel4 = nodeLevel4->next)
1168 for (nodeLevel5 = nodeLevel4->children;
1170 nodeLevel5 = nodeLevel5->next)
1173 if (!xmlStrcmp(nodeLevel5->name, (const xmlChar *)"getetag") ||
1174 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"d:getetag") ||
1175 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"D:getetag")
1178 for (nodeLevel6 = nodeLevel5->children;
1180 nodeLevel6 = nodeLevel6->next)
1183 // Strip the quotes from the ETag.
1185 ETagData = (const char*)nodeLevel6->content;
1186 if (ETagData[0] == '"' && ETagData[(ETagData.size() - 1)] == '"'){
1188 ETagData.erase(0, 1);
1189 ETagData.erase((ETagData.size() - 1));
1209 if (HREFFound == TRUE && ETagFound == TRUE){
1211 // Add to the map data.
1213 ETagValue = ETagData;
1224 xmlFreeDoc(xmlCardDAVDoc);
1230 string CardDAV2::GetETagHeader(){
1232 // Go through each of the lines looking for the
1237 bool FastForward = false;
1239 for (int HeaderSeek = 0; HeaderSeek < PageHeader.size(); HeaderSeek++){
1241 if (FastForward == true){
1243 if (PageHeader[HeaderSeek] == '\n'){
1244 FastForward = false;
1252 PageHeader.substr(HeaderSeek, 5) == "ETag:";
1255 catch (const out_of_range &oor){
1259 if (PageHeader.substr(HeaderSeek, 5) == "ETag:"){
1261 int CharacterSeek = 5;
1263 while ((HeaderSeek + CharacterSeek) < PageHeader.size()){
1265 if (PageHeader.substr((HeaderSeek + CharacterSeek), 2) == "\r\n"){
1269 HeaderValue += PageHeader.substr((HeaderSeek + CharacterSeek), 1);
1282 if (PageHeader[HeaderSeek] == '\n'){
1286 //HeaderName += PageHeader.substr(HeaderSeek, 1);
1290 // Check for quotation marks at the start and end and strip
1293 if (HeaderValue.size() >= 2){
1295 if (HeaderValue[0] == '"'){
1296 HeaderValue.erase((HeaderValue.size() - 1));
1297 HeaderValue.erase(0);
1306 vector<string> CardDAV2::GetDAVHeader(){
1308 // Go through each of the lines looking for the
1313 bool FastForward = false;
1314 vector<string> DAVHeaderList;
1316 for (int HeaderSeek = 0; HeaderSeek < PageHeader.size(); HeaderSeek++){
1318 if (FastForward == true){
1320 if (PageHeader[HeaderSeek] == '\n'){
1321 FastForward = false;
1329 PageHeader.substr(HeaderSeek, 4) == "DAV:";
1332 catch (const out_of_range &oor){
1336 if (PageHeader.substr(HeaderSeek, 4) == "DAV:"){
1338 int CharacterSeek = 5;
1340 while ((HeaderSeek + CharacterSeek) < PageHeader.size()){
1342 if (PageHeader.substr((HeaderSeek + CharacterSeek), 2) == "\r\n"){
1346 HeaderValue += PageHeader.substr((HeaderSeek + CharacterSeek), 1);
1359 if (PageHeader[HeaderSeek] == '\n'){
1363 //HeaderName += PageHeader.substr(HeaderSeek, 1);
1367 // Split the header data.
1369 std::string DAVHeaderValue;
1371 for (int HeaderSeek = 0; HeaderSeek < HeaderValue.size(); HeaderSeek++){
1373 if (HeaderValue.substr(HeaderSeek, 1) == ","){
1374 DAVHeaderList.push_back(DAVHeaderValue);
1375 DAVHeaderValue = "";
1380 DAVHeaderValue += HeaderValue[HeaderSeek];
1384 if (DAVHeaderValue.size() > 0){
1386 DAVHeaderList.push_back(DAVHeaderValue);
1390 return DAVHeaderList;