2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <dpl/log/log.h>
22 #include <ace/parser.h>
27 class ParserWarningLogger
30 void operator()(const std::string& logMsg)
36 class ParserErrorLogger
39 void operator()(const std::string& logMsg)
45 template <class Logger>
46 void xmlLogFunction(void* /*ctx*/, const char *msg, ...)
48 const int BUFFER_SIZE = 1024;
49 char buffer[BUFFER_SIZE];
50 buffer[BUFFER_SIZE - 1] = '\0';
55 vsnprintf(buffer, BUFFER_SIZE - 1, msg, va);
58 std::string logmsg(buffer);
64 const char *Parser::TOKEN_PARAM = "param:";
72 currentCondition(NULL),
73 currentAttribute(NULL),
75 processingSignature(false),
76 canonicalizeOnce(false)
78 processingSignature = true;
79 canonicalizeOnce = true;
84 /* parse function destroys reader */
85 // free(this->xmlFilename);
88 TreeNode* Parser::parse(const std::string& filename, const std::string& schema)
91 root->releaseResources();
95 LogDebug("Parser: opening file " << filename);
97 xmlDocPtr xmlDocument = xmlParseFile(filename.c_str());
99 LogError("Couldn't parse file " << filename);
103 std::unique_ptr <xmlDoc, std::function<void(xmlDoc*)> >
104 doc(xmlDocument, xmlFreeDoc);
106 xmlSchemaParserCtxtPtr xmlSchemaParserContext =
107 xmlSchemaNewParserCtxt(schema.c_str());
109 if (!xmlSchemaParserContext) {
110 LogError("Couldn't load xml schema: " << schema);
116 std::function<void(xmlSchemaParserCtxt*)> >
118 xmlSchemaParserContext,
119 xmlSchemaFreeParserCtxt);
121 LogDebug("Setting callbacks");
123 xmlSchemaSetParserErrors(
125 static_cast<xmlValidityErrorFunc>
126 (&xmlLogFunction<ParserErrorLogger>),
127 static_cast<xmlValidityWarningFunc>
128 (&xmlLogFunction<ParserWarningLogger>),
131 xmlSchemaPtr xmlSchema = xmlSchemaParse(schemaContext.get());
134 LogError("Couldn't parse xml schema: " << xmlSchema);
138 xmlSchemaValidCtxtPtr xmlValidContext = xmlSchemaNewValidCtxt(xmlSchema);
140 if (!xmlValidContext) {
141 LogError("Couldn't create validation context!");
142 xmlSchemaFree(xmlSchema);
148 std::function<void(xmlSchemaValidCtxt*)> >
151 xmlSchemaFreeValidCtxt);
153 xmlSchemaSetValidErrors(
154 schemaValidContext.get(),
155 static_cast<xmlValidityErrorFunc>
156 (&xmlLogFunction<ParserErrorLogger>),
157 static_cast<xmlValidityWarningFunc>
158 (&xmlLogFunction<ParserWarningLogger>),
161 xmlSchemaSetValidOptions(
162 schemaValidContext.get(),
163 XML_SCHEMA_VAL_VC_I_CREATE);
166 (xmlSchemaValidateDoc(
167 schemaValidContext.get(),
168 xmlDocument) == 0 ? true : false);
170 xmlSchemaFree(xmlSchema);
173 LogError("Couldn't validate policy file: " << filename <<
174 " against xml schema: " << schema);
179 LogInfo("Policy file: " << filename << " validated!");
181 xmlTextReaderPtr xmlReader = xmlReaderWalker(xmlDocument);
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 << ")");
189 std::unique_ptr <xmlTextReader, std::function<void(xmlTextReader*)> >
190 reader(xmlReader, xmlFreeTextReader);
193 ret = xmlTextReaderRead(reader.get());
195 std::unique_ptr<xmlChar, std::function<void(xmlChar*)> >
196 name(xmlTextReaderName(reader.get()), xmlFree);
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;
210 //Do not process signature of xml file
211 if(!processingSignature) {
212 processNode(reader.get());
214 ret = xmlTextReaderRead(reader.get());
218 LogError("Error while parsing XML file");
220 root->releaseResources();
228 void Parser::processNode(xmlTextReaderPtr reader)
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));
237 case XML_READER_TYPE_ELEMENT:
238 startNodeHandler(reader);
241 case XML_READER_TYPE_END_ELEMENT:
242 endNodeHandler(reader);
245 case XML_READER_TYPE_TEXT:
246 textNodeHandler(reader);
249 //Do not handle other xml tags
254 void Parser::startNodeHandler(xmlTextReaderPtr reader)
256 xmlChar *name = xmlTextReaderName(reader);
258 Assert(name != NULL && "xml is corrupted");
263 case 'p': //policy and policy-set
264 if (*(name + 6) == 0) {
265 handlePolicy(reader, TreeNode::Policy);
267 handlePolicy(reader, TreeNode::PolicySet);
270 case 'r': //rule and resource-match
271 if (*(name + 1) == 'u') {
273 } else if (*(name + 9) == 'm') {
274 handleMatch(reader, Attribute::Type::Resource);
279 case 's': //subject and subject-match
280 if (*(name + 7) == 0) {
282 } else if (*(name + 8) == 'm') { //subject match
283 handleSubjectMatch(reader);
284 } else { //subject attr
288 case 'c': //condition
289 handleCondition(reader);
291 case 'e': //environment-match
292 if (*(name + 12) == 'm') {
293 handleMatch(reader, Attribute::Type::Environment);
302 void Parser::endNodeHandler(xmlTextReaderPtr reader)
304 xmlChar *name = xmlTextReaderName(reader);
306 Assert(name != NULL && "xml file corrupted");
307 if (name == NULL) return;
310 case 'p': //policy and policy-set
312 currentRoot = currentRoot->getParent();
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
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();
329 //Subject-match end doesn't require handling
331 case 'c': //condition
332 consumeCurrentCondition();
334 case 'e': //environment-match
335 consumeCurrentText(); //consume text if any available
336 consumeCurrentAttribute(); //consume attribute
342 void Parser::textNodeHandler(xmlTextReaderPtr reader)
345 xmlChar * text = xmlTextReaderValue(reader);
346 Assert(text != NULL && "Parser couldn't parse PCDATA");
348 currentText = new std::string(reinterpret_cast<const char * >(text));
353 void Parser::handlePolicy(xmlTextReaderPtr reader,
354 TreeNode::TypeID type)
356 Policy::CombineAlgorithm algorithm;
358 //Get first attribute
359 xmlChar * combAlg = xmlTextReaderGetAttribute(reader, BAD_CAST("combine"));
361 Assert(combAlg != NULL && "Parser error while getting attributes");
362 algorithm = convertToCombineAlgorithm(combAlg);
364 //Create TreeNode element
365 Policy * policy = NULL;
366 if (type == TreeNode::Policy) {
367 policy = new Policy();
369 policy = new PolicySet();
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);
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
387 if (NULL == currentRoot) {
388 node->releaseResources();
394 void Parser::handleRule(xmlTextReaderPtr reader)
396 ExtendedEffect effect(Inapplicable);
398 //[CR] create macros for attribute names
399 xmlChar * eff = xmlTextReaderGetAttribute(reader, BAD_CAST("effect")); //get the rule attribute
401 Assert(eff != NULL && "Parser error while getting attributes");
402 effect = convertToEffect(eff);
406 rule->setEffect(effect);
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);
414 if (!xmlTextReaderIsEmptyElement(reader)) {
418 if (NULL == currentRoot) {
419 node->releaseResources();
425 void Parser::handleSubject()
427 currentSubject = new Subject();
428 //TODO what about empty subject tag
431 void Parser::handleCondition(xmlTextReaderPtr reader)
433 Condition::CombineType combineType = Condition::AND;
435 xmlChar * combine = xmlTextReaderGetAttribute(reader, BAD_CAST("combine")); //get the rule attribute
437 Assert(combine != NULL && "Parser error while getting attributes");
439 combineType = *combine == 'a' ? Condition::AND : Condition::OR;
441 Condition * condition = new Condition();
442 condition->setCombineType(combineType);
443 condition->setParent(currentCondition);
445 currentCondition = condition;
447 //TODO what about empty condition tag?
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)
455 int attributes = xmlTextReaderAttributeCount(reader);
457 xmlChar * func = NULL;
458 xmlChar * value = NULL;
459 xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
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"));
468 Assert(false && "Wrong XML file format");
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");
476 if (attrName != NULL) {
477 std::string temp(reinterpret_cast<const char *>(attrName));
478 attr = new Attribute(&temp, convertToMatchFunction(
479 func), Attribute::Type::Subject);
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);
487 currentAttribute = attr;
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);
495 if (attributes == 2 || attributes == 3) {
502 void Parser::handleMatch(xmlTextReaderPtr reader,
503 Attribute::Type type)
505 int attributes = xmlTextReaderAttributeCount(reader);
507 xmlChar * func = NULL;
508 xmlChar * value = NULL;
509 xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
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"));
519 Assert(false && "Wrong XML file format");
522 // FunctionParam type is sybtype of Resource.
523 // FunctionParam is used to storage attriburess of call functions.
525 xmlStrncmp(attrName, BAD_CAST(TOKEN_PARAM),
526 xmlStrlen(BAD_CAST(TOKEN_PARAM))) && type ==
527 Attribute::Type::Resource) {
528 type = Attribute::Type::FunctionParam;
531 Assert(attrName != NULL && "XML is currupted");
533 if (attrName != NULL) {
534 std::string temp(reinterpret_cast<const char*>(attrName));
535 attr = new Attribute(&temp, convertToMatchFunction(func), type);
536 currentAttribute = attr;
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();
546 if (attributes == 2 || attributes == 3) {
553 Policy::CombineAlgorithm Parser::convertToCombineAlgorithm(xmlChar* algorithm)
555 switch (*algorithm) {
557 if (*(algorithm + 6) == 'a') { //first applicable
558 return Policy::FirstApplicable;
560 return Policy::FirstTargetMatching;
562 return Policy::DenyOverride;
564 return Policy::PermitOverride;
566 Assert(false && "Wrong combine algorithm name");
567 return Policy::DenyOverride;
571 ExtendedEffect Parser::convertToEffect(xmlChar *effect)
578 //permit, prompt-blanket, prompt-session, prompt-oneshot
579 if (*(effect + 1) == 'e') {
580 return ExtendedEffect(Permit, ruleId++);
582 switch (*(effect + 7)) {
584 return ExtendedEffect(PromptBlanket, ruleId++);
586 return ExtendedEffect(PromptSession, ruleId++);
588 return ExtendedEffect(PromptOneShot, ruleId++);
590 Assert(false && "Effect is Error");
591 return ExtendedEffect();
595 Assert(false && "Effect is Error");
596 return ExtendedEffect();
598 //return ExtendedEffect(Inapplicable);
601 Attribute::Match Parser::convertToMatchFunction(xmlChar * func)
604 LogError("[ERROR] match function value is NULL");
605 return Attribute::Match::Error;
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;
615 LogError("[ERROR] match function value is NULL");
616 return Attribute::Match::Error;
621 void Parser::handleAttr(xmlTextReaderPtr reader)
623 xmlChar * attrValue = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
624 Assert(attrValue != NULL && "Error while obtaining attribute");
626 std::string temp(reinterpret_cast<const char*>(attrValue));
627 currentAttribute->addValue(&temp);
632 void Parser::consumeCurrentText()
634 Assert(currentText != NULL);
635 currentAttribute->addValue(currentText);
641 void Parser::consumeCurrentAttribute()
643 Assert(currentAttribute != NULL);
645 currentCondition->addAttribute(*currentAttribute);
646 delete currentAttribute;
648 currentAttribute = NULL;
651 void Parser::consumeCurrentSubject()
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;
660 void Parser::consumeCurrentCondition()
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);
671 temp = currentCondition->getParent();
672 delete currentCondition;
674 currentCondition = temp; //switch current condition ( it may be switched to NULL if condition's parent was rule
677 void Parser::consumeSubjectMatch(xmlChar * value)
680 currentAttribute != NULL &&
681 "consuming subject match without attribute set");
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",
688 !strncmp(currentAttribute->getName()->c_str(), "id", 2)) {
690 currentSubject->setSubjectId(reinterpret_cast<const char *>(
692 } else if (currentAttribute->getValue()->size()) {
693 currentSubject->setSubjectId(
694 currentAttribute->getValue()->front());
699 } else if (currentCondition != NULL) {
700 currentCondition->addAttribute(*currentAttribute);
703 delete currentAttribute;
704 currentAttribute = NULL;
707 void Parser::trim(std::string * str)
709 std::string::size_type pos = str->find_last_not_of(whitespaces);
710 if (pos != std::string::npos) {
712 pos = str->find_first_not_of(whitespaces);
713 if (pos != std::string::npos) {
717 str->erase(str->begin(), str->end());
718 LogInfo("Warning, empty string as attribute value");
722 // KW void Parser::canonicalize(const char * input, const char * output, CanonicalizationAlgorithm canonicalizationAlgorithm){
724 // KW xmlDocPtr doc = xmlParseFile(input);
725 // KW //xmlDocDump(stdout, doc);
727 // KW if(doc == NULL)
729 // KW LogError("Canonicalization error, cannot parser xml file");
734 // KW if(canonicalizationAlgorithm == C14N)
738 // KW else if(canonicalizationAlgorithm == C14NEXCLUSIVE)
744 // KW xmlC14NDocSave(doc, NULL, mode, NULL, 0, output, 0);
746 // KW xmlFreeDoc(doc);
750 // KW int Parser::extractNodeToFile(xmlTextReaderPtr reader, const char * filename){
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);
760 // KW int ret = xmlBufferDump(file, buff);
762 // KW xmlBufferFree(buff);