1#Licensed to the Apache Software Foundation (ASF) under one
2#or more contributor license agreements.  See the NOTICE file
3#distributed with this work for additional information
4#regarding copyright ownership.  The ASF licenses this file
5#to you under the Apache License, Version 2.0 (the
6#"License"); you may not use this file except in compliance
7#with the License.  You may obtain a copy of the License at
8
9#     http://www.apache.org/licenses/LICENSE-2.0
10
11#Unless required by applicable law or agreed to in writing, software
12#distributed under the License is distributed on an "AS IS" BASIS,
13#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#See the License for the specific language governing permissions and
15#limitations under the License.
16# $Id:types.py 6172 2007-05-22 20:26:54Z zim $
17#
18#------------------------------------------------------------------------------
19
20""" Higher level data types and type related classes.
21
22    Supported Types (Verification and Display):
23
24      address        - validates ip:port and host:port tcp addresses
25      ip_address     - validates and IP address
26      net_address    - validates an IP like address, ie netmask
27      hostname       - validates a hostname with DNS
28      eaddress       - validates a single email address or a comma
29                       seperated list of email addresses
30      http_version   - validates a value is a http version (1.0/1.1)
31      tcp_port       - validates a value to be a valid tcp port (2-65535)
32      bool           - validates value is (0, 1, true, false) / converts
33                       true -> 1 and false -> 0
34      directory      - validates a values is a directory / resolves path to
35                       absolute path
36      file           - validates a value is a file / resolves path to absolute
37                       path
38      float          - validates a value is a float, converts string to float
39      pos_float      - validates a value is a float and >= 0, converts string
40                       to float
41      pos_num        - same as pos_float
42      neg_float      - validates a value is a float and < 0, converts string to
43                       float
44      int            - validates a value is an integer, converts string to
45                       integer
46      pos_int        - validates a value is an integer and >= 0, converts
47                       string to integer
48      neg_int        - validates a values is an integer and < 0, converts
49                       striing to integer
50      freq           - frequency, positive integer
51      size           - validates a size in bytes, kb, mb, kb, and tb
52                       (int > 0 post fixed with K, M, G, or T) also converts
53                       value to integer bytes
54      range          - numeric range, x-y normalized to a tuple, if a single
55                       number is supplie a single element tuple is returned
56      timestamp      - utc timestamp of the form YYYYMMDDHHMMSS
57      user_account   - UNIX user account name
58      user_group     - UNIX group name
59      string         - arbitrarily long string
60      list           - comma seperated list of strings of arbitrary length,
61      keyval         - comma seperated list of key=value pairs, key does not
62                       need to be unique.
63      uri            - a uri """
64
65import sys, os, socket, pwd, grp, stat, re, re, string, pprint, urlparse
66
67from tcp import tcpSocket, check_net_address, check_ip_address
68from util import check_timestamp
69
70types = { 'directory'      : { 'db'    : 'string',
71                               'units' : None     },
72
73          'address'        : { 'db'    : 'string',
74                               'units' : None     },
75
76          'ip_address'     : { 'db'    : 'string',
77                               'units' : None     },
78
79          'net_address'    : { 'db'    : 'string',
80                               'units' : None     },
81
82          'bool'           : { 'db'    : 'bool',
83                               'units' : None     },
84
85          'int'            : { 'db'    : 'integer',
86                               'units' : None     },
87
88          'float'          : { 'db'    : 'float',
89                               'units' : None     },
90
91          'pos_int'        : { 'db'    : 'integer',
92                               'units' : None     },
93
94          'neg_int'        : { 'db'    : 'integer',
95                               'units' : None     },
96
97          'pos_num'        : { 'db'    : 'float',
98                               'units' : None     },
99
100          'pos_float'      : { 'db'    : 'float',
101                               'units' : None     },
102
103          'neg_float'      : { 'db'    : 'float',
104                               'units' : None     },
105
106          'string'         : { 'db'    : 'string',
107                               'units' : None     },
108
109          'list'           : { 'db'    : 'string',
110                               'units' : None     },
111
112          'file'           : { 'db'    : 'string',
113                               'units' : None     },
114
115          'size'           : { 'db'    : 'integer',
116                               'units' : 'bytes'  },
117
118          'freq'           : { 'db'    : 'integer',
119                               'units' : 'hz'     },
120
121          'eaddress'       : { 'db'    : 'string',
122                               'units' : None     },
123
124          'tcp_port'       : { 'db'    : 'integer',
125                               'units' : None     },
126
127          'http_version'   : { 'db'    : 'float',
128                               'units' : None     },
129
130          'range'          : { 'db'    : 'string',
131                               'units' : None     },
132
133          'hostname'       : { 'db'    : 'string',
134                               'units' : None     },
135
136          'user_account'   : { 'db'    : 'string',
137                               'units' : None     },
138
139          'user_group'     : { 'db'    : 'string',
140                               'units' : None     },
141
142          'timestamp'      : { 'db'    : 'timestamp',
143                               'units' : None     },
144
145          'keyval'         : { 'db'    : 'string',
146                               'units' : None     },
147
148          'uri'            : { 'db'    : 'string',
149                               'units' : None     },
150
151          ''               : { 'db'    : 'string',
152                               'units' : None     }}
153
154dbTypes = { 'string'  :   { 'type'  : 'varchar',
155                            'store' : 'type_strings_0',
156                            'table' : True              },
157
158            'integer' :   { 'type'  : 'bigint',
159                            'store' : 'integers',
160                            'table' : False             },
161
162            'float' :     { 'type'  : 'real',
163                            'store' : 'floats',
164                            'table' : False             },
165
166            'bool' :      { 'type'  : 'boolean',
167                            'store' : 'bools',
168                            'table' : False             },
169
170            'timestamp' : { 'type'  : 'timestamp(0)',
171                            'store' : 'timestamps',
172                            'table' : False             }}
173
174reSizeFormat = re.compile("^(\d+)(k|m|g|t|p|kb|mb|gb|tb|pb)$", flags=2)
175reDash = re.compile("\s*-\s*")
176
177sizeFactors = { 'b'     : 1,
178                'bytes' : 1,
179                'k'     : 1024,
180                'kb'    : 1024,
181                'm'     : 1048576,
182                'mb'    : 1048576,
183                'g'     : 1073741824,
184                'gb'    : 1073741824,
185                't'     : 1099511627776,
186                'tb'    : 1099511627776,
187                'p'     : 1125899906842624,
188                'pb'    : 1125899906842624 }
189
190freqFactors = { 'hz'  : 1,
191                'khz' : 1000,
192                'mhz' : 1000000,
193                'ghz' : 1000000000,
194                'thz' : 1000000000000,
195                'phz' : 1000000000000000 }
196
197sizeMap = [ { 'factor' : sizeFactors['b'],
198              'long'   : 'byte',
199              'short'  : 'byte'           },
200
201            { 'factor' : sizeFactors['k'],
202              'long'   : 'Kilobyte',
203              'short'  : 'KB'             },
204
205            { 'factor' : sizeFactors['m'],
206              'long'   : 'Megabyte',
207              'short'  : 'MB'             },
208
209            { 'factor' : sizeFactors['g'],
210              'long'   : 'Gigabyte',
211              'short'  : 'GB'             },
212
213            { 'factor' : sizeFactors['t'],
214              'long'   : 'Terabyte',
215              'short'  : 'TB'             },
216
217            { 'factor' : sizeFactors['p'],
218              'long'   : 'Petabyte',
219              'short'  : 'PB'             } ]
220
221freqMap = [ { 'factor' : freqFactors['hz'],
222              'long'   : 'Hertz',
223              'short'  : 'Hz'               },
224
225            { 'factor' : freqFactors['khz'],
226              'long'   : 'Kilohertz',
227              'short'  : 'KHz'              },
228
229            { 'factor' : freqFactors['mhz'],
230              'long'   : 'Megahertz',
231              'short'  : 'MHz'              },
232
233            { 'factor' : freqFactors['ghz'],
234              'long'   : 'Gigahertz',
235              'short'  : 'GHz'              },
236
237            { 'factor' : freqFactors['thz'],
238              'long'   : 'Terahertz',
239              'short'  : 'THz'              },
240
241            { 'factor' : freqFactors['phz'],
242              'long'   : 'Petahertz',
243              'short'  : 'PHz'              } ]
244
245reListString = r"(?<!\\),"
246reList = re.compile(reListString)
247
248reKeyVal = r"(?<!\\)="
249reKeyVal = re.compile(reKeyVal)
250
251class typeToString:
252    """Provides method for converting normalized types to strings."""
253    def __init__(self):
254        self.toStringFunctions = {}
255        self.__build_to_string_functions()
256
257    def __call__(self, type, value):
258        return self.toStringFunctions[type](value)
259
260    def __build_to_string_functions(self):
261        functions = {}
262        for function in dir(self):
263            functions[function] = 1
264
265        for type in types.keys():
266            # kinda bad, need to find out how to know the name of the class
267            #  I'm in.  But it works.
268            functionName = "_typeToString__tostring_%s" % type
269            if functions.has_key(functionName):
270                self.toStringFunctions[type] = getattr(self, functionName)
271            else:
272                if type == '':
273                    self.toStringFunctions[type] = self.__tostring_nothing
274                else:
275                    error = "To string function %s for type %s does not exist." \
276                        % (functionName, type)
277                    raise Exception(error)
278                    sys.exit(1)
279
280    def __tostring(self, value):
281        return str(value)
282
283    def __tostring_directory(self, value):
284        return self.__tostring(value)
285
286    def __tostring_address(self, value):
287        return "%s:%s" % (value[0], value[1])
288
289    def __tostring_ip_address(self, value):
290        return self.__tostring(value)
291
292    def __tostring_net_address(self, value):
293        return self.__tostring(value)
294
295    def __tostring_bool(self, value):
296        if value == False:
297            return 'false'
298        elif value == True:
299            return 'true'
300        else:
301            return str(value)
302
303    def __tostring_int(self, value):
304        return self.__tostring(value)
305
306    def __tostring_float(self, value):
307        return self.__tostring(value)
308
309    def __tostring_pos_int(self, value):
310        return self.__tostring(value)
311
312    def __tostring_neg_int(self, value):
313        return self.__tostring(value)
314
315    def __tostring_freq(self, value):
316        return self.__tostring(value)
317
318    def __tostring_pos_float(self, value):
319        return self.__tostring(value)
320
321    def __tostring_pos_num(self, value):
322        return self.__tostring(value)
323
324    def __tostring_neg_float(self, value):
325        return self.__tostring(value)
326
327    def __tostring_string(self, value):
328        return value
329
330    def __tostring_keyval(self, value):
331        string = '"' # to protect from shell escapes
332        for key in value:
333          # for item in value[key]:
334          #      string = "%s%s=%s," % (string, key, item)
335          # Quotes still cannot protect Double-slashes.
336          # Dealing with them separately
337          val = re.sub(r"\\\\",r"\\\\\\\\",value[key])
338
339          string = "%s%s=%s," % (string, key, val)
340
341        return string[:-1] + '"'
342
343    def __tostring_list(self, value):
344        string = ''
345        for item in value:
346            string = "%s%s," % (string, item)
347
348        return string[:-1]
349
350    def __tostring_file(self, value):
351        return self.__tostring(value)
352
353    def __tostring_size(self, value):
354        return self.__tostring(value)
355
356    def __tostring_eaddress(self, value):
357        return self.__tostring(value)
358
359    def __tostring_tcp_port(self, value):
360        return self.__tostring(value)
361
362    def __tostring_http_version(self, value):
363        return self.__tostring(value)
364
365    def __tostring_range(self, value):
366        if len(value) < 2:
367          return value[0]
368        else:
369          return "%s-%s" % (value[0], value[1])
370
371    def __tostring_timestamp(self, value):
372        return self.__tostring(value)
373
374    def __tostring_hostname(self, value):
375        return self.__tostring(value)
376
377    def __tostring_user_account(self, value):
378        return self.__tostring(value)
379
380    def __tostring_user_group(self, value):
381        return self.__tostring(value)
382
383    def __tostring_uri(self, value):
384        return self.__tostring(value)
385
386    def __tostring_nothing(self, value):
387        return value
388
389class typeValidator:
390    """Type validation class used to normalize values or validated
391       single/large sets of values by type."""
392
393    def __init__(self, originalDir=None):
394        self.verifyFunctions = {}
395        self.__build_verify_functions()
396
397        self.validateList = []
398        self.validatedInfo = []
399        self.__originalDir = originalDir
400
401    def __getattr__(self, attrname):
402        """validateList  = [ { 'func' : <bound method configValidator>,
403                               'name' : 'SA_COMMON.old_xml_dir',
404                               'value': 'var/data/old'                 },
405
406                             { 'func' : <bound method configValidator>,
407                               'name' : 'SA_COMMON.log_level',
408                               'value': '4'                            } ]
409
410           validatedInfo = [ { # name supplied to add()
411                               'name'       : 'SA_COMMON.tmp_xml_dir',
412
413                               # is valid or not
414                               'isValid'    : 1
415
416                               # normalized value
417                               'normalized' : /var/data/tmp,
418
419                               # error string ?
420                               'errorData'  : 0                        },
421
422                             { 'name'       : 'SA_COMMON.new_xml_dir',
423                               'isValid'    : 1
424                               'normalized' : /var/data/new,
425                               'errorData'  : 0                        } ]"""
426
427        if attrname   == "validateList":
428            return self.validateList   # list of items to be validated
429        elif attrname == "validatedInfo":
430            return self.validatedInfo  # list of validation results
431        else: raise AttributeError, attrname
432
433    def __build_verify_functions(self):
434        functions = {}
435        for function in dir(self):
436            functions[function] = 1
437
438        for type in types.keys():
439            # kinda bad, need to find out how to know the name of the class
440            #  I'm in.  But it works.
441            functionName = "_typeValidator__verify_%s" % type
442            if functions.has_key(functionName):
443                self.verifyFunctions[type] = getattr(self, functionName)
444            else:
445                if type == '':
446                    self.verifyFunctions[type] = self.__verify_nothing
447                else:
448                    error = "Verify function %s for type %s does not exist." \
449                        % (functionName, type)
450                    raise Exception(error)
451                    sys.exit(1)
452
453    def __get_value_info(self):
454        valueInfo = { 'isValid' : 0, 'normalized' : 0, 'errorData' : 0 }
455
456        return valueInfo
457
458    def __set_value_info(self, valueInfo, **valueData):
459        try:
460            valueInfo['normalized'] = valueData['normalized']
461            valueInfo['isValid'] = 1
462        except KeyError:
463            valueInfo['isValid'] = 0
464            try:
465                valueInfo['errorData'] = valueData['errorData']
466            except:
467                pass
468
469    # start of 'private' verification methods, each one should correspond to a
470    #   type string (see self.verify_config())
471    def __verify_directory(self, type, value):
472        valueInfo = self.__get_value_info()
473
474        if os.path.isdir(value):
475            self.__set_value_info(valueInfo, normalized=self.normalize(type,
476                                                                       value))
477        else:
478            self.__set_value_info(valueInfo)
479
480        return valueInfo
481
482    def __norm_directory(self, value):
483        return self.__normalizedPath(value)
484
485    def __verify_address(self, type, value):
486        valueInfo = self.__get_value_info()
487
488        try:
489            socket = tcpSocket(value)
490            if socket.verify():
491                self.__set_value_info(valueInfo, normalized=self.normalize(type,
492                                                                       value))
493            else:
494                self.__set_value_info(valueInfo)
495        except:
496            self.__set_value_info(valueInfo)
497
498        return valueInfo
499
500    def __norm_address(self, value):
501        return value.split(':')
502
503    def __verify_ip_address(self, type, value):
504        valueInfo = self.__get_value_info()
505
506        if check_ip_address(value):
507            self.__set_value_info(valueInfo, normalized=self.normalize(type,
508                                                                       value))
509        else:
510            self.__set_value_info(valueInfo)
511
512        return valueInfo
513
514    def __verify_net_address(self, type, value):
515        valueInfo = self.__get_value_info()
516
517        if check_net_address(value):
518            self.__set_value_info(valueInfo, normalized=self.normalize(type,
519                                                                       value))
520        else:
521            self.__set_value_info(valueInfo)
522
523        return valueInfo
524
525    def __verify_bool(self, type, value):
526        valueInfo = self.__get_value_info()
527
528        value = str(value)
529        if re.match("^false|0|f|no$", value, 2):
530            self.__set_value_info(valueInfo, normalized=False)
531        elif re.match("^true|1|t|yes$", value, 2):
532            self.__set_value_info(valueInfo, normalized=True)
533        else:
534            self.__set_value_info(valueInfo)
535
536        return valueInfo
537
538    def __norm_bool(self, value):
539        value = str(value)
540        norm = ""
541        if re.match("^false|0|f|no$", value, 2):
542            norm = False
543        elif re.match("^true|1|t|yes$", value, 2):
544            norm = True
545        else:
546            raise Exception("invalid bool specified: %s" % value)
547
548        return norm
549
550    def __verify_int(self, type, value):
551        valueInfo = self.__get_value_info()
552
553        try:
554            self.__set_value_info(valueInfo, normalized=self.normalize(type,
555                                                                       value))
556        except:
557            self.__set_value_info(valueInfo)
558
559        return valueInfo
560
561    def __norm_int(self, value):
562        return int(value)
563
564    def __verify_float(self, type, value):
565        valueInfo = self.__get_value_info()
566
567        try:
568            self.__set_value_info(valueInfo, normalized=self.normalize(type,
569                                                                       value))
570        except:
571            self.__set_value_info(valueInfo)
572
573        return valueInfo
574
575    def __norm_float(self, value):
576        return float(value)
577
578    def __verify_pos_int(self, type, value):
579        valueInfo = self.__get_value_info()
580
581        try:
582            value = self.normalize(type, value)
583        except:
584            self.__set_value_info(valueInfo)
585        else:
586            self.__set_value_info(valueInfo, normalized=value)
587
588        return valueInfo
589
590    def __norm_pos_int(self, value):
591        value = int(value)
592        if value < 0:
593            raise Exception("value is not positive: %s" % value)
594
595        return value
596
597    def __verify_neg_int(self, type, value):
598        valueInfo = self.__get_value_info()
599
600        try:
601            value = self.normalize(type, value)
602        except:
603            self.__set_value_info(valueInfo)
604        else:
605            self.__set_value_info(valueInfo, normalized=value)
606
607        return valueInfo
608
609    def __norm_neg_int(self, type, value):
610        value = int(value)
611        if value > 0:
612            raise Exception("value is not negative: %s" % value)
613
614        return value
615
616    def __verify_freq(self, type, value):
617        return self.__verify_pos_int(type, value)
618
619    def __norm_freq(self, value):
620        return self.__norm_pos_int(value)
621
622    def __verify_pos_float(self, type, value):
623        valueInfo = self.__get_value_info()
624
625        try:
626            value = self.normalize(type, value)
627        except:
628            self.__set_value_info(valueInfo)
629        else:
630            self.__set_value_info(valueInfo, normalized=value)
631
632        return valueInfo
633
634    def __norm_pos_float(self, value):
635        value = float(value)
636        if value < 0:
637            raise Exception("value is not positive: %s" % value)
638
639        return value
640
641    def __verify_pos_num(self, type, value):
642        return self.__verify_pos_float(value)
643
644    def __norm_pos_num(self, value):
645        return self.__norm_pos_float(value)
646
647    def __verify_neg_float(self, type, value):
648        valueInfo = self.__get_value_info()
649
650        try:
651            value = self.normalize(type, value)
652        except:
653            self.__set_value_info(valueInfo)
654        else:
655            self.__set_value_info(valueInfo, normalized=value)
656
657        return valueInfo
658
659    def __norm_neg_float(self, value):
660        value = float(value)
661        if value >= 0:
662            raise Exception("value is not negative: %s" % value)
663
664        return value
665
666    def __verify_string(self, type, value):
667        valueInfo = self.__get_value_info()
668        self.__set_value_info(valueInfo, normalized=self.normalize(type,
669                                                                   value))
670
671        return valueInfo
672
673    def __norm_string(self, value):
674        return str(value)
675
676    def __verify_keyval(self, type, value):
677        valueInfo = self.__get_value_info()
678
679        if reKeyVal.search(value):
680          try:
681            self.__set_value_info(valueInfo, normalized=self.normalize(type,
682                                value))
683          except:
684            self.__set_value_info(valueInfo, errorData = \
685              "invalid list of key-value pairs : [ %s ]" % value)
686        else:
687            msg = "No key value pairs found?"
688            self.__set_value_info(valueInfo, errorData=msg)
689
690        return valueInfo
691
692    def __norm_keyval(self, value):
693        list = self.__norm_list(value)
694        keyValue = {}
695        for item in list:
696            (key, value) = reKeyVal.split(item)
697            #if not keyValue.has_key(key):
698            #    keyValue[key] = []
699            #keyValue[key].append(value)
700            keyValue[key] = value
701        return keyValue
702
703    def __verify_list(self, type, value):
704        valueInfo = self.__get_value_info()
705
706        self.__set_value_info(valueInfo, normalized=self.normalize(type,value))
707
708        return valueInfo
709
710    def __norm_list(self, value):
711        norm = []
712        if reList.search(value):
713            norm = reList.split(value)
714        else:
715            norm = [value,]
716
717        return norm
718
719    def __verify_file(self, type, value):
720        valueInfo = self.__get_value_info()
721
722        if os.path.isfile(value):
723            self.__set_value_info(valueInfo, normalized=self.normalize(type,
724                                                                       value))
725        else:
726            self.__set_value_info(valueInfo)
727
728        return valueInfo
729
730    def __norm_file(self, value):
731        return self.__normalizedPath(value)
732
733    def __verify_size(self, type, value):
734        valueInfo = self.__get_value_info()
735
736        value = str(value)
737        if reSizeFormat.match(value):
738            numberPart = int(reSizeFormat.sub("\g<1>", value))
739            factorPart = reSizeFormat.sub("\g<2>", value)
740            try:
741                normalized = normalize_size(numberPart, factorPart)
742                self.__set_value_info(valueInfo,
743                    normalized=normalized)
744            except:
745                self.__set_value_info(valueInfo)
746        else:
747            try:
748                value = int(value)
749            except:
750                self.__set_value_info(valueInfo)
751            else:
752                if value >= 0:
753                    self.__set_value_info(valueInfo, normalized=value)
754                else:
755                    self.__set_value_info(valueInfo)
756
757        return valueInfo
758
759    def __norm_size(self, file):
760        norm = None
761        if reSizeFormat.match(value):
762            numberPart = int(reSizeFormat.sub("\g<1>", value))
763            factorPart = reSizeFormat.sub("\g<2>", value)
764            norm = normalize_size(numberPart, factorPart)
765        else:
766            norm = int(value)
767
768        return norm
769
770
771    def __verify_eaddress(self, type, value):
772        valueInfo = self.__get_value_info()
773
774        emailList = reComma.split(value)
775
776        for emailAddress in emailList:
777            if reEmailAddress.match(emailAddress):
778                emailParts = reEmailDelimit.split(emailAddress)
779                try:
780                    socket.gethostbyname(emailParts[1])
781                    self.__set_value_info(valueInfo, normalized=self.normalize(
782                                          type, value))
783                except:
784                    errorString = "%s is invalid (domain lookup failed)" % \
785                        emailAddress
786                    self.__set_value_info(valueInfo, errorData=errorString)
787            else:
788                errorString = "%s is invalid" % emailAddress
789                self.__set_value_info(valueInfo, errorData=errorString)
790
791        return valueInfo
792
793    def __verify_tcp_port(self, type, value):
794        valueInfo = self.__get_value_info()
795
796        try:
797            value = self.__norm_tcp_port(value)
798        except:
799            self.__set_value_info(valueInfo)
800        else:
801            if value in range(2, 65536):
802                self.__set_value_info(valueInfo, normalized=value)
803            else:
804                self.__set_value_info(valueInfo)
805
806        return valueInfo
807
808    def __norm_tcp_port(self, value):
809        return int(value)
810
811    def __verify_http_version(self, type, value):
812        valueInfo = self.__get_value_info()
813
814        if value in ('1.0', '1.1'):
815            self.__set_value_info(valueInfo, normalized=float(value))
816        else:
817            self.__set_value_info(valueInfo)
818
819        return valueInfo
820
821    def __verify_range(self, type, value):
822        valueInfo = self.__get_value_info()
823
824        range = reDash.split(value)
825
826        try:
827            if len(range) > 1:
828                start = int(range[0])
829                end = int(range[1])
830            else:
831                start = int(range[0])
832                end = None
833        except:
834            self.__set_value_info(valueInfo)
835        else:
836            if end:
837                if end - start != 0:
838                    self.__set_value_info(valueInfo, normalized=(start, end))
839                else:
840                    self.__set_value_info(valueInfo)
841            else:
842                self.__set_value_info(valueInfo, normalized=(start,))
843
844        return valueInfo
845
846    def __norm_range(self, value):
847        range = reDash.split(value)
848        if len(range) > 1:
849            start = int(range[0])
850            end = int(range[1])
851        else:
852            start = int(range[0])
853            end = None
854
855        return (start, end)
856
857    def __verify_uri(self, type, value):
858        valueInfo = self.__get_value_info()
859
860        _norm = None
861        try:
862            uriComponents = urlparse.urlparse(value)
863            if uriComponents[0] == '' or uriComponents[0] == 'file':
864              # if scheme is '' or 'file'
865              if not os.path.isfile(uriComponents[2]) and \
866                                         not os.path.isdir(uriComponents[2]):
867                  raise Exception("Invalid local URI")
868              else:
869                  self.__set_value_info(valueInfo, normalized=self.normalize(
870                                                                  type,value))
871            else:
872              # other schemes
873              # currently not checking anything. TODO
874              self.__set_value_info(valueInfo, normalized=self.normalize(
875                                                                   type,value))
876        except:
877            errorString = "%s is an invalid uri" % value
878            self.__set_value_info(valueInfo, errorData=errorString)
879
880        return valueInfo
881
882    def __norm_uri(self, value):
883       uriComponents = list(urlparse.urlparse(value))
884       if uriComponents[0] == '':
885          # if scheme is '''
886          return self.__normalizedPath(uriComponents[2])
887       elif uriComponents[0] == 'file':
888          # if scheme is 'file'
889          normalizedPath = self.__normalizedPath(uriComponents[2])
890          return urlparse.urlunsplit(uriComponents[0:1] + [normalizedPath] + uriComponents[3:])
891
892       # Not dealing with any other case right now
893       return value
894
895    def __verify_timestamp(self, type, value):
896        valueInfo = self.__get_value_info()
897
898        if check_timestamp(value):
899            self.__set_value_info(valueInfo, normalized=self.normalize(type,
900                                                                       value))
901        else:
902            self.__set_value_info(valueInfo)
903
904        return valueInfo
905
906    def __verify_hostname(self, type, value):
907        valueInfo = self.__get_value_info()
908
909        try:
910            socket.gethostbyname(value)
911            self.__set_value_info(valueInfo, normalized=self.normalize(type,
912                                                                       value))
913        except:
914            errorString = "%s is invalid (domain lookup failed)" % value
915            self.__set_value_info(valueInfo, errorData=errorString)
916
917        return valueInfo
918
919    def __verify_user_account(self, type, value):
920        valueInfo = self.__get_value_info()
921
922        try:
923            pwd.getpwnam(value)
924        except:
925            errorString = "'%s' user account does not exist" % value
926            self.__set_value_info(valueInfo, errorData=errorString)
927        else:
928            self.__set_value_info(valueInfo, normalized=self.normalize(type,
929                                                                       value))
930
931        return valueInfo
932
933    def __verify_user_group(self, type, value):
934        valueInfo = self.__get_value_info()
935
936        try:
937            grp.getgrnam(value)
938        except:
939            errorString = "'%s' group does not exist" % value
940            self.__set_value_info(valueInfo, errorData=errorString)
941        else:
942            self.__set_value_info(valueInfo, normalized=self.normalize(type,
943                                                                       value))
944
945        return valueInfo
946
947    def __verify_nothing(self, type, value):
948        valueInfo = self.__get_value_info()
949
950        self.__set_value_info(valueInfo, normalized=self.normalize(type,
951                                                                   value))
952
953        return valueInfo
954
955    #--------------------------------------------------------------------------
956
957    def normalize(self, type, value):
958        try:
959          normFunc = getattr(self, "_typeValidator__norm_%s" % type)
960          return normFunc(value)
961        except AttributeError, A:
962          # this exception should occur only when we don't have corresponding normalize function
963          return value
964
965    def verify(self, type, value, allowNone=False):
966        """Verifies a value based on its type.
967
968           type      - supported configValidator type
969           value     - data to be validated
970           allowNone - don't freak out if None or '' is supplied
971
972           returns a valueInfo dictionary:
973
974            valueInfo = { 'isValid' : 1, 'normalized' : 5, 'errorData' : 0 }
975
976           where:
977
978            isValid    - true or false (0/1)
979            normalized - the normalized value
980            errorData  - if invalid an error string
981
982           supported types:
983
984            see top level"""
985
986        result = None
987        if allowNone:
988            if value == '' or value == None:
989                result = self.__verify_nothing(None, None)
990                result['normalized'] = None
991            else:
992                result = self.verifyFunctions[type](type, value)
993        else:
994            result = self.verifyFunctions[type](type, value)
995
996        return result
997
998    def is_valid_type(self, type):
999        """Returns true if type is valid."""
1000
1001        return types.has_key(type)
1002
1003    def type_info(self, type):
1004        """Returns type info dictionary."""
1005
1006        dbInfo = dbTypes[types[type]['db']]
1007        typeInfo = types[type].copy()
1008        typeInfo['db'] = dbInfo
1009
1010        return typeInfo
1011
1012    def add(self, name, type, value):
1013        """Adds a value and type by name to the configValidate object to be
1014           verified using validate().
1015
1016           name  - name used to key values and access the results of the
1017                   validation
1018           type  - configValidator type
1019           value - data to be verified"""
1020
1021        self.validateList.append({ 'name' : name,
1022                                   'type' : type,
1023                                   'value': value })
1024
1025    def validate(self, allowNone=False):
1026        """Validates configValidate object populating validatedInfo with
1027           valueInfo dictionaries for each value added to the object."""
1028
1029        for valItem in self.validateList:
1030            valueInfo = self.verify(valItem['type'], valItem['value'],
1031                allowNone)
1032            if valueInfo:
1033                valueInfo['name'] = valItem['name']
1034                self.validatedInfo.append(valueInfo)
1035            else:
1036                raise Exception("\nMissing a return value: valueInfo\n%s" % \
1037                    self.verifyFunctions[valItem['type']](valItem['value']))
1038
1039    def __normalizedPath(self, value):
1040        oldWd = os.getcwd()
1041        if self.__originalDir:
1042          os.chdir(self.__originalDir)
1043        normPath = os.path.realpath(value)
1044        os.chdir(oldWd)
1045        return normPath
1046
1047
1048class display:
1049    def __init__(self):
1050        self.displayFunctions = {}
1051        self.__build_dispaly_functions()
1052
1053    def __build_dispaly_functions(self):
1054        functions = {}
1055        for function in dir(self):
1056            functions[function] = 1
1057
1058        for type in types.keys():
1059            # kinda bad, need to find out how to know the name of the class
1060            #  I'm in.  But it works.
1061            functionName = "_cisplay__display_%s" % type
1062            if functions.has_key(functionName):
1063                self.displayFunctions[type] = getattr(self, functionName)
1064            else:
1065                if type == '':
1066                    self.displayFunctions[type] = self.__display_default
1067                else:
1068                    error = "Display function %s for type %s does not exist." \
1069                        % (functionName, type)
1070                    raise Exception(error)
1071                    sys.exit(1)
1072
1073    def __display_default(self, value, style):
1074        return value
1075
1076    def __display_generic_number(self, value):
1077        displayNumber = ''
1078        splitNum = string.split(str(value), sep='.')
1079        numList = list(str(splitNum[0]))
1080        numList.reverse()
1081        length = len(numList)
1082        counter = 0
1083        for char in numList:
1084            counter = counter + 1
1085            if counter % 3 or counter == length:
1086                displayNumber = "%s%s" % (char, displayNumber)
1087            else:
1088                displayNumber = ",%s%s" % (char, displayNumber)
1089
1090        if len(splitNum) > 1:
1091            displayNumber = "%s.%s" % (displayNumber, splitNum[1])
1092
1093        return displayNumber
1094
1095    def __display_generic_mappable(self, map, value, style, plural=True):
1096        displayValue = ''
1097        length = len(str(value))
1098        if length > 3:
1099            for factorSet in map:
1100                displayValue = float(value) / factorSet['factor']
1101                if len(str(int(displayValue))) <= 3 or \
1102                    factorSet['factor'] == map[-1]['factor']:
1103                    displayValue = "%10.2f" % displayValue
1104                    if displayValue[-1] == '0':
1105                        if displayValue > 1 and style != 'short' and plural:
1106                            displayValue = "%s %ss" % (displayValue[:-1],
1107                                                      factorSet[style])
1108                        else:
1109                            displayValue = "%s %s" % (displayValue[:-1],
1110                                                      factorSet[style])
1111                    else:
1112                        if displayValue > 1 and style != 'short' and plural:
1113                            displayValue = "%s %ss" % (displayValue,
1114                                                      factorSet[style])
1115                        else:
1116                            displayValue = "%s %s" % (displayValue,
1117                                                      factorSet[style])
1118                    break
1119
1120        return displayValue
1121
1122    def __display_directory(self, value, style):
1123        return self.__display_default(value, style)
1124
1125    def __display_address(self, value, style):
1126        return self.__display_default(value, style)
1127
1128    def __display_ip_address(self, value, style):
1129        return self.__display_default(value, style)
1130
1131    def __display_net_address(self, value, style):
1132        return self.__display_default(value, style)
1133
1134    def __display_bool(self, value, style):
1135        displayValue = value
1136
1137        if not isinstance(displayValue, bool):
1138            if re.match("^false|0|f|no$", value, 2):
1139                displayValue=False
1140            elif re.match("^true|1|t|yes$", value, 2):
1141                displayValue=True
1142
1143        return displayValue
1144
1145    def __display_int(self, value, style):
1146        return self.__display_generic_number(value)
1147
1148    def __display_float(self, value, style):
1149        return self.__display_generic_number(value)
1150
1151    def __display_pos_int(self, value, style):
1152        return self.__display_generic_number(value)
1153
1154    def __display_neg_int(self, value, style):
1155        return self.__display_generic_number(value)
1156
1157    def __display_pos_num(self, value, style):
1158        return self.__display_generic_number(value)
1159
1160    def __display_pos_float(self, value, style):
1161        return self.__display_generic_number(value)
1162
1163    def __display_neg_float(self, value, style):
1164        return self.__display_generic_number(value)
1165
1166    def __display_string(self, value, style):
1167        return self.__display_default(value, style)
1168
1169    def __display_list(self, value, style):
1170        value = value.rstrip()
1171        return value.rstrip(',')
1172
1173    def __display_keyval(self, value, style):
1174        value = value.rstrip()
1175        return value.rstrip(',')
1176
1177    def __display_file(self, value, style):
1178        return self.__display_default(value, style)
1179
1180    def __display_size(self, value, style):
1181        return self.__display_generic_mappable(sizeMap, value, style)
1182
1183    def __display_freq(self, value, style):
1184        return self.__display_generic_mappable(freqMap, value, style, False)
1185
1186    def __display_eaddress(self, value, style):
1187        return self.__display_default(value, style)
1188
1189    def __display_tcp_port(self, value, style):
1190        return self.__display_default(value, style)
1191
1192    def __display_http_version(self, value, style):
1193        return self.__display_default(value, style)
1194
1195    def __display_range(self, value, style):
1196        return self.__display_default(value, style)
1197
1198    def __display_hostname(self, value, style):
1199        return self.__display_default(value, style)
1200
1201    def __display_user_account(self, value, style):
1202        return self.__display_default(value, style)
1203
1204    def __display_user_group(self, value, style):
1205        return self.__display_default(value, style)
1206
1207    def __display_timestamp(self, value, style):
1208        return self.__display_default(value, style)
1209
1210    def display(self, type, value, style='short'):
1211        displayValue = value
1212        if value != None:
1213            displayValue = self.displayFunctions[type](value, style)
1214
1215        return displayValue
1216
1217typeValidatorInstance = typeValidator()
1218
1219def is_valid_type(type):
1220    """Returns true if type is valid."""
1221
1222    return typeValidatorInstance.is_valid_type(type)
1223
1224def type_info(type):
1225    """Returns type info dictionary."""
1226
1227    return typeValidatorInstance.type_info(type)
1228
1229def verify(type, value, allowNone=False):
1230    """Returns a normalized valueInfo dictionary."""
1231
1232    return typeValidatorInstance.verify(type, value, allowNone)
1233
1234def __normalize(map, val, factor):
1235    normFactor = string.lower(factor)
1236    normVal = float(val)
1237    return int(normVal * map[normFactor])
1238
1239def normalize_size(size, factor):
1240    """ Normalize a size to bytes.
1241
1242        size   - number of B, KB, MB, GB, TB, or PB
1243        factor - size factor (case insensitive):
1244                 b | bytes - bytes
1245                 k | kb    - kilobytes
1246                 m | mb    - megabytes
1247                 g | gb    - gigabytes
1248                 t | tb    - terabytes
1249                 p | pb    - petabytes
1250    """
1251
1252    return __normalize(sizeFactors, size, factor)
1253
1254def normalize_freq(freq, factor):
1255    """ Normalize a frequency to hertz.
1256
1257        freq   - number of Hz, Khz, Mhz, Ghz, Thz, or Phz
1258        factor - size factor (case insensitive):
1259                 Hz  - Hertz
1260                 Mhz - Megahertz
1261                 Ghz - Gigahertz
1262                 Thz - Terahertz
1263                 Phz - Petahertz
1264    """
1265
1266    return __normalize(freqFactors, freq, factor)
1267