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 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;
68 wxString ETagOriginal;
71 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/") + ServerPrefix + ServerFilenameLocation;
72 ServerAddressSSL = wxT("https://") + ServerAddressURL;
73 ServerAddressNormal = wxT("http://") + ServerAddressURL;
75 ServerAuth = ServerUser + wxT(":") + ServerPass;
77 std::map<int,int>::iterator ActIter;
78 struct UploadDataStruc UploadData;
81 ActIter = ActivityListPtr->find((int)ItemIndex);
83 // Update result flag.
87 // Setup the request mode if it is not empty.
89 if (!ServerMethod.IsEmpty()){
91 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, (const char*)ServerMethod.mb_str(wxConvUTF8));
99 wxString ServerCertFilename;
100 bool MatchingCert = FALSE;
102 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
103 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
104 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
105 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
106 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
107 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
108 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
109 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
110 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
111 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
112 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
113 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
114 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
116 if (UploadMode == TRUE){
118 UploadData.readptr = &ServerUploadData;
119 UploadData.sizeleft = ServerUploadData.Len();
120 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
121 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
122 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
126 ServerCertFilename = GetAccountDir(ServerAccount, TRUE);
128 if (wxFile::Exists(ServerCertFilename) == TRUE){
130 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 1);
131 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 2);
132 curl_easy_setopt(conn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8));
136 claconncode = (curl_easy_perform(conn));
138 // If CURLE_PEER_FAILED_VERIFICATION is returned, retry without
139 // the local certificate in use.
141 if (claconncode == CURLE_PEER_FAILED_VERIFICATION){
143 curl_easy_cleanup(conn);
144 conn = curl_easy_init();
146 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
147 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
148 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
149 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
150 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
151 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
152 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
153 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
154 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
155 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
156 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
157 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
158 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
160 if (UploadMode == TRUE){
162 UploadData.readptr = &ServerUploadData;
163 UploadData.sizeleft = ServerUploadData.Len();
164 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
165 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
166 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
170 claconncode = (curl_easy_perform(conn));
172 // If claconncode is CURLE_OK then delete the certificate file as that
173 // is no longer needed.
175 if (claconncode == CURLE_OK){
177 // Delete the certificate file.
179 wxRemoveFile(ServerCertFilename);
185 // Check if it fails with a CURLE_SSL_CACERT then compare
186 // the certificates as PEM files.
188 if (claconncode == CURLE_SSL_CACERT && wxFile::Exists(ServerCertFilename) == TRUE){
191 sslerrconn = curl_easy_init();
192 CURLcode sslerrconncode;
194 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
196 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
197 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
198 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
199 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
200 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
201 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
202 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
203 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
204 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
205 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
206 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
207 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
208 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
209 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
211 wxString SSLLocalData;
212 wxString SSLServerData;
214 sslerrconncode = (curl_easy_perform(sslerrconn));
216 SSLCertCol = BuildSSLCollection(sslerrconn);
217 std::map<int, SSLCertData>::iterator SSLCDIter = SSLCertCol.SSLCollection.find(0);
218 std::multimap<wxString,wxString>::iterator SSLDataIter = SSLCDIter->second.CertData.find(wxT("Cert"));
220 wxFFile SSLLocalFile;
222 #if wxABI_VERSION < 20900
223 SSLLocalFile.Open(ServerCertFilename.c_str(), wxT("r"));
225 SSLLocalFile.Open(ServerCertFilename, wxT("r"));
228 // Load the recovery database for tasks not done.
230 if (SSLLocalFile.IsOpened() == TRUE){
232 // Check if we are using wxWidgets version 2.8 or less and
233 // execute the required command accordingly.
235 SSLLocalFile.ReadAll(&SSLLocalData, wxConvAuto());
240 SSLServerData = SSLDataIter->second;
242 if (SSLLocalData == SSLServerData){
244 // Server key matches with local key so retry with CURLOPT_SSL_VERIFYPEER
245 // and CURLOPT_SSL_VERIFYHOST off.
247 curl_easy_cleanup(conn);
248 conn = curl_easy_init();
253 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8));
254 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
255 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
256 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
257 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
258 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
259 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
260 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
261 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
262 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
263 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
264 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
265 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
267 if (UploadMode == TRUE){
269 UploadData.readptr = &ServerUploadData;
270 UploadData.sizeleft = ServerUploadData.Len();
271 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
272 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
273 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
277 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0);
278 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0);
280 claconncode = (curl_easy_perform(conn));
286 if (MatchingCert == FALSE){
288 claconncode = CURLE_SSL_CACERT;
293 curl_easy_cleanup(sslerrconn);
297 // Sort out SSL error.
299 // When SSL cert error occurs, connect again and fetch certificates.
300 // Display a message to the user explaining that an invalid
301 // certificate has been given and let the user decide what
304 if (claconncode == CURLE_OK){
306 } else if (claconncode == CURLE_SSL_CACERT || claconncode == CURLE_PEER_FAILED_VERIFICATION){
309 sslerrconn = curl_easy_init();
310 CURLcode sslerrconncode;
312 wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
314 // Replace conn with sslerrconn!
316 curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8));
317 curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0);
318 curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
319 curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60);
320 curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE);
321 curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
322 curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc);
323 curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData);
324 curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader);
325 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this);
326 curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
327 curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1);
328 curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0);
329 curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1);
331 sslerrconncode = (curl_easy_perform(sslerrconn));
333 SSLCertCol = BuildSSLCollection(sslerrconn);
334 SSLCertCol.SuccessCode = 1;
338 } else if (claconncode == CURLE_HTTP_RETURNED_ERROR){
340 fprintf(stderr, "curl_easy_perform() failed: %s\n",
341 curl_easy_strerror(claconncode));
343 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
344 fprintf(stderr, "Error code was: %d\n", http_code);
350 fprintf(stderr, "curl_easy_perform() failed: %s\n",
351 curl_easy_strerror(claconncode));
353 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code);
354 fprintf(stderr, "Error code was: %d\n", http_code);
364 curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressNormal.mb_str(wxConvUTF8));
365 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
366 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
367 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
368 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
369 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
370 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
371 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
372 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
373 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
374 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
375 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
376 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
378 if (UploadMode == TRUE){
380 UploadData.readptr = &ServerUploadData;
381 UploadData.sizeleft = ServerUploadData.Len();
382 curl_easy_setopt(conn, CURLOPT_UPLOAD, 1);
383 curl_easy_setopt(conn, CURLOPT_READDATA, &UploadData);
384 curl_easy_setopt(conn, CURLOPT_READFUNCTION, UploadReadFunc);
388 conncode = (curl_easy_perform(conn));
390 if (conncode == CURLE_OK){
392 // Process the server header response and look for
393 // 'addressbook' within the DAV header.
395 wxStringTokenizer wxSHeaderLines(PageHeader, wxT("\r\n"));
396 wxString wxSHeaderLine;
397 std::map<int, wxString> DAVHeaderLines;
399 while (wxSHeaderLines.HasMoreTokens()){
401 wxSHeaderLine = wxSHeaderLines.GetNextToken();
403 if (wxSHeaderLine.Mid(0, 5) == wxT("ETag:")){
405 ETagData = wxSHeaderLine.Mid(5);
407 ETagData.Trim(FALSE);
411 if (ETagData.Mid(0, 1) == wxT("\"") && ETagData.Mid((ETagData.Len() - 1), 1) == wxT("\"")){
413 ETagData.Remove(0, 1);
414 ETagData.RemoveLast();
420 if (wxSHeaderLine.Mid(0, 4) == wxT("DAV:")){
422 // Look for address book in the line.
424 if (wxSHeaderLine.Find(wxT("addressbook")) != wxNOT_FOUND){
426 HasCalDAVSupport = TRUE;
434 // Get the ETag from the header.
436 if (UploadMode == TRUE){
438 wxString PageHeaderLine;
440 wxStringTokenizer PageHeaderSplit(PageHeader, wxT("\r\n"));
442 if (PageHeaderSplit.HasMoreTokens()){
444 PageHeaderLine = PageHeaderSplit.GetNextToken();
453 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
455 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
457 fprintf(stderr, "curl_easy_perform() failed: %s\n",
458 curl_easy_strerror(conncode));
460 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
468 fprintf(stderr, "curl_easy_perform() failed: %s\n",
469 curl_easy_strerror(conncode));
480 *ServerResult = TRUE;
485 void CardDAV::ProcessData(){
489 std::thread ConnectThread(&CardDAV::ProcessDataThread, this);
490 ConnectThread.detach();