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 19 namespace { 20 21 const char kFirstRunTabs[] = "first_run_tabs"; 22 23 base::LazyInstance<installer::MasterPreferences> g_master_preferences = 24 LAZY_INSTANCE_INITIALIZER; 25 26 bool GetURLFromValue(const base::Value* in_value, std::string* out_value) { 27 return in_value && out_value && in_value->GetAsString(out_value); 28 } 29 30 std::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 53 base::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 72 namespace installer { 73 74 MasterPreferences::MasterPreferences() : distribution_(NULL), 75 preferences_read_from_file_(false), 76 chrome_(true), 77 chrome_app_launcher_(false), 78 chrome_frame_(false), 79 multi_install_(false) { 80 InitializeFromCommandLine(*CommandLine::ForCurrentProcess()); 81 } 82 83 MasterPreferences::MasterPreferences(const CommandLine& cmd_line) 84 : distribution_(NULL), 85 preferences_read_from_file_(false), 86 chrome_(true), 87 chrome_app_launcher_(false), 88 chrome_frame_(false), 89 multi_install_(false) { 90 InitializeFromCommandLine(cmd_line); 91 } 92 93 MasterPreferences::MasterPreferences(const base::FilePath& prefs_path) 94 : distribution_(NULL), 95 preferences_read_from_file_(false), 96 chrome_(true), 97 chrome_app_launcher_(false), 98 chrome_frame_(false), 99 multi_install_(false) { 100 std::string json_data; 101 // Failure to read the file is ignored as |json_data| will be the empty string 102 // and the remainder of this MasterPreferences object should still be 103 // initialized as best as possible. 104 if (base::PathExists(prefs_path) && 105 !file_util::ReadFileToString(prefs_path, &json_data)) { 106 LOG(ERROR) << "Failed to read preferences from " << prefs_path.value(); 107 } 108 if (InitializeFromString(json_data)) 109 preferences_read_from_file_ = true; 110 } 111 112 MasterPreferences::MasterPreferences(const std::string& prefs) 113 : distribution_(NULL), 114 preferences_read_from_file_(false), 115 chrome_(true), 116 chrome_app_launcher_(false), 117 chrome_frame_(false), 118 multi_install_(false) { 119 InitializeFromString(prefs); 120 } 121 122 MasterPreferences::~MasterPreferences() { 123 } 124 125 void MasterPreferences::InitializeFromCommandLine(const CommandLine& cmd_line) { 126 #if defined(OS_WIN) 127 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) { 128 base::FilePath prefs_path(cmd_line.GetSwitchValuePath( 129 installer::switches::kInstallerData)); 130 this->MasterPreferences::MasterPreferences(prefs_path); 131 } else { 132 master_dictionary_.reset(new base::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::kChromeAppHostDeprecated, 147 installer::master_preferences::kChromeAppHostDeprecated }, 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(env_vars::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 214 bool MasterPreferences::InitializeFromString(const std::string& json_data) { 215 if (!json_data.empty()) 216 master_dictionary_.reset(ParseDistributionPreferences(json_data)); 217 218 bool data_is_valid = true; 219 if (!master_dictionary_.get()) { 220 master_dictionary_.reset(new base::DictionaryValue()); 221 data_is_valid = false; 222 } else { 223 // Cache a pointer to the distribution dictionary. 224 master_dictionary_->GetDictionary( 225 installer::master_preferences::kDistroDict, &distribution_); 226 } 227 228 InitializeProductFlags(); 229 EnforceLegacyPreferences(); 230 return data_is_valid; 231 } 232 233 void MasterPreferences::InitializeProductFlags() { 234 // Make sure we start out with the correct defaults. 235 multi_install_ = false; 236 chrome_frame_ = false; 237 chrome_app_launcher_ = false; 238 chrome_ = true; 239 240 GetBool(installer::master_preferences::kMultiInstall, &multi_install_); 241 GetBool(installer::master_preferences::kChromeFrame, &chrome_frame_); 242 243 GetBool(installer::master_preferences::kChromeAppLauncher, 244 &chrome_app_launcher_); 245 246 // The deprecated switch --app-host behaves like --app-launcher. 247 bool chrome_app_host = false; 248 GetBool(installer::master_preferences::kChromeAppHostDeprecated, 249 &chrome_app_host); 250 chrome_app_launcher_ = chrome_app_launcher_ || chrome_app_host; 251 252 // When multi-install is specified, the checks are pretty simple (in theory): 253 // In order to be installed/uninstalled, each product must have its switch 254 // present on the command line. 255 // Before multi-install was introduced however, we only supported installing 256 // two products, Chrome and Chrome Frame. For the time being we need to 257 // continue to support this mode where multi-install is not set. 258 // So, when multi-install is not set, we continue to support mutually 259 // exclusive installation of Chrome and Chrome Frame. 260 if (multi_install_) { 261 if (!GetBool(installer::master_preferences::kChrome, &chrome_)) 262 chrome_ = false; 263 } else { 264 // If chrome-frame is on the command line however, we only install CF. 265 chrome_ = !chrome_frame_; 266 } 267 } 268 269 void MasterPreferences::EnforceLegacyPreferences() { 270 // If create_all_shortcuts was explicitly set to false, set 271 // do_not_create_(desktop|quick_launch)_shortcut to true. 272 bool create_all_shortcuts = true; 273 GetBool(installer::master_preferences::kCreateAllShortcuts, 274 &create_all_shortcuts); 275 if (!create_all_shortcuts) { 276 distribution_->SetBoolean( 277 installer::master_preferences::kDoNotCreateDesktopShortcut, true); 278 distribution_->SetBoolean( 279 installer::master_preferences::kDoNotCreateQuickLaunchShortcut, true); 280 } 281 } 282 283 bool MasterPreferences::GetBool(const std::string& name, bool* value) const { 284 bool ret = false; 285 if (distribution_) 286 ret = distribution_->GetBoolean(name, value); 287 return ret; 288 } 289 290 bool MasterPreferences::GetInt(const std::string& name, int* value) const { 291 bool ret = false; 292 if (distribution_) 293 ret = distribution_->GetInteger(name, value); 294 return ret; 295 } 296 297 bool MasterPreferences::GetString(const std::string& name, 298 std::string* value) const { 299 bool ret = false; 300 if (distribution_) 301 ret = (distribution_->GetString(name, value) && !value->empty()); 302 return ret; 303 } 304 305 std::vector<std::string> MasterPreferences::GetFirstRunTabs() const { 306 return GetNamedList(kFirstRunTabs, master_dictionary_.get()); 307 } 308 309 bool MasterPreferences::GetExtensionsBlock( 310 base::DictionaryValue** extensions) const { 311 return master_dictionary_->GetDictionary( 312 master_preferences::kExtensionsBlock, extensions); 313 } 314 315 std::string MasterPreferences::GetVariationsSeed() const { 316 std::string variations_seed; 317 master_dictionary_->GetString(prefs::kVariationsSeed, &variations_seed); 318 return variations_seed; 319 } 320 321 // static 322 const MasterPreferences& MasterPreferences::ForCurrentProcess() { 323 return g_master_preferences.Get(); 324 } 325 326 } // namespace installer 327