1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Implementation of the installation validator.
7 #include "chrome/installer/util/installation_validator.h"
13 #include "base/logging.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/version.h"
17 #include "base/win/registry.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/installer/util/browser_distribution.h"
20 #include "chrome/installer/util/google_update_constants.h"
21 #include "chrome/installer/util/helper.h"
22 #include "chrome/installer/util/install_util.h"
23 #include "chrome/installer/util/installation_state.h"
24 #include "chrome/installer/util/updating_app_registration_data.h"
28 BrowserDistribution::Type
29 InstallationValidator::ChromeRules::distribution_type() const {
30 return BrowserDistribution::CHROME_BROWSER
;
33 void InstallationValidator::ChromeRules::AddUninstallSwitchExpectations(
34 const ProductContext
& ctx
,
35 SwitchExpectations
* expectations
) const {
36 // --chrome should be present for uninstall iff --multi-install. This wasn't
37 // the case in Chrome 10 (between r68996 and r72497), though, so consider it
41 void InstallationValidator::ChromeRules::AddRenameSwitchExpectations(
42 const ProductContext
& ctx
,
43 SwitchExpectations
* expectations
) const {
44 // --chrome should not be present for rename. It was for a time, so we'll be
45 // lenient so that mini_installer tests pass.
47 // --chrome-frame should never be present.
48 expectations
->push_back(
49 std::make_pair(std::string(switches::kChromeFrame
), false));
52 bool InstallationValidator::ChromeRules::UsageStatsAllowed(
53 const ProductContext
& ctx
) const {
54 // Products must not have usagestats consent values when multi-install
55 // (only the multi-install binaries may).
56 return !ctx
.state
.is_multi_install();
59 BrowserDistribution::Type
60 InstallationValidator::ChromeFrameRules::distribution_type() const {
61 return BrowserDistribution::CHROME_FRAME
;
64 void InstallationValidator::ChromeFrameRules::AddUninstallSwitchExpectations(
65 const ProductContext
& ctx
,
66 SwitchExpectations
* expectations
) const {
67 // --chrome-frame must be present.
68 expectations
->push_back(std::make_pair(std::string(switches::kChromeFrame
),
70 // --chrome must not be present.
71 expectations
->push_back(std::make_pair(std::string(switches::kChrome
),
75 void InstallationValidator::ChromeFrameRules::AddRenameSwitchExpectations(
76 const ProductContext
& ctx
,
77 SwitchExpectations
* expectations
) const {
78 // --chrome-frame must be present for SxS rename.
79 expectations
->push_back(std::make_pair(std::string(switches::kChromeFrame
),
80 !ctx
.state
.is_multi_install()));
81 // --chrome must not be present.
82 expectations
->push_back(std::make_pair(std::string(switches::kChrome
),
86 bool InstallationValidator::ChromeFrameRules::UsageStatsAllowed(
87 const ProductContext
& ctx
) const {
88 // Products must not have usagestats consent values when multi-install
89 // (only the multi-install binaries may).
90 return !ctx
.state
.is_multi_install();
93 BrowserDistribution::Type
94 InstallationValidator::ChromeBinariesRules::distribution_type() const {
95 return BrowserDistribution::CHROME_BINARIES
;
98 void InstallationValidator::ChromeBinariesRules::AddUninstallSwitchExpectations(
99 const ProductContext
& ctx
,
100 SwitchExpectations
* expectations
) const {
104 void InstallationValidator::ChromeBinariesRules::AddRenameSwitchExpectations(
105 const ProductContext
& ctx
,
106 SwitchExpectations
* expectations
) const {
110 bool InstallationValidator::ChromeBinariesRules::UsageStatsAllowed(
111 const ProductContext
& ctx
) const {
112 // UsageStats consent values are always allowed on the binaries.
117 const InstallationValidator::InstallationType
118 InstallationValidator::kInstallationTypes
[] = {
123 CHROME_FRAME_SINGLE_CHROME_SINGLE
,
124 CHROME_FRAME_SINGLE_CHROME_MULTI
,
126 CHROME_FRAME_MULTI_CHROME_MULTI
,
129 void InstallationValidator::ValidateAppCommandFlags(
130 const ProductContext
& ctx
,
131 const AppCommand
& app_cmd
,
132 const std::set
<base::string16
>& flags_exp
,
133 const base::string16
& name
,
136 const base::string16 exp_key
;
140 {google_update::kRegSendsPingsField
,
141 app_cmd
.sends_pings(),
142 "be configured to send pings"},
143 {google_update::kRegWebAccessibleField
,
144 app_cmd
.is_web_accessible(),
145 "be web accessible"},
146 {google_update::kRegAutoRunOnOSUpgradeField
,
147 app_cmd
.is_auto_run_on_os_upgrade(),
148 "be marked to run on OS upgrade"},
149 {google_update::kRegRunAsUserField
,
150 app_cmd
.is_run_as_user(),
151 "be marked to run as user"},
153 for (int i
= 0; i
< arraysize(check_list
); ++i
) {
154 bool expected
= flags_exp
.find(check_list
[i
].exp_key
) != flags_exp
.end();
155 if (check_list
[i
].val
!= expected
) {
157 LOG(ERROR
) << ctx
.dist
->GetDisplayName() << ": "
158 << name
<< " command should " << (expected
? "" : "not ")
159 << check_list
[i
].msg
<< ".";
164 // Validates the "on-os-upgrade" Google Update internal command.
165 void InstallationValidator::ValidateOnOsUpgradeCommand(
166 const ProductContext
& ctx
,
167 const AppCommand
& app_cmd
,
171 base::CommandLine
cmd_line(
172 base::CommandLine::FromString(app_cmd
.command_line()));
173 base::string16
name(kCmdOnOsUpgrade
);
175 ValidateSetupPath(ctx
, cmd_line
.GetProgram(), name
, is_valid
);
177 SwitchExpectations expected
;
178 expected
.push_back(std::make_pair(std::string(switches::kOnOsUpgrade
), true));
179 expected
.push_back(std::make_pair(std::string(switches::kSystemLevel
),
180 ctx
.system_install
));
181 expected
.push_back(std::make_pair(std::string(switches::kMultiInstall
),
182 ctx
.state
.is_multi_install()));
183 // Expecting kChrome if and only if kMultiInstall.
184 expected
.push_back(std::make_pair(std::string(switches::kChrome
),
185 ctx
.state
.is_multi_install()));
187 ValidateCommandExpectations(ctx
, cmd_line
, expected
, name
, is_valid
);
189 std::set
<base::string16
> flags_exp
;
190 flags_exp
.insert(google_update::kRegAutoRunOnOSUpgradeField
);
191 ValidateAppCommandFlags(ctx
, app_cmd
, flags_exp
, name
, is_valid
);
194 // Validates a product's set of Google Update product commands against a
195 // collection of expectations.
196 void InstallationValidator::ValidateAppCommandExpectations(
197 const ProductContext
& ctx
,
198 const CommandExpectations
& expectations
,
202 CommandExpectations
the_expectations(expectations
);
204 AppCommands::CommandMapRange
cmd_iterators(
205 ctx
.state
.commands().GetIterators());
206 CommandExpectations::iterator expectation
;
207 for (; cmd_iterators
.first
!= cmd_iterators
.second
; ++cmd_iterators
.first
) {
208 const base::string16
& cmd_id
= cmd_iterators
.first
->first
;
209 // Do we have an expectation for this command?
210 expectation
= the_expectations
.find(cmd_id
);
211 if (expectation
!= the_expectations
.end()) {
212 (expectation
->second
)(ctx
, cmd_iterators
.first
->second
, is_valid
);
213 // Remove this command from the set of expectations since we found it.
214 the_expectations
.erase(expectation
);
217 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
218 << " has an unexpected Google Update product command named \""
223 // Report on any expected commands that weren't present.
224 CommandExpectations::const_iterator
scan(the_expectations
.begin());
225 CommandExpectations::const_iterator
end(the_expectations
.end());
226 for (; scan
!= end
; ++scan
) {
228 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
229 << " is missing the Google Update product command named \""
230 << scan
->first
<< "\".";
234 // Validates the multi-install binaries at level |system_level|.
235 void InstallationValidator::ValidateBinaries(
236 const InstallationState
& machine_state
,
238 const ProductState
& binaries_state
,
240 const ChannelInfo
& channel
= binaries_state
.channel();
242 // ap must have -multi
243 if (!channel
.IsMultiInstall()) {
245 LOG(ERROR
) << "Chrome Binaries are missing \"-multi\" in channel name: \""
246 << channel
.value() << "\"";
249 // ap must have -chrome iff Chrome is installed
250 const ProductState
* chrome_state
= machine_state
.GetProductState(
251 system_install
, BrowserDistribution::CHROME_BROWSER
);
252 if (chrome_state
!= NULL
) {
253 if (!channel
.IsChrome()) {
255 LOG(ERROR
) << "Chrome Binaries are missing \"chrome\" in channel name:"
256 << " \"" << channel
.value() << "\"";
258 } else if (channel
.IsChrome()) {
260 LOG(ERROR
) << "Chrome Binaries have \"-chrome\" in channel name, yet Chrome"
261 " is not installed: \"" << channel
.value() << "\"";
264 // ap must have -chromeframe iff Chrome Frame is installed multi
265 const ProductState
* cf_state
= machine_state
.GetProductState(
266 system_install
, BrowserDistribution::CHROME_FRAME
);
267 if (cf_state
!= NULL
&& cf_state
->is_multi_install()) {
268 if (!channel
.IsChromeFrame()) {
270 LOG(ERROR
) << "Chrome Binaries are missing \"-chromeframe\" in channel"
271 " name: \"" << channel
.value() << "\"";
273 } else if (channel
.IsChromeFrame()) {
275 LOG(ERROR
) << "Chrome Binaries have \"-chromeframe\" in channel name, yet "
276 "Chrome Frame is not installed multi: \"" << channel
.value()
280 // Chrome or Chrome Frame must be present
281 if (chrome_state
== NULL
&& cf_state
== NULL
) {
283 LOG(ERROR
) << "Chrome Binaries are present with no other products.";
287 // Chrome must be multi-install if present.
288 if (chrome_state
!= NULL
&& !chrome_state
->is_multi_install()) {
291 << "Chrome Binaries are present yet Chrome is not multi-install.";
294 // Chrome Frame must be multi-install if Chrome is not present.
295 if (cf_state
!= NULL
&& chrome_state
== NULL
&&
296 !cf_state
->is_multi_install()) {
298 LOG(ERROR
) << "Chrome Binaries are present without Chrome, yet Chrome Frame"
299 << " is not multi-install.";
302 ChromeBinariesRules binaries_rules
;
303 ProductContext
ctx(machine_state
, system_install
, binaries_state
,
306 ValidateUsageStats(ctx
, is_valid
);
309 // Validates the path to |setup_exe| for the product described by |ctx|.
310 void InstallationValidator::ValidateSetupPath(const ProductContext
& ctx
,
311 const base::FilePath
& setup_exe
,
312 const base::string16
& purpose
,
316 BrowserDistribution
* bins_dist
= ctx
.dist
;
317 if (ctx
.state
.is_multi_install()) {
318 bins_dist
= BrowserDistribution::GetSpecificDistribution(
319 BrowserDistribution::CHROME_BINARIES
);
322 base::FilePath expected_path
= installer::GetChromeInstallPath(
323 ctx
.system_install
, bins_dist
);
324 expected_path
= expected_path
325 .AppendASCII(ctx
.state
.version().GetString())
326 .Append(installer::kInstallerDir
)
327 .Append(installer::kSetupExe
);
328 if (!base::FilePath::CompareEqualIgnoreCase(expected_path
.value(),
329 setup_exe
.value())) {
331 LOG(ERROR
) << ctx
.dist
->GetDisplayName() << " path to " << purpose
332 << " is not " << expected_path
.value() << ": "
333 << setup_exe
.value();
337 // Validates that |command| meets the expectations described in |expected|.
338 void InstallationValidator::ValidateCommandExpectations(
339 const ProductContext
& ctx
,
340 const base::CommandLine
& command
,
341 const SwitchExpectations
& expected
,
342 const base::string16
& source
,
344 for (SwitchExpectations::size_type i
= 0, size
= expected
.size(); i
< size
;
346 const SwitchExpectations::value_type
& expectation
= expected
[i
];
347 if (command
.HasSwitch(expectation
.first
) != expectation
.second
) {
349 LOG(ERROR
) << ctx
.dist
->GetDisplayName() << " " << source
350 << (expectation
.second
? " is missing" : " has") << " \""
351 << expectation
.first
<< "\""
352 << (expectation
.second
? "" : " but shouldn't") << ": "
353 << command
.GetCommandLineString();
358 // Validates that |command|, originating from |source|, is formed properly for
359 // the product described by |ctx|
360 void InstallationValidator::ValidateUninstallCommand(
361 const ProductContext
& ctx
,
362 const base::CommandLine
& command
,
363 const base::string16
& source
,
367 ValidateSetupPath(ctx
, command
.GetProgram(),
368 base::ASCIIToUTF16("uninstaller"),
371 const bool is_multi_install
= ctx
.state
.is_multi_install();
372 SwitchExpectations expected
;
374 expected
.push_back(std::make_pair(std::string(switches::kUninstall
), true));
375 expected
.push_back(std::make_pair(std::string(switches::kSystemLevel
),
376 ctx
.system_install
));
377 expected
.push_back(std::make_pair(std::string(switches::kMultiInstall
),
379 ctx
.rules
.AddUninstallSwitchExpectations(ctx
, &expected
);
381 ValidateCommandExpectations(ctx
, command
, expected
, source
, is_valid
);
384 // Validates the rename command for the product described by |ctx|.
385 void InstallationValidator::ValidateRenameCommand(const ProductContext
& ctx
,
388 DCHECK(!ctx
.state
.rename_cmd().empty());
390 base::CommandLine command
=
391 base::CommandLine::FromString(ctx
.state
.rename_cmd());
392 base::string16
name(base::ASCIIToUTF16("in-use renamer"));
394 ValidateSetupPath(ctx
, command
.GetProgram(), name
, is_valid
);
396 SwitchExpectations expected
;
398 expected
.push_back(std::make_pair(std::string(switches::kRenameChromeExe
),
400 expected
.push_back(std::make_pair(std::string(switches::kSystemLevel
),
401 ctx
.system_install
));
402 expected
.push_back(std::make_pair(std::string(switches::kMultiInstall
),
403 ctx
.state
.is_multi_install()));
404 ctx
.rules
.AddRenameSwitchExpectations(ctx
, &expected
);
406 ValidateCommandExpectations(ctx
, command
, expected
, name
, is_valid
);
409 // Validates the "opv" and "cmd" values for the product described in |ctx|.
410 void InstallationValidator::ValidateOldVersionValues(
411 const ProductContext
& ctx
,
415 // opv and cmd must both be present or both absent
416 if (ctx
.state
.old_version() == NULL
) {
417 if (!ctx
.state
.rename_cmd().empty()) {
419 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
420 << " has a rename command but no opv: "
421 << ctx
.state
.rename_cmd();
424 if (ctx
.state
.rename_cmd().empty()) {
426 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
427 << " has an opv but no rename command: "
428 << ctx
.state
.old_version()->GetString();
430 ValidateRenameCommand(ctx
, is_valid
);
435 // Validates the multi-install state of the product described in |ctx|.
436 void InstallationValidator::ValidateMultiInstallProduct(
437 const ProductContext
& ctx
,
441 const ProductState
* binaries
=
442 ctx
.machine_state
.GetProductState(ctx
.system_install
,
443 BrowserDistribution::CHROME_BINARIES
);
446 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
447 << " (" << ctx
.state
.version().GetString() << ") is installed "
448 << "without Chrome Binaries.";
450 // Version must match that of binaries.
451 if (ctx
.state
.version().CompareTo(binaries
->version()) != 0) {
453 LOG(ERROR
) << "Version of " << ctx
.dist
->GetDisplayName()
454 << " (" << ctx
.state
.version().GetString() << ") does not "
455 "match that of Chrome Binaries ("
456 << binaries
->version().GetString() << ").";
459 // Channel value must match that of binaries.
460 if (!ctx
.state
.channel().Equals(binaries
->channel())) {
462 LOG(ERROR
) << "Channel name of " << ctx
.dist
->GetDisplayName()
463 << " (" << ctx
.state
.channel().value()
464 << ") does not match that of Chrome Binaries ("
465 << binaries
->channel().value() << ").";
470 // Validates the Google Update commands for the product described in |ctx|.
471 void InstallationValidator::ValidateAppCommands(
472 const ProductContext
& ctx
,
476 CommandExpectations expectations
;
478 if (ctx
.dist
->GetType() == BrowserDistribution::CHROME_BROWSER
)
479 expectations
[kCmdOnOsUpgrade
] = &ValidateOnOsUpgradeCommand
;
481 ValidateAppCommandExpectations(ctx
, expectations
, is_valid
);
484 // Validates usagestats for the product or binaries in |ctx|.
485 void InstallationValidator::ValidateUsageStats(const ProductContext
& ctx
,
487 DWORD usagestats
= 0;
488 if (ctx
.state
.GetUsageStats(&usagestats
)) {
489 if (!ctx
.rules
.UsageStatsAllowed(ctx
)) {
491 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
492 << " has a usagestats value (" << usagestats
493 << "), yet should not.";
494 } else if (usagestats
!= 0 && usagestats
!= 1) {
496 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
497 << " has an unsupported usagestats value (" << usagestats
503 // Validates the product described in |product_state| according to |rules|.
504 void InstallationValidator::ValidateProduct(
505 const InstallationState
& machine_state
,
507 const ProductState
& product_state
,
508 const ProductRules
& rules
,
512 ProductContext
ctx(machine_state
, system_install
, product_state
, rules
);
514 ValidateUninstallCommand(ctx
, ctx
.state
.uninstall_command(),
516 "Google Update uninstall command"),
519 ValidateOldVersionValues(ctx
, is_valid
);
521 if (ctx
.state
.is_multi_install())
522 ValidateMultiInstallProduct(ctx
, is_valid
);
524 ValidateAppCommands(ctx
, is_valid
);
526 ValidateUsageStats(ctx
, is_valid
);
530 bool InstallationValidator::ValidateInstallationTypeForState(
531 const InstallationState
& machine_state
,
533 InstallationType
* type
) {
538 // Does the system have any multi-installed products?
539 const ProductState
* multi_state
=
540 machine_state
.GetProductState(system_level
,
541 BrowserDistribution::CHROME_BINARIES
);
542 if (multi_state
!= NULL
)
543 ValidateBinaries(machine_state
, system_level
, *multi_state
, &rock_on
);
545 // Is Chrome installed?
546 const ProductState
* product_state
=
547 machine_state
.GetProductState(system_level
,
548 BrowserDistribution::CHROME_BROWSER
);
549 if (product_state
!= NULL
) {
550 ChromeRules chrome_rules
;
551 ValidateProduct(machine_state
, system_level
, *product_state
,
552 chrome_rules
, &rock_on
);
553 *type
= static_cast<InstallationType
>(
554 *type
| (product_state
->is_multi_install() ?
555 ProductBits::CHROME_MULTI
:
556 ProductBits::CHROME_SINGLE
));
559 #if defined(GOOGLE_CHROME_BUILD)
560 if (!InstallUtil::IsChromeSxSProcess()) {
561 // Usage of the app launcher is tracked alongside Chrome.
562 const HKEY root_key
= system_level
? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
563 base::string16
launcher_key(
564 UpdatingAppRegistrationData(kAppLauncherGuid
).GetVersionKey());
565 base::win::RegKey
key(root_key
, launcher_key
.c_str(),
566 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
);
568 key
.Valid() && key
.HasValue(google_update::kRegVersionField
);
570 BrowserDistribution
* chrome_dist
=
571 BrowserDistribution::GetSpecificDistribution(
572 BrowserDistribution::CHROME_BROWSER
);
573 if (!have_launcher
) {
574 LOG_IF(ERROR
, product_state
) << "App launcher is not installed with "
575 << chrome_dist
->GetDisplayName();
577 LOG_IF(ERROR
, !product_state
) << "App launcher is installed without "
578 << chrome_dist
->GetDisplayName();
581 #endif // GOOGLE_CHROME_BUILD
583 // Is Chrome Frame installed?
585 machine_state
.GetProductState(system_level
,
586 BrowserDistribution::CHROME_FRAME
);
587 if (product_state
!= NULL
) {
588 ChromeFrameRules chrome_frame_rules
;
589 ValidateProduct(machine_state
, system_level
, *product_state
,
590 chrome_frame_rules
, &rock_on
);
591 int cf_bit
= !product_state
->is_multi_install() ?
592 ProductBits::CHROME_FRAME_SINGLE
:
593 ProductBits::CHROME_FRAME_MULTI
;
594 *type
= static_cast<InstallationType
>(*type
| cf_bit
);
597 DCHECK_NE(std::find(&kInstallationTypes
[0],
598 &kInstallationTypes
[arraysize(kInstallationTypes
)],
600 &kInstallationTypes
[arraysize(kInstallationTypes
)])
601 << "Invalid combination of products found on system (" << *type
<< ")";
607 bool InstallationValidator::ValidateInstallationType(bool system_level
,
608 InstallationType
* type
) {
610 InstallationState machine_state
;
612 machine_state
.Initialize();
614 return ValidateInstallationTypeForState(machine_state
, system_level
, type
);
617 } // namespace installer