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