File Coverage

blib/lib/FusionInventory/Agent/Task/Inventory/Win32/Softwares.pm
Criterion Covered Total %
statement 49 117 41.8
branch 17 60 28.3
condition 5 11 45.4
subroutine 10 15 66.6
pod 0 2 0.0
total 81 205 39.5


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Task::Inventory::Win32::Softwares;
2              
3 2     2   65603323 use strict;
  2         7  
  2         132  
4 2     2   31 use warnings;
  2         14  
  2         171  
5              
6 2     2   14 use English qw(-no_match_vars);
  2         131  
  2         48  
7             use Win32::TieRegistry (
8 2         15 Delimiter => '/',
9             ArrayValues => 0,
10             qw/KEY_READ/
11 2     2   4018 );
  2         579  
12 2     2   140 use File::Basename;
  2         5  
  2         339  
13              
14 2     2   1045 use FusionInventory::Agent::Tools;
  2         5  
  2         402  
15 2     2   1230 use FusionInventory::Agent::Tools::Win32;
  2         10  
  2         3844  
16              
17             my $seen = {};
18              
19             sub isEnabled {
20 0     0 0 0 my (%params) = @_;
21              
22 0         0 return !$params{no_category}->{software};
23             }
24              
25             sub doInventory {
26 0     0 0 0 my (%params) = @_;
27              
28 0         0 my $inventory = $params{inventory};
29 0         0 my $logger = $params{logger};
30              
31 0         0 my $is64bit = is64bit();
32              
33              
34 0 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       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             my $softwaresKey64 =
44 0         0 $machKey64->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
45 0         0 my $softwares64 =_getSoftwaresList(
46             softwares => $softwaresKey64,
47             is64bit => 1,
48             );
49 0         0 foreach my $software (@$softwares64) {
50 0         0 _addSoftware(inventory => $inventory, entry => $software);
51             }
52             _processMSIE(
53 0         0 machKey => $machKey64,
54             inventory => $inventory,
55             is64bit => 1
56             );
57              
58 0 0       0 if ($params{scan_profiles}) {
59 0         0 _loadUserSoftware(
60             inventory => $inventory,
61             is64bit => 1,
62             logger => $logger
63             );
64             } else {
65 0         0 $logger->warning(
66             "'scan-profiles' configuration parameter disabled, " .
67             "ignoring software in user profiles"
68             );
69             }
70              
71 0 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             my $softwaresKey32 =
75 0         0 $machKey32->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
76 0         0 my $softwares32 = _getSoftwaresList(
77             softwares => $softwaresKey32,
78             is64bit => 0,
79             logger => $logger,
80             );
81 0         0 foreach my $software (@$softwares32) {
82 0         0 _addSoftware(inventory => $inventory, entry => $software);
83             }
84             _processMSIE(
85 0         0 machKey => $machKey32,
86             inventory => $inventory,
87             is64bit => 0
88             );
89             _loadUserSoftware(
90             inventory => $inventory,
91             is64bit => 0,
92             logger => $logger
93 0 0       0 ) if $params{scan_profiles};
94             } else {
95 0 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             my $softwaresKey =
99 0         0 $machKey->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
100 0         0 my $softwares = _getSoftwaresList(
101             softwares => $softwaresKey,
102             is64bit => 0,
103             );
104 0         0 foreach my $software (@$softwares) {
105 0         0 _addSoftware(inventory => $inventory, entry => $software);
106             }
107             _processMSIE(
108 0         0 machKey => $machKey,
109             inventory => $inventory,
110             is64bit => 0
111             );
112             _loadUserSoftware(
113             inventory => $inventory,
114             is64bit => 0,
115             logger => $logger
116 0 0       0 ) if $params{scan_profiles};
117              
118             }
119              
120 0         0 my $hotfixes = _getHotfixesList(is64bit => $is64bit);
121 0         0 foreach my $hotfix (@$hotfixes) {
122             # skip fixes already found in generic software list,
123             # without checking version information
124 0 0       0 next if $seen->{$hotfix->{NAME}};
125 0         0 _addSoftware(inventory => $inventory, entry => $hotfix);
126             }
127              
128             }
129              
130             sub _loadUserSoftware {
131 0     0   0 my (%params) = @_;
132              
133 0         0 my $inventory = $params{inventory};
134 0         0 my $is64bit = $params{is64bit};
135 0         0 my $logger = $params{logger};
136              
137 0 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             my $profileList =
142 0         0 $machKey->{"SOFTWARE/Microsoft/Windows NT/CurrentVersion/ProfileList"};
143              
144 0 0       0 return unless $profileList;
145              
146 0         0 $Registry->AllowLoad(1);
147              
148 0         0 foreach my $profileName (keys %$profileList) {
149             # we're only interested in subkeys
150 0 0       0 next unless $profileName =~ m{/$};
151 0 0       0 next unless length($profileName) > 10;
152              
153 0         0 my $profilePath = $profileList->{$profileName}{'/ProfileImagePath'};
154 0         0 my $sid = $profileList->{$profileName}{'/Sid'};
155              
156 0 0       0 next unless $sid;
157 0 0       0 next unless $profilePath;
158              
159 0         0 $profilePath =~ s/%SystemDrive%/$ENV{SYSTEMDRIVE}/i;
160              
161 0         0 my $user = basename($profilePath);
162             ## no critic (ProhibitBitwise)
163 0 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             my $softwaresKey =
168 0         0 $userKey->{"SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall"};
169              
170 0         0 my $softwares = _getSoftwaresList(
171             softwares => $softwaresKey,
172             is64bit => $is64bit,
173             userid => $sid,
174             username => $user
175             );
176 0         0 foreach my $software (@$softwares) {
177 0         0 _addSoftware(inventory => $inventory, entry => $software);
178             }
179              
180             }
181 0         0 $Registry->AllowLoad(0);
182              
183             }
184              
185              
186              
187             sub _dateFormat {
188 425     425   136859 my ($date) = @_;
189              
190             ## no critic (ExplicitReturnUndef)
191 425 100       1196 return undef unless $date;
192              
193 324 50       1112 if ($date =~ /^(\d{4})(\d{1})(\d{2})$/) {
194 0         0 return "$3/0$2/$1";
195             }
196              
197 324 50       1038 if ($date =~ /^(\d{4})(\d{2})(\d{2})$/) {
198 324         2090 return "$3/$2/$1";
199             }
200              
201 0         0 return undef;
202             }
203              
204             sub _getSoftwaresList {
205 1     1   112989 my (%params) = @_;
206              
207 1         4 my $softwares = $params{softwares};
208              
209 1         2 my @list;
210              
211 1 50       7 return unless $softwares;
212              
213 1         217 foreach my $rawGuid (keys %$softwares) {
214             # skip variables
215 535 100       1235 next if $rawGuid =~ m{^/};
216              
217             # only keep subkeys with more than 1 value
218 534         1458 my $data = $softwares->{$rawGuid};
219 534 100       1733 next unless keys %$data > 1;
220              
221 425         672 my $guid = $rawGuid;
222 425         1675 $guid =~ s/\/$//; # drop the tailing /
223              
224             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 425 50 66     1397 };
244              
245             # Workaround for #415
246 425 100       2512 $software->{VERSION} =~ s/[\000-\037].*// if $software->{VERSION};
247              
248 425         1011 push @list, $software;
249             }
250              
251 1         43 return \@list;
252             }
253              
254             sub _getHotfixesList {
255 1     1   1938 my (%params) = @_;
256              
257 1         3 my $list;
258              
259 1         6 foreach my $object (getWMIObjects(
260             class => 'Win32_QuickFixEngineering',
261             properties => [ qw/HotFixID Description/ ]
262             )) {
263              
264 16         1920 my $releaseType;
265 16 100 100     84 if ($object->{Description} && $object->{Description} =~ /^(Security Update|Hotfix|Update)/) {
266 10         22 $releaseType = $1;
267             }
268              
269 16 100       79 next unless $object->{HotFixID} =~ /KB(\d{4,10})/i;
270             push @$list, {
271             NAME => $object->{HotFixID},
272             COMMENTS => $object->{Description},
273             FROM => "WMI",
274             RELEASE_TYPE => $releaseType,
275 10 50       61 ARCH => $params{is64bit} ? 'x86_64' : 'i586'
276             };
277              
278             }
279              
280 1         11 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             my $name = $params{is64bit} ?
298 0 0         "Internet Explorer (64bit)" : "Internet Explorer";
299             my $version =
300             $params{machKey}->{"SOFTWARE/Microsoft/Internet Explorer/svcVersion"} ||
301 0   0       $params{machKey}->{"SOFTWARE/Microsoft/Internet Explorer/Version"};
302              
303 0 0         return unless $version; # Not installed
304              
305             _addSoftware(
306             inventory => $params{inventory},
307             entry => {
308             FROM => "registry",
309 0 0         ARCH => $params{is64bit} ? 'x86_64' : 'i586',
310             NAME => $name,
311             VERSION => $version,
312             PUBLISHER => "Microsoft Corporation"
313             }
314             );
315              
316             }
317              
318             1;