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