Git init
[external/xmlsec1.git] / src / nodeset.c
1 /** 
2  * XML Security Library (http://www.aleksey.com/xmlsec).
3  *
4  * Enchanced nodes set
5  *
6  * This is free software; see Copyright file in the source
7  * distribution for preciese wording.
8  * 
9  * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
10  */
11 #include "globals.h"
12
13 #include <stdlib.h>
14 #include <string.h>
15  
16 #include <libxml/tree.h>
17 #include <libxml/xpath.h>
18 #include <libxml/xpathInternals.h>
19
20 #include <xmlsec/xmlsec.h>
21 #include <xmlsec/nodeset.h>
22 #include <xmlsec/errors.h>
23
24 #define xmlSecGetParent(node)           \
25     (((node)->type != XML_NAMESPACE_DECL) ? \
26         (node)->parent : \
27         (xmlNodePtr)((xmlNsPtr)(node))->next)
28         
29 static int      xmlSecNodeSetOneContains                (xmlSecNodeSetPtr nset, 
30                                                          xmlNodePtr node, 
31                                                          xmlNodePtr parent);
32 static int      xmlSecNodeSetWalkRecursive              (xmlSecNodeSetPtr nset, 
33                                                          xmlSecNodeSetWalkCallback walkFunc, 
34                                                          void* data, 
35                                                          xmlNodePtr cur, 
36                                                          xmlNodePtr parent);
37
38 /**
39  * xmlSecNodeSetCreate:
40  * @doc:                the pointer to parent XML document.
41  * @nodes:              the list of nodes.
42  * @type:               the nodes set type.
43  *
44  * Creates new nodes set. Caller is responsible for freeng returend object
45  * by calling #xmlSecNodeSetDestroy function.
46  *
47  * Returns: pointer to newly allocated node set or NULL if an error occurs.
48  */
49 xmlSecNodeSetPtr
50 xmlSecNodeSetCreate(xmlDocPtr doc, xmlNodeSetPtr nodes, xmlSecNodeSetType type) {
51     xmlSecNodeSetPtr nset;
52
53     nset = (xmlSecNodeSetPtr)xmlMalloc(sizeof(xmlSecNodeSet));
54     if(nset == NULL) { 
55         xmlSecError(XMLSEC_ERRORS_HERE,
56                     NULL,
57                     NULL,
58                     XMLSEC_ERRORS_R_MALLOC_FAILED,
59                     "sizeof(xmlSecNodeSet)=%d",
60                     sizeof(xmlSecNodeSet));
61         return(NULL);
62     }
63     memset(nset, 0,  sizeof(xmlSecNodeSet));
64     
65     nset->doc   = doc;
66     nset->nodes = nodes;
67     nset->type  = type;
68     nset->next  = nset->prev = nset;
69     return(nset);
70 }
71
72 /**
73  * xmlSecNodeSetDestroy:
74  * @nset:               the pointer to node set.
75  *
76  * Destroys the nodes set created with #xmlSecNodeSetCreate function.
77  */
78 void
79 xmlSecNodeSetDestroy(xmlSecNodeSetPtr nset) {
80     xmlSecNodeSetPtr tmp;
81
82     xmlSecAssert(nset != NULL);
83         
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;          
88             nset = nset->next;
89         } else {
90             nset = NULL;
91         }
92         
93         if(tmp->nodes != NULL) {
94             xmlXPathFreeNodeSet(tmp->nodes);
95         }
96         if(tmp->children != NULL) {
97             xmlSecNodeSetDestroy(tmp->children);
98         }
99         if((tmp->doc != NULL) && (tmp->destroyDoc != 0)) {
100             xmlFreeDoc(tmp->doc);
101         }
102         memset(tmp, 0,  sizeof(xmlSecNodeSet));
103         xmlFree(tmp);
104     }
105 }
106
107 /**
108  * xmlSecNodeSetDocDestroy:
109  * @nset:               the pointer to node set.
110  *
111  * Instructs node set to destroy nodes parent doc when node set is destroyed.
112  */
113 void 
114 xmlSecNodeSetDocDestroy(xmlSecNodeSetPtr nset) {
115     xmlSecAssert(nset != NULL);
116     
117     nset->destroyDoc = 1;
118 }
119
120 static int
121 xmlSecNodeSetOneContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
122     int in_nodes_set = 1;
123     
124     xmlSecAssert2(nset != NULL, 0);
125     xmlSecAssert2(node != NULL, 0);
126         
127     /* special cases: */
128     switch(nset->type) {
129         case xmlSecNodeSetTreeWithoutComments:
130         case xmlSecNodeSetTreeWithoutCommentsInvert:
131             if(node->type == XML_COMMENT_NODE) {
132                 return(0);
133             }
134             break;
135         case xmlSecNodeSetList:
136             return(xmlSecNodeSetContains(nset->children, node, parent));
137         default:
138             break;
139     }
140         
141     if(nset->nodes != NULL) {
142         if(node->type != XML_NAMESPACE_DECL) {
143             in_nodes_set = xmlXPathNodeSetContains(nset->nodes, node);
144         } else {
145             xmlNs ns;
146             
147             memcpy(&ns, node, sizeof(ns)); 
148             
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;
152             } else {
153                 ns.next = (xmlNsPtr)parent; 
154             }
155
156             /* 
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.
159              */
160             in_nodes_set = (xmlXPathNodeSetContains(nset->nodes, (xmlNodePtr)&ns));
161         }
162     }
163     
164     switch(nset->type) {
165     case xmlSecNodeSetNormal:
166         return(in_nodes_set);
167     case xmlSecNodeSetInvert:
168         return(!in_nodes_set);
169     case xmlSecNodeSetTree:
170     case xmlSecNodeSetTreeWithoutComments:
171         if(in_nodes_set) {
172             return(1);
173         }
174         if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
175             return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
176         }
177         return(0);
178     case xmlSecNodeSetTreeInvert:
179     case xmlSecNodeSetTreeWithoutCommentsInvert:
180         if(in_nodes_set) {
181             return(0);
182         }
183         if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
184             return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
185         }
186         return(1);
187     default:
188         xmlSecError(XMLSEC_ERRORS_HERE,
189                     NULL,
190                     NULL,
191                     XMLSEC_ERRORS_R_INVALID_TYPE,
192                     "type=%d", nset->type);
193     }
194     
195     return(0);
196 }
197
198 /**
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.
203  *
204  * Checks whether the @node is in the nodes set or not.
205  *
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.
208  */
209 int
210 xmlSecNodeSetContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
211     int status = 1;
212     xmlSecNodeSetPtr cur;
213     
214     xmlSecAssert2(node != NULL, 0);
215     
216     /* special cases: */
217     if(nset == NULL) {
218         return(1);
219     }
220     
221     status = 1;
222     cur = nset;
223     do {
224         switch(cur->op) {
225         case xmlSecNodeSetIntersection:
226             if(status && !xmlSecNodeSetOneContains(cur, node, parent)) {
227                 status = 0;
228             }
229             break;
230         case xmlSecNodeSetSubtraction:
231             if(status && xmlSecNodeSetOneContains(cur, node, parent)) {
232                 status = 0;
233             }
234             break;
235         case xmlSecNodeSetUnion:
236             if(!status && xmlSecNodeSetOneContains(cur, node, parent)) {
237                 status = 1;
238             }   
239             break;
240         default:
241             xmlSecError(XMLSEC_ERRORS_HERE,
242                         NULL,
243                         NULL,
244                         XMLSEC_ERRORS_R_INVALID_OPERATION,
245                         "operation=%d", cur->op);
246             return(-1);
247         }
248         cur = cur->next;
249     } while(cur != nset);
250     
251     return(status);
252 }
253
254 /**
255  * xmlSecNodeSetAdd:
256  * @nset:               the pointer to currrent nodes set (or NULL).
257  * @newNSet:            the pointer to new nodes set.
258  * @op:                 the operation type.
259  *
260  * Adds @newNSet to the @nset using operation @op. 
261  *
262  * Returns: the pointer to combined nodes set or NULL if an error 
263  * occurs.
264  */
265 xmlSecNodeSetPtr        
266 xmlSecNodeSetAdd(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet, 
267                  xmlSecNodeSetOp op) {
268     xmlSecAssert2(newNSet != NULL, NULL);
269     xmlSecAssert2(newNSet->next == newNSet, NULL);
270
271     newNSet->op = op;
272     if(nset == NULL) {
273         return(newNSet);
274     }
275         
276     newNSet->next = nset;
277     newNSet->prev = nset->prev;
278     nset->prev->next = newNSet;
279     nset->prev       = newNSet;
280     return(nset);
281 }
282
283 /**
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.
288  *
289  * Adds @newNSet to the @nset as child using operation @op. 
290  *
291  * Returns: the pointer to combined nodes set or NULL if an error 
292  * occurs.
293  */
294 xmlSecNodeSetPtr        
295 xmlSecNodeSetAddList(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet, xmlSecNodeSetOp op) {
296     xmlSecNodeSetPtr tmp1, tmp2;
297
298     xmlSecAssert2(newNSet != NULL, NULL);
299     
300     tmp1 = xmlSecNodeSetCreate(newNSet->doc, NULL, xmlSecNodeSetList);
301     if(tmp1 == NULL) {
302         xmlSecError(XMLSEC_ERRORS_HERE,
303                     NULL,
304                     "xmlSecNodeSetCreate",
305                     XMLSEC_ERRORS_R_XMLSEC_FAILED,
306                     XMLSEC_ERRORS_NO_MESSAGE);
307         return(NULL);
308     }
309     tmp1->children = newNSet;
310     
311     tmp2 = xmlSecNodeSetAdd(nset, tmp1, op);
312     if(tmp2 == NULL) {
313         xmlSecError(XMLSEC_ERRORS_HERE,
314                     NULL,
315                     "xmlSecNodeSetAdd",
316                     XMLSEC_ERRORS_R_XMLSEC_FAILED,
317                     XMLSEC_ERRORS_NO_MESSAGE);
318         xmlSecNodeSetDestroy(tmp1);
319         return(NULL);
320     }
321     return(tmp2);
322 }
323
324  
325 /**
326  * xmlSecNodeSetWalk:
327  * @nset:               the pointer to node set.
328  * @walkFunc:           the callback functions.
329  * @data:               the application specific data passed to the @walkFunc.
330  *
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 
333  * is interrupted.
334  *
335  * Returns: 0 on success or a negative value if an error occurs.
336  */
337 int
338 xmlSecNodeSetWalk(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc, void* data) {
339     xmlNodePtr cur;
340     int ret = 0;
341     
342     xmlSecAssert2(nset != NULL, -1);
343     xmlSecAssert2(nset->doc != NULL, -1);
344     xmlSecAssert2(walkFunc != NULL, -1);
345
346     /* special cases */
347     if(nset->nodes != NULL) {
348         int i;
349
350         switch(nset->type) {
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]));
358             }
359             return(ret);
360         default:
361             break;
362         }
363     }
364     
365     for(cur = nset->doc->children; (cur != NULL) && (ret >= 0); cur = cur->next) {
366         ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, cur, xmlSecGetParent(cur));
367     }
368     return(ret);
369 }
370
371 static int
372 xmlSecNodeSetWalkRecursive(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc, 
373                             void* data, xmlNodePtr cur, xmlNodePtr parent) {
374     int ret;
375
376     xmlSecAssert2(nset != NULL, -1);
377     xmlSecAssert2(cur != NULL, -1);
378     xmlSecAssert2(walkFunc != NULL, -1);
379     
380     /* the node itself */
381     if(xmlSecNodeSetContains(nset, cur, parent)) {
382         ret = walkFunc(nset, cur, parent, data);
383         
384         if(ret < 0) {
385             return(ret);
386         }
387     }
388         
389     /* element node has attributes, namespaces  */      
390     if(cur->type == XML_ELEMENT_NODE) {
391         xmlAttrPtr attr;
392         xmlNodePtr node;
393         xmlNsPtr ns, tmp;
394         
395         attr = (xmlAttrPtr)cur->properties;
396         while(attr != NULL) {
397             if(xmlSecNodeSetContains(nset, (xmlNodePtr)attr, cur)) {
398                 ret = walkFunc(nset, (xmlNodePtr)attr, cur, data);
399                 if(ret < 0) {
400                     return(ret);
401                 }
402             }
403             attr = attr->next;
404         }
405
406         node = cur;
407         while(node != NULL) {
408             ns = node->nsDef;
409             while(ns != 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);
413                     if(ret < 0) {
414                         return(ret);
415                     }
416                 }
417                 ns = ns->next;
418             }
419             node = node->parent;
420         }
421     }
422
423     /* element and document nodes have children */
424     if((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_DOCUMENT_NODE)) {
425         xmlNodePtr node;
426         
427         node = cur->children;
428         while(node != NULL) {
429             ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, node, cur);
430             if(ret < 0) {
431                 return(ret);
432             }
433             node = node->next;
434         }
435     }
436     return(0);
437 }
438
439 /**
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.
445  *
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 
455  *    and comment nodes.
456  *
457  * Returns: pointer to the newly created #xmlSecNodeSet structure
458  * or NULL if an error occurs.
459  */
460 xmlSecNodeSetPtr        
461 xmlSecNodeSetGetChildren(xmlDocPtr doc, const xmlNodePtr parent, int withComments, int invert) {
462     xmlNodeSetPtr nodes;
463     xmlSecNodeSetType type;
464
465     xmlSecAssert2(doc != NULL, NULL);
466         
467     nodes = xmlXPathNodeSetCreate(parent);
468     if(nodes == NULL) {
469         xmlSecError(XMLSEC_ERRORS_HERE,
470                     NULL,
471                     "xmlXPathNodeSetCreate",
472                     XMLSEC_ERRORS_R_XML_FAILED,
473                     XMLSEC_ERRORS_NO_MESSAGE);
474         return(NULL);
475     }   
476     
477     /* if parent is NULL then we add all the doc children */
478     if(parent == NULL) {
479         xmlNodePtr cur;
480         for(cur = doc->children; cur != NULL; cur = cur->next) {
481             if(withComments || (cur->type != XML_COMMENT_NODE)) {
482                 xmlXPathNodeSetAdd(nodes, cur);
483             }
484         }
485     }
486
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;
495     }
496
497     return(xmlSecNodeSetCreate(doc, nodes, type));
498 }
499
500 static int
501 xmlSecNodeSetDumpTextNodesWalkCallback(xmlSecNodeSetPtr nset, xmlNodePtr cur, 
502                                    xmlNodePtr parent ATTRIBUTE_UNUSED, 
503                                    void* data) {
504     xmlSecAssert2(nset != NULL, -1);
505     xmlSecAssert2(cur != NULL, -1);
506     xmlSecAssert2(data != NULL, -1);
507
508     if(cur->type == XML_TEXT_NODE) {
509         xmlOutputBufferWriteString((xmlOutputBufferPtr)data, 
510                                     (char*)(cur->content)); 
511     }
512     return(0);
513 }
514
515 /**
516  * xmlSecNodeSetDumpTextNodes:
517  * @nset:               the pointer to node set.
518  * @out:                the output buffer.
519  *
520  * Dumps content of all the text nodes from @nset to @out.
521  *
522  * Returns: 0 on success or a negative value otherwise.
523  */
524 int 
525 xmlSecNodeSetDumpTextNodes(xmlSecNodeSetPtr nset, xmlOutputBufferPtr out) {
526     xmlSecAssert2(nset != NULL, -1);
527     xmlSecAssert2(out != NULL, -1);
528
529     return(xmlSecNodeSetWalk(nset, xmlSecNodeSetDumpTextNodesWalkCallback, out));
530 }
531
532 /**
533  * xmlSecNodeSetDebugDump: 
534  * @nset:               the pointer to node set.
535  * @output:             the pointer to output FILE.
536  * 
537  * Prints information about @nset to the @output.
538  */
539 void
540 xmlSecNodeSetDebugDump(xmlSecNodeSetPtr nset, FILE *output) {
541     int i, l;
542     xmlNodePtr cur;
543
544     xmlSecAssert(nset != NULL);
545     xmlSecAssert(output != NULL);
546
547     fprintf(output, "== Nodes set ");
548     switch(nset->type) {
549     case xmlSecNodeSetNormal:
550         fprintf(output, "(xmlSecNodeSetNormal)\n");
551         break;
552     case xmlSecNodeSetInvert:
553         fprintf(output, "(xmlSecNodeSetInvert)\n");
554         break;
555     case xmlSecNodeSetTree:
556         fprintf(output, "(xmlSecNodeSetTree)\n");
557         break;
558     case xmlSecNodeSetTreeWithoutComments:
559         fprintf(output, "(xmlSecNodeSetTreeWithoutComments)\n");
560         break;
561     case xmlSecNodeSetTreeInvert:
562         fprintf(output, "(xmlSecNodeSetTreeInvert)\n");
563         break;
564     case xmlSecNodeSetTreeWithoutCommentsInvert:
565         fprintf(output, "(xmlSecNodeSetTreeWithoutCommentsInvert)\n");
566         break;
567     case xmlSecNodeSetList:
568         fprintf(output, "(xmlSecNodeSetList)\n");
569         fprintf(output, ">>>\n");
570         xmlSecNodeSetDebugDump(nset->children, output);
571         fprintf(output, "<<<\n");
572         return;
573     default:
574         fprintf(output, "(unknown=%d)\n", nset->type);
575         xmlSecError(XMLSEC_ERRORS_HERE,
576                     NULL,
577                     NULL,
578                     XMLSEC_ERRORS_R_INVALID_TYPE,
579                     "type=%d", nset->type);
580     }
581         
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");
588         } else {
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);
597         }
598     }
599 }