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