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                                          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