Home | History | Annotate | Download | only in util
      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                                          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