windowsclasses.py 13.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Place holder for all windows specific frequently used classes.
Currently here:
  RegistryHandler - Registry based operations
  DecideArchitecture - Accurate way to decide operating system architecture
"""

import os
import argparse
import errno
14
from _winreg import *  # noqa
15
try:
16
    from collections import *  # noqa
17
except ImporError:
18
    from OrderedDict import *  # noqa
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

def parse_arguments():
    """
    Argument parser, based on argparse module

    Keyword arguments:
    @return args -- arguments given by console
    """
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-g", "--global",
        help="Whether we want to edit registry globally or just locally",
        action="store_true")
    parser.add_argument(
        "-d", "--different",
        help="Only handle the architecture specific registry area",
        action="store_true")
    parser.add_argument(
        "-r", "--registry",
39
        help="Which HKEY_* const registry type the program should use",
40 41 42 43 44
        type=str, choices=['HKLM', 'HKCR', 'HKCU', 'HKU', 'HKPD', 'HKCC'],
        default="HKLM")
    args = parser.parse_args()
    return args

45

46 47 48
def main():
    return RegistryHandler(parse_arguments())

49

50 51 52 53 54 55 56
class Struct:
    """
    Parameter bypassing struct
    """
    pass


57 58 59
class ClientRegistry:
    @staticmethod
    def directory():
60 61 62
        custom_param = Struct()
        custom_param.registry = "HKCU"
        handler = RegistryHandler(custom_param)
63 64 65 66 67 68 69 70 71
        directory = None
        try:
            directory = handler.get_key_value(
                "Software\CIRCLE Cloud Client", "running_directory")
        except LookupError:
            directory = os.path.dirname(os.path.abspath(__file__))
        return directory


72 73
class RegistryHandler:
    """
74
    Registry handling class, makes registry based queries and
75
    manipulations easier.
76 77
    This class can handle WOW64 based application differently and
    similarly (default)
78 79
    """

80
    def __init__(self, args=None):
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
        """Initialise RegistryHandler

        Keyword arguments:
        @param args  -- The arguments that decide how the we should
                        handle the registry
        args.registry    -- What base registry should we use
                            (default: HKLM)
        Raises AttributeError if not supported base registry type is
        given
        args.different -- Whether we handle registry redirection or not
        """
        if args is None:
            self.args = Struct()
            self.args.different = None
            self.args.registry = None
        else:
            if not hasattr(args, 'different'):
                args.different = None
            if not hasattr(args, 'registry'):
                args.registry = None
            self.args = args
        if self.args.different is None:
            self.args.different = False
        if self.args.registry is None or self.args.registry == "HKLM":
            self.args.registry = HKEY_LOCAL_MACHINE
        elif self.args.registry == "HKCR":
            self.args.registry = HKEY_CLASSES_ROOT
        elif self.args.registry == "HKCU":
            self.args.registry = HKEY_CURRENT_USER
        elif self.args.registry == "HKU":
            self.args.registry = HKEY_USERS
        elif self.args.registry == "HKPD":
            self.args.registry = HKEY_PERFORMANCE_DATA
        elif self.args.registry == "HKCC":
            self.args.registry = HKEY_CURRENT_CONFIG
        else:
117
            # print "Non supported registry type"
118 119 120 121 122 123 124 125 126
            raise AttributeError

    def connect_registry(self):
        """
        Getting a registry open

        Keyword arguments:
        @return connected_registy    -- Reference to the newly opened
                                        registry
127 128
        """
        return ConnectRegistry(None, self.args.registry)
129 130

    def create_registry_from_dict_chain(
131 132
            self, dict_chain, both=False, architect=KEY_WOW64_64KEY,
            needed_rights=KEY_ALL_ACCESS):
133
        """"
134
        Create registry key and value multilevel tree by chained
135 136 137 138 139
        dictionaries.
        Can raise AttributeError if the provided dict chain isn't
        correct

        Keyword arguments:
