This source file includes following definitions.
- pcmk__xml_tree_foreach
- pcmk__tracking_xml_changes
- pcmk__xml_set_parent_flags
- pcmk__set_xml_doc_flag
- pcmk__mark_xml_node_dirty
- pcmk__xml_reset_node_flags
- mark_xml_dirty_created
- mark_xml_tree_dirty_created
- free_deleted_object
- reset_xml_private_data
- new_private_data
- free_private_data
- pcmk__xml_new_private_data
- pcmk__xml_free_private_data
- xml_track_changes
- xml_tracking_changes
- xml_document_dirty
- pcmk__xml_position
- accept_attr_deletions
- pcmk__xml_match
- xml_accept_changes
- pcmk__xml_new_doc
- pcmk__xml_free_doc
- pcmk__xml_is_name_start_char
- pcmk__xml_is_name_char
- pcmk__xml_sanitize_id
- pcmk__xml_free_node
- free_xml_with_position
- pcmk__xml_free
- pcmk__xml_copy
- pcmk__strip_xml_text
- pcmk__xml_needs_escape
- pcmk__xml_escape
- set_attrs_flag
- mark_attr_deleted
- mark_attr_changed
- mark_attr_moved
- xml_diff_old_attrs
- mark_created_attrs
- xml_diff_attrs
- mark_child_deleted
- mark_child_moved
- mark_xml_changes
- xml_calculate_significant_changes
- xml_calculate_changes
- pcmk__xml_init
- pcmk__xml_cleanup
- pcmk__xml_artefact_root
- find_artefact
- pcmk__xml_artefact_path
- copy_xml
- crm_xml_init
- crm_xml_cleanup
- pcmk_free_xml_subtree
- free_xml
- crm_xml_sanitize_id
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdarg.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19
20 #include <glib.h>
21 #include <libxml/parser.h>
22 #include <libxml/tree.h>
23 #include <libxml/xmlstring.h>
24
25 #include <crm/crm.h>
26 #include <crm/common/xml.h>
27 #include <crm/common/xml_internal.h>
28 #include "crmcommon_private.h"
29
30
31 #define XML_VERSION ((pcmkXmlStr) "1.0")
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 bool
47 pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
48 void *user_data)
49 {
50 if (xml == NULL) {
51 return true;
52 }
53
54 if (!fn(xml, user_data)) {
55 return false;
56 }
57
58 for (xml = pcmk__xml_first_child(xml); xml != NULL;
59 xml = pcmk__xml_next(xml)) {
60
61 if (!pcmk__xml_tree_foreach(xml, fn, user_data)) {
62 return false;
63 }
64 }
65 return true;
66 }
67
68 bool
69 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
70 {
71 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
72 return FALSE;
73 } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
74 pcmk__xf_tracking)) {
75 return FALSE;
76 } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
77 pcmk__xf_lazy)) {
78 return FALSE;
79 }
80 return TRUE;
81 }
82
83 void
84 pcmk__xml_set_parent_flags(xmlNode *xml, uint64_t flags)
85 {
86 for (; xml != NULL; xml = xml->parent) {
87 xml_node_private_t *nodepriv = xml->_private;
88
89 if (nodepriv != NULL) {
90 pcmk__set_xml_flags(nodepriv, flags);
91 }
92 }
93 }
94
95 void
96 pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
97 {
98 if (xml != NULL) {
99 xml_doc_private_t *docpriv = xml->doc->_private;
100
101 pcmk__set_xml_flags(docpriv, flag);
102 }
103 }
104
105
106 void
107 pcmk__mark_xml_node_dirty(xmlNode *xml)
108 {
109 pcmk__set_xml_doc_flag(xml, pcmk__xf_dirty);
110 pcmk__xml_set_parent_flags(xml, pcmk__xf_dirty);
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124 bool
125 pcmk__xml_reset_node_flags(xmlNode *xml, void *user_data)
126 {
127 xml_node_private_t *nodepriv = xml->_private;
128
129 if (nodepriv != NULL) {
130 nodepriv->flags = pcmk__xf_none;
131 }
132 return true;
133 }
134
135
136
137
138
139
140
141
142
143
144
145
146 static bool
147 mark_xml_dirty_created(xmlNode *xml, void *user_data)
148 {
149 xml_node_private_t *nodepriv = xml->_private;
150
151 if (nodepriv != NULL) {
152 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created);
153 }
154 return true;
155 }
156
157
158
159
160
161
162
163
164
165 static void
166 mark_xml_tree_dirty_created(xmlNode *xml)
167 {
168 pcmk__assert(xml != NULL);
169
170 if (!pcmk__tracking_xml_changes(xml, false)) {
171
172 return;
173 }
174
175
176 pcmk__mark_xml_node_dirty(xml);
177
178 pcmk__xml_tree_foreach(xml, mark_xml_dirty_created, NULL);
179 }
180
181
182 static void
183 free_deleted_object(void *data)
184 {
185 if(data) {
186 pcmk__deleted_xml_t *deleted_obj = data;
187
188 g_free(deleted_obj->path);
189 free(deleted_obj);
190 }
191 }
192
193
194 static void
195 reset_xml_private_data(xml_doc_private_t *docpriv)
196 {
197 if (docpriv != NULL) {
198 pcmk__assert(docpriv->check == PCMK__XML_DOC_PRIVATE_MAGIC);
199
200 free(docpriv->user);
201 docpriv->user = NULL;
202
203 if (docpriv->acls != NULL) {
204 pcmk__free_acls(docpriv->acls);
205 docpriv->acls = NULL;
206 }
207
208 if(docpriv->deleted_objs) {
209 g_list_free_full(docpriv->deleted_objs, free_deleted_object);
210 docpriv->deleted_objs = NULL;
211 }
212 }
213 }
214
215
216
217
218
219
220
221
222
223
224
225
226 static bool
227 new_private_data(xmlNode *node, void *user_data)
228 {
229 CRM_CHECK(node != NULL, return true);
230
231 if (node->_private != NULL) {
232 return true;
233 }
234
235 switch (node->type) {
236 case XML_DOCUMENT_NODE:
237 {
238 xml_doc_private_t *docpriv =
239 pcmk__assert_alloc(1, sizeof(xml_doc_private_t));
240
241 docpriv->check = PCMK__XML_DOC_PRIVATE_MAGIC;
242 node->_private = docpriv;
243 pcmk__set_xml_flags(docpriv, pcmk__xf_dirty|pcmk__xf_created);
244 }
245 break;
246
247 case XML_ELEMENT_NODE:
248 case XML_ATTRIBUTE_NODE:
249 case XML_COMMENT_NODE:
250 {
251 xml_node_private_t *nodepriv =
252 pcmk__assert_alloc(1, sizeof(xml_node_private_t));
253
254 nodepriv->check = PCMK__XML_NODE_PRIVATE_MAGIC;
255 node->_private = nodepriv;
256 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created);
257
258 for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
259 iter = iter->next) {
260
261 new_private_data((xmlNode *) iter, user_data);
262 }
263 }
264 break;
265
266 case XML_TEXT_NODE:
267 case XML_DTD_NODE:
268 case XML_CDATA_SECTION_NODE:
269 return true;
270
271 default:
272 CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
273 return true;
274 }
275
276 if (pcmk__tracking_xml_changes(node, false)) {
277 pcmk__mark_xml_node_dirty(node);
278 }
279 return true;
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293 static bool
294 free_private_data(xmlNode *node, void *user_data)
295 {
296 CRM_CHECK(node != NULL, return true);
297
298 if (node->_private == NULL) {
299 return true;
300 }
301
302 if (node->type == XML_DOCUMENT_NODE) {
303 reset_xml_private_data((xml_doc_private_t *) node->_private);
304
305 } else {
306 xml_node_private_t *nodepriv = node->_private;
307
308 pcmk__assert(nodepriv->check == PCMK__XML_NODE_PRIVATE_MAGIC);
309
310 for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
311 iter = iter->next) {
312
313 free_private_data((xmlNode *) iter, user_data);
314 }
315 }
316 free(node->_private);
317 node->_private = NULL;
318 return true;
319 }
320
321
322
323
324
325
326
327 void
328 pcmk__xml_new_private_data(xmlNode *xml)
329 {
330 pcmk__xml_tree_foreach(xml, new_private_data, NULL);
331 }
332
333
334
335
336
337
338
339 void
340 pcmk__xml_free_private_data(xmlNode *xml)
341 {
342 pcmk__xml_tree_foreach(xml, free_private_data, NULL);
343 }
344
345 void
346 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
347 {
348 xml_accept_changes(xml);
349 crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
350 pcmk__set_xml_doc_flag(xml, pcmk__xf_tracking);
351 if(enforce_acls) {
352 if(acl_source == NULL) {
353 acl_source = xml;
354 }
355 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_enabled);
356 pcmk__unpack_acl(acl_source, xml, user);
357 pcmk__apply_acl(xml);
358 }
359 }
360
361 bool xml_tracking_changes(xmlNode * xml)
362 {
363 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
364 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
365 pcmk__xf_tracking);
366 }
367
368 bool xml_document_dirty(xmlNode *xml)
369 {
370 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
371 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
372 pcmk__xf_dirty);
373 }
374
375
376
377
378
379
380
381
382
383
384 int
385 pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
386 {
387 int position = 0;
388
389 for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
390 xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
391
392 if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
393 position++;
394 }
395 }
396
397 return position;
398 }
399
400
401
402
403
404
405
406
407
408
409
410
411 static bool
412 accept_attr_deletions(xmlNode *xml, void *user_data)
413 {
414 pcmk__xml_reset_node_flags(xml, NULL);
415 pcmk__xe_remove_matching_attrs(xml, pcmk__marked_as_deleted, NULL);
416 return true;
417 }
418
419
420
421
422
423
424
425
426
427 xmlNode *
428 pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
429 {
430 CRM_CHECK(needle != NULL, return NULL);
431
432 if (needle->type == XML_COMMENT_NODE) {
433 return pcmk__xc_match(haystack, needle, exact);
434
435 } else {
436 const char *id = pcmk__xe_id(needle);
437 const char *attr = (id == NULL)? NULL : PCMK_XA_ID;
438
439 return pcmk__xe_first_child(haystack, (const char *) needle->name, attr,
440 id);
441 }
442 }
443
444 void
445 xml_accept_changes(xmlNode * xml)
446 {
447 xmlNode *top = NULL;
448 xml_doc_private_t *docpriv = NULL;
449
450 if(xml == NULL) {
451 return;
452 }
453
454 crm_trace("Accepting changes to %p", xml);
455 docpriv = xml->doc->_private;
456 top = xmlDocGetRootElement(xml->doc);
457
458 reset_xml_private_data(xml->doc->_private);
459
460 if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
461 docpriv->flags = pcmk__xf_none;
462 return;
463 }
464
465 docpriv->flags = pcmk__xf_none;
466 pcmk__xml_tree_foreach(top, accept_attr_deletions, NULL);
467 }
468
469
470
471
472
473
474
475
476
477
478 xmlDoc *
479 pcmk__xml_new_doc(void)
480 {
481 xmlDoc *doc = xmlNewDoc(XML_VERSION);
482
483 pcmk__mem_assert(doc);
484 pcmk__xml_new_private_data((xmlNode *) doc);
485 return doc;
486 }
487
488
489
490
491
492
493
494 void
495 pcmk__xml_free_doc(xmlDoc *doc)
496 {
497 if (doc != NULL) {
498 pcmk__xml_free_private_data((xmlNode *) doc);
499 xmlFreeDoc(doc);
500 }
501 }
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519 bool
520 pcmk__xml_is_name_start_char(const char *utf8, int *len)
521 {
522 int c = 0;
523 int local_len = 0;
524
525 if (len == NULL) {
526 len = &local_len;
527 }
528
529
530
531
532
533
534
535
536
537
538
539
540 *len = 4;
541
542
543 c = xmlGetUTF8Char((pcmkXmlStr) utf8, len);
544 if (c < 0) {
545 GString *buf = g_string_sized_new(32);
546
547 for (int i = 0; (i < 4) && (utf8[i] != '\0'); i++) {
548 g_string_append_printf(buf, " 0x%.2X", utf8[i]);
549 }
550 crm_info("Invalid UTF-8 character (bytes:%s)",
551 (pcmk__str_empty(buf->str)? " <none>" : buf->str));
552 g_string_free(buf, TRUE);
553 return false;
554 }
555
556 return (c == '_')
557 || (c == ':')
558 || ((c >= 'a') && (c <= 'z'))
559 || ((c >= 'A') && (c <= 'Z'))
560 || ((c >= 0xC0) && (c <= 0xD6))
561 || ((c >= 0xD8) && (c <= 0xF6))
562 || ((c >= 0xF8) && (c <= 0x2FF))
563 || ((c >= 0x370) && (c <= 0x37D))
564 || ((c >= 0x37F) && (c <= 0x1FFF))
565 || ((c >= 0x200C) && (c <= 0x200D))
566 || ((c >= 0x2070) && (c <= 0x218F))
567 || ((c >= 0x2C00) && (c <= 0x2FEF))
568 || ((c >= 0x3001) && (c <= 0xD7FF))
569 || ((c >= 0xF900) && (c <= 0xFDCF))
570 || ((c >= 0xFDF0) && (c <= 0xFFFD))
571 || ((c >= 0x10000) && (c <= 0xEFFFF));
572 }
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590 bool
591 pcmk__xml_is_name_char(const char *utf8, int *len)
592 {
593 int c = 0;
594 int local_len = 0;
595
596 if (len == NULL) {
597 len = &local_len;
598 }
599
600
601 *len = 4;
602
603
604 c = xmlGetUTF8Char((pcmkXmlStr) utf8, len);
605 if (c < 0) {
606 GString *buf = g_string_sized_new(32);
607
608 for (int i = 0; (i < 4) && (utf8[i] != '\0'); i++) {
609 g_string_append_printf(buf, " 0x%.2X", utf8[i]);
610 }
611 crm_info("Invalid UTF-8 character (bytes:%s)",
612 (pcmk__str_empty(buf->str)? " <none>" : buf->str));
613 g_string_free(buf, TRUE);
614 return false;
615 }
616
617 return ((c >= 'a') && (c <= 'z'))
618 || ((c >= 'A') && (c <= 'Z'))
619 || ((c >= '0') && (c <= '9'))
620 || (c == '_')
621 || (c == ':')
622 || (c == '-')
623 || (c == '.')
624 || (c == 0xB7)
625 || ((c >= 0xC0) && (c <= 0xD6))
626 || ((c >= 0xD8) && (c <= 0xF6))
627 || ((c >= 0xF8) && (c <= 0x2FF))
628 || ((c >= 0x300) && (c <= 0x36F))
629 || ((c >= 0x370) && (c <= 0x37D))
630 || ((c >= 0x37F) && (c <= 0x1FFF))
631 || ((c >= 0x200C) && (c <= 0x200D))
632 || ((c >= 0x203F) && (c <= 0x2040))
633 || ((c >= 0x2070) && (c <= 0x218F))
634 || ((c >= 0x2C00) && (c <= 0x2FEF))
635 || ((c >= 0x3001) && (c <= 0xD7FF))
636 || ((c >= 0xF900) && (c <= 0xFDCF))
637 || ((c >= 0xFDF0) && (c <= 0xFFFD))
638 || ((c >= 0x10000) && (c <= 0xEFFFF));
639 }
640
641
642
643
644
645
646
647
648
649
650
651
652
653 void
654 pcmk__xml_sanitize_id(char *id)
655 {
656 bool valid = true;
657 int len = 0;
658
659
660 pcmk__assert(!pcmk__str_empty(id));
661
662
663
664
665
666
667
668
669 valid = pcmk__xml_is_name_start_char(id, &len);
670 CRM_CHECK(len > 0, return);
671 if (!valid) {
672 *id = '_';
673 for (int i = 1; i < len; i++) {
674 id[i] = '.';
675 }
676 }
677
678 for (id += len; *id != '\0'; id += len) {
679 valid = pcmk__xml_is_name_char(id, &len);
680 CRM_CHECK(len > 0, return);
681 if (!valid) {
682 for (int i = 0; i < len; i++) {
683 id[i] = '.';
684 }
685 }
686 }
687 }
688
689
690
691
692
693
694
695 void
696 pcmk__xml_free_node(xmlNode *xml)
697 {
698 pcmk__xml_free_private_data(xml);
699 xmlUnlinkNode(xml);
700 xmlFreeNode(xml);
701 }
702
703
704
705
706
707
708
709
710
711
712
713
714 static void
715 free_xml_with_position(xmlNode *node, int position)
716 {
717 xmlDoc *doc = NULL;
718 xml_node_private_t *nodepriv = NULL;
719
720 if (node == NULL) {
721 return;
722 }
723 doc = node->doc;
724 nodepriv = node->_private;
725
726 if ((doc != NULL) && (xmlDocGetRootElement(doc) == node)) {
727
728
729
730 pcmk__xml_free_doc(doc);
731 return;
732 }
733
734 if (!pcmk__check_acl(node, NULL, pcmk__xf_acl_write)) {
735 GString *xpath = NULL;
736
737 pcmk__if_tracing({}, return);
738 xpath = pcmk__element_xpath(node);
739 qb_log_from_external_source(__func__, __FILE__,
740 "Cannot remove %s %x", LOG_TRACE,
741 __LINE__, 0, xpath->str, nodepriv->flags);
742 g_string_free(xpath, TRUE);
743 return;
744 }
745
746 if ((doc != NULL) && pcmk__tracking_xml_changes(node, false)
747 && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
748
749 xml_doc_private_t *docpriv = doc->_private;
750 GString *xpath = pcmk__element_xpath(node);
751
752 if (xpath != NULL) {
753 pcmk__deleted_xml_t *deleted_obj = NULL;
754
755 crm_trace("Deleting %s %p from %p", xpath->str, node, doc);
756
757 deleted_obj = pcmk__assert_alloc(1, sizeof(pcmk__deleted_xml_t));
758 deleted_obj->path = g_string_free(xpath, FALSE);
759 deleted_obj->position = -1;
760
761
762 if (node->type == XML_COMMENT_NODE) {
763 if (position >= 0) {
764 deleted_obj->position = position;
765
766 } else {
767 deleted_obj->position = pcmk__xml_position(node,
768 pcmk__xf_skip);
769 }
770 }
771
772 docpriv->deleted_objs = g_list_append(docpriv->deleted_objs,
773 deleted_obj);
774 pcmk__set_xml_doc_flag(node, pcmk__xf_dirty);
775 }
776 }
777 pcmk__xml_free_node(node);
778 }
779
780
781
782
783
784
785
786
787
788 void
789 pcmk__xml_free(xmlNode *xml)
790 {
791 free_xml_with_position(xml, -1);
792 }
793
794
795
796
797
798
799
800
801
802
803
804 xmlNode *
805 pcmk__xml_copy(xmlNode *parent, xmlNode *src)
806 {
807 xmlNode *copy = NULL;
808
809 if (src == NULL) {
810 return NULL;
811 }
812
813 if (parent == NULL) {
814 xmlDoc *doc = NULL;
815
816
817 pcmk__assert(src->type == XML_ELEMENT_NODE);
818
819 doc = pcmk__xml_new_doc();
820 copy = xmlDocCopyNode(src, doc, 1);
821 pcmk__mem_assert(copy);
822
823 xmlDocSetRootElement(doc, copy);
824
825 } else {
826 copy = xmlDocCopyNode(src, parent->doc, 1);
827 pcmk__mem_assert(copy);
828
829 xmlAddChild(parent, copy);
830 }
831
832 pcmk__xml_new_private_data(copy);
833 return copy;
834 }
835
836
837
838
839
840
841
842 void
843 pcmk__strip_xml_text(xmlNode *xml)
844 {
845 xmlNode *iter = xml->children;
846
847 while (iter) {
848 xmlNode *next = iter->next;
849
850 switch (iter->type) {
851 case XML_TEXT_NODE:
852 pcmk__xml_free_node(iter);
853 break;
854
855 case XML_ELEMENT_NODE:
856
857 pcmk__strip_xml_text(iter);
858 break;
859
860 default:
861
862 break;
863 }
864
865 iter = next;
866 }
867 }
868
869
870
871
872
873
874
875
876
877
878
879
880
881 bool
882 pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
883 {
884 if (text == NULL) {
885 return false;
886 }
887
888 while (*text != '\0') {
889 switch (type) {
890 case pcmk__xml_escape_text:
891 switch (*text) {
892 case '<':
893 case '>':
894 case '&':
895 return true;
896 case '\n':
897 case '\t':
898 break;
899 default:
900 if (g_ascii_iscntrl(*text)) {
901 return true;
902 }
903 break;
904 }
905 break;
906
907 case pcmk__xml_escape_attr:
908 switch (*text) {
909 case '<':
910 case '>':
911 case '&':
912 case '"':
913 return true;
914 default:
915 if (g_ascii_iscntrl(*text)) {
916 return true;
917 }
918 break;
919 }
920 break;
921
922 case pcmk__xml_escape_attr_pretty:
923 switch (*text) {
924 case '\n':
925 case '\r':
926 case '\t':
927 case '"':
928 return true;
929 default:
930 break;
931 }
932 break;
933
934 default:
935 pcmk__assert(false);
936 break;
937 }
938
939 text = g_utf8_next_char(text);
940 }
941 return false;
942 }
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963 gchar *
964 pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
965 {
966 GString *copy = NULL;
967
968 if (text == NULL) {
969 return NULL;
970 }
971 copy = g_string_sized_new(strlen(text));
972
973 while (*text != '\0') {
974
975 if ((*text & 0x80) != 0) {
976 size_t bytes = g_utf8_next_char(text) - text;
977
978 g_string_append_len(copy, text, bytes);
979 text += bytes;
980 continue;
981 }
982
983 switch (type) {
984 case pcmk__xml_escape_text:
985 switch (*text) {
986 case '<':
987 g_string_append(copy, PCMK__XML_ENTITY_LT);
988 break;
989 case '>':
990 g_string_append(copy, PCMK__XML_ENTITY_GT);
991 break;
992 case '&':
993 g_string_append(copy, PCMK__XML_ENTITY_AMP);
994 break;
995 case '\n':
996 case '\t':
997 g_string_append_c(copy, *text);
998 break;
999 default:
1000 if (g_ascii_iscntrl(*text)) {
1001 g_string_append_printf(copy, "&#x%.2X;", *text);
1002 } else {
1003 g_string_append_c(copy, *text);
1004 }
1005 break;
1006 }
1007 break;
1008
1009 case pcmk__xml_escape_attr:
1010 switch (*text) {
1011 case '<':
1012 g_string_append(copy, PCMK__XML_ENTITY_LT);
1013 break;
1014 case '>':
1015 g_string_append(copy, PCMK__XML_ENTITY_GT);
1016 break;
1017 case '&':
1018 g_string_append(copy, PCMK__XML_ENTITY_AMP);
1019 break;
1020 case '"':
1021 g_string_append(copy, PCMK__XML_ENTITY_QUOT);
1022 break;
1023 default:
1024 if (g_ascii_iscntrl(*text)) {
1025 g_string_append_printf(copy, "&#x%.2X;", *text);
1026 } else {
1027 g_string_append_c(copy, *text);
1028 }
1029 break;
1030 }
1031 break;
1032
1033 case pcmk__xml_escape_attr_pretty:
1034 switch (*text) {
1035 case '"':
1036 g_string_append(copy, "\\\"");
1037 break;
1038 case '\n':
1039 g_string_append(copy, "\\n");
1040 break;
1041 case '\r':
1042 g_string_append(copy, "\\r");
1043 break;
1044 case '\t':
1045 g_string_append(copy, "\\t");
1046 break;
1047 default:
1048 g_string_append_c(copy, *text);
1049 break;
1050 }
1051 break;
1052
1053 default:
1054 pcmk__assert(false);
1055 break;
1056 }
1057
1058 text = g_utf8_next_char(text);
1059 }
1060 return g_string_free(copy, FALSE);
1061 }
1062
1063
1064
1065
1066
1067
1068
1069
1070 static void
1071 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
1072 {
1073 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
1074 pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
1075 }
1076 }
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092 static void
1093 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
1094 const char *old_value)
1095 {
1096 xml_doc_private_t *docpriv = new_xml->doc->_private;
1097 xmlAttr *attr = NULL;
1098 xml_node_private_t *nodepriv;
1099
1100
1101
1102
1103 pcmk__clear_xml_flags(docpriv, pcmk__xf_tracking);
1104 crm_xml_add(new_xml, attr_name, old_value);
1105 pcmk__set_xml_flags(docpriv, pcmk__xf_tracking);
1106
1107
1108 attr = xmlHasProp(new_xml, (pcmkXmlStr) attr_name);
1109 nodepriv = attr->_private;
1110 nodepriv->flags = 0;
1111
1112
1113 pcmk__xa_remove(attr, false);
1114
1115 crm_trace("XML attribute %s=%s was removed from %s",
1116 attr_name, old_value, element);
1117 }
1118
1119
1120
1121
1122
1123 static void
1124 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
1125 const char *old_value)
1126 {
1127 xml_doc_private_t *docpriv = new_xml->doc->_private;
1128 char *vcopy = crm_element_value_copy(new_xml, attr_name);
1129
1130 crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
1131 attr_name, old_value, vcopy, element);
1132
1133
1134 pcmk__clear_xml_flags(docpriv, pcmk__xf_tracking);
1135 crm_xml_add(new_xml, attr_name, old_value);
1136 pcmk__set_xml_flags(docpriv, pcmk__xf_tracking);
1137
1138
1139 crm_xml_add(new_xml, attr_name, vcopy);
1140 free(vcopy);
1141 }
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 static void
1155 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
1156 xmlAttr *new_attr, int p_old, int p_new)
1157 {
1158 xml_node_private_t *nodepriv = new_attr->_private;
1159
1160 crm_trace("XML attribute %s moved from position %d to %d in %s",
1161 old_attr->name, p_old, p_new, element);
1162
1163
1164 pcmk__mark_xml_node_dirty(new_xml);
1165
1166
1167 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_moved);
1168
1169 nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1170 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
1171 }
1172
1173
1174
1175
1176
1177
1178
1179
1180 static void
1181 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1182 {
1183 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1184
1185 while (attr_iter != NULL) {
1186 const char *name = (const char *) attr_iter->name;
1187 xmlAttr *old_attr = attr_iter;
1188 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1189 const char *old_value = pcmk__xml_attr_value(attr_iter);
1190
1191 attr_iter = attr_iter->next;
1192 if (new_attr == NULL) {
1193 mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
1194 old_value);
1195
1196 } else {
1197 xml_node_private_t *nodepriv = new_attr->_private;
1198 int new_pos = pcmk__xml_position((xmlNode*) new_attr,
1199 pcmk__xf_skip);
1200 int old_pos = pcmk__xml_position((xmlNode*) old_attr,
1201 pcmk__xf_skip);
1202 const char *new_value = crm_element_value(new_xml, name);
1203
1204
1205 pcmk__clear_xml_flags(nodepriv, pcmk__xf_created);
1206
1207 if (strcmp(new_value, old_value) != 0) {
1208 mark_attr_changed(new_xml, (const char *) old_xml->name, name,
1209 old_value);
1210
1211 } else if ((old_pos != new_pos)
1212 && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
1213 mark_attr_moved(new_xml, (const char *) old_xml->name,
1214 old_attr, new_attr, old_pos, new_pos);
1215 }
1216 }
1217 }
1218 }
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229 static void
1230 mark_created_attrs(xmlNode *new_xml)
1231 {
1232 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1233
1234 while (attr_iter != NULL) {
1235 xmlAttr *new_attr = attr_iter;
1236 xml_node_private_t *nodepriv = attr_iter->_private;
1237
1238 attr_iter = attr_iter->next;
1239 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1240 const char *attr_name = (const char *) new_attr->name;
1241
1242 crm_trace("Created new attribute %s=%s in %s",
1243 attr_name, pcmk__xml_attr_value(new_attr),
1244 new_xml->name);
1245
1246
1247
1248
1249 if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
1250 pcmk__mark_xml_attr_dirty(new_attr);
1251 } else {
1252
1253 pcmk__xa_remove(new_attr, true);
1254 }
1255 }
1256 }
1257 }
1258
1259
1260
1261
1262
1263
1264
1265
1266 static void
1267 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1268 {
1269 set_attrs_flag(new_xml, pcmk__xf_created);
1270 xml_diff_old_attrs(old_xml, new_xml);
1271 mark_created_attrs(new_xml);
1272 }
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286 static void
1287 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1288 {
1289
1290 xmlNode *candidate = pcmk__xml_copy(new_parent, old_child);
1291
1292
1293 pcmk__xml_tree_foreach(candidate, pcmk__xml_reset_node_flags, NULL);
1294
1295
1296 pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
1297
1298
1299 free_xml_with_position(candidate,
1300 pcmk__xml_position(old_child, pcmk__xf_skip));
1301
1302 if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
1303 pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
1304 pcmk__xf_skip);
1305 }
1306 }
1307
1308 static void
1309 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
1310 int p_old, int p_new)
1311 {
1312 xml_node_private_t *nodepriv = new_child->_private;
1313
1314 crm_trace("Child element %s with "
1315 PCMK_XA_ID "='%s' moved from position %d to %d under %s",
1316 new_child->name, pcmk__s(pcmk__xe_id(new_child), "<no id>"),
1317 p_old, p_new, new_parent->name);
1318 pcmk__mark_xml_node_dirty(new_parent);
1319 pcmk__set_xml_flags(nodepriv, pcmk__xf_moved);
1320
1321 if (p_old > p_new) {
1322 nodepriv = old_child->_private;
1323 } else {
1324 nodepriv = new_child->_private;
1325 }
1326 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
1327 }
1328
1329
1330 static void
1331 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
1332 {
1333 xmlNode *old_child = NULL;
1334 xmlNode *new_child = NULL;
1335 xml_node_private_t *nodepriv = NULL;
1336
1337 CRM_CHECK(new_xml != NULL, return);
1338 if (old_xml == NULL) {
1339 mark_xml_tree_dirty_created(new_xml);
1340 pcmk__apply_creation_acl(new_xml, check_top);
1341 return;
1342 }
1343
1344 nodepriv = new_xml->_private;
1345 CRM_CHECK(nodepriv != NULL, return);
1346
1347 if(nodepriv->flags & pcmk__xf_processed) {
1348
1349 return;
1350 }
1351 pcmk__set_xml_flags(nodepriv, pcmk__xf_processed);
1352
1353 xml_diff_attrs(old_xml, new_xml);
1354
1355
1356 for (old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
1357 old_child = pcmk__xml_next(old_child)) {
1358
1359 new_child = pcmk__xml_match(new_xml, old_child, true);
1360
1361 if (new_child != NULL) {
1362 mark_xml_changes(old_child, new_child, true);
1363
1364 } else {
1365 mark_child_deleted(old_child, new_xml);
1366 }
1367 }
1368
1369
1370 new_child = pcmk__xml_first_child(new_xml);
1371 while (new_child != NULL) {
1372 xmlNode *next = pcmk__xml_next(new_child);
1373
1374 old_child = pcmk__xml_match(old_xml, new_child, true);
1375
1376 if (old_child == NULL) {
1377
1378 nodepriv = new_child->_private;
1379 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
1380
1381
1382 mark_xml_changes(old_child, new_child, true);
1383
1384 } else {
1385
1386 int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
1387 int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
1388
1389 if(p_old != p_new) {
1390 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
1391 }
1392 }
1393
1394 new_child = next;
1395 }
1396 }
1397
1398 void
1399 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
1400 {
1401 pcmk__set_xml_doc_flag(new_xml, pcmk__xf_lazy);
1402 xml_calculate_changes(old_xml, new_xml);
1403 }
1404
1405
1406 void
1407 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
1408 {
1409 CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
1410 && pcmk__xe_is(old_xml, (const char *) new_xml->name)
1411 && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
1412 pcmk__str_none),
1413 return);
1414
1415 if(xml_tracking_changes(new_xml) == FALSE) {
1416 xml_track_changes(new_xml, NULL, NULL, FALSE);
1417 }
1418
1419 mark_xml_changes(old_xml, new_xml, FALSE);
1420 }
1421
1422
1423
1424
1425
1426
1427
1428
1429 void
1430 pcmk__xml_init(void)
1431 {
1432
1433 static bool initialized = false;
1434
1435 if (!initialized) {
1436 initialized = true;
1437
1438
1439
1440
1441
1442 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
1443
1444
1445 pcmk__schema_init();
1446 }
1447 }
1448
1449
1450
1451
1452
1453
1454
1455 void
1456 pcmk__xml_cleanup(void)
1457 {
1458 pcmk__schema_cleanup();
1459 xmlCleanupParser();
1460 }
1461
1462 char *
1463 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
1464 {
1465 static const char *base = NULL;
1466 char *ret = NULL;
1467
1468 if (base == NULL) {
1469 base = pcmk__env_option(PCMK__ENV_SCHEMA_DIRECTORY);
1470 }
1471 if (pcmk__str_empty(base)) {
1472 base = PCMK_SCHEMA_DIR;
1473 }
1474
1475 switch (ns) {
1476 case pcmk__xml_artefact_ns_legacy_rng:
1477 case pcmk__xml_artefact_ns_legacy_xslt:
1478 ret = strdup(base);
1479 break;
1480 case pcmk__xml_artefact_ns_base_rng:
1481 case pcmk__xml_artefact_ns_base_xslt:
1482 ret = crm_strdup_printf("%s/base", base);
1483 break;
1484 default:
1485 crm_err("XML artefact family specified as %u not recognized", ns);
1486 }
1487 return ret;
1488 }
1489
1490 static char *
1491 find_artefact(enum pcmk__xml_artefact_ns ns, const char *path, const char *filespec)
1492 {
1493 char *ret = NULL;
1494
1495 switch (ns) {
1496 case pcmk__xml_artefact_ns_legacy_rng:
1497 case pcmk__xml_artefact_ns_base_rng:
1498 if (pcmk__ends_with(filespec, ".rng")) {
1499 ret = crm_strdup_printf("%s/%s", path, filespec);
1500 } else {
1501 ret = crm_strdup_printf("%s/%s.rng", path, filespec);
1502 }
1503 break;
1504 case pcmk__xml_artefact_ns_legacy_xslt:
1505 case pcmk__xml_artefact_ns_base_xslt:
1506 if (pcmk__ends_with(filespec, ".xsl")) {
1507 ret = crm_strdup_printf("%s/%s", path, filespec);
1508 } else {
1509 ret = crm_strdup_printf("%s/%s.xsl", path, filespec);
1510 }
1511 break;
1512 default:
1513 crm_err("XML artefact family specified as %u not recognized", ns);
1514 }
1515
1516 return ret;
1517 }
1518
1519 char *
1520 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
1521 {
1522 struct stat sb;
1523 char *base = pcmk__xml_artefact_root(ns);
1524 char *ret = NULL;
1525
1526 ret = find_artefact(ns, base, filespec);
1527 free(base);
1528
1529 if (stat(ret, &sb) != 0 || !S_ISREG(sb.st_mode)) {
1530 const char *remote_schema_dir = pcmk__remote_schema_dir();
1531
1532 free(ret);
1533 ret = find_artefact(ns, remote_schema_dir, filespec);
1534 }
1535
1536 return ret;
1537 }
1538
1539
1540
1541
1542 #include <crm/common/xml_compat.h>
1543
1544 xmlNode *
1545 copy_xml(xmlNode *src)
1546 {
1547 xmlDoc *doc = pcmk__xml_new_doc();
1548 xmlNode *copy = NULL;
1549
1550 copy = xmlDocCopyNode(src, doc, 1);
1551 pcmk__mem_assert(copy);
1552
1553 xmlDocSetRootElement(doc, copy);
1554 pcmk__xml_new_private_data(copy);
1555 return copy;
1556 }
1557
1558 void
1559 crm_xml_init(void)
1560 {
1561 pcmk__xml_init();
1562 }
1563
1564 void
1565 crm_xml_cleanup(void)
1566 {
1567 pcmk__xml_cleanup();
1568 }
1569
1570 void
1571 pcmk_free_xml_subtree(xmlNode *xml)
1572 {
1573 pcmk__xml_free_node(xml);
1574 }
1575
1576 void
1577 free_xml(xmlNode *child)
1578 {
1579 pcmk__xml_free(child);
1580 }
1581
1582 void
1583 crm_xml_sanitize_id(char *id)
1584 {
1585 char *c;
1586
1587 for (c = id; *c; ++c) {
1588 switch (*c) {
1589 case ':':
1590 case '#':
1591 *c = '.';
1592 }
1593 }
1594 }
1595
1596
1597