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