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!");
147 std::function<void(xmlSchemaValidCtxt*)> >
150 xmlSchemaFreeValidCtxt);
152 xmlSchemaSetValidErrors(
153 schemaValidContext.get(),
154 static_cast<xmlValidityErrorFunc>
155 (&xmlLogFunction<ParserErrorLogger>),
156 static_cast<xmlValidityWarningFunc>
157 (&xmlLogFunction<ParserWarningLogger>),
160 xmlSchemaSetValidOptions(
161 schemaValidContext.get(),
162 XML_SCHEMA_VAL_VC_I_CREATE);
165 (xmlSchemaValidateDoc(
166 schemaValidContext.get(),
167 xmlDocument) == 0 ? true : false);
170 LogError("Couldn't validate policy file: " << filename <<
171 " against xml schema: " << schema);
176 LogInfo("Policy file: " << filename << " validated!");
178 xmlTextReaderPtr xmlReader = xmlReaderWalker(xmlDocument);
180 //[CR] consider using ASSERT/DASSERT
181 if (NULL == xmlReader) {
182 LogError("Error, xml reader cannot be created. Probably xml file is missing (opening file " << filename << ")");
186 std::unique_ptr <xmlTextReader, std::function<void(xmlTextReader*)> >
187 reader(xmlReader, xmlFreeTextReader);
190 ret = xmlTextReaderRead(reader.get());
192 std::unique_ptr<xmlChar, std::function<void(xmlChar*)> >
193 name(xmlTextReaderName(reader.get()), xmlFree);
195 if (!strcmp("policy-set", (const char *)name.get())) {
196 processingSignature = false;
197 } else if (!strcmp("SignedInfo",
198 (const char *)name.get()) && canonicalizeOnce) {
199 #if 0 //TODO I think we don't need canonicalization in ACE only in PM,
200 //we have to verify it tough
201 extractNodeToFile(reader, "output.xml");
202 //TODO we should be able to handle more than one canonicalization algorithm
203 canonicalize("output.xml", "canon.xml", Canonicalization::C14N);
204 canonicalizeOnce = false;
207 //Do not process signature of xml file
208 if(!processingSignature) {
209 processNode(reader.get());
211 ret = xmlTextReaderRead(reader.get());
215 LogError("Error while parsing XML file");
217 root->releaseResources();
225 void Parser::processNode(xmlTextReaderPtr reader)
227 //TODO this is interesting, xmlTextReaderNodeType returns int but I am pretty sure
228 //those integers coresponds to xmlReaderTypes
229 xmlReaderTypes type =
230 static_cast<xmlReaderTypes>(xmlTextReaderNodeType(reader));
234 case XML_READER_TYPE_ELEMENT:
235 startNodeHandler(reader);
238 case XML_READER_TYPE_END_ELEMENT:
239 endNodeHandler(reader);
242 case XML_READER_TYPE_TEXT:
243 textNodeHandler(reader);
246 //Do not handle other xml tags
251 void Parser::startNodeHandler(xmlTextReaderPtr reader)
253 xmlChar *name = xmlTextReaderName(reader);
256 case 'p': //policy and policy-set
257 if (*(name + 6) == 0) {
258 handlePolicy(reader, TreeNode::Policy);
260 handlePolicy(reader, TreeNode::PolicySet);
263 case 'r': //rule and resource-match
264 if (*(name + 1) == 'u') {
266 } else if (*(name + 9) == 'm') {
267 handleMatch(reader, Attribute::Type::Resource);
272 case 's': //subject and subject-match
273 if (*(name + 7) == 0) {
275 } else if (*(name + 8) == 'm') { //subject match
276 handleSubjectMatch(reader);
277 } else { //subject attr
281 case 'c': //condition
282 handleCondition(reader);
284 case 'e': //environment-match
285 if (*(name + 12) == 'm') {
286 handleMatch(reader, Attribute::Type::Environment);
295 void Parser::endNodeHandler(xmlTextReaderPtr reader)
297 xmlChar *name = xmlTextReaderName(reader);
300 case 'p': //policy and policy-set
302 currentRoot = currentRoot->getParent();
304 case 'r': //Rule and resource match
305 if (*(name + 1) == 'u') { //Rule
306 currentRoot = currentRoot->getParent();
307 } else { //Resource-match
308 consumeCurrentText(); //consume text if any available
309 consumeCurrentAttribute(); //consume attribute
312 case 's': //subject and subject-match
313 if (*(name + 7) == 0) { //handle subject
314 consumeCurrentSubject();
315 } else if (*(name + 8) == 'm') { //handle subject match
316 consumeCurrentText();
317 consumeSubjectMatch();
319 //Subject-match end doesn't require handling
321 case 'c': //condition
322 consumeCurrentCondition();
324 case 'e': //environment-match
325 consumeCurrentText(); //consume text if any available
326 consumeCurrentAttribute(); //consume attribute
332 void Parser::textNodeHandler(xmlTextReaderPtr reader)
335 xmlChar * text = xmlTextReaderValue(reader);
336 Assert(text != NULL && "Parser couldn't parse PCDATA");
338 currentText = new std::string(reinterpret_cast<const char * >(text));
343 void Parser::handlePolicy(xmlTextReaderPtr reader,
344 TreeNode::TypeID type)
346 Policy::CombineAlgorithm algorithm;
348 //Get first attribute
349 xmlChar * combAlg = xmlTextReaderGetAttribute(reader, BAD_CAST("combine"));
351 Assert(combAlg != NULL && "Parser error while getting attributes");
352 algorithm = convertToCombineAlgorithm(combAlg);
354 //Create TreeNode element
355 Policy * policy = NULL;
356 if (type == TreeNode::Policy) {
357 policy = new Policy();
359 policy = new PolicySet();
361 policy->setCombineAlgorithm(algorithm);
362 TreeNode * node = new TreeNode(currentRoot, type, policy);
363 //Add new tree node to current's root children set
364 if (currentRoot != NULL) {
365 currentRoot->addChild(node);
368 //Switch the current root to the new node
369 if (!xmlTextReaderIsEmptyElement(reader)) {
370 //Current root switching is necessary only if tag is not empty
377 if (NULL == currentRoot) {
378 node->releaseResources();
384 void Parser::handleRule(xmlTextReaderPtr reader)
386 ExtendedEffect effect(Inapplicable);
388 //[CR] create macros for attribute names
389 xmlChar * eff = xmlTextReaderGetAttribute(reader, BAD_CAST("effect")); //get the rule attribute
391 Assert(eff != NULL && "Parser error while getting attributes");
392 effect = convertToEffect(eff);
396 rule->setEffect(effect);
398 TreeNode * node = new TreeNode(currentRoot, TreeNode::Rule, rule);
399 //Add new tree node to current's root children set
400 if (currentRoot != NULL) { //
401 currentRoot->addChild(node);
404 if (!xmlTextReaderIsEmptyElement(reader)) {
408 if (NULL == currentRoot) {
409 node->releaseResources();
415 void Parser::handleSubject()
417 currentSubject = new Subject();
418 //TODO what about empty subject tag
421 void Parser::handleCondition(xmlTextReaderPtr reader)
423 Condition::CombineType combineType = Condition::AND;
425 xmlChar * combine = xmlTextReaderGetAttribute(reader, BAD_CAST("combine")); //get the rule attribute
427 Assert(combine != NULL && "Parser error while getting attributes");
429 combineType = *combine == 'a' ? Condition::AND : Condition::OR;
431 Condition * condition = new Condition();
432 condition->setCombineType(combineType);
433 condition->setParent(currentCondition);
435 currentCondition = condition;
436 //TODO what about empty condition tag?
439 //Subject match is handled differently than resource or environment match
440 //Because it cannot have any children tags and can only include PCDATA
441 void Parser::handleSubjectMatch(xmlTextReaderPtr reader)
444 int attributes = xmlTextReaderAttributeCount(reader);
446 xmlChar * func = NULL;
447 xmlChar * value = NULL;
448 xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
450 if (attributes == 2) {
451 //match attribute ommited, text value will be used
452 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
453 } else if (attributes == 3) {
454 value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
455 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
457 Assert(false && "Wrong XML file format");
460 // creating temporiary object is not good idea
461 // but we have no choice untill Attribute have constructor taking std::string*
462 std::string temp(reinterpret_cast<const char *>(attrName));
463 Attribute * attr = new Attribute(&temp, convertToMatchFunction(
464 func), Attribute::Type::Subject);
465 if (value != NULL) { //add value of the attribute if possible
466 //[CR] consider create Attribute::addValue(char *) function
467 std::string temp(reinterpret_cast<const char *>(value));
468 attr->addValue(&temp);
470 currentAttribute = attr;
472 if (xmlTextReaderIsEmptyElement(reader)) {
473 Assert(value != NULL && "XML file format is wrong");
474 //Attribute value is required to obtain the match value easier
475 consumeSubjectMatch(value);
478 if (attributes == 2 || attributes == 3) {
485 void Parser::handleMatch(xmlTextReaderPtr reader,
486 Attribute::Type type)
488 int attributes = xmlTextReaderAttributeCount(reader);
490 xmlChar * func = NULL;
491 xmlChar * value = NULL;
492 xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
494 if (attributes == 2) {
495 //match attribute ommited, text value will be used
496 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
497 //the content may be resource-attr or PCDATA
498 } else if (attributes == 3) {
499 value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
500 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
502 Assert(false && "Wrong XML file format");
505 // FunctionParam type is sybtype of Resource.
506 // FunctionParam is used to storage attriburess of call functions.
508 xmlStrncmp(attrName, BAD_CAST(TOKEN_PARAM),
509 xmlStrlen(BAD_CAST(TOKEN_PARAM))) && type ==
510 Attribute::Type::Resource) {
511 type = Attribute::Type::FunctionParam;
514 std::string temp(reinterpret_cast<const char*>(attrName));
515 Attribute * attr = new Attribute(&temp, convertToMatchFunction(func), type);
516 currentAttribute = attr;
518 if (xmlTextReaderIsEmptyElement(reader)) {
519 Assert(value != NULL && "XML is currupted");
520 std::string tempVal(reinterpret_cast<const char*>(value));
521 currentAttribute->addValue(&tempVal);
522 consumeCurrentAttribute();
525 if (attributes == 2 || attributes == 3) {
532 Policy::CombineAlgorithm Parser::convertToCombineAlgorithm(xmlChar* algorithm)
534 switch (*algorithm) {
536 if (*(algorithm + 6) == 'a') { //first applicable
537 return Policy::FirstApplicable;
539 return Policy::FirstTargetMatching;
541 return Policy::DenyOverride;
543 return Policy::PermitOverride;
545 Assert(false && "Wrong combine algorithm name");
546 return Policy::DenyOverride;
550 ExtendedEffect Parser::convertToEffect(xmlChar *effect)
557 //permit, prompt-blanket, prompt-session, prompt-oneshot
558 if (*(effect + 1) == 'e') {
559 return ExtendedEffect(Permit, ruleId++);
561 switch (*(effect + 7)) {
563 return ExtendedEffect(PromptBlanket, ruleId++);
565 return ExtendedEffect(PromptSession, ruleId++);
567 return ExtendedEffect(PromptOneShot, ruleId++);
569 Assert(false && "Effect is Error");
570 return ExtendedEffect();
574 Assert(false && "Effect is Error");
575 return ExtendedEffect();
577 //return ExtendedEffect(Inapplicable); //unreachable statement
580 Attribute::Match Parser::convertToMatchFunction(xmlChar * func)
583 LogError("[ERROR] match function value is NULL");
584 return Attribute::Match::Error;
588 return Attribute::Match::Glob;
589 } else if (*func == 'e') {
590 return Attribute::Match::Equal;
591 } else if (*func == 'r') {
592 return Attribute::Match::Regexp;
594 LogError("[ERROR] match function value is NULL");
595 return Attribute::Match::Error;
599 void Parser::handleAttr(xmlTextReaderPtr reader)
601 xmlChar * attrValue = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
602 Assert(attrValue != NULL && "Error while obtaining attribute");
604 std::string temp(reinterpret_cast<const char*>(attrValue));
605 currentAttribute->addValue(&temp);
610 void Parser::consumeCurrentText()
612 Assert(currentText != NULL);
613 currentAttribute->addValue(currentText);
619 void Parser::consumeCurrentAttribute()
621 Assert(currentAttribute != NULL);
623 currentCondition->addAttribute(*currentAttribute);
624 delete currentAttribute;
626 currentAttribute = NULL;
629 void Parser::consumeCurrentSubject()
631 Policy * policy = dynamic_cast<Policy *>(currentRoot->getElement());
632 Assert(policy != NULL);
633 policy->addSubject(currentSubject);
634 //TODO maybe keep subjects not subject pointers in Policies and consume subjects here
635 currentSubject = NULL;
638 void Parser::consumeCurrentCondition()
640 Condition * temp = NULL;
641 if (currentCondition != NULL) {
642 if (currentCondition->getParent() != NULL) { //Condition is a child of another condition
643 currentCondition->getParent()->addCondition(*currentCondition);
644 } else { //Condition parent is a Rule
645 Rule * rule = dynamic_cast<Rule *>(currentRoot->getElement());
646 Assert(rule != NULL);
647 rule->setCondition(*currentCondition);
649 temp = currentCondition->getParent();
650 delete currentCondition;
652 currentCondition = temp; //switch current condition ( it may be switched to NULL if condition's parent was rule
655 void Parser::consumeSubjectMatch(xmlChar * value)
658 currentAttribute != NULL &&
659 "consuming subject match without attribute set");
661 if (currentSubject != NULL) {
662 currentSubject->addNewAttribute(*currentAttribute);
663 //[CR] matching/modyfing functions transform uri.host to uri ( etc. ) so strncmp is not needed, string equality will do
664 if (!strncmp(currentAttribute->getName()->c_str(), "uri",
666 !strncmp(currentAttribute->getName()->c_str(), "id", 2)) {
668 currentSubject->setSubjectId(reinterpret_cast<const char *>(
670 } else if (currentAttribute->getValue()->size()) {
671 currentSubject->setSubjectId(
672 currentAttribute->getValue()->front());
677 } else if (currentCondition != NULL) {
678 currentCondition->addAttribute(*currentAttribute);
681 delete currentAttribute;
682 currentAttribute = NULL;
685 void Parser::trim(std::string * str)
687 std::string::size_type pos = str->find_last_not_of(whitespaces);
688 if (pos != std::string::npos) {
690 pos = str->find_first_not_of(whitespaces);
691 if (pos != std::string::npos) {
695 str->erase(str->begin(), str->end());
696 LogInfo("Warning, empty string as attribute value");
700 // KW void Parser::canonicalize(const char * input, const char * output, CanonicalizationAlgorithm canonicalizationAlgorithm){
702 // KW xmlDocPtr doc = xmlParseFile(input);
703 // KW //xmlDocDump(stdout, doc);
705 // KW if(doc == NULL)
707 // KW LogError("Canonicalization error, cannot parser xml file");
712 // KW if(canonicalizationAlgorithm == C14N)
716 // KW else if(canonicalizationAlgorithm == C14NEXCLUSIVE)
722 // KW xmlC14NDocSave(doc, NULL, mode, NULL, 0, output, 0);
724 // KW xmlFreeDoc(doc);
728 // KW int Parser::extractNodeToFile(xmlTextReaderPtr reader, const char * filename){
730 // KW xmlNodePtr node = xmlTextReaderExpand(reader);
731 // KW xmlBufferPtr buff = xmlBufferCreate();
732 // KW xmlNodeDump(buff, node->doc, node, 0, 0);
733 // KW FILE * file = fopen(filename, "w");
734 // KW if(file == NULL){
735 // KW LogError("Error while trying to open file "<<filename);
738 // KW int ret = xmlBufferDump(file, buff);
740 // KW xmlBufferFree(buff);