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 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   base::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<base::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   base::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<base::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   base::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<base::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   base::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<base::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 base::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 base::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 base::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(
    558     const ProductContext& ctx,
    559     const CommandLine& command,
    560     const base::string16& source,
    561     bool* is_valid) {
    562   DCHECK(is_valid);
    563 
    564   ValidateSetupPath(ctx, command.GetProgram(),
    565                     base::ASCIIToUTF16("uninstaller"),
    566                     is_valid);
    567 
    568   const bool is_multi_install = ctx.state.is_multi_install();
    569   SwitchExpectations expected;
    570 
    571   expected.push_back(std::make_pair(std::string(switches::kUninstall), true));
    572   expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
    573                                     ctx.system_install));
    574   expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
    575                                     is_multi_install));
    576   ctx.rules.AddUninstallSwitchExpectations(ctx, &expected);
    577 
    578   ValidateCommandExpectations(ctx, command, expected, source, is_valid);
    579 }
    580 
    581 // Validates the rename command for the product described by |ctx|.
    582 void InstallationValidator::ValidateRenameCommand(const ProductContext& ctx,
    583                                                   bool* is_valid) {
    584   DCHECK(is_valid);
    585   DCHECK(!ctx.state.rename_cmd().empty());
    586 
    587   CommandLine command = CommandLine::FromString(ctx.state.rename_cmd());
    588   base::string16 name(base::ASCIIToUTF16("in-use renamer"));
    589 
    590   ValidateSetupPath(ctx, command.GetProgram(), name, is_valid);
    591 
    592   SwitchExpectations expected;
    593 
    594   expected.push_back(std::make_pair(std::string(switches::kRenameChromeExe),
    595                                     true));
    596   expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
    597                                     ctx.system_install));
    598   expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
    599                                     ctx.state.is_multi_install()));
    600   ctx.rules.AddRenameSwitchExpectations(ctx, &expected);
    601 
    602   ValidateCommandExpectations(ctx, command, expected, name, is_valid);
    603 }
    604 
    605 // Validates the "opv" and "cmd" values for the product described in |ctx|.
    606 void InstallationValidator::ValidateOldVersionValues(
    607     const ProductContext& ctx,
    608     bool* is_valid) {
    609   DCHECK(is_valid);
    610 
    611   // opv and cmd must both be present or both absent
    612   if (ctx.state.old_version() == NULL) {
    613     if (!ctx.state.rename_cmd().empty()) {
    614       *is_valid = false;
    615       LOG(ERROR) << ctx.dist->GetDisplayName()
    616                  << " has a rename command but no opv: "
    617                  << ctx.state.rename_cmd();
    618     }
    619   } else {
    620     if (ctx.state.rename_cmd().empty()) {
    621       *is_valid = false;
    622       LOG(ERROR) << ctx.dist->GetDisplayName()
    623                  << " has an opv but no rename command: "
    624                  << ctx.state.old_version()->GetString();
    625     } else {
    626       ValidateRenameCommand(ctx, is_valid);
    627     }
    628   }
    629 }
    630 
    631 // Validates the multi-install state of the product described in |ctx|.
    632 void InstallationValidator::ValidateMultiInstallProduct(
    633     const ProductContext& ctx,
    634     bool* is_valid) {
    635   DCHECK(is_valid);
    636 
    637   const ProductState* binaries =
    638       ctx.machine_state.GetProductState(ctx.system_install,
    639                                         BrowserDistribution::CHROME_BINARIES);
    640   if (!binaries) {
    641     if (ctx.dist->GetType() == BrowserDistribution::CHROME_APP_HOST) {
    642       if (!ctx.machine_state.GetProductState(
    643               true,  // system-level
    644               BrowserDistribution::CHROME_BINARIES) &&
    645           !ctx.machine_state.GetProductState(
    646               true,  // system-level
    647               BrowserDistribution::CHROME_BROWSER)) {
    648         *is_valid = false;
    649         LOG(ERROR) << ctx.dist->GetDisplayName()
    650                    << " (" << ctx.state.version().GetString() << ") is "
    651                    << "installed without Chrome Binaries or a system-level "
    652                    << "Chrome.";
    653       }
    654     } else {
    655       *is_valid = false;
    656       LOG(ERROR) << ctx.dist->GetDisplayName()
    657                  << " (" << ctx.state.version().GetString() << ") is installed "
    658                  << "without Chrome Binaries.";
    659     }
    660   } else {
    661     // Version must match that of binaries.
    662     if (ctx.state.version().CompareTo(binaries->version()) != 0) {
    663       *is_valid = false;
    664       LOG(ERROR) << "Version of " << ctx.dist->GetDisplayName()
    665                  << " (" << ctx.state.version().GetString() << ") does not "
    666                     "match that of Chrome Binaries ("
    667                  << binaries->version().GetString() << ").";
    668     }
    669 
    670     // Channel value must match that of binaries.
    671     if (!ctx.state.channel().Equals(binaries->channel())) {
    672       *is_valid = false;
    673       LOG(ERROR) << "Channel name of " << ctx.dist->GetDisplayName()
    674                  << " (" << ctx.state.channel().value()
    675                  << ") does not match that of Chrome Binaries ("
    676                  << binaries->channel().value() << ").";
    677     }
    678   }
    679 }
    680 
    681 // Validates the Google Update commands for the product described in |ctx|.
    682 void InstallationValidator::ValidateAppCommands(
    683     const ProductContext& ctx,
    684     bool* is_valid) {
    685   DCHECK(is_valid);
    686 
    687   CommandExpectations expectations;
    688 
    689   if (ctx.dist->GetType() == BrowserDistribution::CHROME_APP_HOST) {
    690     expectations[kCmdInstallApp] = &ValidateInstallAppCommand;
    691   }
    692   if (ctx.dist->GetType() == BrowserDistribution::CHROME_BROWSER) {
    693     expectations[kCmdInstallExtension] = &ValidateInstallExtensionCommand;
    694     expectations[kCmdOnOsUpgrade] = &ValidateOnOsUpgradeCommand;
    695   }
    696 
    697   ValidateAppCommandExpectations(ctx, expectations, is_valid);
    698 }
    699 
    700 // Validates usagestats for the product or binaries in |ctx|.
    701 void InstallationValidator::ValidateUsageStats(const ProductContext& ctx,
    702                                                bool* is_valid) {
    703   DWORD usagestats = 0;
    704   if (ctx.state.GetUsageStats(&usagestats)) {
    705     if (!ctx.rules.UsageStatsAllowed(ctx)) {
    706       *is_valid = false;
    707       LOG(ERROR) << ctx.dist->GetDisplayName()
    708                  << " has a usagestats value (" << usagestats
    709                  << "), yet should not.";
    710     } else if (usagestats != 0 && usagestats != 1) {
    711       *is_valid = false;
    712       LOG(ERROR) << ctx.dist->GetDisplayName()
    713                  << " has an unsupported usagestats value (" << usagestats
    714                  << ").";
    715     }
    716   }
    717 }
    718 
    719 // Validates the product described in |product_state| according to |rules|.
    720 void InstallationValidator::ValidateProduct(
    721     const InstallationState& machine_state,
    722     bool system_install,
    723     const ProductState& product_state,
    724     const ProductRules& rules,
    725     bool* is_valid) {
    726   DCHECK(is_valid);
    727 
    728   ProductContext ctx(machine_state, system_install, product_state, rules);
    729 
    730   ValidateUninstallCommand(ctx, ctx.state.uninstall_command(),
    731                            base::ASCIIToUTF16(
    732                                "Google Update uninstall command"),
    733                            is_valid);
    734 
    735   ValidateOldVersionValues(ctx, is_valid);
    736 
    737   if (ctx.state.is_multi_install())
    738     ValidateMultiInstallProduct(ctx, is_valid);
    739 
    740   ValidateAppCommands(ctx, is_valid);
    741 
    742   ValidateUsageStats(ctx, is_valid);
    743 }
    744 
    745 // static
    746 bool InstallationValidator::ValidateInstallationTypeForState(
    747     const InstallationState& machine_state,
    748     bool system_level,
    749     InstallationType* type) {
    750   DCHECK(type);
    751   bool rock_on = true;
    752   *type = NO_PRODUCTS;
    753 
    754   // Does the system have any multi-installed products?
    755   const ProductState* multi_state =
    756       machine_state.GetProductState(system_level,
    757                                     BrowserDistribution::CHROME_BINARIES);
    758   if (multi_state != NULL)
    759     ValidateBinaries(machine_state, system_level, *multi_state, &rock_on);
    760 
    761   // Is Chrome installed?
    762   const ProductState* product_state =
    763       machine_state.GetProductState(system_level,
    764                                     BrowserDistribution::CHROME_BROWSER);
    765   if (product_state != NULL) {
    766     ChromeRules chrome_rules;
    767     ValidateProduct(machine_state, system_level, *product_state,
    768                     chrome_rules, &rock_on);
    769     *type = static_cast<InstallationType>(
    770         *type | (product_state->is_multi_install() ?
    771                  ProductBits::CHROME_MULTI :
    772                  ProductBits::CHROME_SINGLE));
    773   }
    774 
    775   // Is Chrome Frame installed?
    776   product_state =
    777       machine_state.GetProductState(system_level,
    778                                     BrowserDistribution::CHROME_FRAME);
    779   if (product_state != NULL) {
    780     ChromeFrameRules chrome_frame_rules;
    781     ValidateProduct(machine_state, system_level, *product_state,
    782                     chrome_frame_rules, &rock_on);
    783     int cf_bit = !product_state->is_multi_install() ?
    784         ProductBits::CHROME_FRAME_SINGLE :
    785         ProductBits::CHROME_FRAME_MULTI;
    786     *type = static_cast<InstallationType>(*type | cf_bit);
    787   }
    788 
    789   // Is Chrome App Host installed?
    790   product_state =
    791       machine_state.GetProductState(system_level,
    792                                     BrowserDistribution::CHROME_APP_HOST);
    793   if (product_state != NULL) {
    794     ChromeAppHostRules chrome_app_host_rules;
    795     ValidateProduct(machine_state, system_level, *product_state,
    796                     chrome_app_host_rules, &rock_on);
    797     *type = static_cast<InstallationType>(*type | ProductBits::CHROME_APP_HOST);
    798     if (!product_state->is_multi_install()) {
    799       LOG(ERROR) << "Chrome App Launcher must always be multi-install.";
    800       rock_on = false;
    801     }
    802   }
    803 
    804   DCHECK_NE(std::find(&kInstallationTypes[0],
    805                       &kInstallationTypes[arraysize(kInstallationTypes)],
    806                       *type),
    807             &kInstallationTypes[arraysize(kInstallationTypes)])
    808       << "Invalid combination of products found on system (" << *type << ")";
    809 
    810   return rock_on;
    811 }
    812 
    813 // static
    814 bool InstallationValidator::ValidateInstallationType(bool system_level,
    815                                                      InstallationType* type) {
    816   DCHECK(type);
    817   InstallationState machine_state;
    818 
    819   machine_state.Initialize();
    820 
    821   return ValidateInstallationTypeForState(machine_state, system_level, type);
    822 }
    823 
    824 }  // namespace installer
    825