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