File Coverage

blib/lib/FusionInventory/Agent/Task/Inventory/Win32/Softwares.pm
Criterion Covered Total %
statement 21 117 17.9
branch 0 60 0.0
condition 0 11 0.0
subroutine 7 15 46.6
pod 0 2 0.0
total 28 205 13.6


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Task::Inventory::Win32::Softwares;
2              
3 1     1   48544345 use strict;
  1         8  
  1         80  
4 1     1   11 use warnings;
  1         4  
  1         73  
5              
6 1     1   4 use English qw(-no_match_vars);
  1         32  
  1         21  
7             use Win32::TieRegistry (
8 1         10 Delimiter => '/',
9             ArrayValues => 0,
10             qw/KEY_READ/
11 1     1   1274 );
  1         232  
12 1     1   38 use File::Basename;
  1         1  
  1         142  
13              
14 1     1   376 use FusionInventory::Agent::Tools;
  1         2  
  1         142  
15 1     1   495 use FusionInventory::Agent::Tools::Win32;
  1         3  
  1         1458  
16              
17             my $seen = {};
18              
19             sub isEnabled {
20 0     0 0   my (%params) = @_;
21              
22 0           return !$params{no_category}->{software};
23             }
24              
25             sub doInventory {
26 0     0 0   my (%params) = @_;
27              
28 0           my $inventory = $params{inventory};
29 0           my $logger = $params{logger};
30              
31 0           my $is64bit = is64bit();
32              
33              
34 0 0         if ($is64bit) {
35              
36             # I don't know why but on Vista 32bit, KEY_WOW64_64 is able to read
37             # 32bit entries. This is not the case on Win2003 and if I correctly
38             # understand MSDN, this sounds very odd
39              
40 0 0         my $machKey64 = $Registry->Open('LMachine', {
41             Access => KEY_READ | KEY_WOW64_64 ## no critic (ProhibitBitwise)
42             }) or $logger->error("Can't open HKEY_LOCAL_MACHINE key: $EXTENDED_OS_ERROR");
43 0           my $softwaresKey64 =
44             $machKey64->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
45 0           my $softwares64 =_getSoftwaresList(
46             softwares => $softwaresKey64,
47             is64bit => 1,
48             );
49 0           foreach my $software (@$softwares64) {
50 0           _addSoftware(inventory => $inventory, entry => $software);
51             }
52             _processMSIE(
53 0           machKey => $machKey64,
54             inventory => $inventory,
55             is64bit => 1
56             );
57              
58 0 0         if ($params{scan_profiles}) {
59 0           _loadUserSoftware(
60             inventory => $inventory,
61             is64bit => 1,
62             logger => $logger
63             );
64             } else {
65 0           $logger->warning(
66             "'scan-profiles' configuration parameter disabled, " .
67             "ignoring software in user profiles"
68             );
69             }
70              
71 0 0         my $machKey32 = $Registry->Open('LMachine', {
72             Access => KEY_READ | KEY_WOW64_32 ## no critic (ProhibitBitwise)
73             }) or $logger->error("Can't open HKEY_LOCAL_MACHINE key: $EXTENDED_OS_ERROR");
74 0           my $softwaresKey32 =
75             $machKey32->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
76 0           my $softwares32 = _getSoftwaresList(
77             softwares => $softwaresKey32,
78             is64bit => 0,
79             logger => $logger,
80             );
81 0           foreach my $software (@$softwares32) {
82 0           _addSoftware(inventory => $inventory, entry => $software);
83             }
84             _processMSIE(
85 0           machKey => $machKey32,
86             inventory => $inventory,
87             is64bit => 0
88             );
89 0 0         _loadUserSoftware(
90             inventory => $inventory,
91             is64bit => 0,
92             logger => $logger
93             ) if $params{scan_profiles};
94             } else {
95 0 0         my $machKey = $Registry->Open('LMachine', {
96             Access => KEY_READ
97             }) or $logger->error("Can't open HKEY_LOCAL_MACHINE key: $EXTENDED_OS_ERROR");
98 0           my $softwaresKey =
99             $machKey->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
100 0           my $softwares = _getSoftwaresList(
101             softwares => $softwaresKey,
102             is64bit => 0,
103             );
104 0           foreach my $software (@$softwares) {
105 0           _addSoftware(inventory => $inventory, entry => $software);
106             }
107             _processMSIE(
108 0           machKey => $machKey,
109             inventory => $inventory,
110             is64bit => 0
111             );
112 0 0         _loadUserSoftware(
113             inventory => $inventory,
114             is64bit => 0,
115             logger => $logger
116             ) if $params{scan_profiles};
117              
118             }
119              
120 0           my $hotfixes = _getHotfixesList(is64bit => $is64bit);
121 0           foreach my $hotfix (@$hotfixes) {
122             # skip fixes already found in generic software list,
123             # without checking version information
124 0 0         next if $seen->{$hotfix->{NAME}};
125 0           _addSoftware(inventory => $inventory, entry => $hotfix);
126             }
127              
128             }
129              
130             sub _loadUserSoftware {
131 0     0     my (%params) = @_;
132              
133 0           my $inventory = $params{inventory};
134 0           my $is64bit = $params{is64bit};
135 0           my $logger = $params{logger};
136              
137 0 0         my $machKey = $Registry->Open('LMachine', {
138             Access => KEY_READ
139             }) or $logger->error("Can't open HKEY_LOCAL_MACHINE key: $EXTENDED_OS_ERROR");
140              
141 0           my $profileList =
142             $machKey->{"SOFTWARE/Microsoft/Windows NT/CurrentVersion/ProfileList"};
143              
144 0 0         return unless $profileList;
145              
146 0           $Registry->AllowLoad(1);
147              
148 0           foreach my $profileName (keys %$profileList) {
149             # we're only interested in subkeys
150 0 0         next unless $profileName =~ m{/$};
151 0 0         next unless length($profileName) > 10;
152              
153 0           my $profilePath = $profileList->{$profileName}{'/ProfileImagePath'};
154 0           my $sid = $profileList->{$profileName}{'/Sid'};
155              
156 0 0         next unless $sid;
157 0 0         next unless $profilePath;
158              
159 0           $profilePath =~ s/%SystemDrive%/$ENV{SYSTEMDRIVE}/i;
160              
161 0           my $user = basename($profilePath);
162             ## no critic (ProhibitBitwise)
163 0 0         my $userKey = $is64bit ?
164             $Registry->Load($profilePath.'\ntuser.dat', { Access=> KEY_READ | KEY_WOW64_64 } ) :
165             $Registry->Load($profilePath.'\ntuser.dat', { Access=> KEY_READ } ) ;
166              
167 0           my $softwaresKey =
168             $userKey->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
169              
170 0           my $softwares = _getSoftwaresList(
171             softwares => $softwaresKey,
172             is64bit => $is64bit,
173             userid => $sid,
174             username => $user
175             );
176 0           foreach my $software (@$softwares) {
177 0           _addSoftware(inventory => $inventory, entry => $software);
178             }
179              
180             }
181 0           $Registry->AllowLoad(0);
182              
183             }
184              
185              
186              
187             sub _dateFormat {
188 0     0     my ($date) = @_;
189              
190             ## no critic (ExplicitReturnUndef)
191 0 0         return undef unless $date;
192              
193 0 0         if ($date =~ /^(\d{4})(\d{1})(\d{2})$/) {
194 0           return "$3/0$2/$1";
195             }
196              
197 0 0         if ($date =~ /^(\d{4})(\d{2})(\d{2})$/) {
198 0           return "$3/$2/$1";
199             }
200              
201 0           return undef;
202             }
203              
204             sub _getSoftwaresList {
205 0     0     my (%params) = @_;
206              
207 0           my $softwares = $params{softwares};
208              
209 0           my @list;
210              
211 0 0         return unless $softwares;
212              
213 0           foreach my $rawGuid (keys %$softwares) {
214             # skip variables
215 0 0         next if $rawGuid =~ m{^/};
216              
217             # only keep subkeys with more than 1 value
218 0           my $data = $softwares->{$rawGuid};
219 0 0         next unless keys %$data > 1;
220              
221 0           my $guid = $rawGuid;
222 0           $guid =~ s/\/$//; # drop the tailing /
223              
224 0 0 0       my $software = {
225             FROM => "registry",
226             NAME => encodeFromRegistry($data->{'/DisplayName'}) ||
227             encodeFromRegistry($guid), # folder name
228             COMMENTS => encodeFromRegistry($data->{'/Comments'}),
229             HELPLINK => encodeFromRegistry($data->{'/HelpLink'}),
230             RELEASE_TYPE => encodeFromRegistry($data->{'/ReleaseType'}),
231             VERSION => encodeFromRegistry($data->{'/DisplayVersion'}),
232             PUBLISHER => encodeFromRegistry($data->{'/Publisher'}),
233             URL_INFO_ABOUT => encodeFromRegistry($data->{'/URLInfoAbout'}),
234             UNINSTALL_STRING => encodeFromRegistry($data->{'/UninstallString'}),
235             INSTALLDATE => _dateFormat($data->{'/InstallDate'}),
236             VERSION_MINOR => hex2dec($data->{'/MinorVersion'}),
237             VERSION_MAJOR => hex2dec($data->{'/MajorVersion'}),
238             NO_REMOVE => hex2dec($data->{'/NoRemove'}),
239             ARCH => $params{is64bit} ? 'x86_64' : 'i586',
240             GUID => $guid,
241             USERNAME => $params{username},
242             USERID => $params{userid},
243             };
244              
245             # Workaround for #415
246 0 0         $software->{VERSION} =~ s/[\000-\037].*// if $software->{VERSION};
247              
248 0           push @list, $software;
249             }
250              
251 0           return \@list;
252             }
253              
254             sub _getHotfixesList {
255 0     0     my (%params) = @_;
256              
257 0           my $list;
258              
259 0           foreach my $object (getWMIObjects(
260             class => 'Win32_QuickFixEngineering',
261             properties => [ qw/HotFixID Description/ ]
262             )) {
263              
264 0           my $releaseType;
265 0 0 0       if ($object->{Description} && $object->{Description} =~ /^(Security Update|Hotfix|Update)/) {
266 0           $releaseType = $1;
267             }
268              
269 0 0         next unless $object->{HotFixID} =~ /KB(\d{4,10})/i;
270 0 0         push @$list, {
271             NAME => $object->{HotFixID},
272             COMMENTS => $object->{Description},
273             FROM => "WMI",
274             RELEASE_TYPE => $releaseType,
275             ARCH => $params{is64bit} ? 'x86_64' : 'i586'
276             };
277              
278             }
279              
280 0           return $list;
281             }
282              
283             sub _addSoftware {
284 0     0     my (%params) = @_;
285              
286 0           my $entry = $params{entry};
287              
288             # avoid duplicates
289 0 0 0       return if $seen->{$entry->{NAME}}->{$entry->{ARCH}}{$entry->{VERSION} || '_undef_'}++;
290              
291 0           $params{inventory}->addEntry(section => 'SOFTWARES', entry => $entry);
292             }
293              
294             sub _processMSIE {
295 0     0     my (%params) = @_;
296              
297 0 0         my $name = $params{is64bit} ?
298             "Internet Explorer (64bit)" : "Internet Explorer";
299 0   0       my $version =
300             $params{machKey}->{"SOFTWARE/Microsoft/Internet Explorer/svcVersion"} ||
301             $params{machKey}->{"SOFTWARE/Microsoft/Internet Explorer/Version"};
302              
303 0 0         return unless $version; # Not installed
304              
305 0 0         _addSoftware(
306             inventory => $params{inventory},
307             entry => {
308             FROM => "registry",
309             ARCH => $params{is64bit} ? 'x86_64' : 'i586',
310             NAME => $name,
311             VERSION => $version,
312             PUBLISHER => "Microsoft Corporation"
313             }
314             );
315              
316             }
317              
318             1;