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