21#include <libxml/tree.h>
22#include <libxml/xmlstring.h>
30#define XML_VERSION ((const xmlChar *) "1.0")
43 static const char *
const element_type_names[] = {
44 [XML_ELEMENT_NODE] =
"element",
45 [XML_ATTRIBUTE_NODE] =
"attribute",
46 [XML_TEXT_NODE] =
"text",
47 [XML_CDATA_SECTION_NODE] =
"CDATA section",
48 [XML_ENTITY_REF_NODE] =
"entity reference",
49 [XML_ENTITY_NODE] =
"entity",
51 [XML_COMMENT_NODE] =
"comment",
52 [XML_DOCUMENT_NODE] =
"document",
53 [XML_DOCUMENT_TYPE_NODE] =
"document type",
54 [XML_DOCUMENT_FRAG_NODE] =
"document fragment",
55 [XML_NOTATION_NODE] =
"notation",
56 [XML_HTML_DOCUMENT_NODE] =
"HTML document",
57 [XML_DTD_NODE] =
"DTD",
58 [XML_ELEMENT_DECL] =
"element declaration",
59 [XML_ATTRIBUTE_DECL] =
"attribute declaration",
60 [XML_ENTITY_DECL] =
"entity declaration",
61 [XML_NAMESPACE_DECL] =
"namespace declaration",
62 [XML_XINCLUDE_START] =
"XInclude start",
63 [XML_XINCLUDE_END] =
"XInclude end",
67 if ((
type < XML_ELEMENT_NODE) || (
type > XML_XINCLUDE_END)) {
68 return "unrecognized type";
70 return element_type_names[
type];
94 if (!fn(xml, user_data)) {
98 for (xml = pcmk__xml_first_child(xml); xml != NULL;
99 xml = pcmk__xml_next(xml)) {
111 for (; xml != NULL; xml = xml->parent) {
114 if (nodepriv != NULL) {
152 return (docpriv != NULL) && pcmk_all_flags_set(docpriv->
flags,
flags);
184 if (nodepriv != NULL) {
202mark_xml_dirty_created(xmlNode *xml,
void *user_data)
206 if (nodepriv != NULL) {
221mark_xml_tree_dirty_created(xmlNode *xml)
238free_deleted_object(
void *
data)
243 g_free(deleted_obj->
path);
252 if (docpriv != NULL) {
257 if (docpriv->
acls != NULL) {
259 docpriv->
acls = NULL;
263 g_list_free_full(docpriv->
deleted_objs, free_deleted_object);
281new_private_data(xmlNode *node,
void *user_data)
283 bool tracking =
false;
287 if (node->_private != NULL) {
293 switch (node->type) {
294 case XML_DOCUMENT_NODE:
300 node->_private = docpriv;
304 case XML_ELEMENT_NODE:
305 case XML_ATTRIBUTE_NODE:
306 case XML_COMMENT_NODE:
312 node->_private = nodepriv;
317 for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
320 new_private_data((xmlNode *) iter, user_data);
327 case XML_CDATA_SECTION_NODE:
353free_private_data(xmlNode *node,
void *user_data)
357 if (node->_private == NULL) {
361 if (node->type == XML_DOCUMENT_NODE) {
369 for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
372 free_private_data((xmlNode *) iter, user_data);
375 free(node->_private);
376 node->_private = NULL;
418 for (
const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
441commit_attr_deletions(xmlNode *xml,
void *user_data)
476 docpriv = doc->_private;
477 if (docpriv == NULL) {
485 reset_xml_private_data(docpriv);
563 c = xmlGetUTF8Char((
const xmlChar *) utf8, len);
565 GString *buf = g_string_sized_new(32);
567 for (
int i = 0; (i < 4) && (utf8[i] !=
'\0'); i++) {
568 g_string_append_printf(buf,
" 0x%.2X", utf8[i]);
570 crm_info(
"Invalid UTF-8 character (bytes:%s)",
571 (pcmk__str_empty(buf->str)?
" <none>" : buf->str));
572 g_string_free(buf, TRUE);
578 || ((c >=
'a') && (c <=
'z'))
579 || ((c >=
'A') && (c <=
'Z'))
580 || ((c >= 0xC0) && (c <= 0xD6))
581 || ((c >= 0xD8) && (c <= 0xF6))
582 || ((c >= 0xF8) && (c <= 0x2FF))
583 || ((c >= 0x370) && (c <= 0x37D))
584 || ((c >= 0x37F) && (c <= 0x1FFF))
585 || ((c >= 0x200C) && (c <= 0x200D))
586 || ((c >= 0x2070) && (c <= 0x218F))
587 || ((c >= 0x2C00) && (c <= 0x2FEF))
588 || ((c >= 0x3001) && (c <= 0xD7FF))
589 || ((c >= 0xF900) && (c <= 0xFDCF))
590 || ((c >= 0xFDF0) && (c <= 0xFFFD))
591 || ((c >= 0x10000) && (c <= 0xEFFFF));
624 c = xmlGetUTF8Char((
const xmlChar *) utf8, len);
626 GString *buf = g_string_sized_new(32);
628 for (
int i = 0; (i < 4) && (utf8[i] !=
'\0'); i++) {
629 g_string_append_printf(buf,
" 0x%.2X", utf8[i]);
631 crm_info(
"Invalid UTF-8 character (bytes:%s)",
632 (pcmk__str_empty(buf->str)?
" <none>" : buf->str));
633 g_string_free(buf, TRUE);
637 return ((c >=
'a') && (c <=
'z'))
638 || ((c >=
'A') && (c <=
'Z'))
639 || ((c >=
'0') && (c <=
'9'))
645 || ((c >= 0xC0) && (c <= 0xD6))
646 || ((c >= 0xD8) && (c <= 0xF6))
647 || ((c >= 0xF8) && (c <= 0x2FF))
648 || ((c >= 0x300) && (c <= 0x36F))
649 || ((c >= 0x370) && (c <= 0x37D))
650 || ((c >= 0x37F) && (c <= 0x1FFF))
651 || ((c >= 0x200C) && (c <= 0x200D))
652 || ((c >= 0x203F) && (c <= 0x2040))
653 || ((c >= 0x2070) && (c <= 0x218F))
654 || ((c >= 0x2C00) && (c <= 0x2FEF))
655 || ((c >= 0x3001) && (c <= 0xD7FF))
656 || ((c >= 0xF900) && (c <= 0xFDCF))
657 || ((c >= 0xFDF0) && (c <= 0xFFFD))
658 || ((c >= 0x10000) && (c <= 0xEFFFF));
693 for (
int i = 1; i < len; i++) {
698 for (
id += len; *
id !=
'\0';
id += len) {
702 for (
int i = 0; i < len; i++) {
737free_xml_with_position(xmlNode *node,
int position)
746 nodepriv = node->_private;
748 if ((doc != NULL) && (xmlDocGetRootElement(doc) == node)) {
761 qb_log_from_external_source(__func__, __FILE__,
763 __LINE__, 0, xpath->str,
765 g_string_free(xpath, TRUE);
781 crm_trace(
"Deleting %s %p from %p", xpath->str, node, doc);
784 deleted_obj->
path = g_string_free(xpath, FALSE);
788 if (node->type == XML_COMMENT_NODE) {
818 free_xml_with_position(xml, -1);
834 xmlNode *copy = NULL;
847 copy = xmlDocCopyNode(src, doc, 1);
850 xmlDocSetRootElement(doc, copy);
853 copy = xmlDocCopyNode(src,
parent->doc, 1);
856 xmlAddChild(
parent, copy);
872 xmlNode *iter = xml->children;
875 xmlNode *next = iter->next;
877 switch (iter->type) {
882 case XML_ELEMENT_NODE:
915 while (*text !=
'\0') {
927 if (g_ascii_iscntrl(*text)) {
942 if (g_ascii_iscntrl(*text)) {
966 text = g_utf8_next_char(text);
993 GString *copy = NULL;
998 copy = g_string_sized_new(strlen(text));
1000 while (*text !=
'\0') {
1002 if ((*text & 0x80) != 0) {
1003 size_t bytes = g_utf8_next_char(text) - text;
1005 g_string_append_len(copy, text, bytes);
1024 g_string_append_c(copy, *text);
1027 if (g_ascii_iscntrl(*text)) {
1028 g_string_append_printf(copy,
"&#x%.2X;", *text);
1030 g_string_append_c(copy, *text);
1051 if (g_ascii_iscntrl(*text)) {
1052 g_string_append_printf(copy,
"&#x%.2X;", *text);
1054 g_string_append_c(copy, *text);
1063 g_string_append(copy,
"\\\"");
1066 g_string_append(copy,
"\\n");
1069 g_string_append(copy,
"\\r");
1072 g_string_append(copy,
"\\t");
1075 g_string_append_c(copy, *text);
1085 text = g_utf8_next_char(text);
1087 return g_string_free(copy, FALSE);
1105mark_attr_deleted(xmlNode *new_xml,
const char *element,
const char *attr_name,
1106 const char *old_value)
1109 xmlAttr *attr = NULL;
1120 attr = xmlHasProp(new_xml, (
const xmlChar *) attr_name);
1121 nodepriv = attr->_private;
1122 nodepriv->
flags = 0;
1127 crm_trace(
"XML attribute %s=%s was removed from %s",
1128 attr_name, old_value, element);
1136mark_attr_changed(xmlNode *new_xml,
const char *element,
const char *attr_name,
1137 const char *old_value)
1142 crm_trace(
"XML attribute %s was changed from '%s' to '%s' in %s",
1143 attr_name, old_value, vcopy, element);
1167mark_attr_moved(xmlNode *new_xml,
const char *element, xmlAttr *old_attr,
1168 xmlAttr *new_attr,
int p_old,
int p_new)
1172 crm_trace(
"XML attribute %s moved from position %d to %d in %s",
1173 old_attr->name, p_old, p_new, element);
1181 nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1193xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1195 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1197 while (attr_iter != NULL) {
1198 const char *
name = (
const char *) attr_iter->name;
1199 xmlAttr *old_attr = attr_iter;
1200 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1201 const char *old_value = pcmk__xml_attr_value(attr_iter);
1203 attr_iter = attr_iter->next;
1204 if (new_attr == NULL) {
1205 mark_attr_deleted(new_xml, (
const char *) old_xml->name,
name,
1219 if (strcmp(new_value, old_value) != 0) {
1220 mark_attr_changed(new_xml, (
const char *) old_xml->name,
name,
1223 }
else if ((old_pos != new_pos)
1231 mark_attr_moved(new_xml, (
const char *) old_xml->name,
1232 old_attr, new_attr, old_pos, new_pos);
1248mark_created_attrs(xmlNode *new_xml)
1250 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1252 while (attr_iter != NULL) {
1253 xmlAttr *new_attr = attr_iter;
1256 attr_iter = attr_iter->next;
1258 const char *attr_name = (
const char *) new_attr->name;
1260 crm_trace(
"Created new attribute %s=%s in %s",
1261 attr_name, pcmk__xml_attr_value(new_attr),
1285xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1288 for (xmlAttr *attr = pcmk__xe_first_attr(new_xml); attr != NULL;
1289 attr = attr->next) {
1295 xml_diff_old_attrs(old_xml, new_xml);
1296 mark_created_attrs(new_xml);
1319mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1335 if (free_xml_with_position(candidate, pos) !=
pcmk_rc_ok) {
1354mark_child_moved(xmlNode *old_child, xmlNode *new_child,
int old_pos,
1357 const char *id_s = pcmk__s(pcmk__xe_id(new_child),
"<no id>");
1358 xmlNode *new_parent = new_child->parent;
1362 "%d to %d under %s",
1363 new_child->name, id_s, old_pos, new_pos, new_parent->name);
1372 if (old_pos > new_pos) {
1373 nodepriv = old_child->_private;
1395new_comment_matches(
const xmlNode *old_comment,
const xmlNode *new_comment)
1437new_element_matches(
const xmlNode *old_element,
const xmlNode *new_element)
1439 return pcmk__xe_is(new_element, (
const char *) old_element->name)
1440 && pcmk__str_eq(pcmk__xe_id(old_element), pcmk__xe_id(new_element),
1465new_child_matches(
const xmlNode *old_child,
const xmlNode *new_child)
1467 if (old_child->type != new_child->type) {
1471 switch (old_child->type) {
1472 case XML_COMMENT_NODE:
1473 return new_comment_matches(old_child, new_child);
1474 case XML_ELEMENT_NODE:
1475 return new_element_matches(old_child, new_child);
1492find_matching_children(xmlNode *old_xml, xmlNode *new_xml)
1494 for (xmlNode *old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
1495 old_child = pcmk__xml_next(old_child)) {
1499 if ((old_nodepriv == NULL) || (old_nodepriv->
match != NULL)) {
1504 for (xmlNode *new_child = pcmk__xml_first_child(new_xml);
1505 new_child != NULL; new_child = pcmk__xml_next(new_child)) {
1509 if ((new_nodepriv == NULL) || (new_nodepriv->
match != NULL)) {
1516 if (new_child_matches(old_child, new_child)) {
1517 old_nodepriv->
match = new_child;
1518 new_nodepriv->
match = old_child;
1547 CRM_CHECK((old_xml != NULL) && (new_xml != NULL),
return);
1548 if ((old_xml->_private == NULL) || (new_xml->_private == NULL)) {
1553 xml_diff_attrs(old_xml, new_xml);
1555 find_matching_children(old_xml, new_xml);
1558 for (xmlNode *old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
1559 old_child = pcmk__xml_next(old_child)) {
1562 xmlNode *new_child = NULL;
1564 if (nodepriv == NULL) {
1568 if (nodepriv->
match == NULL) {
1570 mark_child_deleted(old_child, new_xml);
1577 new_child = nodepriv->
match;
1578 nodepriv->
match = NULL;
1582 if (old_child->type == XML_COMMENT_NODE) {
1594 for (xmlNode *new_child = pcmk__xml_first_child(new_xml),
1595 *next = pcmk__xml_next(new_child);
1597 new_child = next, next = pcmk__xml_next(new_child)) {
1601 if (nodepriv == NULL) {
1605 if (nodepriv->
match != NULL) {
1614 xmlNode *old_child = nodepriv->
match;
1618 if (old_pos != new_pos) {
1619 mark_child_moved(old_child, new_child, old_pos, new_pos);
1621 nodepriv->
match = NULL;
1627 mark_xml_tree_dirty_created(new_child);
1637 static const char *base = NULL;
1643 if (pcmk__str_empty(base)) {
1657 crm_err(
"XML artefact family specified as %u not recognized", ns);
1685 crm_err(
"XML artefact family specified as %u not recognized", ns);
1698 ret = find_artefact(ns, base, filespec);
1701 if (stat(ret, &sb) != 0 || !S_ISREG(sb.st_mode)) {
1705 ret = find_artefact(ns, remote_schema_dir, filespec);
1714#include <libxml/parser.h>
1722 xmlNode *copy = NULL;
1724 copy = xmlDocCopyNode(src, doc, 1);
1727 xmlDocSetRootElement(doc, copy);
1762 for (c =
id; *c; ++c) {
1774 return (xml != NULL)
1781 return (xml != NULL)
1803 (enforce_acls?
" with ACLs" :
""), xml);
1806 if (acl_source == NULL) {
1818 CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
1819 && pcmk__xe_is(old_xml, (
const char *) new_xml->name)
1820 && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
1835 CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
1836 && pcmk__xe_is(old_xml, (
const char *) new_xml->name)
1837 && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
void pcmk__free_acls(GList *acls)
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
void pcmk__apply_acl(xmlNode *xml)
bool pcmk__check_acl(xmlNode *xml, const char *attr_name, enum pcmk__xml_flags mode)
#define pcmk__assert_alloc(nmemb, size)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
enum pcmk_ipc_server type
G_GNUC_INTERNAL int pcmk__xa_remove(xmlAttr *attr, bool force)
G_GNUC_INTERNAL bool pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data)
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
#define PCMK__XML_ENTITY_QUOT
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
G_GNUC_INTERNAL bool pcmk__xc_matches(const xmlNode *comment1, const xmlNode *comment2)
#define PCMK__XML_NODE_PRIVATE_MAGIC
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
#define PCMK__XML_ENTITY_GT
#define PCMK__XML_ENTITY_AMP
#define PCMK__XML_ENTITY_LT
#define PCMK__XML_DOC_PRIVATE_MAGIC
#define crm_info(fmt, args...)
#define CRM_LOG_ASSERT(expr)
#define CRM_CHECK(expr, failure_action)
#define crm_err(fmt, args...)
#define crm_trace(fmt, args...)
#define pcmk__if_tracing(if_action, else_action)
#define PCMK__ENV_SCHEMA_DIRECTORY
const char * pcmk__env_option(const char *option)
#define pcmk__assert(expr)
#define pcmk__mem_assert(ptr)
const char * pcmk__remote_schema_dir(void)
void pcmk__schema_cleanup(void)
void pcmk__schema_init(void)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
void pcmk__str_update(char **str, const char *value)
bool pcmk__ends_with(const char *s, const char *match)
int position
Position of the deleted node among its siblings.
gchar * path
XPath expression identifying the deleted node.
GList * deleted_objs
XML nodes marked as deleted (list of pcmk__deleted_xml_t)
char * acl_user
User affected by acls (for logging)
GList * acls
ACLs to check requested changes against (list of xml_acl_t)
uint32_t check
Magic number for checking integrity.
uint32_t flags
Group of enum pcmk__xml_flags
uint32_t check
Magic number for checking integrity.
uint32_t flags
Group of enum pcmk__xml_flags
xmlNode * match
Pointer to matching node (defined by caller)
int pcmk__xml_position(const xmlNode *xml, enum pcmk__xml_flags ignore_if_set)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
#define XML_VERSION
libxml2 supports only XML version 1.0, at least as of libxml2-2.12.5
void pcmk__xml_free_private_data(xmlNode *xml)
xmlDoc * pcmk__xml_new_doc(void)
void pcmk__xml_sanitize_id(char *id)
void pcmk__xml_free_node(xmlNode *xml)
bool pcmk__xml_doc_all_flags_set(const xmlDoc *doc, uint32_t flags)
void pcmk__xml_commit_changes(xmlDoc *doc)
void xml_accept_changes(xmlNode *xml)
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
xmlNode * copy_xml(xmlNode *src)
bool pcmk__xml_tree_foreach(xmlNode *xml, bool(*fn)(xmlNode *, void *), void *user_data)
gchar * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
void pcmk__strip_xml_text(xmlNode *xml)
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
void pcmk__xml_new_private_data(xmlNode *xml)
void pcmk__xml_doc_set_flags(xmlDoc *doc, uint32_t flags)
bool xml_tracking_changes(xmlNode *xml)
void crm_xml_cleanup(void)
bool xml_document_dirty(xmlNode *xml)
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
void free_xml(xmlNode *child)
void crm_xml_sanitize_id(char *id)
bool pcmk__xml_is_name_start_char(const char *utf8, int *len)
void pcmk__mark_xml_node_dirty(xmlNode *xml)
void pcmk__xml_free(xmlNode *xml)
void pcmk__xml_free_doc(xmlDoc *doc)
void pcmk__xml_set_parent_flags(xmlNode *xml, uint64_t flags)
bool pcmk__xml_is_name_char(const char *utf8, int *len)
void pcmk_free_xml_subtree(xmlNode *xml)
void pcmk__xml_mark_changes(xmlNode *old_xml, xmlNode *new_xml)
bool pcmk__xml_reset_node_flags(xmlNode *xml, void *user_data)
const char * pcmk__xml_element_type_text(xmlElementType type)
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Wrappers for and extensions to libxml2.
Deprecated Pacemaker XML API.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool force, bool(*match)(xmlAttrPtr, void *), void *user_data)
@ pcmk__xml_escape_attr_pretty
@ pcmk__xf_acl_enabled
ACLs are enabled (set for document only)
@ pcmk__xf_created
Node was created.
@ pcmk__xf_ignore_attr_pos
Ignore attribute moves within an element (set for document only)
@ pcmk__xf_skip
Skip counting this node when getting a node's position among siblings.
@ pcmk__xf_acl_write
ACL write permission (implies read permission in most or all contexts)
@ pcmk__xf_tracking
Tracking is enabled (set for document only)
@ pcmk__xf_none
This flag has no effect.
@ pcmk__xf_moved
Node was moved.
@ pcmk__xml_artefact_ns_legacy_xslt
@ pcmk__xml_artefact_ns_legacy_rng
@ pcmk__xml_artefact_ns_base_rng
@ pcmk__xml_artefact_ns_base_xslt
GString * pcmk__element_xpath(const xmlNode *xml)