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