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

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

fixed error condition deadlock in regfi
prevented early garbage collection of hive file handles
fixed module imports under python3

File size: 34.7 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        if self._base:
261            regfi.regfi_free_record(self._hive.file, self._base)
262
263
264    # Any attribute requests not explicitly defined in subclasses gets passed
265    # to the equivalent REGFI_* structure defined in structures.py
266    def __getattr__(self, name):
267        return getattr(self._base.contents, name)
268
269   
270    ## Test for equality
271    #
272    # Records returned by pyregfi may be compared with one another.  For example:
273    # @code
274    #  >>> key2 = key1.subkeys['child']
275    #  >>> key1 == key2
276    #  False
277    #  >>> key1 != key2
278    #  True
279    #  >>> key1 == key2.get_parent()
280    #  True
281    # @endcode
282    def __eq__(self, other):
283        return (type(self) == type(other)) and (self.offset == other.offset)
284
285
286    def __ne__(self, other):
287        return (not self.__eq__(other))
288
289
290class Key():
291    pass
292
293
294class Value():
295    pass
296
297
298
299## Represents a registry SK record which contains a security descriptor
300#
301class Security(_StructureWrapper):
302    ## Number of keys referencing this SK record
303    ref_count = 1
304
305    ## The absolute file offset of the SK record's cell in the Hive file
306    offset = 0xCAFEBABE
307
308    ## The @ref SecurityDescriptor for this SK record
309    descriptor = object()
310
311    def __init__(self, hive, base):
312        super(Security, self).__init__(hive, base)
313        # XXX: add checks for NULL pointers
314        self.descriptor = winsec.SecurityDescriptor(base.contents.sec_desc.contents)
315
316    ## Loads the "previous" Security record in the hive
317    #
318    # @note
319    # SK records are included in a circular, doubly-linked list.
320    # To iterate over all SK records, be sure to check for the repetition of
321    # the SK record you started with to determine when all have been traversed.
322    def next_security(self):
323        return Security(self._hive,
324                        regfi.regfi_next_sk(self._hive.file, self._base))
325
326    ## Loads the "previous" Security record in the hive
327    #
328    # @note
329    # SK records are included in a circular, doubly-linked list.
330    # To iterate over all SK records, be sure to check for the repetition of
331    # the SK record you started with to determine when all have been traversed.
332    def prev_security(self):
333        return Security(self._hive,
334                        regfi.regfi_prev_sk(self._hive.file, self._base))
335
336
337## Abstract class for ValueList and SubkeyList
338class _GenericList(object):
339    _hive = None
340    _key_base = None
341    _length = None
342    _current = None
343
344    # implementation-specific functions for SubkeyList and ValueList
345    _fetch_num = None
346    _find_element = None
347    _get_element = None
348    _constructor = None
349
350    def __init__(self, key):
351        if not key:
352            raise Exception("Could not create _GenericList; key is NULL."
353                            + "Current log:\n" + getLogMessages())
354
355        base = regfi.regfi_reference_record(key._hive.file, key._base)
356        if not base:
357            raise Exception("Could not create _GenericList; memory error."
358                            + "Current log:\n" + getLogMessages())
359        self._key_base = cast(base, type(key._base))
360        self._length = self._fetch_num(self._key_base)
361        self._hive = key._hive
362
363   
364    def __del__(self):
365        regfi.regfi_free_record(self._hive.file, self._key_base)
366
367
368    ## Length of list
369    def __len__(self):
370        return self._length
371
372
373    ## Retrieves a list element by name
374    #
375    # @return the first element whose name matches, or None if the element
376    #         could not be found
377    def __getitem__(self, name):
378        index = ctypes.c_uint32()
379        if isinstance(name, str):
380            name = name.encode('utf-8')
381
382        if name != None:
383            name = create_string_buffer(bytes(name))
384
385        if self._find_element(self._hive.file, self._key_base, 
386                              name, byref(index)):
387            return self._constructor(self._hive,
388                                     self._get_element(self._hive.file,
389                                                       self._key_base,
390                                                       index))
391        raise KeyError('')
392
393    def get(self, name, default):
394        try:
395            return self[name]
396        except KeyError:
397            return default
398   
399    def __iter__(self):
400        self._current = 0
401        return self
402   
403    def __next__(self):
404        if self._current >= self._length:
405            raise StopIteration('')
406
407        elem = self._get_element(self._hive.file, self._key_base,
408                                 ctypes.c_uint32(self._current))
409        self._current += 1
410        return self._constructor(self._hive, elem)
411   
412    # For Python 2.x
413    next = __next__
414
415
416## The list of subkeys associated with a Key
417#
418# This attribute is both iterable:
419# @code
420#   for k in myKey.subkeys:
421#     ...
422# @endcode
423# and accessible as a dictionary:
424# @code
425#   mySubkey = myKey.subkeys["keyName"]
426# @endcode
427#
428# @note SubkeyLists should never be accessed directly and only exist
429#       in association with a parent Key object.  Do not retain references to
430#       SubkeyLists.  Instead, access them via their parent Key at all times.
431class SubkeyList(_GenericList):
432    _fetch_num = regfi.regfi_fetch_num_subkeys
433    _find_element = regfi.regfi_find_subkey
434    _get_element = regfi.regfi_get_subkey
435
436
437## The list of values associated with a Key
438#
439# This attribute is both iterable:
440# @code
441#   for v in myKey.values:
442#     ...
443# @endcode
444# and accessible as a dictionary:
445# @code
446#   myValue = myKey.values["valueName"]
447# @endcode
448#
449# @note ValueLists should never be accessed directly and only exist
450#       in association with a parent Key object.  Do not retain references to
451#       ValueLists.  Instead, access them via their parent Key at all times.
452class ValueList(_GenericList):
453    _fetch_num = regfi.regfi_fetch_num_values
454    _find_element = regfi.regfi_find_value
455    _get_element = regfi.regfi_get_value
456
457
458## Registry key
459# These represent registry keys (@ref REGFI_NK records) and provide
460# access to their subkeys, values, and other metadata.
461#
462# @note Value instances may provide access to more than the attributes
463#       documented here.  However, undocumented attributes may change over time
464#       and are not officially supported.  If you need access to an attribute
465#       not shown here, see pyregfi.structures.
466class Key(_StructureWrapper):
467    ## A @ref ValueList object representing the list of Values
468    #  stored on this Key
469    values = None
470
471    ## A @ref SubkeyList object representing the list of subkeys
472    #  stored on this Key
473    subkeys = None
474
475    ## The raw Key name as an uninterpreted bytearray
476    name_raw = (b"...")
477   
478    ## The name of the Key as a (unicode) string
479    name = "..."
480   
481    ## The absolute file offset of the Key record's cell in the Hive file
482    offset = 0xCAFEBABE
483
484    ## This Key's last modified time represented as the number of seconds
485    #  since the UNIX epoch in UTC; similar to what time.time() returns
486    modified = 1300000000.123456
487
488    ## The NK record's flags field
489    flags = 0x10110001
490
491    def __init__(self, hive, base):
492        super(Key, self).__init__(hive, base)
493        self.values = ValueList(self)
494        self.subkeys = SubkeyList(self)
495
496    def __getattr__(self, name):
497        if name == "name":
498            ret_val = super(Key, self).__getattr__(name)
499
500            if not ret_val:
501                ret_val = self.name_raw
502            else:
503                ret_val = ret_val.decode('utf-8', 'replace')
504               
505        elif name == "name_raw":
506            ret_val = super(Key, self).__getattr__(name)
507            length = super(Key, self).__getattr__('name_length')
508            ret_val = _buffer2bytearray(ret_val, length)
509       
510        elif name == "modified":
511            ret_val = regfi.regfi_nt2unix_time(byref(self._base.contents.mtime))
512
513        else:
514            ret_val = super(Key, self).__getattr__(name)
515
516        return ret_val
517
518
519    ## Retrieves the Security properties for this key
520    def fetch_security(self):
521        return Security(self._hive,
522                        regfi.regfi_fetch_sk(self._hive.file, self._base))
523
524
525    ## Retrieves the class name for this key
526    #
527    # Class names are typically stored as UTF-16LE strings, so these are decoded
528    # into proper python (unicode) strings.  However, if this fails, a bytearray
529    # is instead returned containing the raw buffer stored for the class name.
530    #
531    # @return The class name as a string or bytearray.  None if a class name
532    #         doesn't exist or an unrecoverable error occurred during retrieval.
533    def fetch_classname(self):
534        ret_val = None
535        cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base)
536        if cn_p:
537            cn_struct = cn_p.contents
538            if cn_struct.interpreted:
539                ret_val = cn_struct.interpreted.decode('utf-8', 'replace')
540            else:
541                ret_val = _buffer2bytearray(cn_struct.raw,
542                                            cn_struct.size)
543            regfi.regfi_free_record(self._hive.file, cn_p)
544
545        return ret_val
546
547
548    ## Retrieves this key's parent key
549    #
550    # @return The parent's Key instance or None if current key is root
551    #         (or an error occured)
552    def get_parent(self):
553        if self.is_root():
554            return None
555        parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base)
556        if parent_base:
557            return Key(self._hive, parent_base)
558        return None
559
560    def is_root(self):
561        return (self._hive.root == self)
562
563
564## Registry value (metadata)
565#
566# These represent registry values (@ref REGFI_VK records) and provide
567# access to their associated data.
568#
569# @note Value instances may provide access to more than the attributes
570#       documented here.  However, undocumented attributes may change over time
571#       and are not officially supported.  If you need access to an attribute
572#       not shown here, see pyregfi.structures.
573class Value(_StructureWrapper):
574    ## The raw Value name as an uninterpreted bytearray
575    name_raw = (b"...")
576   
577    ## The name of the Value as a (unicode) string
578    name = "..."
579   
580    ## The absolute file offset of the Value record's cell in the Hive file
581    offset = 0xCAFEBABE
582
583    ## The length of data advertised in the VK record
584    data_size = 0xCAFEBABE
585
586    ## An integer which represents the data type for this Value's data
587    # Typically this value is one of 12 types defined in @ref DATA_TYPES,
588    # but in some cases (the SAM hive) it may be used for other purposes
589    type = DATA_TYPES.NONE
590
591    ## The VK record's flags field
592    flags = 0x10110001
593
594    ## Retrieves the Value's data according to advertised type
595    #
596    # Data is loaded from its cell(s) and then interpreted based on the data
597    # type recorded in the Value.  It is not uncommon for data to be stored with
598    # the wrong type or even with invalid types.  If you have difficulty
599    # obtaining desired data here, use @ref fetch_raw_data().
600    #
601    # @return The interpreted representation of the data as one of several
602    #         possible Python types, as listed below.  None if any failure
603    #         occurred during extraction or conversion.
604    #
605    # @retval string for SZ, EXPAND_SZ, and LINK
606    # @retval int for DWORD, DWORD_BE, and QWORD
607    # @retval list(string) for MULTI_SZ
608    # @retval bytearray for NONE, BINARY, RESOURCE_LIST,
609    #         FULL_RESOURCE_DESCRIPTOR, and RESOURCE_REQUIREMENTS_LIST
610    #
611    def fetch_data(self):
612        ret_val = None
613        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
614        if not data_p:
615            return None
616        data_struct = data_p.contents
617
618        if data_struct.interpreted_size == 0:
619            ret_val = None
620        elif data_struct.type in (DATA_TYPES.SZ, DATA_TYPES.EXPAND_SZ, DATA_TYPES.LINK):
621            # Unicode strings
622            ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
623        elif data_struct.type in (DATA_TYPES.DWORD, DATA_TYPES.DWORD_BE):
624            # 32 bit integers
625            ret_val = data_struct.interpreted.dword
626        elif data_struct.type == DATA_TYPES.QWORD:
627            # 64 bit integers
628            ret_val = data_struct.interpreted.qword
629        elif data_struct.type == DATA_TYPES.MULTI_SZ:
630            ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
631        elif data_struct.type in (DATA_TYPES.NONE, DATA_TYPES.RESOURCE_LIST,
632                                  DATA_TYPES.FULL_RESOURCE_DESCRIPTOR,
633                                  DATA_TYPES.RESOURCE_REQUIREMENTS_LIST,
634                                  DATA_TYPES.BINARY):
635            ret_val = _buffer2bytearray(data_struct.interpreted.none,
636                                        data_struct.interpreted_size)
637
638        regfi.regfi_free_record(self._hive.file, data_p)
639        return ret_val
640   
641
642    ## Retrieves raw representation of Value's data
643    #
644    # @return A bytearray containing the data
645    #
646    def fetch_raw_data(self):
647        ret_val = None
648        # XXX: should we load the data without interpretation instead?
649        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
650        if not data_p:
651            return None
652
653        data_struct = data_p.contents
654        ret_val = _buffer2bytearray(data_struct.raw,
655                                    data_struct.size)
656        regfi.regfi_free_record(self._hive.file, data_p)
657        return ret_val
658
659
660    def __getattr__(self, name):
661        ret_val = super(Value, self).__getattr__(name)
662        if name == "name":
663            if not ret_val:
664                ret_val = self.name_raw
665            else:
666                ret_val = ret_val.decode('utf-8', 'replace')
667
668        elif name == "name_raw":
669            length = super(Value, self).__getattr__('name_length')
670            ret_val = _buffer2bytearray(ret_val, length)
671
672        return ret_val
673
674
675# Avoids chicken/egg class definitions.
676# Also makes for convenient code reuse in these lists' parent classes.
677SubkeyList._constructor = Key
678ValueList._constructor = Value
679
680
681
682## Represents a single registry hive (file)
683class Hive():
684    file = None
685    raw_file = None
686    _fh = None
687    #_root = None
688
689
690    ## The root Key of this Hive
691    root = None
692
693    ## This Hives's last modified time represented as the number of seconds
694    #  since the UNIX epoch in UTC; similar to what time.time() returns
695    modified = 1300000000.123456
696
697    ## First sequence number
698    sequence1 = 12345678
699
700    ## Second sequence number
701    sequence2 = 12345678
702
703    ## Major version
704    major_version = 1
705
706    ## Minor version
707    minor_version = 5
708
709    ## Constructor
710    #
711    # Initialize a new Hive based on a Python file object.  To open a file by
712    # path, see @ref openHive.
713    #
714    # @param fh A Python file object.  The constructor first looks for a valid
715    #           fileno attribute on this object and uses it if possible. 
716    #           Otherwise, the seek and read methods are used for file
717    #           access.
718    #
719    # @note Supplied file must be seekable.  Do not perform any operation on
720    #       the provided file object while a Hive is using it.  Do not
721    #       construct multiple Hive instances from the same file object.
722    #       If a file must be accessed by separate code and pyregfi
723    #       simultaneously, use a separate file descriptor.  Hives are
724    #       thread-safe, so multiple threads may use a single Hive object.
725    def __init__(self, fh):
726        # The fileno method may not exist, or it may throw an exception
727        # when called if the file isn't backed with a descriptor.
728        self._fh = fh
729        fn = None
730        try:
731            # XXX: Native calls to Windows filenos don't seem to work. 
732            #      Need to investigate why.
733            if not is_win32 and hasattr(fh, 'fileno'):
734                fn = fh.fileno()
735        except:
736            pass
737
738        if fn != None:
739            self.file = regfi.regfi_alloc(fn, REGFI_ENCODING_UTF8)
740            if not self.file:
741                # XXX: switch to non-generic exception
742                raise Exception("Could not open registry file.  Current log:\n"
743                                + getLogMessages())
744        else:
745            fh.seek(0)
746            self.raw_file = structures.REGFI_RAW_FILE()
747            self.raw_file.fh = fh
748            self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
749            self.raw_file.read = read_cb_type(self.raw_file.cb_read)
750            self.file = regfi.regfi_alloc_cb(pointer(self.raw_file), REGFI_ENCODING_UTF8)
751            if not self.file:
752                # XXX: switch to non-generic exception
753                raise Exception("Could not open registry file.  Current log:\n"
754                                + getLogMessages())
755
756
757    def __getattr__(self, name):
758        if name == "root":
759            # XXX: This creates reference loops.  Need to cache better inside regfi
760            #if self._root == None:
761            #    self._root = Key(self, regfi.regfi_get_rootkey(self.file))
762            #return self._root
763            return Key(self, regfi.regfi_get_rootkey(self.file))
764
765        elif name == "modified":
766            return regfi.regfi_nt2unix_time(byref(self._base.contents.mtime))
767
768        return getattr(self.file.contents, name)
769
770   
771    def __del__(self):
772        if self.file:
773            regfi.regfi_free(self.file)
774
775    def __iter__(self):
776        return HiveIterator(self)
777
778
779    ## Creates a @ref HiveIterator initialized at the specified path in
780    #  the hive.
781    #
782    # @param path A list of Key names which represent an absolute path within
783    #             the Hive
784    #
785    # @return A @ref HiveIterator which is positioned at the specified path.
786    #
787    # @exception Exception If the path could not be found/traversed
788    def subtree(self, path):
789        hi = HiveIterator(self)
790        hi.descend(path)
791        return hi
792
793
794## A special purpose iterator for registry hives
795#
796# Iterating over an object of this type causes all keys in a specific
797# hive subtree to be returned in a depth-first manner. These iterators
798# are typically created using the @ref Hive.subtree() function on a @ref Hive
799# object.
800#
801# HiveIterators can also be used to manually traverse up and down a
802# registry hive as they retain information about the current position in
803# the hive, along with which iteration state for subkeys and values for
804# every parent key.  See the @ref up and @ref down methods for more
805# information.
806class HiveIterator():
807    _hive = None
808    _iter = None
809    _iteration_root = None
810    _lock = None
811
812    def __init__(self, hive):
813        self._iter = regfi.regfi_iterator_new(hive.file)
814        if not self._iter:
815            raise Exception("Could not create iterator.  Current log:\n"
816                            + getLogMessages())
817        self._hive = hive
818        self._lock = threading.RLock()
819   
820    def __getattr__(self, name):
821        self._lock.acquire()
822        ret_val = getattr(self._iter.contents, name)
823        self._lock.release()
824        return ret_val
825
826    def __del__(self):
827        self._lock.acquire()
828        regfi.regfi_iterator_free(self._iter)
829        self._lock.release()
830
831    def __iter__(self):
832        self._lock.acquire()
833        self._iteration_root = None
834        self._lock.release()
835        return self
836
837    def __next__(self):
838        self._lock.acquire()
839        if self._iteration_root == None:
840            self._iteration_root = self.current_key().offset
841        elif not regfi.regfi_iterator_down(self._iter):
842            up_ret = regfi.regfi_iterator_up(self._iter)
843            while (up_ret and
844                   not regfi.regfi_iterator_next_subkey(self._iter)):
845                if self._iteration_root == self.current_key().offset:
846                    self._iteration_root = None
847                    self._lock.release()
848                    raise StopIteration('')
849                up_ret = regfi.regfi_iterator_up(self._iter)
850
851            if not up_ret:
852                self._iteration_root = None
853                self._lock.release()
854                raise StopIteration('')
855           
856            # XXX: Use non-generic exception
857            if not regfi.regfi_iterator_down(self._iter):
858                self._lock.release()
859                raise Exception('Error traversing iterator downward.'+
860                                ' Current log:\n'+ getLogMessages())
861
862        regfi.regfi_iterator_first_subkey(self._iter)
863        ret_val = self.current_key()
864        self._lock.release()
865
866        return ret_val
867
868
869    # For Python 2.x
870    next = __next__
871
872    # XXX: Should add sanity checks on some of these traversal functions
873    #      to throw exceptions if a traversal/retrieval *should* have worked
874    #      but failed for some reason.
875
876    ## Descends the iterator to a subkey
877    #
878    # Descends the iterator one level to the current subkey, or a subkey
879    # specified by name.
880    #
881    # @param subkey_name If specified, locates specified subkey by name
882    #                    (via find_subkey()) and descends to it.
883    #
884    # @return True if successful, False otherwise
885    def down(self, subkey_name=None):
886        ret_val = None
887        if subkey_name == None:
888            self._lock.acquire()
889            ret_val = regfi.regfi_iterator_down(self._iter)
890        else:
891            if name != None:
892                name = name.encode('utf-8')
893            self._lock.acquire()
894            ret_val = (regfi.regfi_iterator_find_subkey(self._iter, name) 
895                       and regfi.regfi_iterator_down(self._iter))
896       
897        self._lock.release()
898        return ret_val
899
900
901    ## Causes the iterator to ascend to the current Key's parent
902    #
903    # @return True if successful, False otherwise
904    #
905    # @note The state of current subkeys and values at this level in the tree
906    #       is lost as a side effect.  That is, if you go up() and then back
907    #       down() again, current_subkey() and current_value() will return
908    #       default selections.
909    def up(self):
910        self._lock.acquire()
911        ret_val = regfi.regfi_iterator_up(self._iter)
912        self._lock.release()
913        return ret_val
914
915
916    ## Selects first subkey of current key
917    #
918    # @return A Key instance for the first subkey. 
919    #         None on error or if the current key has no subkeys.
920    def first_subkey(self):
921        ret_val = None
922        self._lock.acquire()
923        if regfi.regfi_iterator_first_subkey(self._iter):
924            ret_val = self.current_subkey()
925        self._lock.release()
926        return ret_val
927
928
929    ## Selects first value of current Key
930    #
931    # @return A Value instance for the first value. 
932    #         None on error or if the current key has no values.
933    def first_value(self):
934        ret_val = None
935        self._lock.acquire()
936        if regfi.regfi_iterator_first_value(self._iter):
937            ret_val = self.current_value()
938        self._lock.release()
939        return ret_val
940
941
942    ## Selects the next subkey in the current Key's list
943    #
944    # @return A Key instance for the next subkey.
945    #         None if there are no remaining subkeys or an error occurred.
946    def next_subkey(self):
947        ret_val = None
948        self._lock.acquire()
949        if regfi.regfi_iterator_next_subkey(self._iter):
950            ret_val = self.current_subkey()
951        self._lock.release()
952        return ret_val
953
954
955    ## Selects the next value in the current Key's list
956   
957    # @return A Value instance for the next value.
958    #         None if there are no remaining values or an error occurred.
959    def next_value(self):
960        ret_val = None
961        self._lock.acquire()
962        if regfi.regfi_iterator_next_value(self._iter):
963            ret_val = self.current_value()
964        self._lock.release()
965        return ret_val
966
967
968    ## Selects the first subkey which has the specified name
969    #
970    # @return A Key instance for the selected key.
971    #         None if it could not be located or an error occurred.
972    def find_subkey(self, name):
973        if name != None:
974            name = name.encode('utf-8')
975        ret_val = None
976        self._lock.acquire()
977        if regfi.regfi_iterator_find_subkey(self._iter, name):
978            ret_val = self.current_subkey()
979        self._lock.release()
980        return ret_val
981
982
983    ## Selects the first value which has the specified name
984    #
985    # @return A Value instance for the selected value.
986    #         None if it could not be located or an error occurred.
987    def find_value(self, name):
988        if name != None:
989            name = name.encode('utf-8')
990        ret_val = None
991        self._lock.acquire()
992        if regfi.regfi_iterator_find_value(self._iter, name):
993            ret_val = self.current_value()
994        self._lock.release()
995        return ret_val
996
997    ## Retrieves the currently selected subkey
998    #
999    # @return A Key instance of the current subkey
1000    def current_subkey(self):
1001        self._lock.acquire()
1002        ret_val = Key(self._hive, regfi.regfi_iterator_cur_subkey(self._iter))
1003        self._lock.release()
1004        return ret_val
1005
1006    ## Retrieves the currently selected value
1007    #
1008    # @return A Value instance of the current value
1009    def current_value(self):
1010        self._lock.acquire()
1011        ret_val = Value(self._hive, regfi.regfi_iterator_cur_value(self._iter))
1012        self._lock.release()
1013        return ret_val
1014
1015    ## Retrieves the current key
1016    #
1017    # @return A Key instance of the current position of the iterator
1018    def current_key(self):
1019        self._lock.acquire()
1020        ret_val = Key(self._hive, regfi.regfi_iterator_cur_key(self._iter))
1021        self._lock.release()
1022        return ret_val
1023
1024    ## Traverse downward multiple levels
1025    #
1026    # This is more efficient than calling down() multiple times
1027    #
1028    # @param path A list of Key names which represent the path to descend
1029    #
1030    # @exception Exception If path could not be located
1031    def descend(self, path):
1032        cpath = _strlist2charss(path)
1033
1034        self._lock.acquire()
1035        result = regfi.regfi_iterator_descend(self._iter, cpath)
1036        self._lock.release()
1037        if not result:
1038            # XXX: Use non-generic exception
1039            raise Exception('Could not locate path.\n'+getLogMessages())
1040
1041    ## Obtains a list of the current key's ancestry
1042    #
1043    # @return A list of all parent keys starting with the root Key and ending
1044    #         with the current Key
1045    def ancestry(self):
1046        self._lock.acquire()
1047        result = regfi.regfi_iterator_ancestry(self._iter)
1048        self._lock.release()
1049
1050        ret_val = []
1051        i = 0
1052        k = result[i]
1053        while k:
1054            k = cast(regfi.regfi_reference_record(self._hive.file, k), POINTER(REGFI_NK))
1055            ret_val.append(Key(self._hive, k))
1056            i += 1
1057            k = result[i]
1058
1059        regfi.regfi_free_record(self._hive.file, result)
1060        return ret_val
1061
1062    ## Obtains the current path of the iterator
1063    #
1064    # @return A list of key names starting with the root up to and
1065    #         including the current key
1066    #
1067    def current_path(self):
1068        ancestry = self.ancestry()
1069        return [str(a.name) for a in ancestry]
1070
1071
1072# Freeing symbols defined for the sake of documentation
1073del Value.name,Value.name_raw,Value.offset,Value.data_size,Value.type,Value.flags
1074del Key.name,Key.name_raw,Key.offset,Key.modified,Key.flags
1075del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version
1076del Security.ref_count,Security.offset,Security.descriptor
Note: See TracBrowser for help on using the repository browser.