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#include "chrome/installer/util/installation_state.h"
6
7#include "base/logging.h"
8#include "base/strings/string_util.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/version.h"
11#include "base/win/registry.h"
12#include "chrome/installer/util/google_update_constants.h"
13#include "chrome/installer/util/install_util.h"
14
15namespace installer {
16
17ProductState::ProductState()
18    : uninstall_command_(CommandLine::NO_PROGRAM),
19      eula_accepted_(0),
20      usagestats_(0),
21      msi_(false),
22      multi_install_(false),
23      has_eula_accepted_(false),
24      has_oem_install_(false),
25      has_usagestats_(false) {
26}
27
28bool ProductState::Initialize(bool system_install,
29                              BrowserDistribution::Type type) {
30  return Initialize(system_install,
31                    BrowserDistribution::GetSpecificDistribution(type));
32}
33
34// Initializes |commands| from the "Commands" subkey of |version_key|.
35// Returns false if there is no "Commands" subkey or on error.
36// static
37bool ProductState::InitializeCommands(const base::win::RegKey& version_key,
38                                      AppCommands* commands) {
39  static const DWORD kAccess =
40      KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_32KEY;
41  base::win::RegKey commands_key;
42
43  if (commands_key.Open(version_key.Handle(), google_update::kRegCommandsKey,
44                        kAccess) == ERROR_SUCCESS)
45    return commands->Initialize(commands_key);
46  return false;
47}
48
49bool ProductState::Initialize(bool system_install,
50                              BrowserDistribution* distribution) {
51  static const DWORD kAccess = KEY_QUERY_VALUE | KEY_WOW64_32KEY;
52  const std::wstring version_key(distribution->GetVersionKey());
53  const std::wstring state_key(distribution->GetStateKey());
54  const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
55  base::win::RegKey key;
56
57  // Clear the runway.
58  Clear();
59
60  // Read from the Clients key.
61  if (key.Open(root_key, version_key.c_str(), kAccess) == ERROR_SUCCESS) {
62    base::string16 version_str;
63    if (key.ReadValue(google_update::kRegVersionField,
64                      &version_str) == ERROR_SUCCESS) {
65      version_.reset(new Version(base::UTF16ToASCII(version_str)));
66      if (!version_->IsValid())
67        version_.reset();
68    }
69
70    // Attempt to read the other values even if the "pv" version value was
71    // absent. Note that ProductState instances containing these values will
72    // only be accessible via InstallationState::GetNonVersionedProductState.
73    if (key.ReadValue(google_update::kRegOldVersionField,
74                      &version_str) == ERROR_SUCCESS) {
75      old_version_.reset(new Version(base::UTF16ToASCII(version_str)));
76      if (!old_version_->IsValid())
77        old_version_.reset();
78    }
79
80    key.ReadValue(google_update::kRegRenameCmdField, &rename_cmd_);
81    if (!InitializeCommands(key, &commands_))
82      commands_.Clear();
83  }
84
85  // Read from the ClientState key.
86  if (key.Open(root_key, state_key.c_str(), kAccess) == ERROR_SUCCESS) {
87    std::wstring setup_path;
88    std::wstring uninstall_arguments;
89    // "ap" will be absent if not managed by Google Update.
90    channel_.Initialize(key);
91
92    // Read in the brand code, it may be absent
93    key.ReadValue(google_update::kRegBrandField, &brand_);
94
95    // "UninstallString" will be absent for the multi-installer package.
96    key.ReadValue(kUninstallStringField, &setup_path);
97    // "UninstallArguments" will be absent for the multi-installer package.
98    key.ReadValue(kUninstallArgumentsField, &uninstall_arguments);
99    InstallUtil::MakeUninstallCommand(setup_path, uninstall_arguments,
100                                      &uninstall_command_);
101
102    // "usagestats" may be absent, 0 (false), or 1 (true).  On the chance that
103    // different values are permitted in the future, we'll simply hold whatever
104    // we find.
105    has_usagestats_ = (key.ReadValueDW(google_update::kRegUsageStatsField,
106                                       &usagestats_) == ERROR_SUCCESS);
107    // "oeminstall" may be present with any value or absent.
108    has_oem_install_ = (key.ReadValue(google_update::kRegOemInstallField,
109                                      &oem_install_) == ERROR_SUCCESS);
110    // "eulaaccepted" may be absent, 0 or 1.
111    has_eula_accepted_ = (key.ReadValueDW(google_update::kRegEULAAceptedField,
112                                          &eula_accepted_) == ERROR_SUCCESS);
113    // "msi" may be absent, 0 or 1
114    DWORD dw_value = 0;
115    msi_ = (key.ReadValueDW(google_update::kRegMSIField,
116                            &dw_value) == ERROR_SUCCESS) && (dw_value != 0);
117    // Multi-install is implied or is derived from the command-line.
118    if (distribution->GetType() == BrowserDistribution::CHROME_BINARIES)
119      multi_install_ = true;
120    else
121      multi_install_ = uninstall_command_.HasSwitch(switches::kMultiInstall);
122  }
123
124  // Read from the ClientStateMedium key.  Values here override those in
125  // ClientState.
126  if (system_install &&
127      key.Open(root_key, distribution->GetStateMediumKey().c_str(), kAccess) ==
128          ERROR_SUCCESS) {
129    DWORD dword_value = 0;
130
131    if (key.ReadValueDW(google_update::kRegUsageStatsField,
132                        &dword_value) == ERROR_SUCCESS) {
133      has_usagestats_ = true;
134      usagestats_ = dword_value;
135    }
136
137    if (key.ReadValueDW(google_update::kRegEULAAceptedField,
138                        &dword_value) == ERROR_SUCCESS) {
139      has_eula_accepted_ = true;
140      eula_accepted_ = dword_value;
141    }
142  }
143
144  return version_.get() != NULL;
145}
146
147base::FilePath ProductState::GetSetupPath() const {
148  return uninstall_command_.GetProgram();
149}
150
151const Version& ProductState::version() const {
152  DCHECK(version_.get() != NULL);
153  return *version_;
154}
155
156ProductState& ProductState::CopyFrom(const ProductState& other) {
157  channel_.set_value(other.channel_.value());
158  version_.reset(other.version_.get() ? new Version(*other.version_) : NULL);
159  old_version_.reset(
160      other.old_version_.get() ? new Version(*other.old_version_) : NULL);
161  brand_ = other.brand_;
162  rename_cmd_ = other.rename_cmd_;
163  uninstall_command_ = other.uninstall_command_;
164  oem_install_ = other.oem_install_;
165  commands_.CopyFrom(other.commands_);
166  eula_accepted_ = other.eula_accepted_;
167  usagestats_ = other.usagestats_;
168  msi_ = other.msi_;
169  multi_install_ = other.multi_install_;
170  has_eula_accepted_ = other.has_eula_accepted_;
171  has_oem_install_ = other.has_oem_install_;
172  has_usagestats_ = other.has_usagestats_;
173
174  return *this;
175}
176
177void ProductState::Clear() {
178  channel_.set_value(std::wstring());
179  version_.reset();
180  old_version_.reset();
181  brand_.clear();
182  rename_cmd_.clear();
183  oem_install_.clear();
184  uninstall_command_ = CommandLine(CommandLine::NO_PROGRAM);
185  commands_.Clear();
186  eula_accepted_ = 0;
187  usagestats_ = 0;
188  msi_ = false;
189  multi_install_ = false;
190  has_eula_accepted_ = false;
191  has_oem_install_ = false;
192  has_usagestats_ = false;
193}
194
195bool ProductState::GetEulaAccepted(DWORD* eula_accepted) const {
196  DCHECK(eula_accepted);
197  if (!has_eula_accepted_)
198    return false;
199  *eula_accepted = eula_accepted_;
200  return true;
201}
202
203bool ProductState::GetOemInstall(std::wstring* oem_install) const {
204  DCHECK(oem_install);
205  if (!has_oem_install_)
206    return false;
207  *oem_install = oem_install_;
208  return true;
209}
210
211bool ProductState::GetUsageStats(DWORD* usagestats) const {
212  DCHECK(usagestats);
213  if (!has_usagestats_)
214    return false;
215  *usagestats = usagestats_;
216  return true;
217}
218
219InstallationState::InstallationState() {
220}
221
222// static
223int InstallationState::IndexFromDistType(BrowserDistribution::Type type) {
224  COMPILE_ASSERT(BrowserDistribution::CHROME_BROWSER == CHROME_BROWSER_INDEX,
225                 unexpected_chrome_browser_distribution_value_);
226  COMPILE_ASSERT(BrowserDistribution::CHROME_FRAME == CHROME_FRAME_INDEX,
227                 unexpected_chrome_frame_distribution_value_);
228  COMPILE_ASSERT(BrowserDistribution::CHROME_BINARIES == CHROME_BINARIES_INDEX,
229                 unexpected_chrome_frame_distribution_value_);
230  COMPILE_ASSERT(BrowserDistribution::CHROME_APP_HOST == CHROME_APP_HOST_INDEX,
231                 unexpected_chrome_frame_distribution_value_);
232  DCHECK(type == BrowserDistribution::CHROME_BROWSER ||
233         type == BrowserDistribution::CHROME_FRAME ||
234         type == BrowserDistribution::CHROME_BINARIES ||
235         type == BrowserDistribution::CHROME_APP_HOST);
236  return type;
237}
238
239void InstallationState::Initialize() {
240  BrowserDistribution* distribution;
241
242  distribution = BrowserDistribution::GetSpecificDistribution(
243      BrowserDistribution::CHROME_BROWSER);
244  user_products_[CHROME_BROWSER_INDEX].Initialize(false, distribution);
245  system_products_[CHROME_BROWSER_INDEX].Initialize(true, distribution);
246
247  distribution = BrowserDistribution::GetSpecificDistribution(
248      BrowserDistribution::CHROME_FRAME);
249  user_products_[CHROME_FRAME_INDEX].Initialize(false, distribution);
250  system_products_[CHROME_FRAME_INDEX].Initialize(true, distribution);
251
252  distribution = BrowserDistribution::GetSpecificDistribution(
253      BrowserDistribution::CHROME_BINARIES);
254  user_products_[CHROME_BINARIES_INDEX].Initialize(false, distribution);
255  system_products_[CHROME_BINARIES_INDEX].Initialize(true, distribution);
256
257  distribution = BrowserDistribution::GetSpecificDistribution(
258      BrowserDistribution::CHROME_APP_HOST);
259  user_products_[CHROME_APP_HOST_INDEX].Initialize(false, distribution);
260  system_products_[CHROME_APP_HOST_INDEX].Initialize(true, distribution);
261}
262
263const ProductState* InstallationState::GetNonVersionedProductState(
264    bool system_install,
265    BrowserDistribution::Type type) const {
266  const ProductState& product_state = (system_install ? system_products_ :
267      user_products_)[IndexFromDistType(type)];
268  return &product_state;
269}
270
271const ProductState* InstallationState::GetProductState(
272    bool system_install,
273    BrowserDistribution::Type type) const {
274  const ProductState* product_state =
275      GetNonVersionedProductState(system_install, type);
276  return product_state->version_.get() == NULL ? NULL : product_state;
277}
278}  // namespace installer
279