140
        @param key_value_chain -- The dict chain containing all the
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
                                  information
        @param both            -- Whether create the registry in WOW64
                                  node too
        @param architect       -- The current registry view (
                                  only change it if we want to fill the
                                  WOW6432 registry only [both = False],
                                  on x64 windows)
        @param needed_rights   -- SAM rights to access the key
        """
        if both and architect is KEY_WOW64_64KEY:
            self.create_registry_from_dict_chain(
                dict_chain, False, KEY_WOW64_32KEY, needed_rights)
        if not DecideArchitecture.Is64Windows():
            architect = 0
        connected_registy = self.connect_registry()
156
        if (isinstance(dict_chain, dict)
157 158 159 160 161
                or isinstance(dict_chain, OrderedDict)):
            for key, value in dict_chain.iteritems():
                if isinstance(value, dict) or isinstance(value, OrderedDict):
                    temp_dict = OrderedDict()
                    for my_key, my_value in value.iteritems():
162
                        temp_dict[key + "\\" + my_key] = my_value
163 164 165 166
                    self.create_registry_from_dict_chain(
                        temp_dict, False, architect, needed_rights)
                else:
                    if isinstance(value, list):
167 168
                        if len(value) % 2 != 0:
                            # print "Not enough member in the list"
169 170 171 172 173 174
                            raise AttributeError
                        else:
                            new_key = CreateKeyEx(
                                connected_registy, key, 0,
                                needed_rights | architect)
                            temp_dict = OrderedDict(
175
                                value[i:i + 2] for i in range(
176 177 178 179 180 181
                                    0, len(value), 2))
                            for my_key, my_value in temp_dict.iteritems():
                                if my_key == "default":
                                    my_key = None
                                SetValueEx(
                                    new_key, my_key, 0, REG_SZ, my_value)
182
                    else:
183 184 185 186 187
                        new_key = CreateKeyEx(
                            connected_registy, key, 0,
                            needed_rights | architect)
                        SetValueEx(new_key, None, 0, REG_SZ, value)
        else:
Belákovics Ádám committed
188
            print("The provided attribute wasn't a dictionary chain")
189 190
            raise AttributeError

191
    def get_key(self, key_name, needed_rights=KEY_ALL_ACCESS):
192 193 194 195 196 197 198 199 200 201 202 203
        """
        Getting a registry value by it's key's name
        Can raise KeyError if key is not found in the registry

        Keyword arguments:
        @param key_name      -- The specific key name of which value we
                                are interested in
        @param needed_rights -- SAM rights to access the key
        @return              -- [key, architect]
                                key          -- Reference to the opened
                                                key handler
                                architect    -- 0 for x86
204 205 206
                                                KEY_WOW64_32KEY or
                                                KEY_WOW64_64KEY
                                                depending where we
207 208 209 210 211
                                                found the key on x64
        """
        connected_registy = self.connect_registry()
        architect = 0
        if DecideArchitecture.Is64Windows():
212 213
            architect = KEY_WOW64_64KEY
        try:
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
            key = OpenKey(connected_registy, key_name, 0,
                          needed_rights | architect)
        except WindowsError:
            if DecideArchitecture.Is64Windows() and not self.args.different:
                try:
                    architect = KEY_WOW64_32KEY
                    key = OpenKey(connected_registy, key_name,
                                  0, needed_rights | architect)
                except WindowsError:
                    raise KeyError
            else:
                raise KeyError
        return [key, architect]

    def get_key_values(
229 230
            self, key_name, subkey_list, subroutine=False,
            depth="subkeys"):
231 232 233 234 235
        """
        Getting registry subkeys value by it's key's name and subkeys
        name
        Can raise LookupError exception if there are missing data
        Can raise AttributeError exception if depth attribute is wrong
236

237 238 239
        Keyword arguments:
        @param key_name      -- The specific key name of which subkey's
                                we are interested in
240
        @param subkey_list   -- List containing all the subkeys names
