// 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;
}
COSSLVerified CalDAV::SSLVerify()
{
return SSLVerified;
}
void CalDAV::BypassSSLVerification(bool EnableBypass){
EnableSSLBypass = EnableBypass;
SSLSelfSigned = EnableBypass;
}
#if defined(__APPLE__)
SecTrustRef CalDAV::BuildSSLCollection(){
return CertificateData;
}
#elif defined(__WIN32__)
PCCERT_CONTEXT CalDAV::BuildSSLCollection(){
return CertificateData;
}
#else
SSLCertCollectionString CalDAV::BuildSSLCollection(){
// Build and return the SSL collection.
SSLCertCollectionString SSLCertInfo;
// Grab the certificate data.
union {
struct curl_slist *certdata;
struct curl_certinfo *certinfo;
} certptr;
certptr.certdata = NULL;
CURLcode result = curl_easy_getinfo(ConnectionSession, CURLINFO_CERTINFO, &certptr.certinfo);
std::string CertPropName;
std::string CertPropValue;
for (int i = 0; i < certptr.certinfo->num_of_certs; i++){
struct curl_slist *slist;
SSLCertDataString SSLCertDataInc;
for (slist = certptr.certinfo->certinfo[i]; slist; slist = slist->next){
// Using wxStringTokenizer from wxWidgets.
wxStringTokenizer CertDataInc(wxString::FromUTF8(slist->data), ":");
// Get first token as the property name.
CertPropName = CertDataInc.GetNextToken().ToStdString();
// Get remaining tokens as the property value.
while(CertDataInc.HasMoreTokens()){
CertPropValue.append(CertDataInc.GetNextToken());
}
SSLCertDataInc.CertData.insert(std::make_pair(CertPropName, CertPropValue));
CertPropName.clear();
CertPropValue.clear();
}
SSLCertInfo.SSLCollection.insert(std::make_pair(i, SSLCertDataInc));
}
return SSLCertInfo;
}
#endif
static 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;
}
static 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;
}