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){
834 // Check if authentication was successful, otherwise don't do anything.
836 COServerResponse ServerResponse;
838 if (AuthPassed == false){
839 ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
840 ServerResponse.EntityTag = "";
841 ServerResponse.SessionCode = 0;
842 ServerResponse.ResultCode = 0;
843 ServerResponse.ResultMessage = "";
844 return ServerResponse;
847 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
850 string ServerAddressURL = BuildURL(ServerPrefix + Location);
851 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
852 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "PUT");
853 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, Data.c_str());
854 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(Data.c_str()));
856 HeaderList = curl_slist_append(HeaderList, "Content-Type: text/vcard; charset=utf-8");
858 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPHEADER, HeaderList);
860 if (TestMode == true){
861 SessionResult = curl_easy_perform(ConnectionSession);
863 SessionResult = curl_easy_perform(ConnectionSession);
866 switch(SessionResult){
869 SSLVerified = COSSL_VERIFIED;
871 case CURLE_SSL_CACERT:
872 case CURLE_SSL_CONNECT_ERROR:
874 SSLVerified = COSSL_UNABLETOVERIFY;
880 long SessionResponseCode = 0;
882 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
884 if (SessionResponseCode == 200 || SessionResponseCode == 201 || SessionResponseCode == 204){
886 ValidResponse = true;
887 } else if (SessionResponseCode == 403){
889 ValidResponse = true;
890 } else if (SessionResponseCode >= 400){
892 ValidResponse = true;
895 ValidResponse = false;
898 if (ValidResponse == false || AuthPassed == false){
899 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
900 ServerResponse.EntityTag = "";
901 ServerResponse.SessionCode = SessionResult;
902 ServerResponse.ResultCode = SessionResponseCode;
903 ServerResponse.ResultMessage = "";
904 return ServerResponse;
909 ServerResponse.RequestResult = COREQUEST_OK;
910 ServerResponse.EntityTag = "";
911 ServerResponse.SessionCode = SessionResult;
912 ServerResponse.ResultCode = SessionResponseCode;
913 ServerResponse.ResultMessage = SessionErrorBuffer;
914 return ServerResponse;
918 COServerResponse CardDAV2::DeleteContact(std::string Location){
920 // Check if authentication was successful, otherwise don't do anything.
922 COServerResponse ServerResponse;
924 if (AuthPassed == false){
925 ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
926 ServerResponse.EntityTag = "";
927 ServerResponse.SessionCode = 0;
928 ServerResponse.ResultCode = 0;
929 ServerResponse.ResultMessage = "";
930 return ServerResponse;
933 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
936 string ServerAddressURL = BuildURL(ServerPrefix + Location);
937 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
938 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "DELETE");
940 if (TestMode == true){
941 SessionResult = curl_easy_perform(ConnectionSession);
943 SessionResult = curl_easy_perform(ConnectionSession);
946 switch(SessionResult){
949 SSLVerified = COSSL_VERIFIED;
951 case CURLE_SSL_CACERT:
952 case CURLE_SSL_CONNECT_ERROR:
954 SSLVerified = COSSL_UNABLETOVERIFY;
960 long SessionResponseCode = 0;
962 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
964 if (SessionResponseCode == 200 || SessionResponseCode == 202 || SessionResponseCode == 204){
966 ValidResponse = true;
967 } else if (SessionResponseCode == 403){
969 ValidResponse = true;
970 } else if (SessionResponseCode >= 400){
972 ValidResponse = true;
975 ValidResponse = false;
978 if (ValidResponse == false || AuthPassed == false){
979 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
980 ServerResponse.EntityTag = "";
981 ServerResponse.SessionCode = SessionResult;
982 ServerResponse.ResultCode = SessionResponseCode;
983 ServerResponse.ResultMessage = "";
984 return ServerResponse;
989 ServerResponse.RequestResult = COREQUEST_OK;
990 ServerResponse.EntityTag = "";
991 ServerResponse.SessionCode = SessionResult;
992 ServerResponse.ResultCode = SessionResponseCode;
993 ServerResponse.ResultMessage = SessionErrorBuffer;
994 return ServerResponse;
998 COServerResponse CardDAV2::GetServerEntityTagValue(std::string Location){
1000 // Check if authentication was successful, otherwise don't do anything.
1002 COServerResponse ServerResponse;
1004 if (AuthPassed == false){
1005 ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
1006 ServerResponse.EntityTag = "";
1007 ServerResponse.SessionCode = 0;
1008 ServerResponse.ResultCode = 0;
1009 ServerResponse.ResultMessage = "";
1010 return ServerResponse;
1013 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
1016 static const char* GetETagQuery =
1017 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1018 "<C:addressbook-query xmlns:D=\"DAV:\""
1019 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">"
1020 "<D:prop><D:getetag/>"
1023 "</C:addressbook-query>";
1025 string ServerAddressURL = BuildURL(ServerPrefix + Location);
1026 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
1027 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "REPORT");
1028 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, GetETagQuery);
1029 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, strlen(GetETagQuery));
1031 if (TestMode == true){
1032 SessionResult = curl_easy_perform(ConnectionSession);
1034 SessionResult = curl_easy_perform(ConnectionSession);
1037 switch(SessionResult){
1040 SSLVerified = COSSL_VERIFIED;
1042 case CURLE_SSL_CACERT:
1043 case CURLE_SSL_CONNECT_ERROR:
1045 SSLVerified = COSSL_UNABLETOVERIFY;
1051 long SessionResponseCode = 0;
1053 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
1055 if (SessionResponseCode == 207){
1057 ValidResponse = true;
1058 } else if (SessionResponseCode == 403){
1060 ValidResponse = true;
1061 } else if (SessionResponseCode >= 400){
1063 ValidResponse = true;
1066 ValidResponse = false;
1069 if (ValidResponse == false || AuthPassed == false){
1070 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
1071 ServerResponse.EntityTag = "";
1072 ServerResponse.SessionCode = SessionResult;
1073 ServerResponse.ResultCode = SessionResponseCode;
1074 ServerResponse.ResultMessage = "";
1075 return ServerResponse;
1080 ServerResponse.RequestResult = COREQUEST_OK;
1081 ServerResponse.EntityTag = GetETagValue();
1082 ServerResponse.SessionCode = SessionResult;
1083 ServerResponse.ResultCode = SessionResponseCode;
1084 ServerResponse.ResultMessage = SessionErrorBuffer;
1086 return ServerResponse;
1090 COServerResponse CardDAV2::GetContact(std::string Location, std::string *ContactData){
1092 // Check if authentication was successful, otherwise don't do anything.
1094 COServerResponse ServerResponse;
1096 if (AuthPassed == false){
1097 ServerResponse.RequestResult = COREQUEST_ERROR_NOTCONNECTED;
1098 ServerResponse.EntityTag = "";
1099 ServerResponse.SessionCode = 0;
1100 ServerResponse.ResultCode = 0;
1101 ServerResponse.ResultMessage = "";
1102 return ServerResponse;
1105 ServerSSL ? SetupDefaultParametersSSL(true) : SetupDefaultParametersNonSSL(true);
1108 string ServerAddressURL = BuildURL(ServerPrefix + Location);
1109 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
1110 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "GET");
1112 if (TestMode == true){
1113 SessionResult = curl_easy_perform(ConnectionSession);
1115 SessionResult = curl_easy_perform(ConnectionSession);
1118 switch(SessionResult){
1121 SSLVerified = COSSL_VERIFIED;
1123 case CURLE_SSL_CACERT:
1124 case CURLE_SSL_CONNECT_ERROR:
1126 SSLVerified = COSSL_UNABLETOVERIFY;
1132 long SessionResponseCode = 0;
1134 curl_easy_getinfo(ConnectionSession, CURLINFO_RESPONSE_CODE, &SessionResponseCode);
1136 if (SessionResponseCode == 200){
1138 ValidResponse = true;
1139 } else if (SessionResponseCode == 403){
1141 ValidResponse = true;
1142 } else if (SessionResponseCode >= 400){
1144 ValidResponse = true;
1147 ValidResponse = false;
1150 if (ValidResponse == false && AuthPassed == false){
1151 ServerResponse.RequestResult = COREQUEST_ERROR_SERVER;
1152 ServerResponse.EntityTag = "";
1153 ServerResponse.SessionCode = SessionResult;
1154 ServerResponse.ResultCode = SessionResponseCode;
1155 ServerResponse.ResultMessage = "";
1156 return ServerResponse;
1161 ServerResponse.RequestResult = COREQUEST_OK;
1162 ServerResponse.EntityTag = "";
1163 ServerResponse.SessionCode = SessionResult;
1164 ServerResponse.ResultCode = SessionResponseCode;
1165 ServerResponse.ResultMessage = SessionErrorBuffer;
1167 (*ContactData) = PageData;
1169 return ServerResponse;
1173 COContactList CardDAV2::GetContactList(std::string SyncToken){
1177 bool CardDAV2::CanDoProcessing(){
1181 bool CardDAV2::CanDoSSL(){
1185 COSSLVerified CardDAV2::SSLVerify(){
1189 bool CardDAV2::AbleToLogin(){
1193 bool CardDAV2::HasValidResponse(){
1194 return ValidResponse;
1197 bool CardDAV2::IsSelfSigned(){
1198 return SSLSelfSigned;
1201 void CardDAV2::SetupDefaultParametersNonSSL(bool DoAuthentication){
1203 std::string ServerAddress = "";
1205 string ServerAddressURL = "http://" + ServerAddress + ":" + to_string(ServerPort) + "/";
1206 string UsernamePassword = ServerUser + ":" + ServerPass;
1208 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddress.c_str());
1209 curl_easy_setopt(ConnectionSession, CURLOPT_NOPROGRESS, 1L);
1210 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
1211 curl_easy_setopt(ConnectionSession, CURLOPT_TIMEOUT, 60);
1212 curl_easy_setopt(ConnectionSession, CURLOPT_FAILONERROR, true);
1213 curl_easy_setopt(ConnectionSession, CURLOPT_USERAGENT, XSDAB_USERAGENT);
1214 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEFUNCTION, CardDAV2::WritebackFunc);
1215 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEDATA, &PageData);
1216 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEHEADER, &PageHeader);
1217 curl_easy_setopt(ConnectionSession, CURLOPT_NOSIGNAL, 1);
1218 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "GET");
1219 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPHEADER, nullptr);
1220 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, nullptr);
1221 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, 0L);
1223 if (DoAuthentication == true){
1224 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, UsernamePassword.c_str());
1226 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, ":");
1231 void CardDAV2::SetupDefaultParametersSSL(bool DoAuthentication){
1233 // Setup the default parameters.
1235 string ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + "/";
1236 string UsernamePassword = ServerUser + ":" + ServerPass;
1238 curl_easy_setopt(ConnectionSession, CURLOPT_URL, ServerAddressURL.c_str());
1239 curl_easy_setopt(ConnectionSession, CURLOPT_NOPROGRESS, 1L);
1240 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
1241 curl_easy_setopt(ConnectionSession, CURLOPT_TIMEOUT, 60);
1242 curl_easy_setopt(ConnectionSession, CURLOPT_FAILONERROR, true);
1243 curl_easy_setopt(ConnectionSession, CURLOPT_USERAGENT, XSDAB_USERAGENT);
1244 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEFUNCTION, CardDAV2::WritebackFunc);
1245 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEDATA, &PageData);
1246 curl_easy_setopt(ConnectionSession, CURLOPT_WRITEHEADER, &PageHeader);
1247 curl_easy_setopt(ConnectionSession, CURLOPT_ERRORBUFFER, SessionErrorBuffer);
1248 curl_easy_setopt(ConnectionSession, CURLOPT_NOSIGNAL, 1);
1249 curl_easy_setopt(ConnectionSession, CURLOPT_CERTINFO, 1);
1250 curl_easy_setopt(ConnectionSession, CURLOPT_VERBOSE, 1);
1251 curl_easy_setopt(ConnectionSession, CURLOPT_CUSTOMREQUEST, "GET");
1252 curl_easy_setopt(ConnectionSession, CURLOPT_HTTPHEADER, nullptr);
1253 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDS, nullptr);
1254 curl_easy_setopt(ConnectionSession, CURLOPT_POSTFIELDSIZE, 0L);
1256 if (DoAuthentication == true){
1257 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, UsernamePassword.c_str());
1259 curl_easy_setopt(ConnectionSession, CURLOPT_USERPWD, ":");
1262 if (EnableSSLBypass == true){
1263 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYHOST, 0);
1264 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYPEER, 0);
1266 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYHOST, 2);
1267 curl_easy_setopt(ConnectionSession, CURLOPT_SSL_VERIFYPEER, 1);
1270 if (TestMode == false && ServerAccount.size() > 0){
1272 // Check if the server certificate file exists.
1274 string CertificateFilename = GetAccountDir(ServerAccount, true);
1276 if (wxFile::Exists(CertificateFilename)){
1278 curl_easy_setopt(ConnectionSession, CURLOPT_CAINFO, CertificateFilename.c_str());
1286 string CardDAV2::BuildURL(string URI){
1288 string ServerAddressURL;
1290 if (SSLStatus == true){
1291 ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + URI;
1293 ServerAddressURL = "https://" + ServerAddress + ":" + to_string(ServerPort) + URI;
1296 return ServerAddressURL;
1300 string CardDAV2::GetErrorMessage(){
1302 ErrorMessage = SessionErrorBuffer;
1303 return ErrorMessage;
1307 void CardDAV2::ResetResults(){
1310 COSSLVerified SSLVerified = COSSL_NORESULT;
1311 ValidResponse = false;
1314 SSLSelfSigned = false;
1315 TaskCompleted = false;
1317 SessionErrorBuffer[0] = '\0';
1320 if (HeaderList != nullptr){
1321 curl_slist_free_all(HeaderList);
1322 HeaderList = nullptr;
1327 string CardDAV2::GetETagValue(){
1329 xmlDocPtr xmlCardDAVDoc;
1331 xmlCardDAVDoc = xmlReadMemory(PageData.c_str(), (int)PageData.size(), "noname.xml", NULL, 0);
1333 xmlNodePtr nodeLevel1;
1334 xmlNodePtr nodeLevel2;
1335 xmlNodePtr nodeLevel3;
1336 xmlNodePtr nodeLevel4;
1337 xmlNodePtr nodeLevel5;
1338 xmlNodePtr nodeLevel6;
1340 //std::map<wxString,wxString> xmlDataMap;
1342 std::string DataFilename;
1343 std::string ETagData;
1345 std::string xmlStringSafe;
1346 std::string ETagValue;
1348 // Tranverse through the catacombs of the response to get our ETag for the file.
1350 for (nodeLevel1 = xmlCardDAVDoc->children;
1352 nodeLevel1 = nodeLevel1->next)
1355 bool HREFFound = FALSE;
1356 bool ETagFound = FALSE;
1358 for (nodeLevel2 = nodeLevel1->children;
1360 nodeLevel2 = nodeLevel2->next)
1363 for (nodeLevel3 = nodeLevel2->children;
1365 nodeLevel3 = nodeLevel3->next)
1368 if (!xmlStrcmp(nodeLevel3->name, (const xmlChar *)"href") ||
1369 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"d:href") ||
1370 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"D:href")
1373 // Get the filename.
1375 for (nodeLevel4 = nodeLevel3->children;
1377 nodeLevel4 = nodeLevel4->next)
1380 if (!xmlStrcmp(nodeLevel4->name, (const xmlChar *)"text") ||
1381 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"d:text") ||
1382 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"D:text")
1385 DataFilename = wxString::FromUTF8((const char*)nodeLevel4->content);
1386 wxStringTokenizer wSTDFilename(DataFilename, wxT("/"));
1388 while (wSTDFilename.HasMoreTokens()){
1390 DataFilename = wSTDFilename.GetNextToken().ToStdString();
1404 for (nodeLevel4 = nodeLevel3->children;
1406 nodeLevel4 = nodeLevel4->next)
1409 for (nodeLevel5 = nodeLevel4->children;
1411 nodeLevel5 = nodeLevel5->next)
1414 if (!xmlStrcmp(nodeLevel5->name, (const xmlChar *)"getetag") ||
1415 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"d:getetag") ||
1416 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"D:getetag")
1419 for (nodeLevel6 = nodeLevel5->children;
1421 nodeLevel6 = nodeLevel6->next)
1424 // Strip the quotes from the ETag.
1426 ETagData = (const char*)nodeLevel6->content;
1427 if (ETagData[0] == '"' && ETagData[(ETagData.size() - 1)] == '"'){
1429 ETagData.erase(0, 1);
1430 ETagData.erase((ETagData.size() - 1));
1450 if (HREFFound == TRUE && ETagFound == TRUE){
1452 // Add to the map data.
1454 ETagValue = ETagData;
1465 xmlFreeDoc(xmlCardDAVDoc);
1471 string CardDAV2::GetETagHeader(){
1473 // Go through each of the lines looking for the
1478 bool FastForward = false;
1480 for (int HeaderSeek = 0; HeaderSeek < PageHeader.size(); HeaderSeek++){
1482 if (FastForward == true){
1484 if (PageHeader[HeaderSeek] == '\n'){
1485 FastForward = false;
1493 PageHeader.substr(HeaderSeek, 5) == "ETag:";
1496 catch (const out_of_range &oor){
1500 if (PageHeader.substr(HeaderSeek, 5) == "ETag:"){
1502 int CharacterSeek = 5;
1504 while ((HeaderSeek + CharacterSeek) < PageHeader.size()){
1506 if (PageHeader.substr((HeaderSeek + CharacterSeek), 2) == "\r\n"){
1510 HeaderValue += PageHeader.substr((HeaderSeek + CharacterSeek), 1);
1523 if (PageHeader[HeaderSeek] == '\n'){
1527 //HeaderName += PageHeader.substr(HeaderSeek, 1);
1531 // Check for quotation marks at the start and end and strip
1534 if (HeaderValue.size() >= 2){
1536 if (HeaderValue[0] == '"'){
1537 HeaderValue.erase((HeaderValue.size() - 1));
1538 HeaderValue.erase(0);
1547 vector<string> CardDAV2::GetDAVHeader(){
1549 // Go through each of the lines looking for the
1554 bool FastForward = false;
1555 vector<string> DAVHeaderList;
1557 for (int HeaderSeek = 0; HeaderSeek < PageHeader.size(); HeaderSeek++){
1559 if (FastForward == true){
1561 if (PageHeader[HeaderSeek] == '\n'){
1562 FastForward = false;
1570 PageHeader.substr(HeaderSeek, 4) == "DAV:";
1573 catch (const out_of_range &oor){
1577 if (PageHeader.substr(HeaderSeek, 4) == "DAV:"){
1579 int CharacterSeek = 5;
1581 while ((HeaderSeek + CharacterSeek) < PageHeader.size()){
1583 if (PageHeader.substr((HeaderSeek + CharacterSeek), 2) == "\r\n"){
1587 HeaderValue += PageHeader.substr((HeaderSeek + CharacterSeek), 1);
1600 if (PageHeader[HeaderSeek] == '\n'){
1604 //HeaderName += PageHeader.substr(HeaderSeek, 1);
1608 // Split the header data.
1610 std::string DAVHeaderValue;
1612 for (int HeaderSeek = 0; HeaderSeek < HeaderValue.size(); HeaderSeek++){
1614 if (HeaderValue.substr(HeaderSeek, 1) == ","){
1615 DAVHeaderList.push_back(DAVHeaderValue);
1616 DAVHeaderValue = "";
1621 DAVHeaderValue += HeaderValue[HeaderSeek];
1625 if (DAVHeaderValue.size() > 0){
1627 DAVHeaderList.push_back(DAVHeaderValue);
1631 return DAVHeaderList;