Example #1
0
    def _unquote_tag(tag):
        if tag[0] == '"' and tag[-1] == '"':
            tag = tag[1:-1]
        if tag[0] == "'" and tag[-1] == "'":
            tag = tag[1:-1]

        if not tag:
            raise error.ApacheConfigError('Empty block tag not allowed')

        return tag
Example #2
0
 def __init__(self, raw, parser):
     if len(raw) < 4:
         raise error.ApacheConfigError(
             "Expected properly-formatted data returned from "
             "``apacheconfig.parser``. Got a list that is too short.")
     self._whitespace = ""
     self._type = raw[0]
     if self._type != "block":
         raise error.ApacheConfigError(
             "Expected properly-formatted data returned from "
             "``apacheconfig.parser``. First element of data is not "
             "\"block\" typestring.")
     start = 1
     if isinstance(raw[start], six.string_types) and raw[start].isspace():
         self._whitespace = raw[start]
         start += 1
     self._full_tag = LeafNode(('statement', ) + raw[start])
     self._close_tag = raw[-1]
     self._contents = None
     if raw[start + 1]:  # If we have a list of elements to process.
         super(BlockNode, self).__init__(raw[start + 1], parser)
     self._type = raw[0]
Example #3
0
    def _walkast(self, ast):
        if not ast:
            return

        node_type = ast[0]

        try:
            handler = getattr(self, 'g_' + node_type)

        except AttributeError:
            raise error.ApacheConfigError('Unsupported AST node type %s' % node_type)

        return handler(ast[1:])
Example #4
0
    def _dumpdict(self, obj, indent=0, continue_tag=False):
        if not isinstance(obj, dict):
            raise error.ApacheConfigError('Unknown object type "%r" to dump'
                                          % obj)

        text = ''
        spacing = ' ' * indent

        for key, val in obj.items():
            if isinstance(val, six.text_type):
                if val.isalnum():
                    text += '%s%s %s\n' % (spacing, key, val)
                else:
                    text += '%s%s "%s"\n' % (spacing, key, val)

            elif isinstance(val, list):
                for dup in val:
                    if isinstance(dup, six.text_type):
                        if dup.isalnum():
                            text += '%s%s %s\n' % (spacing, key, dup)
                        else:
                            text += '%s%s "%s"\n' % (spacing, key, dup)
                    else:
                        if self._options.get('namedblocks', True):
                            text += ('%s<%s>\n%s%s</%s>\n'
                                     % (spacing, key,
                                        self._dumpdict(dup, indent + 2),
                                        spacing, key))
                        else:
                            text += ('%s<%s %s%s</%s>\n'
                                     % (spacing, key,
                                        self._dumpdict(dup, indent + 2,
                                                       continue_tag=True),
                                        spacing, key))

            else:
                if self._options.get('namedblocks', True):
                    text += ('%s<%s>\n%s%s</%s>\n'
                             % (spacing, key, self._dumpdict(val, indent + 2),
                                spacing, key))
                else:
                    if continue_tag:
                        text += ('%s>\n%s'
                                 % (key, self._dumpdict(val, indent + 2)))
                    else:
                        text += ('%s<%s %s%s</%s>\n'
                                 % (spacing, key,
                                    self._dumpdict(val, indent + 2,
                                                   continue_tag=True),
                                    spacing, key))
        return text
Example #5
0
            def lookup(match):
                option = match.groups()[0]

                for frame in self._stack:
                    if option in frame:
                        return interpolate(frame[option])

                if self._options.get('interpolateenv', False):
                    if option in self._reader.environ:
                        return interpolate(self._reader.environ[option])

                if self._options.get('strictvars', True):
                    raise error.ApacheConfigError(
                        'Undefined variable "${%s}" referenced' % option)

                return interpolate(match.string)
Example #6
0
    def dump(self, filepath, dct):
        tmpf = tempfile.NamedTemporaryFile(dir=os.path.dirname(filepath), delete=False)

        try:
            with open(tmpf.name, 'w') as f:
                f.write(self.dumps(dct))

            os.rename(tmpf.name, filepath)

        except IOError as ex:
            try:
                os.unlink(tmpf.name)

            except Exception:
                pass

            raise error.ApacheConfigError('File %s can\'t be written: %s' % (filepath, ex))
Example #7
0
    def add(self, index, raw_str):
        """Parses and adds child element at given index.

        Parses given string into an :class:`apacheconfig.AbstractASTNode`
        object, then adds to list at specified index.

        Args:
            index (int): index of list at which to insert the node.
            raw_str (str): string to parse. The parser will automatically
                determine whether it's a :class:`apacheconfig.BlockNode` or
                :class:`apacheconfig.LeafNode`.

        Returns:
            The :class:`apacheconfig.AbstractASTNode` created from parsing
                ``raw_str``.

        Raises:
            ApacheConfigError: If `raw_str` cannot be parsed into a
                :class:`apacheconfig.BlockNode` or
                :class:`apacheconfig.LeafNode`.
            IndexError: If `index` is not within bounds [0, len(self)].
        """
        if index < 0 or index > len(self):
            raise IndexError("supplied index is out of range")
        raw = self._parser.parse(raw_str)
        if len(raw) != 2:
            raise error.ApacheConfigError("Given raw_str should be "
                                          "parsable into a single node.")
        raw = self._parser.parse(raw_str)[1]
        if raw[0] == "block":
            node = BlockNode(raw, self._parser)
        else:
            node = LeafNode(raw)

        # If we're adding an element to the beginning of list, the first
        # item may not have a preceding newline-- so we add one in case.
        # For instance, something like:
        #   Contents("line1\nline2").add(0, "\nline0")
        # should end up as "\nline0\nline1\nline2"
        if (index == 0 and self._contents
                and '\n' not in self._contents[0].whitespace):
            whitespace_after = self._contents[0].whitespace
            self._contents[0].whitespace = '\n' + whitespace_after
        self._contents.insert(index, node)
        return node
