1 // carddav-serveretag.cpp - CardDAV Object - Server ETag subroutines.
3 // (c) 2012-2015 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/>
20 #include "../version.h"
22 #include <wx/tokenzr.h>
24 #include <libxml/parser.h>
25 #include <libxml/tree.h>
28 #include "../vcard/vcard.h"
29 #include "../common/dirs.h"
31 void CardDAV::GetServerETagValueThread()
34 // Get the server etag value (threaded).
41 AbortConnection = FALSE;
43 bool FilenameIsDirectory = FALSE;
46 wxString ServerAddressURL;
48 wxString ServerAddressSSL;
49 wxString ServerAddressNormal;
51 conn = curl_easy_init();
53 struct CardDAVCURLPasser {
56 bool HeaderMode = TRUE;
58 } CardDAVHeader, CardDAVFooter;
60 CardDAVHeader.Data = this;
61 CardDAVHeader.HeaderMode = TRUE;
63 CardDAVFooter.Data = this;
64 CardDAVFooter.HeaderMode = FALSE;
69 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/") + ServerPrefix + ServerFilenameLocation;
70 ServerAddressSSL = wxT("https://") + ServerAddressURL;
71 ServerAddressNormal = wxT("http://") + ServerAddressURL;
73 ServerAuth = ServerUser + wxT(":") + ServerPass;
75 std::map<int,int>::iterator ActIter;
76 struct UploadDataStruc UploadData;
79 ActIter = ActivityListPtr->find((int)ItemIndex);
81 static const char* query =
82 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
83 "<C:addressbook-query xmlns:D=\"DAV:\""
84 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">"
85 "<D:prop><D:getetag/>"
88 "</C:addressbook-query>";
92 wxString ServerCertFilename;
93 bool MatchingCert = FALSE;
95 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
96 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
97 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
98 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
99 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
100 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
101 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
102 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
103 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
104 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
105 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
106 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
107 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
108 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
109 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
110 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
112 ServerCertFilename = GetAccountDir(ServerAccount, TRUE);
114 if (wxFile::Exists(ServerCertFilename) == TRUE){
116 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 1);
117 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 2);
118 curl_easy_setopt(conn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8));
122 claconncode = (curl_easy_perform(conn));
124 // If CURLE_PEER_FAILED_VERIFICATION is returned, retry without
125 // the local certificate in use.
127 if (claconncode == CURLE_PEER_FAILED_VERIFICATION){
129 curl_easy_cleanup(conn);
130 conn = curl_easy_init();
132 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
133 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
134 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
135 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
136 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
137 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
138 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
139 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
140 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
141 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
142 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
143 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
144 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
145 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
146 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
147 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
149 claconncode = (curl_easy_perform(conn));
151 // If claconncode is CURLE_OK then delete the certificate file as that
152 // is no longer needed.
154 if (claconncode == CURLE_OK){
156 // Delete the certificate file.
158 wxRemoveFile(ServerCertFilename);
164 // Check if it fails with a CURLE_SSL_CACERT then compare
165 // the certificates as PEM files.
167 if (claconncode == CURLE_SSL_CACERT && wxFile::Exists(ServerCertFilename) == TRUE){
170 sslerrconn = curl_easy_init();
171 CURLcode sslerrconncode;
173 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
178 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
179 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
180 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
181 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
182 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
183 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
184 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
185 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
186 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
187 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
188 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
189 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
190 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
191 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
192 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 1);
193 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYHOST, 2);
194 curl_easy_setopt(sslerrconn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8));
196 wxString SSLLocalData;
197 wxString SSLServerData;
199 sslerrconncode = (curl_easy_perform(sslerrconn));
201 SSLCertCol = BuildSSLCollection(sslerrconn);
202 std::map<int, SSLCertData>::iterator SSLCDIter = SSLCertCol.SSLCollection.find(0);
203 std::multimap<wxString,wxString>::iterator SSLDataIter = SSLCDIter->second.CertData.find(wxT("Cert"));
205 wxFFile SSLLocalFile;
207 #if wxABI_VERSION < 20900
208 SSLLocalFile.Open(ServerCertFilename.c_str(), wxT("r"));
210 SSLLocalFile.Open(ServerCertFilename, wxT("r"));
213 // Load the recovery database for tasks not done.
215 if (SSLLocalFile.IsOpened() == TRUE){
217 // Check if we are using wxWidgets version 2.8 or less and
218 // execute the required command accordingly.
220 SSLLocalFile.ReadAll(&SSLLocalData, wxConvAuto());
225 SSLServerData = SSLDataIter->second;
227 if (SSLLocalData == SSLServerData){
229 // Server key matches with local key so retry with CURLOPT_SSL_VERIFYPEER
230 // and CURLOPT_SSL_VERIFYHOST off.
232 curl_easy_cleanup(conn);
233 conn = curl_easy_init();
238 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
239 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
240 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
241 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
242 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
243 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
244 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
245 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
246 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
247 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
248 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
249 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
250 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
251 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
252 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
253 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
254 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0);
255 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0);
257 claconncode = (curl_easy_perform(conn));
263 if (MatchingCert == FALSE){
265 claconncode = CURLE_SSL_CACERT;
270 curl_easy_cleanup(sslerrconn);
274 // Sort out SSL error.
276 // When SSL cert error occurs, connect again and fetch certificates.
277 // Display a message to the user explaining that an invalid
278 // certificate has been given and let the user decide what
281 if (claconncode == CURLE_OK){
283 } else if (claconncode == CURLE_SSL_CACERT || claconncode == CURLE_PEER_FAILED_VERIFICATION){
286 sslerrconn = curl_easy_init();
287 CURLcode sslerrconncode;
289 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
291 // Replace conn with sslerrconn!
293 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
294 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
295 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
296 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
297 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
298 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
299 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
300 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
301 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
302 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
303 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
304 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
305 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
306 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
308 sslerrconncode = (curl_easy_perform(sslerrconn));
310 SSLCertCol = BuildSSLCollection(sslerrconn);
311 SSLCertCol.SuccessCode = 1;
313 curl_easy_cleanup(conn);
314 curl_easy_cleanup(sslerrconn);
318 } else if (claconncode == CURLE_HTTP_RETURNED_ERROR){
320 fprintf(stderr, "curl_easy_perform() failed: %s\n",
321 curl_easy_strerror(claconncode));
323 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
324 fprintf(stderr, "Error code was: %d\n", http_code);
326 curl_easy_cleanup(conn);
332 fprintf(stderr, "curl_easy_perform() failed: %s\n",
333 curl_easy_strerror(claconncode));
335 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
336 fprintf(stderr, "Error code was: %d\n", http_code);
338 curl_easy_cleanup(conn);
348 wxString EmptyString;
350 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressNormal.mb_str(wxConvUTF8));
351 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
352 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
353 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
354 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
355 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
356 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
357 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
358 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
359 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
360 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
361 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
362 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
363 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
364 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
365 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
370 conncode = (curl_easy_perform(conn));
372 if (conncode == CURLE_OK){
374 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
376 fprintf(stderr, "curl_easy_perform() failed: %s\n",
377 curl_easy_strerror(conncode));
383 fprintf(stderr, "curl_easy_perform() failed: %s\n",
384 curl_easy_strerror(conncode));
392 xmlDocPtr xmlCardDAVDoc;
394 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
396 xmlNodePtr nodeLevel1;
397 xmlNodePtr nodeLevel2;
398 xmlNodePtr nodeLevel3;
399 xmlNodePtr nodeLevel4;
400 xmlNodePtr nodeLevel5;
401 xmlNodePtr nodeLevel6;
403 std::map<wxString,wxString> xmlDataMap;
405 wxString DataFilename;
408 std::string xmlStringSafe;
410 // Tranverse through the catacombs of the response to get our ETag for the file.
412 for (nodeLevel1 = xmlCardDAVDoc->children;
414 nodeLevel1 = nodeLevel1->next)
417 bool HREFFound = FALSE;
418 bool ETagFound = FALSE;
420 for (nodeLevel2 = nodeLevel1->children;
422 nodeLevel2 = nodeLevel2->next)
425 for (nodeLevel3 = nodeLevel2->children;
427 nodeLevel3 = nodeLevel3->next)
430 if (!xmlStrcmp(nodeLevel3->name, (const xmlChar *)"href") ||
431 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"d:href") ||
432 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"D:href")
437 for (nodeLevel4 = nodeLevel3->children;
439 nodeLevel4 = nodeLevel4->next)
442 if (!xmlStrcmp(nodeLevel4->name, (const xmlChar *)"text") ||
443 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"d:text") ||
444 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"D:text")
447 DataFilename = wxString::FromUTF8((const char*)nodeLevel4->content);
448 wxStringTokenizer wSTDFilename(DataFilename, wxT("/"));
450 while (wSTDFilename.HasMoreTokens()){
452 DataFilename = wSTDFilename.GetNextToken();
466 for (nodeLevel4 = nodeLevel3->children;
468 nodeLevel4 = nodeLevel4->next)
471 for (nodeLevel5 = nodeLevel4->children;
473 nodeLevel5 = nodeLevel5->next)
476 if (!xmlStrcmp(nodeLevel5->name, (const xmlChar *)"getetag") ||
477 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"d:getetag") ||
478 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"D:getetag")
481 for (nodeLevel6 = nodeLevel5->children;
483 nodeLevel6 = nodeLevel6->next)
486 // Strip the quotes from the ETag.
488 ETagData = wxString::FromUTF8((const char*)nodeLevel6->content);
489 if (ETagData.Mid(0, 1) == wxT("\"") && ETagData.Mid((ETagData.Len() - 1), 1) == wxT("\"")){
491 ETagData.Remove(0, 1);
492 ETagData.RemoveLast();
512 if (HREFFound == TRUE && ETagFound == TRUE){
514 // Add to the map data.
516 xmlDataMap.insert(std::make_pair(DataFilename, ETagData));
526 xmlFreeDoc(xmlCardDAVDoc);
528 // Get the first result.
530 for (std::map<wxString,wxString>::iterator iter = xmlDataMap.begin();
531 iter != xmlDataMap.end(); ++iter){
533 ETagResult = iter->second;
538 if (ETagResult.IsEmpty()){
548 void CardDAV::GetServerETagValue(){
550 // Get the server etag value.
552 std::thread ConnectThread(&CardDAV::GetServerETagValueThread, this);
553 ConnectThread.detach();