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 SetConnectionObject(conn);
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;
70 wxString ETagOriginal;
73 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + ServerPrefix + ServerFilenameLocation;
74 ServerAddressSSL = wxT("https://") + ServerAddressURL;
75 ServerAddressNormal = wxT("http://") + ServerAddressURL;
77 ServerAuth = ServerUser + wxT(":") + ServerPass;
79 std::map<int,int>::iterator ActIter;
80 struct UploadDataStruc UploadData;
83 ActIter = ActivityListPtr->find((int)ItemIndex);
85 // Update result flag.
89 // Setup the request mode if it is not empty.
91 if (!ServerMethod.IsEmpty()){
93 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, (const char*)ServerMethod.mb_str(wxConvUTF8));
101 wxString ServerCertFilename;
102 bool MatchingCert = FALSE;
104 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
105 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
106 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
107 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
108 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
109 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
110 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
111 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
112 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
113 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
114 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
115 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
116 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
118 if (UploadMode == TRUE){
120 UploadData.readptr = &ServerUploadData;
121 UploadData.sizeleft = ServerUploadData.Len();
122 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
123 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
124 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
128 #if defined(__APPLE__)
132 ServerCertFilename = GetAccountDir(ServerAccount, TRUE);
134 if (wxFile::Exists(ServerCertFilename) == TRUE){
136 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 1);
137 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 2);
138 curl_easy_setopt(conn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8));
144 claconncode = (curl_easy_perform(conn));
146 // If CURLE_PEER_FAILED_VERIFICATION is returned, retry without
147 // the local certificate in use.
149 if (claconncode == CURLE_PEER_FAILED_VERIFICATION){
151 curl_easy_cleanup(conn);
152 conn = curl_easy_init();
154 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
155 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
156 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
157 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
158 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
159 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
160 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
161 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
162 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
163 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
164 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
165 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
166 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
168 if (UploadMode == TRUE){
170 UploadData.readptr = &ServerUploadData;
171 UploadData.sizeleft = ServerUploadData.Len();
172 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
173 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
174 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
178 claconncode = (curl_easy_perform(conn));
180 // If claconncode is CURLE_OK then delete the certificate file as that
181 // is no longer needed.
183 if (claconncode == CURLE_OK){
185 // Delete the certificate file.
187 wxRemoveFile(ServerCertFilename);
193 // Check if it fails with a CURLE_SSL_CACERT then compare
194 // the certificates as PEM files.
196 #if defined(__APPLE__)
200 if (claconncode == CURLE_SSL_CACERT && wxFile::Exists(ServerCertFilename) == TRUE){
203 sslerrconn = curl_easy_init();
204 CURLcode sslerrconncode;
206 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
208 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
209 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
210 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
211 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
212 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
213 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
214 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
215 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
216 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
217 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
218 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
219 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
220 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
221 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
223 wxString SSLLocalData;
224 wxString SSLServerData;
226 sslerrconncode = (curl_easy_perform(sslerrconn));
228 SSLCertCol = BuildSSLCollection(sslerrconn);
229 std::map<int, SSLCertData>::iterator SSLCDIter = SSLCertCol.SSLCollection.find(0);
230 std::multimap<wxString,wxString>::iterator SSLDataIter = SSLCDIter->second.CertData.find(wxT("Cert"));
232 wxFFile SSLLocalFile;
234 #if wxABI_VERSION < 20900
235 SSLLocalFile.Open(ServerCertFilename.c_str(), wxT("r"));
237 SSLLocalFile.Open(ServerCertFilename, wxT("r"));
240 // Load the recovery database for tasks not done.
242 if (SSLLocalFile.IsOpened() == TRUE){
244 // Check if we are using wxWidgets version 2.8 or less and
245 // execute the required command accordingly.
247 SSLLocalFile.ReadAll(&SSLLocalData, wxConvAuto());
252 SSLServerData = SSLDataIter->second;
254 if (SSLLocalData == SSLServerData){
256 // Server key matches with local key so retry with CURLOPT_SSL_VERIFYPEER
257 // and CURLOPT_SSL_VERIFYHOST off.
259 curl_easy_cleanup(conn);
260 conn = curl_easy_init();
265 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
266 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
267 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
268 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
269 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
270 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
271 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
272 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
273 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
274 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
275 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
276 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
277 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
279 if (UploadMode == TRUE){
281 UploadData.readptr = &ServerUploadData;
282 UploadData.sizeleft = ServerUploadData.Len();
283 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
284 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
285 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
289 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0);
290 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0);
292 claconncode = (curl_easy_perform(conn));
298 if (MatchingCert == FALSE){
300 claconncode = CURLE_SSL_CACERT;
305 curl_easy_cleanup(sslerrconn);
311 // Sort out SSL error.
313 // When SSL cert error occurs, connect again and fetch certificates.
314 // Display a message to the user explaining that an invalid
315 // certificate has been given and let the user decide what
318 if (claconncode == CURLE_OK){
320 } else if (claconncode == CURLE_SSL_CACERT || claconncode == CURLE_PEER_FAILED_VERIFICATION){
323 sslerrconn = curl_easy_init();
324 CURLcode sslerrconncode;
326 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
328 // Replace conn with sslerrconn!
330 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
331 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
332 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
333 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
334 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
335 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
336 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
337 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
338 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
339 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
340 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
341 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
342 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
343 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
345 SetConnectionObject(sslerrconn);
347 sslerrconncode = (curl_easy_perform(sslerrconn));
349 #if defined(__APPLE__)
353 SSLCertCol = BuildSSLCollection(sslerrconn);
354 SSLCertCol.SuccessCode = 1;
360 } else if (claconncode == CURLE_HTTP_RETURNED_ERROR){
362 fprintf(stderr, "ProcessDataThrad(): 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);
372 fprintf(stderr, "curl_easy_perform() failed: %s\n",
373 curl_easy_strerror(claconncode));
375 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
376 fprintf(stderr, "Error code was: %d\n", http_code);
386 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressNormal.mb_str(wxConvUTF8));
387 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
388 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
389 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
390 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
391 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
392 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
393 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
394 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
395 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
396 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
397 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
398 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
400 if (UploadMode == TRUE){
402 UploadData.readptr = &ServerUploadData;
403 UploadData.sizeleft = ServerUploadData.Len();
404 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
405 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
406 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
410 conncode = (curl_easy_perform(conn));
412 if (conncode == CURLE_OK){
414 // Process the server header response and look for
415 // 'addressbook' within the DAV header.
417 wxStringTokenizer wxSHeaderLines(PageHeader, wxT("\r\n"));
418 wxString wxSHeaderLine;
419 std::map<int, wxString> DAVHeaderLines;
421 while (wxSHeaderLines.HasMoreTokens()){
423 wxSHeaderLine = wxSHeaderLines.GetNextToken();
425 if (wxSHeaderLine.Mid(0, 5) == wxT("ETag:")){
427 ETagData = wxSHeaderLine.Mid(5);
429 ETagData.Trim(FALSE);
433 if (ETagData.Mid(0, 1) == wxT("\"") && ETagData.Mid((ETagData.Len() - 1), 1) == wxT("\"")){
435 ETagData.Remove(0, 1);
436 ETagData.RemoveLast();
442 if (wxSHeaderLine.Mid(0, 4) == wxT("DAV:")){
444 // Look for address book in the line.
446 if (wxSHeaderLine.Find(wxT("addressbook")) != wxNOT_FOUND){
448 HasCalDAVSupport = TRUE;
456 // Get the ETag from the header.
458 if (UploadMode == TRUE){
460 wxString PageHeaderLine;
462 wxStringTokenizer PageHeaderSplit(PageHeader, wxT("\r\n"));
464 if (PageHeaderSplit.HasMoreTokens()){
466 PageHeaderLine = PageHeaderSplit.GetNextToken();
475 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
477 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
479 fprintf(stderr, "curl_easy_perform() failed: %s\n",
480 curl_easy_strerror(conncode));
482 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
490 fprintf(stderr, "curl_easy_perform() failed: %s\n",
491 curl_easy_strerror(conncode));
502 *ServerResult = TRUE;
507 void CardDAV::ProcessData(){
511 std::thread ConnectThread(&CardDAV::ProcessDataThread, this);
512 ConnectThread.detach();