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