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

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

added a test case for pyregfi multithreaded use
updated the regfi multithreaded test case
fixed several ctypes interface problems in pyregfi
added locking to pyregfi iterators for thread safety
fixed regfi talloc race conditions with an additional lock

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