1 // CalDAV-XMLProcessing.cpp - CalDAV Connection Object - XML Processing.
3 // (c) 2016-2017 Xestia Software Development.
5 // This file is part of Xestia Calendar.
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.
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.
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 string CalDAV::ProcessXMLUserPrincipal(){
25 string userPrincipalURI;
27 xmlDocPtr xmlCalDAVDoc;
28 xmlCalDAVDoc = xmlReadMemory(serverData.c_str(), (int)serverData.size(), "noname.xml", NULL, 0);
31 bool nodeFound = false;
33 // Start with the first node, look for multistatus.
35 for (nodeSeek = xmlCalDAVDoc->children;
37 nodeSeek = nodeSeek->next)
40 if (!xmlStrcmp(nodeSeek->name, (const xmlChar *)"multistatus") ||
41 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"d:multistatus") ||
42 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"D:multistatus")
52 if (nodeFound == false){
54 return userPrincipalURI;
60 if (nodeFound == false){ return userPrincipalURI; } else { nodeFound = false; }
61 nodeFound = MatchXMLNameTransverse(&nodeSeek, "response");
65 if (nodeFound == false){ return userPrincipalURI; } else { nodeFound = false; }
66 nodeFound = MatchXMLNameTransverse(&nodeSeek, "propstat");
70 if (nodeFound == false){ return userPrincipalURI; } else { nodeFound = false; }
71 nodeFound = MatchXMLNameTransverse(&nodeSeek, "prop");
73 // Look for current-user-principal.
75 if (nodeFound == false){ return userPrincipalURI; } else { nodeFound = false; }
76 nodeFound = MatchXMLNameTransverse(&nodeSeek, "current-user-principal");
80 if (nodeFound == false){ return userPrincipalURI; } else { nodeFound = false; }
81 nodeFound = MatchXMLNameTransverse(&nodeSeek, "href");
83 // Get the data from href.
85 userPrincipalURI = FetchXMLData(&nodeSeek);
87 xmlFreeDoc(xmlCalDAVDoc);
89 return userPrincipalURI;
93 string CalDAV::ProcessXMLCalendarHome(){
95 string calendarHomeURI;
97 xmlDocPtr xmlCalDAVDoc;
98 xmlCalDAVDoc = xmlReadMemory(serverData.c_str(), (int)serverData.size(), "noname.xml", NULL, 0);
101 bool nodeFound = false;
103 // Start with the first node, look for multistatus.
105 for (nodeSeek = xmlCalDAVDoc->children;
107 nodeSeek = nodeSeek->next)
110 if (!xmlStrcmp(nodeSeek->name, (const xmlChar *)"multistatus") ||
111 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"d:multistatus") ||
112 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"D:multistatus")
122 if (nodeFound == false){
124 return calendarHomeURI;
128 // Look for response.
130 if (nodeFound == false){ return calendarHomeURI; } else { nodeFound = false; }
131 nodeFound = MatchXMLNameTransverse(&nodeSeek, "response");
133 // Look for propstat.
135 if (nodeFound == false){ return calendarHomeURI; } else { nodeFound = false; }
136 nodeFound = MatchXMLNameTransverse(&nodeSeek, "propstat");
140 if (nodeFound == false){ return calendarHomeURI; } else { nodeFound = false; }
141 nodeFound = MatchXMLNameTransverse(&nodeSeek, "prop");
143 // Look for calendar-home-set.
145 if (nodeFound == false){ return calendarHomeURI; } else { nodeFound = false; }
146 nodeFound = MatchXMLNameTransverse(&nodeSeek, "calendar-home-set");
150 if (nodeFound == false){ return calendarHomeURI; } else { nodeFound = false; }
151 nodeFound = MatchXMLNameTransverse(&nodeSeek, "href");
153 // Get the data from href.
155 calendarHomeURI = FetchXMLData(&nodeSeek);
157 xmlFreeDoc(xmlCalDAVDoc);
159 return calendarHomeURI;
163 CalDAVCalendarList CalDAV::ProcessXMLCalendarList(){
165 CalDAVCalendarList calendarList;
167 xmlDocPtr xmlCalDAVDoc;
168 xmlCalDAVDoc = xmlReadMemory(serverData.c_str(), (int)serverData.size(), "noname.xml", NULL, 0);
170 xmlNodePtr nodeSeek = NULL;
171 xmlNodePtr nodeResponse = NULL;
172 xmlNodePtr nodeMatch = NULL;
173 xmlNodePtr nodeData = NULL;
174 bool nodeFound = false;
175 int responseCount = 0;
177 // Start with the first node, look for multistatus.
179 for (nodeSeek = xmlCalDAVDoc->children;
181 nodeSeek = nodeSeek->next)
184 if (!xmlStrcmp(nodeSeek->name, (const xmlChar *)"multistatus") ||
185 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"d:multistatus") ||
186 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"D:multistatus")
189 nodeResponse = nodeSeek->children;
197 if (nodeFound == false){
203 for (nodeResponse = nodeSeek->children;
204 nodeResponse != nullptr;
205 nodeResponse = nodeResponse->next)
208 // Go through each of the responses and find the calendars.
210 nodeMatch = xmlCopyNode(nodeResponse, 1);
212 if (MatchXMLName(&nodeMatch, "response")){
214 nodeData = xmlCopyNode(nodeMatch, 1);
216 // Check the resource type is a calendar.
218 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
219 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
220 if (!MatchXMLNameTransverse(&nodeData, "resourcetype")){ continue; }
221 if (!MatchXMLNameTransverse(&nodeData, "calendar")){ continue; }
225 nodeData = xmlCopyNode(nodeMatch, 1);
227 if (!MatchXMLNameTransverse(&nodeData, "href")){ continue; }
229 string hrefAddress = FetchXMLData(&nodeData);
231 // Get the calendar name.
233 nodeData = xmlCopyNode(nodeMatch, 1);
235 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
236 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
237 if (!MatchXMLNameTransverse(&nodeData, "displayname")){ continue; }
239 string calendarName = FetchXMLData(&nodeData);
241 // Get the calendar description.
243 nodeData = xmlCopyNode(nodeMatch, 1);
245 string calendarDescription = "";
247 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
248 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
249 if (MatchXMLNameTransverse(&nodeData, "calendar-description")){
251 calendarDescription = FetchXMLData(&nodeData);
255 // Get the calendar colour.
257 nodeData = xmlCopyNode(nodeMatch, 1);
259 Colour calendarColour;
260 bool colourResult = false;
262 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
263 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
264 if (MatchXMLNameTransverse(&nodeData, "calendar-color")){
266 string calendarColourString = "";
267 string calendarColourHexValue = "";
269 bool keepRunning = true;
271 calendarColourString = FetchXMLData(&nodeData);
273 while(keepRunning == true){
275 if (calendarColourString.substr(0,1) == "#" && calendarColourString.length() == 9){
277 // Get the red colour.
279 calendarColourHexValue = calendarColourString.substr(1,2);
280 if (!HexToInt(&calendarColourHexValue, &colourNumber)){ break; }
281 calendarColour.red = colourNumber;
283 // Get the green colour.
285 calendarColourHexValue = calendarColourString.substr(3,2);
286 if (!HexToInt(&calendarColourHexValue, &colourNumber)){ break; }
287 calendarColour.green = colourNumber;
289 // Get the blue colour.
291 calendarColourHexValue = calendarColourString.substr(5,2);
292 if (!HexToInt(&calendarColourHexValue, &colourNumber)){ break; };
293 calendarColour.blue = colourNumber;
297 calendarColourHexValue = calendarColourString.substr(7,2);
298 if (!HexToInt(&calendarColourHexValue, &colourNumber)){ break; };
299 calendarColour.alpha = colourNumber;
305 colourResult = false;
315 if (colourResult == false){
317 calendarColour.red = 0;
318 calendarColour.blue = 0;
319 calendarColour.green = 0;
320 calendarColour.alpha = 0;
324 // Get the calendar order.
326 nodeData = xmlCopyNode(nodeMatch, 1);
328 int calendarOrder = 0;
330 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
331 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
332 if (MatchXMLNameTransverse(&nodeData, "calendar-order")){
334 string calendarOrderString = FetchXMLData(&nodeData);
335 if (!HexToInt(&calendarOrderString, &calendarOrder)){
341 // Get the calendar tag.
343 nodeData = xmlCopyNode(nodeMatch, 1);
345 string calendarTag = "";
347 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
348 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
349 if (MatchXMLNameTransverse(&nodeData, "getctag")){
351 calendarTag = FetchXMLData(&nodeData);
355 // Get the calendar tag URL.
357 nodeData = xmlCopyNode(nodeMatch, 1);
359 string calendarTagURL = "";
361 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
362 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
363 if (MatchXMLNameTransverse(&nodeData, "sync-token")){
365 calendarTagURL = FetchXMLData(&nodeData);
369 // Insert the calendar information into the
370 // list if all the information is there.
372 calendarList.name.insert(make_pair(responseCount, calendarName));
373 calendarList.description.insert(make_pair(responseCount, calendarDescription));
374 calendarList.href.insert(make_pair(responseCount, hrefAddress));
375 calendarList.calColour.insert(make_pair(responseCount, calendarColour));
376 calendarList.order.insert(make_pair(responseCount, calendarOrder));
377 calendarList.tag.insert(make_pair(responseCount, calendarTag));
378 calendarList.tagURL.insert(make_pair(responseCount, calendarTagURL));
386 xmlFreeDoc(xmlCalDAVDoc);
392 string CalDAV::ProcessXMLEntryETag(){
396 xmlDocPtr xmlCalDAVDoc;
397 xmlCalDAVDoc = xmlReadMemory(serverData.c_str(), (int)serverData.size(), "noname.xml", NULL, 0);
400 bool nodeFound = false;
402 // Start with the first node, look for multistatus.
404 for (nodeSeek = xmlCalDAVDoc->children;
406 nodeSeek = nodeSeek->next)
409 if (!xmlStrcmp(nodeSeek->name, (const xmlChar *)"multistatus") ||
410 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"d:multistatus") ||
411 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"D:multistatus")
421 if (nodeFound == false){
427 // Look for response.
429 if (nodeFound == false){ return entryETag; } else { nodeFound = false; }
430 nodeFound = MatchXMLNameTransverse(&nodeSeek, "response");
432 // Look for propstat.
434 if (nodeFound == false){ return entryETag; } else { nodeFound = false; }
435 nodeFound = MatchXMLNameTransverse(&nodeSeek, "propstat");
439 if (nodeFound == false){ return entryETag; } else { nodeFound = false; }
440 nodeFound = MatchXMLNameTransverse(&nodeSeek, "prop");
442 // Look for calendar-home-set.
444 if (nodeFound == false){ return entryETag; } else { nodeFound = false; }
445 nodeFound = MatchXMLNameTransverse(&nodeSeek, "getetag");
447 // Get the data from href.
449 entryETag = FetchXMLData(&nodeSeek);
451 xmlFreeDoc(xmlCalDAVDoc);
453 // Check if the entity tag contains quote marks
454 // at the start and end and remove them (if needed).
456 if (entryETag.substr(0,1) == "\"" &&
457 entryETag.substr(entryETag.size()-1, 1) == "\"" && entryETag.size() > 2){
459 entryETag.erase(entryETag.begin());
460 entryETag.erase(entryETag.end()-1);
468 CalDAVEntryList CalDAV::ProcessXMLEntryList(){
470 CalDAVEntryList entryList;
472 xmlDocPtr xmlCalDAVDoc;
473 xmlCalDAVDoc = xmlReadMemory(serverData.c_str(), (int)serverData.size(), "noname.xml", NULL, 0);
475 xmlNodePtr nodeSeek = NULL;
476 xmlNodePtr nodeResponse = NULL;
477 xmlNodePtr nodeMatch = NULL;
478 xmlNodePtr nodeData = NULL;
479 bool nodeFound = false;
480 int responseCount = 0;
482 // Start with the first node, look for multistatus.
484 for (nodeSeek = xmlCalDAVDoc->children;
486 nodeSeek = nodeSeek->next)
489 if (!xmlStrcmp(nodeSeek->name, (const xmlChar *)"multistatus") ||
490 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"d:multistatus") ||
491 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"D:multistatus")
494 nodeResponse = nodeSeek->children;
502 if (nodeFound == false){
508 for (nodeResponse = nodeSeek->children;
509 nodeResponse != nullptr;
510 nodeResponse = nodeResponse->next)
513 // Go through each of the responses and find the calendars.
515 nodeMatch = xmlCopyNode(nodeResponse, 1);
517 if (MatchXMLName(&nodeMatch, "response")){
519 nodeData = xmlCopyNode(nodeMatch, 1);
523 nodeData = xmlCopyNode(nodeMatch, 1);
525 if (!MatchXMLNameTransverse(&nodeData, "href")){ continue; }
527 string hrefAddress = FetchXMLData(&nodeData);
529 // Get the calendar data.
531 nodeData = xmlCopyNode(nodeMatch, 1);
533 string entryDescription = "";
535 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
536 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
537 if (MatchXMLNameTransverse(&nodeData, "calendar-data")){
539 // Note: libxml2 will strip the CDATA part at the start and
540 // end of each calendar-data section.
542 entryDescription = FetchXMLData(&nodeData);
546 // Get the entry entity tag.
548 nodeData = xmlCopyNode(nodeMatch, 1);
550 string entryEntityTag = "";
552 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
553 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
554 if (MatchXMLNameTransverse(&nodeData, "getetag")){
556 entryEntityTag = FetchXMLData(&nodeData);
560 // Insert the calendar information into the
561 // list if all the information is there.
563 entryList.href.insert(make_pair(responseCount, hrefAddress));
564 entryList.data.insert(make_pair(responseCount, entryDescription));
565 entryList.tag.insert(make_pair(responseCount, entryEntityTag));
573 xmlFreeDoc(xmlCalDAVDoc);
579 CalDAVEntryList CalDAV::ProcessXMLSyncTokenList(){
581 CalDAVEntryList entryList;
583 xmlDocPtr xmlCalDAVDoc;
584 xmlCalDAVDoc = xmlReadMemory(serverData.c_str(), (int)serverData.size(), "noname.xml", NULL, 0);
586 xmlNodePtr nodeSeek = NULL;
587 xmlNodePtr nodeResponse = NULL;
588 xmlNodePtr nodeMatch = NULL;
589 xmlNodePtr nodeData = NULL;
590 bool nodeFound = false;
591 int responseCount = 0;
593 // Start with the first node, look for multistatus.
595 for (nodeSeek = xmlCalDAVDoc->children;
597 nodeSeek = nodeSeek->next)
600 if (!xmlStrcmp(nodeSeek->name, (const xmlChar *)"multistatus") ||
601 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"d:multistatus") ||
602 !xmlStrcmp(nodeSeek->name, (const xmlChar *)"D:multistatus")
605 nodeResponse = nodeSeek->children;
613 if (nodeFound == false){
619 for (nodeResponse = nodeSeek->children;
620 nodeResponse != nullptr;
621 nodeResponse = nodeResponse->next)
624 // Go through each of the responses and find the calendars.
626 nodeMatch = xmlCopyNode(nodeResponse, 1);
628 if (MatchXMLName(&nodeMatch, "response")){
630 nodeData = xmlCopyNode(nodeMatch, 1);
634 nodeData = xmlCopyNode(nodeMatch, 1);
636 if (!MatchXMLNameTransverse(&nodeData, "href")){ continue; }
638 string hrefAddress = FetchXMLData(&nodeData);
640 // Get the entry entity tag.
642 nodeData = xmlCopyNode(nodeMatch, 1);
644 string entryEntityTag = "";
646 if (!MatchXMLNameTransverse(&nodeData, "propstat")){ continue; }
647 if (!MatchXMLNameTransverse(&nodeData, "prop")){ continue; }
648 if (MatchXMLNameTransverse(&nodeData, "getetag")){
650 entryEntityTag = FetchXMLData(&nodeData);
654 // Insert the calendar information into the
655 // list if all the information is there.
657 entryList.href.insert(make_pair(responseCount, hrefAddress));
658 entryList.data.insert(make_pair(responseCount, ""));
659 entryList.tag.insert(make_pair(responseCount, entryEntityTag));
667 xmlFreeDoc(xmlCalDAVDoc);
673 bool CalDAV::MatchXMLNameTransverse(xmlNodePtr *nodePtr, string nodeName){
675 string nodeNameSmallD = "d:" + nodeName;
676 string nodeNameLargeD = "D:" + nodeName;
678 for ((*nodePtr) = (*nodePtr)->children;
680 (*nodePtr) = (*nodePtr)->next)
683 if (!xmlStrcmp((*nodePtr)->name, (const xmlChar *)nodeName.c_str()) ||
684 !xmlStrcmp((*nodePtr)->name, (const xmlChar *)nodeNameSmallD.c_str()) ||
685 !xmlStrcmp((*nodePtr)->name, (const xmlChar *)nodeNameLargeD.c_str())
698 bool CalDAV::MatchXMLName(xmlNodePtr *nodePtrOriginal, string nodeName){
700 if (nodePtrOriginal == nullptr){
706 string nodeNameSmallD = "d:" + nodeName;
707 string nodeNameLargeD = "D:" + nodeName;
709 xmlNodePtr *nodePtr = nodePtrOriginal;
711 if (!xmlStrcmp((*nodePtr)->name, (const xmlChar *)nodeName.c_str()) ||
712 !xmlStrcmp((*nodePtr)->name, (const xmlChar *)nodeNameSmallD.c_str()) ||
713 !xmlStrcmp((*nodePtr)->name, (const xmlChar *)nodeNameLargeD.c_str())
728 string CalDAV::FetchXMLData(xmlNodePtr *nodePtr){
730 for ((*nodePtr) = (*nodePtr)->children;
732 (*nodePtr) = (*nodePtr)->next)
735 return (const char*)(*nodePtr)->content;