source: trunk/python/pyregfi/__init__.py @ 252

Last change on this file since 252 was 252, checked in by tim, 13 years ago

updated pyregfi to work with regfi changes
renamed some functions for more clarity
fixed some issues related to talloc_reference

File size: 33.3 KB
Line 
1#!/usr/bin/env python
2
3## @package pyregfi
4# Python interface to the regfi library.
5#
6
7## @mainpage API Documentation
8#
9# The pyregfi module provides a Python interface to the @ref regfi Windows
10# registry library. 
11#
12# The library operates on registry hives, each of which is contained within a
13# single file.  To get started, one must first open the registry hive file with
14# the open() or file() Python built-in functions (or equivalent) and then pass
15# the resulting file object to pyregfi. For example:
16# @code
17# >>> import pyregfi
18# >>> fh = open('/mnt/win/c/WINDOWS/system32/config/system', 'rb')
19# >>> myHive = pyregfi.Hive(fh)
20# @endcode
21#
22# Using this Hive object, one can begin investigating what top-level keys
23# exist by starting with the root Key attribute:
24# @code
25# >>> for key in myHive.root.subkeys:
26# ...   print(key.name)
27# ControlSet001
28# ControlSet003
29# LastKnownGoodRecovery
30# MountedDevices
31# Select
32# Setup
33# WPA
34# @endcode
35#
36# From there, accessing subkeys and values by name is a simple matter of:
37# @code
38# >>> myKey = myHive.root.subkeys['Select']
39# >>> myValue = myKey.values['Current']
40# @endcode
41#
42# The data associated with a Value can be obtained through the fetch_data()
43# method:
44# @code
45# >>> print(myValue.fetch_data())
46# 1
47# @endcode
48#
49# While useful for simple exercises, using the subkeys object for deeply nested
50# paths is not efficient and doesn't make for particularly attractive code. 
51# Instead, a special-purpose HiveIterator class is provided for simplicity of
52# use and fast access to specific known paths:
53# @code
54# >>> myIter = pyregfi.HiveIterator(myHive)
55# >>> myIter.descend(['ControlSet001','Control','NetworkProvider','HwOrder'])
56# >>> myKey = myIter.current_key()
57# >>> print(myKey.values['ProviderOrder'].fetch_data())
58# RDPNP,LanmanWorkstation,WebClient
59# @endcode
60#
61# The first two lines above can be simplified in some "syntactic sugar" provided
62# by the Hive.subtree() method.  Also, as one might expect, the HiveIterator
63# also acts as an iterator, producing keys in a depth-first order.
64# For instance, to traverse all keys under the ControlSet003\\Services key,
65# printing their names as we go, we could do:
66# @code
67# >>> for key in Hive.subtree(['ControlSet003','Services']):
68# >>>   print(key.name)
69# Services
70# Abiosdsk
71# abp480n5
72# Parameters
73# PnpInterface
74# ACPI
75# [...]
76# @endcode
77#
78# Note that "Services" was printed first, since the subtree is traversed as a
79# "preordering depth-first" search starting with the HiveIterator's current_key(). 
80# As one might expect, traversals of subtrees stops when all elements in a
81# specific subtree (and none outside of it) have been traversed.
82#
83# For more information, peruse the various attributes and methods available on
84# the Hive, HiveIterator, Key, Value, and Security classes.
85#
86# @note @ref regfi is a read-only library by design and there
87# are no plans to implement write support.
88#
89# @note At present, pyregfi has been tested with Python versions 2.6 and 3.1
90#
91# @note Developers strive to make pyregfi thread-safe.
92#
93# @note Key and Value names are case-sensitive in regfi and pyregfi
94#
95import sys
96import time
97import ctypes
98import ctypes.util
99import threading
100from pyregfi.structures import *
101
102
103## An enumeration of registry Value data types
104#
105# @note This is a static class, there is no need to instantiate it.
106#       Just access its attributes directly as DATA_TYPES.SZ, etc
107class DATA_TYPES(object):
108    ## None / Unknown
109    NONE                       =  0
110    ## String
111    SZ                         =  1
112    ## String with %...% expansions
113    EXPAND_SZ                  =  2
114    ## Binary buffer
115    BINARY                     =  3
116    ## 32 bit integer (little endian)
117    DWORD                      =  4 # DWORD, little endian
118    ## 32 bit integer (little endian)
119    DWORD_LE                   =  4
120    ## 32 bit integer (big endian)
121    DWORD_BE                   =  5 # DWORD, big endian
122    ## Symbolic link
123    LINK                       =  6
124    ## List of strings
125    MULTI_SZ                   =  7
126    ## Unknown structure
127    RESOURCE_LIST              =  8
128    ## Unknown structure
129    FULL_RESOURCE_DESCRIPTOR   =  9
130    ## Unknown structure
131    RESOURCE_REQUIREMENTS_LIST = 10
132    ## 64 bit integer
133    QWORD                      = 11 # 64-bit little endian
134
135
136## An enumeration of log message types
137#
138# @note This is a static class, there is no need to instantiate it.
139#       Just access its attributes directly as LOG_TYPES.INFO, etc
140class LOG_TYPES(object):
141    ## Informational messages, useful in debugging
142    INFO  =  0x01
143    ## Non-critical problems in structure parsing or intepretation
144    WARN  =  0x04
145    ## Major failures
146    ERROR =  0x10
147
148
149def _buffer2bytearray(char_pointer, length):
150    if length == 0 or char_pointer == None:
151        return None
152   
153    ret_val = bytearray(length)
154    for i in range(0,length):
155        ret_val[i] = char_pointer[i][0]
156
157    return ret_val
158
159
160def _strlist2charss(str_list):
161    ret_val = []
162    for s in str_list:
163        ret_val.append(s.encode('utf-8', 'replace'))
164
165    ret_val = (ctypes.c_char_p*(len(str_list)+1))(*ret_val)
166    # Terminate the char** with a NULL pointer
167    ret_val[-1] = 0
168
169    return ret_val
170
171
172def _charss2strlist(chars_pointer):
173    ret_val = []
174    i = 0
175    s = chars_pointer[i]
176    while s:
177        ret_val.append(s.decode('utf-8', 'replace'))
178        i += 1
179        s = chars_pointer[i]
180
181    return ret_val
182
183
184
185## Returns the (py)regfi library version
186#
187# @return A string indicating the version
188def getVersion():
189    return regfi.regfi_version()
190
191
192## Retrieves messages produced by regfi during parsing and interpretation
193#
194# The regfi C library may generate log messages stored in a special thread-safe
195# global data structure.  These messages should be retrieved periodically or
196# after each major operation by callers to determine if any errors or warnings
197# should be reported to the user.  Failure to retrieve these could result in
198# excessive memory consumption.
199def getLogMessages():
200    msgs = regfi.regfi_log_get_str()
201    if not msgs:
202        return ''
203    return msgs.decode('utf-8')
204
205
206## Sets the types of log messages to record
207#
208# @param log_types A sequence of message types that regfi should generate.
209#                  Message types can be found in the LOG_TYPES enumeration.
210#
211# @return True on success, False on failure.  Failures are rare, but could
212#         indicate that global logging is not operating as expected.
213#
214# Example:
215# @code
216# setLogMask((LOG_TYPES.ERROR, LOG_TYPES.WARN, LOG_TYPES.INFO))
217# @endcode
218#
219# The message mask is a global (all hives, iterators), thread-specific value.
220# For more information, see @ref regfi_log_set_mask.
221#
222def setLogMask(log_types):
223    mask = 0
224    for m in log_types:
225        mask |= m
226    return regfi.regfi_log_set_mask(mask)
227
228
229## Opens a file as a registry hive
230#
231# @param path The file path of a hive, as one would provide to the
232#             open() built-in
233#
234# @return A new Hive instance
235def openHive(path):
236    fh = open(path, 'rb')
237    return Hive(fh)
238
239
240## Abstract class for most objects returned by the library
241class _StructureWrapper(object):
242    _hive = None
243    _base = None
244
245    def __init__(self, hive, base):
246        if not hive:
247            raise Exception("Could not create _StructureWrapper,"
248                            + " hive is NULL.  Current log:\n"
249                            + getLogMessages())
250        if not base:
251            raise Exception("Could not create _StructureWrapper,"
252                            + " base is NULL.  Current log:\n"
253                            + getLogMessages())
254        self._hive = hive
255        self._base = base
256
257
258    # Memory management for most regfi structures is taken care of here
259    def __del__(self):
260        regfi.regfi_free_record(self._hive.file, self._base)
261
262
263    # Any attribute requests not explicitly defined in subclasses gets passed
264    # to the equivalent REGFI_* structure defined in structures.py
265    def __getattr__(self, name):
266        return getattr(self._base.contents, name)
267
268   
269    ## Test for equality
270    #
271    # Records returned by pyregfi may be compared with one another.  For example:
272    # @code
273    #  >>> key2 = key1.subkeys['child']
274    #  >>> key1 == key2
275    #  False
276    #  >>> key1 != key2
277    #  True
278    #  >>> key1 == key2.get_parent()
279    #  True
280    # @endcode
281    def __eq__(self, other):
282        return (type(self) == type(other)) and (self.offset == other.offset)
283
284
285    def __ne__(self, other):
286        return (not self.__eq__(other))
287
288
289class Key():
290    pass
291
292
293class Value():
294    pass
295
296
297## Registry security record and descriptor
298# XXX: Access to security descriptors not yet implemented
299class Security(_StructureWrapper):
300    pass
301
302## Abstract class for ValueList and SubkeyList
303class _GenericList(object):
304    _hive = None
305    _key_base = None
306    _length = None
307    _current = None
308
309    # implementation-specific functions for SubkeyList and ValueList
310    _fetch_num = None
311    _find_element = None
312    _get_element = None
313    _constructor = None
314
315    def __init__(self, key):
316        if not key:
317            raise Exception("Could not create _GenericList; key is NULL."
318                            + "Current log:\n" + getLogMessages())
319
320        base = regfi.regfi_reference_record(key._hive.file, key._base)
321        if not base:
322            raise Exception("Could not create _GenericList; memory error."
323                            + "Current log:\n" + getLogMessages())
324        self._key_base = cast(base, type(key._base))
325        self._length = self._fetch_num(self._key_base)
326        self._hive = key._hive
327
328   
329    def __del__(self):
330        regfi.regfi_free_record(self._hive.file, self._key_base)
331
332
333    ## Length of list
334    def __len__(self):
335        return self._length
336
337
338    ## Retrieves a list element by name
339    #
340    # @return the first element whose name matches, or None if the element
341    #         could not be found
342    def __getitem__(self, name):
343        index = ctypes.c_uint32()
344        if isinstance(name, str):
345            name = name.encode('utf-8')
346
347        if name != None:
348            name = create_string_buffer(bytes(name))
349
350        if self._find_element(self._hive.file, self._key_base, 
351                              name, byref(index)):
352            return self._constructor(self._hive,
353                                     self._get_element(self._hive.file,
354                                                       self._key_base,
355                                                       index))
356        raise KeyError('')
357
358    def get(self, name, default):
359        try:
360            return self[name]
361        except KeyError:
362            return default
363   
364    def __iter__(self):
365        self._current = 0
366        return self
367   
368    def __next__(self):
369        if self._current >= self._length:
370            raise StopIteration('')
371
372        elem = self._get_element(self._hive.file, self._key_base,
373                                 ctypes.c_uint32(self._current))
374        self._current += 1
375        return self._constructor(self._hive, elem)
376   
377    # For Python 2.x
378    next = __next__
379
380
381## The list of subkeys associated with a Key
382#
383# This attribute is both iterable:
384# @code
385#   for k in myKey.subkeys:
386#     ...
387# @endcode
388# and accessible as a dictionary:
389# @code
390#   mySubkey = myKey.subkeys["keyName"]
391# @endcode
392#
393# @note SubkeyLists should never be accessed directly and only exist
394#       in association with a parent Key object.  Do not retain references to
395#       SubkeyLists.  Instead, access them via their parent Key at all times.
396class SubkeyList(_GenericList):
397    _fetch_num = regfi.regfi_fetch_num_subkeys
398    _find_element = regfi.regfi_find_subkey
399    _get_element = regfi.regfi_get_subkey
400
401
402## The list of values associated with a Key
403#
404# This attribute is both iterable:
405# @code
406#   for v in myKey.values:
407#     ...
408# @endcode
409# and accessible as a dictionary:
410# @code
411#   myValue = myKey.values["valueName"]
412# @endcode
413#
414# @note ValueLists should never be accessed directly and only exist
415#       in association with a parent Key object.  Do not retain references to
416#       ValueLists.  Instead, access them via their parent Key at all times.
417class ValueList(_GenericList):
418    _fetch_num = regfi.regfi_fetch_num_values
419    _find_element = regfi.regfi_find_value
420    _get_element = regfi.regfi_get_value
421
422
423## Registry key
424# These represent registry keys (@ref REGFI_NK records) and provide
425# access to their subkeys, values, and other metadata.
426#
427# @note Value instances may provide access to more than the attributes
428#       documented here.  However, undocumented attributes may change over time
429#       and are not officially supported.  If you need access to an attribute
430#       not shown here, see pyregfi.structures.
431class Key(_StructureWrapper):
432    ## A @ref ValueList object representing the list of Values
433    #  stored on this Key
434    values = None
435
436    ## A @ref SubkeyList object representing the list of subkeys
437    #  stored on this Key
438    subkeys = None
439
440    ## The raw Key name as an uninterpreted bytearray
441    name_raw = (b"...")
442   
443    ## The name of the Key as a (unicode) string
444    name = "..."
445   
446    ## The absolute file offset of the Key record's cell in the Hive file
447    offset = 0xCAFEBABE
448
449    ## This Key's last modified time represented as the number of seconds
450    #  since the UNIX epoch in UTC; similar to what time.time() returns
451    modified = 1300000000.123456
452
453    ## The NK record's flags field
454    flags = 0x10110001
455
456    def __init__(self, hive, base):
457        super(Key, self).__init__(hive, base)
458        self.values = ValueList(self)
459        self.subkeys = SubkeyList(self)
460
461    def __getattr__(self, name):
462        if name == "name":
463            ret_val = super(Key, self).__getattr__(name)
464
465            if not ret_val:
466                ret_val = self.name_raw
467            else:
468                ret_val = ret_val.decode('utf-8', 'replace')
469               
470        elif name == "name_raw":
471            ret_val = super(Key, self).__getattr__(name)
472            length = super(Key, self).__getattr__('name_length')
473            ret_val = _buffer2bytearray(ret_val, length)
474       
475        elif name == "modified":
476            ret_val = regfi.regfi_nt2unix_time(byref(self._base.contents.mtime))
477
478        else:
479            ret_val = super(Key, self).__getattr__(name)
480
481        return ret_val
482
483
484    ## Retrieves the Security properties for this key
485    def fetch_security(self):
486        return Security(self._hive,
487                        regfi.regfi_fetch_sk(self._hive.file, self._base))
488
489
490    ## Retrieves the class name for this key
491    #
492    # Class names are typically stored as UTF-16LE strings, so these are decoded
493    # into proper python (unicode) strings.  However, if this fails, a bytearray
494    # is instead returned containing the raw buffer stored for the class name.
495    #
496    # @return The class name as a string or bytearray.  None if a class name
497    #         doesn't exist or an unrecoverable error occurred during retrieval.
498    def fetch_classname(self):
499        ret_val = None
500        cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base)
501        if cn_p:
502            cn_struct = cn_p.contents
503            if cn_struct.interpreted:
504                ret_val = cn_struct.interpreted.decode('utf-8', 'replace')
505            else:
506                ret_val = _buffer2bytearray(cn_struct.raw,
507                                            cn_struct.size)
508            regfi.regfi_free_record(self._hive.file, cn_p)
509
510        return ret_val
511
512
513    ## Retrieves this key's parent key
514    #
515    # @return The parent's Key instance or None if current key is root
516    #         (or an error occured)
517    def get_parent(self):
518        if self.is_root():
519            return None
520        parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base)
521        if parent_base:
522            return Key(self._hive, parent_base)
523        return None
524
525    def is_root(self):
526        return (self._hive.root == self)
527
528
529## Registry value (metadata)
530#
531# These represent registry values (@ref REGFI_VK records) and provide
532# access to their associated data.
533#
534# @note Value instances may provide access to more than the attributes
535#       documented here.  However, undocumented attributes may change over time
536#       and are not officially supported.  If you need access to an attribute
537#       not shown here, see pyregfi.structures.
538class Value(_StructureWrapper):
539    ## The raw Value name as an uninterpreted bytearray
540    name_raw = (b"...")
541   
542    ## The name of the Value as a (unicode) string
543    name = "..."
544   
545    ## The absolute file offset of the Value record's cell in the Hive file
546    offset = 0xCAFEBABE
547
548    ## The length of data advertised in the VK record
549    data_size = 0xCAFEBABE
550
551    ## An integer which represents the data type for this Value's data
552    # Typically this value is one of 12 types defined in @ref DATA_TYPES,
553    # but in some cases (the SAM hive) it may be used for other purposes
554    type = DATA_TYPES.NONE
555
556    ## The VK record's flags field
557    flags = 0x10110001
558
559    ## Retrieves the Value's data according to advertised type
560    #
561    # Data is loaded from its cell(s) and then interpreted based on the data
562    # type recorded in the Value.  It is not uncommon for data to be stored with
563    # the wrong type or even with invalid types.  If you have difficulty
564    # obtaining desired data here, use @ref fetch_raw_data().
565    #
566    # @return The interpreted representation of the data as one of several
567    #         possible Python types, as listed below.  None if any failure
568    #         occurred during extraction or conversion.
569    #
570    # @retval string for SZ, EXPAND_SZ, and LINK
571    # @retval int for DWORD, DWORD_BE, and QWORD
572    # @retval list(string) for MULTI_SZ
573    # @retval bytearray for NONE, BINARY, RESOURCE_LIST,
574    #         FULL_RESOURCE_DESCRIPTOR, and RESOURCE_REQUIREMENTS_LIST
575    #
576    def fetch_data(self):
577        ret_val = None
578        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
579        if not data_p:
580            return None
581        data_struct = data_p.contents
582
583        if data_struct.interpreted_size == 0:
584            ret_val = None
585        elif data_struct.type in (DATA_TYPES.SZ, DATA_TYPES.EXPAND_SZ, DATA_TYPES.LINK):
586            # Unicode strings
587            ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
588        elif data_struct.type in (DATA_TYPES.DWORD, DATA_TYPES.DWORD_BE):
589            # 32 bit integers
590            ret_val = data_struct.interpreted.dword
591        elif data_struct.type == DATA_TYPES.QWORD:
592            # 64 bit integers
593            ret_val = data_struct.interpreted.qword
594        elif data_struct.type == DATA_TYPES.MULTI_SZ:
595            ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
596        elif data_struct.type in (DATA_TYPES.NONE, DATA_TYPES.RESOURCE_LIST,
597                                  DATA_TYPES.FULL_RESOURCE_DESCRIPTOR,
598                                  DATA_TYPES.RESOURCE_REQUIREMENTS_LIST,
599                                  DATA_TYPES.BINARY):
600            ret_val = _buffer2bytearray(data_struct.interpreted.none,
601                                        data_struct.interpreted_size)
602
603        regfi.regfi_free_record(self._hive.file, data_p)
604        return ret_val
605   
606
607    ## Retrieves raw representation of Value's data
608    #
609    # @return A bytearray containing the data
610    #
611    def fetch_raw_data(self):
612        ret_val = None
613        # XXX: should we load the data without interpretation instead?
614        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
615        if not data_p:
616            return None
617
618        data_struct = data_p.contents
619        ret_val = _buffer2bytearray(data_struct.raw,
620                                    data_struct.size)
621        regfi.regfi_free_record(self._hive.file, data_p)
622        return ret_val
623
624
625    def __getattr__(self, name):
626        ret_val = super(Value, self).__getattr__(name)
627        if name == "name":
628            if not ret_val:
629                ret_val = self.name_raw
630            else:
631                ret_val = ret_val.decode('utf-8', 'replace')
632
633        elif name == "name_raw":
634            length = super(Value, self).__getattr__('name_length')
635            ret_val = _buffer2bytearray(ret_val, length)
636
637        return ret_val
638
639
640# Avoids chicken/egg class definitions.
641# Also makes for convenient code reuse in these lists' parent classes.
642SubkeyList._constructor = Key
643ValueList._constructor = Value
644
645
646
647## Represents a single registry hive (file)
648class Hive():
649    file = None
650    raw_file = None
651    _root = None
652
653    ## The root Key of this Hive
654    root = None
655
656    ## This Hives's last modified time represented as the number of seconds
657    #  since the UNIX epoch in UTC; similar to what time.time() returns
658    modified = 1300000000.123456
659
660    ## First sequence number
661    sequence1 = 12345678
662
663    ## Second sequence number
664    sequence2 = 12345678
665
666    ## Major version
667    major_version = 1
668
669    ## Minor version
670    minor_version = 5
671
672    ## Constructor
673    #
674    # Initialize a new Hive based on a Python file object.  To open a file by
675    # path, see @ref openHive.
676    #
677    # @param fh A Python file object.  The constructor first looks for a valid
678    #           fileno attribute on this object and uses it if possible. 
679    #           Otherwise, the seek and read methods are used for file
680    #           access.
681    #
682    # @note Supplied file must be seekable.  Do not perform any operation on
683    #       the provided file object while a Hive is using it.  Do not
684    #       construct multiple Hive instances from the same file object.
685    #       If a file must be accessed by separate code and pyregfi
686    #       simultaneously, use a separate file descriptor.  Hives are
687    #       thread-safe, so multiple threads may use a single Hive object.
688    def __init__(self, fh):
689        # The fileno method may not exist, or it may throw an exception
690        # when called if the file isn't backed with a descriptor.
691        fn = None
692        try:
693            # XXX: Native calls to Windows filenos don't seem to work. 
694            #      Need to investigate why.
695            if not is_win32 and hasattr(fh, 'fileno'):
696                fn = fh.fileno()
697        except:
698            pass
699
700        if fn != None:
701            self.file = regfi.regfi_alloc(fn, REGFI_ENCODING_UTF8)
702            if not self.file:
703                # XXX: switch to non-generic exception
704                raise Exception("Could not open registry file.  Current log:\n"
705                                + getLogMessages())
706        else:
707            fh.seek(0)
708            self.raw_file = structures.REGFI_RAW_FILE()
709            self.raw_file.fh = fh
710            self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
711            self.raw_file.read = read_cb_type(self.raw_file.cb_read)
712            self.file = regfi.regfi_alloc_cb(pointer(self.raw_file), REGFI_ENCODING_UTF8)
713            if not self.file:
714                # XXX: switch to non-generic exception
715                raise Exception("Could not open registry file.  Current log:\n"
716                                + getLogMessages())
717
718
719    def __getattr__(self, name):
720        if name == "root":
721            # XXX: This creates reference loops.  Need to cache better inside regfi
722            #if self._root == None:
723            #    self._root = Key(self, regfi.regfi_get_rootkey(self.file))
724            #return self._root
725            return Key(self, regfi.regfi_get_rootkey(self.file))
726
727        elif name == "modified":
728            return regfi.regfi_nt2unix_time(byref(self._base.contents.mtime))
729
730        return getattr(self.file.contents, name)
731
732   
733    def __del__(self):
734        regfi.regfi_free(self.file)
735        if self.raw_file != None:
736            self.raw_file = None
737
738
739    def __iter__(self):
740        return HiveIterator(self)
741
742
743    ## Creates a @ref HiveIterator initialized at the specified path in
744    #  the hive.
745    #
746    # @param path A list of Key names which represent an absolute path within
747    #             the Hive
748    #
749    # @return A @ref HiveIterator which is positioned at the specified path.
750    #
751    # @exception Exception If the path could not be found/traversed
752    def subtree(self, path):
753        hi = HiveIterator(self)
754        hi.descend(path)
755        return hi
756
757
758## A special purpose iterator for registry hives
759#
760# Iterating over an object of this type causes all keys in a specific
761# hive subtree to be returned in a depth-first manner. These iterators
762# are typically created using the @ref Hive.subtree() function on a @ref Hive
763# object.
764#
765# HiveIterators can also be used to manually traverse up and down a
766# registry hive as they retain information about the current position in
767# the hive, along with which iteration state for subkeys and values for
768# every parent key.  See the @ref up and @ref down methods for more
769# information.
770class HiveIterator():
771    _hive = None
772    _iter = None
773    _iteration_root = None
774    _lock = None
775
776    def __init__(self, hive):
777        self._iter = regfi.regfi_iterator_new(hive.file)
778        if not self._iter:
779            raise Exception("Could not create iterator.  Current log:\n"
780                            + getLogMessages())
781        self._hive = hive
782        self._lock = threading.RLock()
783   
784    def __getattr__(self, name):
785        self._lock.acquire()
786        ret_val = getattr(self._iter.contents, name)
787        self._lock.release()
788        return ret_val
789
790    def __del__(self):
791        self._lock.acquire()
792        regfi.regfi_iterator_free(self._iter)
793        self._lock.release()
794
795    def __iter__(self):
796        self._lock.acquire()
797        self._iteration_root = None
798        self._lock.release()
799        return self
800
801    def __next__(self):
802        self._lock.acquire()
803        if self._iteration_root == None:
804            self._iteration_root = self.current_key().offset
805        elif not regfi.regfi_iterator_down(self._iter):
806            up_ret = regfi.regfi_iterator_up(self._iter)
807            while (up_ret and
808                   not regfi.regfi_iterator_next_subkey(self._iter)):
809                if self._iteration_root == self.current_key().offset:
810                    self._iteration_root = None
811                    self._lock.release()
812                    raise StopIteration('')
813                up_ret = regfi.regfi_iterator_up(self._iter)
814
815            if not up_ret:
816                self._iteration_root = None
817                self._lock.release()
818                raise StopIteration('')
819           
820            # XXX: Use non-generic exception
821            if not regfi.regfi_iterator_down(self._iter):
822                self._lock.release()
823                raise Exception('Error traversing iterator downward.'+
824                                ' Current log:\n'+ getLogMessages())
825
826        regfi.regfi_iterator_first_subkey(self._iter)
827        ret_val = self.current_key()
828        self._lock.release()
829
830        return ret_val
831
832
833    # For Python 2.x
834    next = __next__
835
836    # XXX: Should add sanity checks on some of these traversal functions
837    #      to throw exceptions if a traversal/retrieval *should* have worked
838    #      but failed for some reason.
839
840    ## Descends the iterator to a subkey
841    #
842    # Descends the iterator one level to the current subkey, or a subkey
843    # specified by name.
844    #
845    # @param subkey_name If specified, locates specified subkey by name
846    #                    (via find_subkey()) and descends to it.
847    #
848    # @return True if successful, False otherwise
849    def down(self, subkey_name=None):
850        ret_val = None
851        if subkey_name == None:
852            self._lock.acquire()
853            ret_val = regfi.regfi_iterator_down(self._iter)
854        else:
855            if name != None:
856                name = name.encode('utf-8')
857            self._lock.acquire()
858            ret_val = (regfi.regfi_iterator_find_subkey(self._iter, name) 
859                       and regfi.regfi_iterator_down(self._iter))
860       
861        self._lock.release()
862        return ret_val
863
864
865    ## Causes the iterator to ascend to the current Key's parent
866    #
867    # @return True if successful, False otherwise
868    #
869    # @note The state of current subkeys and values at this level in the tree
870    #       is lost as a side effect.  That is, if you go up() and then back
871    #       down() again, current_subkey() and current_value() will return
872    #       default selections.
873    def up(self):
874        self._lock.acquire()
875        ret_val = regfi.regfi_iterator_up(self._iter)
876        self._lock.release()
877        return ret_val
878
879
880    ## Selects first subkey of current key
881    #
882    # @return A Key instance for the first subkey. 
883    #         None on error or if the current key has no subkeys.
884    def first_subkey(self):
885        ret_val = None
886        self._lock.acquire()
887        if regfi.regfi_iterator_first_subkey(self._iter):
888            ret_val = self.current_subkey()
889        self._lock.release()
890        return ret_val
891
892
893    ## Selects first value of current Key
894    #
895    # @return A Value instance for the first value. 
896    #         None on error or if the current key has no values.
897    def first_value(self):
898        ret_val = None
899        self._lock.acquire()
900        if regfi.regfi_iterator_first_value(self._iter):
901            ret_val = self.current_value()
902        self._lock.release()
903        return ret_val
904
905
906    ## Selects the next subkey in the current Key's list
907    #
908    # @return A Key instance for the next subkey.
909    #         None if there are no remaining subkeys or an error occurred.
910    def next_subkey(self):
911        ret_val = None
912        self._lock.acquire()
913        if regfi.regfi_iterator_next_subkey(self._iter):
914            ret_val = self.current_subkey()
915        self._lock.release()
916        return ret_val
917
918
919    ## Selects the next value in the current Key's list
920   
921    # @return A Value instance for the next value.
922    #         None if there are no remaining values or an error occurred.
923    def next_value(self):
924        ret_val = None
925        self._lock.acquire()
926        if regfi.regfi_iterator_next_value(self._iter):
927            ret_val = self.current_value()
928        self._lock.release()
929        return ret_val
930
931
932    ## Selects the first subkey which has the specified name
933    #
934    # @return A Key instance for the selected key.
935    #         None if it could not be located or an error occurred.
936    def find_subkey(self, name):
937        if name != None:
938            name = name.encode('utf-8')
939        ret_val = None
940        self._lock.acquire()
941        if regfi.regfi_iterator_find_subkey(self._iter, name):
942            ret_val = self.current_subkey()
943        self._lock.release()
944        return ret_val
945
946
947    ## Selects the first value which has the specified name
948    #
949    # @return A Value instance for the selected value.
950    #         None if it could not be located or an error occurred.
951    def find_value(self, name):
952        if name != None:
953            name = name.encode('utf-8')
954        ret_val = None
955        self._lock.acquire()
956        if regfi.regfi_iterator_find_value(self._iter, name):
957            ret_val = self.current_value()
958        self._lock.release()
959        return ret_val
960
961    ## Retrieves the currently selected subkey
962    #
963    # @return A Key instance of the current subkey
964    def current_subkey(self):
965        self._lock.acquire()
966        ret_val = Key(self._hive, regfi.regfi_iterator_cur_subkey(self._iter))
967        self._lock.release()
968        return ret_val
969
970    ## Retrieves the currently selected value
971    #
972    # @return A Value instance of the current value
973    def current_value(self):
974        self._lock.acquire()
975        ret_val = Value(self._hive, regfi.regfi_iterator_cur_value(self._iter))
976        self._lock.release()
977        return ret_val
978
979    ## Retrieves the current key
980    #
981    # @return A Key instance of the current position of the iterator
982    def current_key(self):
983        self._lock.acquire()
984        ret_val = Key(self._hive, regfi.regfi_iterator_cur_key(self._iter))
985        self._lock.release()
986        return ret_val
987
988    ## Traverse downward multiple levels
989    #
990    # This is more efficient than calling down() multiple times
991    #
992    # @param path A list of Key names which represent the path to descend
993    #
994    # @exception Exception If path could not be located
995    def descend(self, path):
996        cpath = _strlist2charss(path)
997
998        self._lock.acquire()
999        result = regfi.regfi_iterator_descend(self._iter, cpath)
1000        self._lock.release()
1001        if not result:
1002            # XXX: Use non-generic exception
1003            raise Exception('Could not locate path.\n'+getLogMessages())
1004
1005    ## Obtains a list of the current key's ancestry
1006    #
1007    # @return A list of all parent keys starting with the root Key and ending
1008    #         with the current Key
1009    def ancestry(self):
1010        self._lock.acquire()
1011        result = regfi.regfi_iterator_ancestry(self._iter)
1012        self._lock.release()
1013
1014        ret_val = []
1015        i = 0
1016        k = result[i]
1017        while k:
1018            k = cast(regfi.regfi_reference_record(self._hive.file, k), POINTER(REGFI_NK))
1019            ret_val.append(Key(self._hive, k))
1020            i += 1
1021            k = result[i]
1022
1023        regfi.regfi_free_record(self._hive.file, result)
1024        return ret_val
1025
1026    ## Obtains the current path of the iterator
1027    #
1028    # @return A list of key names starting with the root up to and
1029    #         including the current key
1030    #
1031    def current_path(self):
1032        ancestry = self.ancestry()
1033        return [str(a.name) for a in ancestry]
1034
1035
1036# Freeing symbols defined for the sake of documentation
1037del Value.name,Value.name_raw,Value.offset,Value.data_size,Value.type,Value.flags
1038del Key.name,Key.name_raw,Key.offset,Key.modified,Key.flags
1039del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version
Note: See TracBrowser for help on using the repository browser.