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