Tizen 2.1 base
[framework/security/security-server.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     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
296 #ifdef ALL_LOGS
297     for (int i = 0; i < 9; i++) {
298         LogDebug("val " << i << " :" << val[i]);
299     }
300 #endif
301
302     if (find_error(val)) {
303         LogDebug("Input is not an URI " << *input);
304         for (int i = 0; i < 9; ++i) {
305             val[i].clear();
306         }
307         return false;
308     }
309
310     return true;
311 }
312
313 Attribute::~Attribute()
314 {
315 }
316
317 std::string * Attribute::uriScheme(const std::string *input) const
318 {
319     std::string part[9];
320     if (!parse(input, part)) {
321         return NULL;
322     }
323     return new string(part[1]);
324 }
325
326 std::string *
327 Attribute::uriAuthority(const std::string *input) const
328 {
329     std::string part[9];
330     if (!parse(input, part)) {
331         return NULL;
332     }
333     return new string(part[3]);
334 }
335
336 std::string *
337 Attribute::uriSchemeAuthority(const std::string *input) const
338 {
339     std::string part[9];
340     if (!parse(input, part)) {
341         return NULL;
342     }
343
344     if (part[0].size() == 0 || part[2].size() == 0) {
345         return new std::string();
346     }
347     return new string(part[0] + part[2]);
348 }
349
350 std::string *
351 Attribute::uriHost(const std::string *input) const
352 {
353     std::string part[9];
354     if (!parse(input, part)) {
355         return NULL;
356     }
357     return getHost(&(part[3]));
358 }
359
360 std::string *
361 Attribute::uriPath(const std::string *input) const
362 {
363     //TODO right now uriPath leaves leading '/' in uri, this slash is removed from the string
364     //it's not clear if leading '/' is a part of path component or only the separator
365     std::string part[9];
366     if (!parse(input, part)) {
367         return NULL;
368     }
369
370     std::string * temp = NULL;
371
372     if (part[4].at(0) == '/') {
373         temp = new string(part[4].substr(1, part[4].length() - 1));
374     } else {
375         temp = new string(part[4]);
376     }
377
378     return temp;
379 }
380
381 bool Attribute::find_error(const std::string *tab) const
382 {
383     //We are checking tab[1] which contains scheme without ':' at the end
384     if (!checkScheme(&(tab[1]))) {
385         LogDebug("Check scheme failed, URI is invalid");
386         return true; //error found
387     }
388     if (!checkAuthority(&(tab[3]))) {
389         LogDebug("Check authority failed, URI is invalid");
390         return true; //error found
391     }
392
393     if (!checkPath(&(tab[4]))) {
394         LogDebug("Check path failed, URI is invalid");
395         return true; //error found
396     }
397
398     return false;
399 }
400
401 bool Attribute::checkScheme(const std::string *part) const
402 {
403     Assert(part != NULL && "Checking NULLable string. This should never happen");
404
405     bool result = true;
406
407     //TODO change part->at to data=part->c_str()
408     //TODO can scheme be empty? In absolute URI no, in relative URI yes
409     if (part->empty()) {
410         //Empty string is a correct schema
411         result = true;
412     } else if (alpha[(int) (part->at(0))] == 0) {
413         result = false; // First scheme character must be alpha
414     } else {
415         // rest must be alpha or digit or '+' or '-' or '.'
416         for (unsigned int i = 1; i < part->size(); ++i) {
417             int c = static_cast<int>(part->at(i));
418             if (!isSchemeAllowedCharacter(c)) {
419                 result = false;
420                 break;
421             }
422         }
423     }
424     return result;
425 }
426
427 bool Attribute::checkAuthority(const std::string *part) const
428 {
429     Assert(part != NULL && "Checking NULLable string. This should never happen");
430
431     //Server is a subset of reg_m_names so here we only check if authority matches reg_m_name
432     //Additional check if authority is a valid 'server' component is done in getHost
433     if (part->empty()) {
434         return true; //empty authority is valid uri
435     }
436     bool result = true;
437
438     const char * data = part->c_str();
439     for (size_t i = 0; i < part->length(); ++i) {
440         int c = (int) data[i];
441         if (isUnreserved(c)) {
442             continue;
443         }
444         if (c == '$') {
445             continue;
446         }
447         if (c == ',') {
448             continue;
449         }
450         if (c == ';') {
451             continue;
452         }
453         if (c == ':') {
454             continue;
455         }
456         if (c == '@') {
457             continue;
458         }
459         if (c == '&') {
460             continue;
461         }
462         if (c == '=') {
463             continue;
464         }
465         if (c == '+') {
466             continue;
467         }
468         if (c == '%') {
469             if (isEscaped(data + i)) {
470                 i += 2; //rewind the two escaped characters
471                 continue;
472             }
473         }
474         result = false;
475         break;
476     }
477
478     return result;
479 }
480
481 std::string * Attribute::getHost(const std::string *part) const
482 {
483     if (part->empty()) {
484         return new std::string("");
485     }
486
487     //Check userinfo
488     size_t userInfoPos = part->find("@");
489     if (userInfoPos != std::string::npos) {
490         std::string data = part->substr(0, userInfoPos);
491         if (!isUserInfoAllowedString(&data)) {
492             return new string(""); //the authority is not composed of 'server'  part
493         }
494     }
495
496     std::string host;
497     //If we use host modifier then authority is composed of 'server' part so
498     //the port must contain only digits
499     size_t portPos = part->find(":");
500     if (portPos != std::string::npos) {
501         for (unsigned int i = portPos + 1; i < part->size(); ++i) {
502             if (!digit[(int) part->at(i)]) {
503                 return new string(""); //the authority is not composed of 'server'  part
504             }
505         }
506         host = part->substr(userInfoPos + 1, portPos - (userInfoPos + 1));
507     } else {
508         host = part->substr(userInfoPos + 1, part->length() - (userInfoPos + 1));
509     }
510
511     if (!isHostAllowedString(&host)) {
512         //Even if the string is not allowed for host this can still be a valid uri
513         return new string("");
514     }
515
516     return new std::string(host);
517 }
518
519 bool Attribute::checkPath(const std::string *part) const
520 {
521     bool result = true;
522
523     const char * data = part->c_str();
524
525     for (unsigned int i = 0; i < part->size(); ++i) {
526         int c = data[i];
527         if (c == '/') {
528             //If we found slash then the next character must be a part of segment
529             //It cannot be '/' so we have to check it immediately
530             i++;
531             c = data[i];
532             if (!isSegmentAllowedCharacter(c)) {
533                 result = false;
534                 break;
535             }
536         } else if (c == ';') {
537             //Start param part of segment
538             i++; //Param can be empty so we don't have to check what's right after semicolon
539             continue;
540         } else if (c == '%') {
541             //We have to handle escaped characters differently than other segment allowed characters
542             //because we need an array
543             if (isEscaped(data + i)) {
544                 i += 2;
545             } else {
546                 result = false;
547                 break;
548             }
549         } else {
550             if (!isSegmentAllowedCharacter(c)) {
551                 result = false;
552                 break;
553             }
554         }
555     }
556
557     return result;
558 }
559
560 bool Attribute::isSchemeAllowedCharacter(int c) const
561 {
562     bool result = false;
563     if (isAlphanum(c)) {
564         result = true;
565     } else if (c == '+') {
566         result = true;
567     } else if (c == '-') {
568         result = true;
569     } else if (c == '.') {
570         result = true;
571     }
572
573     return result;
574 }
575
576 bool Attribute::isSegmentAllowedCharacter(int c) const
577 {
578     bool result = true;
579
580     //    LogDebug("Checking is segment allowed for char "<<(char)c);
581
582     if (isUnreserved(c)) { //do nothing, result = true
583     } else if (c == ':') { //do nothing, result = true
584     } else if (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 {
591         result = false;
592     }
593
594     return result;
595 }
596
597 bool Attribute::isUserInfoAllowedString(const std::string * str) const
598 {
599     bool result = false;
600
601     const char * data = str->c_str();
602
603     for (unsigned int i = 0; i < str->length(); ++i) {
604         int c = data[i];
605         if (isUnreserved(c)) {
606             result = true;
607         } else if (c == '%') {
608             //isEsacped method checks if we don't cross array bounds, so we can
609             //safely give data[i] here
610             result = isEscaped((data + i));
611             if (result == false) {
612                 break;
613             }
614             i += 2; //rewind the next two characters sEsacped method checks if we don't cross array bounds, so we can safely rewind
615         } else if (c == ',') {
616             result = true;
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         }
630     }
631     return result;
632 }
633
634 bool Attribute::isUnreserved(int c) const
635 {
636     return isAlphanum(c) || mark[c];
637 }
638
639 bool Attribute::isAlphanum(int c) const
640 {
641     return alpha[c] || digit[c];
642 }
643
644 bool Attribute::isHex(int c) const
645 {
646     bool result = false;
647
648     if (digit[c]) {
649         result = true;
650     } else if (c == 'A') {
651         result = true;
652     } else if (c == 'B') {
653         result = true;
654     } else if (c == 'C') {
655         result = true;
656     } else if (c == 'D') {
657         result = true;
658     } else if (c == 'E') {
659         result = true;
660     } else if (c == 'F') {
661         result = true;
662     } else if (c == 'a') {
663         result = true;
664     } else if (c == 'b') {
665         result = true;
666     } else if (c == 'c') {
667         result = true;
668     } else if (c == 'd') {
669         result = true;
670     } else if (c == 'e') {
671         result = true;
672     } else if (c == 'f') {
673         result = true;
674     }
675
676     return result;
677 }
678
679 bool Attribute::isEscaped(const char esc[3]) const
680 {
681     if (esc == NULL) {
682         return false;
683     }
684
685     if ((esc[0] == 0) || (esc[1] == 0) || (esc[2] == 0)) {
686         //We get an array that seems to be out of bounds.
687         //To be on the safe side return here
688         LogDebug("HEX NULLS");
689         return false;
690     }
691
692     if (esc[0] != '%') {
693         LogDebug(
694             "Error: first character of escaped value must be a precent but is "
695             <<
696             esc[0]);
697         return false;
698     }
699
700 #ifdef ALL_LOGS
701     for (int i = 0; i < 3; i++) {
702         LogDebug("HEX " << esc[i]);
703     }
704 #endif
705     return isHex((int) esc[1]) && isHex((int) esc[2]);
706 }
707
708 bool Attribute::isHostAllowedString(const std::string * str) const
709 {
710     bool result = true;
711
712     if (digit[(int) str->at(0)]) {
713         //IPv4 address
714         result = isIPv4AllowedString(str);
715     } else {
716         //Hostname
717         result = isHostNameAllowedString(str);
718     }
719
720     return result;
721 }
722
723 bool Attribute::isIPv4AllowedString(const std::string * str) const
724 {
725     LogDebug("Is hostIPv4 allowed String for " << *str);
726
727     const char * data = str->c_str();
728     bool result = true;
729     int digitCounter = 0;
730     int dotCounter = 0;
731
732     for (unsigned int i = 0; i < str->length(); ++i) {
733         if (data[i] == '.') {
734             dotCounter++;
735             digitCounter = 0;
736         } else if (digit[(int) data[i]]) {
737             digitCounter++;
738             if ((digitCounter > 3) || !digitCounter) {
739                 result = false;
740                 break;
741             }
742         } else {
743             result = false;
744             break;
745         }
746     }
747     if (dotCounter != 3) {
748         result = false;
749     }
750     return result;
751 }
752
753 bool Attribute::isHostNameAllowedString(const std::string * str) const
754 {
755     LogDebug("Is hostname allowed String for " << *str);
756
757     int lastPosition = 0; //the position of last dot + 1
758     const char * data = str->c_str();
759     bool finalDot = false;
760     size_t end = str->length();
761     bool result = false;
762
763     for (size_t i = 0; i < end; ++i) {
764         if (data[i] == '.') {
765             if (i == str->length() - 1) { //ending dot
766                 //There can be a leading '.' int the hostm_name
767                 finalDot = true;
768                 break;
769             } else {
770                 //we found domain label
771                 if (!isDomainLabelAllowedString(data + lastPosition, i -
772                                                 lastPosition)) {
773                     result = false;
774                     goto end;
775                 }
776                 lastPosition = i + 1; //Set position to position of last dot + 1
777             }
778         }
779     }
780
781     if (finalDot) {
782         //we have to rewind one position to check the rightmost string
783         //but only in case we find final dot
784         end--;
785     }
786     //Compare only the rightmost string aaa.bbbb.rightmostString.
787     result = isTopLabelAllowedString(data + lastPosition, end - lastPosition);
788
789 end:
790
791     if (result) {
792         LogInfo("Hostname is allowed");
793     } else {
794         LogInfo("Hostname is NOT allowed");
795     }
796
797     return result;
798 }
799
800 bool Attribute::isDomainLabelAllowedString(const char * data,
801         int length) const
802 {
803     LogDebug(
804         "Is domain allowed String for " << data << " taking first " <<
805         length <<
806         " chars");
807
808     if (!isAlphanum((int) data[0]) || !isAlphanum((int) data[length - 1])) {
809         return false;
810     }
811
812     for (int i = 0; i < length; i++) {
813         if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
814             return false;
815         }
816     }
817     return true;
818 }
819
820 bool Attribute::isTopLabelAllowedString(const char * data,
821         int length) const
822 {
823     if ((!alpha[(int) data[0]]) || (!isAlphanum((int) data[length - 1]))) {
824         return false;
825     }
826
827     for (int i = 1; i < length - 1; i++) {
828         if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
829             return false;
830         }
831     }
832     return true;
833 }
834
835 void printAttributes(const AttributeSet& attrs)
836 {
837     if (attrs.empty()) {
838         LogWarning("Empty attribute set");
839     } else {
840         LogDebug("PRINT ATTRIBUTES:");
841         for (AttributeSet::const_iterator it = attrs.begin();
842              it != attrs.end();
843              ++it)
844         {
845             LogDebug("name: " << *(*it)->getName());
846         }
847     }
848 }
849
850 void printAttributes(const std::list<Attribute> & attrs)
851 {
852     if (attrs.empty()) {
853         LogWarning("Empty attribute set");
854     } else {
855         LogDebug("PRINT ATTRIBUTES:");
856         for (std::list<Attribute>::const_iterator it = attrs.begin();
857              it != attrs.end();
858              ++it
859              ) {
860             LogDebug(*it);
861         }
862     }
863 }
864
865 //KW const char * matchResultToString(Attribute::MatchResult result){
866 //KW
867 //KW     const char * ret = NULL;
868 //KW
869 //KW     switch(result){
870 //KW
871 //KW         case Attribute::MRTrue:
872 //KW             ret = "true";
873 //KW             break;
874 //KW         case Attribute::MRFalse:
875 //KW             ret = "false";
876 //KW            break;
877 //KW         case Attribute::MRUndetermined:
878 //KW             ret = "undetermined";
879 //KW             break;
880 //KW         default:
881 //KW             ret = "Wrong match result";
882 //KW     }
883 //KW
884 //KW     return ret;
885 //KW
886 //KW }