Home | News | Projects | Releases
Bugs | RFE | Repositories | Help
48fdb7480d870fee74dbd9ab86aba53ce928f20c
[xestiacalendar/.git] / source / objects / CalDAV / CalDAV.cpp
1 // CalDAV.cpp - CalDAV Connection Object.
2 //
3 // (c) 2016-2017 Xestia Software Development.
4 //
5 // This file is part of Xestia Calendar.
6 //
7 // Xestia Calendar 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.
10 //
11 // Xestia Calendar 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.
15 //
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/>
19 #include "CalDAV.h"
21 using namespace std;
23 size_t CalDAV::CalDAVReceive(char *receivedBuffer, size_t size, size_t newMemoryBytes, void *stream)
24 {
25         
26         // Writeback function for the CardDAV object.
28         CalDAVPassObject *data = static_cast<CalDAVPassObject*>(stream);
29         data->DataSetting->append(receivedBuffer);
31         // Get the SSL engine pointer and trust if required on certain operating systems.
33         if (data->ServerUsingSSL == true) {
35 #if defined(__APPLE__)
37                 const struct curl_tlssessioninfo *TLSInfo;
38                 CURLcode TLSCode;
39                 TLSCode = curl_easy_getinfo(data->ConnectionSessionObject, CURLINFO_TLS_SSL_PTR, &TLSInfo);
41                 SecTrustRef CertificateData;
43                 if (TLSInfo->internals != nullptr && TLSCode == CURLE_OK) {
44                         SSLCopyPeerTrust((SSLContext*)TLSInfo->internals, &CertificateData);
45                         data->SSLContext = CertificateData;
46                 }
48 #elif defined(__WIN32__)
50                 const struct curl_tlssessioninfo *TLSInfo;
51                 CURLcode TLSCode;
52                 TLSCode = curl_easy_getinfo(data->ConnectionSessionObject, CURLINFO_TLS_SSL_PTR, &TLSInfo);
54                 if (TLSInfo->internals != nullptr && TLSCode == CURLE_OK) {
56                         // Free the previous certificate data.
58                         //CertFreeCertificateContext(CertificateData);
60                         PCCERT_CONTEXT CertificateData;
62                         PCtxtHandle SSLHandle = (PCtxtHandle)TLSInfo->internals;
63                         SECURITY_STATUS GetData = QueryContextAttributes(SSLHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &CertificateData);
65                         data->SSLContext = CertificateData;
67                 }
69 #endif
71         }
73         return size * newMemoryBytes;
74         
75 }
77 size_t CalDAVSend(char *sendBuffer, size_t size, size_t newMemoryBytes, void *dataStruct){
79         struct CalDAVSendData *uploadPtr = (struct CalDAVSendData *)dataStruct;
80         
81         if (uploadPtr->sizeleft){
83                 uploadPtr->sizeleft--;
84                 char charSend;
86                 charSend = (*uploadPtr->readptr)[uploadPtr->seek];
87                 
88                 *sendBuffer = charSend;
89                 
90                 uploadPtr->seek++;
92                 return 1;
94         }
96         return 0;
98 }
100 CalDAV::CalDAV(){
102         // Setup the objects within the CalDAV connection 
103         // object.
104         
105         connectionHandle = curl_easy_init();
106         
109 CalDAV::~CalDAV(){
110         
111         // Destory the objects within the CalDAV connection
112         // object.
113         
114         curl_easy_cleanup(connectionHandle);
115         connectionHandle = nullptr;
116         
119 void CalDAV::SetupConnectionData(CalDAVConnectionData *connData){
120         
121         // Check if ConnData is a nullptr, return if it is.
122         
123         if (connData == nullptr){
124                 return;
125         }
126         
127         // Set the connection settings to the values from ConnData.
129         connectionData = (*connData);
130         
133 CalDAVStatus CalDAV::GetConnectionData(){
134         
135         // Get the current connection settings for the CalDAV
136         // connection object and return a CalDAVStatus object.
137         
138         CalDAVStatus connectionStatus;
139         
140         connectionStatus.hostname = connectionData.hostname;
141         connectionStatus.port = connectionData.port;
142         connectionStatus.username = connectionData.username;
143         connectionStatus.prefix = connectionData.prefix;
144         connectionStatus.useSSL = connectionData.useSSL;
145         connectionStatus.timeout = connectionData.timeout;
146         
147         return connectionStatus;
148         
151 CalDAVServerResult CalDAV::Connect(bool doAuthentication){
153         connectionData.useSSL ? SetupDefaultParametersSSL(doAuthentication) : SetupDefaultParametersNonSSL(doAuthentication);
154         ResetResults();
156         CalDAVServerResult serverResult;
158         string serverAddress = "";
159         string serverUserPass = "";
161         // Setup the server address.
162         
163         serverAddress = BuildServerAddress(&connectionData, "/principals/");
165         // Setup the server password.
166         
167         serverUserPass += connectionData.username;
168         serverUserPass += ":";
169         serverUserPass += connectionData.password;
171         PageDataObject.CalDAVObject = this;
172         PageDataObject.ConnectionSessionObject = connectionHandle;
173         PageDataObject.DataSetting = &serverData;
174         PageDataObject.ServerUsingSSL = true;
176         PageHeaderObject.CalDAVObject = this;
177         PageHeaderObject.ConnectionSessionObject = connectionHandle;
178         PageHeaderObject.DataSetting = &serverHeader;
179         PageHeaderObject.ServerUsingSSL = true;
181         curl_easy_setopt(connectionHandle, CURLOPT_URL, serverAddress.c_str());
182         curl_easy_setopt(connectionHandle, CURLOPT_USERPWD, serverUserPass.c_str());
183         curl_easy_setopt(connectionHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
184         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
185         curl_easy_setopt(connectionHandle, CURLOPT_FAILONERROR, 1L);
186         curl_easy_setopt(connectionHandle, CURLOPT_TIMEOUT, connectionData.timeout);
187         curl_easy_setopt(connectionHandle, CURLOPT_WRITEFUNCTION, CalDAV::CalDAVReceive);
188         curl_easy_setopt(connectionHandle, CURLOPT_WRITEDATA, &PageDataObject);
189         curl_easy_setopt(connectionHandle, CURLOPT_WRITEHEADER, &PageHeaderObject);
190         
191         // Connect to the CalDAV server.
192         
193         serverResult.code = curl_easy_perform(connectionHandle);
195         // Process the result received from the server.
197         // Get the HTTP code.
198         
199         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
201         switch(serverResult.code){
202                 case CURLE_OK:
203                         authPassed = true;
204                 case CURLE_HTTP_RETURNED_ERROR:
205                         if (connectionData.useSSL)
206                         {
207                                 sslStatus = connectionData.useSSL;
208                                 sslVerified = COSSL_VERIFIED;
209                         }
210                         serverResult.result = CALDAVQUERYRESULT_OK;
211                         if (serverResult.httpCode == 401)
212                         {
213                                 authPassed = false;
214                         }
215                         break;
216                 case CURLE_SSL_CACERT:
217                 case CURLE_SSL_CONNECT_ERROR:
218                         if (connectionData.useSSL)
219                         {
220                                 sslStatus = connectionData.useSSL;
221                                 sslVerified = COSSL_UNABLETOVERIFY;
222                         }
223                         serverResult.result = CALDAVQUERYRESULT_SSLFAILURE;
224                         authPassed = false;
225                         break;
226                 default:
227                         serverResult.result = CALDAVQUERYRESULT_SERVERERROR;
228                         authPassed = false;
229                         break;
230         };
232         // Set the certificate data (if required).
234 #if defined(__APPLE__)
236         if (connectionData.useSSL) {
238                 certificateData = PageHeaderObject.SSLContext;
240         }
242 #elif defined(__WIN32__)
244         if (connectionData.useSSL) {
246                 certificateData = PageHeaderObject.SSLContext;
248         }
250 #endif
252         // Check if a valid response was received before continuing.
254         if (serverResult.httpCode >= 200 && serverResult.httpCode <= 299)
255         {
256                 validResponse = true;
257         }
258         
259         return serverResult;
260         
263 CalDAVServerResult CalDAV::GetServerResult(){
264         
265         return connectionServerResult;
266         
269 CalDAVServerSupport CalDAV::GetServerSupport(){
271         CalDAVServerSupport serverStatus;
272         
273         // Setup the server connection.
274         
275         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "OPTIONS");
276         
277         CURLcode serverResult = curl_easy_perform(connectionHandle);
278         
279         // Set the results.
280         
281         if (serverResult == CURLE_OK){
282                 connectionServerResult.result = CALDAVQUERYRESULT_OK;
283         } else {
284                 connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR;          
285         }
286         connectionServerResult.code = serverResult;
287         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode);
288         
289         if (serverResult != CURLE_OK){
290                 return serverStatus;
291         }
292         
293         // Check that the server header has data in,
294         // otherwise return an "empty" CalDAVServerSupport.
295         
296         if (serverHeader.size() == 0){
297                 return serverStatus;
298         }
299         
300         // Process each line looking for the first DAV header 
301         // line.
302         
303         bool newlineMode = true;
304         
305         string davLine;
306         
307         for (int charSeek = 0; charSeek < serverHeader.size(); charSeek++){
308                 
309                 if (newlineMode == true){
310                         
311                         // Check if we have reached the end of the string.
312                         
313                         if (charSeek >= serverHeader.size()){
314                                 
315                                 break;
316                                 
317                         }
318                         
319                         // Check the first four letters to make sure
320                         // they are 'DAV:'.
321                         
322                         string davHeaderCheck = "";
323                         
324                         try {
325                                 davHeaderCheck = serverHeader.substr(charSeek, 4);
326                         }
327                         
328                         catch (out_of_range &oor){
329                                 break;
330                         }
331                         
332                         if (davHeaderCheck == "DAV:"){
333                                 
334                                 charSeek += 5;
335                                 
336                                 for (; charSeek < serverHeader.size(); charSeek++){
337                                         
338                                         if (serverHeader[charSeek] == '\n'){
339                                         
340                                                 break;
341                                                 
342                                         }
343                                         
344                                         davLine.push_back(serverHeader[charSeek]);
345                                         
346                                 }
347                                 
348                                 break;
349                                 
350                         }
351                         
352                         newlineMode = false;
353                         
354                 }
355                 
356                 if (serverHeader[charSeek] == '\n'){
357                         
358                         newlineMode = true;
359                         
360                 }
361                 
362         }
363         
364         // Process the DAV line.
365         
366         vector<string> davLineData;
367         string davSegmentString;
368         
369         for (int charSeek = 0; charSeek < davLine.size(); charSeek++){
370                 
371                 if (davLine[charSeek] == ' '){
372                         continue;
373                 }
374                 
375                 if (davLine[charSeek] == ','){
376                         
377                         davLineData.push_back(davSegmentString);
378                         davSegmentString.clear();
379                         continue;
380                         
381                 }
382                 
383                 davSegmentString += davLine[charSeek];
384                 
385         }
386         
387         // Process the DAV values and set each value
388         // to true as required.
389         
390         for (int davItemSeek = 0; 
391                 davItemSeek < davLineData.size();
392                 davItemSeek++){
393                         
394                 if (davLineData.at(davItemSeek) == "calendar-access"){
395                         
396                         serverStatus.basicSupport = true;
397                 
398                 }
399                         
400         }
401         
402         // Reset the connection status.
403         
404         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);
405         
406         return serverStatus;
407         
410 string CalDAV::GetUserPrincipal(){
411         
412         string currentUserPrincipal = "";
413         string userPrincipalRequest = "";
414         CalDAVSendData userPrincipalSendData;
415         
416         userPrincipalRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
417         "<d:propfind xmlns:d=\"DAV:\">\n"
418         " <d:prop>\n"
419         "  <d:current-user-principal />\n"
420         " </d:prop>\n"
421         "</d:propfind>";
422         
423         userPrincipalSendData.readptr = &userPrincipalRequest;
424         userPrincipalSendData.sizeleft = userPrincipalRequest.size();
425         
426         // Setup the header.
427         
428         struct curl_slist *userPrincipalRequestHeader = NULL;
429         
430         userPrincipalRequestHeader = curl_slist_append(userPrincipalRequestHeader, "Depth: 0");
431         userPrincipalRequestHeader = curl_slist_append(userPrincipalRequestHeader, "Prefer: return-minimal");
432         userPrincipalRequestHeader = curl_slist_append(userPrincipalRequestHeader, "Content-Type: application/xml; charset=utf-8");
433         
434         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, userPrincipalRequestHeader);
436         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
437         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
438         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
439         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &userPrincipalSendData);
440         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
441         
442         // Process the data.
443         
444         serverData.clear();
445         serverHeader.clear();
446         
447         CURLcode serverResult = curl_easy_perform(connectionHandle);
448         
449         // Set the results.
450         
451         if (serverResult == CURLE_OK){
452                 connectionServerResult.result = CALDAVQUERYRESULT_OK;
453         } else {
454                 connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR;          
455         }
456         connectionServerResult.code = serverResult;
457         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode);
458         
459         if (serverResult != CURLE_OK){
460                 
461                 return currentUserPrincipal;
462                 
463         }
464         
465         // Process the User Principal from the ServerData.
467         currentUserPrincipal = ProcessXMLUserPrincipal();
468         
469         // Reset the changed settings.
470         
471         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
472         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
473         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
474         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
476         return currentUserPrincipal;
480 string CalDAV::GetCalendarHome(string userPrincipalURI){
481         
482         string calendarHomeURI = "";
484         // Build the Calendar Home URL address.
485         
486         string calendarHomeURL = BuildServerAddress(&connectionData, userPrincipalURI);
487         
488         // Setup the header request.
489         
490         CalDAVSendData calendarHomeSendData;
491         
492         string calendarHomeRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
493         "<d:propfind xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
494         " <d:prop>\n"
495         "  <c:calendar-home-set />\n"
496         " </d:prop>\n"
497         "</d:propfind>";
498         
499         calendarHomeSendData.readptr = &calendarHomeRequest;
500         calendarHomeSendData.sizeleft = calendarHomeRequest.size();
501         
502         // Setup the header.
503         
504         struct curl_slist *calendarRequestHeader = NULL;
505         
506         calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Depth: 0");
507         calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Prefer: return-minimal");
508         calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Content-Type: application/xml; charset=utf-8");
509         
510         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarRequestHeader);
511         curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarHomeURL.c_str());
512         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
513         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
514         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
515         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarHomeSendData);
516         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
517         
518         // Process the data.
519         
520         serverData.clear();
521         serverHeader.clear();
522         
523         CURLcode serverResult = curl_easy_perform(connectionHandle);
524         
525         // Set the results.
526         
527         if (serverResult == CURLE_OK){
528                 connectionServerResult.result = CALDAVQUERYRESULT_OK;
529         } else {
530                 connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR;          
531         }
532         connectionServerResult.code = serverResult;
533         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode);
534         
535         if (serverResult != CURLE_OK){
536                 
537                 return calendarHomeURI;
538                 
539         }
540         
541         // Process the User Principal from the ServerData.
543         calendarHomeURI = ProcessXMLCalendarHome();
544         
545         // Reset the changed settings.
546         
547         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
548         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
549         
550         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
551         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
552         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
553         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
554         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL);
555         
556         return calendarHomeURI;
557         
560 CalDAVCalendarList CalDAV::GetCalendars(){
561         
562         CalDAVCalendarList serverList;
563         CalDAVSendData calendarListSendData;
564         
565         // Build the server address.
566         
567         string userPrincipalURI = "";
568         userPrincipalURI = GetUserPrincipal();
569         
570         if (userPrincipalURI.size() == 0){
571                 
572                 return serverList;
573                 
574         }
575         
576         string calendarHomeURI = "";
577         calendarHomeURI = GetCalendarHome(userPrincipalURI);
578         
579         string calendarListURLAddress = BuildServerAddress(&connectionData, calendarHomeURI);
580         
581         string calendarListRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
582         "<d:propfind xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\""
583         " xmlns:c=\"urn:ietf:params:xml:ns:caldav\" xmlns:x0=\"http://apple.com/ns/ical/\">\n"
584         " <d:prop>\n"
585         "  <d:resourcetype />\n"
586         "  <d:displayname />\n"
587         "  <d:sync-token />\n"
588         "  <x0:calendar-color />\n"
589         "  <x0:calendar-order />\n"
590         "  <cs:getctag />\n"
591         "  <c:supported-calendar-component-set />\n"
592         "  <c:calendar-description />\n"
593         " </d:prop>\n"
594         "</d:propfind>";
595         
596         calendarListSendData.readptr = &calendarListRequest;
597         calendarListSendData.sizeleft = calendarListRequest.size();
598         
599         // Setup the header.
600         
601         struct curl_slist *calendarListRequestHeader = NULL;
602         
603         calendarListRequestHeader = curl_slist_append(calendarListRequestHeader, "Depth: 1");
604         calendarListRequestHeader = curl_slist_append(calendarListRequestHeader, "Prefer: return-minimal");
605         calendarListRequestHeader = curl_slist_append(calendarListRequestHeader, "Content-Type: application/xml; charset=utf-8");
606         
607         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarListRequestHeader);
608         curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarListURLAddress.c_str());
609         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
610         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
611         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
612         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarListSendData);
613         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
614         
615         // Process the data.
616         
617         serverData.clear();
618         serverHeader.clear();
619         
620         CURLcode serverResult = curl_easy_perform(connectionHandle);
621         
622         //ServerList = ProcessXMLCalendarList();
623         
624         if (serverResult == CURLE_OK){
625                 connectionServerResult.result = CALDAVQUERYRESULT_OK;
626         } else {
627                 connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR;          
628         }
629         connectionServerResult.code = serverResult;
630         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode);
631         
632         if (serverResult != CURLE_OK){
633                 
634                 return serverList;
635                 
636         }
637         
638         // Process the received XML data into a list of calendars
639         // and locations.
640         
641         serverList = ProcessXMLCalendarList();
642         
643         // Restore the original settings.
644         
645         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
646         
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_ERRORBUFFER, sessionErrorBuffer);
651         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
652         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
653         
654         return serverList;
655         
658 CalDAVEntryList CalDAV::GetEntryList(string *calendarHREF){
659         
660         CalDAVEntryList entryList;
661         CalDAVSendData entryListSendData;
662         
663         if (calendarHREF->size() == 0){
664                 
665                 return entryList;
666                 
667         }
668         
669         string entryListURLAddress = BuildServerAddress(&connectionData, *calendarHREF);
670         
671         string entryListRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
672         
673         /*if (CalendarTag == nullptr){*/
674                 
675         entryListRequest += "<c:calendar-query xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\""
676         " xmlns:c=\"urn:ietf:params:xml:ns:caldav\" xmlns:x0=\"http://apple.com/ns/ical/\">\n"
677         " <d:prop>\n"
678         "  <d:getetag />\n"
679         "  <c:calendar-data />\n"
680         " </d:prop>\n"
681         " <c:filter>\n"
682         "  <c:comp-filter name=\"VCALENDAR\" />\n"
683         " </c:filter>\n"
684         "</c:calendar-query>";
685                 
686         /*} else {
688                 EntryListRequest += "<d:sync-collection xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\""
689                 " xmlns:c=\"urn:ietf:params:xml:ns:caldav\" xmlns:x0=\"http://apple.com/ns/ical/\">\n"          
690                 " <d:sync-token>";
691                 EntryListRequest += *CalendarTag;
692                 EntryListRequest += "</d:sync-token>\n"
693                 " <d:sync-level>1</d:sync-level>\n"
694                 " <d:prop>\n"
695                 "  <d:getetag />\n"
696                 "  <c:calendar-data />\n"
697                 " </d:prop>\n"
698                 "</d:sync-collection>";
699                 
700         }*/
701         
702         entryListSendData.readptr = &entryListRequest;
703         entryListSendData.sizeleft = entryListRequest.size();
704         
705         struct curl_slist *entryListRequestHeader = NULL;
706         
707         entryListRequestHeader = curl_slist_append(entryListRequestHeader, "Content-Type: application/xml; charset=utf-8");
708         
709         /*if (CalendarTag != nullptr){
710         
711                 EntryListRequestHeader = curl_slist_append(EntryListRequestHeader, "Content-Type: application/xml; charset=utf-8");
712                 EntryListRequestHeader = curl_slist_append(EntryListRequestHeader, "Content-Type: application/xml; charset=utf-8");
713                 
714         }*/
715         
716         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, entryListRequestHeader);
717         curl_easy_setopt(connectionHandle, CURLOPT_URL, entryListURLAddress.c_str());
718         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT");
719         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
720         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
721         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryListSendData);
722         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
723         
724         // Process the data.
725         
726         serverData.clear();
727         serverHeader.clear();
728         
729         CURLcode serverResult = curl_easy_perform(connectionHandle);
730         
731         //ServerList = ProcessXMLCalendarList();
732         
733         if (serverResult == CURLE_OK){
734                 connectionServerResult.result = CALDAVQUERYRESULT_OK;
735         } else {
736                 connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR;          
737         }
738         connectionServerResult.code = serverResult;
739         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode);
740         
741         if (serverResult != CURLE_OK){
742                 
743                 return entryList;
744                 
745         }
746         
747         // Process the received XML data into a list of calendars
748         // and locations.
749         
750         entryList = ProcessXMLEntryList();
751         
752         // Restore the original settings.
753         
754         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
755         
756         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
757         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
758         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
759         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
760         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
761         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
762         
763         return entryList;
764         
767 CalDAVEntryList CalDAV::GetEntryList(string *calendarHREF, string *calendarTag){
768         
769         CalDAVEntryList entryList;
770         CalDAVSendData entryListSendData;
771         
772         if (calendarHREF->size() == 0){
773                 
774                 return entryList;
775                 
776         }
777         
778         string entryListURLAddress = BuildServerAddress(&connectionData, *calendarHREF);
779         
780         // First query: Get the list of contacts that need to be updated.
781         
782         string entryListRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
784         entryListRequest += "<d:sync-collection xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\""
785         " xmlns:c=\"urn:ietf:params:xml:ns:caldav\" xmlns:x0=\"http://apple.com/ns/ical/\">\n"
786         " <d:sync-token>";
787         
788         if (calendarTag != nullptr){
789         
790                 entryListRequest += *calendarTag;
791                 
792         } else {
793         
794                 entryListRequest += "";
795                 
796         }
798         entryListRequest += "</d:sync-token>\n"
799         " <d:sync-level>1</d:sync-level>\n"
800         " <d:prop>\n"
801         "  <d:getetag />\n"
802         " </d:prop>\n"
803         "</d:sync-collection>";
804         
805         entryListSendData.readptr = &entryListRequest;
806         entryListSendData.sizeleft = entryListRequest.size();
807         
808         struct curl_slist *entryListRequestHeader = NULL;
809         
810         entryListRequestHeader = curl_slist_append(entryListRequestHeader, "Content-Type: application/xml; charset=utf-8");
811         
812         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, entryListRequestHeader);
813         curl_easy_setopt(connectionHandle, CURLOPT_URL, entryListURLAddress.c_str());
814         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT");
815         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
816         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
817         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryListSendData);
818         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
819         
820         // Process the data.
821         
822         serverData.clear();
823         serverHeader.clear();
824         
825         CURLcode serverResult = curl_easy_perform(connectionHandle);
826         
827         if (serverResult == CURLE_OK){
828                 connectionServerResult.result = CALDAVQUERYRESULT_OK;
829         } else {
830                 connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR;          
831         }
832         connectionServerResult.code = serverResult;
833         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode);
834         
835         if (serverResult != CURLE_OK){
836                 
837                 return entryList;
838                 
839         }
840         
841         entryList = ProcessXMLSyncTokenList();
842         
843         // Check the last entry matches the HREF and if it 
844         // does then delete it.
845         
846         if (entryList.href.size() > 0) {
847         
848                 if (entryList.href.rbegin()->second == *calendarHREF){
849                         
850                         entryList.href.erase((entryList.href.size() - 1));
851                         entryList.tag.erase((entryList.href.size() - 1));
852                         entryList.data.erase((entryList.href.size() - 1));
853                 
854                 }
855         
856         }
857         
858         // Build the list into a new list for getting the new 
859         // calendar data with.
860         
861         entryListRequest.clear();
862         
863         entryListRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
865         entryListRequest += "<c:calendar-multiget xmlns:d=\"DAV:\" "
866         " xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
867         " <d:prop>\n"
868         "  <d:getetag />\n"
869         "  <c:calendar-data />\n"
870         " </d:prop>\n";
871         
872         for (std::map<int,string>::iterator hrefIter = entryList.href.begin(); 
873                 hrefIter != entryList.href.end(); hrefIter++){
874                 
875                 string entryListHREFString = hrefIter->second;
876                         
877                 entryListRequest += " <d:href>";
878                 entryListRequest += entryListHREFString;
879                 entryListRequest += "</d:href>\n";
880                         
881         }
882         
883         entryListRequest += "</c:calendar-multiget>";
884         
885         CalDAVSendData updatedEntryListSendData;        
886         
887         updatedEntryListSendData.readptr = &entryListRequest;
888         updatedEntryListSendData.sizeleft = entryListRequest.size();
889         
890         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, entryListRequestHeader);
891         curl_easy_setopt(connectionHandle, CURLOPT_URL, entryListURLAddress.c_str());
892         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT");
893         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
894         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
895         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &updatedEntryListSendData);
896         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
897         
898         // Get the updated calendar data.
899         
900         serverData.clear();
901         serverHeader.clear();
902         entryList.href.clear();
903         entryList.tag.clear();
904         entryList.data.clear();
905         
906         serverResult = curl_easy_perform(connectionHandle);
908         // Check the last entry matches the HREF and if it 
909         // does then delete it.
910         
911         if (serverResult == CURLE_OK){
912                 connectionServerResult.result = CALDAVQUERYRESULT_OK;
913         } else {
914                 connectionServerResult.result = CALDAVQUERYRESULT_SERVERERROR;          
915         }
916         connectionServerResult.code = serverResult;
917         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &connectionServerResult.httpCode);
918         
919         if (serverResult != CURLE_OK){
920                 
921                 return entryList;
922                 
923         }
924         
925         entryList = ProcessXMLEntryList();
926         
927         // Second query: Get the list of contact data for the contacts that have
928         // beenchanged.
929         
930         // Restore the original settings.
931         
932         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
933         
934         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
935         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
936         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
937         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
938         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
939         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
940         
941         return entryList;
942         
945 CalDAVServerResult CalDAV::AddCalendar(string calendarName){
946         
947         CalDAVServerResult serverResult;
948         
949         AddCalendar(&calendarName, nullptr);
950         
951         return serverResult;
952         
955 CalDAVServerResult CalDAV::AddCalendar(string *calendarName, string *calendarShortName){
956         
957         CalDAVServerResult serverResult;
958         CalDAVSendData calendarAddSendData;
959         
960         // Build the server address.
961         
962         string userPrincipalURI = "";
963         userPrincipalURI = GetUserPrincipal();
964         
965         if (userPrincipalURI.size() == 0){
966                 
967                 return serverResult;
968                 
969         }
970         
971         string calendarHomeURI = "";
972         calendarHomeURI = GetCalendarHome(userPrincipalURI);
973         
974         // Generate the UUID.
975         
976         string UUIDValue = "";
977         
978         if (calendarShortName == nullptr){
979         
980                 UUIDValue = GenerateUUID();
981                 UUIDValue.erase(UUIDValue.end()-1);
983         } else {
984         
985                 UUIDValue = *calendarShortName;
986                 
987         }
988                 
989         string calendarHomeURL = calendarHomeURI;
990         calendarHomeURL.append(UUIDValue);
991         calendarHomeURL.append("/");
992         
993         // Build the calendar list address.
994         
995         string calendarListURLAddress = BuildServerAddress(&connectionData, calendarHomeURL);
996         
997         string calendarAddRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
998         "<c:mkcalendar xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
999         " <d:set>\n"
1000         "  <d:prop>\n"
1001         "   <d:displayname>";
1002         calendarAddRequest += *calendarName;
1003         calendarAddRequest += "</d:displayname>\n"
1004         "   <c:supported-calendar-component-set>\n"
1005         "    <c:comp name=\"VTODO\"/>\n"
1006         "    <c:comp name=\"VEVENT\"/>\n"
1007         "   </c:supported-calendar-component-set>\n"
1008         "  </d:prop>\n"
1009         " </d:set>\n"
1010         "</c:mkcalendar>";
1011         
1012         calendarAddSendData.readptr = &calendarAddRequest;
1013         calendarAddSendData.sizeleft = calendarAddRequest.size();
1014         
1015         // Setup the header.
1016         
1017         struct curl_slist *calendarRequestHeader = NULL;
1018         
1019         //curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
1020         curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarListURLAddress.c_str());
1021         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "MKCALENDAR");
1022         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
1023         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1024         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarAddSendData);
1025         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1026         
1027         // Process the data.
1028         
1029         serverData.clear();
1030         serverHeader.clear();
1031         
1032         CURLcode serverConnectionResult = curl_easy_perform(connectionHandle);
1033         
1034         if (serverConnectionResult == CURLE_OK){
1035                 serverResult.result = CALDAVQUERYRESULT_OK;
1036         } else {
1037                 serverResult.result = CALDAVQUERYRESULT_SERVERERROR;            
1038         }
1039         serverResult.code = serverConnectionResult;
1040         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
1041         
1042         // Restore the original settings.
1043         
1044         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
1045         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
1046         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
1047         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
1048         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1049         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
1050         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
1051         
1052         return serverResult;
1053         
1056 CalDAVServerResult CalDAV::EditCalendarProcess(string *calendarHREF,
1057                         string *calendarName,
1058                         Colour *calendarColour,
1059                         string *calendarDescription,
1060                         int *calendarOrder){
1062         CalDAVServerResult serverResult;
1063         CalDAVSendData calendarEditSendData;
1064         
1065         // Build the server address.
1066         
1067         string userPrincipalURI = "";
1068         userPrincipalURI = GetUserPrincipal();
1069         
1070         if (userPrincipalURI.size() == 0){
1071                 
1072                 return serverResult;
1073                 
1074         }
1075         
1076         string calendarHomeURI = "";
1077         calendarHomeURI = GetCalendarHome(userPrincipalURI);
1078         
1079         // Generate the UUID.
1080         
1081         string UUIDValue = GenerateUUID();
1082         UUIDValue.erase(UUIDValue.end()-1);
1083         
1084         string calendarHomeURL = calendarHomeURI;
1085         calendarHomeURL.append(UUIDValue);
1086         calendarHomeURL.append("/");
1087         
1088         // Build the calendar list address.
1089         
1090         string calendarEditURLAddress = BuildServerAddress(&connectionData, (*calendarHREF));
1091         
1092         string calendarEditRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1093         "<d:propertyupdate xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\"\n"
1094         "       xmlns:x0=\"http://apple.com/ns/ical/\">\n"
1095         " <d:set>\n"
1096         "  <d:prop>\n";
1097         
1098         // Update the calendar name.
1099         
1100         if (calendarName != nullptr){
1101         
1102                 calendarEditRequest += "<d:displayname>";
1103                 calendarEditRequest += (*calendarName);
1104                 calendarEditRequest += "</d:displayname>\n";
1105                 
1106         }
1107         
1108         // Update the calendar colour.
1109         
1110         if (calendarColour != nullptr){
1111                 
1112                 calendarEditRequest += "<x0:calendar-color>";
1113                 calendarEditRequest += (*calendarColour);
1114                 calendarEditRequest += "</x0:calendar-color>\n";
1115                 
1116         }
1117         
1118         // Update the calendar description.
1119         
1120         if (calendarDescription != nullptr){
1121                 
1122                 calendarEditRequest += "<c:calendar-description>";
1123                 calendarEditRequest += (*calendarDescription);
1124                 calendarEditRequest += "</c:calendar-description>\n";           
1125                 
1126         }
1127         
1128         // Update the calendar order.
1129         
1130         if (calendarOrder != nullptr){
1131                 
1132                 calendarEditRequest += "<x0:calendar-order>";
1133                 calendarEditRequest += to_string((*calendarOrder));
1134                 calendarEditRequest += "</x0:calendar-order>\n";
1135                 
1136         }
1137         
1138         calendarEditRequest += "  </d:prop>\n"
1139         " </d:set>\n"
1140         "</d:propertyupdate>";
1141         
1142         calendarEditSendData.readptr = &calendarEditRequest;
1143         calendarEditSendData.sizeleft = calendarEditRequest.size();
1144         
1145         // Setup the header.
1146         
1147         struct curl_slist *calendarRequestHeader = NULL;
1148         
1149         //curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, CalendarRequestHeader);
1150         curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarEditURLAddress.c_str());
1151         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
1152         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
1153         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1154         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &calendarEditSendData);
1155         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1156         
1157         // Process the data.
1158         
1159         serverData.clear();
1160         serverHeader.clear();
1161         
1162         CURLcode serverConnectionResult = curl_easy_perform(connectionHandle);
1163         
1164         if (serverConnectionResult == CURLE_OK){
1165                 serverResult.result = CALDAVQUERYRESULT_OK;
1166         } else {
1167                 serverResult.result = CALDAVQUERYRESULT_SERVERERROR;            
1168         }
1169         serverResult.code = serverConnectionResult;
1170         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
1171         
1172         // Restore the original settings.
1173         
1174         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
1175         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
1176         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
1177         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
1178         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1179         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
1180         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
1181         
1182         return serverResult;
1186 CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF,
1187                         string *calendarName,
1188                         Colour *calendarColour,
1189                         string *calendarDescription,
1190                         int *calendarOrder){
1191         
1192         CalDAVServerResult serverResult;
1193         
1194         serverResult = EditCalendarProcess(calendarHREF,
1195                 calendarName,
1196                 calendarColour,
1197                 calendarDescription,
1198                 calendarOrder);
1199                                 
1200         return serverResult;
1201         
1204 CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF,
1205                         Colour *calendarColour){
1207         CalDAVServerResult serverResult;
1209         serverResult = EditCalendarProcess(calendarHREF,
1210                 nullptr,
1211                 calendarColour,
1212                 nullptr,
1213                 nullptr);
1214                                 
1215         return serverResult;    
1216         
1219 CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF,
1220                         string *calendarName){
1221         
1222         CalDAVServerResult serverResult;
1223         
1224         serverResult = EditCalendarProcess(calendarHREF,
1225                 calendarName,
1226                 nullptr,
1227                 nullptr,
1228                 nullptr);       
1229         
1230         return serverResult;
1231         
1234 CalDAVServerResult CalDAV::EditCalendar(string *calendarHREF,
1235                         int *calendarOrder){
1236         
1237         CalDAVServerResult serverResult;
1238         
1239         serverResult = EditCalendarProcess(calendarHREF,
1240                 nullptr,
1241                 nullptr,
1242                 nullptr,
1243                 calendarOrder);
1244         
1245         return serverResult;
1246         
1249 CalDAVServerResult CalDAV::EditCalendarDescription(string *calendarHREF,
1250                         string *calendarDescription){
1251         
1252         CalDAVServerResult serverResult;
1253         
1254         serverResult = EditCalendarProcess(calendarHREF,
1255                 nullptr,
1256                 nullptr,
1257                 calendarDescription,
1258                 nullptr);
1259         
1260         return serverResult;
1261         
1264 CalDAVServerResult CalDAV::DeleteCalendar(string *calendarHREF){
1266         CalDAVServerResult serverResult;
1267         
1268         // Build the server address.
1269         
1270         string userPrincipalURI = "";
1271         userPrincipalURI = GetUserPrincipal();
1272         
1273         if (userPrincipalURI.size() == 0){
1274                 
1275                 return serverResult;
1276                 
1277         }
1278         
1279         string calendarHomeURI = "";
1280         calendarHomeURI = GetCalendarHome(userPrincipalURI);
1281         
1282         // Generate the UUID.
1283         
1284         string UUIDValue = GenerateUUID();
1285         UUIDValue.erase(UUIDValue.end()-1);
1286         
1287         string calendarHomeURL = calendarHomeURI;
1288         calendarHomeURL.append(UUIDValue);
1289         calendarHomeURL.append("/");
1290         
1291         // Build the calendar list address.
1292         
1293         struct curl_slist *deleteRequestHeader = NULL;
1294         
1295         deleteRequestHeader = curl_slist_append(deleteRequestHeader, "Depth: infinity");
1296         
1297         string calendarDeleteURLAddress = BuildServerAddress(&connectionData, (*calendarHREF));
1298         
1299         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, deleteRequestHeader);
1300         curl_easy_setopt(connectionHandle, CURLOPT_URL, calendarDeleteURLAddress.c_str());
1301         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
1302         
1303         // Delete the calendar.
1304         
1305         serverData.clear();
1306         serverHeader.clear();
1307         
1308         CURLcode serverConnectionResult = curl_easy_perform(connectionHandle);
1309         
1310         if (serverConnectionResult == CURLE_OK){
1311                 serverResult.result = CALDAVQUERYRESULT_OK;
1312         } else {
1313                 serverResult.result = CALDAVQUERYRESULT_SERVERERROR;            
1314         }
1315         serverResult.code = serverConnectionResult;
1316         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
1317         
1318         // Restore the original settings.
1319         
1320         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
1321         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
1322         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
1323         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
1324         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1325         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
1326         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
1327         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL);
1328         
1329         return serverResult;
1330         
1333 CalDAVServerResult CalDAV::GetEntryETag(string *calendarEntryHREF, string *eTagValue){
1334         
1335         CalDAVServerResult serverResult;
1336         CalDAVSendData entryETagGetData;
1337         
1338         // Build the server address.
1339         
1340         string userPrincipalURI = "";
1341         userPrincipalURI = GetUserPrincipal();
1342         
1343         if (userPrincipalURI.size() == 0){
1344                 
1345                 return serverResult;
1346                 
1347         }
1348         
1349         string calendarHomeURI = "";
1350         calendarHomeURI = GetCalendarHome(userPrincipalURI);
1352         // Split the path and filename.
1353         
1354         string entryURIPath;
1355         string entryFilename;
1356         
1357         SplitPathFilename(calendarEntryHREF, &entryURIPath, &entryFilename);
1358         
1359         // Build the request for the server.
1361         string entryETagRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1362         "<c:calendar-multiget xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">\n"
1363         " <d:prop>\n"
1364         "  <d:getetag />\n"
1365         " </d:prop>\n"
1366         " <d:href>";
1367         entryETagRequest += (*calendarEntryHREF);
1368         entryETagRequest += "</d:href>\n"
1369         "</c:calendar-multiget>";
1370         
1371         entryETagGetData.readptr = &entryETagRequest;
1372         entryETagGetData.sizeleft = entryETagRequest.size();
1373         
1374         // Build the calendar list address.
1375         
1376         struct curl_slist *getETagRequestHeader = NULL;
1378         getETagRequestHeader = curl_slist_append(getETagRequestHeader, "Depth: 1");     
1379         getETagRequestHeader = curl_slist_append(getETagRequestHeader, "Prefer: return-minimal");
1380         getETagRequestHeader = curl_slist_append(getETagRequestHeader, "Content-Type: application/xml; charset=utf-8");
1381         
1382         string getETagURLAddress = BuildServerAddress(&connectionData, entryURIPath);
1383         
1384         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, getETagRequestHeader);
1385         curl_easy_setopt(connectionHandle, CURLOPT_URL, getETagURLAddress.c_str());
1386         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "REPORT");
1387         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
1388         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1389         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryETagGetData);
1390         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1391         
1392         // Attempt to get the entity tag.
1393         
1394         serverData.clear();
1395         serverHeader.clear();
1396         
1397         CURLcode serverConnectionResult = curl_easy_perform(connectionHandle);
1398         
1399         if (serverConnectionResult == CURLE_OK){
1400                 serverResult.result = CALDAVQUERYRESULT_OK;
1401         } else {
1402                 serverResult.result = CALDAVQUERYRESULT_SERVERERROR;            
1403         }
1404         serverResult.code = serverConnectionResult;
1405         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
1406         
1407         if (serverConnectionResult != CURLE_OK){
1408                 return serverResult;
1409         }
1410         
1411         // Get the entity tag from the result.
1412         
1413         *eTagValue = ProcessXMLEntryETag();
1414         
1415         // Restore the original settings.
1416         
1417         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
1418         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
1419         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
1420         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
1421         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1422         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
1423         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
1424         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL);
1425         
1426         return serverResult;
1427         
1430 CalDAVServerResult CalDAV::AddEntry(string *calendarEntryHREF, string *entryData){
1431         
1432         // Add an entry to the calendar collection.
1433         
1434         CalDAVServerResult serverResult;
1435         CalDAVSendData entryAddSendData;
1436         
1437         // Build the calendar list address.
1438         
1439         string entryAddURLAddress = BuildServerAddress(&connectionData, (*calendarEntryHREF));
1440         
1441         entryAddSendData.readptr = entryData;
1442         entryAddSendData.sizeleft = entryData->size();
1443         
1444         struct curl_slist *calendarRequestHeader = NULL;
1445         
1446         calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Content-Type: text/calendar; charset=utf-8");
1447         
1448         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarRequestHeader);
1449         curl_easy_setopt(connectionHandle, CURLOPT_URL, entryAddURLAddress.c_str());
1450         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PUT");
1451         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
1452         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1453         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryAddSendData);
1454         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1455         
1456         // Process the data.
1457         
1458         serverData.clear();
1459         serverHeader.clear();
1460         
1461         CURLcode serverConnectionResult = curl_easy_perform(connectionHandle);
1462         
1463         if (serverConnectionResult == CURLE_OK){
1464                 serverResult.result = CALDAVQUERYRESULT_OK;
1465         } else {
1466                 serverResult.result = CALDAVQUERYRESULT_SERVERERROR;            
1467         }
1468         serverResult.code = serverConnectionResult;
1469         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
1470         
1471         // Restore the original settings.
1472         
1473         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
1474         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
1475         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
1476         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
1477         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1478         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
1479         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
1480         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL);
1481         
1482         return serverResult;
1483         
1486 CalDAVServerResult CalDAV::EditEntry(string *calendarEntryHREF, string *entryData, string *entryETag){
1487         
1488         // Edit an entry in the calendar collection.
1490         // Add an entry to the calendar collection.
1491         
1492         CalDAVServerResult serverResult;
1493         CalDAVSendData entryAddSendData;
1494         
1495         // Build the calendar list address.
1496         
1497         string entryAddURLAddress = BuildServerAddress(&connectionData, (*calendarEntryHREF));
1498         
1499         entryAddSendData.readptr = entryData;
1500         entryAddSendData.sizeleft = entryData->size();
1501         
1502         string ifMatchHeader = "If-Match: \"";
1503         ifMatchHeader.append(*entryETag);
1504         ifMatchHeader.append("\"");
1505         
1506         struct curl_slist *calendarRequestHeader = NULL;
1507         
1508         calendarRequestHeader = curl_slist_append(calendarRequestHeader, "Content-Type: text/calendar; charset=utf-8");
1509         calendarRequestHeader = curl_slist_append(calendarRequestHeader, ifMatchHeader.c_str());        
1510         
1511         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, calendarRequestHeader);
1512         curl_easy_setopt(connectionHandle, CURLOPT_URL, entryAddURLAddress.c_str());
1513         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "PUT");
1514         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 1L);
1515         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1516         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &entryAddSendData);
1517         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, CalDAVSend);
1518         
1519         // Process the data.
1520         
1521         serverData.clear();
1522         serverHeader.clear();
1523         
1524         CURLcode serverConnectionResult = curl_easy_perform(connectionHandle);
1525         
1526         if (serverConnectionResult == CURLE_OK){
1527                 serverResult.result = CALDAVQUERYRESULT_OK;
1528         } else {
1529                 serverResult.result = CALDAVQUERYRESULT_SERVERERROR;            
1530         }
1531         serverResult.code = serverConnectionResult;
1532         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
1533         
1534         // Restore the original settings.
1535         
1536         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
1537         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
1538         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
1539         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
1540         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1541         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
1542         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
1543         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL);
1544         
1545         return serverResult;
1546         
1549 CalDAVServerResult CalDAV::DeleteEntry(string *calendarEntryHREF){
1550         
1551         // Delete an entry in the calendar collection.
1552         
1553         CalDAVServerResult serverResult;
1554         
1555         // Build the calendar list address.
1556         
1557         string entryDeleteURLAddress = BuildServerAddress(&connectionData, (*calendarEntryHREF));
1558         
1559         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL);
1560         curl_easy_setopt(connectionHandle, CURLOPT_URL, entryDeleteURLAddress.c_str());
1561         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
1562         
1563         // Delete the calendar.
1564         
1565         serverData.clear();
1566         serverHeader.clear();
1567         
1568         CURLcode serverConnectionResult = curl_easy_perform(connectionHandle);
1569         
1570         if (serverConnectionResult == CURLE_OK){
1571                 serverResult.result = CALDAVQUERYRESULT_OK;
1572         } else {
1573                 serverResult.result = CALDAVQUERYRESULT_SERVERERROR;            
1574         }
1575         serverResult.code = serverConnectionResult;
1576         curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &serverResult.httpCode);
1577         
1578         // Restore the original settings.
1579         
1580         string originalServerAddress = BuildServerAddress(&connectionData, "/principals/");
1581         curl_easy_setopt(connectionHandle, CURLOPT_URL, originalServerAddress.c_str());
1582         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, NULL);        
1583         curl_easy_setopt(connectionHandle, CURLOPT_UPLOAD, 0L);
1584         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1585         curl_easy_setopt(connectionHandle, CURLOPT_READDATA, NULL);
1586         curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, NULL);
1587         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, NULL);
1588         
1589         return serverResult;
1590         
1593 COSSLVerified CalDAV::SSLVerify()
1595         return sslVerified;
1598 void CalDAV::BypassSSLVerification(bool enableBypass){
1599         enableSSLBypass = enableBypass;
1600         sslSelfSigned = enableBypass;
1603 #if defined(__APPLE__)
1605 SecTrustRef CalDAV::BuildSSLCollection(){
1606         
1607         return certificateData;
1608         
1611 #elif defined(__WIN32__)
1613 PCCERT_CONTEXT CalDAV::BuildSSLCollection(){
1615         return certificateData;
1619 #else
1621 SSLCertCollectionString CalDAV::BuildSSLCollection(){
1623         // Build and return the SSL collection.
1624         
1625         SSLCertCollectionString sslCertInfo;
1627         // Grab the certificate data.
1629         union {
1630                 struct curl_slist *certdata;
1631                 struct curl_certinfo *certinfo;
1632         } certptr;
1634         certptr.certdata = NULL;
1635         
1636         CURLcode result = curl_easy_getinfo(connectionHandle, CURLINFO_CERTINFO, &certptr.certinfo);
1637         
1638         std::string certPropName;
1639         std::string certPropValue;
1640         
1641         for (int i = 0; i < certptr.certinfo->num_of_certs; i++){
1642                 
1643                 struct curl_slist *slist;
1644                 SSLCertDataString sslCertDataInc;
1645                 
1646                 for (slist = certptr.certinfo->certinfo[i]; slist; slist = slist->next){
1647                         
1648                         // Using wxStringTokenizer from wxWidgets.
1649                         
1650                         wxStringTokenizer certDataInc(wxString::FromUTF8(slist->data), ":");
1651                         
1652                         // Get first token as the property name.
1653                         
1654                         certPropName = certDataInc.GetNextToken().ToStdString();
1655                         
1656                         // Get remaining tokens as the property value.
1657                         
1658                         while(certDataInc.HasMoreTokens()){
1659                         
1660                                 certPropValue.append(certDataInc.GetNextToken());
1661                         
1662                         }
1663                         
1664                         sslCertDataInc.CertData.insert(std::make_pair(certPropName, certPropValue));
1665                         certPropName.clear();
1666                         certPropValue.clear();
1667                         
1668                 }
1669         
1670                 sslCertInfo.SSLCollection.insert(std::make_pair(i, sslCertDataInc));
1671         
1672         }
1673         
1674         return sslCertInfo;
1678 #endif
1680 bool CalDAV::CanDoSSL(){
1681         return sslStatus;
1684 bool CalDAV::HasValidResponse(){
1685         return validResponse;
1688 bool CalDAV::AbleToLogin(){
1689         return authPassed;
1692 bool CalDAV::IsSelfSigned(){
1693         return sslSelfSigned;
1696 string CalDAV::GetErrorMessage(){
1697         
1698         errorMessage = sessionErrorBuffer;      
1699         return errorMessage;
1700         
1703 static bool CalDAVObjectValidSettings(CalDAVConnectionData *connData){
1705         // Check if the passed CalDAV Connection Data is has
1706         // an address set. Return false if nullptr is used.
1708         if (connData == nullptr){
1709         
1710                 return false;
1711         
1712         }
1713         
1714         // Check the server hostname. Return false
1715         // if no value has been set.
1716         
1717         if (connData->hostname.size() == 0){
1718         
1719                 return false;
1720         
1721         }
1722         
1723         // Check the server port. Return false if
1724         // no value has been set or the port number
1725         // is less than 1 or higher than 65535.
1726         
1727         if (connData->port < 1 || connData->port > 65535){
1728         
1729                 return false;
1730         
1731         }
1732         
1733         // Check the server username. Return false
1734         // if no value has been set.
1735         
1736         if (connData->username.size() == 0){
1737                 
1738                 return false;
1739                 
1740         }       
1741         
1742         // Check the server password. Return false
1743         // if no value has been set.
1744         
1745         if (connData->password.size() == 0){
1746                 
1747                 return false;
1748                 
1749         }
1751         // Cannot check UseSSL: It is either true
1752         // or false.
1753         
1754         // Cannot check Prefix: The prefix may need
1755         // to be worked out first.
1757         // No errors were found whilst checking so
1758         // return true.
1759         
1760         return true;
1764 string CalDAV::BuildServerAddress(CalDAVConnectionData *connData, string uriAddress){
1765         
1766         string serverAddress;
1767         
1768         // Setup the server address.
1769         
1770         if (connData->useSSL == true){
1771                 serverAddress += "https://";
1772         } else {
1773                 serverAddress += "http://";
1774         }
1775         
1776         serverAddress += connData->hostname;
1777         
1778         // Check if server port is 80, otherwise
1779         // specifiy the port number in the address.
1780         
1781         if (connData->port != 80){
1782                 serverAddress += ":";
1783                 serverAddress += to_string(connData->port);
1784         }
1785         
1786         serverAddress += uriAddress;
1787         
1788         return serverAddress;
1789         
1792 void CalDAV::SetupDefaultParametersNonSSL(bool doAuthentication){
1793         
1794         std::string serverAddress = "";
1796         string serverAddressURL = "http://" + connectionData.hostname + ":" + to_string(connectionData.port) + "/";
1797         string usernamePassword = connectionData.username + ":" + connectionData.password;
1798         
1799         curl_easy_setopt(connectionHandle, CURLOPT_URL, serverAddressURL.c_str());
1800         curl_easy_setopt(connectionHandle, CURLOPT_NOPROGRESS, 1L);
1801         curl_easy_setopt(connectionHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC);
1802         curl_easy_setopt(connectionHandle, CURLOPT_TIMEOUT, 60);
1803         curl_easy_setopt(connectionHandle, CURLOPT_FAILONERROR, true);
1804         curl_easy_setopt(connectionHandle, CURLOPT_USERAGENT, XSDCAL_USERAGENT);
1805         curl_easy_setopt(connectionHandle, CURLOPT_NOSIGNAL, 1L);
1806         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "GET");
1807         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, nullptr);
1808         curl_easy_setopt(connectionHandle, CURLOPT_POSTFIELDS, nullptr);
1809         curl_easy_setopt(connectionHandle, CURLOPT_POSTFIELDSIZE, 0L);
1811         if (doAuthentication == true){
1812                 curl_easy_setopt(connectionHandle, CURLOPT_USERPWD, usernamePassword.c_str());
1813         } else {
1814                 curl_easy_setopt(connectionHandle, CURLOPT_USERPWD, NULL);              
1815         }
1816         
1819 void CalDAV::SetupDefaultParametersSSL(bool doAuthentication){
1820         
1821         // Setup the default parameters.
1822         
1823         string ServerAddressURL = "https://" + connectionData.hostname + ":" + to_string(connectionData.port) + "/";
1824         string UsernamePassword = connectionData.username + ":" + connectionData.password;
1825         
1826         curl_easy_setopt(connectionHandle, CURLOPT_URL, ServerAddressURL.c_str());
1827         curl_easy_setopt(connectionHandle, CURLOPT_NOPROGRESS, 1L);
1828         curl_easy_setopt(connectionHandle, CURLOPT_CERTINFO, 1L);
1829         curl_easy_setopt(connectionHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC);
1830         curl_easy_setopt(connectionHandle, CURLOPT_TIMEOUT, 60);
1831         curl_easy_setopt(connectionHandle, CURLOPT_FAILONERROR, 0L);
1832         curl_easy_setopt(connectionHandle, CURLOPT_USERAGENT, XSDCAL_USERAGENT);
1833         curl_easy_setopt(connectionHandle, CURLOPT_ERRORBUFFER, sessionErrorBuffer);
1834         curl_easy_setopt(connectionHandle, CURLOPT_CUSTOMREQUEST, "GET");
1835         curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, nullptr);
1836         curl_easy_setopt(connectionHandle, CURLOPT_POSTFIELDS, nullptr);
1837         curl_easy_setopt(connectionHandle, CURLOPT_POSTFIELDSIZE, 0L);
1839         if (doAuthentication == true){
1840                 curl_easy_setopt(connectionHandle, CURLOPT_USERPWD, UsernamePassword.c_str());
1841         } else {
1842                 curl_easy_setopt(connectionHandle, CURLOPT_USERPWD, NULL);              
1843         }
1844         
1845         if (enableSSLBypass == true){
1846                 curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYHOST, 0L);
1847                 curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYPEER, 0L);
1848                 curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYSTATUS, 0L);
1849         } else {
1850                 curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYHOST, 2L);
1851                 curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYPEER, 1L);
1852                 curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYSTATUS, 1L);
1853         }
1854         
1855 #if !defined(__APPLE__) || defined(__WIN32__)
1856         
1857         if (connectionData.account.size() > 0){
1858                 
1859                 // Check if the server certificate file exists.
1860                 
1861                 string certificateFilename = GetAccountDir(connectionData.account, true);
1862                 
1863                 if (wxFile::Exists(certificateFilename)){
1864                         
1865                         curl_easy_setopt(connectionHandle, CURLOPT_CAINFO, certificateFilename.c_str());
1866                         
1867                 }
1868                 
1869         }
1871 #endif
1872         
1875 void CalDAV::ResetResults(){
1876         
1877         sslStatus = false;
1878         COSSLVerified SSLVerified = COSSL_NORESULT;
1879         validResponse = false;
1880         authPassed = false;
1881         sslSelfSigned = false;
1882         //taskCompleted = false;
1883         errorMessage = "";
1884         sessionErrorBuffer[0] = '\0';
1885         //sessionResult = CURLE_OK;
1886         serverData = "";
1887         serverHeader = "";
1888         /*if (headerList != nullptr){
1889                 curl_slist_free_all(headerList);
1890                 headerList = nullptr;
1891         }*/
1892         
Xestia Software Development
Yn Maystri
© 2006 - 2019 Xestia Software Development
Software

Xestia Address Book
Xestia Calendar
Development

Xestia Gelforn
Everything else

About
News
Privacy Policy