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. 4 5 // Implementation of the installation validator. 6 7 #include "chrome/installer/util/installation_validator.h" 8 9 #include <algorithm> 10 #include <set> 11 #include <string> 12 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" 21 22 namespace installer { 23 24 BrowserDistribution::Type 25 InstallationValidator::ChromeRules::distribution_type() const { 26 return BrowserDistribution::CHROME_BROWSER; 27 } 28 29 void InstallationValidator::ChromeRules::AddUninstallSwitchExpectations( 30 const ProductContext& ctx, 31 SwitchExpectations* expectations) const { 32 const bool is_multi_install = 33 ctx.state.uninstall_command().HasSwitch(switches::kMultiInstall); 34 35 // --chrome should be present for uninstall iff --multi-install. This wasn't 36 // the case in Chrome 10 (between r68996 and r72497), though, so consider it 37 // optional. 38 39 // --chrome-frame --ready-mode should be present for uninstall iff CF in ready 40 // mode. 41 const ProductState* cf_state = 42 ctx.machine_state.GetProductState(ctx.system_install, 43 BrowserDistribution::CHROME_FRAME); 44 const bool ready_mode = 45 cf_state != NULL && 46 cf_state->uninstall_command().HasSwitch(switches::kChromeFrameReadyMode); 47 expectations->push_back(std::make_pair(std::string(switches::kChromeFrame), 48 ready_mode)); 49 expectations->push_back( 50 std::make_pair(std::string(switches::kChromeFrameReadyMode), ready_mode)); 51 } 52 53 void InstallationValidator::ChromeRules::AddRenameSwitchExpectations( 54 const ProductContext& ctx, 55 SwitchExpectations* expectations) const { 56 const bool is_multi_install = 57 ctx.state.uninstall_command().HasSwitch(switches::kMultiInstall); 58 59 // --chrome should not be present for rename. It was for a time, so we'll be 60 // lenient so that mini_installer tests pass. 61 62 // --chrome-frame --ready-mode should never be present. 63 expectations->push_back( 64 std::make_pair(std::string(switches::kChromeFrame), false)); 65 expectations->push_back( 66 std::make_pair(std::string(switches::kChromeFrameReadyMode), false)); 67 } 68 69 bool InstallationValidator::ChromeRules::UsageStatsAllowed( 70 const ProductContext& ctx) const { 71 // Products must not have usagestats consent values when multi-install 72 // (only the multi-install binaries may). 73 return !ctx.state.is_multi_install(); 74 } 75 76 BrowserDistribution::Type 77 InstallationValidator::ChromeFrameRules::distribution_type() const { 78 return BrowserDistribution::CHROME_FRAME; 79 } 80 81 void InstallationValidator::ChromeFrameRules::AddUninstallSwitchExpectations( 82 const ProductContext& ctx, 83 SwitchExpectations* expectations) const { 84 // --chrome-frame must be present. 85 expectations->push_back(std::make_pair(std::string(switches::kChromeFrame), 86 true)); 87 // --chrome must not be present. 88 expectations->push_back(std::make_pair(std::string(switches::kChrome), 89 false)); 90 } 91 92 void InstallationValidator::ChromeFrameRules::AddRenameSwitchExpectations( 93 const ProductContext& ctx, 94 SwitchExpectations* expectations) const { 95 // --chrome-frame must be present for SxS rename. 96 expectations->push_back(std::make_pair(std::string(switches::kChromeFrame), 97 !ctx.state.is_multi_install())); 98 // --chrome must not be present. 99 expectations->push_back(std::make_pair(std::string(switches::kChrome), 100 false)); 101 } 102 103 bool InstallationValidator::ChromeFrameRules::UsageStatsAllowed( 104 const ProductContext& ctx) const { 105 // Products must not have usagestats consent values when multi-install 106 // (only the multi-install binaries may). 107 return !ctx.state.is_multi_install(); 108 } 109 110 BrowserDistribution::Type 111 InstallationValidator::ChromeAppHostRules::distribution_type() const { 112 return BrowserDistribution::CHROME_APP_HOST; 113 } 114 115 void InstallationValidator::ChromeAppHostRules::AddUninstallSwitchExpectations( 116 const ProductContext& ctx, 117 SwitchExpectations* expectations) const { 118 // --app-launcher must be present. 119 expectations->push_back( 120 std::make_pair(std::string(switches::kChromeAppLauncher), true)); 121 122 // --chrome must not be present. 123 expectations->push_back(std::make_pair(std::string(switches::kChrome), 124 false)); 125 // --chrome-frame must not be present. 126 expectations->push_back(std::make_pair(std::string(switches::kChromeFrame), 127 false)); 128 } 129 130 void InstallationValidator::ChromeAppHostRules::AddRenameSwitchExpectations( 131 const ProductContext& ctx, 132 SwitchExpectations* expectations) const { 133 // TODO(erikwright): I guess there will be none? 134 } 135 136 bool InstallationValidator::ChromeAppHostRules::UsageStatsAllowed( 137 const ProductContext& ctx) const { 138 // App Host doesn't manage usage stats. The Chrome Binaries will. 139 return false; 140 } 141 142 BrowserDistribution::Type 143 InstallationValidator::ChromeBinariesRules::distribution_type() const { 144 return BrowserDistribution::CHROME_BINARIES; 145 } 146 147 void InstallationValidator::ChromeBinariesRules::AddUninstallSwitchExpectations( 148 const ProductContext& ctx, 149 SwitchExpectations* expectations) const { 150 NOTREACHED(); 151 } 152 153 void InstallationValidator::ChromeBinariesRules::AddRenameSwitchExpectations( 154 const ProductContext& ctx, 155 SwitchExpectations* expectations) const { 156 NOTREACHED(); 157 } 158 159 bool InstallationValidator::ChromeBinariesRules::UsageStatsAllowed( 160 const ProductContext& ctx) const { 161 // UsageStats consent values are always allowed on the binaries. 162 return true; 163 } 164 165 // static 166 const InstallationValidator::InstallationType 167 InstallationValidator::kInstallationTypes[] = { 168 NO_PRODUCTS, 169 CHROME_SINGLE, 170 CHROME_MULTI, 171 CHROME_FRAME_SINGLE, 172 CHROME_FRAME_SINGLE_CHROME_SINGLE, 173 CHROME_FRAME_SINGLE_CHROME_MULTI, 174 CHROME_FRAME_MULTI, 175 CHROME_FRAME_MULTI_CHROME_MULTI, 176 CHROME_FRAME_READY_MODE_CHROME_MULTI, 177 CHROME_APP_HOST, 178 CHROME_APP_HOST_CHROME_FRAME_SINGLE, 179 CHROME_APP_HOST_CHROME_FRAME_SINGLE_CHROME_MULTI, 180 CHROME_APP_HOST_CHROME_FRAME_MULTI, 181 CHROME_APP_HOST_CHROME_FRAME_MULTI_CHROME_MULTI, 182 CHROME_APP_HOST_CHROME_MULTI, 183 CHROME_APP_HOST_CHROME_MULTI_CHROME_FRAME_READY_MODE, 184 }; 185 186 void InstallationValidator::ValidateAppCommandFlags( 187 const ProductContext& ctx, 188 const AppCommand& app_cmd, 189 const std::set<string16>& flags_exp, 190 const string16& name, 191 bool* is_valid) { 192 const struct { 193 const string16 exp_key; 194 bool val; 195 const char* msg; 196 } check_list[] = { 197 {google_update::kRegSendsPingsField, 198 app_cmd.sends_pings(), 199 "be configured to send pings"}, 200 {google_update::kRegWebAccessibleField, 201 app_cmd.is_web_accessible(), 202 "be web accessible"}, 203 {google_update::kRegAutoRunOnOSUpgradeField, 204 app_cmd.is_auto_run_on_os_upgrade(), 205 "be marked to run on OS upgrade"}, 206 {google_update::kRegRunAsUserField, 207 app_cmd.is_run_as_user(), 208 "be marked to run as user"}, 209 }; 210 for (int i = 0; i < arraysize(check_list); ++i) { 211 bool expected = flags_exp.find(check_list[i].exp_key) != flags_exp.end(); 212 if (check_list[i].val != expected) { 213 *is_valid = false; 214 LOG(ERROR) << ctx.dist->GetAppShortCutName() << ": " 215 << name << " command should " << (expected ? "" : "not ") 216 << check_list[i].msg << "."; 217 } 218 } 219 } 220 221 // Validates both "install-application" and "install-extension" depending on 222 // what is passed in. 223 void InstallationValidator::ValidateInstallCommand( 224 const ProductContext& ctx, 225 const AppCommand& app_cmd, 226 const wchar_t* expected_command, 227 const wchar_t* expected_app_name, 228 const char* expected_switch, 229 bool* is_valid) { 230 DCHECK(is_valid); 231 232 CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line())); 233 string16 name(expected_command); 234 235 base::FilePath expected_path( 236 installer::GetChromeInstallPath(ctx.system_install, ctx.dist) 237 .Append(expected_app_name)); 238 239 if (!base::FilePath::CompareEqualIgnoreCase(expected_path.value(), 240 cmd_line.GetProgram().value())) { 241 *is_valid = false; 242 LOG(ERROR) << name << "'s path is not " 243 << expected_path.value() << ": " 244 << cmd_line.GetProgram().value(); 245 } 246 247 SwitchExpectations expected; 248 expected.push_back(std::make_pair(std::string(expected_switch), true)); 249 250 ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid); 251 252 std::set<string16> flags_exp; 253 flags_exp.insert(google_update::kRegSendsPingsField); 254 flags_exp.insert(google_update::kRegWebAccessibleField); 255 flags_exp.insert(google_update::kRegRunAsUserField); 256 ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid); 257 } 258 259 // Validates the "install-application" Google Update product command. 260 void InstallationValidator::ValidateInstallAppCommand( 261 const ProductContext& ctx, 262 const AppCommand& app_cmd, 263 bool* is_valid) { 264 ValidateInstallCommand(ctx, app_cmd, kCmdInstallApp, 265 installer::kChromeAppHostExe, 266 ::switches::kInstallFromWebstore, is_valid); 267 } 268 269 // Validates the "install-extension" Google Update product command. 270 void InstallationValidator::ValidateInstallExtensionCommand( 271 const ProductContext& ctx, 272 const AppCommand& app_cmd, 273 bool* is_valid) { 274 ValidateInstallCommand(ctx, app_cmd, kCmdInstallExtension, 275 installer::kChromeExe, 276 ::switches::kLimitedInstallFromWebstore, is_valid); 277 } 278 279 // Validates the "on-os-upgrade" Google Update internal command. 280 void InstallationValidator::ValidateOnOsUpgradeCommand( 281 const ProductContext& ctx, 282 const AppCommand& app_cmd, 283 bool* is_valid) { 284 DCHECK(is_valid); 285 286 CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line())); 287 string16 name(kCmdOnOsUpgrade); 288 289 ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid); 290 291 SwitchExpectations expected; 292 expected.push_back(std::make_pair(std::string(switches::kOnOsUpgrade), true)); 293 expected.push_back(std::make_pair(std::string(switches::kSystemLevel), 294 ctx.system_install)); 295 expected.push_back(std::make_pair(std::string(switches::kMultiInstall), 296 ctx.state.is_multi_install())); 297 // Expecting kChrome if and only if kMultiInstall. 298 expected.push_back(std::make_pair(std::string(switches::kChrome), 299 ctx.state.is_multi_install())); 300 301 ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid); 302 303 std::set<string16> flags_exp; 304 flags_exp.insert(google_update::kRegAutoRunOnOSUpgradeField); 305 ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid); 306 } 307 308 // Validates the "query-eula-acceptance" Google Update product command. 309 void InstallationValidator::ValidateQueryEULAAcceptanceCommand( 310 const ProductContext& ctx, 311 const AppCommand& app_cmd, 312 bool* is_valid) { 313 DCHECK(is_valid); 314 315 CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line())); 316 string16 name(kCmdQueryEULAAcceptance); 317 318 ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid); 319 320 SwitchExpectations expected; 321 expected.push_back(std::make_pair(std::string(switches::kQueryEULAAcceptance), 322 true)); 323 expected.push_back(std::make_pair(std::string(switches::kSystemLevel), 324 ctx.system_install)); 325 326 ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid); 327 328 std::set<string16> flags_exp; 329 flags_exp.insert(google_update::kRegWebAccessibleField); 330 flags_exp.insert(google_update::kRegRunAsUserField); 331 ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid); 332 } 333 334 // Validates the "quick-enable-cf" Google Update product command. 335 void InstallationValidator::ValidateQuickEnableCfCommand( 336 const ProductContext& ctx, 337 const AppCommand& app_cmd, 338 bool* is_valid) { 339 DCHECK(is_valid); 340 341 CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line())); 342 string16 name(kCmdQuickEnableCf); 343 344 ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid); 345 346 SwitchExpectations expected; 347 348 expected.push_back( 349 std::make_pair(std::string(switches::kChromeFrameQuickEnable), true)); 350 expected.push_back(std::make_pair(std::string(switches::kSystemLevel), 351 ctx.system_install)); 352 expected.push_back(std::make_pair(std::string(switches::kMultiInstall), 353 ctx.state.is_multi_install())); 354 355 ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid); 356 357 std::set<string16> flags_exp; 358 flags_exp.insert(google_update::kRegSendsPingsField); 359 flags_exp.insert(google_update::kRegWebAccessibleField); 360 ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid); 361 } 362 363 // Validates the "quick-enable-application-host" Google Update product command. 364 void InstallationValidator::ValidateQuickEnableApplicationHostCommand( 365 const ProductContext& ctx, 366 const AppCommand& app_cmd, 367 bool* is_valid) { 368 DCHECK(is_valid); 369 370 CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line())); 371 string16 name(kCmdQuickEnableApplicationHost); 372 373 ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid); 374 375 SwitchExpectations expected; 376 377 expected.push_back(std::make_pair( 378 std::string(switches::kChromeAppLauncher), true)); 379 expected.push_back(std::make_pair( 380 std::string(switches::kSystemLevel), false)); 381 expected.push_back(std::make_pair( 382 std::string(switches::kMultiInstall), true)); 383 expected.push_back(std::make_pair( 384 std::string(switches::kEnsureGoogleUpdatePresent), true)); 385 386 ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid); 387 388 std::set<string16> flags_exp; 389 flags_exp.insert(google_update::kRegSendsPingsField); 390 flags_exp.insert(google_update::kRegWebAccessibleField); 391 flags_exp.insert(google_update::kRegRunAsUserField); 392 ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid); 393 } 394 395 // Validates a product's set of Google Update product commands against a 396 // collection of expectations. 397 void InstallationValidator::ValidateAppCommandExpectations( 398 const ProductContext& ctx, 399 const CommandExpectations& expectations, 400 bool* is_valid) { 401 DCHECK(is_valid); 402 403 CommandExpectations the_expectations(expectations); 404 405 AppCommands::CommandMapRange cmd_iterators( 406 ctx.state.commands().GetIterators()); 407 CommandExpectations::iterator expectation; 408 for (; cmd_iterators.first != cmd_iterators.second; ++cmd_iterators.first) { 409 const string16& cmd_id = cmd_iterators.first->first; 410 // Do we have an expectation for this command? 411 expectation = the_expectations.find(cmd_id); 412 if (expectation != the_expectations.end()) { 413 (expectation->second)(ctx, cmd_iterators.first->second, is_valid); 414 // Remove this command from the set of expectations since we found it. 415 the_expectations.erase(expectation); 416 } else { 417 *is_valid = false; 418 LOG(ERROR) << ctx.dist->GetAppShortCutName() 419 << " has an unexpected Google Update product command named \"" 420 << cmd_id << "\"."; 421 } 422 } 423 424 // Report on any expected commands that weren't present. 425 CommandExpectations::const_iterator scan(the_expectations.begin()); 426 CommandExpectations::const_iterator end(the_expectations.end()); 427 for (; scan != end; ++scan) { 428 *is_valid = false; 429 LOG(ERROR) << ctx.dist->GetAppShortCutName() 430 << " is missing the Google Update product command named \"" 431 << scan->first << "\"."; 432 } 433 } 434 435 // Validates the multi-install binaries' Google Update commands. 436 void InstallationValidator::ValidateBinariesCommands( 437 const ProductContext& ctx, 438 bool* is_valid) { 439 DCHECK(is_valid); 440 441 // The quick-enable-cf command must be present if Chrome Binaries are 442 // installed and Chrome Frame is not installed (or installed in ready mode). 443 const ChannelInfo& channel = ctx.state.channel(); 444 const ProductState* binaries_state = ctx.machine_state.GetProductState( 445 ctx.system_install, BrowserDistribution::CHROME_BINARIES); 446 const ProductState* cf_state = ctx.machine_state.GetProductState( 447 ctx.system_install, BrowserDistribution::CHROME_FRAME); 448 449 CommandExpectations expectations; 450 451 if (binaries_state != NULL) { 452 if (cf_state == NULL || channel.IsReadyMode()) 453 expectations[kCmdQuickEnableCf] = &ValidateQuickEnableCfCommand; 454 455 expectations[kCmdQuickEnableApplicationHost] = 456 &ValidateQuickEnableApplicationHostCommand; 457 458 expectations[kCmdQueryEULAAcceptance] = &ValidateQueryEULAAcceptanceCommand; 459 } 460 461 ValidateAppCommandExpectations(ctx, expectations, is_valid); 462 } 463 464 // Validates the multi-install binaries at level |system_level|. 465 void InstallationValidator::ValidateBinaries( 466 const InstallationState& machine_state, 467 bool system_install, 468 const ProductState& binaries_state, 469 bool* is_valid) { 470 const ChannelInfo& channel = binaries_state.channel(); 471 472 // ap must have -multi 473 if (!channel.IsMultiInstall()) { 474 *is_valid = false; 475 LOG(ERROR) << "Chrome Binaries are missing \"-multi\" in channel name: \"" 476 << channel.value() << "\""; 477 } 478 479 // ap must have -chrome iff Chrome is installed 480 const ProductState* chrome_state = machine_state.GetProductState( 481 system_install, BrowserDistribution::CHROME_BROWSER); 482 if (chrome_state != NULL) { 483 if (!channel.IsChrome()) { 484 *is_valid = false; 485 LOG(ERROR) << "Chrome Binaries are missing \"chrome\" in channel name:" 486 << " \"" << channel.value() << "\""; 487 } 488 } else if (channel.IsChrome()) { 489 *is_valid = false; 490 LOG(ERROR) << "Chrome Binaries have \"-chrome\" in channel name, yet Chrome" 491 " is not installed: \"" << channel.value() << "\""; 492 } 493 494 // ap must have -chromeframe iff Chrome Frame is installed multi 495 const ProductState* cf_state = machine_state.GetProductState( 496 system_install, BrowserDistribution::CHROME_FRAME); 497 if (cf_state != NULL && cf_state->is_multi_install()) { 498 if (!channel.IsChromeFrame()) { 499 *is_valid = false; 500 LOG(ERROR) << "Chrome Binaries are missing \"-chromeframe\" in channel" 501 " name: \"" << channel.value() << "\""; 502 } 503 } else if (channel.IsChromeFrame()) { 504 *is_valid = false; 505 LOG(ERROR) << "Chrome Binaries have \"-chromeframe\" in channel name, yet " 506 "Chrome Frame is not installed multi: \"" << channel.value() 507 << "\""; 508 } 509 510 // ap must have -readymode iff Chrome Frame is installed in ready-mode 511 if (cf_state != NULL && 512 cf_state->uninstall_command().HasSwitch( 513 installer::switches::kChromeFrameReadyMode)) { 514 if (!channel.IsReadyMode()) { 515 *is_valid = false; 516 LOG(ERROR) << "Chrome Binaries are missing \"-readymode\" in channel" 517 " name: \"" << channel.value() << "\""; 518 } 519 } else if (channel.IsReadyMode()) { 520 *is_valid = false; 521 LOG(ERROR) << "Chrome Binaries have \"-readymode\" in channel name, yet " 522 "Chrome Frame is not in ready mode: \"" << channel.value() 523 << "\""; 524 } 525 526 // ap must have -applauncher iff Chrome App Launcher is installed multi 527 const ProductState* app_host_state = machine_state.GetProductState( 528 system_install, BrowserDistribution::CHROME_APP_HOST); 529 if (app_host_state != NULL) { 530 if (!app_host_state->is_multi_install()) { 531 *is_valid = false; 532 LOG(ERROR) << "Chrome App Launcher is installed in non-multi mode."; 533 } 534 if (!channel.IsAppLauncher()) { 535 *is_valid = false; 536 LOG(ERROR) << "Chrome Binaries are missing \"-applauncher\" in channel" 537 " name: \"" << channel.value() << "\""; 538 } 539 } else if (channel.IsAppLauncher()) { 540 *is_valid = false; 541 LOG(ERROR) << "Chrome Binaries have \"-applauncher\" in channel name, yet " 542 "Chrome App Launcher is not installed: \"" << channel.value() 543 << "\""; 544 } 545 546 // Chrome, Chrome Frame, or App Host must be present 547 if (chrome_state == NULL && cf_state == NULL && app_host_state == NULL) { 548 *is_valid = false; 549 LOG(ERROR) << "Chrome Binaries are present with no other products."; 550 } 551 552 // Chrome must be multi-install if present. 553 if (chrome_state != NULL && !chrome_state->is_multi_install()) { 554 *is_valid = false; 555 LOG(ERROR) 556 << "Chrome Binaries are present yet Chrome is not multi-install."; 557 } 558 559 // Chrome Frame must be multi-install if Chrome & App Host are not present. 560 if (cf_state != NULL && app_host_state == NULL && chrome_state == NULL && 561 !cf_state->is_multi_install()) { 562 *is_valid = false; 563 LOG(ERROR) << "Chrome Binaries are present without Chrome nor App Launcher " 564 << "yet Chrome Frame is not multi-install."; 565 } 566 567 ChromeBinariesRules binaries_rules; 568 ProductContext ctx(machine_state, system_install, binaries_state, 569 binaries_rules); 570 571 ValidateBinariesCommands(ctx, is_valid); 572 573 ValidateUsageStats(ctx, is_valid); 574 } 575 576 // Validates the path to |setup_exe| for the product described by |ctx|. 577 void InstallationValidator::ValidateSetupPath(const ProductContext& ctx, 578 const base::FilePath& setup_exe, 579 const string16& purpose, 580 bool* is_valid) { 581 DCHECK(is_valid); 582 583 BrowserDistribution* bins_dist = ctx.dist; 584 if (ctx.state.is_multi_install()) { 585 bins_dist = BrowserDistribution::GetSpecificDistribution( 586 BrowserDistribution::CHROME_BINARIES); 587 } 588 589 base::FilePath expected_path = installer::GetChromeInstallPath( 590 ctx.system_install, bins_dist); 591 expected_path = expected_path 592 .AppendASCII(ctx.state.version().GetString()) 593 .Append(installer::kInstallerDir) 594 .Append(installer::kSetupExe); 595 if (!base::FilePath::CompareEqualIgnoreCase(expected_path.value(), 596 setup_exe.value())) { 597 *is_valid = false; 598 LOG(ERROR) << ctx.dist->GetAppShortCutName() << " path to " << purpose 599 << " is not " << expected_path.value() << ": " 600 << setup_exe.value(); 601 } 602 } 603 604 // Validates that |command| meets the expectations described in |expected|. 605 void InstallationValidator::ValidateCommandExpectations( 606 const ProductContext& ctx, 607 const CommandLine& command, 608 const SwitchExpectations& expected, 609 const string16& source, 610 bool* is_valid) { 611 for (SwitchExpectations::size_type i = 0, size = expected.size(); i < size; 612 ++i) { 613 const SwitchExpectations::value_type& expectation = expected[i]; 614 if (command.HasSwitch(expectation.first) != expectation.second) { 615 *is_valid = false; 616 LOG(ERROR) << ctx.dist->GetAppShortCutName() << " " << source 617 << (expectation.second ? " is missing" : " has") << " \"" 618 << expectation.first << "\"" 619 << (expectation.second ? "" : " but shouldn't") << ": " 620 << command.GetCommandLineString(); 621 } 622 } 623 } 624 625 // Validates that |command|, originating from |source|, is formed properly for 626 // the product described by |ctx| 627 void InstallationValidator::ValidateUninstallCommand(const ProductContext& ctx, 628 const CommandLine& command, 629 const string16& source, 630 bool* is_valid) { 631 DCHECK(is_valid); 632 633 ValidateSetupPath(ctx, command.GetProgram(), ASCIIToUTF16("uninstaller"), 634 is_valid); 635 636 const bool is_multi_install = ctx.state.is_multi_install(); 637 SwitchExpectations expected; 638 639 expected.push_back(std::make_pair(std::string(switches::kUninstall), true)); 640 expected.push_back(std::make_pair(std::string(switches::kSystemLevel), 641 ctx.system_install)); 642 expected.push_back(std::make_pair(std::string(switches::kMultiInstall), 643 is_multi_install)); 644 ctx.rules.AddUninstallSwitchExpectations(ctx, &expected); 645 646 ValidateCommandExpectations(ctx, command, expected, source, is_valid); 647 } 648 649 // Validates the rename command for the product described by |ctx|. 650 void InstallationValidator::ValidateRenameCommand(const ProductContext& ctx, 651 bool* is_valid) { 652 DCHECK(is_valid); 653 DCHECK(!ctx.state.rename_cmd().empty()); 654 655 CommandLine command = CommandLine::FromString(ctx.state.rename_cmd()); 656 string16 name(ASCIIToUTF16("in-use renamer")); 657 658 ValidateSetupPath(ctx, command.GetProgram(), name, is_valid); 659 660 SwitchExpectations expected; 661 662 expected.push_back(std::make_pair(std::string(switches::kRenameChromeExe), 663 true)); 664 expected.push_back(std::make_pair(std::string(switches::kSystemLevel), 665 ctx.system_install)); 666 expected.push_back(std::make_pair(std::string(switches::kMultiInstall), 667 ctx.state.is_multi_install())); 668 ctx.rules.AddRenameSwitchExpectations(ctx, &expected); 669 670 ValidateCommandExpectations(ctx, command, expected, name, is_valid); 671 } 672 673 // Validates the "opv" and "cmd" values for the product described in |ctx|. 674 void InstallationValidator::ValidateOldVersionValues( 675 const ProductContext& ctx, 676 bool* is_valid) { 677 DCHECK(is_valid); 678 679 // opv and cmd must both be present or both absent 680 if (ctx.state.old_version() == NULL) { 681 if (!ctx.state.rename_cmd().empty()) { 682 *is_valid = false; 683 LOG(ERROR) << ctx.dist->GetAppShortCutName() 684 << " has a rename command but no opv: " 685 << ctx.state.rename_cmd(); 686 } 687 } else { 688 if (ctx.state.rename_cmd().empty()) { 689 *is_valid = false; 690 LOG(ERROR) << ctx.dist->GetAppShortCutName() 691 << " has an opv but no rename command: " 692 << ctx.state.old_version()->GetString(); 693 } else { 694 ValidateRenameCommand(ctx, is_valid); 695 } 696 } 697 } 698 699 // Validates the multi-install state of the product described in |ctx|. 700 void InstallationValidator::ValidateMultiInstallProduct( 701 const ProductContext& ctx, 702 bool* is_valid) { 703 DCHECK(is_valid); 704 705 const ProductState* binaries = 706 ctx.machine_state.GetProductState(ctx.system_install, 707 BrowserDistribution::CHROME_BINARIES); 708 if (!binaries) { 709 if (ctx.dist->GetType() == BrowserDistribution::CHROME_APP_HOST) { 710 if (!ctx.machine_state.GetProductState( 711 true, // system-level 712 BrowserDistribution::CHROME_BINARIES) && 713 !ctx.machine_state.GetProductState( 714 true, // system-level 715 BrowserDistribution::CHROME_BROWSER)) { 716 *is_valid = false; 717 LOG(ERROR) << ctx.dist->GetAppShortCutName() 718 << " (" << ctx.state.version().GetString() << ") is " 719 << "installed without Chrome Binaries or a system-level " 720 << "Chrome."; 721 } 722 } else { 723 *is_valid = false; 724 LOG(ERROR) << ctx.dist->GetAppShortCutName() 725 << " (" << ctx.state.version().GetString() << ") is installed " 726 << "without Chrome Binaries."; 727 } 728 } else { 729 // Version must match that of binaries. 730 if (ctx.state.version().CompareTo(binaries->version()) != 0) { 731 *is_valid = false; 732 LOG(ERROR) << "Version of " << ctx.dist->GetAppShortCutName() 733 << " (" << ctx.state.version().GetString() << ") does not " 734 "match that of Chrome Binaries (" 735 << binaries->version().GetString() << ")."; 736 } 737 738 // Channel value must match that of binaries. 739 if (!ctx.state.channel().Equals(binaries->channel())) { 740 *is_valid = false; 741 LOG(ERROR) << "Channel name of " << ctx.dist->GetAppShortCutName() 742 << " (" << ctx.state.channel().value() 743 << ") does not match that of Chrome Binaries (" 744 << binaries->channel().value() << ")."; 745 } 746 } 747 } 748 749 // Validates the Google Update commands for the product described in |ctx|. 750 void InstallationValidator::ValidateAppCommands( 751 const ProductContext& ctx, 752 bool* is_valid) { 753 DCHECK(is_valid); 754 755 CommandExpectations expectations; 756 757 if (ctx.dist->GetType() == BrowserDistribution::CHROME_APP_HOST) { 758 expectations[kCmdInstallApp] = &ValidateInstallAppCommand; 759 } 760 if (ctx.dist->GetType() == BrowserDistribution::CHROME_BROWSER) { 761 expectations[kCmdInstallExtension] = &ValidateInstallExtensionCommand; 762 expectations[kCmdOnOsUpgrade] = &ValidateOnOsUpgradeCommand; 763 } 764 765 ValidateAppCommandExpectations(ctx, expectations, is_valid); 766 } 767 768 // Validates usagestats for the product or binaries in |ctx|. 769 void InstallationValidator::ValidateUsageStats(const ProductContext& ctx, 770 bool* is_valid) { 771 DWORD usagestats = 0; 772 if (ctx.state.GetUsageStats(&usagestats)) { 773 if (!ctx.rules.UsageStatsAllowed(ctx)) { 774 *is_valid = false; 775 LOG(ERROR) << ctx.dist->GetAppShortCutName() 776 << " has a usagestats value (" << usagestats 777 << "), yet should not."; 778 } else if (usagestats != 0 && usagestats != 1) { 779 *is_valid = false; 780 LOG(ERROR) << ctx.dist->GetAppShortCutName() 781 << " has an unsupported usagestats value (" << usagestats 782 << ")."; 783 } 784 } 785 } 786 787 // Validates the product described in |product_state| according to |rules|. 788 void InstallationValidator::ValidateProduct( 789 const InstallationState& machine_state, 790 bool system_install, 791 const ProductState& product_state, 792 const ProductRules& rules, 793 bool* is_valid) { 794 DCHECK(is_valid); 795 796 ProductContext ctx(machine_state, system_install, product_state, rules); 797 798 ValidateUninstallCommand(ctx, ctx.state.uninstall_command(), 799 ASCIIToUTF16("Google Update uninstall command"), 800 is_valid); 801 802 ValidateOldVersionValues(ctx, is_valid); 803 804 if (ctx.state.is_multi_install()) 805 ValidateMultiInstallProduct(ctx, is_valid); 806 807 ValidateAppCommands(ctx, is_valid); 808 809 ValidateUsageStats(ctx, is_valid); 810 } 811 812 // static 813 bool InstallationValidator::ValidateInstallationTypeForState( 814 const InstallationState& machine_state, 815 bool system_level, 816 InstallationType* type) { 817 DCHECK(type); 818 bool rock_on = true; 819 *type = NO_PRODUCTS; 820 821 // Does the system have any multi-installed products? 822 const ProductState* multi_state = 823 machine_state.GetProductState(system_level, 824 BrowserDistribution::CHROME_BINARIES); 825 if (multi_state != NULL) 826 ValidateBinaries(machine_state, system_level, *multi_state, &rock_on); 827 828 // Is Chrome installed? 829 const ProductState* product_state = 830 machine_state.GetProductState(system_level, 831 BrowserDistribution::CHROME_BROWSER); 832 if (product_state != NULL) { 833 ChromeRules chrome_rules; 834 ValidateProduct(machine_state, system_level, *product_state, 835 chrome_rules, &rock_on); 836 *type = static_cast<InstallationType>( 837 *type | (product_state->is_multi_install() ? 838 ProductBits::CHROME_MULTI : 839 ProductBits::CHROME_SINGLE)); 840 } 841 842 // Is Chrome Frame installed? 843 product_state = 844 machine_state.GetProductState(system_level, 845 BrowserDistribution::CHROME_FRAME); 846 if (product_state != NULL) { 847 ChromeFrameRules chrome_frame_rules; 848 ValidateProduct(machine_state, system_level, *product_state, 849 chrome_frame_rules, &rock_on); 850 int cf_bit = !product_state->is_multi_install() ? 851 ProductBits::CHROME_FRAME_SINGLE : 852 (product_state->uninstall_command().HasSwitch( 853 switches::kChromeFrameReadyMode) ? 854 ProductBits::CHROME_FRAME_READY_MODE : 855 ProductBits::CHROME_FRAME_MULTI); 856 *type = static_cast<InstallationType>(*type | cf_bit); 857 } 858 859 // Is Chrome App Host installed? 860 product_state = 861 machine_state.GetProductState(system_level, 862 BrowserDistribution::CHROME_APP_HOST); 863 if (product_state != NULL) { 864 ChromeAppHostRules chrome_app_host_rules; 865 ValidateProduct(machine_state, system_level, *product_state, 866 chrome_app_host_rules, &rock_on); 867 *type = static_cast<InstallationType>(*type | ProductBits::CHROME_APP_HOST); 868 if (!product_state->is_multi_install()) { 869 LOG(ERROR) << "Chrome App Launcher must always be multi-install."; 870 rock_on = false; 871 } 872 } 873 874 DCHECK_NE(std::find(&kInstallationTypes[0], 875 &kInstallationTypes[arraysize(kInstallationTypes)], 876 *type), 877 &kInstallationTypes[arraysize(kInstallationTypes)]) 878 << "Invalid combination of products found on system (" << *type << ")"; 879 880 return rock_on; 881 } 882 883 // static 884 bool InstallationValidator::ValidateInstallationType(bool system_level, 885 InstallationType* type) { 886 DCHECK(type); 887 InstallationState machine_state; 888 889 machine_state.Initialize(); 890 891 return ValidateInstallationTypeForState(machine_state, system_level, type); 892 } 893 894 } // namespace installer 895