1 // CalDAV.cpp - CalDAV Connection Object.
3 // (c) 2016 Xestia Software Development.
5 // This file is part of Xestia Calendar.
7 // Xestia Address Book is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by the
9 // Free Software Foundation, version 3 of the license.
11 // Xestia Address Book is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with Xestia Calendar. If not, see <http://www.gnu.org/licenses/>
23 size_t CalDAVReceive(char *ReceivedBuffer, size_t Size, size_t NewMemoryBytes, string *StringPointer)
26 string ReceivedBufferString = "";
27 ReceivedBufferString.append(ReceivedBuffer, NewMemoryBytes);
29 StringPointer->append(ReceivedBufferString);
31 return Size * NewMemoryBytes;
35 size_t CalDAVSend(char *SendBuffer, size_t Size, size_t NewMemoryBytes, void *DataStruct){
37 struct CalDAVSendData *UploadPtr = (struct CalDAVSendData *)DataStruct;
39 if (UploadPtr->sizeleft){
41 UploadPtr->sizeleft--;
44 CharSend = (*UploadPtr->readptr)[UploadPtr->seek];
46 *SendBuffer = CharSend;
60 // Setup the objects within the CalDAV connection
63 ConnectionHandle = curl_easy_init();
69 // Destory the objects within the CalDAV connection
72 curl_easy_cleanup(ConnectionHandle);
73 ConnectionHandle = nullptr;
77 void CalDAV::SetupConnectionData(CalDAVConnectionData *ConnData){
79 // Check if ConnData is a nullptr, return if it is.
81 if (ConnData == nullptr){
85 // Set the connection settings to the values from ConnData.
87 ConnectionData = (*ConnData);
91 CalDAVStatus CalDAV::GetConnectionData(){
93 // Get the current connection settings for the CalDAV
94 // connection object and return a CalDAVStatus object.
96 CalDAVStatus ConnectionStatus;
98 ConnectionStatus.Hostname = ConnectionData.Hostname;
99 ConnectionStatus.Port = ConnectionData.Port;
100 ConnectionStatus.Username = ConnectionData.Username;
101 ConnectionStatus.Prefix = ConnectionData.Prefix;
102 ConnectionStatus.UseSSL = ConnectionData.UseSSL;
103 ConnectionStatus.Timeout = ConnectionData.Timeout;
105 return ConnectionStatus;
109 CalDAVServerResult CalDAV::Connect(){
111 CalDAVServerResult ServerResult;
113 string ServerAddress = "";
114 string ServerUserPass = "";
116 // Setup the server address.
118 ServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
120 // Setup the server password.
122 ServerUserPass += ConnectionData.Username;
123 ServerUserPass += ":";
124 ServerUserPass += ConnectionData.Password;
126 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, ServerAddress.c_str());
127 curl_easy_setopt(ConnectionHandle, CURLOPT_USERPWD, ServerUserPass.c_str());
128 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
129 curl_easy_setopt(ConnectionHandle, CURLOPT_FAILONERROR, 1L);
130 curl_easy_setopt(ConnectionHandle, CURLOPT_TIMEOUT, ConnectionData.Timeout);
131 curl_easy_setopt(ConnectionHandle, CURLOPT_WRITEFUNCTION, CalDAVReceive);
132 curl_easy_setopt(ConnectionHandle, CURLOPT_WRITEDATA, &ServerData);
133 curl_easy_setopt(ConnectionHandle, CURLOPT_WRITEHEADER, &ServerHeader);
135 // Connect to the CalDAV server.
137 ServerResult.Code = curl_easy_perform(ConnectionHandle);
139 // Process the result received from the server.
141 if (ServerResult.Code != CURLE_OK){
143 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
147 ServerResult.Result = CALDAVQUERYRESULT_OK;
151 // Get the HTTP code.
153 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
159 CalDAVServerResult CalDAV::GetServerResult(){
161 return ConnectionServerResult;
165 CalDAVServerSupport CalDAV::GetServerSupport(){
167 CalDAVServerSupport ServerStatus;
169 // Setup the server connection.
171 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "OPTIONS");
173 CURLcode ServerResult = curl_easy_perform(ConnectionHandle);
177 if (ServerResult == CURLE_OK){
178 ConnectionServerResult.Result = CALDAVQUERYRESULT_OK;
180 ConnectionServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
182 ConnectionServerResult.Code = ServerResult;
183 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ConnectionServerResult.HTTPCode);
185 if (ServerResult != CURLE_OK){
189 // Check that the server header has data in,
190 // otherwise return an "empty" CalDAVServerSupport.
192 if (ServerHeader.size() == 0){
196 // Process each line looking for the first DAV header
199 bool NewlineMode = true;
203 for (int CharSeek = 0; CharSeek < ServerHeader.size(); CharSeek++){
205 if (NewlineMode == true){
207 // Check if we have reached the end of the string.
209 if (CharSeek >= ServerHeader.size()){
215 // Check the first four letters to make sure
218 string DAVHeaderCheck = "";
221 DAVHeaderCheck = ServerHeader.substr(CharSeek, 4);
224 catch (out_of_range &oor){
228 if (DAVHeaderCheck == "DAV:"){
232 for (; CharSeek < ServerHeader.size(); CharSeek++){
234 if (ServerHeader[CharSeek] == '\n'){
240 DAVLine.push_back(ServerHeader[CharSeek]);
252 if (ServerHeader[CharSeek] == '\n'){
260 // Process the DAV line.
262 vector<string> DAVLineData;
263 string DAVSegmentString;
265 for (int CharSeek = 0; CharSeek < DAVLine.size(); CharSeek++){
267 if (DAVLine[CharSeek] == ' '){
271 if (DAVLine[CharSeek] == ','){
273 DAVLineData.push_back(DAVSegmentString);
274 DAVSegmentString.clear();
279 DAVSegmentString += DAVLine[CharSeek];
283 // Process the DAV values and set each value
284 // to true as required.
286 for (int DAVItemSeek = 0;
287 DAVItemSeek < DAVLineData.size();
290 if (DAVLineData.at(DAVItemSeek) == "calendar-access"){
292 ServerStatus.BasicSupport = true;
298 // Reset the connection status.
300 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
306 string CalDAV::GetUserPrincipal(){
308 string CurrentUserPrincipal = "";
309 string UserPrincipalRequest = "";
310 CalDAVSendData UserPrincipalSendData;
312 UserPrincipalRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
313 "<d:propfind xmlns:d=\"DAV:\">\n"
315 " <d:current-user-principal />\n"
319 UserPrincipalSendData.readptr = &UserPrincipalRequest;
320 UserPrincipalSendData.sizeleft = UserPrincipalRequest.size();
324 struct curl_slist *UserPrincipalRequestHeader = NULL;
326 UserPrincipalRequestHeader = curl_slist_append(UserPrincipalRequestHeader, "Depth: 0");
327 UserPrincipalRequestHeader = curl_slist_append(UserPrincipalRequestHeader, "Prefer: return-minimal");
328 UserPrincipalRequestHeader = curl_slist_append(UserPrincipalRequestHeader, "Content-Type: application/xml; charset=utf-8");
330 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, UserPrincipalRequestHeader);
332 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
333 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
334 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &UserPrincipalSendData);
335 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
340 ServerHeader.clear();
342 CURLcode ServerResult = curl_easy_perform(ConnectionHandle);
346 if (ServerResult == CURLE_OK){
347 ConnectionServerResult.Result = CALDAVQUERYRESULT_OK;
349 ConnectionServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
351 ConnectionServerResult.Code = ServerResult;
352 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ConnectionServerResult.HTTPCode);
354 if (ServerResult != CURLE_OK){
356 return CurrentUserPrincipal;
360 // Process the User Principal from the ServerData.
362 CurrentUserPrincipal = ProcessXMLUserPrincipal();
364 // Reset the changed settings.
366 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
367 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
368 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
370 return CurrentUserPrincipal;
374 string CalDAV::GetCalendarHome(string UserPrincipalURI){
376 string CalendarHomeURI = "";
378 // Build the Calendar Home URL address.
380 string CalendarHomeURL = BuildServerAddress(&ConnectionData, UserPrincipalURI);
382 // Setup the header request.
384 CalDAVSendData CalendarHomeSendData;
386 string CalendarHomeRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
387 "<d:propfind xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
389 " <c:calendar-home-set />\n"
393 CalendarHomeSendData.readptr = &CalendarHomeRequest;
394 CalendarHomeSendData.sizeleft = CalendarHomeRequest.size();
398 struct curl_slist *CalendarRequestHeader = NULL;
400 CalendarRequestHeader = curl_slist_append(CalendarRequestHeader, "Depth: 0");
401 CalendarRequestHeader = curl_slist_append(CalendarRequestHeader, "Prefer: return-minimal");
402 CalendarRequestHeader = curl_slist_append(CalendarRequestHeader, "Content-Type: application/xml; charset=utf-8");
404 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
405 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarHomeURL.c_str());
406 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
407 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
408 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &CalendarHomeSendData);
409 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
414 ServerHeader.clear();
416 CURLcode ServerResult = curl_easy_perform(ConnectionHandle);
420 if (ServerResult == CURLE_OK){
421 ConnectionServerResult.Result = CALDAVQUERYRESULT_OK;
423 ConnectionServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
425 ConnectionServerResult.Code = ServerResult;
426 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ConnectionServerResult.HTTPCode);
428 if (ServerResult != CURLE_OK){
430 return CalendarHomeURI;
434 // Process the User Principal from the ServerData.
436 CalendarHomeURI = ProcessXMLCalendarHome();
438 // Reset the changed settings.
440 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
441 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
443 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
444 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
445 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
446 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
448 return CalendarHomeURI;
452 CalDAVCalendarList CalDAV::GetCalendars(){
454 CalDAVCalendarList ServerList;
455 CalDAVSendData CalendarListSendData;
457 // Build the server address.
459 string UserPrincipalURI = "";
460 UserPrincipalURI = GetUserPrincipal();
462 if (UserPrincipalURI.size() == 0){
468 string CalendarHomeURI = "";
469 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
471 string CalendarListURLAddress = BuildServerAddress(&ConnectionData, CalendarHomeURI);
473 string CalendarListRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
474 "<d:propfind xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\""
475 " xmlns:c=\"urn:ietf:params:xml:ns:caldav\" xmlns:x0=\"http://apple.com/ns/ical/\">\n"
477 " <d:resourcetype />\n"
478 " <d:displayname />\n"
479 " <x0:calendar-color />\n"
480 " <x0:calendar-order />\n"
482 " <c:supported-calendar-component-set />\n"
483 " <c:calendar-description />\n"
487 CalendarListSendData.readptr = &CalendarListRequest;
488 CalendarListSendData.sizeleft = CalendarListRequest.size();
492 struct curl_slist *CalendarListRequestHeader = NULL;
494 CalendarListRequestHeader = curl_slist_append(CalendarListRequestHeader, "Depth: 1");
495 CalendarListRequestHeader = curl_slist_append(CalendarListRequestHeader, "Prefer: return-minimal");
496 CalendarListRequestHeader = curl_slist_append(CalendarListRequestHeader, "Content-Type: application/xml; charset=utf-8");
498 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarListRequestHeader);
499 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarListURLAddress.c_str());
500 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
501 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
502 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &CalendarListSendData);
503 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
508 ServerHeader.clear();
510 CURLcode ServerResult = curl_easy_perform(ConnectionHandle);
512 //ServerList = ProcessXMLCalendarList();
514 if (ServerResult == CURLE_OK){
515 ConnectionServerResult.Result = CALDAVQUERYRESULT_OK;
517 ConnectionServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
519 ConnectionServerResult.Code = ServerResult;
520 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ConnectionServerResult.HTTPCode);
522 if (ServerResult != CURLE_OK){
528 // Process the received XML data into a list of calendars
531 ServerList = ProcessXMLCalendarList();
533 // Restore the original settings.
535 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
537 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
538 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
539 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
540 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
541 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
547 CalDAVServerResult CalDAV::AddCalendar(string CalendarName){
549 CalDAVServerResult ServerResult;
550 CalDAVSendData CalendarAddSendData;
552 // Build the server address.
554 string UserPrincipalURI = "";
555 UserPrincipalURI = GetUserPrincipal();
557 if (UserPrincipalURI.size() == 0){
563 string CalendarHomeURI = "";
564 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
566 // Generate the UUID.
568 string UUIDValue = GenerateUUID();
569 UUIDValue.erase(UUIDValue.end()-1);
571 string CalendarHomeURL = CalendarHomeURI;
572 CalendarHomeURL.append(UUIDValue);
573 CalendarHomeURL.append("/");
575 // Build the calendar list address.
577 string CalendarListURLAddress = BuildServerAddress(&ConnectionData, CalendarHomeURL);
579 string CalendarAddRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
580 "<c:mkcalendar xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
584 CalendarAddRequest += CalendarName;
585 CalendarAddRequest += "</d:displayname>\n"
586 " <c:supported-calendar-component-set>\n"
587 " <c:comp name=\"VTODO\"/>\n"
588 " <c:comp name=\"VEVENT\"/>\n"
589 " </c:supported-calendar-component-set>\n"
594 CalendarAddSendData.readptr = &CalendarAddRequest;
595 CalendarAddSendData.sizeleft = CalendarAddRequest.size();
599 struct curl_slist *CalendarRequestHeader = NULL;
601 //curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
602 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarListURLAddress.c_str());
603 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "MKCALENDAR");
604 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
605 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &CalendarAddSendData);
606 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
611 ServerHeader.clear();
613 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
615 if (ServerConnectionResult == CURLE_OK){
616 ServerResult.Result = CALDAVQUERYRESULT_OK;
618 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
620 ServerResult.Code = ServerConnectionResult;
621 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
623 // Restore the original settings.
625 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
626 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
627 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
628 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
629 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
630 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
636 CalDAVServerResult CalDAV::EditCalendarProcess(string *CalendarHREF,
637 string *CalendarName,
638 Colour *CalendarColour,
639 string *CalendarDescription,
642 CalDAVServerResult ServerResult;
643 CalDAVSendData CalendarEditSendData;
645 // Build the server address.
647 string UserPrincipalURI = "";
648 UserPrincipalURI = GetUserPrincipal();
650 if (UserPrincipalURI.size() == 0){
656 string CalendarHomeURI = "";
657 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
659 // Generate the UUID.
661 string UUIDValue = GenerateUUID();
662 UUIDValue.erase(UUIDValue.end()-1);
664 string CalendarHomeURL = CalendarHomeURI;
665 CalendarHomeURL.append(UUIDValue);
666 CalendarHomeURL.append("/");
668 // Build the calendar list address.
670 string CalendarEditURLAddress = BuildServerAddress(&ConnectionData, (*CalendarHREF));
672 string CalendarEditRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
673 "<d:propertyupdate xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\"\n"
674 " xmlns:x0=\"http://apple.com/ns/ical/\">\n"
678 // Update the calendar name.
680 if (CalendarName != nullptr){
682 CalendarEditRequest += "<d:displayname>";
683 CalendarEditRequest += (*CalendarName);
684 CalendarEditRequest += "</d:displayname>\n";
688 // Update the calendar colour.
690 if (CalendarColour != nullptr){
692 CalendarEditRequest += "<x0:calendar-color>";
693 CalendarEditRequest += (*CalendarColour);
694 CalendarEditRequest += "</x0:calendar-color>\n";
698 // Update the calendar description.
700 if (CalendarDescription != nullptr){
702 CalendarEditRequest += "<c:calendar-description>";
703 CalendarEditRequest += (*CalendarDescription);
704 CalendarEditRequest += "</c:calendar-description>\n";
708 // Update the calendar order.
710 if (CalendarOrder != nullptr){
712 CalendarEditRequest += "<x0:calendar-order>";
713 CalendarEditRequest += to_string((*CalendarOrder));
714 CalendarEditRequest += "</x0:calendar-order>\n";
718 CalendarEditRequest += " </d:prop>\n"
720 "</d:propertyupdate>";
722 CalendarEditSendData.readptr = &CalendarEditRequest;
723 CalendarEditSendData.sizeleft = CalendarEditRequest.size();
727 struct curl_slist *CalendarRequestHeader = NULL;
729 //curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
730 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarEditURLAddress.c_str());
731 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
732 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
733 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &CalendarEditSendData);
734 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
739 ServerHeader.clear();
741 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
743 if (ServerConnectionResult == CURLE_OK){
744 ServerResult.Result = CALDAVQUERYRESULT_OK;
746 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
748 ServerResult.Code = ServerConnectionResult;
749 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
751 // Restore the original settings.
753 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
754 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
755 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
756 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
757 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
758 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
764 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
765 string *CalendarName,
766 Colour *CalendarColour,
767 string *CalendarDescription,
770 CalDAVServerResult ServerResult;
772 ServerResult = EditCalendarProcess(CalendarHREF,
782 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
783 Colour *CalendarColour){
786 CalDAVServerResult ServerResult;
788 ServerResult = EditCalendarProcess(CalendarHREF,
798 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
799 string *CalendarName){
801 CalDAVServerResult ServerResult;
803 ServerResult = EditCalendarProcess(CalendarHREF,
813 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
816 CalDAVServerResult ServerResult;
818 ServerResult = EditCalendarProcess(CalendarHREF,
828 CalDAVServerResult CalDAV::EditCalendarDescription(string *CalendarHREF,
829 string *CalendarDescription){
831 CalDAVServerResult ServerResult;
833 ServerResult = EditCalendarProcess(CalendarHREF,
843 CalDAVServerResult CalDAV::DeleteCalendar(string *CalendarHREF){
845 CalDAVServerResult ServerResult;
847 // Build the server address.
849 string UserPrincipalURI = "";
850 UserPrincipalURI = GetUserPrincipal();
852 if (UserPrincipalURI.size() == 0){
858 string CalendarHomeURI = "";
859 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
861 // Generate the UUID.
863 string UUIDValue = GenerateUUID();
864 UUIDValue.erase(UUIDValue.end()-1);
866 string CalendarHomeURL = CalendarHomeURI;
867 CalendarHomeURL.append(UUIDValue);
868 CalendarHomeURL.append("/");
870 // Build the calendar list address.
872 struct curl_slist *DeleteRequestHeader = NULL;
874 DeleteRequestHeader = curl_slist_append(DeleteRequestHeader, "Depth: infinity");
876 string CalendarDeleteURLAddress = BuildServerAddress(&ConnectionData, (*CalendarHREF));
878 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, DeleteRequestHeader);
879 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarDeleteURLAddress.c_str());
880 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
882 // Delete the calendar.
885 ServerHeader.clear();
887 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
889 if (ServerConnectionResult == CURLE_OK){
890 ServerResult.Result = CALDAVQUERYRESULT_OK;
892 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
894 ServerResult.Code = ServerConnectionResult;
895 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
897 // Restore the original settings.
899 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
900 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
901 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
902 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
903 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
904 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
905 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
911 CalDAVServerResult CalDAV::GetEntryETag(string *CalendarEntryHREF, string *ETagValue){
913 CalDAVServerResult ServerResult;
914 CalDAVSendData EntryETagGetData;
916 // Build the server address.
918 string UserPrincipalURI = "";
919 UserPrincipalURI = GetUserPrincipal();
921 if (UserPrincipalURI.size() == 0){
927 string CalendarHomeURI = "";
928 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
930 // Split the path and filename.
933 string EntryFilename;
935 SplitPathFilename(CalendarEntryHREF, &EntryURIPath, &EntryFilename);
937 // Build the request for the server.
939 string EntryETagRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
940 "<c:calendar-multiget xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
945 EntryETagRequest += (*CalendarEntryHREF);
946 EntryETagRequest += "</d:href>\n"
947 "</c:calendar-multiget>";
949 EntryETagGetData.readptr = &EntryETagRequest;
950 EntryETagGetData.sizeleft = EntryETagRequest.size();
952 // Build the calendar list address.
954 struct curl_slist *GetETagRequestHeader = NULL;
956 GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Depth: 1");
957 GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Prefer: return-minimal");
958 GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Content-Type: application/xml; charset=utf-8");
960 string GetETagURLAddress = BuildServerAddress(&ConnectionData, EntryURIPath);
962 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, GetETagRequestHeader);
963 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, GetETagURLAddress.c_str());
964 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT");
965 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
966 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &EntryETagGetData);
967 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
969 // Attempt to get the entity tag.
972 ServerHeader.clear();
974 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
976 if (ServerConnectionResult == CURLE_OK){
977 ServerResult.Result = CALDAVQUERYRESULT_OK;
979 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
981 ServerResult.Code = ServerConnectionResult;
982 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
984 if (ServerConnectionResult != CURLE_OK){
988 // Get the entity tag from the result.
990 *ETagValue = ProcessXMLEntryETag();
992 // Restore the original settings.
994 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
995 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
996 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
997 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
998 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
999 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
1000 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1002 return ServerResult;
1006 CalDAVServerResult CalDAV::AddEntry(string *CalendarEntryHREF, string *EntryData){
1008 // Add an entry to the calendar collection.
1010 CalDAVServerResult ServerResult;
1011 CalDAVSendData EntryAddSendData;
1013 // Build the calendar list address.
1015 string EntryAddURLAddress = BuildServerAddress(&ConnectionData, (*CalendarEntryHREF));
1017 EntryAddSendData.readptr = EntryData;
1018 EntryAddSendData.sizeleft = EntryData->size();
1020 struct curl_slist *CalendarRequestHeader = NULL;
1022 CalendarRequestHeader = curl_slist_append(CalendarRequestHeader, "Content-Type: text/calendar; charset=utf-8");
1024 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
1025 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, EntryAddURLAddress.c_str());
1026 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PUT");
1027 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
1028 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &EntryAddSendData);
1029 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1031 // Process the data.
1034 ServerHeader.clear();
1036 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
1038 if (ServerConnectionResult == CURLE_OK){
1039 ServerResult.Result = CALDAVQUERYRESULT_OK;
1041 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
1043 ServerResult.Code = ServerConnectionResult;
1044 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
1046 // Restore the original settings.
1048 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
1049 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
1050 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
1051 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
1052 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
1053 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
1054 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1056 return ServerResult;
1060 CalDAVServerResult CalDAV::DeleteEntry(string *CalendarEntryHREF){
1062 // Delete an entry in the calendar collection.
1064 CalDAVServerResult ServerResult;
1066 // Build the calendar list address.
1068 string EntryDeleteURLAddress = BuildServerAddress(&ConnectionData, (*CalendarEntryHREF));
1070 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1071 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, EntryDeleteURLAddress.c_str());
1072 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
1074 // Delete the calendar.
1077 ServerHeader.clear();
1079 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
1081 if (ServerConnectionResult == CURLE_OK){
1082 ServerResult.Result = CALDAVQUERYRESULT_OK;
1084 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
1086 ServerResult.Code = ServerConnectionResult;
1087 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
1089 // Restore the original settings.
1091 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
1092 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
1093 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
1094 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
1095 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
1096 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
1097 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1099 return ServerResult;
1103 bool CalDAVObjectValidSettings(CalDAVConnectionData *ConnData){
1105 // Check if the passed CalDAV Connection Data is has
1106 // an address set. Return false if nullptr is used.
1108 if (ConnData == nullptr){
1114 // Check the server hostname. Return false
1115 // if no value has been set.
1117 if (ConnData->Hostname.size() == 0){
1123 // Check the server port. Return false if
1124 // no value has been set or the port number
1125 // is less than 1 or higher than 65535.
1127 if (ConnData->Port < 1 || ConnData->Port > 65535){
1133 // Check the server username. Return false
1134 // if no value has been set.
1136 if (ConnData->Username.size() == 0){
1142 // Check the server password. Return false
1143 // if no value has been set.
1145 if (ConnData->Password.size() == 0){
1151 // Cannot check UseSSL: It is either true
1154 // Cannot check Prefix: The prefix may need
1155 // to be worked out first.
1157 // No errors were found whilst checking so
1164 string BuildServerAddress(CalDAVConnectionData *ConnData, string URIAddress){
1166 string ServerAddress;
1168 // Setup the server address.
1170 if (ConnData->UseSSL == true){
1171 ServerAddress += "https://";
1173 ServerAddress += "http://";
1176 ServerAddress += ConnData->Hostname;
1178 // Check if server port is 80, otherwise
1179 // specifiy the port number in the address.
1181 if (ConnData->Port != 80){
1182 ServerAddress += ":";
1183 ServerAddress += to_string(ConnData->Port);
1186 ServerAddress += URIAddress;
1188 return ServerAddress;