1from jailconf.exceptions import ConfSemanticError 2from jailconf.lexer import Lexer 3 4from collections import OrderedDict 5import os 6import re 7from tempfile import mkstemp 8 9class Append(object): 10 def __init__(self, param, value): 11 self.param = param 12 self.value = value 13 14class Conf(OrderedDict): 15 16 valid_param_rgx = re.compile('^%s$' % Lexer.t_NAME.__doc__) 17 valid_value_rgx = re.compile('^(%s)$' % '|'.join(( 18 Lexer.t_NAME.__doc__, 19 Lexer.t_SINGLE_QUOTED_STRING.__doc__, 20 Lexer.t_DOUBLE_QUOTED_STRING.__doc__ 21 ))) 22 23 @classmethod 24 def validate_param(cls, param): 25 if cls.valid_param_rgx.match(param): 26 return param 27 else: 28 raise ValueError("Invalid parameter %s" % repr(param)) 29 30 @classmethod 31 def validate_value(cls, value): 32 if cls.valid_value_rgx.match(value): 33 return value 34 else: 35 raise ValueError("Invalid value %s" % repr(value)) 36 37 @classmethod 38 def validate_key_value(cls, key, value): 39 if isinstance(value, JailBlock): 40 cls.validate_value(key) 41 else: 42 cls.validate_param(key) 43 if isinstance(value, str): 44 cls.validate_value(value) 45 elif isinstance(value, list): 46 if len(value) < 2: 47 raise ValueError("Lists of values should have at least 2 elements.") 48 for v in value: 49 cls.validate_value(v) 50 elif value is not True: 51 raise ValueError( 52 "Invalid type for value %s. Should be of type str, list, JailBlock, or should be the value True." 53 % repr(value) 54 ) 55 return key, value 56 57 def update(self, *args, **kwargs): 58 if len(args) > 1: 59 raise TypeError("Expected at most 1 argument, got 2") 60 if args: 61 if hasattr(args[0], 'keys'): 62 lst = [(k,args[0][k]) for k in args[0].keys()] 63 else: 64 lst = args[0] 65 else: 66 lst = [] 67 if kwargs: 68 lst.extend(kwargs.items()) 69 for item in lst: 70 if isinstance(item, Append): 71 if item.param in self: 72 if isinstance(self[item.param], str): 73 super(Conf, self).__setitem__(item.param, [self[item.param], item.value]) 74 elif isinstance(self[item.param], list): 75 self[item.param].append(item.value) 76 else: 77 raise ConfSemanticError( 78 "Trying to append to parameter %s but it has type %s" % ( 79 item.param, type(self[item.param]) 80 ) 81 ) 82 else: 83 raise ConfSemanticError( 84 "Trying to append to undefined parameter %s" % item.param 85 ) 86 else: 87 key, value = item 88 self[key] = value 89 90 91 def __setitem__(self, k, v): 92 self.__class__.validate_key_value(k, v) 93 super(Conf, self).__setitem__(k, v) 94 95 def __init__(self, *args, **kwargs): 96 super(Conf, self).__init__() 97 self.update(*args, **kwargs) 98 99 def strgen(self, indentation = '\t', current_indent = 0): 100 for key, value in self.items(): 101 yield current_indent * indentation 102 yield key 103 if isinstance(value, JailBlock): 104 yield ' {\n' 105 yield from value.strgen(indentation, current_indent + 1) 106 yield '}\n' 107 elif value is True: 108 yield ';\n' 109 elif isinstance(value, list): 110 yield ' = %s;\n' % (', '.join(value)) 111 else: 112 yield ' = %s;\n' % value 113 114 def dumps(self, indentation = '\t', current_indent = 0): 115 return ''.join(self.strgen(indentation, current_indent)) 116 117 def write(self, path, indentation = '\t'): 118 handle, temp_path = mkstemp(prefix = path) 119 os.write(handle, self.dumps(indentation = indentation).encode('utf-8')) 120 os.close(handle) 121 os.rename(temp_path, path) 122 123class JailConf(Conf): 124 def jails(self): 125 for key, value in self.items(): 126 if isinstance(value, JailBlock): 127 yield (key, value) 128 129class JailBlock(Conf): 130 @classmethod 131 def validate_key_value(cls, k, v): 132 if isinstance(v, JailBlock): 133 raise ValueError("Jail block not allowed in this context.") 134 return super(JailBlock, cls).validate_key_value(k, v) 135