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