1 // carddav-processdata.cpp - CardDAV Object - Process Data 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::ProcessDataThread(){
33 // Process the data (threaded).
40 AbortConnection = FALSE;
44 wxString ServerAddressURL;
46 wxString ServerAddressSSL;
47 wxString ServerAddressNormal;
49 conn = curl_easy_init();
51 #if defined(__APPLE__)
53 SetConnectionObject(conn);
57 struct CardDAVCURLPasser {
60 bool HeaderMode = TRUE;
62 } CardDAVHeader, CardDAVFooter;
64 CardDAVHeader.Data = this;
65 CardDAVHeader.HeaderMode = TRUE;
67 CardDAVFooter.Data = this;
68 CardDAVFooter.HeaderMode = FALSE;
74 wxString ETagOriginal;
77 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + ServerPrefix + ServerFilenameLocation;
78 ServerAddressSSL = wxT("https://") + ServerAddressURL;
79 ServerAddressNormal = wxT("http://") + ServerAddressURL;
81 ServerAuth = ServerUser + wxT(":") + ServerPass;
83 std::map<int,int>::iterator ActIter;
84 struct UploadDataStruc UploadData;
87 ActIter = ActivityListPtr->find((int)ItemIndex);
89 // Update result flag.
93 // Setup the request mode if it is not empty.
95 if (!ServerMethod.IsEmpty()){
97 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, (const char*)ServerMethod.mb_str(wxConvUTF8));
105 wxString ServerCertFilename;
106 bool MatchingCert = FALSE;
108 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
109 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
110 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
111 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
112 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
113 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
114 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
115 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
116 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
117 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
118 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
119 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
120 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
122 if (UploadMode == TRUE){
124 UploadData.readptr = &ServerUploadData;
125 UploadData.sizeleft = ServerUploadData.Len();
126 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
127 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
128 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
132 #if defined(__APPLE__) || defined(__WIN32__)
136 ServerCertFilename = GetAccountDir(ServerAccount, TRUE);
138 if (wxFile::Exists(ServerCertFilename) == TRUE){
140 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 1);
141 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 2);
142 curl_easy_setopt(conn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8));
148 claconncode = (curl_easy_perform(conn));
150 // If CURLE_PEER_FAILED_VERIFICATION is returned, retry without
151 // the local certificate in use.
153 if (claconncode == CURLE_PEER_FAILED_VERIFICATION){
155 curl_easy_cleanup(conn);
156 conn = curl_easy_init();
158 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
159 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
160 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
161 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
162 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
163 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
164 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
165 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
166 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
167 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
168 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
169 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
170 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
172 if (UploadMode == TRUE){
174 UploadData.readptr = &ServerUploadData;
175 UploadData.sizeleft = ServerUploadData.Len();
176 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
177 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
178 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
182 claconncode = (curl_easy_perform(conn));
184 // If claconncode is CURLE_OK then delete the certificate file as that
185 // is no longer needed.
187 if (claconncode == CURLE_OK){
189 // Delete the certificate file.
191 wxRemoveFile(ServerCertFilename);
197 // Check if it fails with a CURLE_SSL_CACERT then compare
198 // the certificates as PEM files.
200 #if defined(__APPLE__)
204 if (claconncode == CURLE_SSL_CACERT && wxFile::Exists(ServerCertFilename) == TRUE){
207 sslerrconn = curl_easy_init();
208 CURLcode sslerrconncode;
210 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
212 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
213 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
214 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
215 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
216 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
217 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
218 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
219 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
220 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
221 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
222 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
223 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
224 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
225 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
227 wxString SSLLocalData;
228 wxString SSLServerData;
230 sslerrconncode = (curl_easy_perform(sslerrconn));
232 SSLCertCol = BuildSSLCollection(sslerrconn);
233 std::map<int, SSLCertData>::iterator SSLCDIter = SSLCertCol.SSLCollection.find(0);
234 std::multimap<wxString,wxString>::iterator SSLDataIter = SSLCDIter->second.CertData.find(wxT("Cert"));
236 wxFFile SSLLocalFile;
238 #if wxABI_VERSION < 20900
239 SSLLocalFile.Open(ServerCertFilename.c_str(), wxT("r"));
241 SSLLocalFile.Open(ServerCertFilename, wxT("r"));
244 // Load the recovery database for tasks not done.
246 if (SSLLocalFile.IsOpened() == TRUE){
248 // Check if we are using wxWidgets version 2.8 or less and
249 // execute the required command accordingly.
251 SSLLocalFile.ReadAll(&SSLLocalData, wxConvAuto());
256 SSLServerData = SSLDataIter->second;
258 if (SSLLocalData == SSLServerData){
260 // Server key matches with local key so retry with CURLOPT_SSL_VERIFYPEER
261 // and CURLOPT_SSL_VERIFYHOST off.
263 curl_easy_cleanup(conn);
264 conn = curl_easy_init();
269 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
270 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
271 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
272 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
273 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
274 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
275 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
276 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
277 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
278 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
279 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
280 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
281 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
283 if (UploadMode == TRUE){
285 UploadData.readptr = &ServerUploadData;
286 UploadData.sizeleft = ServerUploadData.Len();
287 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
288 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
289 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
293 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0);
294 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0);
296 claconncode = (curl_easy_perform(conn));
302 if (MatchingCert == FALSE){
304 claconncode = CURLE_SSL_CACERT;
309 curl_easy_cleanup(sslerrconn);
315 // Sort out SSL error.
317 // When SSL cert error occurs, connect again and fetch certificates.
318 // Display a message to the user explaining that an invalid
319 // certificate has been given and let the user decide what
322 if (claconncode == CURLE_OK){
324 } else if (claconncode == CURLE_SSL_CACERT || claconncode == CURLE_PEER_FAILED_VERIFICATION){
327 sslerrconn = curl_easy_init();
328 CURLcode sslerrconncode;
330 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
332 // Replace conn with sslerrconn!
334 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
335 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
336 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
337 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
338 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
339 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
340 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
341 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
342 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
343 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
344 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
345 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
346 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
347 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
349 #if defined(__APPLE__)
351 SetConnectionObject(sslerrconn);
355 sslerrconncode = (curl_easy_perform(sslerrconn));
357 #if defined(__APPLE__)
361 SSLCertCol = BuildSSLCollection(sslerrconn);
362 SSLCertCol.SuccessCode = 1;
368 } else if (claconncode == CURLE_HTTP_RETURNED_ERROR){
370 fprintf(stderr, "ProcessDataThrad(): curl_easy_perform() failed: %s\n",
371 curl_easy_strerror(claconncode));
373 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
374 fprintf(stderr, "Error code was: %d\n", http_code);
380 fprintf(stderr, "curl_easy_perform() failed: %s\n",
381 curl_easy_strerror(claconncode));
383 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
384 fprintf(stderr, "Error code was: %d\n", http_code);
394 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressNormal.mb_str(wxConvUTF8));
395 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
396 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
397 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
398 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
399 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
400 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
401 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
402 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
403 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
404 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
405 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
406 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
408 if (UploadMode == TRUE){
410 UploadData.readptr = &ServerUploadData;
411 UploadData.sizeleft = ServerUploadData.Len();
412 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
413 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
414 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
418 conncode = (curl_easy_perform(conn));
420 if (conncode == CURLE_OK){
422 // Process the server header response and look for
423 // 'addressbook' within the DAV header.
425 wxStringTokenizer wxSHeaderLines(PageHeader, wxT("\r\n"));
426 wxString wxSHeaderLine;
427 std::map<int, wxString> DAVHeaderLines;
429 while (wxSHeaderLines.HasMoreTokens()){
431 wxSHeaderLine = wxSHeaderLines.GetNextToken();
433 if (wxSHeaderLine.Mid(0, 5) == wxT("ETag:")){
435 ETagData = wxSHeaderLine.Mid(5);
437 ETagData.Trim(FALSE);
441 if (ETagData.Mid(0, 1) == wxT("\"") && ETagData.Mid((ETagData.Len() - 1), 1) == wxT("\"")){
443 ETagData.Remove(0, 1);
444 ETagData.RemoveLast();
450 if (wxSHeaderLine.Mid(0, 4) == wxT("DAV:")){
452 // Look for address book in the line.
454 if (wxSHeaderLine.Find(wxT("addressbook")) != wxNOT_FOUND){
456 HasCalDAVSupport = TRUE;
464 // Get the ETag from the header.
466 if (UploadMode == TRUE){
468 wxString PageHeaderLine;
470 wxStringTokenizer PageHeaderSplit(PageHeader, wxT("\r\n"));
472 if (PageHeaderSplit.HasMoreTokens()){
474 PageHeaderLine = PageHeaderSplit.GetNextToken();
483 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
485 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
487 fprintf(stderr, "curl_easy_perform() failed: %s\n",
488 curl_easy_strerror(conncode));
490 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
498 fprintf(stderr, "curl_easy_perform() failed: %s\n",
499 curl_easy_strerror(conncode));
510 *ServerResult = TRUE;
515 void CardDAV::ProcessData(){
519 std::thread ConnectThread(&CardDAV::ProcessDataThread, this);
520 ConnectThread.detach();