1# -*- coding: utf-8 -*-
2"""Virtual Windows Registry key implementation."""
3
4from __future__ import unicode_literals
5
6import collections
7
8from dfwinreg import definitions
9from dfwinreg import interface
10from dfwinreg import key_paths
11
12
13class VirtualWinRegistryKey(interface.WinRegistryKey):
14  """Virtual Windows Registry key.
15
16  Virtual Windows Registry key are keys that do not exist on-disk but do exist
17  at run-time, sucha a example HKEY_LOCAL_MACHINE\\System. The virtual key is
18  used to "mount" the SYSTEM Windows Registry file onto the key
19  HKEY_LOCAL_MACHINE\\System.
20  """
21
22  def __init__(self, name, key_path='', registry=None):
23    """Initializes a Windows Registry key.
24
25    Args:
26      name (str): name of the Windows Registry key.
27      key_path (Optional[str]): Windows Registry key path.
28      registry (Optional[WinRegistry]): Windows Registry.
29    """
30    super(VirtualWinRegistryKey, self).__init__(key_path=key_path)
31    self._name = name
32    self._registry = registry
33    self._registry_key = None
34    self._subkeys = collections.OrderedDict()
35
36  @property
37  def class_name(self):
38    """str: class name of the key or None if not available."""
39    if not self._registry_key and self._registry:
40      self._GetKeyFromRegistry()
41
42    if not self._registry_key:
43      return None
44
45    return self._registry_key.class_name
46
47  @property
48  def last_written_time(self):
49    """dfdatetime.DateTimeValues: last written time or None."""
50    if not self._registry_key and self._registry:
51      self._GetKeyFromRegistry()
52
53    if not self._registry_key:
54      return None
55
56    return self._registry_key.last_written_time
57
58  @property
59  def name(self):
60    """str: name of the key."""
61    return self._name
62
63  @property
64  def number_of_subkeys(self):
65    """int: number of subkeys within the key."""
66    if not self._registry_key and self._registry:
67      self._GetKeyFromRegistry()
68
69    return len(self._subkeys)
70
71  @property
72  def number_of_values(self):
73    """int: number of values within the key."""
74    if not self._registry_key and self._registry:
75      self._GetKeyFromRegistry()
76
77    if self._registry_key:
78      return self._registry_key.number_of_values
79
80    return 0
81
82  @property
83  def offset(self):
84    """int: offset of the key within the Windows Registry file or None."""
85    if not self._registry_key and self._registry:
86      self._GetKeyFromRegistry()
87
88    if not self._registry_key:
89      return None
90
91    return self._registry_key.offset
92
93  def _GetKeyFromRegistry(self):
94    """Determines the key from the Windows Registry."""
95    if not self._registry:
96      return
97
98    try:
99      self._registry_key = self._registry.GetKeyByPath(self._key_path)
100    except RuntimeError:
101      pass
102
103    if not self._registry_key:
104      return
105
106    for sub_registry_key in self._registry_key.GetSubkeys():
107      self.AddSubkey(sub_registry_key.name, sub_registry_key)
108
109    self._registry = None
110
111  def _JoinKeyPath(self, path_segments):
112    """Joins the path segments into key path.
113
114    Args:
115      path_segments (list[str]): Windows Registry key path segments.
116
117    Returns:
118      str: key path.
119    """
120    # This is an optimized way to combine the path segments into a single path
121    # and combine multiple successive path separators to one.
122
123    # Split all the path segments based on the path (segment) separator.
124    path_segments = [
125        segment.split(definitions.KEY_PATH_SEPARATOR)
126        for segment in path_segments]
127
128    # Flatten the sublists into one list.
129    path_segments = [
130        element for sublist in path_segments for element in sublist]
131
132    # Remove empty path segments.
133    path_segments = filter(None, path_segments)
134
135    return definitions.KEY_PATH_SEPARATOR.join(path_segments)
136
137  def AddSubkey(self, name, registry_key):
138    """Adds a subkey.
139
140    Args:
141      name (str): name of the Windows Registry subkey.
142      registry_key (WinRegistryKey): Windows Registry subkey.
143
144    Raises:
145      KeyError: if the subkey already exists.
146    """
147    name_upper = name.upper()
148    if name_upper in self._subkeys:
149      raise KeyError('Subkey: {0:s} already exists.'.format(name))
150
151    self._subkeys[name_upper] = registry_key
152
153    key_path = self._JoinKeyPath([self._key_path, name])
154    registry_key._key_path = key_path  # pylint: disable=protected-access
155
156  def GetSubkeyByIndex(self, index):
157    """Retrieves a subkey by index.
158
159    Args:
160      index (int): index of the subkey.
161
162    Returns:
163      WinRegistryKey: Windows Registry subkey or None if not found.
164
165    Raises:
166      IndexError: if the index is out of bounds.
167    """
168    if not self._registry_key and self._registry:
169      self._GetKeyFromRegistry()
170
171    subkeys = list(self._subkeys.values())
172
173    if index < 0 or index >= len(subkeys):
174      raise IndexError('Index out of bounds.')
175
176    return subkeys[index]
177
178  def GetSubkeyByName(self, name):
179    """Retrieves a subkey by name.
180
181    Args:
182      name (str): name of the subkey.
183
184    Returns:
185      WinRegistryKey: Windows Registry subkey or None if not found.
186    """
187    if not self._registry_key and self._registry:
188      self._GetKeyFromRegistry()
189
190    return self._subkeys.get(name.upper(), None)
191
192  def GetSubkeyByPath(self, key_path):
193    """Retrieves a subkey by path.
194
195    Args:
196      key_path (str): path of the subkey.
197
198    Returns:
199      WinRegistryKey: Windows Registry subkey or None if not found.
200    """
201    if not self._registry_key and self._registry:
202      self._GetKeyFromRegistry()
203
204    subkey = self
205    for path_segment in key_paths.SplitKeyPath(key_path):
206      subkey = subkey.GetSubkeyByName(path_segment)
207      if not subkey:
208        break
209
210    return subkey
211
212  def GetSubkeys(self):
213    """Retrieves all subkeys within the key.
214
215    Returns:
216      generator[WinRegistryKey]: Windows Registry subkey generator.
217    """
218    if not self._registry_key and self._registry:
219      self._GetKeyFromRegistry()
220
221    return iter(self._subkeys.values())
222
223  def GetValueByName(self, name):
224    """Retrieves a value by name.
225
226    Args:
227      name (str): name of the value or an empty string for the default value.
228
229    Returns:
230      WinRegistryValue: Windows Registry value or None if not found.
231    """
232    if not self._registry_key and self._registry:
233      self._GetKeyFromRegistry()
234
235    if not self._registry_key:
236      return None
237
238    return self._registry_key.GetValueByName(name)
239
240  def GetValues(self):
241    """Retrieves all values within the key.
242
243    Returns:
244      generator[WinRegistryValue]: Windows Registry value generator.
245    """
246    if not self._registry_key and self._registry:
247      self._GetKeyFromRegistry()
248
249    if self._registry_key:
250      return self._registry_key.GetValues()
251
252    return iter([])
253