// 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; }