diff --git a/volatility/framework/interfaces/configuration.py b/volatility/framework/interfaces/configuration.py index 2496cb797..867cbaf9d 100644 --- a/volatility/framework/interfaces/configuration.py +++ b/volatility/framework/interfaces/configuration.py @@ -76,6 +76,104 @@ class ConfigurationSchemaNode(validity.ValidityRoutines, metaclass = ABCMeta): """ +class HierarchicalDict(collections.Mapping): + def __init__(self, separator, initial_dict = None): + if not (isinstance(separator, str) and len(separator) == 1): + raise TypeError("Separator must be a one character string") + self._separator = separator + self._data = {} + self._subdict = {} + if isinstance(initial_dict, dict): + for k, v in initial_dict.items(): + self[k] = v + + @property + def separator(self): + return self._separator + + def _key_head(self, key): + """Returns the first division of a key based on the dict separator, + or the full key if the separator is not present + """ + if self.separator in key: + return key[:key.index(self.separator)] + else: + return key + + def _key_tail(self, key): + """Returns all but the first division of a key based on the dict separator, + or None if the separator is not in the key + """ + if self.separator in key: + return key[key.index(self.separator) + 1:] + return None + + def __iter__(self): + """Returns an iterator object that supports the iterator protocol""" + return self + + def __next__(self): + """Yields the next element in the iterator""" + for key in self._data: + yield key + for subdict in self._subdict: + for key in subdict: + yield key + + def __getitem__(self, key): + """Gets an item, traversing down the trees to get to the final value""" + try: + if self.separator in key: + subdict = self._subdict[self._key_head(key)] + return subdict[self._key_tail(key)] + else: + return self._data[key] + except KeyError: + raise KeyError(key) + + def __setitem__(self, key, value): + """Sets an item or creates a subdict and sets the item within that""" + if self.separator in key: + subdict = self._subdict.get(self._key_head(key), HierarchicalDict(self.separator)) + subdict[self._key_tail(key)] = value + self._subdict[self._key_head(key)] = subdict + else: + self._data[key] = value + + def __delitem__(self, key): + """Deletes an item from the hierarchical dict""" + try: + if self.separator in key: + subdict = self._subdict[self._key_head(key)] + del subdict[self._key_tail(key)] + if not subdict: + del self._subdict[self._key_head(key)] + except KeyError: + raise KeyError(key) + + def __contains__(self, key): + """Determines whether the key is present in the hierarchy""" + if self.separator in key: + try: + subdict = self._subdict[self._key_head(key)] + return self._key_tail(key) in subdict + except KeyError: + return False + else: + return key in self._data + + def __len__(self): + """Returns the length of all items""" + return len(self._data) + sum([len(subdict) for subdict in self._subdict]) + + def branch(self, key): + """Returns the HierarchicalDict housed under the key""" + if self.separator in key: + return self._subdict[self._key_head(key)].branch(self._key_tail(key)) + else: + return self._subdict[key] + + class Configurable(object): """Class to allow objects to have requirements and populate the context config tree"""