// carddav-serveretag.cpp - CardDAV Object - Server ETag subroutines. // // (c) 2012-2015 Xestia Software Development. // // This file is part of Xestia Address Book. // // Xestia Address Book is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by the // Free Software Foundation, version 3 of the license. // // Xestia Address Book is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with Xestia Address Book. If not, see #include "carddav.h" #include "../version.h" #include #include #include #include #include #include #include #include "../vcard/vcard.h" #include "../common/dirs.h" void CardDAV::GetServerETagValueThread() { // Get the server etag value (threaded). PageData.Clear(); PageHeader.Clear(); SSLStatus = TRUE; AuthPassed = TRUE; AbortConnection = FALSE; bool FilenameIsDirectory = FALSE; CURL *conn; CURLcode conncode; wxString ServerAddressURL; wxString ServerAuth; wxString ServerAddressSSL; wxString ServerAddressNormal; conn = curl_easy_init(); #if defined(__APPLE__) SetConnectionObject(conn); #endif struct CardDAVCURLPasser { CardDAV *Data; bool HeaderMode = TRUE; } CardDAVHeader, CardDAVFooter; CardDAVHeader.Data = this; CardDAVHeader.HeaderMode = TRUE; CardDAVFooter.Data = this; CardDAVFooter.HeaderMode = FALSE; wxString Data1; wxString Data2; ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + ServerPrefix + ServerFilenameLocation; ServerAddressSSL = wxT("https://") + ServerAddressURL; ServerAddressNormal = wxT("http://") + ServerAddressURL; ServerAuth = ServerUser + wxT(":") + ServerPass; std::map::iterator ActIter; struct UploadDataStruc UploadData; ActIter = ActivityListPtr->find((int)ItemIndex); static const char* query = "" "" "" "" "" ""; if (ServerSSL){ wxString ServerCertFilename; bool MatchingCert = FALSE; curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60); curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE); curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT); curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc); curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData); curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader); curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this); curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc); curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query); curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query)); #if defined(__APPLE__) || defined(__WIN32__) #else ServerCertFilename = GetAccountDir(ServerAccount, TRUE); if (wxFile::Exists(ServerCertFilename) == TRUE){ curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 2); curl_easy_setopt(conn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8)); } #endif claconncode = (curl_easy_perform(conn)); // If CURLE_PEER_FAILED_VERIFICATION is returned, retry without // the local certificate in use. if (claconncode == CURLE_PEER_FAILED_VERIFICATION){ curl_easy_cleanup(conn); conn = curl_easy_init(); curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60); curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE); curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT); curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc); curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData); curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader); curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this); curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc); curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query); curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query)); claconncode = (curl_easy_perform(conn)); // If claconncode is CURLE_OK then delete the certificate file as that // is no longer needed. if (claconncode == CURLE_OK){ // Delete the certificate file. wxRemoveFile(ServerCertFilename); } } // Check if it fails with a CURLE_SSL_CACERT then compare // the certificates as PEM files. #if defined(__APPLE__) #else if (claconncode == CURLE_SSL_CACERT && wxFile::Exists(ServerCertFilename) == TRUE){ CURL *sslerrconn; sslerrconn = curl_easy_init(); CURLcode sslerrconncode; wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/"); PageData.clear(); PageHeader.clear(); curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8)); curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60); curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE); curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT); curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc); curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData); curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader); curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this); curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc); curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1); curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYHOST, 2); curl_easy_setopt(sslerrconn, CURLOPT_CAINFO, (const char*)ServerCertFilename.mb_str(wxConvUTF8)); wxString SSLLocalData; wxString SSLServerData; sslerrconncode = (curl_easy_perform(sslerrconn)); SSLCertCol = BuildSSLCollection(sslerrconn); std::map::iterator SSLCDIter = SSLCertCol.SSLCollection.find(0); std::multimap::iterator SSLDataIter = SSLCDIter->second.CertData.find(wxT("Cert")); wxFFile SSLLocalFile; #if wxABI_VERSION < 20900 SSLLocalFile.Open(ServerCertFilename.c_str(), wxT("r")); #else SSLLocalFile.Open(ServerCertFilename, wxT("r")); #endif // Load the recovery database for tasks not done. if (SSLLocalFile.IsOpened() == TRUE){ // Check if we are using wxWidgets version 2.8 or less and // execute the required command accordingly. SSLLocalFile.ReadAll(&SSLLocalData, wxConvAuto()); } SSLServerData = SSLDataIter->second; if (SSLLocalData == SSLServerData){ // Server key matches with local key so retry with CURLOPT_SSL_VERIFYPEER // and CURLOPT_SSL_VERIFYHOST off. curl_easy_cleanup(conn); conn = curl_easy_init(); PageData.clear(); PageHeader.clear(); curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressSSL.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60); curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE); curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT); curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc); curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData); curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader); curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this); curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc); curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query); curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query)); curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0); claconncode = (curl_easy_perform(conn)); MatchingCert = TRUE; } if (MatchingCert == FALSE){ claconncode = CURLE_SSL_CACERT; return; } curl_easy_cleanup(sslerrconn); } #endif // Sort out SSL error. // When SSL cert error occurs, connect again and fetch certificates. // Display a message to the user explaining that an invalid // certificate has been given and let the user decide what // to do next. if (claconncode == CURLE_OK){ } else if (claconncode == CURLE_SSL_CACERT || claconncode == CURLE_PEER_FAILED_VERIFICATION){ CURL *sslerrconn; sslerrconn = curl_easy_init(); CURLcode sslerrconncode; wxString ServerAddressOnly = wxT("https://") + ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/"); // Replace conn with sslerrconn! curl_easy_setopt(sslerrconn, CURLOPT_URL, (const char*)ServerAddressOnly.mb_str(wxConvUTF8)); curl_easy_setopt(sslerrconn, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(sslerrconn, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(sslerrconn, CURLOPT_TIMEOUT, 60); curl_easy_setopt(sslerrconn, CURLOPT_FAILONERROR, TRUE); curl_easy_setopt(sslerrconn, CURLOPT_USERAGENT, XSDAB_USERAGENT); curl_easy_setopt(sslerrconn, CURLOPT_WRITEFUNCTION, WritebackFunc); curl_easy_setopt(sslerrconn, CURLOPT_WRITEDATA, &PageData); curl_easy_setopt(sslerrconn, CURLOPT_WRITEHEADER, &PageHeader); curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSDATA, this); curl_easy_setopt(sslerrconn, CURLOPT_PROGRESSFUNCTION, ProgressFunc); curl_easy_setopt(sslerrconn, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(sslerrconn, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(sslerrconn, CURLOPT_CERTINFO, 1); sslerrconncode = (curl_easy_perform(sslerrconn)); #if defined(__APPLE__) SetConnectionObject(sslerrconn); #endif SSLCertCol = BuildSSLCollection(sslerrconn); SSLCertCol.SuccessCode = 1; curl_easy_cleanup(conn); curl_easy_cleanup(sslerrconn); return; } else if (claconncode == CURLE_HTTP_RETURNED_ERROR){ fprintf(stderr, "GetServerETagValueThread(): curl_easy_perform() failed: %s\n", curl_easy_strerror(claconncode)); int http_code = 0; curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code); fprintf(stderr, "Error code was: %d\n", http_code); curl_easy_cleanup(conn); return; } else { fprintf(stderr, "GetServerETagValueThread(): curl_easy_perform() failed: %s\n", curl_easy_strerror(claconncode)); int http_code = 0; curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &http_code); fprintf(stderr, "Error code was: %d\n", http_code); curl_easy_cleanup(conn); return; } } else { // No SSL. wxString EmptyString; curl_easy_setopt(conn, CURLOPT_URL, (const char*)ServerAddressNormal.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60); curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE); curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT); curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8)); curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc); curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData); curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader); curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this); curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc); curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query); curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query)); PageData.Clear(); PageHeader.Clear(); conncode = (curl_easy_perform(conn)); if (conncode == CURLE_OK){ } else if (conncode == CURLE_HTTP_RETURNED_ERROR){ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(conncode)); return; } else { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(conncode)); return; } } xmlDocPtr xmlCardDAVDoc; xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0); xmlNodePtr nodeLevel1; xmlNodePtr nodeLevel2; xmlNodePtr nodeLevel3; xmlNodePtr nodeLevel4; xmlNodePtr nodeLevel5; xmlNodePtr nodeLevel6; std::map xmlDataMap; wxString DataFilename; wxString ETagData; std::string xmlStringSafe; // Tranverse through the catacombs of the response to get our ETag for the file. for (nodeLevel1 = xmlCardDAVDoc->children; nodeLevel1 != NULL; nodeLevel1 = nodeLevel1->next) { bool HREFFound = FALSE; bool ETagFound = FALSE; for (nodeLevel2 = nodeLevel1->children; nodeLevel2 != NULL; nodeLevel2 = nodeLevel2->next) { for (nodeLevel3 = nodeLevel2->children; nodeLevel3 != NULL; nodeLevel3 = nodeLevel3->next) { if (!xmlStrcmp(nodeLevel3->name, (const xmlChar *)"href") || !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"d:href") || !xmlStrcmp(nodeLevel3->name, (const xmlChar *)"D:href") ){ // Get the filename. for (nodeLevel4 = nodeLevel3->children; nodeLevel4 != NULL; nodeLevel4 = nodeLevel4->next) { if (!xmlStrcmp(nodeLevel4->name, (const xmlChar *)"text") || !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"d:text") || !xmlStrcmp(nodeLevel4->name, (const xmlChar *)"D:text") ){ DataFilename = wxString::FromUTF8((const char*)nodeLevel4->content); wxStringTokenizer wSTDFilename(DataFilename, wxT("/")); while (wSTDFilename.HasMoreTokens()){ DataFilename = wSTDFilename.GetNextToken(); } HREFFound = TRUE; } } } else { for (nodeLevel4 = nodeLevel3->children; nodeLevel4 != NULL; nodeLevel4 = nodeLevel4->next) { for (nodeLevel5 = nodeLevel4->children; nodeLevel5 != NULL; nodeLevel5 = nodeLevel5->next) { if (!xmlStrcmp(nodeLevel5->name, (const xmlChar *)"getetag") || !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"d:getetag") || !xmlStrcmp(nodeLevel5->name, (const xmlChar *)"D:getetag") ){ for (nodeLevel6 = nodeLevel5->children; nodeLevel6 != NULL; nodeLevel6 = nodeLevel6->next) { // Strip the quotes from the ETag. ETagData = wxString::FromUTF8((const char*)nodeLevel6->content); if (ETagData.Mid(0, 1) == wxT("\"") && ETagData.Mid((ETagData.Len() - 1), 1) == wxT("\"")){ ETagData.Remove(0, 1); ETagData.RemoveLast(); } ETagFound = TRUE; } } } } } } } if (HREFFound == TRUE && ETagFound == TRUE){ // Add to the map data. xmlDataMap.insert(std::make_pair(DataFilename, ETagData)); HREFFound = FALSE; ETagFound = FALSE; } } xmlFreeDoc(xmlCardDAVDoc); // Get the first result. for (std::map::iterator iter = xmlDataMap.begin(); iter != xmlDataMap.end(); ++iter){ ETagResult = iter->second; break; } if (ETagResult.IsEmpty()){ return; } return; } void CardDAV::GetServerETagValue(){ // Get the server etag value. std::thread ConnectThread(&CardDAV::GetServerETagValueThread, this); ConnectThread.detach(); }