241 242 243 244
                                which values we are interested in
        @param subroutine    -- Whether suppress exception about not
                                having enough results or not
                                (default: False)
245
        @param depth         -- How depth the search should go for
246
                                [options: key, subkeys, all]
247 248 249 250 251
                                (default: subkeys)
        @return results{}    -- Dictionary with the subkey_name - value
                                combinations as keys and values
        """
        if depth == "key":
252
            int_depth = 0
253
        elif depth == "subkeys":
254
            int_depth = 1
255
        elif depth == "all":
256
            int_depth = 2
257 258 259 260 261 262 263
        else:
            raise AttributeError
        try:
            key_and_architect = self.get_key(key_name)
            key = key_and_architect[0]
            architect = key_and_architect[1]
        except KeyError:
264
            # print "%s doesn't exist in the registry" % key_name
265
            raise LookupError
266
        # print "%s found in the registry" % key_name
267 268
        results = {}
        if int_depth >= 1:
269
            for i in xrange(0, QueryInfoKey(key)[0] - 1):
270 271 272 273 274 275 276 277 278 279 280 281
                skey_name = EnumKey(key, i)
                skey = OpenKey(key, skey_name, 0, KEY_ALL_ACCESS | architect)
                if int_depth == 2 and QueryInfoKey(skey)[0] > 0:
                    for key, value in self.get_key_values(
                            skey_name, subkey_list, True, depth).iteritems():
                        results[key] = value
                for subkey_name in subkey_list:
                    try:
                        results[subkey_name] = QueryValueEx(
                            skey, subkey_name)[0]
                    except OSError as e:
                        if e.errno == errno.ENOENT:
282 283
                            # print ("%s doesn't exist in this" % subkey_name
                            #        " subkey")
284 285 286
                            pass
                skey.Close()
        if not results or len(results) != len(subkey_list):
287
            for subkey_name in subkey_list:
288 289 290 291 292 293
                try:
                    results[subkey_name] = QueryValueEx(key, subkey_name)[0]
                except OSError as e:
                    pass
        key.Close()
        if len(results) != len(subkey_list):
294
            # print "We are missing important variables"
295 296 297 298
            raise LookupError
        return results

    def get_key_value(
299
            self, key_name, subkey_name, subroutine=None, depth=None):
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
        """
        This is a wrapper for the get_key_values to be easier to use
        for single subkeys.
        Getting registry subkey value by it's key's name and subkey
        name

        Keyword arguments:
        @param key_name      -- The specific key name of which subkey's
                                we are interested in
        @param subkey_name   -- The specific subkey name which value we
                                are interested in
        @param subroutine    -- can be found at: get_key_values
        @param depth         -- can be found at: get_key_values
        @return value        -- Value of the specific subkey
        """
        try:
            if subroutine is None:
317 318
                return self.get_key_values(
                    key_name, [subkey_name])[subkey_name]
319
            elif depth is None:
320 321
                return self.get_key_values(
                    key_name, [subkey_name], subroutine)[subkey_name]
322
            else:
323 324
                return self.get_key_values(
                    key_name, [subkey_name], subroutine, depth)[subkey_name]
325 326 327 328 329 330 331 332 333
        except:
            raise


class DecideArchitecture:
    """
    Helper class to get the true ProgramFiles directory.
    This class doesn't depend on Phyton or Windows architecture.
    """
334

335 336 337
    @staticmethod
    def Is64Windows():
        return 'PROGRAMFILES(X86)' in os.environ
338

339 340 341 342 343 344
    @staticmethod
    def GetProgramFiles32():
        if DecideArchitecture.Is64Windows():
            return os.environ['PROGRAMFILES(X86)']
        else:
            return os.environ['PROGRAMFILES']
345

346 347 348 349 350
    @staticmethod
    def GetProgramFiles64():
        if DecideArchitecture.Is64Windows():
            return os.environ['PROGRAMW6432']
        else:
351
            return None