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<string16>& flags_exp,
    173     const string16& name,
    174     bool* is_valid) {
    175   const struct {
    176     const 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 both "install-application" and "install-extension" depending on
    205 // what is passed in.
    206 void InstallationValidator::ValidateInstallCommand(
    207     const ProductContext& ctx,
    208     const AppCommand& app_cmd,
    209     const wchar_t* expected_command,
    210     const wchar_t* expected_app_name,
    211     const char* expected_switch,
    212     bool* is_valid) {
    213   DCHECK(is_valid);
    214 
    215   CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line()));
    216   string16 name(expected_command);
    217 
    218   base::FilePath expected_path(
    219       installer::GetChromeInstallPath(ctx.system_install, ctx.dist)
    220       .Append(expected_app_name));
    221 
    222   if (!base::FilePath::CompareEqualIgnoreCase(expected_path.value(),
    223                                               cmd_line.GetProgram().value())) {
    224     *is_valid = false;
    225     LOG(ERROR) << name << "'s path is not "
    226                << expected_path.value() << ": "
    227                << cmd_line.GetProgram().value();
    228   }
    229 
    230   SwitchExpectations expected;
    231   expected.push_back(std::make_pair(std::string(expected_switch), true));
    232 
    233   ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid);
    234 
    235   std::set<string16> flags_exp;
    236   flags_exp.insert(google_update::kRegSendsPingsField);
    237   flags_exp.insert(google_update::kRegWebAccessibleField);
    238   flags_exp.insert(google_update::kRegRunAsUserField);
    239   ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid);
    240 }
    241 
    242 // Validates the "install-application" Google Update product command.
    243 void InstallationValidator::ValidateInstallAppCommand(
    244     const ProductContext& ctx,
    245     const AppCommand& app_cmd,
    246     bool* is_valid) {
    247   ValidateInstallCommand(ctx, app_cmd, kCmdInstallApp,
    248                          installer::kChromeAppHostExe,
    249                          ::switches::kInstallFromWebstore, is_valid);
    250 }
    251 
    252 // Validates the "install-extension" Google Update product command.
    253 void InstallationValidator::ValidateInstallExtensionCommand(
    254     const ProductContext& ctx,
    255     const AppCommand& app_cmd,
    256     bool* is_valid) {
    257   ValidateInstallCommand(ctx, app_cmd, kCmdInstallExtension,
    258                          installer::kChromeExe,
    259                          ::switches::kLimitedInstallFromWebstore, is_valid);
    260 }
    261 
    262 // Validates the "on-os-upgrade" Google Update internal command.
    263 void InstallationValidator::ValidateOnOsUpgradeCommand(
    264     const ProductContext& ctx,
    265     const AppCommand& app_cmd,
    266     bool* is_valid) {
    267   DCHECK(is_valid);
    268 
    269   CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line()));
    270   string16 name(kCmdOnOsUpgrade);
    271 
    272   ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid);
    273 
    274   SwitchExpectations expected;
    275   expected.push_back(std::make_pair(std::string(switches::kOnOsUpgrade), true));
    276   expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
    277                                     ctx.system_install));
    278   expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
    279                                     ctx.state.is_multi_install()));
    280   // Expecting kChrome if and only if kMultiInstall.
    281   expected.push_back(std::make_pair(std::string(switches::kChrome),
    282                                     ctx.state.is_multi_install()));
    283 
    284   ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid);
    285 
    286   std::set<string16> flags_exp;
    287   flags_exp.insert(google_update::kRegAutoRunOnOSUpgradeField);
    288   ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid);
    289 }
    290 
    291 // Validates the "query-eula-acceptance" Google Update product command.
    292 void InstallationValidator::ValidateQueryEULAAcceptanceCommand(
    293     const ProductContext& ctx,
    294     const AppCommand& app_cmd,
    295     bool* is_valid) {
    296   DCHECK(is_valid);
    297 
    298   CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line()));
    299   string16 name(kCmdQueryEULAAcceptance);
    300 
    301   ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid);
    302 
    303   SwitchExpectations expected;
    304   expected.push_back(std::make_pair(std::string(switches::kQueryEULAAcceptance),
    305                                     true));
    306   expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
    307                                     ctx.system_install));
    308 
    309   ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid);
    310 
    311   std::set<string16> flags_exp;
    312   flags_exp.insert(google_update::kRegWebAccessibleField);
    313   flags_exp.insert(google_update::kRegRunAsUserField);
    314   ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid);
    315 }
    316 
    317 // Validates the "quick-enable-application-host" Google Update product command.
    318 void InstallationValidator::ValidateQuickEnableApplicationHostCommand(
    319     const ProductContext& ctx,
    320     const AppCommand& app_cmd,
    321     bool* is_valid) {
    322   DCHECK(is_valid);
    323 
    324   CommandLine cmd_line(CommandLine::FromString(app_cmd.command_line()));
    325   string16 name(kCmdQuickEnableApplicationHost);
    326 
    327   ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid);
    328 
    329   SwitchExpectations expected;
    330 
    331   expected.push_back(std::make_pair(
    332       std::string(switches::kChromeAppLauncher), true));
    333   expected.push_back(std::make_pair(
    334       std::string(switches::kSystemLevel), false));
    335   expected.push_back(std::make_pair(
    336       std::string(switches::kMultiInstall), true));
    337   expected.push_back(std::make_pair(
    338       std::string(switches::kEnsureGoogleUpdatePresent), true));
    339 
    340   ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid);
    341 
    342   std::set<string16> flags_exp;
    343   flags_exp.insert(google_update::kRegSendsPingsField);
    344   flags_exp.insert(google_update::kRegWebAccessibleField);
    345   flags_exp.insert(google_update::kRegRunAsUserField);
    346   ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid);
    347 }
    348 
    349 // Validates a product's set of Google Update product commands against a
    350 // collection of expectations.
    351 void InstallationValidator::ValidateAppCommandExpectations(
    352     const ProductContext& ctx,
    353     const CommandExpectations& expectations,
    354     bool* is_valid) {
    355   DCHECK(is_valid);
    356 
    357   CommandExpectations the_expectations(expectations);
    358 
    359   AppCommands::CommandMapRange cmd_iterators(
    360       ctx.state.commands().GetIterators());
    361   CommandExpectations::iterator expectation;
    362   for (; cmd_iterators.first != cmd_iterators.second; ++cmd_iterators.first) {
    363     const string16& cmd_id = cmd_iterators.first->first;
    364     // Do we have an expectation for this command?
    365     expectation = the_expectations.find(cmd_id);
    366     if (expectation != the_expectations.end()) {
    367       (expectation->second)(ctx, cmd_iterators.first->second, is_valid);
    368       // Remove this command from the set of expectations since we found it.
    369       the_expectations.erase(expectation);
    370     } else {
    371       *is_valid = false;
    372       LOG(ERROR) << ctx.dist->GetDisplayName()
    373                  << " has an unexpected Google Update product command named \""
    374                  << cmd_id << "\".";
    375     }
    376   }
    377 
    378   // Report on any expected commands that weren't present.
    379   CommandExpectations::const_iterator scan(the_expectations.begin());
    380   CommandExpectations::const_iterator end(the_expectations.end());
    381   for (; scan != end; ++scan) {
    382     *is_valid = false;
    383     LOG(ERROR) << ctx.dist->GetDisplayName()
    384                << " is missing the Google Update product command named \""
    385                << scan->first << "\".";
    386   }
    387 }
    388 
    389 // Validates the multi-install binaries' Google Update commands.
    390 void InstallationValidator::ValidateBinariesCommands(
    391     const ProductContext& ctx,
    392     bool* is_valid) {
    393   DCHECK(is_valid);
    394 
    395   const ProductState* binaries_state = ctx.machine_state.GetProductState(
    396       ctx.system_install, BrowserDistribution::CHROME_BINARIES);
    397 
    398   CommandExpectations expectations;
    399 
    400   if (binaries_state != NULL) {
    401     expectations[kCmdQuickEnableApplicationHost] =
    402         &ValidateQuickEnableApplicationHostCommand;
    403 
    404     expectations[kCmdQueryEULAAcceptance] = &ValidateQueryEULAAcceptanceCommand;
    405   }
    406 
    407   ValidateAppCommandExpectations(ctx, expectations, is_valid);
    408 }
    409 
    410 // Validates the multi-install binaries at level |system_level|.
    411 void InstallationValidator::ValidateBinaries(
    412     const InstallationState& machine_state,
    413     bool system_install,
    414     const ProductState& binaries_state,
    415     bool* is_valid) {
    416   const ChannelInfo& channel = binaries_state.channel();
    417 
    418   // ap must have -multi
    419   if (!channel.IsMultiInstall()) {
    420     *is_valid = false;
    421     LOG(ERROR) << "Chrome Binaries are missing \"-multi\" in channel name: \""
    422                << channel.value() << "\"";
    423   }
    424 
    425   // ap must have -chrome iff Chrome is installed
    426   const ProductState* chrome_state = machine_state.GetProductState(
    427       system_install, BrowserDistribution::CHROME_BROWSER);
    428   if (chrome_state != NULL) {
    429     if (!channel.IsChrome()) {
    430       *is_valid = false;
    431       LOG(ERROR) << "Chrome Binaries are missing \"chrome\" in channel name:"
    432                  << " \"" << channel.value() << "\"";
    433     }
    434   } else if (channel.IsChrome()) {
    435     *is_valid = false;
    436     LOG(ERROR) << "Chrome Binaries have \"-chrome\" in channel name, yet Chrome"
    437                   " is not installed: \"" << channel.value() << "\"";
    438   }
    439 
    440   // ap must have -chromeframe iff Chrome Frame is installed multi
    441   const ProductState* cf_state = machine_state.GetProductState(
    442       system_install, BrowserDistribution::CHROME_FRAME);
    443   if (cf_state != NULL && cf_state->is_multi_install()) {
    444     if (!channel.IsChromeFrame()) {
    445       *is_valid = false;
    446       LOG(ERROR) << "Chrome Binaries are missing \"-chromeframe\" in channel"
    447                     " name: \"" << channel.value() << "\"";
    448     }
    449   } else if (channel.IsChromeFrame()) {
    450     *is_valid = false;
    451     LOG(ERROR) << "Chrome Binaries have \"-chromeframe\" in channel name, yet "
    452                   "Chrome Frame is not installed multi: \"" << channel.value()
    453                << "\"";
    454   }
    455 
    456   // ap must have -applauncher iff Chrome App Launcher is installed multi
    457   const ProductState* app_host_state = machine_state.GetProductState(
    458       system_install, BrowserDistribution::CHROME_APP_HOST);
    459   if (app_host_state != NULL) {
    460     if (!app_host_state->is_multi_install()) {
    461       *is_valid = false;
    462       LOG(ERROR) << "Chrome App Launcher is installed in non-multi mode.";
    463     }
    464     if (!channel.IsAppLauncher()) {
    465       *is_valid = false;
    466       LOG(ERROR) << "Chrome Binaries are missing \"-applauncher\" in channel"
    467                     " name: \"" << channel.value() << "\"";
    468     }
    469   } else if (channel.IsAppLauncher()) {
    470     *is_valid = false;
    471     LOG(ERROR) << "Chrome Binaries have \"-applauncher\" in channel name, yet "
    472                   "Chrome App Launcher is not installed: \"" << channel.value()
    473                << "\"";
    474   }
    475 
    476   // Chrome, Chrome Frame, or App Host must be present
    477   if (chrome_state == NULL && cf_state == NULL && app_host_state == NULL) {
    478     *is_valid = false;
    479     LOG(ERROR) << "Chrome Binaries are present with no other products.";
    480   }
    481 
    482   // Chrome must be multi-install if present.
    483   if (chrome_state != NULL && !chrome_state->is_multi_install()) {
    484     *is_valid = false;
    485     LOG(ERROR)
    486         << "Chrome Binaries are present yet Chrome is not multi-install.";
    487   }
    488 
    489   // Chrome Frame must be multi-install if Chrome & App Host are not present.
    490   if (cf_state != NULL && app_host_state == NULL && chrome_state == NULL &&
    491       !cf_state->is_multi_install()) {
    492     *is_valid = false;
    493     LOG(ERROR) << "Chrome Binaries are present without Chrome nor App Launcher "
    494                << "yet Chrome Frame is not multi-install.";
    495   }
    496 
    497   ChromeBinariesRules binaries_rules;
    498   ProductContext ctx(machine_state, system_install, binaries_state,
    499                      binaries_rules);
    500 
    501   ValidateBinariesCommands(ctx, is_valid);
    502 
    503   ValidateUsageStats(ctx, is_valid);
    504 }
    505 
    506 // Validates the path to |setup_exe| for the product described by |ctx|.
    507 void InstallationValidator::ValidateSetupPath(const ProductContext& ctx,
    508                                               const base::FilePath& setup_exe,
    509                                               const string16& purpose,
    510                                               bool* is_valid) {
    511   DCHECK(is_valid);
    512 
    513   BrowserDistribution* bins_dist = ctx.dist;
    514   if (ctx.state.is_multi_install()) {
    515     bins_dist = BrowserDistribution::GetSpecificDistribution(
    516         BrowserDistribution::CHROME_BINARIES);
    517   }
    518 
    519   base::FilePath expected_path = installer::GetChromeInstallPath(
    520       ctx.system_install, bins_dist);
    521   expected_path = expected_path
    522       .AppendASCII(ctx.state.version().GetString())
    523       .Append(installer::kInstallerDir)
    524       .Append(installer::kSetupExe);
    525   if (!base::FilePath::CompareEqualIgnoreCase(expected_path.value(),
    526                                               setup_exe.value())) {
    527     *is_valid = false;
    528     LOG(ERROR) << ctx.dist->GetDisplayName() << " path to " << purpose
    529                << " is not " << expected_path.value() << ": "
    530                << setup_exe.value();
    531   }
    532 }
    533 
    534 // Validates that |command| meets the expectations described in |expected|.
    535 void InstallationValidator::ValidateCommandExpectations(
    536     const ProductContext& ctx,
    537     const CommandLine& command,
    538     const SwitchExpectations& expected,
    539     const string16& source,
    540     bool* is_valid) {
    541   for (SwitchExpectations::size_type i = 0, size = expected.size(); i < size;
    542        ++i) {
    543     const SwitchExpectations::value_type& expectation = expected[i];
    544     if (command.HasSwitch(expectation.first) != expectation.second) {
    545       *is_valid = false;
    546       LOG(ERROR) << ctx.dist->GetDisplayName() << " " << source
    547                  << (expectation.second ? " is missing" : " has") << " \""
    548                  << expectation.first << "\""
    549                  << (expectation.second ? "" : " but shouldn't") << ": "
    550                  << command.GetCommandLineString();
    551     }
    552   }
    553 }
    554 
    555 // Validates that |command|, originating from |source|, is formed properly for
    556 // the product described by |ctx|
    557 void InstallationValidator::ValidateUninstallCommand(const ProductContext& ctx,
    558                                                      const CommandLine& command,
    559                                                      const string16& source,
    560                                                      bool* is_valid) {
    561   DCHECK(is_valid);
    562 
    563   ValidateSetupPath(ctx, command.GetProgram(), ASCIIToUTF16("uninstaller"),
    564                     is_valid);
    565 
    566   const bool is_multi_install = ctx.state.is_multi_install();
    567   SwitchExpectations expected;
    568 
    569   expected.push_back(std::make_pair(std::string(switches::kUninstall), true));
    570   expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
    571                                     ctx.system_install));
    572   expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
    573                                     is_multi_install));
    574   ctx.rules.AddUninstallSwitchExpectations(ctx, &expected);
    575 
    576   ValidateCommandExpectations(ctx, command, expected, source, is_valid);
    577 }
    578 
    579 // Validates the rename command for the product described by |ctx|.
    580 void InstallationValidator::ValidateRenameCommand(const ProductContext& ctx,
    581                                                   bool* is_valid) {
    582   DCHECK(is_valid);
    583   DCHECK(!ctx.state.rename_cmd().empty());
    584 
    585   CommandLine command = CommandLine::FromString(ctx.state.rename_cmd());
    586   string16 name(ASCIIToUTF16("in-use renamer"));
    587 
    588   ValidateSetupPath(ctx, command.GetProgram(), name, is_valid);
    589 
    590   SwitchExpectations expected;
    591 
    592   expected.push_back(std::make_pair(std::string(switches::kRenameChromeExe),
    593                                     true));
    594   expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
    595                                     ctx.system_install));
    596   expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
    597                                     ctx.state.is_multi_install()));
    598   ctx.rules.AddRenameSwitchExpectations(ctx, &expected);
    599 
    600   ValidateCommandExpectations(ctx, command, expected, name, is_valid);
    601 }
    602 
    603 // Validates the "opv" and "cmd" values for the product described in |ctx|.
    604 void InstallationValidator::ValidateOldVersionValues(
    605     const ProductContext& ctx,
    606     bool* is_valid) {
    607   DCHECK(is_valid);
    608 
    609   // opv and cmd must both be present or both absent
    610   if (ctx.state.old_version() == NULL) {
    611     if (!ctx.state.rename_cmd().empty()) {
    612       *is_valid = false;
    613       LOG(ERROR) << ctx.dist->GetDisplayName()
    614                  << " has a rename command but no opv: "
    615                  << ctx.state.rename_cmd();
    616     }
    617   } else {
    618     if (ctx.state.rename_cmd().empty()) {
    619       *is_valid = false;
    620       LOG(ERROR) << ctx.dist->GetDisplayName()
    621                  << " has an opv but no rename command: "
    622                  << ctx.state.old_version()->GetString();
    623     } else {
    624       ValidateRenameCommand(ctx, is_valid);
    625     }
    626   }
    627 }
    628 
    629 // Validates the multi-install state of the product described in |ctx|.
    630 void InstallationValidator::ValidateMultiInstallProduct(
    631     const ProductContext& ctx,
    632     bool* is_valid) {
    633   DCHECK(is_valid);
    634 
    635   const ProductState* binaries =
    636       ctx.machine_state.GetProductState(ctx.system_install,
    637                                         BrowserDistribution::CHROME_BINARIES);
    638   if (!binaries) {
    639     if (ctx.dist->GetType() == BrowserDistribution::CHROME_APP_HOST) {
    640       if (!ctx.machine_state.GetProductState(
    641               true,  // system-level
    642               BrowserDistribution::CHROME_BINARIES) &&
    643           !ctx.machine_state.GetProductState(
    644               true,  // system-level
    645               BrowserDistribution::CHROME_BROWSER)) {
    646         *is_valid = false;
    647         LOG(ERROR) << ctx.dist->GetDisplayName()
    648                    << " (" << ctx.state.version().GetString() << ") is "
    649                    << "installed without Chrome Binaries or a system-level "
    650                    << "Chrome.";
    651       }
    652     } else {
    653       *is_valid = false;
    654       LOG(ERROR) << ctx.dist->GetDisplayName()
    655                  << " (" << ctx.state.version().GetString() << ") is installed "
    656                  << "without Chrome Binaries.";
    657     }
    658   } else {
    659     // Version must match that of binaries.
    660     if (ctx.state.version().CompareTo(binaries->version()) != 0) {
    661       *is_valid = false;
    662       LOG(ERROR) << "Version of " << ctx.dist->GetDisplayName()
    663                  << " (" << ctx.state.version().GetString() << ") does not "
    664                     "match that of Chrome Binaries ("
    665                  << binaries->version().GetString() << ").";
    666     }
    667 
    668     // Channel value must match that of binaries.
    669     if (!ctx.state.channel().Equals(binaries->channel())) {
    670       *is_valid = false;
    671       LOG(ERROR) << "Channel name of " << ctx.dist->GetDisplayName()
    672                  << " (" << ctx.state.channel().value()
    673                  << ") does not match that of Chrome Binaries ("
    674                  << binaries->channel().value() << ").";
    675     }
    676   }
    677 }
    678 
    679 // Validates the Google Update commands for the product described in |ctx|.
    680 void InstallationValidator::ValidateAppCommands(
    681     const ProductContext& ctx,
    682     bool* is_valid) {
    683   DCHECK(is_valid);
    684 
    685   CommandExpectations expectations;
    686 
    687   if (ctx.dist->GetType() == BrowserDistribution::CHROME_APP_HOST) {
    688     expectations[kCmdInstallApp] = &ValidateInstallAppCommand;
    689   }
    690   if (ctx.dist->GetType() == BrowserDistribution::CHROME_BROWSER) {
    691     expectations[kCmdInstallExtension] = &ValidateInstallExtensionCommand;
    692     expectations[kCmdOnOsUpgrade] = &ValidateOnOsUpgradeCommand;
    693   }
    694 
    695   ValidateAppCommandExpectations(ctx, expectations, is_valid);
    696 }
    697 
    698 // Validates usagestats for the product or binaries in |ctx|.
    699 void InstallationValidator::ValidateUsageStats(const ProductContext& ctx,
    700                                                bool* is_valid) {
    701   DWORD usagestats = 0;
    702   if (ctx.state.GetUsageStats(&usagestats)) {
    703     if (!ctx.rules.UsageStatsAllowed(ctx)) {
    704       *is_valid = false;
    705       LOG(ERROR) << ctx.dist->GetDisplayName()
    706                  << " has a usagestats value (" << usagestats
    707                  << "), yet should not.";
    708     } else if (usagestats != 0 && usagestats != 1) {
    709       *is_valid = false;
    710       LOG(ERROR) << ctx.dist->GetDisplayName()
    711                  << " has an unsupported usagestats value (" << usagestats
    712                  << ").";
    713     }
    714   }
    715 }
    716 
    717 // Validates the product described in |product_state| according to |rules|.
    718 void InstallationValidator::ValidateProduct(
    719     const InstallationState& machine_state,
    720     bool system_install,
    721     const ProductState& product_state,
    722     const ProductRules& rules,
    723     bool* is_valid) {
    724   DCHECK(is_valid);
    725 
    726   ProductContext ctx(machine_state, system_install, product_state, rules);
    727 
    728   ValidateUninstallCommand(ctx, ctx.state.uninstall_command(),
    729                            ASCIIToUTF16("Google Update uninstall command"),
    730                            is_valid);
    731 
    732   ValidateOldVersionValues(ctx, is_valid);
    733 
    734   if (ctx.state.is_multi_install())
    735     ValidateMultiInstallProduct(ctx, is_valid);
    736 
    737   ValidateAppCommands(ctx, is_valid);
    738 
    739   ValidateUsageStats(ctx, is_valid);
    740 }
    741 
    742 // static
    743 bool InstallationValidator::ValidateInstallationTypeForState(
    744     const InstallationState& machine_state,
    745     bool system_level,
    746     InstallationType* type) {
    747   DCHECK(type);
    748   bool rock_on = true;
    749   *type = NO_PRODUCTS;
    750 
    751   // Does the system have any multi-installed products?
    752   const ProductState* multi_state =
    753       machine_state.GetProductState(system_level,
    754                                     BrowserDistribution::CHROME_BINARIES);
    755   if (multi_state != NULL)
    756     ValidateBinaries(machine_state, system_level, *multi_state, &rock_on);
    757 
    758   // Is Chrome installed?
    759   const ProductState* product_state =
    760       machine_state.GetProductState(system_level,
    761                                     BrowserDistribution::CHROME_BROWSER);
    762   if (product_state != NULL) {
    763     ChromeRules chrome_rules;
    764     ValidateProduct(machine_state, system_level, *product_state,
    765                     chrome_rules, &rock_on);
    766     *type = static_cast<InstallationType>(
    767         *type | (product_state->is_multi_install() ?
    768                  ProductBits::CHROME_MULTI :
    769                  ProductBits::CHROME_SINGLE));
    770   }
    771 
    772   // Is Chrome Frame installed?
    773   product_state =
    774       machine_state.GetProductState(system_level,
    775                                     BrowserDistribution::CHROME_FRAME);
    776   if (product_state != NULL) {
    777     ChromeFrameRules chrome_frame_rules;
    778     ValidateProduct(machine_state, system_level, *product_state,
    779                     chrome_frame_rules, &rock_on);
    780     int cf_bit = !product_state->is_multi_install() ?
    781         ProductBits::CHROME_FRAME_SINGLE :
    782         ProductBits::CHROME_FRAME_MULTI;
    783     *type = static_cast<InstallationType>(*type | cf_bit);
    784   }
    785 
    786   // Is Chrome App Host installed?
    787   product_state =
    788       machine_state.GetProductState(system_level,
    789                                     BrowserDistribution::CHROME_APP_HOST);
    790   if (product_state != NULL) {
    791     ChromeAppHostRules chrome_app_host_rules;
    792     ValidateProduct(machine_state, system_level, *product_state,
    793                     chrome_app_host_rules, &rock_on);
    794     *type = static_cast<InstallationType>(*type | ProductBits::CHROME_APP_HOST);
    795     if (!product_state->is_multi_install()) {
    796       LOG(ERROR) << "Chrome App Launcher must always be multi-install.";
    797       rock_on = false;
    798     }
    799   }
    800 
    801   DCHECK_NE(std::find(&kInstallationTypes[0],
    802                       &kInstallationTypes[arraysize(kInstallationTypes)],
    803                       *type),
    804             &kInstallationTypes[arraysize(kInstallationTypes)])
    805       << "Invalid combination of products found on system (" << *type << ")";
    806 
    807   return rock_on;
    808 }
    809 
    810 // static
    811 bool InstallationValidator::ValidateInstallationType(bool system_level,
    812                                                      InstallationType* type) {
    813   DCHECK(type);
    814   InstallationState machine_state;
    815 
    816   machine_state.Initialize();
    817 
    818   return ValidateInstallationTypeForState(machine_state, system_level, type);
    819 }
    820 
    821 }  // namespace installer
    822