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()
39 AbortConnection = FALSE;
41 bool FilenameIsDirectory = FALSE;
44 wxString ServerAddressURL;
46 wxString ServerAddressSSL;
47 wxString ServerAddressNormal;
49 conn = curl_easy_init();
51 struct CardDAVCURLPasser {
54 bool HeaderMode = TRUE;
56 } CardDAVHeader, CardDAVFooter;
58 CardDAVHeader.Data = this;
59 CardDAVHeader.HeaderMode = TRUE;
61 CardDAVFooter.Data = this;
62 CardDAVFooter.HeaderMode = FALSE;
67 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/") + ServerPrefix + ServerFilenameLocation;
68 ServerAddressSSL = wxT("https://") + ServerAddressURL;
69 ServerAddressNormal = wxT("http://") + ServerAddressURL;
71 ServerAuth = ServerUser + wxT(":") + ServerPass;
73 // Workout if path is directory or filename.
76 FilenameIsDirectory = TRUE;
78 FilenameIsDirectory = FALSE;
85 char *ServerAdrSSLChar = new char[(ServerAddressSSL.Length() - 1)];
86 //memset(ServerAdrSSLChar, 0, ServerAddressSSL.Length());
87 strncpy(ServerAdrSSLChar, (const char*)ServerAddressSSL.mb_str(wxConvUTF8), (ServerAddressSSL.Length() - 1));
89 char *ServerAdrNorChar = new char[(ServerAddressNormal.Length() - 1)];
90 //memset(ServerAdrNorChar, 0, ServerAddressSSL.Length());
91 strncpy(ServerAdrNorChar, (const char*)ServerAddressNormal.mb_str(wxConvUTF8), (ServerAddressNormal.Length() - 1));
93 char *ServerAuthChar = new char[(ServerAuth.Length() - 1)];
94 //memset(ServerAuthChar, 0, ServerAddressSSL.Length());
95 strncpy(ServerAuthChar, (const char*)ServerAuth.mb_str(wxConvUTF8), (ServerAuth.Length() - 1));
99 //std::string WriteDataString = std::string(ServerUploadData.mb_str());
101 std::map<int,int>::iterator ActIter;
102 struct UploadDataStruc UploadData;
105 ActIter = ActivityListPtr->find((int)ItemIndex);
107 static const char* query =
108 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
109 "<C:addressbook-query xmlns:D=\"DAV:\""
110 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">"
111 "<D:prop><D:getetag/>"
114 //"</C:address-data></D:prop>"
117 "</C:addressbook-query>";
121 wxString ServerCertFilename;
122 bool MatchingCert = FALSE;
124 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
125 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
126 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
127 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
128 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
129 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
130 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
131 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
132 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
133 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
134 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
135 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
136 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
137 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
139 //UploadData.readptr = &CardDAVDataQuery;
140 //UploadData.sizeleft = CardDAVDataQuery.Len();
141 //curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
142 //curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
143 //curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
145 //curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, writefunc);
146 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
147 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
149 ServerCertFilename = GetAccountDir(ServerAccount, TRUE);
151 if (wxFile::Exists(ServerCertFilename) == TRUE){
153 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 1);
154 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 2);
155 curl_easy_setopt(conn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8));
159 claconncode = (curl_easy_perform(conn));
161 // If CURLE_PEER_FAILED_VERIFICATION is returned, retry without
162 // the local certificate in use.
164 if (claconncode == CURLE_PEER_FAILED_VERIFICATION){
166 curl_easy_cleanup(conn);
167 conn = curl_easy_init();
169 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
170 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
171 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
172 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
173 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
174 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
175 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
176 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
177 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
178 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
179 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
180 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
181 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
182 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
183 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
184 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
186 claconncode = (curl_easy_perform(conn));
188 // If claconncode is CURLE_OK then delete the certificate file as that
189 // is no longer needed.
191 if (claconncode == CURLE_OK){
193 // Delete the certificate file.
195 wxRemoveFile(ServerCertFilename);
201 // Check if it fails with a CURLE_SSL_CACERT then compare
202 // the certificates as PEM files.
204 if (claconncode == CURLE_SSL_CACERT && wxFile::Exists(ServerCertFilename) == TRUE){
206 //curl_easy_cleanup(conn);
207 //conn = curl_easy_init();
210 sslerrconn = curl_easy_init();
211 CURLcode sslerrconncode;
213 //claconncode = (curl_easy_perform(conn));
215 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
220 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
221 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
222 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
223 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
224 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
225 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
226 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
227 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
228 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
229 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
230 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
231 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
232 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
233 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
234 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 1);
235 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYHOST, 2);
236 curl_easy_setopt(sslerrconn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8));
238 wxString SSLLocalData;
239 wxString SSLServerData;
241 sslerrconncode = (curl_easy_perform(sslerrconn));
243 SSLCertCol = BuildSSLCollection(sslerrconn);
244 std::map<int, SSLCertData>::iterator SSLCDIter = SSLCertCol.SSLCollection.find(0);
245 std::multimap<wxString,wxString>::iterator SSLDataIter = SSLCDIter->second.CertData.find(wxT("Cert"));
247 wxFFile SSLLocalFile;
249 #if wxABI_VERSION < 20900
250 SSLLocalFile.Open(ServerCertFilename.c_str(), wxT("r"));
252 SSLLocalFile.Open(ServerCertFilename, wxT("r"));
255 // Load the recovery database for tasks not done.
257 if (SSLLocalFile.IsOpened() == TRUE){
259 // Check if we are using wxWidgets version 2.8 or less and
260 // execute the required command accordingly.
262 SSLLocalFile.ReadAll(&SSLLocalData, wxConvAuto());
267 SSLServerData = SSLDataIter->second;
269 if (SSLLocalData == SSLServerData){
271 // Server key matches with local key so retry with CURLOPT_SSL_VERIFYPEER
272 // and CURLOPT_SSL_VERIFYHOST off.
274 curl_easy_cleanup(conn);
275 conn = curl_easy_init();
280 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
281 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
282 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
283 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
284 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
285 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
286 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
287 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
288 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
289 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
290 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
291 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
292 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
293 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
294 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
295 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
296 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0);
297 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0);
299 claconncode = (curl_easy_perform(conn));
305 if (MatchingCert == FALSE){
307 claconncode = CURLE_SSL_CACERT;
312 curl_easy_cleanup(sslerrconn);
316 // Sort out SSL error.
318 // When SSL cert error occurs, connect again and fetch certificates.
319 // Display a message to the user explaining that an invalid
320 // certificate has been given and let the user decide what
323 if (claconncode == CURLE_OK){
325 } else if (claconncode == CURLE_SSL_CACERT || claconncode == CURLE_PEER_FAILED_VERIFICATION){
328 sslerrconn = curl_easy_init();
329 CURLcode sslerrconncode;
331 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
333 // Replace conn with sslerrconn!
335 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
336 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
337 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
338 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
339 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
340 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
341 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
342 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
343 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
344 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
345 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
346 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
347 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
348 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
350 sslerrconncode = (curl_easy_perform(sslerrconn));
352 SSLCertCol = BuildSSLCollection(sslerrconn);
353 SSLCertCol.SuccessCode = 1;
355 curl_easy_cleanup(conn);
356 curl_easy_cleanup(sslerrconn);
360 } else if (claconncode == CURLE_HTTP_RETURNED_ERROR){
362 fprintf(stderr, "curl_easy_perform() failed: %s\n",
363 curl_easy_strerror(claconncode));
365 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
366 fprintf(stderr, "Error code was: %d\n", http_code);
368 curl_easy_cleanup(conn);
374 fprintf(stderr, "curl_easy_perform() failed: %s\n",
375 curl_easy_strerror(claconncode));
377 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
378 fprintf(stderr, "Error code was: %d\n", http_code);
380 curl_easy_cleanup(conn);
390 wxString EmptyString;
392 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressNormal.mb_str(wxConvUTF8));
393 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
394 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
395 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
396 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
397 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
398 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
399 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
400 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
401 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
402 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
403 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
404 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT");
405 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
407 //UploadData.readptr = &CardDAVDataQuery;
408 //UploadData.sizeleft = CardDAVDataQuery.Len();
409 //curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
410 //curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
411 //curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
413 //curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, writefunc);
414 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
415 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
420 conncode = (curl_easy_perform(conn));
422 if (conncode == CURLE_OK){
424 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
426 fprintf(stderr, "curl_easy_perform() failed: %s\n",
427 curl_easy_strerror(conncode));
433 fprintf(stderr, "curl_easy_perform() failed: %s\n",
434 curl_easy_strerror(conncode));
442 xmlDocPtr xmlCardDAVDoc;
444 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
446 xmlNodePtr nodeLevel1;
447 xmlNodePtr nodeLevel2;
448 xmlNodePtr nodeLevel3;
449 xmlNodePtr nodeLevel4;
450 xmlNodePtr nodeLevel5;
451 xmlNodePtr nodeLevel6;
453 std::map<wxString,wxString> xmlDataMap;
455 wxString DataFilename;
458 std::string xmlStringSafe;
460 // Tranverse through the catacombs of the response to get our ETag for the file.
462 for (nodeLevel1 = xmlCardDAVDoc->children;
464 nodeLevel1 = nodeLevel1->next)
467 bool HREFFound = FALSE;
468 bool ETagFound = FALSE;
470 for (nodeLevel2 = nodeLevel1->children;
472 nodeLevel2 = nodeLevel2->next)
475 for (nodeLevel3 = nodeLevel2->children;
477 nodeLevel3 = nodeLevel3->next)
480 if (!xmlStrcmp(nodeLevel3->name, (const xmlChar *)"href") ||
481 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"d:href") ||
482 !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"D:href")
487 for (nodeLevel4 = nodeLevel3->children;
489 nodeLevel4 = nodeLevel4->next)
492 if (!xmlStrcmp(nodeLevel4->name, (const xmlChar *)"text") ||
493 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"d:text") ||
494 !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"D:text")
497 DataFilename = wxString::FromUTF8((const char*)nodeLevel4->content);
498 wxStringTokenizer wSTDFilename(DataFilename, wxT("/"));
500 while (wSTDFilename.HasMoreTokens()){
502 DataFilename = wSTDFilename.GetNextToken();
516 for (nodeLevel4 = nodeLevel3->children;
518 nodeLevel4 = nodeLevel4->next)
521 for (nodeLevel5 = nodeLevel4->children;
523 nodeLevel5 = nodeLevel5->next)
526 if (!xmlStrcmp(nodeLevel5->name, (const xmlChar *)"getetag") ||
527 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"d:getetag") ||
528 !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"D:getetag")
531 for (nodeLevel6 = nodeLevel5->children;
533 nodeLevel6 = nodeLevel6->next)
536 // Strip the quotes from the ETag.
538 ETagData = wxString::FromUTF8((const char*)nodeLevel6->content);
539 if (ETagData.Mid(0, 1) == wxT("\"") && ETagData.Mid((ETagData.Len() - 1), 1) == wxT("\"")){
541 ETagData.Remove(0, 1);
542 ETagData.RemoveLast();
562 if (HREFFound == TRUE && ETagFound == TRUE){
564 // Add to the map data.
566 xmlDataMap.insert(std::make_pair(DataFilename, ETagData));
576 xmlFreeDoc(xmlCardDAVDoc);
578 // Get the first result.
580 for (std::map<wxString,wxString>::iterator iter = xmlDataMap.begin();
581 iter != xmlDataMap.end(); ++iter){
583 ETagResult = iter->second;
588 if (ETagResult.IsEmpty()){
598 void CardDAV::GetServerETagValue(){
600 std::thread ConnectThread(&CardDAV::GetServerETagValueThread, this);
601 ConnectThread.detach();