Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2013 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/browser/extensions/unpacked_installer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/command_line.h"
     10 #include "base/file_util.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/threading/thread_restrictions.h"
     14 #include "chrome/browser/extensions/extension_error_reporter.h"
     15 #include "chrome/browser/extensions/extension_install_prompt.h"
     16 #include "chrome/browser/extensions/extension_install_ui.h"
     17 #include "chrome/browser/extensions/extension_service.h"
     18 #include "chrome/browser/extensions/permissions_updater.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/common/chrome_switches.h"
     21 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "extensions/browser/extension_prefs.h"
     24 #include "extensions/browser/extension_registry.h"
     25 #include "extensions/browser/install_flag.h"
     26 #include "extensions/common/extension.h"
     27 #include "extensions/common/extension_l10n_util.h"
     28 #include "extensions/common/file_util.h"
     29 #include "extensions/common/id_util.h"
     30 #include "extensions/common/manifest.h"
     31 #include "sync/api/string_ordinal.h"
     32 
     33 using content::BrowserThread;
     34 using extensions::Extension;
     35 
     36 namespace {
     37 
     38 const char kUnpackedExtensionsBlacklistedError[] =
     39     "Loading of unpacked extensions is disabled by the administrator.";
     40 
     41 // Manages an ExtensionInstallPrompt for a particular extension.
     42 class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate {
     43  public:
     44   SimpleExtensionLoadPrompt(const Extension* extension,
     45                             Profile* profile,
     46                             const base::Closure& callback);
     47   virtual ~SimpleExtensionLoadPrompt();
     48 
     49   void ShowPrompt();
     50 
     51   // ExtensionInstallUI::Delegate
     52   virtual void InstallUIProceed() OVERRIDE;
     53   virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
     54 
     55  private:
     56   scoped_ptr<ExtensionInstallPrompt> install_ui_;
     57   scoped_refptr<const Extension> extension_;
     58   base::Closure callback_;
     59 };
     60 
     61 SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
     62     const Extension* extension,
     63     Profile* profile,
     64     const base::Closure& callback)
     65     : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile(
     66           profile)),
     67       extension_(extension),
     68       callback_(callback) {
     69 }
     70 
     71 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
     72 }
     73 
     74 void SimpleExtensionLoadPrompt::ShowPrompt() {
     75   std::string confirm = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
     76       switches::kAppsGalleryInstallAutoConfirmForTests);
     77   if (confirm == "accept") {
     78     InstallUIProceed();
     79     return;
     80   }
     81   if (confirm == "cancel") {
     82     InstallUIAbort(false);
     83     return;
     84   }
     85 
     86   install_ui_->ConfirmInstall(
     87       this,
     88       extension_.get(),
     89       ExtensionInstallPrompt::GetDefaultShowDialogCallback());
     90 }
     91 
     92 void SimpleExtensionLoadPrompt::InstallUIProceed() {
     93   callback_.Run();
     94   delete this;
     95 }
     96 
     97 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) {
     98   delete this;
     99 }
    100 
    101 }  // namespace
    102 
    103 namespace extensions {
    104 
    105 // static
    106 scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
    107     ExtensionService* extension_service) {
    108   DCHECK(extension_service);
    109   return scoped_refptr<UnpackedInstaller>(
    110       new UnpackedInstaller(extension_service));
    111 }
    112 
    113 UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
    114     : service_weak_(extension_service->AsWeakPtr()),
    115       prompt_for_plugins_(true),
    116       require_modern_manifest_version_(true),
    117       be_noisy_on_failure_(true),
    118       installer_(extension_service->profile()) {
    119   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    120 }
    121 
    122 UnpackedInstaller::~UnpackedInstaller() {
    123   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
    124         BrowserThread::CurrentlyOn(BrowserThread::FILE));
    125 }
    126 
    127 void UnpackedInstaller::Load(const base::FilePath& path_in) {
    128   DCHECK(extension_path_.empty());
    129   extension_path_ = path_in;
    130   BrowserThread::PostTask(
    131       BrowserThread::FILE,
    132       FROM_HERE,
    133       base::Bind(&UnpackedInstaller::GetAbsolutePath, this));
    134 }
    135 
    136 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
    137                                             std::string* extension_id) {
    138   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    139   DCHECK(extension_path_.empty());
    140 
    141   if (!service_weak_.get())
    142     return false;
    143   // Load extensions from the command line synchronously to avoid a race
    144   // between extension loading and loading an URL from the command line.
    145   base::ThreadRestrictions::ScopedAllowIO allow_io;
    146 
    147   extension_path_ = base::MakeAbsoluteFilePath(path_in);
    148 
    149   if (!IsLoadingUnpackedAllowed()) {
    150     ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
    151     return false;
    152   }
    153 
    154   std::string error;
    155   installer_.set_extension(
    156       file_util::LoadExtension(
    157           extension_path_, Manifest::COMMAND_LINE, GetFlags(), &error).get());
    158 
    159   if (!installer_.extension().get() ||
    160       !extension_l10n_util::ValidateExtensionLocales(
    161           extension_path_,
    162           installer_.extension()->manifest()->value(),
    163           &error)) {
    164     ReportExtensionLoadError(error);
    165     return false;
    166   }
    167 
    168   ShowInstallPrompt();
    169 
    170   *extension_id = installer_.extension()->id();
    171   return true;
    172 }
    173 
    174 void UnpackedInstaller::ShowInstallPrompt() {
    175   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    176   if (!service_weak_.get())
    177     return;
    178 
    179   const ExtensionSet& disabled_extensions =
    180       ExtensionRegistry::Get(service_weak_->profile())->disabled_extensions();
    181   if (service_weak_->show_extensions_prompts() && prompt_for_plugins_ &&
    182       PluginInfo::HasPlugins(installer_.extension().get()) &&
    183       !disabled_extensions.Contains(installer_.extension()->id())) {
    184     SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt(
    185         installer_.extension().get(),
    186         installer_.profile(),
    187         base::Bind(&UnpackedInstaller::CallCheckRequirements, this));
    188     prompt->ShowPrompt();
    189     return;
    190   }
    191   CallCheckRequirements();
    192 }
    193 
    194 void UnpackedInstaller::CallCheckRequirements() {
    195   installer_.CheckRequirements(
    196       base::Bind(&UnpackedInstaller::OnRequirementsChecked, this));
    197 }
    198 
    199 void UnpackedInstaller::OnRequirementsChecked(
    200     std::vector<std::string> requirement_errors) {
    201   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    202 
    203   if (!requirement_errors.empty()) {
    204     ReportExtensionLoadError(JoinString(requirement_errors, ' '));
    205     return;
    206   }
    207 
    208   ConfirmInstall();
    209 }
    210 
    211 int UnpackedInstaller::GetFlags() {
    212   std::string id = id_util::GenerateIdForPath(extension_path_);
    213   bool allow_file_access =
    214       Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
    215   ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
    216   if (prefs->HasAllowFileAccessSetting(id))
    217     allow_file_access = prefs->AllowFileAccess(id);
    218 
    219   int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
    220   if (allow_file_access)
    221     result |= Extension::ALLOW_FILE_ACCESS;
    222   if (require_modern_manifest_version_)
    223     result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
    224 
    225   return result;
    226 }
    227 
    228 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
    229   if (!service_weak_.get())
    230     return true;
    231   // If there is a "*" in the extension blacklist, then no extensions should be
    232   // allowed at all (except explicitly whitelisted extensions).
    233   ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
    234   return !prefs->ExtensionsBlacklistedByDefault();
    235 }
    236 
    237 void UnpackedInstaller::GetAbsolutePath() {
    238   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    239 
    240   extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
    241 
    242   std::string error;
    243   if (!file_util::CheckForIllegalFilenames(extension_path_, &error)) {
    244     BrowserThread::PostTask(
    245         BrowserThread::UI,
    246         FROM_HERE,
    247         base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
    248     return;
    249   }
    250   BrowserThread::PostTask(
    251       BrowserThread::UI, FROM_HERE,
    252       base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this));
    253 }
    254 
    255 void UnpackedInstaller::CheckExtensionFileAccess() {
    256   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    257   if (!service_weak_.get())
    258     return;
    259 
    260   if (!IsLoadingUnpackedAllowed()) {
    261     ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
    262     return;
    263   }
    264 
    265   BrowserThread::PostTask(
    266       BrowserThread::FILE,
    267       FROM_HERE,
    268       base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
    269 }
    270 
    271 void UnpackedInstaller::LoadWithFileAccess(int flags) {
    272   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    273 
    274   std::string error;
    275   installer_.set_extension(
    276       file_util::LoadExtension(
    277           extension_path_, Manifest::UNPACKED, flags, &error).get());
    278 
    279   if (!installer_.extension().get() ||
    280       !extension_l10n_util::ValidateExtensionLocales(
    281           extension_path_,
    282           installer_.extension()->manifest()->value(),
    283           &error)) {
    284     BrowserThread::PostTask(
    285         BrowserThread::UI,
    286         FROM_HERE,
    287         base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
    288     return;
    289   }
    290 
    291   BrowserThread::PostTask(
    292       BrowserThread::UI,
    293       FROM_HERE,
    294       base::Bind(&UnpackedInstaller::ShowInstallPrompt, this));
    295 }
    296 
    297 void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
    298   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    299   if (!on_failure_callback_.is_null())
    300     on_failure_callback_.Run(extension_path_, error);
    301 
    302   if (service_weak_.get()) {
    303     ExtensionErrorReporter::GetInstance()->ReportLoadError(
    304         extension_path_,
    305         error,
    306         service_weak_->profile(),
    307         be_noisy_on_failure_);
    308   }
    309 }
    310 
    311 void UnpackedInstaller::ConfirmInstall() {
    312   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    313   base::string16 error = installer_.CheckManagementPolicy();
    314   if (!error.empty()) {
    315     ReportExtensionLoadError(base::UTF16ToUTF8(error));
    316     return;
    317   }
    318 
    319   PermissionsUpdater perms_updater(service_weak_->profile());
    320   perms_updater.GrantActivePermissions(installer_.extension().get());
    321 
    322   service_weak_->OnExtensionInstalled(installer_.extension().get(),
    323                                       syncer::StringOrdinal(),
    324                                       kInstallFlagInstallImmediately);
    325 }
    326 
    327 }  // namespace extensions
    328