// CalDAV.cpp - CalDAV Connection Object.
//
// (c) 2016 Xestia Software Development.
//
// This file is part of Xestia Calendar.
//
// Xestia Address Book is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by the
// Free Software Foundation, version 3 of the license.
//
// Xestia Address Book is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with Xestia Calendar. If not, see
#include "CalDAV.h"
using namespace std;
size_t CalDAVOutput(char *ReceivedBuffer, size_t Size, size_t NewMemoryBytes, string *StringPointer)
{
string ReceivedBufferString = "";
ReceivedBufferString.append(ReceivedBuffer, NewMemoryBytes);
StringPointer->append(ReceivedBufferString);
return Size * NewMemoryBytes;
}
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, CalDAVOutput);
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;
}
CalDAVServerSupport CalDAV::GetServerSupport(){
CalDAVServerSupport ServerStatus;
// Setup the server connection.
curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "OPTIONS");
CURLcode ServerResult = curl_easy_perform(ConnectionHandle);
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;
}
}
return ServerStatus;
}
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;
}