xmlsecurityengine/xmlsec/src/xmlsec_nodeset.c
changeset 0 e35f40988205
child 20 889504eac4fb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmlsecurityengine/xmlsec/src/xmlsec_nodeset.c	Thu Dec 17 09:29:21 2009 +0200
@@ -0,0 +1,616 @@
+/** 
+ * XML Security Library (http://www.aleksey.com/xmlsec).
+ *
+ * Enchanced nodes set
+ *
+ * This is free software; see Copyright file in the source
+ * distribution for preciese wording.
+ * 
+ * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
+ * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. 
+ */
+#include "xmlsec_globals.h"
+
+#include <stdlib.h>
+#include <string.h>
+ 
+#include <libxml2_tree.h>
+#include <libxml2_xpath.h>
+#include <libxml2_xpathinternals.h>
+#include <libxml2_globals.h>
+
+#include "xmlsec_xmlsec.h"
+#include "xmlsec_nodeset.h"
+#include "xmlsec_errors.h"
+
+#define xmlSecGetParent(node)           \
+    (((node)->type != XML_NAMESPACE_DECL) ? \
+        (node)->parent : \
+        (xmlNodePtr)((xmlNsPtr)(node))->next)
+	
+static int	xmlSecNodeSetOneContains		(xmlSecNodeSetPtr nset, 
+							 xmlNodePtr node, 
+							 xmlNodePtr parent);
+static int	xmlSecNodeSetWalkRecursive		(xmlSecNodeSetPtr nset, 
+							 xmlSecNodeSetWalkCallback walkFunc, 
+							 void* data, 
+							 xmlNodePtr cur, 
+							 xmlNodePtr parent);
+
+/**
+ * xmlSecNodeSetCreate:
+ * @doc: 		the pointer to parent XML document.
+ * @nodes: 		the list of nodes.
+ * @type: 		the nodes set type.
+ *
+ * Creates new nodes set. Caller is responsible for freeng returend object
+ * by calling #xmlSecNodeSetDestroy function.
+ *
+ * Returns pointer to newly allocated node set or NULL if an error occurs.
+ */
+EXPORT_C
+xmlSecNodeSetPtr
+xmlSecNodeSetCreate(xmlDocPtr doc, xmlNodeSetPtr nodes, xmlSecNodeSetType type) {
+    xmlSecNodeSetPtr nset;
+
+    nset = (xmlSecNodeSetPtr)xmlMalloc(sizeof(xmlSecNodeSet));
+    if(nset == NULL) { 
+	xmlSecError(XMLSEC_ERRORS_HERE,
+		    NULL,
+		    NULL,
+		    XMLSEC_ERRORS_R_MALLOC_FAILED,
+		    "sizeof(xmlSecNodeSet)=%d",
+		    sizeof(xmlSecNodeSet));
+	return(NULL);
+    }
+    memset(nset, 0,  sizeof(xmlSecNodeSet));
+    
+    nset->doc 	= doc;
+    nset->nodes = nodes;
+    nset->type	= type;
+    nset->next 	= nset->prev = nset;
+    return(nset);
+}
+
+/**
+ * xmlSecNodeSetDestroy:
+ * @nset: 		the pointer to node set.
+ *
+ * Destroys the nodes set created with #xmlSecNodeSetCreate function.
+ */
+EXPORT_C
+void
+xmlSecNodeSetDestroy(xmlSecNodeSetPtr nset) {
+    xmlSecNodeSetPtr tmp;
+
+    xmlSecAssert(nset != NULL);
+    	
+    while((tmp = nset) != NULL) {
+	if((nset->next != NULL) && (nset->next != nset)) {
+	    nset->next->prev = nset->prev;
+	    nset->prev->next = nset->next;	    
+	    nset = nset->next;
+	} else {
+	    nset = NULL;
+	}
+	
+	if(tmp->nodes != NULL) {
+    	    xmlXPathFreeNodeSet(tmp->nodes);
+	}
+	if(tmp->children != NULL) {
+	    xmlSecNodeSetDestroy(tmp->children);
+	}
+	if((tmp->doc != NULL) && (tmp->destroyDoc != 0)) {
+	    xmlFreeDoc(tmp->doc);
+	}
+	memset(tmp, 0,  sizeof(xmlSecNodeSet));
+        xmlFree(tmp);
+    }
+}
+
+/**
+ * xmlSecNodeSetDocDestroy:
+ * @nset: 		the pointer to node set.
+ *
+ * Instructs node set to destroy nodes parent doc when node set is destroyed.
+ */
+EXPORT_C
+void 
+xmlSecNodeSetDocDestroy(xmlSecNodeSetPtr nset) {
+    xmlSecAssert(nset != NULL);
+    
+    nset->destroyDoc = 1;
+}
+
+static int
+xmlSecNodeSetOneContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
+    int in_nodes_set = 1;
+    
+    xmlSecAssert2(nset != NULL, 0);
+    xmlSecAssert2(node != NULL, 0);
+        
+    /* special cases: */
+    switch(nset->type) {
+	case xmlSecNodeSetTreeWithoutComments:
+        case xmlSecNodeSetTreeWithoutCommentsInvert:
+	    if(node->type == XML_COMMENT_NODE) {
+		return(0);
+	    }
+	    break;
+	case xmlSecNodeSetList:
+	    return(xmlSecNodeSetContains(nset->children, node, parent));
+	default:
+	    break;
+    }
+        
+    if(nset->nodes != NULL) {
+	if(node->type != XML_NAMESPACE_DECL) {
+	    in_nodes_set = xmlXPathNodeSetContains(nset->nodes, node);
+	} else {
+	    xmlNs ns;
+	    
+	    memcpy(&ns, node, sizeof(ns)); 
+	    
+	    if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
+		ns.next = (xmlNsPtr)parent->parent;
+	    } else {
+		ns.next = (xmlNsPtr)parent; 
+	    }
+
+	    /* 
+	     * If the input is an XPath node-set, then the node-set must explicitly 
+	     * contain every node to be rendered to the canonical form.
+	     */
+	    in_nodes_set = (xmlXPathNodeSetContains(nset->nodes, (xmlNodePtr)&ns));
+	}
+    }
+    
+    switch(nset->type) {
+    case xmlSecNodeSetNormal:
+	return(in_nodes_set);
+    case xmlSecNodeSetInvert:
+	return(!in_nodes_set);
+    case xmlSecNodeSetTree:
+    case xmlSecNodeSetTreeWithoutComments:
+	if(in_nodes_set) {
+	    return(1);
+	}
+	if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
+	    return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
+	}
+	return(0);
+    case xmlSecNodeSetTreeInvert:
+    case xmlSecNodeSetTreeWithoutCommentsInvert:
+	if(in_nodes_set) {
+	    return(0);
+	}
+	if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
+	    return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
+	}
+	return(1);
+    default:
+	xmlSecError(XMLSEC_ERRORS_HERE,
+		    NULL,
+		    NULL,
+		    XMLSEC_ERRORS_R_INVALID_TYPE,
+		    "type=%d", nset->type);
+    }
+    
+    return(0);
+}
+
+/**
+ * xmlSecNodeSetContains:
+ * @nset: 		the pointer to node set.
+ * @node: 		the pointer to XML node to check.
+ * @parent: 		the pointer to @node parent node.
+ *
+ * Checks whether the @node is in the nodes set or not.
+ *
+ * Returns 1 if the @node is in the nodes set @nset, 0 if it is not
+ * and a negative value if an error occurs.
+ */
+EXPORT_C
+int
+xmlSecNodeSetContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
+    int status = 1;
+    xmlSecNodeSetPtr cur;
+    
+    xmlSecAssert2(node != NULL, 0);
+    
+    /* special cases: */
+    if(nset == NULL) {
+	return(1);
+    }
+    
+    status = 1;
+    cur = nset;
+    do {
+	switch(cur->op) {
+	case xmlSecNodeSetIntersection:
+	    if(status && !xmlSecNodeSetOneContains(cur, node, parent)) {
+		status = 0;
+	    }
+    	    break;
+	case xmlSecNodeSetSubtraction:
+	    if(status && xmlSecNodeSetOneContains(cur, node, parent)) {
+		status = 0;
+	    }
+	    break;
+	case xmlSecNodeSetUnion:
+	    if(!status && xmlSecNodeSetOneContains(cur, node, parent)) {
+		status = 1;
+	    }	
+	    break;
+	default:
+	    xmlSecError(XMLSEC_ERRORS_HERE,
+			NULL,
+			NULL,
+			XMLSEC_ERRORS_R_INVALID_OPERATION,
+			"operation=%d", cur->op);
+	    return(-1);
+	}
+	cur = cur->next;
+    } while(cur != nset);
+    
+    return(status);
+}
+
+/**
+ * xmlSecNodeSetAdd:
+ * @nset: 		the pointer to currrent nodes set (or NULL).
+ * @newNSet: 		the pointer to new nodes set.
+ * @op: 		the operation type.
+ *
+ * Adds @newNSet to the @nset using operation @op. 
+ *
+ * Returns the pointer to combined nodes set or NULL if an error 
+ * occurs.
+ */
+EXPORT_C
+xmlSecNodeSetPtr	
+xmlSecNodeSetAdd(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet, 
+		 xmlSecNodeSetOp op) {
+    xmlSecAssert2(newNSet != NULL, NULL);
+    xmlSecAssert2(newNSet->next == newNSet, NULL);
+
+    newNSet->op	= op;
+    if(nset == NULL) {
+	return(newNSet);
+    }
+        
+    newNSet->next = nset;
+    newNSet->prev = nset->prev;
+    nset->prev->next = newNSet;
+    nset->prev 	     = newNSet;
+    return(nset);
+}
+
+/**
+ * xmlSecNodeSetAddList:
+ * @nset: 		the pointer to currrent nodes set (or NULL).
+ * @newNSet: 		the pointer to new nodes set.
+ * @op: 		the operation type.
+ *
+ * Adds @newNSet to the @nset as child using operation @op. 
+ *
+ * Returns the pointer to combined nodes set or NULL if an error 
+ * occurs.
+ */
+EXPORT_C
+xmlSecNodeSetPtr	
+xmlSecNodeSetAddList(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet, xmlSecNodeSetOp op) {
+    xmlSecNodeSetPtr tmp1, tmp2;
+
+    xmlSecAssert2(newNSet != NULL, NULL);
+    
+    tmp1 = xmlSecNodeSetCreate(newNSet->doc, NULL, xmlSecNodeSetList);
+    if(tmp1 == NULL) {
+	xmlSecError(XMLSEC_ERRORS_HERE,
+		    NULL,
+		    "xmlSecNodeSetCreate",
+		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
+		    XMLSEC_ERRORS_NO_MESSAGE);
+	return(NULL);
+    }
+    tmp1->children = newNSet;
+    
+    tmp2 = xmlSecNodeSetAdd(nset, tmp1, op);
+    if(tmp2 == NULL) {
+	xmlSecError(XMLSEC_ERRORS_HERE,
+		    NULL,
+		    "xmlSecNodeSetAdd",
+		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
+		    XMLSEC_ERRORS_NO_MESSAGE);
+	xmlSecNodeSetDestroy(tmp1);
+	return(NULL);
+    }
+    return(tmp2);
+}
+
+ 
+/**
+ * xmlSecNodeSetWalk:
+ * @nset: 		the pointer to node set.
+ * @walkFunc: 		the callback functions.
+ * @data: 		the application specific data passed to the @walkFunc.
+ *
+ * Calls the function @walkFunc once per each node in the nodes set @nset.
+ * If the @walkFunc returns a negative value, then the walk procedure 
+ * is interrupted.
+ *
+ * Returns 0 on success or a negative value if an error occurs.
+ */
+EXPORT_C
+int
+xmlSecNodeSetWalk(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc, void* data) {
+    xmlNodePtr cur;
+    int ret = 0;
+    
+    xmlSecAssert2(nset != NULL, -1);
+    xmlSecAssert2(nset->doc != NULL, -1);
+    xmlSecAssert2(walkFunc != NULL, -1);
+
+    /* special cases */
+    if(nset->nodes != NULL) {
+	int i;
+
+	switch(nset->type) {
+	case xmlSecNodeSetNormal:
+	case xmlSecNodeSetTree:
+	case xmlSecNodeSetTreeWithoutComments:
+	    for(i = 0; (ret >= 0) && (i < nset->nodes->nodeNr); ++i) {
+		ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, 
+		    nset->nodes->nodeTab[i], 
+		    xmlSecGetParent(nset->nodes->nodeTab[i]));
+	    }
+	    return(ret);
+	default:
+	    break;
+	}
+    }
+    
+    for(cur = nset->doc->children; (cur != NULL) && (ret >= 0); cur = cur->next) {
+	ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, cur, xmlSecGetParent(cur));
+    }
+    return(ret);
+}
+
+static int
+xmlSecNodeSetWalkRecursive(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc, 
+			    void* data, xmlNodePtr cur, xmlNodePtr parent) {
+    int ret;
+
+    xmlSecAssert2(nset != NULL, -1);
+    xmlSecAssert2(cur != NULL, -1);
+    xmlSecAssert2(walkFunc != NULL, -1);
+    
+    /* the node itself */
+    if(xmlSecNodeSetContains(nset, cur, parent)) {
+	ret = walkFunc(nset, cur, parent, data);
+	
+	if(ret < 0) {
+	    return(ret);
+	}
+    }
+	
+    /* element node has attributes, namespaces  */	
+    if(cur->type == XML_ELEMENT_NODE) {
+        xmlAttrPtr attr;
+	xmlNodePtr node;
+	xmlNsPtr ns, tmp;
+	
+        attr = (xmlAttrPtr)cur->properties;
+        while(attr != NULL) {
+	    if(xmlSecNodeSetContains(nset, (xmlNodePtr)attr, cur)) {
+		ret = walkFunc(nset, (xmlNodePtr)attr, cur, data);
+		if(ret < 0) {
+		    return(ret);
+		}
+	    }
+	    attr = attr->next;
+	}
+
+	node = cur;
+	while(node != NULL) {
+    	    ns = node->nsDef;
+    	    while(ns != NULL) {
+		tmp = xmlSearchNs(nset->doc, cur, ns->prefix);
+		if((tmp == ns) && xmlSecNodeSetContains(nset, (xmlNodePtr)ns, cur)) {
+		    ret = walkFunc(nset, (xmlNodePtr)ns, cur, data);
+		    if(ret < 0) {
+			return(ret);
+		    }
+		}
+		ns = ns->next;
+	    }
+	    node = node->parent;
+	}
+    }
+
+    /* element and document nodes have children */
+    if((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_DOCUMENT_NODE)) {
+	xmlNodePtr node;
+	
+	node = cur->children;
+	while(node != NULL) {
+	    ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, node, cur);
+	    if(ret < 0) {
+		return(ret);
+	    }
+	    node = node->next;
+	}
+    }
+    return(0);
+}
+
+/**
+ * xmlSecNodeSetGetChildren:
+ * @doc: 		the pointer to an XML document.
+ * @parent:	 	the pointer to parent XML node or NULL if we want to include all document nodes.
+ * @withComments: 	the flag include  comments or not.
+ * @invert: 		the "invert" flag.
+ *
+ * Creates a new nodes set that contains:
+ *  - if @withComments is not 0 and @invert is 0:
+ *    all nodes in the @parent subtree;
+ *  - if @withComments is 0 and @invert is 0:
+ *    all nodes in the @parent subtree except comment nodes;
+ *  - if @withComments is not 0 and @invert not is 0:
+ *    all nodes in the @doc except nodes in the @parent subtree;
+ *  - if @withComments is 0 and @invert is 0:
+ *    all nodes in the @doc except nodes in the @parent subtree 
+ *    and comment nodes.
+ *
+ * Returns pointer to the newly created #xmlSecNodeSet structure
+ * or NULL if an error occurs.
+ */
+EXPORT_C
+xmlSecNodeSetPtr	
+xmlSecNodeSetGetChildren(xmlDocPtr doc, const xmlNodePtr parent, int withComments, int invert) {
+    xmlNodeSetPtr nodes;
+    xmlSecNodeSetType type;
+    xmlSecNodeSetPtr result = NULL;
+
+    xmlSecAssert2(doc != NULL, NULL);
+        
+    nodes = xmlXPathNodeSetCreate(parent);
+    if(nodes == NULL) {
+	xmlSecError(XMLSEC_ERRORS_HERE,
+		    NULL,
+		    "xmlXPathNodeSetCreate",
+		    XMLSEC_ERRORS_R_XML_FAILED,
+		    XMLSEC_ERRORS_NO_MESSAGE);
+	return(NULL);
+    }	
+    
+    /* if parent is NULL then we add all the doc children */
+    if(parent == NULL) {
+	xmlNodePtr cur;
+	for(cur = doc->children; cur != NULL; cur = cur->next) {
+	    if(withComments || (cur->type != XML_COMMENT_NODE)) {
+	        xmlXPathNodeSetAdd(nodes, cur);
+	    }
+	}
+    }
+
+    if(withComments && invert) {
+	type = xmlSecNodeSetTreeInvert;
+    } else if(withComments && !invert) {
+	type = xmlSecNodeSetTree;
+    } else if(!withComments && invert) {
+	type = xmlSecNodeSetTreeWithoutCommentsInvert;
+    } else { /* if(!withComments && !invert) */
+	type = xmlSecNodeSetTreeWithoutComments;
+    }
+
+    result = xmlSecNodeSetCreate(doc, nodes, type);
+    if ( !result )
+        {
+        xmlXPathFreeNodeSet( nodes );   
+        }
+    return result;
+}
+
+static int
+xmlSecNodeSetDumpTextNodesWalkCallback(xmlSecNodeSetPtr nset, xmlNodePtr cur, 
+				   xmlNodePtr parent, 
+				   void* data) {
+    xmlSecAssert2(nset != NULL, -1);
+    xmlSecAssert2(cur != NULL, -1);
+    xmlSecAssert2(data != NULL, -1);
+
+    if(cur->type == XML_TEXT_NODE) {
+	xmlOutputBufferWriteString((xmlOutputBufferPtr)data, 
+				    (char*)(cur->content)); 
+    }
+    return(0);
+}
+
+/**
+ * xmlSecNodeSetDumpTextNodes:
+ * @nset: 		the pointer to node set.
+ * @out:		the output buffer.
+ *
+ * Dumps content of all the text nodes from @nset to @out.
+ *
+ * Returns 0 on success or a negative value otherwise.
+ */
+EXPORT_C
+int 
+xmlSecNodeSetDumpTextNodes(xmlSecNodeSetPtr nset, xmlOutputBufferPtr out) {
+    xmlSecAssert2(nset != NULL, -1);
+    xmlSecAssert2(out != NULL, -1);
+
+    return(xmlSecNodeSetWalk(nset, xmlSecNodeSetDumpTextNodesWalkCallback, out));
+}
+
+/**
+ * xmlSecNodeSetDebugDump: 
+ * @nset: 		the pointer to node set.
+ * @output: 		the pointer to output FILE.
+ * 
+ * Prints information about @nset to the @output.
+ */
+EXPORT_C
+void
+xmlSecNodeSetDebugDump(xmlSecNodeSetPtr nset, FILE *output) {
+    int i, l;
+    xmlNodePtr cur;
+
+    xmlSecAssert(nset != NULL);
+    xmlSecAssert(output != NULL);
+
+    fprintf(output, "== Nodes set ");
+    switch(nset->type) {
+    case xmlSecNodeSetNormal:
+	fprintf(output, "(xmlSecNodeSetNormal)\n");
+	break;
+    case xmlSecNodeSetInvert:
+	fprintf(output, "(xmlSecNodeSetInvert)\n");
+	break;
+    case xmlSecNodeSetTree:
+	fprintf(output, "(xmlSecNodeSetTree)\n");
+	break;
+    case xmlSecNodeSetTreeWithoutComments:
+	fprintf(output, "(xmlSecNodeSetTreeWithoutComments)\n");
+	break;
+    case xmlSecNodeSetTreeInvert:
+	fprintf(output, "(xmlSecNodeSetTreeInvert)\n");
+	break;
+    case xmlSecNodeSetTreeWithoutCommentsInvert:
+	fprintf(output, "(xmlSecNodeSetTreeWithoutCommentsInvert)\n");
+	break;
+    case xmlSecNodeSetList:
+	fprintf(output, "(xmlSecNodeSetList)\n");
+	fprintf(output, ">>>\n");
+	xmlSecNodeSetDebugDump(nset->children, output);
+	fprintf(output, "<<<\n");
+	return;
+    default:
+	fprintf(output, "(unknown=%d)\n", nset->type);
+	xmlSecError(XMLSEC_ERRORS_HERE,
+		    NULL,
+		    NULL,
+		    XMLSEC_ERRORS_R_INVALID_TYPE,
+		    "type=%d", nset->type);
+    }
+        
+    l = xmlXPathNodeSetGetLength(nset->nodes);
+    for(i = 0; i < l; ++i) {
+	cur = xmlXPathNodeSetItem(nset->nodes, i);
+	if(cur->type != XML_NAMESPACE_DECL) {
+	    fprintf(output, "%d: %s\n", cur->type, 
+		(cur->name) ? cur->name : BAD_CAST "null");
+	} else {
+	    xmlNsPtr ns = (xmlNsPtr)cur;
+	    fprintf(output, "%d: %s=%s (%s:%s)\n", cur->type, 
+		(ns->prefix) ? ns->prefix : BAD_CAST "null",
+		(ns->href) ? ns->href : BAD_CAST "null",
+		(((xmlNodePtr)ns->next)->ns && 
+		 ((xmlNodePtr)ns->next)->ns->prefix) ? 
+		  ((xmlNodePtr)ns->next)->ns->prefix : BAD_CAST "null",		
+		((xmlNodePtr)ns->next)->name);
+	}
+    }
+}