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 " <d:sync-token />\n"
480 " <x0:calendar-color />\n"
481 " <x0:calendar-order />\n"
483 " <c:supported-calendar-component-set />\n"
484 " <c:calendar-description />\n"
488 CalendarListSendData.readptr = &CalendarListRequest;
489 CalendarListSendData.sizeleft = CalendarListRequest.size();
493 struct curl_slist *CalendarListRequestHeader = NULL;
495 CalendarListRequestHeader = curl_slist_append(CalendarListRequestHeader, "Depth: 1");
496 CalendarListRequestHeader = curl_slist_append(CalendarListRequestHeader, "Prefer: return-minimal");
497 CalendarListRequestHeader = curl_slist_append(CalendarListRequestHeader, "Content-Type: application/xml; charset=utf-8");
499 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarListRequestHeader);
500 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarListURLAddress.c_str());
501 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
502 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
503 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &CalendarListSendData);
504 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
509 ServerHeader.clear();
511 CURLcode ServerResult = curl_easy_perform(ConnectionHandle);
513 //ServerList = ProcessXMLCalendarList();
515 if (ServerResult == CURLE_OK){
516 ConnectionServerResult.Result = CALDAVQUERYRESULT_OK;
518 ConnectionServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
520 ConnectionServerResult.Code = ServerResult;
521 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ConnectionServerResult.HTTPCode);
523 if (ServerResult != CURLE_OK){
529 // Process the received XML data into a list of calendars
532 ServerList = ProcessXMLCalendarList();
534 // Restore the original settings.
536 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
538 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
539 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
540 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
541 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
542 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
548 CalDAVServerResult CalDAV::AddCalendar(string CalendarName){
550 CalDAVServerResult ServerResult;
552 AddCalendar(&CalendarName, nullptr);
558 CalDAVServerResult CalDAV::AddCalendar(string *CalendarName, string *CalendarShortName){
560 CalDAVServerResult ServerResult;
561 CalDAVSendData CalendarAddSendData;
563 // Build the server address.
565 string UserPrincipalURI = "";
566 UserPrincipalURI = GetUserPrincipal();
568 if (UserPrincipalURI.size() == 0){
574 string CalendarHomeURI = "";
575 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
577 // Generate the UUID.
579 string UUIDValue = "";
581 if (CalendarShortName == nullptr){
583 UUIDValue = GenerateUUID();
584 UUIDValue.erase(UUIDValue.end()-1);
588 UUIDValue = *CalendarShortName;
592 string CalendarHomeURL = CalendarHomeURI;
593 CalendarHomeURL.append(UUIDValue);
594 CalendarHomeURL.append("/");
596 // Build the calendar list address.
598 string CalendarListURLAddress = BuildServerAddress(&ConnectionData, CalendarHomeURL);
600 string CalendarAddRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
601 "<c:mkcalendar xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
605 CalendarAddRequest += *CalendarName;
606 CalendarAddRequest += "</d:displayname>\n"
607 " <c:supported-calendar-component-set>\n"
608 " <c:comp name=\"VTODO\"/>\n"
609 " <c:comp name=\"VEVENT\"/>\n"
610 " </c:supported-calendar-component-set>\n"
615 CalendarAddSendData.readptr = &CalendarAddRequest;
616 CalendarAddSendData.sizeleft = CalendarAddRequest.size();
620 struct curl_slist *CalendarRequestHeader = NULL;
622 //curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
623 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarListURLAddress.c_str());
624 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "MKCALENDAR");
625 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
626 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &CalendarAddSendData);
627 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
632 ServerHeader.clear();
634 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
636 if (ServerConnectionResult == CURLE_OK){
637 ServerResult.Result = CALDAVQUERYRESULT_OK;
639 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
641 ServerResult.Code = ServerConnectionResult;
642 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
644 // Restore the original settings.
646 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
647 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
648 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
649 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
650 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
651 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
657 CalDAVServerResult CalDAV::EditCalendarProcess(string *CalendarHREF,
658 string *CalendarName,
659 Colour *CalendarColour,
660 string *CalendarDescription,
663 CalDAVServerResult ServerResult;
664 CalDAVSendData CalendarEditSendData;
666 // Build the server address.
668 string UserPrincipalURI = "";
669 UserPrincipalURI = GetUserPrincipal();
671 if (UserPrincipalURI.size() == 0){
677 string CalendarHomeURI = "";
678 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
680 // Generate the UUID.
682 string UUIDValue = GenerateUUID();
683 UUIDValue.erase(UUIDValue.end()-1);
685 string CalendarHomeURL = CalendarHomeURI;
686 CalendarHomeURL.append(UUIDValue);
687 CalendarHomeURL.append("/");
689 // Build the calendar list address.
691 string CalendarEditURLAddress = BuildServerAddress(&ConnectionData, (*CalendarHREF));
693 string CalendarEditRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
694 "<d:propertyupdate xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\"\n"
695 " xmlns:x0=\"http://apple.com/ns/ical/\">\n"
699 // Update the calendar name.
701 if (CalendarName != nullptr){
703 CalendarEditRequest += "<d:displayname>";
704 CalendarEditRequest += (*CalendarName);
705 CalendarEditRequest += "</d:displayname>\n";
709 // Update the calendar colour.
711 if (CalendarColour != nullptr){
713 CalendarEditRequest += "<x0:calendar-color>";
714 CalendarEditRequest += (*CalendarColour);
715 CalendarEditRequest += "</x0:calendar-color>\n";
719 // Update the calendar description.
721 if (CalendarDescription != nullptr){
723 CalendarEditRequest += "<c:calendar-description>";
724 CalendarEditRequest += (*CalendarDescription);
725 CalendarEditRequest += "</c:calendar-description>\n";
729 // Update the calendar order.
731 if (CalendarOrder != nullptr){
733 CalendarEditRequest += "<x0:calendar-order>";
734 CalendarEditRequest += to_string((*CalendarOrder));
735 CalendarEditRequest += "</x0:calendar-order>\n";
739 CalendarEditRequest += " </d:prop>\n"
741 "</d:propertyupdate>";
743 CalendarEditSendData.readptr = &CalendarEditRequest;
744 CalendarEditSendData.sizeleft = CalendarEditRequest.size();
748 struct curl_slist *CalendarRequestHeader = NULL;
750 //curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
751 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarEditURLAddress.c_str());
752 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
753 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
754 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &CalendarEditSendData);
755 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
760 ServerHeader.clear();
762 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
764 if (ServerConnectionResult == CURLE_OK){
765 ServerResult.Result = CALDAVQUERYRESULT_OK;
767 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
769 ServerResult.Code = ServerConnectionResult;
770 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
772 // Restore the original settings.
774 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
775 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
776 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
777 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
778 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
779 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
785 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
786 string *CalendarName,
787 Colour *CalendarColour,
788 string *CalendarDescription,
791 CalDAVServerResult ServerResult;
793 ServerResult = EditCalendarProcess(CalendarHREF,
803 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
804 Colour *CalendarColour){
807 CalDAVServerResult ServerResult;
809 ServerResult = EditCalendarProcess(CalendarHREF,
819 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
820 string *CalendarName){
822 CalDAVServerResult ServerResult;
824 ServerResult = EditCalendarProcess(CalendarHREF,
834 CalDAVServerResult CalDAV::EditCalendar(string *CalendarHREF,
837 CalDAVServerResult ServerResult;
839 ServerResult = EditCalendarProcess(CalendarHREF,
849 CalDAVServerResult CalDAV::EditCalendarDescription(string *CalendarHREF,
850 string *CalendarDescription){
852 CalDAVServerResult ServerResult;
854 ServerResult = EditCalendarProcess(CalendarHREF,
864 CalDAVServerResult CalDAV::DeleteCalendar(string *CalendarHREF){
866 CalDAVServerResult ServerResult;
868 // Build the server address.
870 string UserPrincipalURI = "";
871 UserPrincipalURI = GetUserPrincipal();
873 if (UserPrincipalURI.size() == 0){
879 string CalendarHomeURI = "";
880 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
882 // Generate the UUID.
884 string UUIDValue = GenerateUUID();
885 UUIDValue.erase(UUIDValue.end()-1);
887 string CalendarHomeURL = CalendarHomeURI;
888 CalendarHomeURL.append(UUIDValue);
889 CalendarHomeURL.append("/");
891 // Build the calendar list address.
893 struct curl_slist *DeleteRequestHeader = NULL;
895 DeleteRequestHeader = curl_slist_append(DeleteRequestHeader, "Depth: infinity");
897 string CalendarDeleteURLAddress = BuildServerAddress(&ConnectionData, (*CalendarHREF));
899 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, DeleteRequestHeader);
900 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, CalendarDeleteURLAddress.c_str());
901 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
903 // Delete the calendar.
906 ServerHeader.clear();
908 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
910 if (ServerConnectionResult == CURLE_OK){
911 ServerResult.Result = CALDAVQUERYRESULT_OK;
913 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
915 ServerResult.Code = ServerConnectionResult;
916 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
918 // Restore the original settings.
920 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
921 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
922 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
923 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
924 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
925 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
926 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
932 CalDAVServerResult CalDAV::GetEntryETag(string *CalendarEntryHREF, string *ETagValue){
934 CalDAVServerResult ServerResult;
935 CalDAVSendData EntryETagGetData;
937 // Build the server address.
939 string UserPrincipalURI = "";
940 UserPrincipalURI = GetUserPrincipal();
942 if (UserPrincipalURI.size() == 0){
948 string CalendarHomeURI = "";
949 CalendarHomeURI = GetCalendarHome(UserPrincipalURI);
951 // Split the path and filename.
954 string EntryFilename;
956 SplitPathFilename(CalendarEntryHREF, &EntryURIPath, &EntryFilename);
958 // Build the request for the server.
960 string EntryETagRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
961 "<c:calendar-multiget xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
966 EntryETagRequest += (*CalendarEntryHREF);
967 EntryETagRequest += "</d:href>\n"
968 "</c:calendar-multiget>";
970 EntryETagGetData.readptr = &EntryETagRequest;
971 EntryETagGetData.sizeleft = EntryETagRequest.size();
973 // Build the calendar list address.
975 struct curl_slist *GetETagRequestHeader = NULL;
977 GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Depth: 1");
978 GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Prefer: return-minimal");
979 GetETagRequestHeader = curl_slist_append(GetETagRequestHeader, "Content-Type: application/xml; charset=utf-8");
981 string GetETagURLAddress = BuildServerAddress(&ConnectionData, EntryURIPath);
983 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, GetETagRequestHeader);
984 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, GetETagURLAddress.c_str());
985 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT");
986 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
987 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &EntryETagGetData);
988 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
990 // Attempt to get the entity tag.
993 ServerHeader.clear();
995 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
997 if (ServerConnectionResult == CURLE_OK){
998 ServerResult.Result = CALDAVQUERYRESULT_OK;
1000 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
1002 ServerResult.Code = ServerConnectionResult;
1003 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
1005 if (ServerConnectionResult != CURLE_OK){
1006 return ServerResult;
1009 // Get the entity tag from the result.
1011 *ETagValue = ProcessXMLEntryETag();
1013 // Restore the original settings.
1015 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
1016 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
1017 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
1018 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
1019 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
1020 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
1021 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1023 return ServerResult;
1027 CalDAVServerResult CalDAV::AddEntry(string *CalendarEntryHREF, string *EntryData){
1029 // Add an entry to the calendar collection.
1031 CalDAVServerResult ServerResult;
1032 CalDAVSendData EntryAddSendData;
1034 // Build the calendar list address.
1036 string EntryAddURLAddress = BuildServerAddress(&ConnectionData, (*CalendarEntryHREF));
1038 EntryAddSendData.readptr = EntryData;
1039 EntryAddSendData.sizeleft = EntryData->size();
1041 struct curl_slist *CalendarRequestHeader = NULL;
1043 CalendarRequestHeader = curl_slist_append(CalendarRequestHeader, "Content-Type: text/calendar; charset=utf-8");
1045 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
1046 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, EntryAddURLAddress.c_str());
1047 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PUT");
1048 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
1049 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &EntryAddSendData);
1050 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1052 // Process the data.
1055 ServerHeader.clear();
1057 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
1059 if (ServerConnectionResult == CURLE_OK){
1060 ServerResult.Result = CALDAVQUERYRESULT_OK;
1062 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
1064 ServerResult.Code = ServerConnectionResult;
1065 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
1067 // Restore the original settings.
1069 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
1070 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
1071 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
1072 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
1073 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
1074 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
1075 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1077 return ServerResult;
1081 CalDAVServerResult CalDAV::EditEntry(string *CalendarEntryHREF, string *EntryData, string *EntryETag){
1083 // Edit an entry in the calendar collection.
1085 // Add an entry to the calendar collection.
1087 CalDAVServerResult ServerResult;
1088 CalDAVSendData EntryAddSendData;
1090 // Build the calendar list address.
1092 string EntryAddURLAddress = BuildServerAddress(&ConnectionData, (*CalendarEntryHREF));
1094 EntryAddSendData.readptr = EntryData;
1095 EntryAddSendData.sizeleft = EntryData->size();
1097 string IfMatchHeader = "If-Match: \"";
1098 IfMatchHeader.append(*EntryETag);
1099 IfMatchHeader.append("\"");
1101 struct curl_slist *CalendarRequestHeader = NULL;
1103 CalendarRequestHeader = curl_slist_append(CalendarRequestHeader, "Content-Type: text/calendar; charset=utf-8");
1104 CalendarRequestHeader = curl_slist_append(CalendarRequestHeader, IfMatchHeader.c_str());
1106 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
1107 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, EntryAddURLAddress.c_str());
1108 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "PUT");
1109 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 1L);
1110 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, &EntryAddSendData);
1111 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1113 // Process the data.
1116 ServerHeader.clear();
1118 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
1120 if (ServerConnectionResult == CURLE_OK){
1121 ServerResult.Result = CALDAVQUERYRESULT_OK;
1123 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
1125 ServerResult.Code = ServerConnectionResult;
1126 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
1128 // Restore the original settings.
1130 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
1131 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
1132 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
1133 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
1134 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
1135 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
1136 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1138 return ServerResult;
1142 CalDAVServerResult CalDAV::DeleteEntry(string *CalendarEntryHREF){
1144 // Delete an entry in the calendar collection.
1146 CalDAVServerResult ServerResult;
1148 // Build the calendar list address.
1150 string EntryDeleteURLAddress = BuildServerAddress(&ConnectionData, (*CalendarEntryHREF));
1152 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1153 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, EntryDeleteURLAddress.c_str());
1154 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
1156 // Delete the calendar.
1159 ServerHeader.clear();
1161 CURLcode ServerConnectionResult = curl_easy_perform(ConnectionHandle);
1163 if (ServerConnectionResult == CURLE_OK){
1164 ServerResult.Result = CALDAVQUERYRESULT_OK;
1166 ServerResult.Result = CALDAVQUERYRESULT_SERVERERROR;
1168 ServerResult.Code = ServerConnectionResult;
1169 curl_easy_getinfo(ConnectionHandle, CURLINFO_RESPONSE_CODE, &ServerResult.HTTPCode);
1171 // Restore the original settings.
1173 string OriginalServerAddress = BuildServerAddress(&ConnectionData, "/principals/");
1174 curl_easy_setopt(ConnectionHandle, CURLOPT_URL, OriginalServerAddress.c_str());
1175 curl_easy_setopt(ConnectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
1176 curl_easy_setopt(ConnectionHandle, CURLOPT_UPLOAD, 0L);
1177 curl_easy_setopt(ConnectionHandle, CURLOPT_READDATA, NULL);
1178 curl_easy_setopt(ConnectionHandle, CURLOPT_READFUNCTION, NULL);
1179 curl_easy_setopt(ConnectionHandle, CURLOPT_HTTPHEADER, NULL);
1181 return ServerResult;
1185 bool CalDAVObjectValidSettings(CalDAVConnectionData *ConnData){
1187 // Check if the passed CalDAV Connection Data is has
1188 // an address set. Return false if nullptr is used.
1190 if (ConnData == nullptr){
1196 // Check the server hostname. Return false
1197 // if no value has been set.
1199 if (ConnData->Hostname.size() == 0){
1205 // Check the server port. Return false if
1206 // no value has been set or the port number
1207 // is less than 1 or higher than 65535.
1209 if (ConnData->Port < 1 || ConnData->Port > 65535){
1215 // Check the server username. Return false
1216 // if no value has been set.
1218 if (ConnData->Username.size() == 0){
1224 // Check the server password. Return false
1225 // if no value has been set.
1227 if (ConnData->Password.size() == 0){
1233 // Cannot check UseSSL: It is either true
1236 // Cannot check Prefix: The prefix may need
1237 // to be worked out first.
1239 // No errors were found whilst checking so
1246 string BuildServerAddress(CalDAVConnectionData *ConnData, string URIAddress){
1248 string ServerAddress;
1250 // Setup the server address.
1252 if (ConnData->UseSSL == true){
1253 ServerAddress += "https://";
1255 ServerAddress += "http://";
1258 ServerAddress += ConnData->Hostname;
1260 // Check if server port is 80, otherwise
1261 // specifiy the port number in the address.
1263 if (ConnData->Port != 80){
1264 ServerAddress += ":";
1265 ServerAddress += to_string(ConnData->Port);
1268 ServerAddress += URIAddress;
1270 return ServerAddress;