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