Line data Source code
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 0 : xmlSecNodeSetCreate(xmlDocPtr doc, xmlNodeSetPtr nodes, xmlSecNodeSetType type) {
51 : xmlSecNodeSetPtr nset;
52 :
53 0 : nset = (xmlSecNodeSetPtr)xmlMalloc(sizeof(xmlSecNodeSet));
54 0 : if(nset == NULL) {
55 0 : xmlSecError(XMLSEC_ERRORS_HERE,
56 : NULL,
57 : NULL,
58 : XMLSEC_ERRORS_R_MALLOC_FAILED,
59 : "sizeof(xmlSecNodeSet)=%d",
60 : sizeof(xmlSecNodeSet));
61 0 : return(NULL);
62 : }
63 0 : memset(nset, 0, sizeof(xmlSecNodeSet));
64 :
65 0 : nset->doc = doc;
66 0 : nset->nodes = nodes;
67 0 : nset->type = type;
68 0 : nset->next = nset->prev = nset;
69 0 : 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 0 : xmlSecNodeSetDestroy(xmlSecNodeSetPtr nset) {
80 : xmlSecNodeSetPtr tmp;
81 :
82 0 : xmlSecAssert(nset != NULL);
83 :
84 0 : while((tmp = nset) != NULL) {
85 0 : if((nset->next != NULL) && (nset->next != nset)) {
86 0 : nset->next->prev = nset->prev;
87 0 : nset->prev->next = nset->next;
88 0 : nset = nset->next;
89 : } else {
90 0 : nset = NULL;
91 : }
92 :
93 0 : if(tmp->nodes != NULL) {
94 0 : xmlXPathFreeNodeSet(tmp->nodes);
95 : }
96 0 : if(tmp->children != NULL) {
97 0 : xmlSecNodeSetDestroy(tmp->children);
98 : }
99 0 : if((tmp->doc != NULL) && (tmp->destroyDoc != 0)) {
100 0 : xmlFreeDoc(tmp->doc);
101 : }
102 0 : memset(tmp, 0, sizeof(xmlSecNodeSet));
103 0 : 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 0 : xmlSecNodeSetDocDestroy(xmlSecNodeSetPtr nset) {
115 0 : xmlSecAssert(nset != NULL);
116 :
117 0 : nset->destroyDoc = 1;
118 : }
119 :
120 : static int
121 0 : xmlSecNodeSetOneContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
122 0 : int in_nodes_set = 1;
123 :
124 0 : xmlSecAssert2(nset != NULL, 0);
125 0 : xmlSecAssert2(node != NULL, 0);
126 :
127 : /* special cases: */
128 0 : switch(nset->type) {
129 : case xmlSecNodeSetTreeWithoutComments:
130 : case xmlSecNodeSetTreeWithoutCommentsInvert:
131 0 : if(node->type == XML_COMMENT_NODE) {
132 0 : return(0);
133 : }
134 0 : break;
135 : case xmlSecNodeSetList:
136 0 : return(xmlSecNodeSetContains(nset->children, node, parent));
137 : default:
138 0 : break;
139 : }
140 :
141 0 : if(nset->nodes != NULL) {
142 0 : if(node->type != XML_NAMESPACE_DECL) {
143 0 : in_nodes_set = xmlXPathNodeSetContains(nset->nodes, node);
144 : } else {
145 : xmlNs ns;
146 :
147 0 : memcpy(&ns, node, sizeof(ns));
148 :
149 : /* this is a libxml hack! check xpath.c for details */
150 0 : if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
151 0 : ns.next = (xmlNsPtr)parent->parent;
152 : } else {
153 0 : 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 0 : in_nodes_set = (xmlXPathNodeSetContains(nset->nodes, (xmlNodePtr)&ns));
161 : }
162 : }
163 :
164 0 : switch(nset->type) {
165 : case xmlSecNodeSetNormal:
166 0 : return(in_nodes_set);
167 : case xmlSecNodeSetInvert:
168 0 : return(!in_nodes_set);
169 : case xmlSecNodeSetTree:
170 : case xmlSecNodeSetTreeWithoutComments:
171 0 : if(in_nodes_set) {
172 0 : return(1);
173 : }
174 0 : if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
175 0 : return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
176 : }
177 0 : return(0);
178 : case xmlSecNodeSetTreeInvert:
179 : case xmlSecNodeSetTreeWithoutCommentsInvert:
180 0 : if(in_nodes_set) {
181 0 : return(0);
182 : }
183 0 : if((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) {
184 0 : return(xmlSecNodeSetOneContains(nset, parent, parent->parent));
185 : }
186 0 : return(1);
187 : default:
188 0 : xmlSecError(XMLSEC_ERRORS_HERE,
189 : NULL,
190 : NULL,
191 : XMLSEC_ERRORS_R_INVALID_TYPE,
192 0 : "type=%d", nset->type);
193 : }
194 :
195 0 : 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 0 : xmlSecNodeSetContains(xmlSecNodeSetPtr nset, xmlNodePtr node, xmlNodePtr parent) {
211 0 : int status = 1;
212 : xmlSecNodeSetPtr cur;
213 :
214 0 : xmlSecAssert2(node != NULL, 0);
215 :
216 : /* special cases: */
217 0 : if(nset == NULL) {
218 0 : return(1);
219 : }
220 :
221 0 : status = 1;
222 0 : cur = nset;
223 : do {
224 0 : switch(cur->op) {
225 : case xmlSecNodeSetIntersection:
226 0 : if(status && !xmlSecNodeSetOneContains(cur, node, parent)) {
227 0 : status = 0;
228 : }
229 0 : break;
230 : case xmlSecNodeSetSubtraction:
231 0 : if(status && xmlSecNodeSetOneContains(cur, node, parent)) {
232 0 : status = 0;
233 : }
234 0 : break;
235 : case xmlSecNodeSetUnion:
236 0 : if(!status && xmlSecNodeSetOneContains(cur, node, parent)) {
237 0 : status = 1;
238 : }
239 0 : break;
240 : default:
241 0 : xmlSecError(XMLSEC_ERRORS_HERE,
242 : NULL,
243 : NULL,
244 : XMLSEC_ERRORS_R_INVALID_OPERATION,
245 0 : "operation=%d", cur->op);
246 0 : return(-1);
247 : }
248 0 : cur = cur->next;
249 0 : } while(cur != nset);
250 :
251 0 : 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 0 : xmlSecNodeSetAdd(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet,
267 : xmlSecNodeSetOp op) {
268 0 : xmlSecAssert2(newNSet != NULL, NULL);
269 0 : xmlSecAssert2(newNSet->next == newNSet, NULL);
270 :
271 0 : newNSet->op = op;
272 0 : if(nset == NULL) {
273 0 : return(newNSet);
274 : }
275 :
276 0 : newNSet->next = nset;
277 0 : newNSet->prev = nset->prev;
278 0 : nset->prev->next = newNSet;
279 0 : nset->prev = newNSet;
280 0 : 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 0 : xmlSecNodeSetAddList(xmlSecNodeSetPtr nset, xmlSecNodeSetPtr newNSet, xmlSecNodeSetOp op) {
296 : xmlSecNodeSetPtr tmp1, tmp2;
297 :
298 0 : xmlSecAssert2(newNSet != NULL, NULL);
299 :
300 0 : tmp1 = xmlSecNodeSetCreate(newNSet->doc, NULL, xmlSecNodeSetList);
301 0 : if(tmp1 == NULL) {
302 0 : xmlSecError(XMLSEC_ERRORS_HERE,
303 : NULL,
304 : "xmlSecNodeSetCreate",
305 : XMLSEC_ERRORS_R_XMLSEC_FAILED,
306 : XMLSEC_ERRORS_NO_MESSAGE);
307 0 : return(NULL);
308 : }
309 0 : tmp1->children = newNSet;
310 :
311 0 : tmp2 = xmlSecNodeSetAdd(nset, tmp1, op);
312 0 : if(tmp2 == NULL) {
313 0 : xmlSecError(XMLSEC_ERRORS_HERE,
314 : NULL,
315 : "xmlSecNodeSetAdd",
316 : XMLSEC_ERRORS_R_XMLSEC_FAILED,
317 : XMLSEC_ERRORS_NO_MESSAGE);
318 0 : xmlSecNodeSetDestroy(tmp1);
319 0 : return(NULL);
320 : }
321 0 : 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 0 : xmlSecNodeSetWalk(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc, void* data) {
339 : xmlNodePtr cur;
340 0 : int ret = 0;
341 :
342 0 : xmlSecAssert2(nset != NULL, -1);
343 0 : xmlSecAssert2(nset->doc != NULL, -1);
344 0 : xmlSecAssert2(walkFunc != NULL, -1);
345 :
346 : /* special cases */
347 0 : if(nset->nodes != NULL) {
348 : int i;
349 :
350 0 : switch(nset->type) {
351 : case xmlSecNodeSetNormal:
352 : case xmlSecNodeSetTree:
353 : case xmlSecNodeSetTreeWithoutComments:
354 0 : for(i = 0; (ret >= 0) && (i < nset->nodes->nodeNr); ++i) {
355 0 : ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data,
356 0 : nset->nodes->nodeTab[i],
357 0 : xmlSecGetParent(nset->nodes->nodeTab[i]));
358 : }
359 0 : return(ret);
360 : default:
361 0 : break;
362 : }
363 : }
364 :
365 0 : for(cur = nset->doc->children; (cur != NULL) && (ret >= 0); cur = cur->next) {
366 0 : ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, cur, xmlSecGetParent(cur));
367 : }
368 0 : return(ret);
369 : }
370 :
371 : static int
372 0 : xmlSecNodeSetWalkRecursive(xmlSecNodeSetPtr nset, xmlSecNodeSetWalkCallback walkFunc,
373 : void* data, xmlNodePtr cur, xmlNodePtr parent) {
374 : int ret;
375 :
376 0 : xmlSecAssert2(nset != NULL, -1);
377 0 : xmlSecAssert2(cur != NULL, -1);
378 0 : xmlSecAssert2(walkFunc != NULL, -1);
379 :
380 : /* the node itself */
381 0 : if(xmlSecNodeSetContains(nset, cur, parent)) {
382 0 : ret = walkFunc(nset, cur, parent, data);
383 :
384 0 : if(ret < 0) {
385 0 : return(ret);
386 : }
387 : }
388 :
389 : /* element node has attributes, namespaces */
390 0 : if(cur->type == XML_ELEMENT_NODE) {
391 : xmlAttrPtr attr;
392 : xmlNodePtr node;
393 : xmlNsPtr ns, tmp;
394 :
395 0 : attr = (xmlAttrPtr)cur->properties;
396 0 : while(attr != NULL) {
397 0 : if(xmlSecNodeSetContains(nset, (xmlNodePtr)attr, cur)) {
398 0 : ret = walkFunc(nset, (xmlNodePtr)attr, cur, data);
399 0 : if(ret < 0) {
400 0 : return(ret);
401 : }
402 : }
403 0 : attr = attr->next;
404 : }
405 :
406 0 : node = cur;
407 0 : while(node != NULL) {
408 0 : ns = node->nsDef;
409 0 : while(ns != NULL) {
410 0 : tmp = xmlSearchNs(nset->doc, cur, ns->prefix);
411 0 : if((tmp == ns) && xmlSecNodeSetContains(nset, (xmlNodePtr)ns, cur)) {
412 0 : ret = walkFunc(nset, (xmlNodePtr)ns, cur, data);
413 0 : if(ret < 0) {
414 0 : return(ret);
415 : }
416 : }
417 0 : ns = ns->next;
418 : }
419 0 : node = node->parent;
420 : }
421 : }
422 :
423 : /* element and document nodes have children */
424 0 : if((cur->type == XML_ELEMENT_NODE) || (cur->type == XML_DOCUMENT_NODE)) {
425 : xmlNodePtr node;
426 :
427 0 : node = cur->children;
428 0 : while(node != NULL) {
429 0 : ret = xmlSecNodeSetWalkRecursive(nset, walkFunc, data, node, cur);
430 0 : if(ret < 0) {
431 0 : return(ret);
432 : }
433 0 : node = node->next;
434 : }
435 : }
436 0 : 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 0 : xmlSecNodeSetGetChildren(xmlDocPtr doc, const xmlNodePtr parent, int withComments, int invert) {
462 : xmlNodeSetPtr nodes;
463 : xmlSecNodeSetType type;
464 :
465 0 : xmlSecAssert2(doc != NULL, NULL);
466 :
467 0 : nodes = xmlXPathNodeSetCreate(parent);
468 0 : if(nodes == NULL) {
469 0 : xmlSecError(XMLSEC_ERRORS_HERE,
470 : NULL,
471 : "xmlXPathNodeSetCreate",
472 : XMLSEC_ERRORS_R_XML_FAILED,
473 : XMLSEC_ERRORS_NO_MESSAGE);
474 0 : return(NULL);
475 : }
476 :
477 : /* if parent is NULL then we add all the doc children */
478 0 : if(parent == NULL) {
479 : xmlNodePtr cur;
480 0 : for(cur = doc->children; cur != NULL; cur = cur->next) {
481 0 : if(withComments || (cur->type != XML_COMMENT_NODE)) {
482 0 : xmlXPathNodeSetAdd(nodes, cur);
483 : }
484 : }
485 : }
486 :
487 0 : if(withComments && invert) {
488 0 : type = xmlSecNodeSetTreeInvert;
489 0 : } else if(withComments && !invert) {
490 0 : type = xmlSecNodeSetTree;
491 0 : } else if(!withComments && invert) {
492 0 : type = xmlSecNodeSetTreeWithoutCommentsInvert;
493 : } else { /* if(!withComments && !invert) */
494 0 : type = xmlSecNodeSetTreeWithoutComments;
495 : }
496 :
497 0 : return(xmlSecNodeSetCreate(doc, nodes, type));
498 : }
499 :
500 : static int
501 0 : xmlSecNodeSetDumpTextNodesWalkCallback(xmlSecNodeSetPtr nset, xmlNodePtr cur,
502 : xmlNodePtr parent ATTRIBUTE_UNUSED,
503 : void* data) {
504 0 : xmlSecAssert2(nset != NULL, -1);
505 0 : xmlSecAssert2(cur != NULL, -1);
506 0 : xmlSecAssert2(data != NULL, -1);
507 :
508 0 : if(cur->type == XML_TEXT_NODE) {
509 0 : xmlOutputBufferWriteString((xmlOutputBufferPtr)data,
510 0 : (char*)(cur->content));
511 : }
512 0 : 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 0 : xmlSecNodeSetDumpTextNodes(xmlSecNodeSetPtr nset, xmlOutputBufferPtr out) {
526 0 : xmlSecAssert2(nset != NULL, -1);
527 0 : xmlSecAssert2(out != NULL, -1);
528 :
529 0 : 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 0 : xmlSecNodeSetDebugDump(xmlSecNodeSetPtr nset, FILE *output) {
541 : int i, l;
542 : xmlNodePtr cur;
543 :
544 0 : xmlSecAssert(nset != NULL);
545 0 : xmlSecAssert(output != NULL);
546 :
547 0 : fprintf(output, "== Nodes set ");
548 0 : switch(nset->type) {
549 : case xmlSecNodeSetNormal:
550 0 : fprintf(output, "(xmlSecNodeSetNormal)\n");
551 0 : break;
552 : case xmlSecNodeSetInvert:
553 0 : fprintf(output, "(xmlSecNodeSetInvert)\n");
554 0 : break;
555 : case xmlSecNodeSetTree:
556 0 : fprintf(output, "(xmlSecNodeSetTree)\n");
557 0 : break;
558 : case xmlSecNodeSetTreeWithoutComments:
559 0 : fprintf(output, "(xmlSecNodeSetTreeWithoutComments)\n");
560 0 : break;
561 : case xmlSecNodeSetTreeInvert:
562 0 : fprintf(output, "(xmlSecNodeSetTreeInvert)\n");
563 0 : break;
564 : case xmlSecNodeSetTreeWithoutCommentsInvert:
565 0 : fprintf(output, "(xmlSecNodeSetTreeWithoutCommentsInvert)\n");
566 0 : break;
567 : case xmlSecNodeSetList:
568 0 : fprintf(output, "(xmlSecNodeSetList)\n");
569 0 : fprintf(output, ">>>\n");
570 0 : xmlSecNodeSetDebugDump(nset->children, output);
571 0 : fprintf(output, "<<<\n");
572 0 : return;
573 : default:
574 0 : fprintf(output, "(unknown=%d)\n", nset->type);
575 0 : xmlSecError(XMLSEC_ERRORS_HERE,
576 : NULL,
577 : NULL,
578 : XMLSEC_ERRORS_R_INVALID_TYPE,
579 0 : "type=%d", nset->type);
580 : }
581 :
582 0 : l = xmlXPathNodeSetGetLength(nset->nodes);
583 0 : for(i = 0; i < l; ++i) {
584 0 : cur = xmlXPathNodeSetItem(nset->nodes, i);
585 0 : if(cur->type != XML_NAMESPACE_DECL) {
586 0 : fprintf(output, "%d: %s\n", cur->type,
587 0 : (cur->name) ? cur->name : BAD_CAST "null");
588 : } else {
589 0 : xmlNsPtr ns = (xmlNsPtr)cur;
590 0 : fprintf(output, "%d: %s=%s (%s:%s)\n", cur->type,
591 0 : (ns->prefix) ? ns->prefix : BAD_CAST "null",
592 0 : (ns->href) ? ns->href : BAD_CAST "null",
593 0 : (((xmlNodePtr)ns->next)->ns &&
594 0 : ((xmlNodePtr)ns->next)->ns->prefix) ?
595 0 : ((xmlNodePtr)ns->next)->ns->prefix : BAD_CAST "null",
596 0 : ((xmlNodePtr)ns->next)->name);
597 : }
598 : }
599 : }
|