class Inspec::Resources::RegistryKey

rubocop:disable Metrics/ClassLength

def children(filter = nil)

returns an arrray of child nodes
def children(filter = nil)
  children_keys(@options[:path], filter)
end

def children_keys(path, filter = '')

def children_keys(path, filter = '')
  return @children_cache if defined?(@children_cache)
  filter = filter.source if filter.is_a? ::Regexp
  script = <<-EOH
  Function InSpec-FindChildsRegistryKeys($path, $filter) {
    # get information about the child registry keys
    $items = Get-ChildItem -Path ('Registry::' + $path) -rec -ea SilentlyContinue
    # filter entries
    $items | Where-Object {
        $name = $_.Name
        $simple = $name -replace "HKEY_LOCAL_MACHINE\\\\",""
        $simple = $name -replace "HKEY_USERS\\\\",""
        $simple -Match $filter
    } | % { $_.Name }
  }
  $path = '#{path}'
  $filter = "#{filter}"
  ConvertTo-Json @(InSpec-FindChildsRegistryKeys $path $filter)
  EOH
  cmd = inspec.powershell(script)
  begin
    @children_cache = JSON.parse(cmd.stdout)
  rescue JSON::ParserError => _e
    @children_cache = []
  end
  @children_cache
end

def exists?

def exists?
  !registry_key(@options[:path]).nil?
end

def has_property?(property_name, property_type = nil)

def has_property?(property_name, property_type = nil)
  val = registry_key(@options[:path])
  !val.nil? && registry_property_exists(val, property_name) && (property_type.nil? || registry_property_type(val, property_name) == map2type(property_type)) ? true : false
end

def has_property_value?(property_name, property_type = nil, value)

rubocop:disable Style/OptionalArguments
deactivate rubocop, because we need to stay compatible with Serverspe
def has_property_value?(property_name, property_type = nil, value)
  # rubocop:enable Style/OptionalArguments
  val = registry_key(@options[:path])
  # convert value to binary if required
  value = value.bytes if !property_type.nil? && map2type(property_type) == 3 && !value.is_a?(Array)
  !val.nil? && registry_property_value(val, property_name) == value && (property_type.nil? || registry_property_type(val, property_name) == map2type(property_type)) ? true : false
end

def has_value?(value)

def has_value?(value)
  val = registry_key(@options[:path])
  !val.nil? && registry_property_value(val, '(default)') == value ? true : false
end

def initialize(name, reg_key = nil)

def initialize(name, reg_key = nil)
  # if we have one parameter, we use it as name
  reg_key ||= name
  @options = {}
  if reg_key && reg_key.is_a?(Hash)
    @options = @options.merge!(reg_key)
    # generate registry_key if we do not have a regular expression
    @options[:path] = @options[:hive]
    # add optional key path
    @options[:path] += '\\' + @options[:key] if @options[:key]
    @options[:name] ||= @options[:path]
  else
    @options[:name] = name
    @options[:path] = reg_key
  end
  return skip_resource 'The `registry_key` resource is not supported on your OS yet.' if !inspec.os.windows?
end

def map2type(symbol)

Other tags:
    See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx -
def map2type(symbol)
  options = {}
  # chef symbols, we prefer those
  options[:binary] = 3
  options[:string] = 1
  options[:multi_string] = 7
  options[:expand_string] = 2
  options[:dword] = 4
  options[:dword_big_endian] = 5
  options[:qword] = 11
  # serverspec symbols
  options[:type_string] = 1
  options[:type_binary] = 3
  options[:type_dword] = 4
  options[:type_qword] = 11
  options[:type_multistring] = 7
  options[:type_expandstring] = 2
  options[symbol]
end

def method_missing(meth)

returns nil, if not existant or value
def method_missing(meth)
  # get data
  val = registry_key(@options[:path])
  registry_property_value(val, meth)
end

def prep_prop(property)

def prep_prop(property)
  property.to_s.downcase
end

def registry_key(path)

def registry_key(path)
  return @registry_cache if defined?(@registry_cache)
  # load registry key and all properties
  script = <<-EOH
  Function InSpec-GetRegistryKey($path) {
    $reg = Get-Item ('Registry::' + $path)
    if ($reg -eq $null) {
      Write-Error "InSpec: Failed to find registry key"
      exit 1001
    }
    $properties = New-Object -Type PSObject
    $reg.Property | ForEach-Object {
        $key = $_
        if ("(default)".Equals($key)) { $key = '' }
        $value = New-Object psobject -Property @{
          "value" =  $reg.GetValue($key);
          "type"  = $reg.GetValueKind($key);
        }
        $properties | Add-Member NoteProperty $_ $value
    }
    $properties
  }
  $path = '#{path}'
  InSpec-GetRegistryKey($path) | ConvertTo-Json
  EOH
  cmd = inspec.powershell(script)
  # cannot rely on exit code for now, successful command returns exit code 1
  # return nil if cmd.exit_status != 0, try to parse json
  begin
    if cmd.exit_status == 1001 && cmd.stderr =~ /InSpec: Failed to find registry key/
      # TODO: provide the stderr output
      @registry_cache = nil
    else
      @registry_cache = JSON.parse(cmd.stdout)
      # convert keys to lower case
      @registry_cache = Hash[@registry_cache.map do |key, value|
        [key.downcase, value]
      end]
    end
  rescue JSON::ParserError => _e
    @registry_cache = nil
  end
  @registry_cache
end

def registry_property_exists(regkey, property)

def registry_property_exists(regkey, property)
  return false if regkey.nil? || property.nil?
  # always ensure the key is lower case
  !regkey[prep_prop(property)].nil?
end

def registry_property_type(regkey, property)

def registry_property_type(regkey, property)
  return nil if !registry_property_exists(regkey, property)
  # always ensure the key is lower case
  regkey[prep_prop(property)]['type']
end

def registry_property_value(regkey, property)

def registry_property_value(regkey, property)
  return nil if !registry_property_exists(regkey, property)
  # always ensure the key is lower case
  regkey[prep_prop(property)]['value']
end

def to_s

def to_s
  "Registry Key #{@options[:name]}"
end