master_preferences.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/master_preferences.h"
6
7#include "base/environment.h"
8#include "base/file_util.h"
9#include "base/json/json_string_value_serializer.h"
10#include "base/lazy_instance.h"
11#include "base/logging.h"
12#include "base/path_service.h"
13#include "base/strings/string_util.h"
14#include "chrome/common/env_vars.h"
15#include "chrome/common/pref_names.h"
16#include "chrome/installer/util/master_preferences_constants.h"
17#include "chrome/installer/util/util_constants.h"
18
19namespace {
20
21const char kFirstRunTabs[] = "first_run_tabs";
22
23base::LazyInstance<installer::MasterPreferences> g_master_preferences =
24    LAZY_INSTANCE_INITIALIZER;
25
26bool GetURLFromValue(const base::Value* in_value, std::string* out_value) {
27  return in_value && out_value && in_value->GetAsString(out_value);
28}
29
30std::vector<std::string> GetNamedList(const char* name,
31                                      const base::DictionaryValue* prefs) {
32  std::vector<std::string> list;
33  if (!prefs)
34    return list;
35
36  const base::ListValue* value_list = NULL;
37  if (!prefs->GetList(name, &value_list))
38    return list;
39
40  list.reserve(value_list->GetSize());
41  for (size_t i = 0; i < value_list->GetSize(); ++i) {
42    const base::Value* entry;
43    std::string url_entry;
44    if (!value_list->Get(i, &entry) || !GetURLFromValue(entry, &url_entry)) {
45      NOTREACHED();
46      break;
47    }
48    list.push_back(url_entry);
49  }
50  return list;
51}
52
53base::DictionaryValue* ParseDistributionPreferences(
54    const std::string& json_data) {
55  JSONStringValueSerializer json(json_data);
56  std::string error;
57  scoped_ptr<base::Value> root(json.Deserialize(NULL, &error));
58  if (!root.get()) {
59    LOG(WARNING) << "Failed to parse master prefs file: " << error;
60    return NULL;
61  }
62  if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
63    LOG(WARNING) << "Failed to parse master prefs file: "
64                 << "Root item must be a dictionary.";
65    return NULL;
66  }
67  return static_cast<base::DictionaryValue*>(root.release());
68}
69
70}  // namespace
71
72namespace installer {
73
74MasterPreferences::MasterPreferences() : distribution_(NULL),
75                                         preferences_read_from_file_(false),
76                                         chrome_(true),
77                                         chrome_app_launcher_(false),
78                                         multi_install_(false) {
79  InitializeFromCommandLine(*CommandLine::ForCurrentProcess());
80}
81
82MasterPreferences::MasterPreferences(const CommandLine& cmd_line)
83    : distribution_(NULL),
84      preferences_read_from_file_(false),
85      chrome_(true),
86      chrome_app_launcher_(false),
87      multi_install_(false) {
88  InitializeFromCommandLine(cmd_line);
89}
90
91MasterPreferences::MasterPreferences(const base::FilePath& prefs_path)
92    : distribution_(NULL),
93      preferences_read_from_file_(false),
94      chrome_(true),
95      chrome_app_launcher_(false),
96      multi_install_(false) {
97  std::string json_data;
98  // Failure to read the file is ignored as |json_data| will be the empty string
99  // and the remainder of this MasterPreferences object should still be
100  // initialized as best as possible.
101  if (base::PathExists(prefs_path) &&
102      !base::ReadFileToString(prefs_path, &json_data)) {
103    LOG(ERROR) << "Failed to read preferences from " << prefs_path.value();
104  }
105  if (InitializeFromString(json_data))
106    preferences_read_from_file_ = true;
107}
108
109MasterPreferences::MasterPreferences(const std::string& prefs)
110    : distribution_(NULL),
111      preferences_read_from_file_(false),
112      chrome_(true),
113      chrome_app_launcher_(false),
114      multi_install_(false) {
115  InitializeFromString(prefs);
116}
117
118MasterPreferences::~MasterPreferences() {
119}
120
121void MasterPreferences::InitializeFromCommandLine(const CommandLine& cmd_line) {
122#if defined(OS_WIN)
123  if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
124    base::FilePath prefs_path(cmd_line.GetSwitchValuePath(
125        installer::switches::kInstallerData));
126    this->MasterPreferences::MasterPreferences(prefs_path);
127  } else {
128    master_dictionary_.reset(new base::DictionaryValue());
129  }
130
131  DCHECK(master_dictionary_.get());
132
133  // A simple map from command line switches to equivalent switches in the
134  // distribution dictionary.  Currently all switches added will be set to
135  // 'true'.
136  static const struct CmdLineSwitchToDistributionSwitch {
137    const char* cmd_line_switch;
138    const char* distribution_switch;
139  } translate_switches[] = {
140    { installer::switches::kAutoLaunchChrome,
141      installer::master_preferences::kAutoLaunchChrome },
142    { installer::switches::kChromeAppHostDeprecated,
143      installer::master_preferences::kChromeAppHostDeprecated },
144    { installer::switches::kChromeAppLauncher,
145      installer::master_preferences::kChromeAppLauncher },
146    { installer::switches::kChrome,
147      installer::master_preferences::kChrome },
148    { installer::switches::kDisableLogging,
149      installer::master_preferences::kDisableLogging },
150    { installer::switches::kMsi,
151      installer::master_preferences::kMsi },
152    { installer::switches::kMultiInstall,
153      installer::master_preferences::kMultiInstall },
154    { installer::switches::kDoNotRegisterForUpdateLaunch,
155      installer::master_preferences::kDoNotRegisterForUpdateLaunch },
156    { installer::switches::kDoNotLaunchChrome,
157      installer::master_preferences::kDoNotLaunchChrome },
158    { installer::switches::kMakeChromeDefault,
159      installer::master_preferences::kMakeChromeDefault },
160    { installer::switches::kSystemLevel,
161      installer::master_preferences::kSystemLevel },
162    { installer::switches::kVerboseLogging,
163      installer::master_preferences::kVerboseLogging },
164  };
165
166  std::string name(installer::master_preferences::kDistroDict);
167  for (int i = 0; i < arraysize(translate_switches); ++i) {
168    if (cmd_line.HasSwitch(translate_switches[i].cmd_line_switch)) {
169      name.assign(installer::master_preferences::kDistroDict);
170      name.append(".").append(translate_switches[i].distribution_switch);
171      master_dictionary_->SetBoolean(name, true);
172    }
173  }
174
175  // See if the log file path was specified on the command line.
176  std::wstring str_value(cmd_line.GetSwitchValueNative(
177      installer::switches::kLogFile));
178  if (!str_value.empty()) {
179    name.assign(installer::master_preferences::kDistroDict);
180    name.append(".").append(installer::master_preferences::kLogFile);
181    master_dictionary_->SetString(name, str_value);
182  }
183
184  // Handle the special case of --system-level being implied by the presence of
185  // the kGoogleUpdateIsMachineEnvVar environment variable.
186  scoped_ptr<base::Environment> env(base::Environment::Create());
187  if (env != NULL) {
188    std::string is_machine_var;
189    env->GetVar(env_vars::kGoogleUpdateIsMachineEnvVar, &is_machine_var);
190    if (!is_machine_var.empty() && is_machine_var[0] == '1') {
191      VLOG(1) << "Taking system-level from environment.";
192      name.assign(installer::master_preferences::kDistroDict);
193      name.append(".").append(installer::master_preferences::kSystemLevel);
194      master_dictionary_->SetBoolean(name, true);
195    }
196  }
197
198  // Cache a pointer to the distribution dictionary. Ignore errors if any.
199  master_dictionary_->GetDictionary(installer::master_preferences::kDistroDict,
200                                    &distribution_);
201
202  InitializeProductFlags();
203#endif
204}
205
206bool MasterPreferences::InitializeFromString(const std::string& json_data) {
207  if (!json_data.empty())
208    master_dictionary_.reset(ParseDistributionPreferences(json_data));
209
210  bool data_is_valid = true;
211  if (!master_dictionary_.get()) {
212    master_dictionary_.reset(new base::DictionaryValue());
213    data_is_valid = false;
214  } else {
215    // Cache a pointer to the distribution dictionary.
216    master_dictionary_->GetDictionary(
217        installer::master_preferences::kDistroDict, &distribution_);
218  }
219
220  InitializeProductFlags();
221  EnforceLegacyPreferences();
222  return data_is_valid;
223}
224
225void MasterPreferences::InitializeProductFlags() {
226  // Make sure we start out with the correct defaults.
227  multi_install_ = false;
228  chrome_app_launcher_ = false;
229  chrome_ = true;
230
231  GetBool(installer::master_preferences::kMultiInstall, &multi_install_);
232
233  GetBool(installer::master_preferences::kChromeAppLauncher,
234          &chrome_app_launcher_);
235
236  // The deprecated switch --app-host behaves like --app-launcher.
237  bool chrome_app_host = false;
238  GetBool(installer::master_preferences::kChromeAppHostDeprecated,
239          &chrome_app_host);
240  chrome_app_launcher_ = chrome_app_launcher_ || chrome_app_host;
241
242  // When multi-install is specified, the checks are pretty simple (in theory):
243  // In order to be installed/uninstalled, each product must have its switch
244  // present on the command line.
245  // When multi-install is not set, operate on Chrome.
246  if (multi_install_) {
247    if (!GetBool(installer::master_preferences::kChrome, &chrome_))
248      chrome_ = false;
249  } else {
250    chrome_ = true;
251  }
252}
253
254void MasterPreferences::EnforceLegacyPreferences() {
255  // If create_all_shortcuts was explicitly set to false, set
256  // do_not_create_(desktop|quick_launch)_shortcut to true.
257  bool create_all_shortcuts = true;
258  GetBool(installer::master_preferences::kCreateAllShortcuts,
259          &create_all_shortcuts);
260  if (!create_all_shortcuts) {
261    distribution_->SetBoolean(
262        installer::master_preferences::kDoNotCreateDesktopShortcut, true);
263    distribution_->SetBoolean(
264        installer::master_preferences::kDoNotCreateQuickLaunchShortcut, true);
265  }
266
267  // If there is no entry for kURLsToRestoreOnStartup and there is one for
268  // kURLsToRestoreOnStartupOld, copy the old to the new.
269  const base::ListValue* startup_urls_list = NULL;
270  if (master_dictionary_ &&
271      !master_dictionary_->GetList(prefs::kURLsToRestoreOnStartup, NULL) &&
272      master_dictionary_->GetList(prefs::kURLsToRestoreOnStartupOld,
273                                  &startup_urls_list) &&
274      startup_urls_list) {
275    base::ListValue* new_startup_urls_list = startup_urls_list->DeepCopy();
276    master_dictionary_->Set(prefs::kURLsToRestoreOnStartup,
277                            new_startup_urls_list);
278  }
279}
280
281bool MasterPreferences::GetBool(const std::string& name, bool* value) const {
282  bool ret = false;
283  if (distribution_)
284    ret = distribution_->GetBoolean(name, value);
285  return ret;
286}
287
288bool MasterPreferences::GetInt(const std::string& name, int* value) const {
289  bool ret = false;
290  if (distribution_)
291    ret = distribution_->GetInteger(name, value);
292  return ret;
293}
294
295bool MasterPreferences::GetString(const std::string& name,
296                                  std::string* value) const {
297  bool ret = false;
298  if (distribution_)
299    ret = (distribution_->GetString(name, value) && !value->empty());
300  return ret;
301}
302
303std::vector<std::string> MasterPreferences::GetFirstRunTabs() const {
304  return GetNamedList(kFirstRunTabs, master_dictionary_.get());
305}
306
307bool MasterPreferences::GetExtensionsBlock(
308    base::DictionaryValue** extensions) const {
309  return master_dictionary_->GetDictionary(
310      master_preferences::kExtensionsBlock, extensions);
311}
312
313std::string MasterPreferences::GetVariationsSeed() const {
314  return ExtractPrefString(prefs::kVariationsSeed);
315}
316
317std::string MasterPreferences::GetVariationsSeedSignature() const {
318  return ExtractPrefString(prefs::kVariationsSeedSignature);
319}
320
321std::string MasterPreferences::ExtractPrefString(
322    const std::string& name) const {
323  std::string result;
324  scoped_ptr<base::Value> pref_value;
325  if (master_dictionary_->Remove(name, &pref_value)) {
326    if (!pref_value->GetAsString(&result))
327      NOTREACHED();
328  }
329  return result;
330}
331
332// static
333const MasterPreferences& MasterPreferences::ForCurrentProcess() {
334  return g_master_preferences.Get();
335}
336
337}  // namespace installer
338