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
22namespace installer {
23
24BrowserDistribution::Type
25    InstallationValidator::ChromeRules::distribution_type() const {
26  return BrowserDistribution::CHROME_BROWSER;
27}
28
29void 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
40void 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
54bool 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
61BrowserDistribution::Type
62    InstallationValidator::ChromeFrameRules::distribution_type() const {
63  return BrowserDistribution::CHROME_FRAME;
64}
65
66void 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
77void 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
88bool 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
95BrowserDistribution::Type
96    InstallationValidator::ChromeAppHostRules::distribution_type() const {
97  return BrowserDistribution::CHROME_APP_HOST;
98}
99
100void 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
115void InstallationValidator::ChromeAppHostRules::AddRenameSwitchExpectations(
116    const ProductContext& ctx,
117    SwitchExpectations* expectations) const {
118  // TODO(erikwright): I guess there will be none?
119}
120
121bool 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
127BrowserDistribution::Type
128    InstallationValidator::ChromeBinariesRules::distribution_type() const {
129  return BrowserDistribution::CHROME_BINARIES;
130}
131
132void InstallationValidator::ChromeBinariesRules::AddUninstallSwitchExpectations(
133    const ProductContext& ctx,
134    SwitchExpectations* expectations) const {
135  NOTREACHED();
136}
137
138void InstallationValidator::ChromeBinariesRules::AddRenameSwitchExpectations(
139    const ProductContext& ctx,
140    SwitchExpectations* expectations) const {
141  NOTREACHED();
142}
143
144bool 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
151const 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
169void 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.
206void 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.
243void 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.
253void 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.
263void 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.
292void 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.
318void 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.
351void 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.
390void 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|.
411void 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|.
507void 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|.
535void 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|
557void 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|.
582void 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|.
606void 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|.
632void 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|.
682void 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|.
701void 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|.
720void 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
746bool 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
814bool 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