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