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