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/utf_string_conversions.h"
15 #include "base/version.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/installer/util/browser_distribution.h"
18 #include "chrome/installer/util/google_update_constants.h"
19 #include "chrome/installer/util/helper.h"
20 #include "chrome/installer/util/installation_state.h"
24 BrowserDistribution::Type
25 InstallationValidator::ChromeRules::distribution_type() const {
26 return BrowserDistribution::CHROME_BROWSER
;
29 void InstallationValidator::ChromeRules::AddUninstallSwitchExpectations(
30 const ProductContext
& ctx
,
31 SwitchExpectations
* expectations
) const {
32 // --chrome should be present for uninstall iff --multi-install. This wasn't
33 // the case in Chrome 10 (between r68996 and r72497), though, so consider it
37 void InstallationValidator::ChromeRules::AddRenameSwitchExpectations(
38 const ProductContext
& ctx
,
39 SwitchExpectations
* expectations
) const {
40 // --chrome should not be present for rename. It was for a time, so we'll be
41 // lenient so that mini_installer tests pass.
43 // --chrome-frame should never be present.
44 expectations
->push_back(
45 std::make_pair(std::string(switches::kChromeFrame
), false));
48 bool InstallationValidator::ChromeRules::UsageStatsAllowed(
49 const ProductContext
& ctx
) const {
50 // Products must not have usagestats consent values when multi-install
51 // (only the multi-install binaries may).
52 return !ctx
.state
.is_multi_install();
55 BrowserDistribution::Type
56 InstallationValidator::ChromeFrameRules::distribution_type() const {
57 return BrowserDistribution::CHROME_FRAME
;
60 void InstallationValidator::ChromeFrameRules::AddUninstallSwitchExpectations(
61 const ProductContext
& ctx
,
62 SwitchExpectations
* expectations
) const {
63 // --chrome-frame must be present.
64 expectations
->push_back(std::make_pair(std::string(switches::kChromeFrame
),
66 // --chrome must not be present.
67 expectations
->push_back(std::make_pair(std::string(switches::kChrome
),
71 void InstallationValidator::ChromeFrameRules::AddRenameSwitchExpectations(
72 const ProductContext
& ctx
,
73 SwitchExpectations
* expectations
) const {
74 // --chrome-frame must be present for SxS rename.
75 expectations
->push_back(std::make_pair(std::string(switches::kChromeFrame
),
76 !ctx
.state
.is_multi_install()));
77 // --chrome must not be present.
78 expectations
->push_back(std::make_pair(std::string(switches::kChrome
),
82 bool InstallationValidator::ChromeFrameRules::UsageStatsAllowed(
83 const ProductContext
& ctx
) const {
84 // Products must not have usagestats consent values when multi-install
85 // (only the multi-install binaries may).
86 return !ctx
.state
.is_multi_install();
89 BrowserDistribution::Type
90 InstallationValidator::ChromeBinariesRules::distribution_type() const {
91 return BrowserDistribution::CHROME_BINARIES
;
94 void InstallationValidator::ChromeBinariesRules::AddUninstallSwitchExpectations(
95 const ProductContext
& ctx
,
96 SwitchExpectations
* expectations
) const {
100 void InstallationValidator::ChromeBinariesRules::AddRenameSwitchExpectations(
101 const ProductContext
& ctx
,
102 SwitchExpectations
* expectations
) const {
106 bool InstallationValidator::ChromeBinariesRules::UsageStatsAllowed(
107 const ProductContext
& ctx
) const {
108 // UsageStats consent values are always allowed on the binaries.
113 const InstallationValidator::InstallationType
114 InstallationValidator::kInstallationTypes
[] = {
119 CHROME_FRAME_SINGLE_CHROME_SINGLE
,
120 CHROME_FRAME_SINGLE_CHROME_MULTI
,
122 CHROME_FRAME_MULTI_CHROME_MULTI
,
125 void InstallationValidator::ValidateAppCommandFlags(
126 const ProductContext
& ctx
,
127 const AppCommand
& app_cmd
,
128 const std::set
<base::string16
>& flags_exp
,
129 const base::string16
& name
,
132 const base::string16 exp_key
;
136 {google_update::kRegSendsPingsField
,
137 app_cmd
.sends_pings(),
138 "be configured to send pings"},
139 {google_update::kRegWebAccessibleField
,
140 app_cmd
.is_web_accessible(),
141 "be web accessible"},
142 {google_update::kRegAutoRunOnOSUpgradeField
,
143 app_cmd
.is_auto_run_on_os_upgrade(),
144 "be marked to run on OS upgrade"},
145 {google_update::kRegRunAsUserField
,
146 app_cmd
.is_run_as_user(),
147 "be marked to run as user"},
149 for (int i
= 0; i
< arraysize(check_list
); ++i
) {
150 bool expected
= flags_exp
.find(check_list
[i
].exp_key
) != flags_exp
.end();
151 if (check_list
[i
].val
!= expected
) {
153 LOG(ERROR
) << ctx
.dist
->GetDisplayName() << ": "
154 << name
<< " command should " << (expected
? "" : "not ")
155 << check_list
[i
].msg
<< ".";
160 // Validates the "on-os-upgrade" Google Update internal command.
161 void InstallationValidator::ValidateOnOsUpgradeCommand(
162 const ProductContext
& ctx
,
163 const AppCommand
& app_cmd
,
167 base::CommandLine
cmd_line(
168 base::CommandLine::FromString(app_cmd
.command_line()));
169 base::string16
name(kCmdOnOsUpgrade
);
171 ValidateSetupPath(ctx
, cmd_line
.GetProgram(), name
, is_valid
);
173 SwitchExpectations expected
;
174 expected
.push_back(std::make_pair(std::string(switches::kOnOsUpgrade
), true));
175 expected
.push_back(std::make_pair(std::string(switches::kSystemLevel
),
176 ctx
.system_install
));
177 expected
.push_back(std::make_pair(std::string(switches::kMultiInstall
),
178 ctx
.state
.is_multi_install()));
179 // Expecting kChrome if and only if kMultiInstall.
180 expected
.push_back(std::make_pair(std::string(switches::kChrome
),
181 ctx
.state
.is_multi_install()));
183 ValidateCommandExpectations(ctx
, cmd_line
, expected
, name
, is_valid
);
185 std::set
<base::string16
> flags_exp
;
186 flags_exp
.insert(google_update::kRegAutoRunOnOSUpgradeField
);
187 ValidateAppCommandFlags(ctx
, app_cmd
, flags_exp
, name
, is_valid
);
190 // Validates a product's set of Google Update product commands against a
191 // collection of expectations.
192 void InstallationValidator::ValidateAppCommandExpectations(
193 const ProductContext
& ctx
,
194 const CommandExpectations
& expectations
,
198 CommandExpectations
the_expectations(expectations
);
200 AppCommands::CommandMapRange
cmd_iterators(
201 ctx
.state
.commands().GetIterators());
202 CommandExpectations::iterator expectation
;
203 for (; cmd_iterators
.first
!= cmd_iterators
.second
; ++cmd_iterators
.first
) {
204 const base::string16
& cmd_id
= cmd_iterators
.first
->first
;
205 // Do we have an expectation for this command?
206 expectation
= the_expectations
.find(cmd_id
);
207 if (expectation
!= the_expectations
.end()) {
208 (expectation
->second
)(ctx
, cmd_iterators
.first
->second
, is_valid
);
209 // Remove this command from the set of expectations since we found it.
210 the_expectations
.erase(expectation
);
213 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
214 << " has an unexpected Google Update product command named \""
219 // Report on any expected commands that weren't present.
220 CommandExpectations::const_iterator
scan(the_expectations
.begin());
221 CommandExpectations::const_iterator
end(the_expectations
.end());
222 for (; scan
!= end
; ++scan
) {
224 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
225 << " is missing the Google Update product command named \""
226 << scan
->first
<< "\".";
230 // Validates the multi-install binaries at level |system_level|.
231 void InstallationValidator::ValidateBinaries(
232 const InstallationState
& machine_state
,
234 const ProductState
& binaries_state
,
236 const ChannelInfo
& channel
= binaries_state
.channel();
238 // ap must have -multi
239 if (!channel
.IsMultiInstall()) {
241 LOG(ERROR
) << "Chrome Binaries are missing \"-multi\" in channel name: \""
242 << channel
.value() << "\"";
245 // ap must have -chrome iff Chrome is installed
246 const ProductState
* chrome_state
= machine_state
.GetProductState(
247 system_install
, BrowserDistribution::CHROME_BROWSER
);
248 if (chrome_state
!= NULL
) {
249 if (!channel
.IsChrome()) {
251 LOG(ERROR
) << "Chrome Binaries are missing \"chrome\" in channel name:"
252 << " \"" << channel
.value() << "\"";
254 } else if (channel
.IsChrome()) {
256 LOG(ERROR
) << "Chrome Binaries have \"-chrome\" in channel name, yet Chrome"
257 " is not installed: \"" << channel
.value() << "\"";
260 // ap must have -chromeframe iff Chrome Frame is installed multi
261 const ProductState
* cf_state
= machine_state
.GetProductState(
262 system_install
, BrowserDistribution::CHROME_FRAME
);
263 if (cf_state
!= NULL
&& cf_state
->is_multi_install()) {
264 if (!channel
.IsChromeFrame()) {
266 LOG(ERROR
) << "Chrome Binaries are missing \"-chromeframe\" in channel"
267 " name: \"" << channel
.value() << "\"";
269 } else if (channel
.IsChromeFrame()) {
271 LOG(ERROR
) << "Chrome Binaries have \"-chromeframe\" in channel name, yet "
272 "Chrome Frame is not installed multi: \"" << channel
.value()
276 // Chrome or Chrome Frame must be present
277 if (chrome_state
== NULL
&& cf_state
== NULL
) {
279 LOG(ERROR
) << "Chrome Binaries are present with no other products.";
283 // Chrome must be multi-install if present.
284 if (chrome_state
!= NULL
&& !chrome_state
->is_multi_install()) {
287 << "Chrome Binaries are present yet Chrome is not multi-install.";
290 // Chrome Frame must be multi-install if Chrome is not present.
291 if (cf_state
!= NULL
&& chrome_state
== NULL
&&
292 !cf_state
->is_multi_install()) {
294 LOG(ERROR
) << "Chrome Binaries are present without Chrome, yet Chrome Frame"
295 << " is not multi-install.";
298 ChromeBinariesRules binaries_rules
;
299 ProductContext
ctx(machine_state
, system_install
, binaries_state
,
302 ValidateUsageStats(ctx
, is_valid
);
305 // Validates the path to |setup_exe| for the product described by |ctx|.
306 void InstallationValidator::ValidateSetupPath(const ProductContext
& ctx
,
307 const base::FilePath
& setup_exe
,
308 const base::string16
& purpose
,
312 BrowserDistribution
* bins_dist
= ctx
.dist
;
313 if (ctx
.state
.is_multi_install()) {
314 bins_dist
= BrowserDistribution::GetSpecificDistribution(
315 BrowserDistribution::CHROME_BINARIES
);
318 base::FilePath expected_path
= installer::GetChromeInstallPath(
319 ctx
.system_install
, bins_dist
);
320 expected_path
= expected_path
321 .AppendASCII(ctx
.state
.version().GetString())
322 .Append(installer::kInstallerDir
)
323 .Append(installer::kSetupExe
);
324 if (!base::FilePath::CompareEqualIgnoreCase(expected_path
.value(),
325 setup_exe
.value())) {
327 LOG(ERROR
) << ctx
.dist
->GetDisplayName() << " path to " << purpose
328 << " is not " << expected_path
.value() << ": "
329 << setup_exe
.value();
333 // Validates that |command| meets the expectations described in |expected|.
334 void InstallationValidator::ValidateCommandExpectations(
335 const ProductContext
& ctx
,
336 const base::CommandLine
& command
,
337 const SwitchExpectations
& expected
,
338 const base::string16
& source
,
340 for (SwitchExpectations::size_type i
= 0, size
= expected
.size(); i
< size
;
342 const SwitchExpectations::value_type
& expectation
= expected
[i
];
343 if (command
.HasSwitch(expectation
.first
) != expectation
.second
) {
345 LOG(ERROR
) << ctx
.dist
->GetDisplayName() << " " << source
346 << (expectation
.second
? " is missing" : " has") << " \""
347 << expectation
.first
<< "\""
348 << (expectation
.second
? "" : " but shouldn't") << ": "
349 << command
.GetCommandLineString();
354 // Validates that |command|, originating from |source|, is formed properly for
355 // the product described by |ctx|
356 void InstallationValidator::ValidateUninstallCommand(
357 const ProductContext
& ctx
,
358 const base::CommandLine
& command
,
359 const base::string16
& source
,
363 ValidateSetupPath(ctx
, command
.GetProgram(),
364 base::ASCIIToUTF16("uninstaller"),
367 const bool is_multi_install
= ctx
.state
.is_multi_install();
368 SwitchExpectations expected
;
370 expected
.push_back(std::make_pair(std::string(switches::kUninstall
), true));
371 expected
.push_back(std::make_pair(std::string(switches::kSystemLevel
),
372 ctx
.system_install
));
373 expected
.push_back(std::make_pair(std::string(switches::kMultiInstall
),
375 ctx
.rules
.AddUninstallSwitchExpectations(ctx
, &expected
);
377 ValidateCommandExpectations(ctx
, command
, expected
, source
, is_valid
);
380 // Validates the rename command for the product described by |ctx|.
381 void InstallationValidator::ValidateRenameCommand(const ProductContext
& ctx
,
384 DCHECK(!ctx
.state
.rename_cmd().empty());
386 base::CommandLine command
=
387 base::CommandLine::FromString(ctx
.state
.rename_cmd());
388 base::string16
name(base::ASCIIToUTF16("in-use renamer"));
390 ValidateSetupPath(ctx
, command
.GetProgram(), name
, is_valid
);
392 SwitchExpectations expected
;
394 expected
.push_back(std::make_pair(std::string(switches::kRenameChromeExe
),
396 expected
.push_back(std::make_pair(std::string(switches::kSystemLevel
),
397 ctx
.system_install
));
398 expected
.push_back(std::make_pair(std::string(switches::kMultiInstall
),
399 ctx
.state
.is_multi_install()));
400 ctx
.rules
.AddRenameSwitchExpectations(ctx
, &expected
);
402 ValidateCommandExpectations(ctx
, command
, expected
, name
, is_valid
);
405 // Validates the "opv" and "cmd" values for the product described in |ctx|.
406 void InstallationValidator::ValidateOldVersionValues(
407 const ProductContext
& ctx
,
411 // opv and cmd must both be present or both absent
412 if (ctx
.state
.old_version() == NULL
) {
413 if (!ctx
.state
.rename_cmd().empty()) {
415 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
416 << " has a rename command but no opv: "
417 << ctx
.state
.rename_cmd();
420 if (ctx
.state
.rename_cmd().empty()) {
422 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
423 << " has an opv but no rename command: "
424 << ctx
.state
.old_version()->GetString();
426 ValidateRenameCommand(ctx
, is_valid
);
431 // Validates the multi-install state of the product described in |ctx|.
432 void InstallationValidator::ValidateMultiInstallProduct(
433 const ProductContext
& ctx
,
437 const ProductState
* binaries
=
438 ctx
.machine_state
.GetProductState(ctx
.system_install
,
439 BrowserDistribution::CHROME_BINARIES
);
442 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
443 << " (" << ctx
.state
.version().GetString() << ") is installed "
444 << "without Chrome Binaries.";
446 // Version must match that of binaries.
447 if (ctx
.state
.version().CompareTo(binaries
->version()) != 0) {
449 LOG(ERROR
) << "Version of " << ctx
.dist
->GetDisplayName()
450 << " (" << ctx
.state
.version().GetString() << ") does not "
451 "match that of Chrome Binaries ("
452 << binaries
->version().GetString() << ").";
455 // Channel value must match that of binaries.
456 if (!ctx
.state
.channel().Equals(binaries
->channel())) {
458 LOG(ERROR
) << "Channel name of " << ctx
.dist
->GetDisplayName()
459 << " (" << ctx
.state
.channel().value()
460 << ") does not match that of Chrome Binaries ("
461 << binaries
->channel().value() << ").";
466 // Validates the Google Update commands for the product described in |ctx|.
467 void InstallationValidator::ValidateAppCommands(
468 const ProductContext
& ctx
,
472 CommandExpectations expectations
;
474 if (ctx
.dist
->GetType() == BrowserDistribution::CHROME_BROWSER
)
475 expectations
[kCmdOnOsUpgrade
] = &ValidateOnOsUpgradeCommand
;
477 ValidateAppCommandExpectations(ctx
, expectations
, is_valid
);
480 // Validates usagestats for the product or binaries in |ctx|.
481 void InstallationValidator::ValidateUsageStats(const ProductContext
& ctx
,
483 DWORD usagestats
= 0;
484 if (ctx
.state
.GetUsageStats(&usagestats
)) {
485 if (!ctx
.rules
.UsageStatsAllowed(ctx
)) {
487 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
488 << " has a usagestats value (" << usagestats
489 << "), yet should not.";
490 } else if (usagestats
!= 0 && usagestats
!= 1) {
492 LOG(ERROR
) << ctx
.dist
->GetDisplayName()
493 << " has an unsupported usagestats value (" << usagestats
499 // Validates the product described in |product_state| according to |rules|.
500 void InstallationValidator::ValidateProduct(
501 const InstallationState
& machine_state
,
503 const ProductState
& product_state
,
504 const ProductRules
& rules
,
508 ProductContext
ctx(machine_state
, system_install
, product_state
, rules
);
510 ValidateUninstallCommand(ctx
, ctx
.state
.uninstall_command(),
512 "Google Update uninstall command"),
515 ValidateOldVersionValues(ctx
, is_valid
);
517 if (ctx
.state
.is_multi_install())
518 ValidateMultiInstallProduct(ctx
, is_valid
);
520 ValidateAppCommands(ctx
, is_valid
);
522 ValidateUsageStats(ctx
, is_valid
);
526 bool InstallationValidator::ValidateInstallationTypeForState(
527 const InstallationState
& machine_state
,
529 InstallationType
* type
) {
534 // Does the system have any multi-installed products?
535 const ProductState
* multi_state
=
536 machine_state
.GetProductState(system_level
,
537 BrowserDistribution::CHROME_BINARIES
);
538 if (multi_state
!= NULL
)
539 ValidateBinaries(machine_state
, system_level
, *multi_state
, &rock_on
);
541 // Is Chrome installed?
542 const ProductState
* product_state
=
543 machine_state
.GetProductState(system_level
,
544 BrowserDistribution::CHROME_BROWSER
);
545 if (product_state
!= NULL
) {
546 ChromeRules chrome_rules
;
547 ValidateProduct(machine_state
, system_level
, *product_state
,
548 chrome_rules
, &rock_on
);
549 *type
= static_cast<InstallationType
>(
550 *type
| (product_state
->is_multi_install() ?
551 ProductBits::CHROME_MULTI
:
552 ProductBits::CHROME_SINGLE
));
555 // Is Chrome Frame installed?
557 machine_state
.GetProductState(system_level
,
558 BrowserDistribution::CHROME_FRAME
);
559 if (product_state
!= NULL
) {
560 ChromeFrameRules chrome_frame_rules
;
561 ValidateProduct(machine_state
, system_level
, *product_state
,
562 chrome_frame_rules
, &rock_on
);
563 int cf_bit
= !product_state
->is_multi_install() ?
564 ProductBits::CHROME_FRAME_SINGLE
:
565 ProductBits::CHROME_FRAME_MULTI
;
566 *type
= static_cast<InstallationType
>(*type
| cf_bit
);
569 DCHECK_NE(std::find(&kInstallationTypes
[0],
570 &kInstallationTypes
[arraysize(kInstallationTypes
)],
572 &kInstallationTypes
[arraysize(kInstallationTypes
)])
573 << "Invalid combination of products found on system (" << *type
<< ")";
579 bool InstallationValidator::ValidateInstallationType(bool system_level
,
580 InstallationType
* type
) {
582 InstallationState machine_state
;
584 machine_state
.Initialize();
586 return ValidateInstallationTypeForState(machine_state
, system_level
, type
);
589 } // namespace installer