Class: MiqWin32::Software

Inherits:
Object
  • Object
show all
Defined in:
lib/metadata/util/win32/Win32Software.rb

Constant Summary collapse

PRODUCTS_MAPPING =
[
  'DisplayName', :name,
  'Publisher', :vendor,
  'DisplayVersion', :version,
  'Comments', :description,
  'InstallLocation', :path,
  #     'PackageName', :package_name,
  #     'ProductIcon', :product_icon,
  #     'PackageName', :package_name,
  'InstallDate', :install_time,
]
APP_PATHS_MAPPING =
[
  'FileDescription', :name,
  'CompanyName', :vendor,
  'ProductVersion', :version,
  'FileDescription', :description,
  'ProductName', :package_name,
  'lang', :language,
  'path', :path,
]
UNINSTALL_MAPPING =
[
  'DisplayName', :name,
  'Publisher', :vendor,
  'DisplayVersion', :version,
  'FileDescription', :description,
  'ReleaseType', :release_type,
  'InstallDate', :installed_on
]
HOTFIX_MAPPING =
[
  'Fix Description', :description,   # Check Fix Decription, and then if
  'Comments',        :description2,  # not found, check Comments
  'Installed',       :installed,
  'Service Pack',    :service_pack,
  'Valid',           :valid,
]
HOTFIX_MAPPING_VISTA =
[
  'CurrentState',    :current_state,
  'Visibility',      :visibility,
  'InstallTimeHigh', :install_time_high,
  'InstallTimeLow',  :install_time_low
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(_c, fs) ⇒ Software

Returns a new instance of Software.



56
57
58
59
60
61
62
63
64
65
# File 'lib/metadata/util/win32/Win32Software.rb', line 56

def initialize(_c, fs)
  @patches = []
  @applications = []
  @patch_install_dates = {}
  @product_keys = {}

  reg_doc = initialize_registry_doc(fs)
  registry_applications(reg_doc, fs)
  registry_patches(reg_doc)
end

Instance Attribute Details

#applicationsObject (readonly)

Returns the value of attribute applications.



8
9
10
# File 'lib/metadata/util/win32/Win32Software.rb', line 8

def applications
  @applications
end

#patchesObject (readonly)

Returns the value of attribute patches.



8
9
10
# File 'lib/metadata/util/win32/Win32Software.rb', line 8

def patches
  @patches
end

#product_keysObject (readonly)

Returns the value of attribute product_keys.



8
9
10
# File 'lib/metadata/util/win32/Win32Software.rb', line 8

def product_keys
  @product_keys
end

Class Method Details

.DecodeProductKey(product_key) ⇒ Object



254
255
256
257
258
259
260
261
262
# File 'lib/metadata/util/win32/Win32Software.rb', line 254

def self.DecodeProductKey(product_key)
  parts = product_key.to_s.split(",")
  return if parts.length < 67

  ManageIQ::Smartstate::Util.base24_decode(parts[52..67].map(&:hex))
rescue => err
  $log.error "MIQ(DecodeProductKey): [#{err}]"
  nil
end

Instance Method Details

#add_applications(mapping, type_name, element) ⇒ Object



138
139
140
141
142
143
144
# File 'lib/metadata/util/win32/Win32Software.rb', line 138

def add_applications(mapping, type_name, element)
  return if (attrs = XmlFind.decode(element, mapping))[:name].nil?
  attrs[:typename] = type_name; attrs[:product_key] = @product_keys[attrs[:name]]
  clean_up_path(attrs)
  convert_times(attrs)
  @applications << attrs unless isDupApp?(attrs)
end

#clean_up_path(attrs) ⇒ Object



240
241
242
243
244
245
246
# File 'lib/metadata/util/win32/Win32Software.rb', line 240

def clean_up_path(attrs)
  if attrs[:path]
    ["\\", ";"].each { |c| attrs[:path].chomp!(c) }
    attrs[:path].gsub!(/^"/, "")
    attrs[:path].gsub!(/"$/, "")
  end
end

#convert_times(attrs) ⇒ Object



248
249
250
251
252
# File 'lib/metadata/util/win32/Win32Software.rb', line 248

def convert_times(attrs)
  [:install_time].each do |i|
    attrs[i] = attrs[i].in_time_zone if attrs[i]
  end
end

#element_to_xml(doc = nil, software_type, doc_element, node_element) ⇒ Object



224
225
226
227
228
229
230
# File 'lib/metadata/util/win32/Win32Software.rb', line 224

def element_to_xml(doc = nil, software_type, doc_element, node_element)
  doc ||= MiqXml.createDoc(nil)
  return doc if software_type.empty?
  node = doc.add_element(doc_element)
  software_type.each { |a| node.add_element(node_element, XmlHelpers.stringify_keys(a)) }
  doc
end

#hotfix_id(element) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/metadata/util/win32/Win32Software.rb', line 198

def hotfix_id(element)
  # Expected pattern: Package_for_KBxxx_RTM~xxxx
  # Get the hotfix id (KB #) out of the keyname
  package = element.attributes[:keyname].split("_")
  # If the package identifier starts with KB, use this
  # otherwise grab the ID from the end of the string (if it's long enough)
  if package[2][0, 2] == 'KB'
    hotfix_id = package[2]
  elsif package.size >= 4
    hotfix_id = package[3]
  else
    # Unknown package ID pattern, print this out
    str = ''
    element.write(str)
    $log.warn("Win32Software::initialize - Can't determine patch element's hotfix id: #{str}")
  end
  hotfix_id.split('~')[0] unless hotfix_id.nil?
end

#initialize_registry_doc(fs) ⇒ Object

# Process application images

e.find_each("./descendant::image") { |i| nh[:image_md5] = i.attributes['md5']; validate_image(i, nh)}

def self.validate_image(imageNode, nh)
  #logger.warn("MIQ(applications-add_elements): Checking application image [#{nh.inspect}] -- [#{imageNode.to_s}]")
  x = ApplicationImage.find_by_md5(imageNode.attributes['md5'])
  if x
    #logger.warn("MIQ(applications-add_elements): Application image     found [#{imageNode.attributes['md5']}]")
  else
    #logger.warn("MIQ(applications-add_elements): Application image NOT found [#{imageNode.attributes['md5']}]")
    ApplicationImage.create({:md5=>imageNode.attributes['md5'], :name=>nh['name'], :vendor=>nh['vendor'], :version=>nh['version']})
  end
end


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/metadata/util/win32/Win32Software.rb', line 81

def initialize_registry_doc(fs)
  regHnd = RemoteRegistry.new(fs, true)
  #     reg_doc = regHnd.loadHive("software", ["Microsoft"])
  reg_doc = regHnd.loadHive('software',
                            [{:key => 'Microsoft/Windows NT/CurrentVersion/Hotfix', :value => ['fix description', 'comments', 'installed', 'service pack', 'valid']},
                             {:key => 'Microsoft/Windows/CurrentVersion/Installer/UserData', :value => ['DisplayName', 'Publisher', 'DisplayVersion', 'Comments', 'InstallLocation', 'InstallDate']},
                             {:key => 'Microsoft/Windows/CurrentVersion/Uninstall', :value => ['DisplayName', 'Publisher', 'DisplayVersion', 'FileDescription', 'ReleaseType', 'InstallDate']},
                             {:key => 'Wow6432Node/Microsoft/Windows/CurrentVersion/Uninstall', :value => ['DisplayName', 'Publisher', 'DisplayVersion', 'FileDescription', 'ReleaseType', 'InstallDate']},
                             {:key => 'Microsoft/Windows/CurrentVersion/App Paths', :value => ['(Default)', 'FileDescription', 'CompanyName', 'ProductVersion', 'FileDescription', 'ProductName', 'lang', 'path']},
                             {:key => 'Wow6432Node/Microsoft/Windows/CurrentVersion/App Paths', :value => ['(Default)', 'FileDescription', 'CompanyName', 'ProductVersion', 'FileDescription', 'ProductName', 'lang', 'path']},
                             # These locations are used to check for Product keys
                             {:key => 'Microsoft/Internet Explorer/Registration', :value => ['digitalproductid']},
                             {:key => 'Microsoft/Windows/CurrentVersion/Component Based Servicing/Packages', :value => ['CurrentState', 'Visibility', 'InstallTimeHigh', 'InstallTimeLow']},
                             {:key => 'Microsoft/Office', :value => ['digitalproductid']}])
  @digital_product_keys = regHnd.digitalProductKeys
  regHnd.close

  @product_keys = collectProductKeys(@digital_product_keys)
  reg_doc
end

#isDupApp?(attrs) ⇒ Boolean

Returns:

  • (Boolean)


232
233
234
235
236
237
238
# File 'lib/metadata/util/win32/Win32Software.rb', line 232

def isDupApp?(attrs)
  clean_up_path(attrs)
  @applications.each do |app|
    return true if app[:name] == attrs[:name]
  end
  false
end

#registry_applications(registry_doc, fs) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/metadata/util/win32/Win32Software.rb', line 102

def registry_applications(registry_doc, fs)
  # Get the applications
  reg_node = MIQRexml.findRegElement("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData", registry_doc.root)
  registry_applications_user_data(reg_node) if reg_node
  ["HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths",
   "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths"].each do |reg_path|
    registry_applications_app_paths(registry_doc, reg_path, fs)
  end
  ["HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
   "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"].each do |reg_path|
    reg_node = MIQRexml.findRegElement(reg_path, registry_doc.root)
    reg_node&.each_element_with_attribute(:keyname) do |e|
      registry_applications_uninstall(e)
    end
  end
end

#registry_applications_app_paths(registry_doc, registry_path, fs) ⇒ Object



130
131
132
133
134
135
136
# File 'lib/metadata/util/win32/Win32Software.rb', line 130

def registry_applications_app_paths(registry_doc, registry_path, fs)
  return unless (reg_node = MIQRexml.findRegElement(registry_path, registry_doc.root))
  postProcessApps(reg_node, fs)
  reg_node.each_element_with_attribute(:keyname) do |e|
    add_applications(APP_PATHS_MAPPING, "app_path", e)
  end
end

#registry_applications_uninstall(element) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/metadata/util/win32/Win32Software.rb', line 146

def registry_applications_uninstall(element)
  return if (attrs = XmlFind.decode(element, UNINSTALL_MAPPING))[:name].nil?
  if ["security update", "update"].include?(attrs.delete(:release_type).to_s.downcase)
    @patch_install_dates[e.attributes[:keyname]] = Time.parse.getlocal(attrs[:installed_on]) unless attrs[:installed_on].nil?
    return
  else
    attrs.delete(:installed_on)
  end
  attrs[:typename] = "uninstall"; attrs[:product_key] = @product_keys[attrs[:name]]
  @applications << attrs unless isDupApp?(attrs)
end

#registry_applications_user_data(reg_node) ⇒ Object



119
120
121
122
123
124
125
126
127
128
# File 'lib/metadata/util/win32/Win32Software.rb', line 119

def registry_applications_user_data(reg_node)
  reg_node.each_element_with_attribute(:keyname) do |users|
    users.each_element_with_attribute(:keyname) do |components|
      next unless components.attributes[:keyname].downcase == "products"
      components.each_element_with_attribute(:keyname) do |products|
        add_applications(PRODUCTS_MAPPING, "win32_product", products)
      end
    end
  end
end

#registry_patches(registry_doc) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/metadata/util/win32/Win32Software.rb', line 158

def registry_patches(registry_doc)
  # Get the patches (Win2000, Win2003, WinXP)
  reg_node = MIQRexml.findRegElement("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix", registry_doc.root)
  reg_node&.each_element_with_attribute(:keyname) do |e|
    registry_patches_hotfixes(e)
  end
  # Get the patches (Vista, Win2008, Windows 7)
  reg_node = MIQRexml.findRegElement("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages", registry_doc.root)
  hotfix = {}
  reg_node&.each_element do |e|
    registry_patches_packages(hotfix, e)
  end
end

#registry_patches_hotfixes(element) ⇒ Object



172
173
174
175
176
177
178
179
180
# File 'lib/metadata/util/win32/Win32Software.rb', line 172

def registry_patches_hotfixes(element)
  attrs = XmlFind.decode(element, HOTFIX_MAPPING)
  # Check both descriptions and take the first one with a value
  attrs.delete(:description2) if attrs[:description] || attrs[:description2].blank?
  attrs[:description] = attrs.delete(:description2) if attrs[:description2]

  attrs.merge!(:name => element.attributes[:keyname], :vendor => "Microsoft Corporation", :installed_on => @patch_install_dates[element.attributes[:keyname]]) unless element.attributes.nil? || element.attributes[:keyname].nil?
  @patches << attrs
end

#registry_patches_packages(hotfix, element) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/metadata/util/win32/Win32Software.rb', line 182

def registry_patches_packages(hotfix, element)
  return if element.attributes.nil? || element.attributes[:keyname].nil?
  if element.attributes[:keyname][0, 8] == 'Package_'
    # don't add this package if the ID is nil
    return if (hotfix_id = hotfix_id(element)).nil?

    hotfix[hotfix_id] ||=
      begin
        attrs = XmlFind.decode(element, HOTFIX_MAPPING_VISTA)
        install_time = wtime2time(attrs[:install_time_high], attrs[:install_time_low])
        @patches << {:name => hotfix_id, :vendor => "Microsoft Corporation", :installed_on => install_time, :installed => 1}
        true
      end
  end
end

#to_xml(doc = nil) ⇒ Object



217
218
219
220
221
222
# File 'lib/metadata/util/win32/Win32Software.rb', line 217

def to_xml(doc = nil)
  doc ||= MiqXml.createDoc(nil)
  element_to_xml(doc, @applications, "applications", "application")
  element_to_xml(doc, @patches, "patches", "patch")
  doc
end