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/files/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 multi_install_(false) { 79 InitializeFromCommandLine(*CommandLine::ForCurrentProcess()); 80 } 81 82 MasterPreferences::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 91 MasterPreferences::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 109 MasterPreferences::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 118 MasterPreferences::~MasterPreferences() { 119 } 120 121 void 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 206 bool 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 225 void 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 254 void 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 281 bool 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 288 bool 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 295 bool 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 303 std::vector<std::string> MasterPreferences::GetFirstRunTabs() const { 304 return GetNamedList(kFirstRunTabs, master_dictionary_.get()); 305 } 306 307 bool MasterPreferences::GetExtensionsBlock( 308 base::DictionaryValue** extensions) const { 309 return master_dictionary_->GetDictionary( 310 master_preferences::kExtensionsBlock, extensions); 311 } 312 313 std::string MasterPreferences::GetVariationsSeed() const { 314 return ExtractPrefString(prefs::kVariationsSeed); 315 } 316 317 std::string MasterPreferences::GetVariationsSeedSignature() const { 318 return ExtractPrefString(prefs::kVariationsSeedSignature); 319 } 320 321 std::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 333 const MasterPreferences& MasterPreferences::ForCurrentProcess() { 334 return g_master_preferences.Get(); 335 } 336 337 } // namespace installer 338