// CalDAV.cpp - CalDAV Connection Object. // // (c) 2016-2017 Xestia Software Development. // // This file is part of Xestia Calendar. // // Xestia Calendar 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 Calendar 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 Calendar. If not, see #include "CalDAV.h" using namespace std; size_t CalDAVReceive(char *receivedBuffer, size_t size, size_t newMemoryBytes, string *stringPointer) { stringPointer->append(receivedBuffer, newMemoryBytes); return size * newMemoryBytes; } size_t CalDAVSend(char *sendBuffer, size_t size, size_t newMemoryBytes, void *dataStruct){ struct CalDAVSendData *uploadPtr = (struct CalDAVSendData *)dataStruct; if (uploadPtr->sizeleft){ uploadPtr->sizeleft--; char charSend; charSend = (*uploadPtr->readptr)[uploadPtr->seek]; *sendBuffer = charSend; uploadPtr->seek++; return 1; } return 0; } CalDAV::CalDAV(){ // Setup the objects within the CalDAV connection // object. connectionHandle = curl_easy_init(); } CalDAV::~CalDAV(){ // Destory the objects within the CalDAV connection // object. curl_easy_cleanup(connectionHandle); connectionHandle = nullptr; } void CalDAV::SetupConnectionData(CalDAVConnectionData *connData){ // Check if ConnData is a nullptr, return if it is. if (connData == nullptr){ return; } // Set the connection settings to the values from ConnData. connectionData = (*connData); } CalDAVStatus CalDAV::GetConnectionData(){ // Get the current connection settings for the CalDAV // connection object and return a CalDAVStatus object. CalDAVStatus connectionStatus; connectionStatus.hostname = connectionData.hostname; connectionStatus.port = connectionData.port; connectionStatus.username = connectionData.username; connectionStatus.prefix = connectionData.prefix; connectionStatus.useSSL = connectionData.useSSL; connectionStatus.timeout = connectionData.timeout; return connectionStatus; } CalDAVServerResult CalDAV::Connect(){ CalDAVServerResult serverResult; string serverAddress = ""; string serverUserPass = ""; // Setup the server address. serverAddress = BuildServerAddress(&connectionData, "/principals/"); // Setup the server password. serverUserPass += connectionData.username; serverUserPass += ":"; serverUserPass += connectionData.password; curl_easy_setopt(connectionHandle, CURLOPT_URL, serverAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_USERPWD, serverUserPass.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); curl_easy_setopt(connectionHandle, CURLOPT_FAILONERROR, 1L); curl_easy_setopt(connectionHandle, CURLOPT_TIMEOUT, connectionData.timeout); curl_easy_setopt(connectionHandle, CURLOPT_WRITEFUNCTION, CalDAVReceive); curl_easy_setopt(connectionHandle, CURLOPT_WRITEDATA, &serverData); curl_easy_setopt(connectionHandle, CURLOPT_WRITEHEADER, &serverHeader); // Connect to the CalDAV server. serverResult.code = curl_easy_perform(connectionHandle); // Process the result received from the server. if (serverResult.code != CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } else { serverResult.result = CALDAVQUERYRESULT_OK; } // Get the HTTP code. curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); return serverResult; } CalDAVServerResult CalDAV::GetServerResult(){ return connectionServerResult; } CalDAVServerSupport CalDAV::GetServerSupport(){ CalDAVServerSupport serverStatus; // Setup the server connection. curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "OPTIONS"); CURLcode serverResult = curl_easy_perform(connectionHandle); // Set the results. if (serverResult == CURLE_OK){ connectionServerResult.result = CALDAVQUERYRESULT_OK; } else { connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR; } connectionServerResult.code = serverResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode); if (serverResult != CURLE_OK){ return serverStatus; } // Check that the server header has data in, // otherwise return an "empty" CalDAVServerSupport. if (serverHeader.size() == 0){ return serverStatus; } // Process each line looking for the first DAV header // line. bool newlineMode = true; string davLine; for (int charSeek = 0; charSeek < serverHeader.size(); charSeek++){ if (newlineMode == true){ // Check if we have reached the end of the string. if (charSeek >= serverHeader.size()){ break; } // Check the first four letters to make sure // they are 'DAV:'. string davHeaderCheck = ""; try { davHeaderCheck = serverHeader.substr(charSeek, 4); } catch (out_of_range &oor){ break; } if (davHeaderCheck == "DAV:"){ charSeek += 5; for (; charSeek < serverHeader.size(); charSeek++){ if (serverHeader[charSeek] == '\n'){ break; } davLine.push_back(serverHeader[charSeek]); } break; } newlineMode = false; } if (serverHeader[charSeek] == '\n'){ newlineMode = true; } } // Process the DAV line. vector davLineData; string davSegmentString; for (int charSeek = 0; charSeek < davLine.size(); charSeek++){ if (davLine[charSeek] == ' '){ continue; } if (davLine[charSeek] == ','){ davLineData.push_back(davSegmentString); davSegmentString.clear(); continue; } davSegmentString += davLine[charSeek]; } // Process the DAV values and set each value // to true as required. for (int davItemSeek = 0; davItemSeek < davLineData.size(); davItemSeek++){ if (davLineData.at(davItemSeek) == "calendar-access"){ serverStatus.basicSupport = true; } } // Reset the connection status. curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); return serverStatus; } string CalDAV::GetUserPrincipal(){ string currentUserPrincipal = ""; string userPrincipalRequest = ""; CalDAVSendData userPrincipalSendData; userPrincipalRequest = "\n" "\n" " \n" " \n" " \n" ""; userPrincipalSendData.readptr = &userPrincipalRequest; userPrincipalSendData.sizeleft = userPrincipalRequest.size(); // Setup the header. struct curl_slist *userPrincipalRequestHeader = NULL; userPrincipalRequestHeader = curl_slist_append(userPrincipalRequestHeader, "Depth: 0"); userPrincipalRequestHeader = curl_slist_append(userPrincipalRequestHeader, "Prefer: return-minimal"); userPrincipalRequestHeader = curl_slist_append(userPrincipalRequestHeader, "Content-Type: application/xml; charset=utf-8"); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, userPrincipalRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &userPrincipalSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverResult = curl_easy_perform(connectionHandle); // Set the results. if (serverResult == CURLE_OK){ connectionServerResult.result = CALDAVQUERYRESULT_OK; } else { connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR; } connectionServerResult.code = serverResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode); if (serverResult != CURLE_OK){ return currentUserPrincipal; } // Process the User Principal from the ServerData. currentUserPrincipal = ProcessXMLUserPrincipal(); // Reset the changed settings. curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); return currentUserPrincipal; } string CalDAV::GetCalendarHome(string userPrincipalURI){ string calendarHomeURI = ""; // Build the Calendar Home URL address. string calendarHomeURL = BuildServerAddress(&connectionData, userPrincipalURI); // Setup the header request. CalDAVSendData calendarHomeSendData; string calendarHomeRequest = "\n" "\n" " \n" " \n" " \n" ""; calendarHomeSendData.readptr = &calendarHomeRequest; calendarHomeSendData.sizeleft = calendarHomeRequest.size(); // Setup the header. struct curl_slist *calendarRequestHeader = NULL; calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Depth: 0"); calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Prefer: return-minimal"); calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Content-Type: application/xml; charset=utf-8"); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarHomeURL.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarHomeSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverResult = curl_easy_perform(connectionHandle); // Set the results. if (serverResult == CURLE_OK){ connectionServerResult.result = CALDAVQUERYRESULT_OK; } else { connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR; } connectionServerResult.code = serverResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode); if (serverResult != CURLE_OK){ return calendarHomeURI; } // Process the User Principal from the ServerData. calendarHomeURI = ProcessXMLCalendarHome(); // Reset the changed settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL); return calendarHomeURI; } CalDAVCalendarList CalDAV::GetCalendars(){ CalDAVCalendarList serverList; CalDAVSendData calendarListSendData; // Build the server address. string userPrincipalURI = ""; userPrincipalURI = GetUserPrincipal(); if (userPrincipalURI.size() == 0){ return serverList; } string calendarHomeURI = ""; calendarHomeURI = GetCalendarHome(userPrincipalURI); string calendarListURLAddress = BuildServerAddress(&connectionData, calendarHomeURI); string calendarListRequest = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; calendarListSendData.readptr = &calendarListRequest; calendarListSendData.sizeleft = calendarListRequest.size(); // Setup the header. struct curl_slist *calendarListRequestHeader = NULL; calendarListRequestHeader = curl_slist_append(calendarListRequestHeader, "Depth: 1"); calendarListRequestHeader = curl_slist_append(calendarListRequestHeader, "Prefer: return-minimal"); calendarListRequestHeader = curl_slist_append(calendarListRequestHeader, "Content-Type: application/xml; charset=utf-8"); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarListRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarListURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarListSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverResult = curl_easy_perform(connectionHandle); //ServerList = ProcessXMLCalendarList(); if (serverResult == CURLE_OK){ connectionServerResult.result = CALDAVQUERYRESULT_OK; } else { connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR; } connectionServerResult.code = serverResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode); if (serverResult != CURLE_OK){ return serverList; } // Process the received XML data into a list of calendars // and locations. serverList = ProcessXMLCalendarList(); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); return serverList; } CalDAVEntryList CalDAV::GetEntryList(string *calendarHREF){ CalDAVEntryList entryList; CalDAVSendData entryListSendData; if (calendarHREF->size() == 0){ return entryList; } string entryListURLAddress = BuildServerAddress(&connectionData, *calendarHREF); string entryListRequest = "\n"; /*if (CalendarTag == nullptr){*/ entryListRequest += "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; /*} else { EntryListRequest += "\n" " "; EntryListRequest += *CalendarTag; EntryListRequest += "\n" " 1\n" " \n" " \n" " \n" " \n" ""; }*/ entryListSendData.readptr = &entryListRequest; entryListSendData.sizeleft = entryListRequest.size(); struct curl_slist *entryListRequestHeader = NULL; entryListRequestHeader = curl_slist_append(entryListRequestHeader, "Content-Type: application/xml; charset=utf-8"); /*if (CalendarTag != nullptr){ EntryListRequestHeader = curl_slist_append(EntryListRequestHeader, "Content-Type: application/xml; charset=utf-8"); EntryListRequestHeader = curl_slist_append(EntryListRequestHeader, "Content-Type: application/xml; charset=utf-8"); }*/ curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, entryListRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, entryListURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryListSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverResult = curl_easy_perform(connectionHandle); //ServerList = ProcessXMLCalendarList(); if (serverResult == CURLE_OK){ connectionServerResult.result = CALDAVQUERYRESULT_OK; } else { connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR; } connectionServerResult.code = serverResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode); if (serverResult != CURLE_OK){ return entryList; } // Process the received XML data into a list of calendars // and locations. entryList = ProcessXMLEntryList(); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); return entryList; } CalDAVEntryList CalDAV::GetEntryList(string *calendarHREF, string *calendarTag){ CalDAVEntryList entryList; CalDAVSendData entryListSendData; if (calendarHREF->size() == 0){ return entryList; } string entryListURLAddress = BuildServerAddress(&connectionData, *calendarHREF); // First query: Get the list of contacts that need to be updated. string entryListRequest = "\n"; entryListRequest += "\n" " "; if (calendarTag != nullptr){ entryListRequest += *calendarTag; } else { entryListRequest += ""; } entryListRequest += "\n" " 1\n" " \n" " \n" " \n" ""; entryListSendData.readptr = &entryListRequest; entryListSendData.sizeleft = entryListRequest.size(); struct curl_slist *entryListRequestHeader = NULL; entryListRequestHeader = curl_slist_append(entryListRequestHeader, "Content-Type: application/xml; charset=utf-8"); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, entryListRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, entryListURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryListSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverResult = curl_easy_perform(connectionHandle); if (serverResult == CURLE_OK){ connectionServerResult.result = CALDAVQUERYRESULT_OK; } else { connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR; } connectionServerResult.code = serverResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode); if (serverResult != CURLE_OK){ return entryList; } entryList = ProcessXMLSyncTokenList(); // Check the last entry matches the HREF and if it // does then delete it. if (entryList.href.size() > 0) { if (entryList.href.rbegin()->second == *calendarHREF){ entryList.href.erase((entryList.href.size() - 1)); entryList.tag.erase((entryList.href.size() - 1)); entryList.data.erase((entryList.href.size() - 1)); } } // Build the list into a new list for getting the new // calendar data with. entryListRequest.clear(); entryListRequest = "\n"; entryListRequest += "\n" " \n" " \n" " \n" " \n"; for (std::map::iterator hrefIter = entryList.href.begin(); hrefIter != entryList.href.end(); hrefIter++){ string entryListHREFString = hrefIter->second; entryListRequest += " "; entryListRequest += entryListHREFString; entryListRequest += "\n"; } entryListRequest += ""; CalDAVSendData updatedEntryListSendData; updatedEntryListSendData.readptr = &entryListRequest; updatedEntryListSendData.sizeleft = entryListRequest.size(); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, entryListRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, entryListURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &updatedEntryListSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Get the updated calendar data. serverData.clear(); serverHeader.clear(); entryList.href.clear(); entryList.tag.clear(); entryList.data.clear(); serverResult = curl_easy_perform(connectionHandle); // Check the last entry matches the HREF and if it // does then delete it. if (serverResult == CURLE_OK){ connectionServerResult.result = CALDAVQUERYRESULT_OK; } else { connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR; } connectionServerResult.code = serverResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode); if (serverResult != CURLE_OK){ return entryList; } entryList = ProcessXMLEntryList(); // Second query: Get the list of contact data for the contacts that have // beenchanged. // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); return entryList; } CalDAVServerResult CalDAV::AddCalendar(string calendarName){ CalDAVServerResult serverResult; AddCalendar(&calendarName, nullptr); return serverResult; } CalDAVServerResult CalDAV::AddCalendar(string *calendarName, string *calendarShortName){ CalDAVServerResult serverResult; CalDAVSendData calendarAddSendData; // Build the server address. string userPrincipalURI = ""; userPrincipalURI = GetUserPrincipal(); if (userPrincipalURI.size() == 0){ return serverResult; } string calendarHomeURI = ""; calendarHomeURI = GetCalendarHome(userPrincipalURI); // Generate the UUID. string UUIDValue = ""; if (calendarShortName == nullptr){ UUIDValue = GenerateUUID(); UUIDValue.erase(UUIDValue.end()-1); } else { UUIDValue = *calendarShortName; } string calendarHomeURL = calendarHomeURI; calendarHomeURL.append(UUIDValue); calendarHomeURL.append("/"); // Build the calendar list address. string calendarListURLAddress = BuildServerAddress(&connectionData, calendarHomeURL); string calendarAddRequest = "\n" "\n" " \n" " \n" " "; calendarAddRequest += *calendarName; calendarAddRequest += "\n" " \n" " \n" " \n" " \n" " \n" " \n" ""; calendarAddSendData.readptr = &calendarAddRequest; calendarAddSendData.sizeleft = calendarAddRequest.size(); // Setup the header. struct curl_slist *calendarRequestHeader = NULL; //curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarListURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "MKCALENDAR"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarAddSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverConnectionResult = curl_easy_perform(connectionHandle); if (serverConnectionResult == CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_OK; } else { serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } serverResult.code = serverConnectionResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); return serverResult; } CalDAVServerResult CalDAV::EditCalendarProcess(string *calendarHREF, string *calendarName, Colour *calendarColour, string *calendarDescription, int *calendarOrder){ CalDAVServerResult serverResult; CalDAVSendData calendarEditSendData; // Build the server address. string userPrincipalURI = ""; userPrincipalURI = GetUserPrincipal(); if (userPrincipalURI.size() == 0){ return serverResult; } string calendarHomeURI = ""; calendarHomeURI = GetCalendarHome(userPrincipalURI); // Generate the UUID. string UUIDValue = GenerateUUID(); UUIDValue.erase(UUIDValue.end()-1); string calendarHomeURL = calendarHomeURI; calendarHomeURL.append(UUIDValue); calendarHomeURL.append("/"); // Build the calendar list address. string calendarEditURLAddress = BuildServerAddress(&connectionData, (*calendarHREF)); string calendarEditRequest = "\n" "\n" " \n" " \n"; // Update the calendar name. if (calendarName != nullptr){ calendarEditRequest += ""; calendarEditRequest += (*calendarName); calendarEditRequest += "\n"; } // Update the calendar colour. if (calendarColour != nullptr){ calendarEditRequest += ""; calendarEditRequest += (*calendarColour); calendarEditRequest += "\n"; } // Update the calendar description. if (calendarDescription != nullptr){ calendarEditRequest += ""; calendarEditRequest += (*calendarDescription); calendarEditRequest += "\n"; } // Update the calendar order. if (calendarOrder != nullptr){ calendarEditRequest += ""; calendarEditRequest += to_string((*calendarOrder)); calendarEditRequest += "\n"; } calendarEditRequest += " \n" " \n" ""; calendarEditSendData.readptr = &calendarEditRequest; calendarEditSendData.sizeleft = calendarEditRequest.size(); // Setup the header. struct curl_slist *calendarRequestHeader = NULL; //curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarEditURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPPATCH"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarEditSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverConnectionResult = curl_easy_perform(connectionHandle); if (serverConnectionResult == CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_OK; } else { serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } serverResult.code = serverConnectionResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); return serverResult; } CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF, string *calendarName, Colour *calendarColour, string *calendarDescription, int *calendarOrder){ CalDAVServerResult serverResult; serverResult = EditCalendarProcess(calendarHREF, calendarName, calendarColour, calendarDescription, calendarOrder); return serverResult; } CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF, Colour *calendarColour){ CalDAVServerResult serverResult; serverResult = EditCalendarProcess(calendarHREF, nullptr, calendarColour, nullptr, nullptr); return serverResult; } CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF, string *calendarName){ CalDAVServerResult serverResult; serverResult = EditCalendarProcess(calendarHREF, calendarName, nullptr, nullptr, nullptr); return serverResult; } CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF, int *calendarOrder){ CalDAVServerResult serverResult; serverResult = EditCalendarProcess(calendarHREF, nullptr, nullptr, nullptr, calendarOrder); return serverResult; } CalDAVServerResult CalDAV::EditCalendarDescription(string *calendarHREF, string *calendarDescription){ CalDAVServerResult serverResult; serverResult = EditCalendarProcess(calendarHREF, nullptr, nullptr, calendarDescription, nullptr); return serverResult; } CalDAVServerResult CalDAV::DeleteCalendar(string *calendarHREF){ CalDAVServerResult serverResult; // Build the server address. string userPrincipalURI = ""; userPrincipalURI = GetUserPrincipal(); if (userPrincipalURI.size() == 0){ return serverResult; } string calendarHomeURI = ""; calendarHomeURI = GetCalendarHome(userPrincipalURI); // Generate the UUID. string UUIDValue = GenerateUUID(); UUIDValue.erase(UUIDValue.end()-1); string calendarHomeURL = calendarHomeURI; calendarHomeURL.append(UUIDValue); calendarHomeURL.append("/"); // Build the calendar list address. struct curl_slist *deleteRequestHeader = NULL; deleteRequestHeader = curl_slist_append(deleteRequestHeader, "Depth: infinity"); string calendarDeleteURLAddress = BuildServerAddress(&connectionData, (*calendarHREF)); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, deleteRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarDeleteURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE"); // Delete the calendar. serverData.clear(); serverHeader.clear(); CURLcode serverConnectionResult = curl_easy_perform(connectionHandle); if (serverConnectionResult == CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_OK; } else { serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } serverResult.code = serverConnectionResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL); return serverResult; } CalDAVServerResult CalDAV::GetEntryETag(string *calendarEntryHREF, string *eTagValue){ CalDAVServerResult serverResult; CalDAVSendData entryETagGetData; // Build the server address. string userPrincipalURI = ""; userPrincipalURI = GetUserPrincipal(); if (userPrincipalURI.size() == 0){ return serverResult; } string calendarHomeURI = ""; calendarHomeURI = GetCalendarHome(userPrincipalURI); // Split the path and filename. string entryURIPath; string entryFilename; SplitPathFilename(calendarEntryHREF, &entryURIPath, &entryFilename); // Build the request for the server. string entryETagRequest = "\n" "\n" " \n" " \n" " \n" " "; entryETagRequest += (*calendarEntryHREF); entryETagRequest += "\n" ""; entryETagGetData.readptr = &entryETagRequest; entryETagGetData.sizeleft = entryETagRequest.size(); // Build the calendar list address. struct curl_slist *getETagRequestHeader = NULL; getETagRequestHeader = curl_slist_append(getETagRequestHeader, "Depth: 1"); getETagRequestHeader = curl_slist_append(getETagRequestHeader, "Prefer: return-minimal"); getETagRequestHeader = curl_slist_append(getETagRequestHeader, "Content-Type: application/xml; charset=utf-8"); string getETagURLAddress = BuildServerAddress(&connectionData, entryURIPath); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, getETagRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, getETagURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryETagGetData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Attempt to get the entity tag. serverData.clear(); serverHeader.clear(); CURLcode serverConnectionResult = curl_easy_perform(connectionHandle); if (serverConnectionResult == CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_OK; } else { serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } serverResult.code = serverConnectionResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); if (serverConnectionResult != CURLE_OK){ return serverResult; } // Get the entity tag from the result. *eTagValue = ProcessXMLEntryETag(); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL); return serverResult; } CalDAVServerResult CalDAV::AddEntry(string *calendarEntryHREF, string *entryData){ // Add an entry to the calendar collection. CalDAVServerResult serverResult; CalDAVSendData entryAddSendData; // Build the calendar list address. string entryAddURLAddress = BuildServerAddress(&connectionData, (*calendarEntryHREF)); entryAddSendData.readptr = entryData; entryAddSendData.sizeleft = entryData->size(); struct curl_slist *calendarRequestHeader = NULL; calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Content-Type: text/calendar; charset=utf-8"); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, entryAddURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PUT"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryAddSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverConnectionResult = curl_easy_perform(connectionHandle); if (serverConnectionResult == CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_OK; } else { serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } serverResult.code = serverConnectionResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL); return serverResult; } CalDAVServerResult CalDAV::EditEntry(string *calendarEntryHREF, string *entryData, string *entryETag){ // Edit an entry in the calendar collection. // Add an entry to the calendar collection. CalDAVServerResult serverResult; CalDAVSendData entryAddSendData; // Build the calendar list address. string entryAddURLAddress = BuildServerAddress(&connectionData, (*calendarEntryHREF)); entryAddSendData.readptr = entryData; entryAddSendData.sizeleft = entryData->size(); string ifMatchHeader = "If-Match: \""; ifMatchHeader.append(*entryETag); ifMatchHeader.append("\""); struct curl_slist *calendarRequestHeader = NULL; calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Content-Type: text/calendar; charset=utf-8"); calendarRequestHeader = curl_slist_append(calendarRequestHeader, ifMatchHeader.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarRequestHeader); curl_easy_setopt(connectionHandle, CURLOPT_URL, entryAddURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PUT"); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryAddSendData); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend); // Process the data. serverData.clear(); serverHeader.clear(); CURLcode serverConnectionResult = curl_easy_perform(connectionHandle); if (serverConnectionResult == CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_OK; } else { serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } serverResult.code = serverConnectionResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL); return serverResult; } CalDAVServerResult CalDAV::DeleteEntry(string *calendarEntryHREF){ // Delete an entry in the calendar collection. CalDAVServerResult serverResult; // Build the calendar list address. string entryDeleteURLAddress = BuildServerAddress(&connectionData, (*calendarEntryHREF)); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL); curl_easy_setopt(connectionHandle, CURLOPT_URL, entryDeleteURLAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE"); // Delete the calendar. serverData.clear(); serverHeader.clear(); CURLcode serverConnectionResult = curl_easy_perform(connectionHandle); if (serverConnectionResult == CURLE_OK){ serverResult.result = CALDAVQUERYRESULT_OK; } else { serverResult.result = CALDAVQUERYRESULT_SERVERERROR; } serverResult.code = serverConnectionResult; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode); // Restore the original settings. string originalServerAddress = BuildServerAddress(&connectionData, "/principals/"); curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL); curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL); return serverResult; } bool CalDAVObjectValidSettings(CalDAVConnectionData *connData){ // Check if the passed CalDAV Connection Data is has // an address set. Return false if nullptr is used. if (connData == nullptr){ return false; } // Check the server hostname. Return false // if no value has been set. if (connData->hostname.size() == 0){ return false; } // Check the server port. Return false if // no value has been set or the port number // is less than 1 or higher than 65535. if (connData->port < 1 || connData->port > 65535){ return false; } // Check the server username. Return false // if no value has been set. if (connData->username.size() == 0){ return false; } // Check the server password. Return false // if no value has been set. if (connData->password.size() == 0){ return false; } // Cannot check UseSSL: It is either true // or false. // Cannot check Prefix: The prefix may need // to be worked out first. // No errors were found whilst checking so // return true. return true; } string BuildServerAddress(CalDAVConnectionData *connData, string uriAddress){ string serverAddress; // Setup the server address. if (connData->useSSL == true){ serverAddress += "https://"; } else { serverAddress += "http://"; } serverAddress += connData->hostname; // Check if server port is 80, otherwise // specifiy the port number in the address. if (connData->port != 80){ serverAddress += ":"; serverAddress += to_string(connData->port); } serverAddress += uriAddress; return serverAddress; }