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);
259 case 'p': //policy and policy-set
260 if (*(name + 6) == 0) {
261 handlePolicy(reader, TreeNode::Policy);
263 handlePolicy(reader, TreeNode::PolicySet);
266 case 'r': //rule and resource-match
267 if (*(name + 1) == 'u') {
269 } else if (*(name + 9) == 'm') {
270 handleMatch(reader, Attribute::Type::Resource);
275 case 's': //subject and subject-match
276 if (*(name + 7) == 0) {
278 } else if (*(name + 8) == 'm') { //subject match
279 handleSubjectMatch(reader);
280 } else { //subject attr
284 case 'c': //condition
285 handleCondition(reader);
287 case 'e': //environment-match
288 if (*(name + 12) == 'm') {
289 handleMatch(reader, Attribute::Type::Environment);
298 void Parser::endNodeHandler(xmlTextReaderPtr reader)
300 xmlChar *name = xmlTextReaderName(reader);
303 case 'p': //policy and policy-set
305 currentRoot = currentRoot->getParent();
307 case 'r': //Rule and resource match
308 if (*(name + 1) == 'u') { //Rule
309 currentRoot = currentRoot->getParent();
310 } else { //Resource-match
311 consumeCurrentText(); //consume text if any available
312 consumeCurrentAttribute(); //consume attribute
315 case 's': //subject and subject-match
316 if (*(name + 7) == 0) { //handle subject
317 consumeCurrentSubject();
318 } else if (*(name + 8) == 'm') { //handle subject match
319 consumeCurrentText();
320 consumeSubjectMatch();
322 //Subject-match end doesn't require handling
324 case 'c': //condition
325 consumeCurrentCondition();
327 case 'e': //environment-match
328 consumeCurrentText(); //consume text if any available
329 consumeCurrentAttribute(); //consume attribute
335 void Parser::textNodeHandler(xmlTextReaderPtr reader)
338 xmlChar * text = xmlTextReaderValue(reader);
339 Assert(text != NULL && "Parser couldn't parse PCDATA");
341 currentText = new std::string(reinterpret_cast<const char * >(text));
346 void Parser::handlePolicy(xmlTextReaderPtr reader,
347 TreeNode::TypeID type)
349 Policy::CombineAlgorithm algorithm;
351 //Get first attribute
352 xmlChar * combAlg = xmlTextReaderGetAttribute(reader, BAD_CAST("combine"));
354 Assert(combAlg != NULL && "Parser error while getting attributes");
355 algorithm = convertToCombineAlgorithm(combAlg);
357 //Create TreeNode element
358 Policy * policy = NULL;
359 if (type == TreeNode::Policy) {
360 policy = new Policy();
362 policy = new PolicySet();
364 policy->setCombineAlgorithm(algorithm);
365 TreeNode * node = new TreeNode(currentRoot, type, policy);
366 //Add new tree node to current's root children set
367 if (currentRoot != NULL) {
368 currentRoot->addChild(node);
371 //Switch the current root to the new node
372 if (!xmlTextReaderIsEmptyElement(reader)) {
373 //Current root switching is necessary only if tag is not empty
380 if (NULL == currentRoot) {
381 node->releaseResources();
387 void Parser::handleRule(xmlTextReaderPtr reader)
389 ExtendedEffect effect(Inapplicable);
391 //[CR] create macros for attribute names
392 xmlChar * eff = xmlTextReaderGetAttribute(reader, BAD_CAST("effect")); //get the rule attribute
394 Assert(eff != NULL && "Parser error while getting attributes");
395 effect = convertToEffect(eff);
399 rule->setEffect(effect);
401 TreeNode * node = new TreeNode(currentRoot, TreeNode::Rule, rule);
402 //Add new tree node to current's root children set
403 if (currentRoot != NULL) { //
404 currentRoot->addChild(node);
407 if (!xmlTextReaderIsEmptyElement(reader)) {
411 if (NULL == currentRoot) {
412 node->releaseResources();
418 void Parser::handleSubject()
420 currentSubject = new Subject();
421 //TODO what about empty subject tag
424 void Parser::handleCondition(xmlTextReaderPtr reader)
426 Condition::CombineType combineType = Condition::AND;
428 xmlChar * combine = xmlTextReaderGetAttribute(reader, BAD_CAST("combine")); //get the rule attribute
430 Assert(combine != NULL && "Parser error while getting attributes");
432 combineType = *combine == 'a' ? Condition::AND : Condition::OR;
434 Condition * condition = new Condition();
435 condition->setCombineType(combineType);
436 condition->setParent(currentCondition);
438 currentCondition = condition;
440 //TODO what about empty condition tag?
443 //Subject match is handled differently than resource or environment match
444 //Because it cannot have any children tags and can only include PCDATA
445 void Parser::handleSubjectMatch(xmlTextReaderPtr reader)
448 int attributes = xmlTextReaderAttributeCount(reader);
450 xmlChar * func = NULL;
451 xmlChar * value = NULL;
452 xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
454 if (attributes == 2) {
455 //match attribute ommited, text value will be used
456 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
457 } else if (attributes == 3) {
458 value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
459 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
461 Assert(false && "Wrong XML file format");
464 // creating temporiary object is not good idea
465 // but we have no choice untill Attribute have constructor taking std::string*
466 std::string temp(reinterpret_cast<const char *>(attrName));
467 Attribute * attr = new Attribute(&temp, convertToMatchFunction(
468 func), Attribute::Type::Subject);
469 if (value != NULL) { //add value of the attribute if possible
470 //[CR] consider create Attribute::addValue(char *) function
471 std::string temp(reinterpret_cast<const char *>(value));
472 attr->addValue(&temp);
474 currentAttribute = attr;
476 if (xmlTextReaderIsEmptyElement(reader)) {
477 Assert(value != NULL && "XML file format is wrong");
478 //Attribute value is required to obtain the match value easier
479 consumeSubjectMatch(value);
482 if (attributes == 2 || attributes == 3) {
489 void Parser::handleMatch(xmlTextReaderPtr reader,
490 Attribute::Type type)
492 int attributes = xmlTextReaderAttributeCount(reader);
494 xmlChar * func = NULL;
495 xmlChar * value = NULL;
496 xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
498 if (attributes == 2) {
499 //match attribute ommited, text value will be used
500 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
501 //the content may be resource-attr or PCDATA
502 } else if (attributes == 3) {
503 value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
504 func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
506 Assert(false && "Wrong XML file format");
509 // FunctionParam type is sybtype of Resource.
510 // FunctionParam is used to storage attriburess of call functions.
512 xmlStrncmp(attrName, BAD_CAST(TOKEN_PARAM),
513 xmlStrlen(BAD_CAST(TOKEN_PARAM))) && type ==
514 Attribute::Type::Resource) {
515 type = Attribute::Type::FunctionParam;
518 std::string temp(reinterpret_cast<const char*>(attrName));
519 Attribute * attr = new Attribute(&temp, convertToMatchFunction(func), type);
520 currentAttribute = attr;
522 if (xmlTextReaderIsEmptyElement(reader)) {
523 Assert(value != NULL && "XML is currupted");
524 std::string tempVal(reinterpret_cast<const char*>(value));
525 currentAttribute->addValue(&tempVal);
526 consumeCurrentAttribute();
529 if (attributes == 2 || attributes == 3) {
536 Policy::CombineAlgorithm Parser::convertToCombineAlgorithm(xmlChar* algorithm)
538 switch (*algorithm) {
540 if (*(algorithm + 6) == 'a') { //first applicable
541 return Policy::FirstApplicable;
543 return Policy::FirstTargetMatching;
545 return Policy::DenyOverride;
547 return Policy::PermitOverride;
549 Assert(false && "Wrong combine algorithm name");
550 return Policy::DenyOverride;
554 ExtendedEffect Parser::convertToEffect(xmlChar *effect)
561 //permit, prompt-blanket, prompt-session, prompt-oneshot
562 if (*(effect + 1) == 'e') {
563 return ExtendedEffect(Permit, ruleId++);
565 switch (*(effect + 7)) {
567 return ExtendedEffect(PromptBlanket, ruleId++);
569 return ExtendedEffect(PromptSession, ruleId++);
571 return ExtendedEffect(PromptOneShot, ruleId++);
573 Assert(false && "Effect is Error");
574 return ExtendedEffect();
578 Assert(false && "Effect is Error");
579 return ExtendedEffect();
581 //return ExtendedEffect(Inapplicable);
584 Attribute::Match Parser::convertToMatchFunction(xmlChar * func)
587 LogError("[ERROR] match function value is NULL");
588 return Attribute::Match::Error;
592 return Attribute::Match::Glob;
593 } else if (*func == 'e') {
594 return Attribute::Match::Equal;
595 } else if (*func == 'r') {
596 return Attribute::Match::Regexp;
598 LogError("[ERROR] match function value is NULL");
599 return Attribute::Match::Error;
604 void Parser::handleAttr(xmlTextReaderPtr reader)
606 xmlChar * attrValue = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
607 Assert(attrValue != NULL && "Error while obtaining attribute");
609 std::string temp(reinterpret_cast<const char*>(attrValue));
610 currentAttribute->addValue(&temp);
615 void Parser::consumeCurrentText()
617 Assert(currentText != NULL);
618 currentAttribute->addValue(currentText);
624 void Parser::consumeCurrentAttribute()
626 Assert(currentAttribute != NULL);
628 currentCondition->addAttribute(*currentAttribute);
629 delete currentAttribute;
631 currentAttribute = NULL;
634 void Parser::consumeCurrentSubject()
636 Policy * policy = dynamic_cast<Policy *>(currentRoot->getElement());
637 Assert(policy != NULL);
638 policy->addSubject(currentSubject);
639 //TODO maybe keep subjects not subject pointers in Policies and consume subjects here
640 currentSubject = NULL;
643 void Parser::consumeCurrentCondition()
645 Condition * temp = NULL;
646 if (currentCondition != NULL) {
647 if (currentCondition->getParent() != NULL) { //Condition is a child of another condition
648 currentCondition->getParent()->addCondition(*currentCondition);
649 } else { //Condition parent is a Rule
650 Rule * rule = dynamic_cast<Rule *>(currentRoot->getElement());
651 Assert(rule != NULL);
652 rule->setCondition(*currentCondition);
654 temp = currentCondition->getParent();
655 delete currentCondition;
657 currentCondition = temp; //switch current condition ( it may be switched to NULL if condition's parent was rule
660 void Parser::consumeSubjectMatch(xmlChar * value)
663 currentAttribute != NULL &&
664 "consuming subject match without attribute set");
666 if (currentSubject != NULL) {
667 currentSubject->addNewAttribute(*currentAttribute);
668 //[CR] matching/modyfing functions transform uri.host to uri ( etc. ) so strncmp is not needed, string equality will do
669 if (!strncmp(currentAttribute->getName()->c_str(), "uri",
671 !strncmp(currentAttribute->getName()->c_str(), "id", 2)) {
673 currentSubject->setSubjectId(reinterpret_cast<const char *>(
675 } else if (currentAttribute->getValue()->size()) {
676 currentSubject->setSubjectId(
677 currentAttribute->getValue()->front());
682 } else if (currentCondition != NULL) {
683 currentCondition->addAttribute(*currentAttribute);
686 delete currentAttribute;
687 currentAttribute = NULL;
690 void Parser::trim(std::string * str)
692 std::string::size_type pos = str->find_last_not_of(whitespaces);
693 if (pos != std::string::npos) {
695 pos = str->find_first_not_of(whitespaces);
696 if (pos != std::string::npos) {
700 str->erase(str->begin(), str->end());
701 LogInfo("Warning, empty string as attribute value");
705 // KW void Parser::canonicalize(const char * input, const char * output, CanonicalizationAlgorithm canonicalizationAlgorithm){
707 // KW xmlDocPtr doc = xmlParseFile(input);
708 // KW //xmlDocDump(stdout, doc);
710 // KW if(doc == NULL)
712 // KW LogError("Canonicalization error, cannot parser xml file");
717 // KW if(canonicalizationAlgorithm == C14N)
721 // KW else if(canonicalizationAlgorithm == C14NEXCLUSIVE)
727 // KW xmlC14NDocSave(doc, NULL, mode, NULL, 0, output, 0);
729 // KW xmlFreeDoc(doc);
733 // KW int Parser::extractNodeToFile(xmlTextReaderPtr reader, const char * filename){
735 // KW xmlNodePtr node = xmlTextReaderExpand(reader);
736 // KW xmlBufferPtr buff = xmlBufferCreate();
737 // KW xmlNodeDump(buff, node->doc, node, 0, 0);
738 // KW FILE * file = fopen(filename, "w");
739 // KW if(file == NULL){
740 // KW LogError("Error while trying to open file "<<filename);
743 // KW int ret = xmlBufferDump(file, buff);
745 // KW xmlBufferFree(buff);