1 // CalDAV-XMLProcessing.cpp - CalDAV Connection Object - XML Processing.
3 // (c) 2016 Xestia Software Development.
5 // This file is part of Xestia Calendar.
7 // Xestia Address Book is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by the
9 // Free Software Foundation, version 3 of the license.
11 // Xestia Address Book is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with Xestia Calendar. If not, see <http://www.gnu.org/licenses/>
23 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")){
213 \r NodeData = xmlCopyNode(NodeMatch, 1);
215 // Check the resource type is a calendar.
217 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
218 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
219 if (!MatchXMLNameTransverse(&NodeData, "resourcetype")){ continue; }
220 if (!MatchXMLNameTransverse(&NodeData, "calendar")){ continue; }
224 NodeData = xmlCopyNode(NodeMatch, 1);
226 if (!MatchXMLNameTransverse(&NodeData, "href")){ continue; }
228 string HREFAddress = FetchXMLData(&NodeData);
230 // Get the calendar name.
232 NodeData = xmlCopyNode(NodeMatch, 1);
234 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
235 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
236 if (!MatchXMLNameTransverse(&NodeData, "displayname")){ continue; }
238 string CalendarName = FetchXMLData(&NodeData);
240 // Get the calendar description.
242 NodeData = xmlCopyNode(NodeMatch, 1);
244 string CalendarDescription = "";
246 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
247 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
248 if (MatchXMLNameTransverse(&NodeData, "calendar-description")){
250 CalendarDescription = FetchXMLData(&NodeData);
254 // Get the calendar colour.
256 NodeData = xmlCopyNode(NodeMatch, 1);
258 Colour CalendarColour;
259 bool ColourResult = false;
261 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
262 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
263 if (MatchXMLNameTransverse(&NodeData, "calendar-color")){
265 string CalendarColourString = "";
266 string CalendarColourHexValue = "";
268 bool KeepRunning = true;
270 CalendarColourString = FetchXMLData(&NodeData);
272 while(KeepRunning == true){
274 if (CalendarColourString.substr(0,1) == "#" && CalendarColourString.length() == 9){
276 // Get the red colour.
278 CalendarColourHexValue = CalendarColourString.substr(1,2);
279 if (!HexToInt(&CalendarColourHexValue, &ColourNumber)){ break; }
280 CalendarColour.red = ColourNumber;
282 // Get the green colour.
284 CalendarColourHexValue = CalendarColourString.substr(3,2);
285 if (!HexToInt(&CalendarColourHexValue, &ColourNumber)){ break; }
286 CalendarColour.green = ColourNumber;
288 // Get the blue colour.
290 CalendarColourHexValue = CalendarColourString.substr(5,2);
291 if (!HexToInt(&CalendarColourHexValue, &ColourNumber)){ break; };
292 CalendarColour.blue = ColourNumber;
296 CalendarColourHexValue = CalendarColourString.substr(7,2);
297 if (!HexToInt(&CalendarColourHexValue, &ColourNumber)){ break; };
298 CalendarColour.alpha = ColourNumber;
304 ColourResult = false;
314 if (ColourResult == false){
316 CalendarColour.red = 0;
317 CalendarColour.blue = 0;
318 CalendarColour.green = 0;
319 CalendarColour.alpha = 0;
323 // Get the calendar order.
325 NodeData = xmlCopyNode(NodeMatch, 1);
327 int CalendarOrder = 0;
329 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
330 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
331 if (MatchXMLNameTransverse(&NodeData, "calendar-order")){
333 string CalendarOrderString = FetchXMLData(&NodeData);
334 if (!HexToInt(&CalendarOrderString, &CalendarOrder)){
340 // Get the calendar tag.
342 NodeData = xmlCopyNode(NodeMatch, 1);
344 string CalendarTag = "";
346 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
347 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
348 if (MatchXMLNameTransverse(&NodeData, "getctag")){
350 CalendarTag = FetchXMLData(&NodeData);
354 // Get the calendar tag URL.
356 NodeData = xmlCopyNode(NodeMatch, 1);
358 string CalendarTagURL = "";
360 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
361 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
362 if (MatchXMLNameTransverse(&NodeData, "sync-token")){
364 CalendarTagURL = FetchXMLData(&NodeData);
368 // Insert the calendar information into the
369 // list if all the information is there.
371 CalendarList.Name.insert(make_pair(ResponseCount, CalendarName));
372 CalendarList.Description.insert(make_pair(ResponseCount, CalendarDescription));
373 CalendarList.HREF.insert(make_pair(ResponseCount, HREFAddress));
374 CalendarList.CalColour.insert(make_pair(ResponseCount, CalendarColour));
375 CalendarList.Order.insert(make_pair(ResponseCount, CalendarOrder));
376 CalendarList.Tag.insert(make_pair(ResponseCount, CalendarTag));
377 CalendarList.TagURL.insert(make_pair(ResponseCount, CalendarTagURL));
385 xmlFreeDoc(xmlCalDAVDoc);
391 string CalDAV::ProcessXMLEntryETag(){
395 xmlDocPtr xmlCalDAVDoc;
396 xmlCalDAVDoc = xmlReadMemory(ServerData.c_str(), (int)ServerData.size(), "noname.xml", NULL, 0);
399 bool NodeFound = false;
401 // Start with the first node, look for multistatus.
403 for (NodeSeek = xmlCalDAVDoc->children;
405 NodeSeek = NodeSeek->next)
408 if (!xmlStrcmp(NodeSeek->name, (const xmlChar *)"multistatus") ||
409 !xmlStrcmp(NodeSeek->name, (const xmlChar *)"d:multistatus") ||
410 !xmlStrcmp(NodeSeek->name, (const xmlChar *)"D:multistatus")
420 if (NodeFound == false){
426 // Look for response.
428 if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
429 NodeFound = MatchXMLNameTransverse(&NodeSeek, "response");
431 // Look for propstat.
433 if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
434 NodeFound = MatchXMLNameTransverse(&NodeSeek, "propstat");
438 if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
439 NodeFound = MatchXMLNameTransverse(&NodeSeek, "prop");
441 // Look for calendar-home-set.
443 if (NodeFound == false){ return EntryETag; } else { NodeFound = false; }
444 NodeFound = MatchXMLNameTransverse(&NodeSeek, "getetag");
446 // Get the data from href.
448 EntryETag = FetchXMLData(&NodeSeek);
450 xmlFreeDoc(xmlCalDAVDoc);
452 // Check if the entity tag contains quote marks
453 // at the start and end and remove them (if needed).
455 if (EntryETag.substr(0,1) == "\"" &&
456 EntryETag.substr(EntryETag.size()-1, 1) == "\"" && EntryETag.size() > 2){
458 EntryETag.erase(EntryETag.begin());
459 EntryETag.erase(EntryETag.end()-1);
467 CalDAVEntryList CalDAV::ProcessXMLEntryList(){
469 CalDAVEntryList EntryList;
471 xmlDocPtr xmlCalDAVDoc;
472 xmlCalDAVDoc = xmlReadMemory(ServerData.c_str(), (int)ServerData.size(), "noname.xml", NULL, 0);
474 xmlNodePtr NodeSeek = NULL;
475 xmlNodePtr NodeResponse = NULL;
476 xmlNodePtr NodeMatch = NULL;
477 xmlNodePtr NodeData = NULL;
478 bool NodeFound = false;
479 int ResponseCount = 0;
481 // Start with the first node, look for multistatus.
483 for (NodeSeek = xmlCalDAVDoc->children;
485 NodeSeek = NodeSeek->next)
488 if (!xmlStrcmp(NodeSeek->name, (const xmlChar *)"multistatus") ||
489 !xmlStrcmp(NodeSeek->name, (const xmlChar *)"d:multistatus") ||
490 !xmlStrcmp(NodeSeek->name, (const xmlChar *)"D:multistatus")
493 NodeResponse = NodeSeek->children;
501 if (NodeFound == false){
507 for (NodeResponse = NodeSeek->children;
508 NodeResponse != nullptr;
509 NodeResponse = NodeResponse->next)
512 // Go through each of the responses and find the calendars.
514 NodeMatch = xmlCopyNode(NodeResponse, 1);
516 if (MatchXMLName(&NodeMatch, "response")){
518 NodeData = xmlCopyNode(NodeMatch, 1);
522 NodeData = xmlCopyNode(NodeMatch, 1);
524 if (!MatchXMLNameTransverse(&NodeData, "href")){ continue; }
526 string HREFAddress = FetchXMLData(&NodeData);
528 // Get the calendar data.
530 NodeData = xmlCopyNode(NodeMatch, 1);
532 string EntryDescription = "";
534 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
535 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
536 if (MatchXMLNameTransverse(&NodeData, "calendar-data")){
538 // Note: libxml2 will strip the CDATA part at the start and
539 // end of each calendar-data section.
541 EntryDescription = FetchXMLData(&NodeData);
545 // Get the entry entity tag.
547 NodeData = xmlCopyNode(NodeMatch, 1);
549 string EntryEntityTag = "";
551 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
552 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
553 if (MatchXMLNameTransverse(&NodeData, "getetag")){
555 EntryEntityTag = FetchXMLData(&NodeData);
559 // Insert the calendar information into the
560 // list if all the information is there.
562 EntryList.HREF.insert(make_pair(ResponseCount, HREFAddress));
563 EntryList.Data.insert(make_pair(ResponseCount, EntryDescription));
564 EntryList.Tag.insert(make_pair(ResponseCount, EntryEntityTag));
572 xmlFreeDoc(xmlCalDAVDoc);
578 CalDAVEntryList CalDAV::ProcessXMLSyncTokenList(){
580 CalDAVEntryList EntryList;
582 xmlDocPtr xmlCalDAVDoc;
583 xmlCalDAVDoc = xmlReadMemory(ServerData.c_str(), (int)ServerData.size(), "noname.xml", NULL, 0);
585 xmlNodePtr NodeSeek = NULL;
586 xmlNodePtr NodeResponse = NULL;
587 xmlNodePtr NodeMatch = NULL;
588 xmlNodePtr NodeData = NULL;
589 bool NodeFound = false;
590 int ResponseCount = 0;
592 // Start with the first node, look for multistatus.
594 for (NodeSeek = xmlCalDAVDoc->children;
596 NodeSeek = NodeSeek->next)
599 if (!xmlStrcmp(NodeSeek->name, (const xmlChar *)"multistatus") ||
600 !xmlStrcmp(NodeSeek->name, (const xmlChar *)"d:multistatus") ||
601 !xmlStrcmp(NodeSeek->name, (const xmlChar *)"D:multistatus")
604 NodeResponse = NodeSeek->children;
612 if (NodeFound == false){
618 for (NodeResponse = NodeSeek->children;
619 NodeResponse != nullptr;
620 NodeResponse = NodeResponse->next)
623 // Go through each of the responses and find the calendars.
625 NodeMatch = xmlCopyNode(NodeResponse, 1);
627 if (MatchXMLName(&NodeMatch, "response")){
629 NodeData = xmlCopyNode(NodeMatch, 1);
633 NodeData = xmlCopyNode(NodeMatch, 1);
635 if (!MatchXMLNameTransverse(&NodeData, "href")){ continue; }
637 string HREFAddress = FetchXMLData(&NodeData);
639 // Get the entry entity tag.
641 NodeData = xmlCopyNode(NodeMatch, 1);
643 string EntryEntityTag = "";
645 if (!MatchXMLNameTransverse(&NodeData, "propstat")){ continue; }
646 if (!MatchXMLNameTransverse(&NodeData, "prop")){ continue; }
647 if (MatchXMLNameTransverse(&NodeData, "getetag")){
649 EntryEntityTag = FetchXMLData(&NodeData);
653 // Insert the calendar information into the
654 // list if all the information is there.
656 EntryList.HREF.insert(make_pair(ResponseCount, HREFAddress));
657 EntryList.Data.insert(make_pair(ResponseCount, ""));
658 EntryList.Tag.insert(make_pair(ResponseCount, EntryEntityTag));
666 xmlFreeDoc(xmlCalDAVDoc);
672 bool CalDAV::MatchXMLNameTransverse(xmlNodePtr *NodePtr, string NodeName){
674 string NodeNameSmallD = "d:" + NodeName;
675 string NodeNameLargeD = "D:" + NodeName;
677 for ((*NodePtr) = (*NodePtr)->children;
679 (*NodePtr) = (*NodePtr)->next)
682 if (!xmlStrcmp((*NodePtr)->name, (const xmlChar *)NodeName.c_str()) ||
683 !xmlStrcmp((*NodePtr)->name, (const xmlChar *)NodeNameSmallD.c_str()) ||
684 !xmlStrcmp((*NodePtr)->name, (const xmlChar *)NodeNameLargeD.c_str())
697 bool CalDAV::MatchXMLName(xmlNodePtr *NodePtrOriginal, string NodeName){
699 if (NodePtrOriginal == nullptr){
705 string NodeNameSmallD = "d:" + NodeName;
706 string NodeNameLargeD = "D:" + NodeName;
708 xmlNodePtr *NodePtr = NodePtrOriginal;
710 if (!xmlStrcmp((*NodePtr)->name, (const xmlChar *)NodeName.c_str()) ||
711 !xmlStrcmp((*NodePtr)->name, (const xmlChar *)NodeNameSmallD.c_str()) ||
712 !xmlStrcmp((*NodePtr)->name, (const xmlChar *)NodeNameLargeD.c_str())
727 string CalDAV::FetchXMLData(xmlNodePtr *NodePtr){
729 for ((*NodePtr) = (*NodePtr)->children;
731 (*NodePtr) = (*NodePtr)->next)
734 return (const char*)(*NodePtr)->content;