Example #8
0
 def _merge_dicts(self, dict1, dict2, path=[]):
     "merges dict2 into dict1"
     for key in dict2:
         if key in dict1:
             if isinstance(dict1[key], dict) and isinstance(dict2[key], dict):
                 self._merge_dicts(dict1[key], dict2[key], path + [str(key)])
             elif dict1[key] != dict2[key]:
                 if self._options.get('allowmultioptions', True):
                     if not isinstance(dict1[key], list):
                         dict1[key] = [dict1[key]]
                     if not isinstance(dict2[key], list):
                         dict2[key] = [dict2[key]]
                     dict1[key] = self._merge_lists(dict1[key], dict2[key])
                 else:
                     if self._options.get('mergeduplicateoptions', False):
                         dict1[key] = dict2[key]
                     else:
                         raise error.ApacheConfigError('Duplicate option "%s" prohibited' % '.'.join(path + [str(key)]))
         else:
             dict1[key] = dict2[key]
     return dict1
Example #9
0
    def dump(self, filepath, dct):
        """Dumps the configuration in `dct` to a file.

        Args:
            filepath (Text): Filepath to write config to, in UTF-8 encoding.
            dct (dict): Configuration represented as a dictionary.
        """
        tmpf = tempfile.NamedTemporaryFile(dir=os.path.dirname(filepath),
                                           delete=False)

        try:
            with io.open(tmpf.name, mode='w', encoding='utf-8') as f:
                f.write(self.dumps(dct))

            os.rename(tmpf.name, filepath)

        except IOError as ex:
            if os.path.exists(tmpf.name):
                os.unlink(tmpf.name)

            raise error.ApacheConfigError('File %s can\'t be written: %s' %
                                          (filepath, ex))
Example #10
0
    def _merge_item(self, contents, key, value, path=[]):
        """Merges a single "key, value" item into contents dictionary, and
        returns new contents. Merging rules differ depending on flags set,
        and whether `value` is a dictionary (block).
        """
        if key not in contents:
            contents[key] = value
            return contents

        # In case of duplicate keys, AST can contain a list of values.
        # Here all values forced into being a list to unify further processing.
        if isinstance(value, list):
            vector = value
        else:
            vector = [value]

        if isinstance(value, dict):  # Merge block into contents
            if self._options.get('mergeduplicateblocks'):
                contents = self._merge_dicts(contents[key], value)
            else:
                if not isinstance(contents[key], list):
                    contents[key] = [contents[key]]
                contents[key] += vector
        else:  # Merge statement/option into contents
            if not self._options.get('allowmultioptions', True) \
                    and not self._options.get('mergeduplicateoptions', False):
                raise error.ApacheConfigError(
                    'Duplicate option "%s" prohibited'
                    % '.'.join(path + [six.text_type(key)]))
            if self._options.get('mergeduplicateoptions', False):
                contents[key] = value
            else:
                if not isinstance(contents[key], list):
                    contents[key] = [contents[key]]
                contents[key] += vector
        return contents
Example #11
0
    def g_statements(self, ast):
        statements = {}

        for subtree in ast:
            items = self._walkast(subtree)
            for item in items:
                if item in statements:
                    if (self._options.get('allowmultioptions', True)
                            and not self._options.get('mergeduplicateoptions',
                                                      False)):
                        if not isinstance(statements[item], list):
                            statements[item] = [statements[item]]
                        statements[item].append(items[item])
                    elif self._options.get('mergeduplicateoptions', False):
                        statements[item] = items[item]
                    else:
                        raise error.ApacheConfigError(
                            'Duplicate option "%s" prohibited' % item)
                else:
                    statements[item] = items[item]

        if (self._options.get('interpolateenv', False)
                or self._options.get('allowsinglequoteinterpolation', False)):
            self._options['interpolatevars'] = True

        if self._options.get('interpolatevars', False):

            def lookup(match):
                option = match.groups()[0]

                if option in statements:
                    return interpolate(statements[option])

                for frame in self._stack:
                    if option in frame:
                        return interpolate(frame[option])

                if self._options.get('interpolateenv', False):
                    if option in self._reader.environ:
                        return interpolate(self._reader.environ[option])

                if self._options.get('strictvars', True):
                    raise error.ApacheConfigError(
                        'Undefined variable "${%s}" referenced' % option)

                return interpolate(match.string)

            def interpolate(value):
                expanded = re.sub(r'(?<!\\)\${([^\n\r]+?)}', lookup, value)
                if expanded != value:
                    return expanded
                return re.sub(r'(?<!\\)\$([^\n\r $]+?)', lookup, value)

            for option, value in tuple(statements.items()):
                if (not getattr(value, 'is_single_quoted', False)
                        or self._options.get('allowsinglequoteinterpolation',
                                             False)):
                    if isinstance(value, list):
                        statements[option] = [interpolate(x) for x in value]
                    else:
                        statements[option] = interpolate(value)

        def remove_escapes(value):
            if self._options.get('noescape'):
                return value
            if not isinstance(value, str):
                return value
            return re.sub(r'\\([$\\"#])', lambda x: x.groups()[0], value)

        for option, value in tuple(statements.items()):
            if isinstance(value, list):
                statements[option] = [remove_escapes(x) for x in value]
            else:
                statements[option] = remove_escapes(value)

        self._stack.insert(0, statements)

        return statements