This source file includes following definitions.
- pcmk__xml_tree_foreach
- pcmk__tracking_xml_changes
- set_parent_flag
- pcmk__set_xml_doc_flag
- pcmk__mark_xml_node_dirty
- reset_xml_node_flags
- mark_xml_dirty_created
- pcmk__xml_mark_created
- free_deleted_object
- reset_xml_private_data
- free_private_data
- new_private_data
- xml_track_changes
- xml_tracking_changes
- xml_document_dirty
- pcmk__xml_position
- accept_attr_deletions
- pcmk__xml_match
- xml_accept_changes
- pcmk__xe_first_child
- pcmk__xe_set_score
- pcmk__xe_copy_attrs
- remove_xe_attr
- pcmk__xe_remove_attr
- pcmk__xe_remove_attr_cb
- pcmk__xe_remove_matching_attrs
- pcmk__xe_create
- G_GNUC_PRINTF
- pcmk_free_xml_subtree
- free_xml_with_position
- free_xml
- pcmk__xml_copy
- pcmk__strip_xml_text
- pcmk__xe_add_last_written
- crm_xml_sanitize_id
- crm_xml_set_id
- 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__xc_match
- pcmk__xc_update
- pcmk__xml_update
- delete_xe_if_matching
- pcmk__xe_delete_match
- replace_node
- replace_xe_if_matching
- pcmk__xe_replace_match
- update_xe_if_matching
- pcmk__xe_update_match
- sorted_xml
- pcmk__xe_next_same
- crm_xml_init
- crm_xml_cleanup
- expand_idref
- pcmk__xml_artefact_root
- find_artefact
- pcmk__xml_artefact_path
- pcmk__xe_set_propv
- pcmk__xe_set_props
- pcmk__xe_foreach_child
- find_entity
- crm_destroy_xml
- getDocPtr
- add_node_copy
- add_node_nocopy
- xml_has_children
- replace_text
- crm_xml_escape
- copy_xml
- create_xml_node
- pcmk_create_xml_text_node
- pcmk_create_html_node
- first_named_child
- find_xml_node
- crm_next_same_xml
- xml_remove_prop
- replace_xml_child
- update_xml_child
- find_xml_children
- fix_plus_plus_recursive
- copy_in_properties
- expand_plus_plus
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 <libxml/parser.h>
21 #include <libxml/tree.h>
22
23 #include <crm/crm.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/xml_internal.h>
26 #include "crmcommon_private.h"
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 bool
42 pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
43 void *user_data)
44 {
45 if (!fn(xml, user_data)) {
46 return false;
47 }
48
49 for (xml = pcmk__xml_first_child(xml); xml != NULL;
50 xml = pcmk__xml_next(xml)) {
51
52 if (!pcmk__xml_tree_foreach(xml, fn, user_data)) {
53 return false;
54 }
55 }
56 return true;
57 }
58
59 bool
60 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
61 {
62 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
63 return FALSE;
64 } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
65 pcmk__xf_tracking)) {
66 return FALSE;
67 } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
68 pcmk__xf_lazy)) {
69 return FALSE;
70 }
71 return TRUE;
72 }
73
74 static inline void
75 set_parent_flag(xmlNode *xml, long flag)
76 {
77 for(; xml; xml = xml->parent) {
78 xml_node_private_t *nodepriv = xml->_private;
79
80 if (nodepriv == NULL) {
81
82 } else {
83 pcmk__set_xml_flags(nodepriv, flag);
84 }
85 }
86 }
87
88 void
89 pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
90 {
91 if(xml && xml->doc && xml->doc->_private){
92
93 xml_doc_private_t *docpriv = xml->doc->_private;
94
95 pcmk__set_xml_flags(docpriv, flag);
96 }
97 }
98
99
100 void
101 pcmk__mark_xml_node_dirty(xmlNode *xml)
102 {
103 pcmk__set_xml_doc_flag(xml, pcmk__xf_dirty);
104 set_parent_flag(xml, pcmk__xf_dirty);
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118 static bool
119 reset_xml_node_flags(xmlNode *xml, void *user_data)
120 {
121 xml_node_private_t *nodepriv = xml->_private;
122
123 if (nodepriv != NULL) {
124 nodepriv->flags = pcmk__xf_none;
125 }
126 return true;
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140 static bool
141 mark_xml_dirty_created(xmlNode *xml, void *user_data)
142 {
143 xml_node_private_t *nodepriv = xml->_private;
144
145 if (nodepriv != NULL) {
146 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created);
147 }
148 return true;
149 }
150
151
152
153
154
155
156
157
158
159 void
160 pcmk__xml_mark_created(xmlNode *xml)
161 {
162 CRM_ASSERT(xml != NULL);
163
164 if (!pcmk__tracking_xml_changes(xml, false)) {
165
166 return;
167 }
168
169
170 pcmk__mark_xml_node_dirty(xml);
171
172 pcmk__xml_tree_foreach(xml, mark_xml_dirty_created, NULL);
173 }
174
175 #define XML_DOC_PRIVATE_MAGIC 0x81726354UL
176 #define XML_NODE_PRIVATE_MAGIC 0x54637281UL
177
178
179 static void
180 free_deleted_object(void *data)
181 {
182 if(data) {
183 pcmk__deleted_xml_t *deleted_obj = data;
184
185 g_free(deleted_obj->path);
186 free(deleted_obj);
187 }
188 }
189
190
191 static void
192 reset_xml_private_data(xml_doc_private_t *docpriv)
193 {
194 if (docpriv != NULL) {
195 CRM_ASSERT(docpriv->check == XML_DOC_PRIVATE_MAGIC);
196
197 free(docpriv->user);
198 docpriv->user = NULL;
199
200 if (docpriv->acls != NULL) {
201 pcmk__free_acls(docpriv->acls);
202 docpriv->acls = NULL;
203 }
204
205 if(docpriv->deleted_objs) {
206 g_list_free_full(docpriv->deleted_objs, free_deleted_object);
207 docpriv->deleted_objs = NULL;
208 }
209 }
210 }
211
212
213 static void
214 free_private_data(xmlNode *node)
215 {
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 if (node->name == NULL || node->name[0] != ' ') {
243 if (node->_private) {
244 if (node->type == XML_DOCUMENT_NODE) {
245 reset_xml_private_data(node->_private);
246 } else {
247 CRM_ASSERT(((xml_node_private_t *) node->_private)->check
248 == XML_NODE_PRIVATE_MAGIC);
249
250 }
251 free(node->_private);
252 node->_private = NULL;
253 }
254 }
255 }
256
257
258 static void
259 new_private_data(xmlNode *node)
260 {
261 switch (node->type) {
262 case XML_DOCUMENT_NODE: {
263 xml_doc_private_t *docpriv =
264 pcmk__assert_alloc(1, sizeof(xml_doc_private_t));
265
266 docpriv->check = XML_DOC_PRIVATE_MAGIC;
267
268 pcmk__set_xml_flags(docpriv, pcmk__xf_dirty|pcmk__xf_created);
269 node->_private = docpriv;
270 break;
271 }
272 case XML_ELEMENT_NODE:
273 case XML_ATTRIBUTE_NODE:
274 case XML_COMMENT_NODE: {
275 xml_node_private_t *nodepriv =
276 pcmk__assert_alloc(1, sizeof(xml_node_private_t));
277
278 nodepriv->check = XML_NODE_PRIVATE_MAGIC;
279
280 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created);
281 node->_private = nodepriv;
282 if (pcmk__tracking_xml_changes(node, FALSE)) {
283
284
285
286 pcmk__mark_xml_node_dirty(node);
287 }
288 break;
289 }
290 case XML_TEXT_NODE:
291 case XML_DTD_NODE:
292 case XML_CDATA_SECTION_NODE:
293 break;
294 default:
295
296 crm_trace("Ignoring %p %d", node, node->type);
297 CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
298 break;
299 }
300 }
301
302 void
303 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
304 {
305 xml_accept_changes(xml);
306 crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
307 pcmk__set_xml_doc_flag(xml, pcmk__xf_tracking);
308 if(enforce_acls) {
309 if(acl_source == NULL) {
310 acl_source = xml;
311 }
312 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_enabled);
313 pcmk__unpack_acl(acl_source, xml, user);
314 pcmk__apply_acl(xml);
315 }
316 }
317
318 bool xml_tracking_changes(xmlNode * xml)
319 {
320 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
321 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
322 pcmk__xf_tracking);
323 }
324
325 bool xml_document_dirty(xmlNode *xml)
326 {
327 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
328 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
329 pcmk__xf_dirty);
330 }
331
332
333
334
335
336
337
338
339
340
341 int
342 pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
343 {
344 int position = 0;
345
346 for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
347 xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
348
349 if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
350 position++;
351 }
352 }
353
354 return position;
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368 static bool
369 accept_attr_deletions(xmlNode *xml, void *user_data)
370 {
371 reset_xml_node_flags(xml, NULL);
372 pcmk__xe_remove_matching_attrs(xml, pcmk__marked_as_deleted, NULL);
373 return true;
374 }
375
376
377
378
379
380
381
382
383
384 xmlNode *
385 pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
386 {
387 CRM_CHECK(needle != NULL, return NULL);
388
389 if (needle->type == XML_COMMENT_NODE) {
390 return pcmk__xc_match(haystack, needle, exact);
391
392 } else {
393 const char *id = pcmk__xe_id(needle);
394 const char *attr = (id == NULL)? NULL : PCMK_XA_ID;
395
396 return pcmk__xe_first_child(haystack, (const char *) needle->name, attr,
397 id);
398 }
399 }
400
401 void
402 xml_accept_changes(xmlNode * xml)
403 {
404 xmlNode *top = NULL;
405 xml_doc_private_t *docpriv = NULL;
406
407 if(xml == NULL) {
408 return;
409 }
410
411 crm_trace("Accepting changes to %p", xml);
412 docpriv = xml->doc->_private;
413 top = xmlDocGetRootElement(xml->doc);
414
415 reset_xml_private_data(xml->doc->_private);
416
417 if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
418 docpriv->flags = pcmk__xf_none;
419 return;
420 }
421
422 docpriv->flags = pcmk__xf_none;
423 pcmk__xml_tree_foreach(top, accept_attr_deletions, NULL);
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439 xmlNode *
440 pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
441 const char *attr_n, const char *attr_v)
442 {
443 xmlNode *child = NULL;
444 const char *parent_name = "<null>";
445
446 CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL);
447
448 if (parent != NULL) {
449 child = parent->children;
450 while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
451 child = child->next;
452 }
453
454 parent_name = (const char *) parent->name;
455 }
456
457 for (; child != NULL; child = pcmk__xe_next(child)) {
458 const char *value = NULL;
459
460 if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) {
461
462 continue;
463 }
464 if (attr_n == NULL) {
465
466 return child;
467 }
468
469 value = crm_element_value(child, attr_n);
470
471 if ((attr_v == NULL) && (value != NULL)) {
472
473 return child;
474 }
475 if ((attr_v != NULL) && (pcmk__str_eq(value, attr_v, pcmk__str_none))) {
476
477 return child;
478 }
479 }
480
481 if (node_name == NULL) {
482 node_name = "(any)";
483 }
484 if (attr_n != NULL) {
485 crm_trace("XML child node <%s %s=%s> not found in %s",
486 node_name, attr_n, attr_v, parent_name);
487 } else {
488 crm_trace("XML child node <%s> not found in %s",
489 node_name, parent_name);
490 }
491 return NULL;
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524 int
525 pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
526 {
527 const char *old_value = NULL;
528
529 CRM_CHECK((target != NULL) && (name != NULL), return EINVAL);
530
531 if (value == NULL) {
532 return pcmk_rc_ok;
533 }
534
535 old_value = crm_element_value(target, name);
536
537
538 if (old_value != NULL) {
539 const char *n = name;
540 const char *v = value;
541
542
543 for (; (*n == *v) && (*n != '\0'); n++, v++);
544
545
546 if ((*n == '\0')
547 && (*v++ == '+')
548 && ((*v == '+') || (*v == '='))) {
549
550
551 int old_value_i = (old_value != value)? char2score(old_value) : 0;
552
553
554
555
556 int add = (*v == '+')? 1 : char2score(++v);
557
558 crm_xml_add_int(target, name, pcmk__add_scores(old_value_i, add));
559 return pcmk_rc_ok;
560 }
561 }
562
563
564 if (old_value != value) {
565 crm_xml_add(target, name, value);
566 }
567 return pcmk_rc_ok;
568 }
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583 int
584 pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
585 {
586 CRM_CHECK((src != NULL) && (target != NULL), return EINVAL);
587
588 for (xmlAttr *attr = pcmk__xe_first_attr(src); attr != NULL;
589 attr = attr->next) {
590
591 const char *name = (const char *) attr->name;
592 const char *value = pcmk__xml_attr_value(attr);
593
594 if (pcmk_is_set(flags, pcmk__xaf_no_overwrite)
595 && (crm_element_value(target, name) != NULL)) {
596 continue;
597 }
598
599 if (pcmk_is_set(flags, pcmk__xaf_score_update)) {
600 pcmk__xe_set_score(target, name, value);
601 } else {
602 crm_xml_add(target, name, value);
603 }
604 }
605
606 return pcmk_rc_ok;
607 }
608
609
610
611
612
613
614
615
616
617
618
619 static int
620 remove_xe_attr(xmlNode *element, xmlAttr *attr)
621 {
622 if (attr == NULL) {
623 return pcmk_rc_ok;
624 }
625
626 if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
627
628 crm_trace("ACLs prevent removal of attributes from %s element",
629 (const char *) element->name);
630 return EPERM;
631 }
632
633 if (pcmk__tracking_xml_changes(element, false)) {
634
635 set_parent_flag(element, pcmk__xf_dirty);
636 pcmk__set_xml_flags((xml_node_private_t *) attr->_private,
637 pcmk__xf_deleted);
638 } else {
639 xmlRemoveProp(attr);
640 }
641 return pcmk_rc_ok;
642 }
643
644
645
646
647
648
649
650
651 void
652 pcmk__xe_remove_attr(xmlNode *element, const char *name)
653 {
654 if (name != NULL) {
655 remove_xe_attr(element, xmlHasProp(element, (pcmkXmlStr) name));
656 }
657 }
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673 bool
674 pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
675 {
676 const char *name = user_data;
677
678 pcmk__xe_remove_attr(xml, name);
679 return true;
680 }
681
682
683
684
685
686
687
688
689
690
691 void
692 pcmk__xe_remove_matching_attrs(xmlNode *element,
693 bool (*match)(xmlAttrPtr, void *),
694 void *user_data)
695 {
696 xmlAttrPtr next = NULL;
697
698 for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
699 next = a->next;
700 if ((match == NULL) || match(a, user_data)) {
701 if (remove_xe_attr(element, a) != pcmk_rc_ok) {
702 return;
703 }
704 }
705 }
706 }
707
708
709
710
711
712
713
714
715
716
717
718
719 xmlNode *
720 pcmk__xe_create(xmlNode *parent, const char *name)
721 {
722 xmlNode *node = NULL;
723
724 CRM_ASSERT(!pcmk__str_empty(name));
725
726 if (parent == NULL) {
727 xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
728
729 pcmk__mem_assert(doc);
730
731 node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
732 pcmk__mem_assert(node);
733
734 xmlDocSetRootElement(doc, node);
735
736 } else {
737 node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
738 pcmk__mem_assert(node);
739 }
740
741 pcmk__xml_mark_created(node);
742 return node;
743 }
744
745
746
747
748
749
750
751
752
753
754
755
756 G_GNUC_PRINTF(2, 3)
757 void
758 pcmk__xe_set_content(xmlNode *node, const char *format, ...)
759 {
760 if (node != NULL) {
761 const char *content = NULL;
762 char *buf = NULL;
763
764 if (strchr(format, '%') == NULL) {
765
766 content = format;
767
768 } else {
769 va_list ap;
770
771 va_start(ap, format);
772
773 if (pcmk__str_eq(format, "%s", pcmk__str_none)) {
774
775 content = va_arg(ap, const char *);
776
777 } else {
778 CRM_ASSERT(vasprintf(&buf, format, ap) >= 0);
779 content = buf;
780 }
781 va_end(ap);
782 }
783
784 xmlNodeSetContent(node, (pcmkXmlStr) content);
785 free(buf);
786 }
787 }
788
789
790
791
792
793
794 void
795 pcmk_free_xml_subtree(xmlNode *xml)
796 {
797 xmlUnlinkNode(xml);
798 xmlFreeNode(xml);
799 }
800
801 static void
802 free_xml_with_position(xmlNode *child, int position)
803 {
804 xmlDoc *doc = NULL;
805 xml_node_private_t *nodepriv = NULL;
806
807 if (child == NULL) {
808 return;
809 }
810 doc = child->doc;
811 nodepriv = child->_private;
812
813 if ((doc != NULL) && (xmlDocGetRootElement(doc) == child)) {
814
815 xmlFreeDoc(doc);
816 return;
817 }
818
819 if (!pcmk__check_acl(child, NULL, pcmk__xf_acl_write)) {
820 GString *xpath = NULL;
821
822 pcmk__if_tracing({}, return);
823 xpath = pcmk__element_xpath(child);
824 qb_log_from_external_source(__func__, __FILE__,
825 "Cannot remove %s %x", LOG_TRACE,
826 __LINE__, 0, xpath->str, nodepriv->flags);
827 g_string_free(xpath, TRUE);
828 return;
829 }
830
831 if ((doc != NULL) && pcmk__tracking_xml_changes(child, false)
832 && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
833
834 xml_doc_private_t *docpriv = doc->_private;
835 GString *xpath = pcmk__element_xpath(child);
836
837 if (xpath != NULL) {
838 pcmk__deleted_xml_t *deleted_obj = NULL;
839
840 crm_trace("Deleting %s %p from %p", xpath->str, child, doc);
841
842 deleted_obj = pcmk__assert_alloc(1, sizeof(pcmk__deleted_xml_t));
843 deleted_obj->path = g_string_free(xpath, FALSE);
844 deleted_obj->position = -1;
845
846
847 if (child->type == XML_COMMENT_NODE) {
848 if (position >= 0) {
849 deleted_obj->position = position;
850
851 } else {
852 deleted_obj->position = pcmk__xml_position(child,
853 pcmk__xf_skip);
854 }
855 }
856
857 docpriv->deleted_objs = g_list_append(docpriv->deleted_objs,
858 deleted_obj);
859 pcmk__set_xml_doc_flag(child, pcmk__xf_dirty);
860 }
861 }
862 pcmk_free_xml_subtree(child);
863 }
864
865
866 void
867 free_xml(xmlNode * child)
868 {
869 free_xml_with_position(child, -1);
870 }
871
872
873
874
875
876
877
878
879
880
881
882 xmlNode *
883 pcmk__xml_copy(xmlNode *parent, xmlNode *src)
884 {
885 xmlNode *copy = NULL;
886
887 if (src == NULL) {
888 return NULL;
889 }
890
891 if (parent == NULL) {
892 xmlDoc *doc = NULL;
893
894
895 CRM_ASSERT(src->type == XML_ELEMENT_NODE);
896
897 doc = xmlNewDoc(PCMK__XML_VERSION);
898 pcmk__mem_assert(doc);
899
900 copy = xmlDocCopyNode(src, doc, 1);
901 pcmk__mem_assert(copy);
902
903 xmlDocSetRootElement(doc, copy);
904
905 } else {
906 copy = xmlDocCopyNode(src, parent->doc, 1);
907 pcmk__mem_assert(copy);
908
909 xmlAddChild(parent, copy);
910 }
911
912 pcmk__xml_mark_created(copy);
913 return copy;
914 }
915
916
917
918
919
920
921
922 void
923 pcmk__strip_xml_text(xmlNode *xml)
924 {
925 xmlNode *iter = xml->children;
926
927 while (iter) {
928 xmlNode *next = iter->next;
929
930 switch (iter->type) {
931 case XML_TEXT_NODE:
932
933 pcmk_free_xml_subtree(iter);
934 break;
935
936 case XML_ELEMENT_NODE:
937
938 pcmk__strip_xml_text(iter);
939 break;
940
941 default:
942
943 break;
944 }
945
946 iter = next;
947 }
948 }
949
950
951
952
953
954
955
956
957
958 const char *
959 pcmk__xe_add_last_written(xmlNode *xe)
960 {
961 char *now_s = pcmk__epoch2str(NULL, 0);
962 const char *result = NULL;
963
964 result = crm_xml_add(xe, PCMK_XA_CIB_LAST_WRITTEN,
965 pcmk__s(now_s, "Could not determine current time"));
966 free(now_s);
967 return result;
968 }
969
970
971
972
973
974
975 void
976 crm_xml_sanitize_id(char *id)
977 {
978 char *c;
979
980 for (c = id; *c; ++c) {
981
982 switch (*c) {
983 case ':':
984 case '#':
985 *c = '.';
986 }
987 }
988 }
989
990
991
992
993
994
995
996
997 void
998 crm_xml_set_id(xmlNode *xml, const char *format, ...)
999 {
1000 va_list ap;
1001 int len = 0;
1002 char *id = NULL;
1003
1004
1005 va_start(ap, format);
1006 len = vasprintf(&id, format, ap);
1007 va_end(ap);
1008 CRM_ASSERT(len > 0);
1009
1010 crm_xml_sanitize_id(id);
1011 crm_xml_add(xml, PCMK_XA_ID, id);
1012 free(id);
1013 }
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027 bool
1028 pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
1029 {
1030 if (text == NULL) {
1031 return false;
1032 }
1033
1034 while (*text != '\0') {
1035 switch (type) {
1036 case pcmk__xml_escape_text:
1037 switch (*text) {
1038 case '<':
1039 case '>':
1040 case '&':
1041 return true;
1042 case '\n':
1043 case '\t':
1044 break;
1045 default:
1046 if (g_ascii_iscntrl(*text)) {
1047 return true;
1048 }
1049 break;
1050 }
1051 break;
1052
1053 case pcmk__xml_escape_attr:
1054 switch (*text) {
1055 case '<':
1056 case '>':
1057 case '&':
1058 case '"':
1059 return true;
1060 default:
1061 if (g_ascii_iscntrl(*text)) {
1062 return true;
1063 }
1064 break;
1065 }
1066 break;
1067
1068 case pcmk__xml_escape_attr_pretty:
1069 switch (*text) {
1070 case '\n':
1071 case '\r':
1072 case '\t':
1073 case '"':
1074 return true;
1075 default:
1076 break;
1077 }
1078 break;
1079
1080 default:
1081 CRM_ASSERT(false);
1082 break;
1083 }
1084
1085 text = g_utf8_next_char(text);
1086 }
1087 return false;
1088 }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109 gchar *
1110 pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
1111 {
1112 GString *copy = NULL;
1113
1114 if (text == NULL) {
1115 return NULL;
1116 }
1117 copy = g_string_sized_new(strlen(text));
1118
1119 while (*text != '\0') {
1120
1121 if ((*text & 0x80) != 0) {
1122 size_t bytes = g_utf8_next_char(text) - text;
1123
1124 g_string_append_len(copy, text, bytes);
1125 text += bytes;
1126 continue;
1127 }
1128
1129 switch (type) {
1130 case pcmk__xml_escape_text:
1131 switch (*text) {
1132 case '<':
1133 g_string_append(copy, PCMK__XML_ENTITY_LT);
1134 break;
1135 case '>':
1136 g_string_append(copy, PCMK__XML_ENTITY_GT);
1137 break;
1138 case '&':
1139 g_string_append(copy, PCMK__XML_ENTITY_AMP);
1140 break;
1141 case '\n':
1142 case '\t':
1143 g_string_append_c(copy, *text);
1144 break;
1145 default:
1146 if (g_ascii_iscntrl(*text)) {
1147 g_string_append_printf(copy, "&#x%.2X;", *text);
1148 } else {
1149 g_string_append_c(copy, *text);
1150 }
1151 break;
1152 }
1153 break;
1154
1155 case pcmk__xml_escape_attr:
1156 switch (*text) {
1157 case '<':
1158 g_string_append(copy, PCMK__XML_ENTITY_LT);
1159 break;
1160 case '>':
1161 g_string_append(copy, PCMK__XML_ENTITY_GT);
1162 break;
1163 case '&':
1164 g_string_append(copy, PCMK__XML_ENTITY_AMP);
1165 break;
1166 case '"':
1167 g_string_append(copy, PCMK__XML_ENTITY_QUOT);
1168 break;
1169 default:
1170 if (g_ascii_iscntrl(*text)) {
1171 g_string_append_printf(copy, "&#x%.2X;", *text);
1172 } else {
1173 g_string_append_c(copy, *text);
1174 }
1175 break;
1176 }
1177 break;
1178
1179 case pcmk__xml_escape_attr_pretty:
1180 switch (*text) {
1181 case '"':
1182 g_string_append(copy, "\\\"");
1183 break;
1184 case '\n':
1185 g_string_append(copy, "\\n");
1186 break;
1187 case '\r':
1188 g_string_append(copy, "\\r");
1189 break;
1190 case '\t':
1191 g_string_append(copy, "\\t");
1192 break;
1193 default:
1194 g_string_append_c(copy, *text);
1195 break;
1196 }
1197 break;
1198
1199 default:
1200 CRM_ASSERT(false);
1201 break;
1202 }
1203
1204 text = g_utf8_next_char(text);
1205 }
1206 return g_string_free(copy, FALSE);
1207 }
1208
1209
1210
1211
1212
1213
1214
1215
1216 static void
1217 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
1218 {
1219 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
1220 pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
1221 }
1222 }
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238 static void
1239 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
1240 const char *old_value)
1241 {
1242 xml_doc_private_t *docpriv = new_xml->doc->_private;
1243 xmlAttr *attr = NULL;
1244 xml_node_private_t *nodepriv;
1245
1246
1247 pcmk__clear_xml_flags(docpriv, pcmk__xf_tracking);
1248
1249
1250 attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1251 pcmk__set_xml_flags(docpriv, pcmk__xf_tracking);
1252
1253
1254 nodepriv = attr->_private;
1255 nodepriv->flags = 0;
1256
1257
1258 remove_xe_attr(new_xml, attr);
1259
1260 crm_trace("XML attribute %s=%s was removed from %s",
1261 attr_name, old_value, element);
1262 }
1263
1264
1265
1266
1267
1268 static void
1269 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
1270 const char *old_value)
1271 {
1272 char *vcopy = crm_element_value_copy(new_xml, attr_name);
1273
1274 crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
1275 attr_name, old_value, vcopy, element);
1276
1277
1278 xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1279
1280
1281 crm_xml_add(new_xml, attr_name, vcopy);
1282 free(vcopy);
1283 }
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296 static void
1297 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
1298 xmlAttr *new_attr, int p_old, int p_new)
1299 {
1300 xml_node_private_t *nodepriv = new_attr->_private;
1301
1302 crm_trace("XML attribute %s moved from position %d to %d in %s",
1303 old_attr->name, p_old, p_new, element);
1304
1305
1306 pcmk__mark_xml_node_dirty(new_xml);
1307
1308
1309 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_moved);
1310
1311 nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1312 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
1313 }
1314
1315
1316
1317
1318
1319
1320
1321
1322 static void
1323 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1324 {
1325 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1326
1327 while (attr_iter != NULL) {
1328 const char *name = (const char *) attr_iter->name;
1329 xmlAttr *old_attr = attr_iter;
1330 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1331 const char *old_value = pcmk__xml_attr_value(attr_iter);
1332
1333 attr_iter = attr_iter->next;
1334 if (new_attr == NULL) {
1335 mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
1336 old_value);
1337
1338 } else {
1339 xml_node_private_t *nodepriv = new_attr->_private;
1340 int new_pos = pcmk__xml_position((xmlNode*) new_attr,
1341 pcmk__xf_skip);
1342 int old_pos = pcmk__xml_position((xmlNode*) old_attr,
1343 pcmk__xf_skip);
1344 const char *new_value = crm_element_value(new_xml, name);
1345
1346
1347 pcmk__clear_xml_flags(nodepriv, pcmk__xf_created);
1348
1349 if (strcmp(new_value, old_value) != 0) {
1350 mark_attr_changed(new_xml, (const char *) old_xml->name, name,
1351 old_value);
1352
1353 } else if ((old_pos != new_pos)
1354 && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
1355 mark_attr_moved(new_xml, (const char *) old_xml->name,
1356 old_attr, new_attr, old_pos, new_pos);
1357 }
1358 }
1359 }
1360 }
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371 static void
1372 mark_created_attrs(xmlNode *new_xml)
1373 {
1374 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1375
1376 while (attr_iter != NULL) {
1377 xmlAttr *new_attr = attr_iter;
1378 xml_node_private_t *nodepriv = attr_iter->_private;
1379
1380 attr_iter = attr_iter->next;
1381 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1382 const char *attr_name = (const char *) new_attr->name;
1383
1384 crm_trace("Created new attribute %s=%s in %s",
1385 attr_name, pcmk__xml_attr_value(new_attr),
1386 new_xml->name);
1387
1388
1389
1390
1391 if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
1392 pcmk__mark_xml_attr_dirty(new_attr);
1393 } else {
1394
1395 xmlUnsetProp(new_xml, new_attr->name);
1396 }
1397 }
1398 }
1399 }
1400
1401
1402
1403
1404
1405
1406
1407
1408 static void
1409 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1410 {
1411 set_attrs_flag(new_xml, pcmk__xf_created);
1412 xml_diff_old_attrs(old_xml, new_xml);
1413 mark_created_attrs(new_xml);
1414 }
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428 static void
1429 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1430 {
1431
1432 xmlNode *candidate = pcmk__xml_copy(new_parent, old_child);
1433
1434
1435 pcmk__xml_tree_foreach(candidate, reset_xml_node_flags, NULL);
1436
1437
1438 pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
1439
1440
1441 free_xml_with_position(candidate,
1442 pcmk__xml_position(old_child, pcmk__xf_skip));
1443
1444 if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
1445 pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
1446 pcmk__xf_skip);
1447 }
1448 }
1449
1450 static void
1451 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
1452 int p_old, int p_new)
1453 {
1454 xml_node_private_t *nodepriv = new_child->_private;
1455
1456 crm_trace("Child element %s with "
1457 PCMK_XA_ID "='%s' moved from position %d to %d under %s",
1458 new_child->name, pcmk__s(pcmk__xe_id(new_child), "<no id>"),
1459 p_old, p_new, new_parent->name);
1460 pcmk__mark_xml_node_dirty(new_parent);
1461 pcmk__set_xml_flags(nodepriv, pcmk__xf_moved);
1462
1463 if (p_old > p_new) {
1464 nodepriv = old_child->_private;
1465 } else {
1466 nodepriv = new_child->_private;
1467 }
1468 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
1469 }
1470
1471
1472 static void
1473 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
1474 {
1475 xmlNode *old_child = NULL;
1476 xmlNode *new_child = NULL;
1477 xml_node_private_t *nodepriv = NULL;
1478
1479 CRM_CHECK(new_xml != NULL, return);
1480 if (old_xml == NULL) {
1481 pcmk__xml_mark_created(new_xml);
1482 pcmk__apply_creation_acl(new_xml, check_top);
1483 return;
1484 }
1485
1486 nodepriv = new_xml->_private;
1487 CRM_CHECK(nodepriv != NULL, return);
1488
1489 if(nodepriv->flags & pcmk__xf_processed) {
1490
1491 return;
1492 }
1493 pcmk__set_xml_flags(nodepriv, pcmk__xf_processed);
1494
1495 xml_diff_attrs(old_xml, new_xml);
1496
1497
1498 for (old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
1499 old_child = pcmk__xml_next(old_child)) {
1500
1501 new_child = pcmk__xml_match(new_xml, old_child, true);
1502
1503 if (new_child != NULL) {
1504 mark_xml_changes(old_child, new_child, true);
1505
1506 } else {
1507 mark_child_deleted(old_child, new_xml);
1508 }
1509 }
1510
1511
1512 new_child = pcmk__xml_first_child(new_xml);
1513 while (new_child != NULL) {
1514 xmlNode *next = pcmk__xml_next(new_child);
1515
1516 old_child = pcmk__xml_match(old_xml, new_child, true);
1517
1518 if (old_child == NULL) {
1519
1520 nodepriv = new_child->_private;
1521 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
1522
1523
1524 mark_xml_changes(old_child, new_child, true);
1525
1526 } else {
1527
1528 int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
1529 int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
1530
1531 if(p_old != p_new) {
1532 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
1533 }
1534 }
1535
1536 new_child = next;
1537 }
1538 }
1539
1540 void
1541 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
1542 {
1543 pcmk__set_xml_doc_flag(new_xml, pcmk__xf_lazy);
1544 xml_calculate_changes(old_xml, new_xml);
1545 }
1546
1547
1548 void
1549 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
1550 {
1551 CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
1552 && pcmk__xe_is(old_xml, (const char *) new_xml->name)
1553 && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
1554 pcmk__str_none),
1555 return);
1556
1557 if(xml_tracking_changes(new_xml) == FALSE) {
1558 xml_track_changes(new_xml, NULL, NULL, FALSE);
1559 }
1560
1561 mark_xml_changes(old_xml, new_xml, FALSE);
1562 }
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572 xmlNode *
1573 pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
1574 {
1575 xmlNode *a_child = NULL;
1576 int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
1577
1578 CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
1579
1580 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
1581 a_child = pcmk__xml_next(a_child)) {
1582 if (exact) {
1583 int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
1584 xml_node_private_t *nodepriv = a_child->_private;
1585
1586 if (offset < search_offset) {
1587 continue;
1588
1589 } else if (offset > search_offset) {
1590 return NULL;
1591 }
1592
1593 if (pcmk_is_set(nodepriv->flags, pcmk__xf_skip)) {
1594 continue;
1595 }
1596 }
1597
1598 if (a_child->type == XML_COMMENT_NODE
1599 && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
1600 return a_child;
1601
1602 } else if (exact) {
1603 return NULL;
1604 }
1605 }
1606
1607 return NULL;
1608 }
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621 void
1622 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
1623 {
1624 CRM_CHECK(update != NULL, return);
1625 CRM_CHECK(update->type == XML_COMMENT_NODE, return);
1626
1627 if (target == NULL) {
1628 target = pcmk__xc_match(parent, update, false);
1629 }
1630
1631 if (target == NULL) {
1632 pcmk__xml_copy(parent, update);
1633
1634 } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
1635 xmlFree(target->content);
1636 target->content = xmlStrdup(update->content);
1637 }
1638 }
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674 void
1675 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
1676 uint32_t flags, bool as_diff)
1677 {
1678
1679
1680
1681
1682 const char *update_name = NULL;
1683 const char *update_id_attr = NULL;
1684 const char *update_id_val = NULL;
1685 char *trace_s = NULL;
1686
1687 crm_log_xml_trace(update, "update");
1688 crm_log_xml_trace(target, "target");
1689
1690 CRM_CHECK(update != NULL, goto done);
1691
1692 if (update->type == XML_COMMENT_NODE) {
1693 pcmk__xc_update(parent, target, update);
1694 goto done;
1695 }
1696
1697 update_name = (const char *) update->name;
1698
1699 CRM_CHECK(update_name != NULL, goto done);
1700 CRM_CHECK((target != NULL) || (parent != NULL), goto done);
1701
1702 update_id_val = pcmk__xe_id(update);
1703 if (update_id_val != NULL) {
1704 update_id_attr = PCMK_XA_ID;
1705
1706 } else {
1707 update_id_val = crm_element_value(update, PCMK_XA_ID_REF);
1708 if (update_id_val != NULL) {
1709 update_id_attr = PCMK_XA_ID_REF;
1710 }
1711 }
1712
1713 pcmk__if_tracing(
1714 {
1715 if (update_id_attr != NULL) {
1716 trace_s = crm_strdup_printf("<%s %s=%s/>",
1717 update_name, update_id_attr,
1718 update_id_val);
1719 } else {
1720 trace_s = crm_strdup_printf("<%s/>", update_name);
1721 }
1722 },
1723 {}
1724 );
1725
1726 if (target == NULL) {
1727
1728 target = pcmk__xe_first_child(parent, update_name, update_id_attr,
1729 update_id_val);
1730 }
1731
1732 if (target == NULL) {
1733
1734 target = pcmk__xe_create(parent, update_name);
1735 crm_trace("Added %s", pcmk__s(trace_s, update_name));
1736
1737 } else {
1738
1739 crm_trace("Found node %s to update", pcmk__s(trace_s, update_name));
1740 }
1741
1742 CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return);
1743
1744 if (!as_diff) {
1745 pcmk__xe_copy_attrs(target, update, flags);
1746
1747 } else {
1748
1749 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
1750 a = a->next) {
1751 const char *p_value = pcmk__xml_attr_value(a);
1752
1753
1754 xmlUnsetProp(target, a->name);
1755 xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
1756 }
1757 }
1758
1759 for (xmlNode *child = pcmk__xml_first_child(update); child != NULL;
1760 child = pcmk__xml_next(child)) {
1761
1762 crm_trace("Updating child of %s", pcmk__s(trace_s, update_name));
1763 pcmk__xml_update(target, NULL, child, flags, as_diff);
1764 }
1765
1766 crm_trace("Finished with %s", pcmk__s(trace_s, update_name));
1767
1768 done:
1769 free(trace_s);
1770 }
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790 static bool
1791 delete_xe_if_matching(xmlNode *xml, void *user_data)
1792 {
1793 xmlNode *search = user_data;
1794
1795 if (!pcmk__xe_is(search, (const char *) xml->name)) {
1796
1797 return true;
1798 }
1799
1800 for (const xmlAttr *attr = pcmk__xe_first_attr(search); attr != NULL;
1801 attr = attr->next) {
1802
1803 const char *search_val = pcmk__xml_attr_value(attr);
1804 const char *xml_val = crm_element_value(xml, (const char *) attr->name);
1805
1806 if (!pcmk__str_eq(search_val, xml_val, pcmk__str_casei)) {
1807
1808 return true;
1809 }
1810 }
1811
1812 crm_log_xml_trace(xml, "delete-match");
1813 crm_log_xml_trace(search, "delete-search");
1814 free_xml(xml);
1815
1816
1817 return false;
1818 }
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838 int
1839 pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
1840 {
1841
1842 CRM_CHECK((xml != NULL) && (search != NULL), return EINVAL);
1843
1844 for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
1845 xml = pcmk__xe_next(xml)) {
1846
1847 if (!pcmk__xml_tree_foreach(xml, delete_xe_if_matching, search)) {
1848
1849 return pcmk_rc_ok;
1850 }
1851 }
1852
1853
1854 return ENXIO;
1855 }
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868 static void
1869 replace_node(xmlNode *old, xmlNode *new)
1870 {
1871 new = xmlCopyNode(new, 1);
1872 pcmk__mem_assert(new);
1873
1874
1875 pcmk__xml_tree_foreach(new, reset_xml_node_flags, NULL);
1876
1877 old = xmlReplaceNode(old, new);
1878
1879 if (xml_tracking_changes(new)) {
1880
1881 pcmk__apply_acl(new);
1882 }
1883 xml_calculate_changes(old, new);
1884 xmlFreeNode(old);
1885 }
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904 static bool
1905 replace_xe_if_matching(xmlNode *xml, void *user_data)
1906 {
1907 xmlNode *replace = user_data;
1908 const char *xml_id = NULL;
1909 const char *replace_id = NULL;
1910
1911 xml_id = pcmk__xe_id(xml);
1912 replace_id = pcmk__xe_id(replace);
1913
1914 if (!pcmk__xe_is(replace, (const char *) xml->name)) {
1915
1916 return true;
1917 }
1918
1919 if ((replace_id != NULL)
1920 && !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) {
1921
1922
1923 return true;
1924 }
1925
1926 crm_log_xml_trace(xml, "replace-match");
1927 crm_log_xml_trace(replace, "replace-with");
1928 replace_node(xml, replace);
1929
1930
1931 return false;
1932 }
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951 int
1952 pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
1953 {
1954
1955
1956
1957
1958
1959
1960 CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL);
1961
1962 for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
1963 xml = pcmk__xe_next(xml)) {
1964
1965 if (!pcmk__xml_tree_foreach(xml, replace_xe_if_matching, replace)) {
1966
1967 return pcmk_rc_ok;
1968 }
1969 }
1970
1971
1972 return ENXIO;
1973 }
1974
1975
1976 struct update_data {
1977 xmlNode *update;
1978 uint32_t flags;
1979 };
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002 static bool
2003 update_xe_if_matching(xmlNode *xml, void *user_data)
2004 {
2005 struct update_data *data = user_data;
2006 xmlNode *update = data->update;
2007
2008 if (!pcmk__xe_is(update, (const char *) xml->name)) {
2009
2010 return true;
2011 }
2012
2013 if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) {
2014
2015 return true;
2016 }
2017
2018 crm_log_xml_trace(xml, "update-match");
2019 crm_log_xml_trace(update, "update-with");
2020 pcmk__xml_update(NULL, xml, update, data->flags, false);
2021
2022
2023 return false;
2024 }
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045 int
2046 pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
2047 {
2048
2049
2050
2051
2052
2053
2054
2055 struct update_data data = {
2056 .update = update,
2057 .flags = flags,
2058 };
2059
2060 CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL);
2061
2062 if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, &data)) {
2063
2064 return pcmk_rc_ok;
2065 }
2066
2067
2068 return ENXIO;
2069 }
2070
2071 xmlNode *
2072 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2073 {
2074 xmlNode *child = NULL;
2075 GSList *nvpairs = NULL;
2076 xmlNode *result = NULL;
2077
2078 CRM_CHECK(input != NULL, return NULL);
2079
2080 result = pcmk__xe_create(parent, (const char *) input->name);
2081 nvpairs = pcmk_xml_attrs2nvpairs(input);
2082 nvpairs = pcmk_sort_nvpairs(nvpairs);
2083 pcmk_nvpairs2xml_attrs(nvpairs, result);
2084 pcmk_free_nvpairs(nvpairs);
2085
2086 for (child = pcmk__xe_first_child(input, NULL, NULL, NULL); child != NULL;
2087 child = pcmk__xe_next(child)) {
2088
2089 if (recursive) {
2090 sorted_xml(child, result, recursive);
2091 } else {
2092 pcmk__xml_copy(result, child);
2093 }
2094 }
2095
2096 return result;
2097 }
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107 xmlNode *
2108 pcmk__xe_next_same(const xmlNode *node)
2109 {
2110 for (xmlNode *match = pcmk__xe_next(node); match != NULL;
2111 match = pcmk__xe_next(match)) {
2112
2113 if (pcmk__xe_is(match, (const char *) node->name)) {
2114 return match;
2115 }
2116 }
2117 return NULL;
2118 }
2119
2120 void
2121 crm_xml_init(void)
2122 {
2123 static bool init = true;
2124
2125 if(init) {
2126 init = false;
2127
2128
2129
2130
2131
2132 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2133
2134
2135 xmlDeregisterNodeDefault(free_private_data);
2136 xmlRegisterNodeDefault(new_private_data);
2137
2138 crm_schema_init();
2139 }
2140 }
2141
2142 void
2143 crm_xml_cleanup(void)
2144 {
2145 crm_schema_cleanup();
2146 xmlCleanupParser();
2147 }
2148
2149 #define XPATH_MAX 512
2150
2151 xmlNode *
2152 expand_idref(xmlNode * input, xmlNode * top)
2153 {
2154 char *xpath = NULL;
2155 const char *ref = NULL;
2156 xmlNode *result = NULL;
2157
2158 if (input == NULL) {
2159 return NULL;
2160 }
2161
2162 ref = crm_element_value(input, PCMK_XA_ID_REF);
2163 if (ref == NULL) {
2164 return input;
2165 }
2166
2167 if (top == NULL) {
2168 top = input;
2169 }
2170
2171 xpath = crm_strdup_printf("//%s[@" PCMK_XA_ID "='%s']", input->name, ref);
2172 result = get_xpath_object(xpath, top, LOG_DEBUG);
2173 if (result == NULL) {
2174 pcmk__config_err("Ignoring invalid %s configuration: "
2175 PCMK_XA_ID_REF " '%s' does not reference "
2176 "a valid object " CRM_XS " xpath=%s",
2177 input->name, ref, xpath);
2178 }
2179 free(xpath);
2180 return result;
2181 }
2182
2183 char *
2184 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
2185 {
2186 static const char *base = NULL;
2187 char *ret = NULL;
2188
2189 if (base == NULL) {
2190 base = pcmk__env_option(PCMK__ENV_SCHEMA_DIRECTORY);
2191 }
2192 if (pcmk__str_empty(base)) {
2193 base = CRM_SCHEMA_DIRECTORY;
2194 }
2195
2196 switch (ns) {
2197 case pcmk__xml_artefact_ns_legacy_rng:
2198 case pcmk__xml_artefact_ns_legacy_xslt:
2199 ret = strdup(base);
2200 break;
2201 case pcmk__xml_artefact_ns_base_rng:
2202 case pcmk__xml_artefact_ns_base_xslt:
2203 ret = crm_strdup_printf("%s/base", base);
2204 break;
2205 default:
2206 crm_err("XML artefact family specified as %u not recognized", ns);
2207 }
2208 return ret;
2209 }
2210
2211 static char *
2212 find_artefact(enum pcmk__xml_artefact_ns ns, const char *path, const char *filespec)
2213 {
2214 char *ret = NULL;
2215
2216 switch (ns) {
2217 case pcmk__xml_artefact_ns_legacy_rng:
2218 case pcmk__xml_artefact_ns_base_rng:
2219 if (pcmk__ends_with(filespec, ".rng")) {
2220 ret = crm_strdup_printf("%s/%s", path, filespec);
2221 } else {
2222 ret = crm_strdup_printf("%s/%s.rng", path, filespec);
2223 }
2224 break;
2225 case pcmk__xml_artefact_ns_legacy_xslt:
2226 case pcmk__xml_artefact_ns_base_xslt:
2227 if (pcmk__ends_with(filespec, ".xsl")) {
2228 ret = crm_strdup_printf("%s/%s", path, filespec);
2229 } else {
2230 ret = crm_strdup_printf("%s/%s.xsl", path, filespec);
2231 }
2232 break;
2233 default:
2234 crm_err("XML artefact family specified as %u not recognized", ns);
2235 }
2236
2237 return ret;
2238 }
2239
2240 char *
2241 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2242 {
2243 struct stat sb;
2244 char *base = pcmk__xml_artefact_root(ns);
2245 char *ret = NULL;
2246
2247 ret = find_artefact(ns, base, filespec);
2248 free(base);
2249
2250 if (stat(ret, &sb) != 0 || !S_ISREG(sb.st_mode)) {
2251 const char *remote_schema_dir = pcmk__remote_schema_dir();
2252
2253 free(ret);
2254 ret = find_artefact(ns, remote_schema_dir, filespec);
2255 }
2256
2257 return ret;
2258 }
2259
2260 void
2261 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2262 {
2263 while (true) {
2264 const char *name, *value;
2265
2266 name = va_arg(pairs, const char *);
2267 if (name == NULL) {
2268 return;
2269 }
2270
2271 value = va_arg(pairs, const char *);
2272 if (value != NULL) {
2273 crm_xml_add(node, name, value);
2274 }
2275 }
2276 }
2277
2278 void
2279 pcmk__xe_set_props(xmlNodePtr node, ...)
2280 {
2281 va_list pairs;
2282 va_start(pairs, node);
2283 pcmk__xe_set_propv(node, pairs);
2284 va_end(pairs);
2285 }
2286
2287 int
2288 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
2289 int (*handler)(xmlNode *xml, void *userdata),
2290 void *userdata)
2291 {
2292 xmlNode *children = (xml? xml->children : NULL);
2293
2294 CRM_ASSERT(handler != NULL);
2295
2296 for (xmlNode *node = children; node != NULL; node = node->next) {
2297 if ((node->type == XML_ELEMENT_NODE)
2298 && ((child_element_name == NULL)
2299 || pcmk__xe_is(node, child_element_name))) {
2300 int rc = handler(node, userdata);
2301
2302 if (rc != pcmk_rc_ok) {
2303 return rc;
2304 }
2305 }
2306 }
2307
2308 return pcmk_rc_ok;
2309 }
2310
2311
2312
2313
2314 #include <crm/common/xml_compat.h>
2315
2316 xmlNode *
2317 find_entity(xmlNode *parent, const char *node_name, const char *id)
2318 {
2319 return pcmk__xe_first_child(parent, node_name,
2320 ((id == NULL)? id : PCMK_XA_ID), id);
2321 }
2322
2323 void
2324 crm_destroy_xml(gpointer data)
2325 {
2326 free_xml(data);
2327 }
2328
2329 xmlDoc *
2330 getDocPtr(xmlNode *node)
2331 {
2332 xmlDoc *doc = NULL;
2333
2334 CRM_CHECK(node != NULL, return NULL);
2335
2336 doc = node->doc;
2337 if (doc == NULL) {
2338 doc = xmlNewDoc(PCMK__XML_VERSION);
2339 xmlDocSetRootElement(doc, node);
2340 }
2341 return doc;
2342 }
2343
2344 xmlNode *
2345 add_node_copy(xmlNode *parent, xmlNode *src_node)
2346 {
2347 xmlNode *child = NULL;
2348
2349 CRM_CHECK((parent != NULL) && (src_node != NULL), return NULL);
2350
2351 child = xmlDocCopyNode(src_node, parent->doc, 1);
2352 if (child == NULL) {
2353 return NULL;
2354 }
2355 xmlAddChild(parent, child);
2356 pcmk__xml_mark_created(child);
2357 return child;
2358 }
2359
2360 int
2361 add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
2362 {
2363 add_node_copy(parent, child);
2364 free_xml(child);
2365 return 1;
2366 }
2367
2368 gboolean
2369 xml_has_children(const xmlNode * xml_root)
2370 {
2371 if (xml_root != NULL && xml_root->children != NULL) {
2372 return TRUE;
2373 }
2374 return FALSE;
2375 }
2376
2377 static char *
2378 replace_text(char *text, size_t *index, size_t *length, const char *replace)
2379 {
2380
2381 size_t offset = strlen(replace) - 1;
2382
2383 if (offset > 0) {
2384 *length += offset;
2385 text = pcmk__realloc(text, *length + 1);
2386
2387
2388 for (size_t i = *length; i > (*index + offset); i--) {
2389 text[i] = text[i - offset];
2390 }
2391 }
2392
2393
2394 memcpy(text + *index, replace, offset + 1);
2395
2396
2397 *index += offset;
2398 return text;
2399 }
2400
2401 char *
2402 crm_xml_escape(const char *text)
2403 {
2404 size_t length = 0;
2405 char *copy = NULL;
2406
2407 if (text == NULL) {
2408 return NULL;
2409 }
2410
2411 length = strlen(text);
2412 copy = pcmk__str_copy(text);
2413 for (size_t index = 0; index <= length; index++) {
2414 if(copy[index] & 0x80 && copy[index+1] & 0x80){
2415 index++;
2416 continue;
2417 }
2418 switch (copy[index]) {
2419 case 0:
2420
2421 break;
2422 case '<':
2423 copy = replace_text(copy, &index, &length, "<");
2424 break;
2425 case '>':
2426 copy = replace_text(copy, &index, &length, ">");
2427 break;
2428 case '"':
2429 copy = replace_text(copy, &index, &length, """);
2430 break;
2431 case '\'':
2432 copy = replace_text(copy, &index, &length, "'");
2433 break;
2434 case '&':
2435 copy = replace_text(copy, &index, &length, "&");
2436 break;
2437 case '\t':
2438
2439 copy = replace_text(copy, &index, &length, " ");
2440 break;
2441 case '\n':
2442 copy = replace_text(copy, &index, &length, "\\n");
2443 break;
2444 case '\r':
2445 copy = replace_text(copy, &index, &length, "\\r");
2446 break;
2447 default:
2448
2449 if(copy[index] < ' ' || copy[index] > '~') {
2450 char *replace = crm_strdup_printf("\\%.3o", copy[index]);
2451
2452 copy = replace_text(copy, &index, &length, replace);
2453 free(replace);
2454 }
2455 }
2456 }
2457 return copy;
2458 }
2459
2460 xmlNode *
2461 copy_xml(xmlNode *src)
2462 {
2463 xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
2464 xmlNode *copy = NULL;
2465
2466 pcmk__mem_assert(doc);
2467
2468 copy = xmlDocCopyNode(src, doc, 1);
2469 pcmk__mem_assert(copy);
2470
2471 xmlDocSetRootElement(doc, copy);
2472 return copy;
2473 }
2474
2475 xmlNode *
2476 create_xml_node(xmlNode *parent, const char *name)
2477 {
2478
2479 xmlNode *node = NULL;
2480
2481 CRM_CHECK(!pcmk__str_empty(name), return NULL);
2482
2483 if (parent == NULL) {
2484 xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
2485
2486 if (doc == NULL) {
2487 return NULL;
2488 }
2489
2490 node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
2491 if (node == NULL) {
2492 xmlFreeDoc(doc);
2493 return NULL;
2494 }
2495 xmlDocSetRootElement(doc, node);
2496
2497 } else {
2498 node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
2499 if (node == NULL) {
2500 return NULL;
2501 }
2502 }
2503 pcmk__xml_mark_created(node);
2504 return node;
2505 }
2506
2507 xmlNode *
2508 pcmk_create_xml_text_node(xmlNode *parent, const char *name,
2509 const char *content)
2510 {
2511 xmlNode *node = pcmk__xe_create(parent, name);
2512
2513 pcmk__xe_set_content(node, "%s", content);
2514 return node;
2515 }
2516
2517 xmlNode *
2518 pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id,
2519 const char *class_name, const char *text)
2520 {
2521 xmlNode *node = pcmk__html_create(parent, element_name, id, class_name);
2522
2523 pcmk__xe_set_content(node, "%s", text);
2524 return node;
2525 }
2526
2527 xmlNode *
2528 first_named_child(const xmlNode *parent, const char *name)
2529 {
2530 return pcmk__xe_first_child(parent, name, NULL, NULL);
2531 }
2532
2533 xmlNode *
2534 find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
2535 {
2536 xmlNode *result = NULL;
2537
2538 if (search_path == NULL) {
2539 crm_warn("Will never find <NULL>");
2540 return NULL;
2541 }
2542
2543 result = pcmk__xe_first_child(root, search_path, NULL, NULL);
2544
2545 if (must_find && (result == NULL)) {
2546 crm_warn("Could not find %s in %s",
2547 search_path,
2548 ((root != NULL)? (const char *) root->name : "<NULL>"));
2549 }
2550
2551 return result;
2552 }
2553
2554 xmlNode *
2555 crm_next_same_xml(const xmlNode *sibling)
2556 {
2557 return pcmk__xe_next_same(sibling);
2558 }
2559
2560 void
2561 xml_remove_prop(xmlNode * obj, const char *name)
2562 {
2563 pcmk__xe_remove_attr(obj, name);
2564 }
2565
2566 gboolean
2567 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2568 {
2569 bool is_match = false;
2570 const char *child_id = NULL;
2571 const char *update_id = NULL;
2572
2573 CRM_CHECK(child != NULL, return FALSE);
2574 CRM_CHECK(update != NULL, return FALSE);
2575
2576 child_id = pcmk__xe_id(child);
2577 update_id = pcmk__xe_id(update);
2578
2579
2580
2581
2582 is_match = (parent != NULL)
2583 && pcmk__xe_is(update, (const char *) child->name)
2584 && ((update_id == NULL)
2585 || pcmk__str_eq(update_id, child_id, pcmk__str_none));
2586
2587
2588
2589
2590 if (is_match && delete_only) {
2591 for (xmlAttr *attr = pcmk__xe_first_attr(update); attr != NULL;
2592 attr = attr->next) {
2593 const char *name = (const char *) attr->name;
2594 const char *update_val = pcmk__xml_attr_value(attr);
2595 const char *child_val = crm_element_value(child, name);
2596
2597 if (!pcmk__str_eq(update_val, child_val, pcmk__str_casei)) {
2598 is_match = false;
2599 break;
2600 }
2601 }
2602 }
2603
2604 if (is_match) {
2605 if (delete_only) {
2606 crm_log_xml_trace(child, "delete-match");
2607 crm_log_xml_trace(update, "delete-search");
2608 free_xml(child);
2609
2610 } else {
2611 crm_log_xml_trace(child, "replace-match");
2612 crm_log_xml_trace(update, "replace-with");
2613 replace_node(child, update);
2614 }
2615 return TRUE;
2616 }
2617
2618
2619 parent = child;
2620 for (child = pcmk__xml_first_child(parent); child != NULL;
2621 child = pcmk__xml_next(child)) {
2622
2623
2624 if (replace_xml_child(parent, child, update, delete_only)) {
2625 return TRUE;
2626 }
2627 }
2628
2629
2630 return FALSE;
2631 }
2632
2633 gboolean
2634 update_xml_child(xmlNode *child, xmlNode *to_update)
2635 {
2636 return pcmk__xe_update_match(child, to_update,
2637 pcmk__xaf_score_update) == pcmk_rc_ok;
2638 }
2639
2640 int
2641 find_xml_children(xmlNode **children, xmlNode *root, const char *tag,
2642 const char *field, const char *value, gboolean search_matches)
2643 {
2644 int match_found = 0;
2645
2646 CRM_CHECK(root != NULL, return FALSE);
2647 CRM_CHECK(children != NULL, return FALSE);
2648
2649 if ((tag != NULL) && !pcmk__xe_is(root, tag)) {
2650
2651 } else if ((value != NULL)
2652 && !pcmk__str_eq(value, crm_element_value(root, field),
2653 pcmk__str_casei)) {
2654
2655 } else {
2656 if (*children == NULL) {
2657 *children = pcmk__xe_create(NULL, __func__);
2658 }
2659 pcmk__xml_copy(*children, root);
2660 match_found = 1;
2661 }
2662
2663 if (search_matches || match_found == 0) {
2664 xmlNode *child = NULL;
2665
2666 for (child = pcmk__xml_first_child(root); child != NULL;
2667 child = pcmk__xml_next(child)) {
2668 match_found += find_xml_children(children, child, tag, field, value,
2669 search_matches);
2670 }
2671 }
2672
2673 return match_found;
2674 }
2675
2676 void
2677 fix_plus_plus_recursive(xmlNode *target)
2678 {
2679
2680 xmlNode *child = NULL;
2681
2682 for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
2683 const char *p_name = (const char *) a->name;
2684 const char *p_value = pcmk__xml_attr_value(a);
2685
2686 expand_plus_plus(target, p_name, p_value);
2687 }
2688 for (child = pcmk__xe_first_child(target, NULL, NULL, NULL); child != NULL;
2689 child = pcmk__xe_next(child)) {
2690
2691 fix_plus_plus_recursive(child);
2692 }
2693 }
2694
2695 void
2696 copy_in_properties(xmlNode *target, const xmlNode *src)
2697 {
2698 if (src == NULL) {
2699 crm_warn("No node to copy properties from");
2700
2701 } else if (target == NULL) {
2702 crm_err("No node to copy properties into");
2703
2704 } else {
2705 for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
2706 const char *p_name = (const char *) a->name;
2707 const char *p_value = pcmk__xml_attr_value(a);
2708
2709 expand_plus_plus(target, p_name, p_value);
2710 if (xml_acl_denied(target)) {
2711 crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name);
2712 return;
2713 }
2714 }
2715 }
2716 }
2717
2718 void
2719 expand_plus_plus(xmlNode * target, const char *name, const char *value)
2720 {
2721 pcmk__xe_set_score(target, name, value);
2722 }
2723
2724
2725