tizen beta release
[framework/web/wrt-commons.git] / modules / ace / engine / Attribute.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
17 #include <fnmatch.h>
18 #include <pcrecpp.h>
19 #include <sstream>
20 #include <dpl/foreach.h>
21 #include <dpl/log/log.h>
22 #include <dpl/ace/Attribute.h>
23 #include <dpl/ace/Serializer.h>
24
25 const bool Attribute::alpha[256] = {
26     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
31     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
32     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
33     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
34 };
35 const bool Attribute::digit[256] = {
36     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
38     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0
40 };
41
42 const bool Attribute::mark[256] = {
43     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45     0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0,
46     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
49     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0
51 };
52
53 bool Attribute::searchAndCut(const char *str)
54 {
55     //TODO
56     size_t pos = m_name.rfind(str);
57     if (pos == std::string::npos) {
58         return false;
59     }
60     if ((strlen(str) + pos) == m_name.size()) {
61         m_name.erase(pos, std::string::npos);
62         return true;
63     }
64     return false;
65 }
66
67 Attribute::Attribute(const std::string *name,
68                      const Match matchFunc,
69                      const Type type_) :
70     matchFunction(matchFunc)
71 {
72     m_name = *name;
73     m_typeId = type_;
74     m_undetermindState = false;
75     if (matchFunction != Match::Equal
76         && matchFunction != Match::Glob
77         && matchFunction != Match::Regexp)
78     {
79         //LogDebug("MID: " << matchFunction);
80         Assert(0 && "Match function problem");
81     }
82
83     if (searchAndCut(".scheme")) {
84         modifierFunction = Modifier::Scheme;
85     } else if (searchAndCut(".authority")) {
86         modifierFunction = Modifier::Authority;
87     } else if (searchAndCut(".scheme-authority")) {
88         modifierFunction = Modifier::SchemeAuthority;
89     } else if (searchAndCut(".host")) {
90         modifierFunction = Modifier::Host;
91     } else if (searchAndCut(".path")) {
92         modifierFunction = Modifier::Path;
93     } else {
94         modifierFunction = Modifier::Non;
95     }
96 }
97
98 static Attribute::MatchResult equal_comparator(const std::string *first,
99                                                const std::string *second)
100 {
101     if((*first) == (*second)) {
102         return Attribute::MatchResult::MRTrue;
103     }
104     return  Attribute::MatchResult::MRFalse;
105 }
106
107 static Attribute::MatchResult glob_comparator(const std::string *first,
108         const std::string *second)
109 {
110     // order is important
111     if (!fnmatch(first->c_str(), second->c_str(), 0)) {
112         return Attribute::MatchResult::MRTrue;
113     }
114     return  Attribute::MatchResult::MRFalse;
115 }
116
117 static Attribute::MatchResult regexp_comparator(const std::string *first,
118                                                 const std::string *second)
119 {
120     // order is important
121     pcrecpp::RE re(first->c_str());
122     if (re.FullMatch(second->c_str())) {
123         return Attribute::MatchResult::MRTrue;
124     }
125     return  Attribute::MatchResult::MRFalse;
126 }
127
128 Attribute::MatchResult Attribute::lists_comparator(
129         const std::list<std::string> *first,
130         const std::list<std::string> *second,
131         Attribute::MatchResult (*comparator)(const std::string *,
132                                              const std::string *)) const
133 {
134     //NOTE: BONDI defines all availabe matching function as: if some string from first input bag
135     //matches some input string from second input bag, so it's required to find only one matching string
136     MatchResult result = MatchResult::MRFalse;
137
138     for (std::list<std::string>::const_iterator second_iter = second->begin();
139          (second_iter != second->end()) && (result != MatchResult::MRTrue);
140          ++second_iter)
141     {
142         std::string *modified_value = applyModifierFunction(&(*second_iter));
143         //Value was not an URI, it will be removed from the string bag (ignored)
144         if (modified_value == NULL) {
145             continue;
146         }
147
148         for (std::list<std::string>::const_iterator first_iter = first->begin();
149              first_iter != first->end();
150              ++first_iter) {
151             //Compare attributes
152             if ((*comparator)(&(*first_iter), modified_value) == MatchResult::MRTrue) {
153                 result = MatchResult::MRTrue;
154                 break; //Only one match is enough
155             }
156         }
157         if (modified_value) {
158             delete modified_value;
159             modified_value = NULL;
160         }
161     }
162
163     if (result == MatchResult::MRTrue) {
164         LogDebug("Returning TRUE");
165     } else if (result == MatchResult::MRFalse) {
166         LogDebug("Returning FALSE");
167     } else if (result == MatchResult::MRUndetermined) {
168         LogDebug("Returning UNDETERMINED");
169     }
170     return result;
171 }
172
173 std::string * Attribute::applyModifierFunction(const std::string * val) const
174 {
175     std::string * result = NULL;
176     switch (modifierFunction) {
177     case Modifier::Scheme:
178         result = uriScheme(val);
179         break;
180     case Modifier::Authority:
181         result = uriAuthority(val);
182         break;
183     case Modifier::SchemeAuthority:
184         result = uriSchemeAuthority(val);
185         break;
186     case Modifier::Host:
187         result = uriHost(val);
188         break;
189     case Modifier::Path:
190         result = uriPath(val);
191         break;
192     default:
193         result = new std::string(*val);
194     }
195
196     return result;
197 }
198
199 /**
200  * this - attribute obtained from xmlPolicy tree
201  * attribute - attribute obtained from PIP
202  */
203 Attribute::MatchResult Attribute::matchAttributes(
204         const BaseAttribute *attribute) const
205 {
206     std::string tempNam = *(attribute->getName());
207     std::string tempVal;
208     std::string myVal;
209
210     if (!(attribute->getValue()->empty())) {
211         tempVal = attribute->getValue()->front();
212     }
213
214     if (!(this->value.empty())) {
215         myVal = this->value.front();
216     }
217
218     LogDebug("Comparing attribute: " << this->m_name << "(" <<
219         myVal << ") with: " << tempNam <<
220         "(" << tempVal << ")");
221
222     Assert(
223         (this->m_name == *(attribute->getName())) &&
224         "Two completely different attributes are being compared!");
225     Assert(
226         (this->m_typeId == attribute->getType()) &&
227         "Two completely different attributes are being compared!");
228
229     if (attribute->isUndetermind()) {
230         LogDebug("Attribute match undetermined");
231         return MatchResult::MRUndetermined;
232     }
233
234     //Regardles the algorithm used, if we have empty
235     //bag the result is always false
236     if (this->isValueEmpty() || attribute->isValueEmpty()) {
237         if (this->isValueEmpty()) {
238             LogDebug("empty bag in condition comparing");
239         }
240         if (attribute->isValueEmpty()) {
241             LogDebug("empty bag in attribute comparing");
242         }
243         return MatchResult::MRFalse;
244     }
245
246     if (this->matchFunction == Match::Equal) {
247         return lists_comparator(&(this->value),
248                                 attribute->getValue(),
249                                 equal_comparator);
250     } else if (this->matchFunction == Match::Glob) {
251         return lists_comparator(&(this->value),
252                                 attribute->getValue(),
253                                 glob_comparator);
254     } else if (this->matchFunction == Match::Regexp) {
255         return lists_comparator(&(this->value),
256                                 attribute->getValue(),
257                                 regexp_comparator);
258     }        //[CR] Change to Assert
259     Assert(false && " ** Critical :: no match function selected!");
260     return MatchResult::MRFalse; // to remove compilator warning
261 }
262
263 void Attribute::addValue(const std::string *val)
264 {
265     this->getValue()->push_back(*val);
266 }
267
268 std::ostream & operator<<(std::ostream & out,
269                           const Attribute & attr)
270 {
271     out << "attr: m_name: " << *(attr.getName())
272         << " type: " << Attribute::typeToString(attr.getType())
273         << " value: ";
274     if (attr.m_undetermindState) {
275         out << "Undetermined";
276     } else if (attr.getValue()->empty()) {
277         out << "Empty string bag";
278     } else {
279         FOREACH (it, *attr.getValue()) {
280             out << *it;
281         }
282     }
283     return out;
284 }
285
286 bool
287 Attribute::parse(const std::string *input,
288                  std::string *val) const
289 {
290     static const char *pattern =
291         "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?";
292     pcrecpp::RE re(pattern);
293     re.FullMatch(input->c_str(), &val[0], &val[1],
294                  &val[2], &val[3], &val[4],
295                  &val[5], &val[6], &val[7], &val[8]);
296
297 #ifdef ALL_LOGS
298     for (int i = 0; i < 9; i++) {
299         LogDebug("val " << i << " :" << val[i]);
300     }
301 #endif
302
303     if (find_error(val)) {
304         LogDebug("Input is not an URI " << *input);
305         for (int i = 0; i < 9; ++i) {
306             val[i].clear();
307         }
308         return false;
309     }
310
311     return true;
312 }
313
314 Attribute::Attribute(std::istream& is)
315 {
316     Serializer* serializer = Serializer::getInstance();
317
318     m_name = serializer->deserializeString(is);
319     m_typeId = serializer->deserializeType(is);
320
321     setValue(serializer->deserializeListStrings(is));
322
323     matchFunction = serializer->deserializeMatch(is);
324     modifierFunction = serializer->deserializeModifier(is);
325 }
326
327 Attribute::~Attribute()
328 {
329 }
330
331 bool Attribute::serialize(std::ostream& os) const
332 {
333     Serializer* serializer = Serializer::getInstance();
334
335     serializer->serializeString(os, m_name);
336     serializer->serializeType(os, m_typeId);
337     serializer->serializeListStrings(os, value);
338
339     serializer->serializeMatch(os, matchFunction);
340     serializer->serializeModifier(os, modifierFunction);
341     return 0;
342 }
343
344 std::string * Attribute::uriScheme(const std::string *input) const
345 {
346     std::string part[9];
347     if (!parse(input, part)) {
348         return NULL;
349     }
350     return new string(part[1]);
351 }
352
353 std::string *
354 Attribute::uriAuthority(const std::string *input) const
355 {
356     std::string part[9];
357     if (!parse(input, part)) {
358         return NULL;
359     }
360     return new string(part[3]);
361 }
362
363 std::string *
364 Attribute::uriSchemeAuthority(const std::string *input) const
365 {
366     std::string part[9];
367     if (!parse(input, part)) {
368         return NULL;
369     }
370
371     if (part[0].size() == 0 || part[2].size() == 0) {
372         return new std::string();
373     }
374     return new string(part[0] + part[2]);
375 }
376
377 std::string *
378 Attribute::uriHost(const std::string *input) const
379 {
380     std::string part[9];
381     if (!parse(input, part)) {
382         return NULL;
383     }
384     return getHost(&(part[3]));
385 }
386
387 std::string *
388 Attribute::uriPath(const std::string *input) const
389 {
390     //TODO right now uriPath leaves leading '/' in uri, this slash is removed from the string
391     //it's not clear if leading '/' is a part of path component or only the separator
392     std::string part[9];
393     if (!parse(input, part)) {
394         return NULL;
395     }
396
397     std::string * temp = NULL;
398
399     if (part[4].at(0) == '/') {
400         temp = new string(part[4].substr(1, part[4].length() - 1));
401     } else {
402         temp = new string(part[4]);
403     }
404
405     return temp;
406 }
407
408 bool Attribute::find_error(const std::string *tab) const
409 {
410     //We are checking tab[1] which contains scheme without ':' at the end
411     if (!checkScheme(&(tab[1]))) {
412         LogDebug("Check scheme failed, URI is invalid");
413         return true; //error found
414     }
415     if (!checkAuthority(&(tab[3]))) {
416         LogDebug("Check authority failed, URI is invalid");
417         return true; //error found
418     }
419
420     if (!checkPath(&(tab[4]))) {
421         LogDebug("Check path failed, URI is invalid");
422         return true; //error found
423     }
424
425     return false;
426 }
427
428 bool Attribute::checkScheme(const std::string *part) const
429 {
430     Assert(part != NULL && "Checking NULLable string. This should never happen");
431
432     bool result = true;
433
434     //TODO change part->at to data=part->c_str()
435     //TODO can scheme be empty? In absolute URI no, in relative URI yes
436     if (part->empty()) {
437         //Empty string is a correct schema
438         result = true;
439     } else if (alpha[(int) (part->at(0))] == 0) {
440         result = false; // First scheme character must be alpha
441     } else {
442         // rest must be alpha or digit or '+' or '-' or '.'
443         for (unsigned int i = 1; i < part->size(); ++i) {
444             int c = static_cast<int>(part->at(i));
445             if (!isSchemeAllowedCharacter(c)) {
446                 result = false;
447                 break;
448             }
449         }
450     }
451     return result;
452 }
453
454 bool Attribute::checkAuthority(const std::string *part) const
455 {
456     Assert(part != NULL && "Checking NULLable string. This should never happen");
457
458     //Server is a subset of reg_m_names so here we only check if authority matches reg_m_name
459     //Additional check if authority is a valid 'server' component is done in getHost
460     if (part->empty()) {
461         return true; //empty authority is valid uri
462     }
463     bool result = true;
464
465     const char * data = part->c_str();
466     for (size_t i = 0; i < part->length(); ++i) {
467         int c = (int) data[i];
468         if (isUnreserved(c)) {
469             continue;
470         }
471         if (c == '$') {
472             continue;
473         }
474         if (c == ',') {
475             continue;
476         }
477         if (c == ';') {
478             continue;
479         }
480         if (c == ':') {
481             continue;
482         }
483         if (c == '@') {
484             continue;
485         }
486         if (c == '&') {
487             continue;
488         }
489         if (c == '=') {
490             continue;
491         }
492         if (c == '+') {
493             continue;
494         }
495         if (c == '%') {
496             if (isEscaped(data + i)) {
497                 i += 2; //rewind the two escaped characters
498                 continue;
499             }
500         }
501         result = false;
502         break;
503     }
504
505     return result;
506 }
507
508 std::string * Attribute::getHost(const std::string *part) const
509 {
510     if (part->empty()) {
511         return new std::string("");
512     }
513
514     //Check userinfo
515     size_t userInfoPos = part->find("@");
516     if (userInfoPos != std::string::npos) {
517         std::string data = part->substr(0, userInfoPos);
518         if (!isUserInfoAllowedString(&data)) {
519             return new string(""); //the authority is not composed of 'server'  part
520         }
521     }
522
523     std::string host;
524     //If we use host modifier then authority is composed of 'server' part so
525     //the port must contain only digits
526     size_t portPos = part->find(":");
527     if (portPos != std::string::npos) {
528         for (unsigned int i = portPos + 1; i < part->size(); ++i) {
529             if (!digit[(int) part->at(i)]) {
530                 return new string(""); //the authority is not composed of 'server'  part
531             }
532         }
533         host = part->substr(userInfoPos + 1, portPos - (userInfoPos + 1));
534     } else {
535         host = part->substr(userInfoPos + 1, part->length() - (userInfoPos + 1));
536     }
537
538     if (!isHostAllowedString(&host)) {
539         //Even if the string is not allowed for host this can still be a valid uri
540         return new string("");
541     }
542
543     return new std::string(host);
544 }
545
546 bool Attribute::checkPath(const std::string *part) const
547 {
548     bool result = true;
549
550     const char * data = part->c_str();
551
552     for (unsigned int i = 0; i < part->size(); ++i) {
553         int c = data[i];
554         if (c == '/') {
555             //If we found slash then the next character must be a part of segment
556             //It cannot be '/' so we have to check it immediately
557             i++;
558             c = data[i];
559             if (!isSegmentAllowedCharacter(c)) {
560                 result = false;
561                 break;
562             }
563         } else if (c == ';') {
564             //Start param part of segment
565             i++; //Param can be empty so we don't have to check what's right after semicolon
566             continue;
567         } else if (c == '%') {
568             //We have to handle escaped characters differently than other segment allowed characters
569             //because we need an array
570             if (isEscaped(data + i)) {
571                 i += 2;
572             } else {
573                 result = false;
574                 break;
575             }
576         } else {
577             if (!isSegmentAllowedCharacter(c)) {
578                 result = false;
579                 break;
580             }
581         }
582     }
583
584     return result;
585 }
586
587 bool Attribute::isSchemeAllowedCharacter(int c) const
588 {
589     bool result = false;
590     if (isAlphanum(c)) {
591         result = true;
592     } else if (c == '+') {
593         result = true;
594     } else if (c == '-') {
595         result = true;
596     } else if (c == '.') {
597         result = true;
598     }
599
600     return result;
601 }
602
603 bool Attribute::isSegmentAllowedCharacter(int c) const
604 {
605     bool result = true;
606
607     //    LogDebug("Checking is segment allowed for char "<<(char)c);
608
609     if (isUnreserved(c)) { //do nothing, result = true
610     } else if (c == ':') { //do nothing, result = true
611     } else if (c == '@') { //do nothing, result = true
612     } else if (c == '&') { //do nothing, result = true
613     } else if (c == '=') { //do nothing, result = true
614     } else if (c == '+') { //do nothing, result = true
615     } else if (c == '$') { //do nothing, result = true
616     } else if (c == ',') { //do nothing, result = true
617     } else {
618         result = false;
619     }
620
621     return result;
622 }
623
624 bool Attribute::isUserInfoAllowedString(const std::string * str) const
625 {
626     bool result = false;
627
628     const char * data = str->c_str();
629
630     for (unsigned int i = 0; i < str->length(); ++i) {
631         int c = data[i];
632         if (isUnreserved(c)) {
633             result = true;
634         } else if (c == '%') {
635             //isEsacped method checks if we don't cross array bounds, so we can
636             //safely give data[i] here
637             result = isEscaped((data + i));
638             if (result == false) {
639                 break;
640             }
641             i += 2; //rewind the next two characters sEsacped method checks if we don't cross array bounds, so we can safely rewind
642         } else if (c == ',') {
643             result = true;
644         } else if (c == '$') {
645             result = true;
646         } else if (c == '+') {
647             result = true;
648         } else if (c == '=') {
649             result = true;
650         } else if (c == '&') {
651             result = true;
652         } else if (c == '@') {
653             result = true;
654         } else if (c == ':') {
655             result = true;
656         }
657     }
658     return result;
659 }
660
661 bool Attribute::isUnreserved(int c) const
662 {
663     return isAlphanum(c) || mark[c];
664 }
665
666 bool Attribute::isAlphanum(int c) const
667 {
668     return alpha[c] || digit[c];
669 }
670
671 bool Attribute::isHex(int c) const
672 {
673     bool result = false;
674
675     if (digit[c]) {
676         result = true;
677     } else if (c == 'A') {
678         result = true;
679     } else if (c == 'B') {
680         result = true;
681     } else if (c == 'C') {
682         result = true;
683     } else if (c == 'D') {
684         result = true;
685     } else if (c == 'E') {
686         result = true;
687     } else if (c == 'F') {
688         result = true;
689     } else if (c == 'a') {
690         result = true;
691     } else if (c == 'b') {
692         result = true;
693     } else if (c == 'c') {
694         result = true;
695     } else if (c == 'd') {
696         result = true;
697     } else if (c == 'e') {
698         result = true;
699     } else if (c == 'f') {
700         result = true;
701     }
702
703     return result;
704 }
705
706 bool Attribute::isEscaped(const char esc[3]) const
707 {
708     if (esc == NULL) {
709         return false;
710     }
711
712     if ((esc[0] == 0) || (esc[1] == 0) || (esc[2] == 0)) {
713         //We get an array that seems to be out of bounds.
714         //To be on the safe side return here
715         LogDebug("HEX NULLS");
716         return false;
717     }
718
719     if (esc[0] != '%') {
720         LogDebug(
721             "Error: first character of escaped value must be a precent but is "
722             <<
723             esc[0]);
724         return false;
725     }
726
727 #ifdef ALL_LOGS
728     for (int i = 0; i < 3; i++) {
729         LogDebug("HEX " << esc[i]);
730     }
731 #endif
732     return isHex((int) esc[1]) && isHex((int) esc[2]);
733 }
734
735 bool Attribute::isHostAllowedString(const std::string * str) const
736 {
737     bool result = true;
738
739     if (digit[(int) str->at(0)]) {
740         //IPv4 address
741         result = isIPv4AllowedString(str);
742     } else {
743         //Hostname
744         result = isHostNameAllowedString(str);
745     }
746
747     return result;
748 }
749
750 bool Attribute::isIPv4AllowedString(const std::string * str) const
751 {
752     LogDebug("Is hostIPv4 allowed String for " << *str);
753
754     const char * data = str->c_str();
755     bool result = true;
756     int digitCounter = 0;
757     int dotCounter = 0;
758
759     for (unsigned int i = 0; i < str->length(); ++i) {
760         if (data[i] == '.') {
761             dotCounter++;
762             digitCounter = 0;
763         } else if (digit[(int) data[i]]) {
764             digitCounter++;
765             if ((digitCounter > 3) || !digitCounter) {
766                 result = false;
767                 break;
768             }
769         } else {
770             result = false;
771             break;
772         }
773     }
774     if (dotCounter != 3) {
775         result = false;
776     }
777     return result;
778 }
779
780 bool Attribute::isHostNameAllowedString(const std::string * str) const
781 {
782     LogDebug("Is hostname allowed String for " << *str);
783
784     int lastPosition = 0; //the position of last dot + 1
785     const char * data = str->c_str();
786     bool finalDot = false;
787     size_t end = str->length();
788     bool result = false;
789
790     for (size_t i = 0; i < end; ++i) {
791         if (data[i] == '.') {
792             if (i == str->length() - 1) { //ending dot
793                 //There can be a leading '.' int the hostm_name
794                 finalDot = true;
795                 break;
796             } else {
797                 //we found domain label
798                 if (!isDomainLabelAllowedString(data + lastPosition, i -
799                                                 lastPosition)) {
800                     result = false;
801                     goto end;
802                 }
803                 lastPosition = i + 1; //Set position to position of last dot + 1
804             }
805         }
806     }
807
808     if (finalDot) {
809         //we have to rewind one position to check the rightmost string
810         //but only in case we find final dot
811         end--;
812     }
813     //Compare only the rightmost string aaa.bbbb.rightmostString.
814     result = isTopLabelAllowedString(data + lastPosition, end - lastPosition);
815
816 end:
817
818     if (result) {
819         LogInfo("Hostname is allowed");
820     } else {
821         LogInfo("Hostname is NOT allowed");
822     }
823
824     return result;
825 }
826
827 bool Attribute::isDomainLabelAllowedString(const char * data,
828         int length) const
829 {
830     LogDebug(
831         "Is domain allowed String for " << data << " taking first " <<
832         length <<
833         " chars");
834
835     if (!isAlphanum((int) data[0]) || !isAlphanum((int) data[length - 1])) {
836         return false;
837     }
838
839     for (int i = 0; i < length; i++) {
840         if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
841             return false;
842         }
843     }
844     return true;
845 }
846
847 bool Attribute::isTopLabelAllowedString(const char * data,
848         int length) const
849 {
850     if ((!alpha[(int) data[0]]) || (!isAlphanum((int) data[length - 1]))) {
851         return false;
852     }
853
854     for (int i = 1; i < length - 1; i++) {
855         if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
856             return false;
857         }
858     }
859     return true;
860 }
861
862 void printAttributes(const AttributeSet& attrs)
863 {
864     if (attrs.empty()) {
865         LogWarning("Empty attribute set");
866     } else {
867         LogDebug("PRINT ATTRIBUTES:");
868         for (AttributeSet::const_iterator it = attrs.begin();
869              it != attrs.end();
870              ++it)
871         {
872             LogDebug("name: " << *(*it)->getName());
873         }
874     }
875 }
876
877 void printAttributes(const std::list<Attribute> & attrs)
878 {
879     if (attrs.empty()) {
880         LogWarning("Empty attribute set");
881     } else {
882         LogDebug("PRINT ATTRIBUTES:");
883         for (std::list<Attribute>::const_iterator it = attrs.begin();
884              it != attrs.end();
885              ++it
886              ) {
887             LogDebug(*it);
888         }
889     }
890 }
891
892 //KW const char * matchResultToString(Attribute::MatchResult result){
893 //KW
894 //KW     const char * ret = NULL;
895 //KW
896 //KW     switch(result){
897 //KW
898 //KW         case Attribute::MRTrue:
899 //KW             ret = "true";
900 //KW             break;
901 //KW         case Attribute::MRFalse:
902 //KW             ret = "false";
903 //KW            break;
904 //KW         case Attribute::MRUndetermined:
905 //KW             ret = "undetermined";
906 //KW             break;
907 //KW         default:
908 //KW             ret = "Wrong match result";
909 //KW     }
910 //KW
911 //KW     return ret;
912 //KW
913 //KW }