tizen 2.3.1 release
[framework/web/wearable/wrt-security.git] / ace / engine / parser.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 #include <memory>
17 #include <functional>
18 #include <string.h>
19 #include <stdarg.h>
20 #include <dpl/log/log.h>
21
22 #include <ace/parser.h>
23 #include <string.h>
24
25 namespace {
26
27 class ParserWarningLogger
28 {
29   public:
30     void operator()(const std::string& logMsg)
31     {
32         LogWarning(logMsg);
33     }
34 };
35
36 class ParserErrorLogger
37 {
38   public:
39     void operator()(const std::string& logMsg)
40     {
41         LogError(logMsg);
42     }
43 };
44
45 template <class Logger>
46 void xmlLogFunction(void* /*ctx*/, const char *msg, ...)
47 {
48     const int BUFFER_SIZE = 1024;
49     char buffer[BUFFER_SIZE];
50     buffer[BUFFER_SIZE - 1] = '\0';
51     Logger l;
52
53     va_list va;
54     va_start(va, msg);
55     vsnprintf(buffer, BUFFER_SIZE - 1, msg, va);
56     va_end(va);
57
58     std::string logmsg(buffer);
59     l(logmsg);
60 }
61
62 }
63
64 const char *Parser::TOKEN_PARAM = "param:";
65
66 Parser::Parser() :
67     ruleId(0),
68     reader(NULL),
69     root(NULL),
70     currentRoot(NULL),
71     currentSubject(NULL),
72     currentCondition(NULL),
73     currentAttribute(NULL),
74     currentText(NULL),
75     processingSignature(false),
76     canonicalizeOnce(false)
77 {
78     processingSignature = true;
79     canonicalizeOnce = true;
80 }
81
82 Parser::~Parser()
83 {
84     /* parse function destroys reader */
85     //  free(this->xmlFilename);
86 }
87
88 TreeNode* Parser::parse(const std::string& filename, const std::string& schema)
89 {
90     if(root != NULL) {
91         root->releaseResources();
92         root = NULL;
93     }
94
95     LogDebug("Parser: opening file " << filename);
96
97     xmlDocPtr xmlDocument = xmlParseFile(filename.c_str());
98     if (!xmlDocument) {
99         LogError("Couldn't parse file " << filename);
100         return root;
101     }
102
103     std::unique_ptr <xmlDoc, std::function<void(xmlDoc*)> >
104         doc(xmlDocument, xmlFreeDoc);
105
106     xmlSchemaParserCtxtPtr xmlSchemaParserContext =
107         xmlSchemaNewParserCtxt(schema.c_str());
108
109     if (!xmlSchemaParserContext) {
110         LogError("Couldn't load xml schema: " << schema);
111         return root;
112     }
113
114     std::unique_ptr <
115                      xmlSchemaParserCtxt,
116                      std::function<void(xmlSchemaParserCtxt*)> >
117                      schemaContext(
118                                    xmlSchemaParserContext,
119                                    xmlSchemaFreeParserCtxt);
120
121     LogDebug("Setting callbacks");
122
123     xmlSchemaSetParserErrors(
124         schemaContext.get(),
125         static_cast<xmlValidityErrorFunc>
126             (&xmlLogFunction<ParserErrorLogger>),
127         static_cast<xmlValidityWarningFunc>
128             (&xmlLogFunction<ParserWarningLogger>),
129         NULL);
130
131     xmlSchemaPtr xmlSchema = xmlSchemaParse(schemaContext.get());
132
133     if (!xmlSchema) {
134         LogError("Couldn't parse xml schema: " << xmlSchema);
135         return root;
136     }
137
138     xmlSchemaValidCtxtPtr xmlValidContext = xmlSchemaNewValidCtxt(xmlSchema);
139
140     if (!xmlValidContext) {
141         LogError("Couldn't create validation context!");
142         xmlSchemaFree(xmlSchema);
143         return root;
144     }
145
146     std::unique_ptr <
147                      xmlSchemaValidCtxt,
148                      std::function<void(xmlSchemaValidCtxt*)> >
149                      schemaValidContext(
150                                         xmlValidContext,
151                                         xmlSchemaFreeValidCtxt);
152
153     xmlSchemaSetValidErrors(
154         schemaValidContext.get(),
155         static_cast<xmlValidityErrorFunc>
156             (&xmlLogFunction<ParserErrorLogger>),
157         static_cast<xmlValidityWarningFunc>
158             (&xmlLogFunction<ParserWarningLogger>),
159         NULL);
160
161     xmlSchemaSetValidOptions(
162                              schemaValidContext.get(),
163                              XML_SCHEMA_VAL_VC_I_CREATE);
164
165     bool result =
166         (xmlSchemaValidateDoc(
167                               schemaValidContext.get(),
168                               xmlDocument) == 0 ? true : false);
169
170     xmlSchemaFree(xmlSchema);
171
172     if (!result) {
173         LogError("Couldn't validate policy file: " << filename <<
174                  " against xml schema: " << schema);
175
176         return root;
177     }
178
179     LogInfo("Policy file: " << filename << " validated!");
180
181     xmlTextReaderPtr xmlReader = xmlReaderWalker(xmlDocument);
182
183     //[CR] consider using ASSERT/DASSERT
184     if (NULL == xmlReader) {
185         LogError("Error, xml reader cannot be created. Probably xml file is missing (opening file " << filename << ")");
186         return root;
187     }
188
189     std::unique_ptr <xmlTextReader, std::function<void(xmlTextReader*)> >
190          reader(xmlReader, xmlFreeTextReader);
191
192     int ret;
193     ret = xmlTextReaderRead(reader.get());
194     while (ret == 1) {
195         std::unique_ptr<xmlChar, std::function<void(xmlChar*)> >
196             name(xmlTextReaderName(reader.get()), xmlFree);
197
198         if (!strcmp("policy-set", (const char *)name.get())) {
199             processingSignature = false;
200         } else if (!strcmp("SignedInfo",
201                            (const char *)name.get()) && canonicalizeOnce) {
202             #if 0 //TODO I think we don't need canonicalization in ACE only in PM,
203             //we have to  verify it tough
204             extractNodeToFile(reader, "output.xml");
205             //TODO we should be able to handle more than one canonicalization algorithm
206             canonicalize("output.xml", "canon.xml", Canonicalization::C14N);
207             canonicalizeOnce = false;
208             #endif
209         }
210         //Do not process signature of xml file
211         if(!processingSignature) {
212             processNode(reader.get());
213         }
214         ret = xmlTextReaderRead(reader.get());
215     }
216
217     if (ret != 0) {
218         LogError("Error while parsing XML file");
219         if (root) {
220             root->releaseResources();
221             root = NULL;
222         }
223     }
224
225     return root;
226 }
227
228 void Parser::processNode(xmlTextReaderPtr reader)
229 {
230     //TODO this is interesting, xmlTextReaderNodeType returns int but I am pretty sure
231     //those integers coresponds to xmlReaderTypes
232     xmlReaderTypes type =
233         static_cast<xmlReaderTypes>(xmlTextReaderNodeType(reader));
234
235     switch (type) {
236     //Start element
237     case XML_READER_TYPE_ELEMENT:
238         startNodeHandler(reader);
239         break;
240     //End element
241     case XML_READER_TYPE_END_ELEMENT:
242         endNodeHandler(reader);
243         break;
244     //Text element
245     case XML_READER_TYPE_TEXT:
246         textNodeHandler(reader);
247         break;
248     default:
249         //Do not handle other xml tags
250         break;
251     }
252 }
253
254 void Parser::startNodeHandler(xmlTextReaderPtr reader)
255 {
256     xmlChar *name = xmlTextReaderName(reader);
257
258     Assert(name != NULL && "xml is corrupted");
259     if (name == NULL)
260         return;
261
262     switch (*name) {
263     case 'p':     //policy and policy-set
264         if (*(name + 6) == 0) {
265             handlePolicy(reader, TreeNode::Policy);
266         } else {
267             handlePolicy(reader, TreeNode::PolicySet);
268         }
269         break;
270     case 'r':     //rule and resource-match
271         if (*(name + 1) == 'u') {
272             handleRule(reader);
273         } else if (*(name + 9) == 'm') {
274             handleMatch(reader, Attribute::Type::Resource);
275         } else {
276             handleAttr(reader);
277         }
278         break;
279     case 's':     //subject and subject-match
280         if (*(name + 7) == 0) {
281             handleSubject();
282         } else if (*(name + 8) == 'm') { //subject match
283             handleSubjectMatch(reader);
284         } else {  //subject attr
285             handleAttr(reader);
286         }
287         break;
288     case 'c':    //condition
289         handleCondition(reader);
290         break;
291     case 'e':    //environment-match
292         if (*(name + 12) == 'm') {
293             handleMatch(reader, Attribute::Type::Environment);
294         } else {  //env-attr
295             handleAttr(reader);
296         }
297         break;
298     }
299     xmlFree(name);
300 }
301
302 void Parser::endNodeHandler(xmlTextReaderPtr reader)
303 {
304     xmlChar *name = xmlTextReaderName(reader);
305
306     Assert(name != NULL && "xml file corrupted");
307     if (name == NULL) return;
308
309     switch (*name) {
310     case 'p':     //policy and policy-set
311         //Restore old root
312         currentRoot = currentRoot->getParent();
313         break;
314     case 'r':     //Rule and resource match
315         if (*(name + 1) == 'u') { //Rule
316             currentRoot = currentRoot->getParent();
317         } else {  //Resource-match
318             consumeCurrentText();     //consume text if any available
319             consumeCurrentAttribute();     //consume attribute
320         }
321         break;
322     case 's':     //subject and subject-match
323         if (*(name + 7) == 0) { //handle subject
324             consumeCurrentSubject();
325         } else if (*(name + 8) == 'm') { //handle subject match
326             consumeCurrentText();
327             consumeSubjectMatch();
328         }
329         //Subject-match end doesn't require handling
330         break;
331     case 'c':    //condition
332         consumeCurrentCondition();
333         break;
334     case 'e':    //environment-match
335         consumeCurrentText();     //consume text if any available
336         consumeCurrentAttribute();     //consume attribute
337         break;
338     }
339     xmlFree(name);
340 }
341
342 void Parser::textNodeHandler(xmlTextReaderPtr reader)
343 {
344     delete currentText;
345     xmlChar * text = xmlTextReaderValue(reader);
346     Assert(text != NULL && "Parser couldn't parse PCDATA");
347
348     currentText = new std::string(reinterpret_cast<const char * >(text));
349     trim(currentText);
350     xmlFree(text);
351 }
352
353 void Parser::handlePolicy(xmlTextReaderPtr reader,
354         TreeNode::TypeID type)
355 {
356     Policy::CombineAlgorithm algorithm;
357
358     //Get first attribute
359     xmlChar * combAlg = xmlTextReaderGetAttribute(reader, BAD_CAST("combine"));
360
361     Assert(combAlg != NULL && "Parser error while getting attributes");
362     algorithm = convertToCombineAlgorithm(combAlg);
363
364     //Create TreeNode element
365     Policy * policy = NULL;
366     if (type == TreeNode::Policy) {
367         policy = new Policy();
368     } else {
369         policy = new PolicySet();
370     }
371     policy->setCombineAlgorithm(algorithm);
372     TreeNode * node = new TreeNode(currentRoot, type, policy);
373     //Add new tree node to current's root children set
374     if (currentRoot != NULL) {
375         currentRoot->addChild(node);
376     }
377
378     //Switch the current root to the new node
379     if (!xmlTextReaderIsEmptyElement(reader)) {
380         //Current root switching is necessary only if tag is not empty
381         currentRoot = node;
382     }
383     if (root == NULL) {
384         root = currentRoot;
385     }
386
387     if (NULL == currentRoot) {
388         node->releaseResources();
389     }
390
391     xmlFree(combAlg);
392 }
393
394 void Parser::handleRule(xmlTextReaderPtr reader)
395 {
396     ExtendedEffect effect(Inapplicable);
397
398     //[CR] create macros for attribute names
399     xmlChar * eff = xmlTextReaderGetAttribute(reader, BAD_CAST("effect")); //get the rule attribute
400
401     Assert(eff != NULL && "Parser error while getting attributes");
402     effect = convertToEffect(eff);
403
404     Rule * rule = NULL;
405     rule = new Rule();
406     rule->setEffect(effect);
407
408     TreeNode * node = new TreeNode(currentRoot, TreeNode::Rule, rule);
409     //Add new tree node to current's root children set
410     if (currentRoot != NULL) { //
411         currentRoot->addChild(node);
412     }
413
414     if (!xmlTextReaderIsEmptyElement(reader)) {
415         currentRoot = node;
416     }
417
418     if (NULL == currentRoot) {
419         node->releaseResources();
420     }
421
422     xmlFree(eff);
423 }
424
425 void Parser::handleSubject()
426 {
427     currentSubject = new Subject();
428     //TODO what about empty subject tag
429 }
430
431 void Parser::handleCondition(xmlTextReaderPtr reader)
432 {
433     Condition::CombineType combineType = Condition::AND;
434
435     xmlChar * combine = xmlTextReaderGetAttribute(reader, BAD_CAST("combine")); //get the rule attribute
436
437     Assert(combine != NULL && "Parser error while getting attributes");
438
439     combineType = *combine == 'a' ? Condition::AND : Condition::OR;
440
441     Condition * condition = new Condition();
442     condition->setCombineType(combineType);
443     condition->setParent(currentCondition);
444
445     currentCondition = condition;
446     xmlFree(combine);
447     //TODO what about empty condition tag?
448 }
449
450 //Subject match is handled differently than resource or environment match
451 //Because it cannot have any children tags and can only include PCDATA
452 void Parser::handleSubjectMatch(xmlTextReaderPtr reader)
453 {
454     //processing Subject
455     int attributes = xmlTextReaderAttributeCount(reader);
456
457     xmlChar * func = NULL;
458     xmlChar * value = NULL;
459     xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
460
461     if (attributes == 2) {
462         //match attribute ommited, text value will be used
463         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
464     } else if (attributes == 3) {
465         value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
466         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
467     } else {
468         Assert(false && "Wrong XML file format");
469     }
470
471     // creating temporiary object is not good idea
472     // but we have no choice untill Attribute have constructor taking std::string*
473     Assert(attrName != NULL && "XML file format is wrong");
474
475     Attribute *attr;
476     if (attrName != NULL) {
477         std::string temp(reinterpret_cast<const char *>(attrName));
478         attr = new Attribute(&temp, convertToMatchFunction(
479                     func), Attribute::Type::Subject);
480     }
481
482     if (value != NULL) { //add value of the attribute if possible
483         //[CR] consider create Attribute::addValue(char *) function
484         std::string temp(reinterpret_cast<const char *>(value));
485         attr->addValue(&temp);
486     }
487     currentAttribute = attr;
488
489     if (xmlTextReaderIsEmptyElement(reader)) {
490         Assert(value != NULL && "XML file format is wrong");
491         //Attribute value is required to obtain the match value easier
492         consumeSubjectMatch(value);
493     }
494
495     if (attributes == 2 || attributes == 3) {
496         xmlFree(func);
497     }
498     xmlFree(value);
499     xmlFree(attrName);
500 }
501
502 void Parser::handleMatch(xmlTextReaderPtr reader,
503         Attribute::Type type)
504 {
505     int attributes = xmlTextReaderAttributeCount(reader);
506
507     xmlChar * func = NULL;
508     xmlChar * value = NULL;
509     xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
510
511     if (attributes == 2) {
512         //match attribute ommited, text value will be used
513         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
514         //the content may be resource-attr or PCDATA
515     } else if (attributes == 3) {
516         value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
517         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
518     } else {
519         Assert(false && "Wrong XML file format");
520     }
521
522     // FunctionParam type is sybtype of Resource.
523     // FunctionParam is used to storage attriburess of call functions.
524     if (0 ==
525         xmlStrncmp(attrName, BAD_CAST(TOKEN_PARAM),
526                    xmlStrlen(BAD_CAST(TOKEN_PARAM))) && type ==
527         Attribute::Type::Resource) {
528         type = Attribute::Type::FunctionParam;
529     }
530
531     Assert(attrName != NULL && "XML is currupted");
532     Attribute *attr;
533     if (attrName != NULL) {
534         std::string temp(reinterpret_cast<const char*>(attrName));
535         attr = new Attribute(&temp, convertToMatchFunction(func), type);
536         currentAttribute = attr;
537     }
538
539     if (xmlTextReaderIsEmptyElement(reader)) {
540         Assert(value != NULL && "XML is currupted");
541         std::string tempVal(reinterpret_cast<const char*>(value));
542         currentAttribute->addValue(&tempVal);
543         consumeCurrentAttribute();
544     }
545
546     if (attributes == 2 || attributes == 3) {
547         xmlFree(func);
548     }
549     xmlFree(value);
550     xmlFree(attrName);
551 }
552
553 Policy::CombineAlgorithm Parser::convertToCombineAlgorithm(xmlChar* algorithm)
554 {
555     switch (*algorithm) {
556     case 'f':
557         if (*(algorithm + 6) == 'a') { //first applicable
558             return Policy::FirstApplicable;
559         }
560         return Policy::FirstTargetMatching;
561     case 'd':
562         return Policy::DenyOverride;
563     case 'p':
564         return Policy::PermitOverride;
565     default:
566         Assert(false && "Wrong combine algorithm name");
567         return Policy::DenyOverride;
568     }
569 }
570
571 ExtendedEffect Parser::convertToEffect(xmlChar *effect)
572 {
573     switch (*effect) {
574     case 'd':     //deny
575         return Deny;
576         break;
577     case 'p':
578         //permit, prompt-blanket, prompt-session, prompt-oneshot
579         if (*(effect + 1) == 'e') {
580             return ExtendedEffect(Permit, ruleId++);
581         }
582         switch (*(effect + 7)) {
583         case 'b':
584             return ExtendedEffect(PromptBlanket, ruleId++);
585         case 's':
586             return ExtendedEffect(PromptSession, ruleId++);
587         case 'o':
588             return ExtendedEffect(PromptOneShot, ruleId++);
589         default:
590             Assert(false && "Effect is Error");
591             return ExtendedEffect();
592         }
593         break;
594     default:
595         Assert(false && "Effect is Error");
596         return ExtendedEffect();
597     }
598     //return ExtendedEffect(Inapplicable);
599 }
600
601 Attribute::Match Parser::convertToMatchFunction(xmlChar * func)
602 {
603     if (func == NULL) {
604         LogError("[ERROR] match function value is NULL");
605         return Attribute::Match::Error;
606     }
607
608     if (*func == 'g') {
609         return Attribute::Match::Glob;
610     } else if (*func == 'e') {
611         return Attribute::Match::Equal;
612     } else if (*func == 'r') {
613         return Attribute::Match::Regexp;
614     } else {
615         LogError("[ERROR] match function value is NULL");
616         return Attribute::Match::Error;
617     }
618     //Assert(false);
619 }
620
621 void Parser::handleAttr(xmlTextReaderPtr reader)
622 {
623     xmlChar * attrValue = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
624     Assert(attrValue != NULL && "Error while obtaining attribute");
625
626     std::string temp(reinterpret_cast<const char*>(attrValue));
627     currentAttribute->addValue(&temp);
628
629     xmlFree(attrValue);
630 }
631
632 void Parser::consumeCurrentText()
633 {
634     Assert(currentText != NULL);
635     currentAttribute->addValue(currentText);
636     delete currentText;
637
638     currentText = NULL;
639 }
640
641 void Parser::consumeCurrentAttribute()
642 {
643     Assert(currentAttribute != NULL);
644
645     currentCondition->addAttribute(*currentAttribute);
646     delete currentAttribute;
647
648     currentAttribute = NULL;
649 }
650
651 void Parser::consumeCurrentSubject()
652 {
653     Policy * policy = dynamic_cast<Policy *>(currentRoot->getElement());
654     Assert(policy != NULL);
655     policy->addSubject(currentSubject);
656     //TODO maybe keep subjects not subject pointers in Policies and consume subjects here
657     currentSubject = NULL;
658 }
659
660 void Parser::consumeCurrentCondition()
661 {
662     Condition * temp = NULL;
663     if (currentCondition != NULL) {
664         if (currentCondition->getParent() != NULL) { //Condition is a child of another condition
665             currentCondition->getParent()->addCondition(*currentCondition);
666         } else { //Condition parent is a Rule
667             Rule * rule = dynamic_cast<Rule *>(currentRoot->getElement());
668             Assert(rule != NULL);
669             rule->setCondition(*currentCondition);
670         }
671         temp = currentCondition->getParent();
672         delete currentCondition;
673     }
674     currentCondition = temp;  //switch current condition ( it may be switched to NULL if condition's parent was rule
675 }
676
677 void Parser::consumeSubjectMatch(xmlChar * value)
678 {
679     Assert(
680         currentAttribute != NULL &&
681         "consuming subject match without attribute set");
682
683     if (currentSubject != NULL) {
684         currentSubject->addNewAttribute(*currentAttribute);
685         //[CR] matching/modyfing functions transform uri.host to uri ( etc. ) so strncmp is not needed, string equality will do
686         if (!strncmp(currentAttribute->getName()->c_str(), "uri",
687                      3) ||
688             !strncmp(currentAttribute->getName()->c_str(), "id", 2)) {
689             if (value != NULL) {
690                 currentSubject->setSubjectId(reinterpret_cast<const char *>(
691                                                  value));
692             } else if (currentAttribute->getValue()->size()) {
693                 currentSubject->setSubjectId(
694                     currentAttribute->getValue()->front());
695             } else {
696                 Assert(false);
697             }
698         }
699     } else if (currentCondition != NULL) {
700         currentCondition->addAttribute(*currentAttribute);
701     }
702
703     delete currentAttribute;
704     currentAttribute = NULL;
705 }
706
707 void Parser::trim(std::string * str)
708 {
709     std::string::size_type pos = str->find_last_not_of(whitespaces);
710     if (pos != std::string::npos) {
711         str->erase(pos + 1);
712         pos = str->find_first_not_of(whitespaces);
713         if (pos != std::string::npos) {
714             str->erase(0, pos);
715         }
716     } else {
717         str->erase(str->begin(), str->end());
718         LogInfo("Warning, empty string as attribute value");
719     }
720 }
721
722 // KW void Parser::canonicalize(const char * input, const char * output, CanonicalizationAlgorithm canonicalizationAlgorithm){
723 // KW
724 // KW     xmlDocPtr       doc =  xmlParseFile(input);
725 // KW     //xmlDocDump(stdout, doc);
726 // KW
727 // KW     if(doc == NULL)
728 // KW     {
729 // KW         LogError("Canonicalization error, cannot parser xml file");
730 // KW     }
731 // KW
732 // KW
733 // KW     int mode = -1;
734 // KW     if(canonicalizationAlgorithm == C14N)
735 // KW     {
736 // KW         mode = 0;
737 // KW     }
738 // KW     else if(canonicalizationAlgorithm == C14NEXCLUSIVE)
739 // KW     {
740 // KW         mode = 1;
741 // KW     }
742 // KW
743 // KW
744 // KW     xmlC14NDocSave(doc, NULL, mode, NULL, 0, output, 0);
745 // KW
746 // KW     xmlFreeDoc(doc);
747 // KW
748 // KW }
749
750 // KW int Parser::extractNodeToFile(xmlTextReaderPtr reader, const char * filename){
751 // KW
752 // KW        xmlNodePtr node = xmlTextReaderExpand(reader);
753 // KW        xmlBufferPtr buff = xmlBufferCreate();
754 // KW        xmlNodeDump(buff, node->doc, node, 0, 0);
755 // KW        FILE * file = fopen(filename, "w");
756 // KW        if(file == NULL){
757 // KW            LogError("Error while trying to open file "<<filename);
758 // KW            return -1;
759 // KW        }
760 // KW        int ret = xmlBufferDump(file, buff);
761 // KW        fclose(file);
762 // KW        xmlBufferFree(buff);
763 // KW        return ret;
764 // KW
765 // KW }
766