2 * XML Security Library (http://www.aleksey.com/xmlsec).
6 * This is free software; see Copyright file in the source
7 * distribution for preciese wording.
9 * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
16 #include <libxml/tree.h>
17 #include <libxml/xpath.h>
18 #include <libxml/xpathInternals.h>
20 #include <xmlsec/xmlsec.h>
21 #include <xmlsec/nodeset.h>
22 #include <xmlsec/errors.h>
24 #define xmlSecGetParent(node) \
25 (((node)->type != XML_NAMESPACE_DECL) ? \
27 (xmlNodePtr)((xmlNsPtr)(node))->next)
29 static int xmlSecNodeSetOneContains (xmlSecNodeSetPtr nset,
32 static int xmlSecNodeSetWalkRecursive (xmlSecNodeSetPtr nset,
33 xmlSecNodeSetWalkCallback walkFunc,
39 * xmlSecNodeSetCreate:
40 * @doc: the pointer to parent XML document.
41 * @nodes: the list of nodes.
42 * @type: the nodes set type.
44 * Creates new nodes set. Caller is responsible for freeng returend object
45 * by calling #xmlSecNodeSetDestroy function.
47 * Returns: pointer to newly allocated node set or NULL if an error occurs.
50 xmlSecNodeSetCreate(xmlDocPtr doc, xmlNodeSetPtr nodes, xmlSecNodeSetType type) {
51 xmlSecNodeSetPtr nset;
53 nset = (xmlSecNodeSetPtr)xmlMalloc(sizeof(xmlSecNodeSet));
55 xmlSecError(XMLSEC_ERRORS_HERE,
58 XMLSEC_ERRORS_R_MALLOC_FAILED,
59 "sizeof(xmlSecNodeSet)=%d",
60 sizeof(xmlSecNodeSet));
63 memset(nset, 0, sizeof(xmlSecNodeSet));
68 nset->next = nset->prev = nset;
73 * xmlSecNodeSetDestroy:
74 * @nset: the pointer to node set.
76 * Destroys the nodes set created with #xmlSecNodeSetCreate function.
79 xmlSecNodeSetDestroy(xmlSecNodeSetPtr nset) {
82 xmlSecAssert(nset != NULL);
84 while((tmp = nset) != NULL) {
85 if((nset->next != NULL) && (nset->next != nset)) {
86 nset->next->prev = nset->prev;
87 nset->prev->next = nset->next;
93 if(tmp->nodes != NULL) {
94 xmlXPathFreeNodeSet(tmp->nodes);
96 if(tmp->children != NULL) {
97 xmlSecNodeSetDestroy(tmp->children);
99 if((tmp->doc != NULL) && (tmp->destroyDoc != 0)) {
100 xmlFreeDoc(tmp->doc);
102 memset(tmp, 0, sizeof(xmlSecNodeSet));
108 * xmlSecNodeSetDocDestroy:
109 * @nset: the pointer to node set.
111 * Instructs node set to destroy nodes parent doc when node set is destroyed.
114 xmlSecNodeSetDocDestroy(xmlSecNodeSetPtr nset) {
115 xmlSecAssert(nset != NULL);
117 nset->destroyDoc = 1;
121 xmlSecNodeSetOneContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
122 int in_nodes_set = 1;
124 xmlSecAssert2(nset != NULL, 0);
125 xmlSecAssert2(node != NULL, 0);
129 case xmlSecNodeSetTreeWithoutComments:
130 case xmlSecNodeSetTreeWithoutCommentsInvert:
131 if(node->type == XML_COMMENT_NODE) {
135 case xmlSecNodeSetList:
136 return(xmlSecNodeSetContains(nset->children, node, parent));
141 if(nset->nodes != NULL) {
142 if(node->type != XML_NAMESPACE_DECL) {
143 in_nodes_set = xmlXPathNodeSetContains(nset->nodes, node);
147 memcpy(&ns, node, sizeof(ns));
149 /* this is a libxml hack! check xpath.c for details */
150 if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
151 ns.next = (xmlNsPtr)parent->parent;
153 ns.next = (xmlNsPtr)parent;
157 * If the input is an XPath node-set, then the node-set must explicitly
158 * contain every node to be rendered to the canonical form.
160 in_nodes_set = (xmlXPathNodeSetContains(nset->nodes, (xmlNodePtr)&ns));
165 case xmlSecNodeSetNormal:
166 return(in_nodes_set);
167 case xmlSecNodeSetInvert:
168 return(!in_nodes_set);
169 case xmlSecNodeSetTree:
170 case xmlSecNodeSetTreeWithoutComments:
174 if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
175 return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
178 case xmlSecNodeSetTreeInvert:
179 case xmlSecNodeSetTreeWithoutCommentsInvert:
183 if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
184 return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
188 xmlSecError(XMLSEC_ERRORS_HERE,
191 XMLSEC_ERRORS_R_INVALID_TYPE,
192 "type=%d", nset->type);
199 * xmlSecNodeSetContains:
200 * @nset: the pointer to node set.
201 * @node: the pointer to XML node to check.
202 * @parent: the pointer to @node parent node.
204 * Checks whether the @node is in the nodes set or not.
206 * Returns: 1 if the @node is in the nodes set @nset, 0 if it is not
207 * and a negative value if an error occurs.
210 xmlSecNodeSetContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
212 xmlSecNodeSetPtr cur;
214 xmlSecAssert2(node != NULL, 0);
225 case xmlSecNodeSetIntersection:
226 if(status && !xmlSecNodeSetOneContains(cur, node, parent)) {
230 case xmlSecNodeSetSubtraction:
231 if(status && xmlSecNodeSetOneContains(cur, node, parent)) {
235 case xmlSecNodeSetUnion:
236 if(!status && xmlSecNodeSetOneContains(cur, node, parent)) {
241 xmlSecError(XMLSEC_ERRORS_HERE,
244 XMLSEC_ERRORS_R_INVALID_OPERATION,
245 "operation=%d", cur->op);
249 } while(cur != nset);
256 * @nset: the pointer to currrent nodes set (or NULL).
257 * @newNSet: the pointer to new nodes set.
258 * @op: the operation type.
260 * Adds @newNSet to the @nset using operation @op.
262 * Returns: the pointer to combined nodes set or NULL if an error
266 xmlSecNodeSetAdd(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet,
267 xmlSecNodeSetOp op) {
268 xmlSecAssert2(newNSet != NULL, NULL);
269 xmlSecAssert2(newNSet->next == newNSet, NULL);
276 newNSet->next = nset;
277 newNSet->prev = nset->prev;
278 nset->prev->next = newNSet;
279 nset->prev = newNSet;
284 * xmlSecNodeSetAddList:
285 * @nset: the pointer to currrent nodes set (or NULL).
286 * @newNSet: the pointer to new nodes set.
287 * @op: the operation type.
289 * Adds @newNSet to the @nset as child using operation @op.
291 * Returns: the pointer to combined nodes set or NULL if an error
295 xmlSecNodeSetAddList(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet, xmlSecNodeSetOp op) {
296 xmlSecNodeSetPtr tmp1, tmp2;
298 xmlSecAssert2(newNSet != NULL, NULL);
300 tmp1 = xmlSecNodeSetCreate(newNSet->doc, NULL, xmlSecNodeSetList);
302 xmlSecError(XMLSEC_ERRORS_HERE,
304 "xmlSecNodeSetCreate",
305 XMLSEC_ERRORS_R_XMLSEC_FAILED,
306 XMLSEC_ERRORS_NO_MESSAGE);
309 tmp1->children = newNSet;
311 tmp2 = xmlSecNodeSetAdd(nset, tmp1, op);
313 xmlSecError(XMLSEC_ERRORS_HERE,
316 XMLSEC_ERRORS_R_XMLSEC_FAILED,
317 XMLSEC_ERRORS_NO_MESSAGE);
318 xmlSecNodeSetDestroy(tmp1);
327 * @nset: the pointer to node set.
328 * @walkFunc: the callback functions.
329 * @data: the application specific data passed to the @walkFunc.
331 * Calls the function @walkFunc once per each node in the nodes set @nset.
332 * If the @walkFunc returns a negative value, then the walk procedure
335 * Returns: 0 on success or a negative value if an error occurs.
338 xmlSecNodeSetWalk(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc, void* data) {
342 xmlSecAssert2(nset != NULL, -1);
343 xmlSecAssert2(nset->doc != NULL, -1);
344 xmlSecAssert2(walkFunc != NULL, -1);
347 if(nset->nodes != NULL) {
351 case xmlSecNodeSetNormal:
352 case xmlSecNodeSetTree:
353 case xmlSecNodeSetTreeWithoutComments:
354 for(i = 0; (ret >= 0) && (i < nset->nodes->nodeNr); ++i) {
355 ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data,
356 nset->nodes->nodeTab[i],
357 xmlSecGetParent(nset->nodes->nodeTab[i]));
365 for(cur = nset->doc->children; (cur != NULL) && (ret >= 0); cur = cur->next) {
366 ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, cur, xmlSecGetParent(cur));
372 xmlSecNodeSetWalkRecursive(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc,
373 void* data, xmlNodePtr cur, xmlNodePtr parent) {
376 xmlSecAssert2(nset != NULL, -1);
377 xmlSecAssert2(cur != NULL, -1);
378 xmlSecAssert2(walkFunc != NULL, -1);
380 /* the node itself */
381 if(xmlSecNodeSetContains(nset, cur, parent)) {
382 ret = walkFunc(nset, cur, parent, data);
389 /* element node has attributes, namespaces */
390 if(cur->type == XML_ELEMENT_NODE) {
395 attr = (xmlAttrPtr)cur->properties;
396 while(attr != NULL) {
397 if(xmlSecNodeSetContains(nset, (xmlNodePtr)attr, cur)) {
398 ret = walkFunc(nset, (xmlNodePtr)attr, cur, data);
407 while(node != NULL) {
410 tmp = xmlSearchNs(nset->doc, cur, ns->prefix);
411 if((tmp == ns) && xmlSecNodeSetContains(nset, (xmlNodePtr)ns, cur)) {
412 ret = walkFunc(nset, (xmlNodePtr)ns, cur, data);
423 /* element and document nodes have children */
424 if((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_DOCUMENT_NODE)) {
427 node = cur->children;
428 while(node != NULL) {
429 ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, node, cur);
440 * xmlSecNodeSetGetChildren:
441 * @doc: the pointer to an XML document.
442 * @parent: the pointer to parent XML node or NULL if we want to include all document nodes.
443 * @withComments: the flag include comments or not.
444 * @invert: the "invert" flag.
446 * Creates a new nodes set that contains:
447 * - if @withComments is not 0 and @invert is 0:
448 * all nodes in the @parent subtree;
449 * - if @withComments is 0 and @invert is 0:
450 * all nodes in the @parent subtree except comment nodes;
451 * - if @withComments is not 0 and @invert not is 0:
452 * all nodes in the @doc except nodes in the @parent subtree;
453 * - if @withComments is 0 and @invert is 0:
454 * all nodes in the @doc except nodes in the @parent subtree
457 * Returns: pointer to the newly created #xmlSecNodeSet structure
458 * or NULL if an error occurs.
461 xmlSecNodeSetGetChildren(xmlDocPtr doc, const xmlNodePtr parent, int withComments, int invert) {
463 xmlSecNodeSetType type;
465 xmlSecAssert2(doc != NULL, NULL);
467 nodes = xmlXPathNodeSetCreate(parent);
469 xmlSecError(XMLSEC_ERRORS_HERE,
471 "xmlXPathNodeSetCreate",
472 XMLSEC_ERRORS_R_XML_FAILED,
473 XMLSEC_ERRORS_NO_MESSAGE);
477 /* if parent is NULL then we add all the doc children */
480 for(cur = doc->children; cur != NULL; cur = cur->next) {
481 if(withComments || (cur->type != XML_COMMENT_NODE)) {
482 xmlXPathNodeSetAdd(nodes, cur);
487 if(withComments && invert) {
488 type = xmlSecNodeSetTreeInvert;
489 } else if(withComments && !invert) {
490 type = xmlSecNodeSetTree;
491 } else if(!withComments && invert) {
492 type = xmlSecNodeSetTreeWithoutCommentsInvert;
493 } else { /* if(!withComments && !invert) */
494 type = xmlSecNodeSetTreeWithoutComments;
497 return(xmlSecNodeSetCreate(doc, nodes, type));
501 xmlSecNodeSetDumpTextNodesWalkCallback(xmlSecNodeSetPtr nset, xmlNodePtr cur,
502 xmlNodePtr parent ATTRIBUTE_UNUSED,
504 xmlSecAssert2(nset != NULL, -1);
505 xmlSecAssert2(cur != NULL, -1);
506 xmlSecAssert2(data != NULL, -1);
508 if(cur->type == XML_TEXT_NODE) {
509 xmlOutputBufferWriteString((xmlOutputBufferPtr)data,
510 (char*)(cur->content));
516 * xmlSecNodeSetDumpTextNodes:
517 * @nset: the pointer to node set.
518 * @out: the output buffer.
520 * Dumps content of all the text nodes from @nset to @out.
522 * Returns: 0 on success or a negative value otherwise.
525 xmlSecNodeSetDumpTextNodes(xmlSecNodeSetPtr nset, xmlOutputBufferPtr out) {
526 xmlSecAssert2(nset != NULL, -1);
527 xmlSecAssert2(out != NULL, -1);
529 return(xmlSecNodeSetWalk(nset, xmlSecNodeSetDumpTextNodesWalkCallback, out));
533 * xmlSecNodeSetDebugDump:
534 * @nset: the pointer to node set.
535 * @output: the pointer to output FILE.
537 * Prints information about @nset to the @output.
540 xmlSecNodeSetDebugDump(xmlSecNodeSetPtr nset, FILE *output) {
544 xmlSecAssert(nset != NULL);
545 xmlSecAssert(output != NULL);
547 fprintf(output, "== Nodes set ");
549 case xmlSecNodeSetNormal:
550 fprintf(output, "(xmlSecNodeSetNormal)\n");
552 case xmlSecNodeSetInvert:
553 fprintf(output, "(xmlSecNodeSetInvert)\n");
555 case xmlSecNodeSetTree:
556 fprintf(output, "(xmlSecNodeSetTree)\n");
558 case xmlSecNodeSetTreeWithoutComments:
559 fprintf(output, "(xmlSecNodeSetTreeWithoutComments)\n");
561 case xmlSecNodeSetTreeInvert:
562 fprintf(output, "(xmlSecNodeSetTreeInvert)\n");
564 case xmlSecNodeSetTreeWithoutCommentsInvert:
565 fprintf(output, "(xmlSecNodeSetTreeWithoutCommentsInvert)\n");
567 case xmlSecNodeSetList:
568 fprintf(output, "(xmlSecNodeSetList)\n");
569 fprintf(output, ">>>\n");
570 xmlSecNodeSetDebugDump(nset->children, output);
571 fprintf(output, "<<<\n");
574 fprintf(output, "(unknown=%d)\n", nset->type);
575 xmlSecError(XMLSEC_ERRORS_HERE,
578 XMLSEC_ERRORS_R_INVALID_TYPE,
579 "type=%d", nset->type);
582 l = xmlXPathNodeSetGetLength(nset->nodes);
583 for(i = 0; i < l; ++i) {
584 cur = xmlXPathNodeSetItem(nset->nodes, i);
585 if(cur->type != XML_NAMESPACE_DECL) {
586 fprintf(output, "%d: %s\n", cur->type,
587 (cur->name) ? cur->name : BAD_CAST "null");
589 xmlNsPtr ns = (xmlNsPtr)cur;
590 fprintf(output, "%d: %s=%s (%s:%s)\n", cur->type,
591 (ns->prefix) ? ns->prefix : BAD_CAST "null",
592 (ns->href) ? ns->href : BAD_CAST "null",
593 (((xmlNodePtr)ns->next)->ns &&
594 ((xmlNodePtr)ns->next)->ns->prefix) ?
595 ((xmlNodePtr)ns->next)->ns->prefix : BAD_CAST "null",
596 ((xmlNodePtr)ns->next)->name);