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;