blob: 4ef4d086f63473c6ac60be70f33c9e63b3e35ecd [file] [log] [blame]
[email protected]ce5c4502009-05-06 16:46:111// Copyright (c) 2009 The Chromium Authors. All rights reserved.
[email protected]6014d672008-12-05 00:38:252// 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/extensions_service.h"
6
[email protected]e2eb43112009-05-29 21:19:547#include "app/l10n_util.h"
8#include "base/command_line.h"
[email protected]fbcc40302009-06-12 20:45:459#include "base/crypto/signature_verifier.h"
[email protected]6014d672008-12-05 00:38:2510#include "base/file_util.h"
[email protected]902f7cd2009-05-22 19:02:1911#include "base/gfx/png_encoder.h"
[email protected]cc655912009-01-29 23:19:1912#include "base/scoped_handle.h"
13#include "base/scoped_temp_dir.h"
[email protected]5c238752009-06-13 10:29:0714#include "base/stl_util-inl.h"
[email protected]6014d672008-12-05 00:38:2515#include "base/string_util.h"
[email protected]cc655912009-01-29 23:19:1916#include "base/third_party/nss/blapi.h"
17#include "base/third_party/nss/sha256.h"
[email protected]6014d672008-12-05 00:38:2518#include "base/thread.h"
[email protected]cc655912009-01-29 23:19:1919#include "base/values.h"
20#include "net/base/file_stream.h"
[email protected]a57209872009-05-04 22:53:1421#include "chrome/browser/browser.h"
22#include "chrome/browser/browser_list.h"
[email protected]6014d672008-12-05 00:38:2523#include "chrome/browser/browser_process.h"
[email protected]1fca1492009-05-15 22:23:4324#include "chrome/browser/chrome_thread.h"
[email protected]fbcc40302009-06-12 20:45:4525#include "chrome/browser/extensions/extension_creator.h"
[email protected]b68d5ed2009-04-16 02:41:2826#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]481e1a42009-05-06 20:56:0527#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]a1257b12009-06-12 02:51:3428#include "chrome/browser/extensions/external_extension_provider.h"
29#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]81e63782009-02-27 19:35:0930#include "chrome/browser/profile.h"
[email protected]1fca1492009-05-15 22:23:4331#include "chrome/browser/utility_process_host.h"
[email protected]e2eb43112009-05-29 21:19:5432#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5833#include "chrome/common/extensions/extension.h"
34#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]1fca1492009-05-15 22:23:4335#include "chrome/common/extensions/extension_unpacker.h"
[email protected]6014d672008-12-05 00:38:2536#include "chrome/common/json_value_serializer.h"
[email protected]82891262008-12-24 00:21:2637#include "chrome/common/notification_service.h"
[email protected]25b34332009-06-05 21:53:1938#include "chrome/common/pref_names.h"
[email protected]894bb502009-05-21 22:39:5739#include "chrome/common/pref_service.h"
[email protected]4777bc562009-06-01 02:53:0040#include "chrome/common/zip.h"
[email protected]a57209872009-05-04 22:53:1441#include "chrome/common/url_constants.h"
[email protected]e2eb43112009-05-29 21:19:5442#include "grit/chromium_strings.h"
43#include "grit/generated_resources.h"
[email protected]fbcc40302009-06-12 20:45:4544#include "net/base/base64.h"
[email protected]902f7cd2009-05-22 19:02:1945#include "third_party/skia/include/core/SkBitmap.h"
[email protected]c64631652009-04-29 22:24:3146
[email protected]79db6232009-02-13 20:51:2047#if defined(OS_WIN)
[email protected]e2eb43112009-05-29 21:19:5448#include "app/win_util.h"
[email protected]e2eb43112009-05-29 21:19:5449#include "base/win_util.h"
[email protected]a1257b12009-06-12 02:51:3450#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2051#endif
[email protected]6014d672008-12-05 00:38:2552
[email protected]25b34332009-06-05 21:53:1953// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2554
[email protected]fbcc40302009-06-12 20:45:4555const char ExtensionsService::kExtensionHeaderMagic[] = "Cr24";
56
[email protected]cc655912009-01-29 23:19:1957const char* ExtensionsService::kInstallDirectoryName = "Extensions";
58const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
59const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
[email protected]b0beaa662009-02-26 00:04:1560
61namespace {
[email protected]6014d672008-12-05 00:38:2562
[email protected]1fca1492009-05-15 22:23:4363// A temporary subdirectory where we unpack extensions.
64const char* kUnpackExtensionDir = "TEMP_UNPACK";
65
[email protected]fbcc40302009-06-12 20:45:4566// Unpacking errors
67const char* kBadMagicNumberError = "Bad magic number";
68const char* kBadHeaderSizeError = "Excessively large key or signature";
69const char* kBadVersionNumberError = "Bad version number";
70const char* kInvalidExtensionHeaderError = "Invalid extension header";
71const char* kInvalidPublicKeyError = "Invalid public key";
72const char* kInvalidSignatureError = "Invalid signature";
73const char* kSignatureVerificationFailed = "Signature verification failed";
74const char* kSignatureVerificationInitFailed =
75 "Signature verification initialization failed. This is most likely "
76 "caused by a public key in the wrong format (should encode algorithm).";
[email protected]b0beaa662009-02-26 00:04:1577}
78
[email protected]1fca1492009-05-15 22:23:4379// This class coordinates an extension unpack task which is run in a separate
80// process. Results are sent back to this class, which we route to the
81// ExtensionServiceBackend.
82class ExtensionsServiceBackend::UnpackerClient
83 : public UtilityProcessHost::Client {
84 public:
85 UnpackerClient(ExtensionsServiceBackend* backend,
86 const FilePath& extension_path,
[email protected]fbcc40302009-06-12 20:45:4587 const std::string& public_key,
[email protected]9f1087e2009-06-15 17:29:3288 const std::string& expected_id)
[email protected]1fca1492009-05-15 22:23:4389 : backend_(backend), extension_path_(extension_path),
[email protected]9f1087e2009-06-15 17:29:3290 public_key_(public_key), expected_id_(expected_id), got_response_(false) {
[email protected]1fca1492009-05-15 22:23:4391 }
92
93 // Starts the unpack task. We call back to the backend when the task is done,
94 // or a problem occurs.
95 void Start() {
96 AddRef(); // balanced in OnUnpackExtensionReply()
97
98 // TODO(mpcomplete): handle multiple installs
99 FilePath temp_dir = backend_->install_directory_.AppendASCII(
100 kUnpackExtensionDir);
101 if (!file_util::CreateDirectory(temp_dir)) {
102 backend_->ReportExtensionInstallError(extension_path_,
103 "Failed to create temporary directory.");
104 return;
105 }
106
107 temp_extension_path_ = temp_dir.Append(extension_path_.BaseName());
108 if (!file_util::CopyFile(extension_path_, temp_extension_path_)) {
109 backend_->ReportExtensionInstallError(extension_path_,
110 "Failed to copy extension file to temporary directory.");
111 return;
112 }
113
114 if (backend_->resource_dispatcher_host_) {
115 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE,
116 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread,
117 backend_->resource_dispatcher_host_,
118 MessageLoop::current()));
119 } else {
120 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're
121 // in a unit test and run the unpacker directly in-process.
122 ExtensionUnpacker unpacker(temp_extension_path_);
[email protected]902f7cd2009-05-22 19:02:19123 if (unpacker.Run()) {
[email protected]facd7a7652009-06-05 23:15:02124 OnUnpackExtensionSucceededImpl(*unpacker.parsed_manifest(),
125 unpacker.decoded_images());
[email protected]902f7cd2009-05-22 19:02:19126 } else {
127 OnUnpackExtensionFailed(unpacker.error_message());
128 }
[email protected]1fca1492009-05-15 22:23:43129 }
130 }
131
132 private:
133 // UtilityProcessHost::Client
134 virtual void OnProcessCrashed() {
[email protected]2b4053c2009-05-29 20:28:09135 // Don't report crashes if they happen after we got a response.
136 if (got_response_)
137 return;
138
139 OnUnpackExtensionFailed("Chrome crashed while trying to install.");
[email protected]1fca1492009-05-15 22:23:43140 }
141
[email protected]facd7a7652009-06-05 23:15:02142 virtual void OnUnpackExtensionSucceeded(const DictionaryValue& manifest) {
143 ExtensionUnpacker::DecodedImages images;
144 if (!ExtensionUnpacker::ReadImagesFromFile(temp_extension_path_,
145 &images)) {
146 OnUnpackExtensionFailed("Couldn't read image data from disk.");
147 } else {
148 OnUnpackExtensionSucceededImpl(manifest, images);
149 }
150 }
151
152 void OnUnpackExtensionSucceededImpl(
[email protected]902f7cd2009-05-22 19:02:19153 const DictionaryValue& manifest,
[email protected]facd7a7652009-06-05 23:15:02154 const ExtensionUnpacker::DecodedImages& images) {
[email protected]fbcc40302009-06-12 20:45:45155 // Add our public key into the parsed manifest. We want it to be saved so
156 // that we can later refer to it (eg for generating ids, validating
157 // signatures, etc).
158 // The const_cast is hacky, but seems like the right thing here, rather than
159 // making a full copy just to make this change.
160 const_cast<DictionaryValue*>(&manifest)->SetString(
161 Extension::kPublicKeyKey, public_key_);
162
[email protected]902f7cd2009-05-22 19:02:19163 // The extension was unpacked to the temp dir inside our unpacking dir.
164 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII(
165 ExtensionsServiceBackend::kTempExtensionName);
166 backend_->OnExtensionUnpacked(extension_path_, extension_dir,
[email protected]9f1087e2009-06-15 17:29:32167 expected_id_, manifest, images);
[email protected]1fca1492009-05-15 22:23:43168 Cleanup();
[email protected]902f7cd2009-05-22 19:02:19169 }
170
171 virtual void OnUnpackExtensionFailed(const std::string& error_message) {
172 backend_->ReportExtensionInstallError(extension_path_, error_message);
173 Cleanup();
[email protected]1fca1492009-05-15 22:23:43174 }
175
176 // Cleans up our temp directory.
177 void Cleanup() {
[email protected]2b4053c2009-05-29 20:28:09178 if (got_response_)
179 return;
180
181 got_response_ = true;
[email protected]1fca1492009-05-15 22:23:43182 file_util::Delete(temp_extension_path_.DirName(), true);
[email protected]902f7cd2009-05-22 19:02:19183 Release(); // balanced in Run()
[email protected]1fca1492009-05-15 22:23:43184 }
185
186 // Starts the utility process that unpacks our extension.
187 void StartProcessOnIOThread(ResourceDispatcherHost* rdh,
188 MessageLoop* file_loop) {
189 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop);
190 host->StartExtensionUnpacker(temp_extension_path_);
191 }
192
193 scoped_refptr<ExtensionsServiceBackend> backend_;
194
195 // The path to the crx file that we're installing.
196 FilePath extension_path_;
197
[email protected]fbcc40302009-06-12 20:45:45198 // The public key of the extension we're installing.
199 std::string public_key_;
200
[email protected]1fca1492009-05-15 22:23:43201 // The path to the copy of the crx file in the temporary directory where we're
202 // unpacking it.
203 FilePath temp_extension_path_;
204
205 // The ID we expect this extension to have, if any.
206 std::string expected_id_;
207
[email protected]2b4053c2009-05-29 20:28:09208 // True if we got a response from the utility process and have cleaned up
209 // already.
210 bool got_response_;
[email protected]1fca1492009-05-15 22:23:43211};
212
[email protected]81e63782009-02-27 19:35:09213ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08214 const CommandLine* command_line,
[email protected]894bb502009-05-21 22:39:57215 MessageLoop* frontend_loop,
[email protected]a1257b12009-06-12 02:51:34216 MessageLoop* backend_loop)
[email protected]e72e8eb82009-06-18 17:21:51217 : extension_prefs_(new ExtensionPrefs(profile->GetPrefs())),
[email protected]894bb502009-05-21 22:39:57218 backend_loop_(backend_loop),
[email protected]81e63782009-02-27 19:35:09219 install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
[email protected]abe7a8942009-06-23 05:14:29220 extensions_enabled_(false),
[email protected]e81dba32009-06-19 20:19:13221 show_extensions_prompts_(true),
222 ready_(false) {
[email protected]36a784c2009-06-23 06:21:08223 // Figure out if extension installation should be enabled.
224 if (command_line->HasSwitch(switches::kEnableExtensions))
225 extensions_enabled_ = true;
226 else if (profile->GetPrefs()->GetBoolean(prefs::kEnableExtensions))
227 extensions_enabled_ = true;
228
[email protected]a1257b12009-06-12 02:51:34229 // We pass ownership of this object to the Backend.
[email protected]e72e8eb82009-06-18 17:21:51230 DictionaryValue* extensions = extension_prefs_->CopyCurrentExtensions();
[email protected]a1257b12009-06-12 02:51:34231 backend_ = new ExtensionsServiceBackend(
232 install_directory_, g_browser_process->resource_dispatcher_host(),
[email protected]abe7a8942009-06-23 05:14:29233 frontend_loop, extensions, extensions_enabled());
[email protected]6014d672008-12-05 00:38:25234}
235
236ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32237 UnloadAllExtensions();
[email protected]6014d672008-12-05 00:38:25238}
239
[email protected]abe7a8942009-06-23 05:14:29240void ExtensionsService::SetExtensionsEnabled(bool enabled) {
241 extensions_enabled_ = true;
242 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
243 &ExtensionsServiceBackend::set_extensions_enabled, enabled));
244}
245
[email protected]9f1087e2009-06-15 17:29:32246void ExtensionsService::Init() {
247 DCHECK(extensions_.size() == 0);
248
[email protected]b68d5ed2009-04-16 02:41:28249 // Start up the extension event routers.
250 ExtensionBrowserEventRouter::GetInstance()->Init();
251
[email protected]9f1087e2009-06-15 17:29:32252 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57253
[email protected]9f1087e2009-06-15 17:29:32254 // TODO(erikkay) this should probably be deferred to a future point
255 // rather than running immediately at startup.
256 CheckForUpdates();
[email protected]894bb502009-05-21 22:39:57257
[email protected]9f1087e2009-06-15 17:29:32258 // TODO(erikkay) this should probably be deferred as well.
259 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25260}
261
[email protected]3cf4f0992009-02-03 23:00:30262void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]894bb502009-05-21 22:39:57263 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
264 &ExtensionsServiceBackend::InstallExtension,
265 extension_path,
266 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30267}
268
[email protected]631cf822009-05-15 07:01:25269void ExtensionsService::UninstallExtension(const std::string& extension_id) {
[email protected]9f1087e2009-06-15 17:29:32270 Extension* extension = GetExtensionById(extension_id);
[email protected]631cf822009-05-15 07:01:25271
[email protected]9f1087e2009-06-15 17:29:32272 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51273 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32274
[email protected]e72e8eb82009-06-18 17:21:51275 extension_prefs_->OnExtensionUninstalled(extension);
[email protected]9f1087e2009-06-15 17:29:32276
277 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51278 if (Extension::LOAD != extension->location()) {
[email protected]9f1087e2009-06-15 17:29:32279 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
280 &ExtensionsServiceBackend::UninstallExtension, extension_id));
281 }
282
283 UnloadExtension(extension_id);
284}
285
286void ExtensionsService::LoadExtension(const FilePath& extension_path) {
287 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
288 &ExtensionsServiceBackend::LoadSingleExtension,
289 extension_path, scoped_refptr<ExtensionsService>(this)));
290}
291
292void ExtensionsService::LoadAllExtensions() {
[email protected]e72e8eb82009-06-18 17:21:51293 // Load the previously installed extensions.
294 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
295 &ExtensionsServiceBackend::LoadInstalledExtensions,
296 scoped_refptr<ExtensionsService>(this),
297 new InstalledExtensions(extension_prefs_.get())));
[email protected]9f1087e2009-06-15 17:29:32298}
299
300void ExtensionsService::CheckForUpdates() {
301 // This installs or updates externally provided extensions.
302 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51303 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]9f1087e2009-06-15 17:29:32304 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
305 &ExtensionsServiceBackend::CheckForExternalUpdates,
306 killed_extensions,
307 scoped_refptr<ExtensionsService>(this)));
308}
309
310void ExtensionsService::UnloadExtension(const std::string& extension_id) {
311 Extension* extension = NULL;
[email protected]631cf822009-05-15 07:01:25312 ExtensionList::iterator iter;
313 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
314 if ((*iter)->id() == extension_id) {
315 extension = *iter;
316 break;
317 }
318 }
319
[email protected]894bb502009-05-21 22:39:57320 // Callers should not send us nonexistant extensions.
321 CHECK(extension);
322
[email protected]631cf822009-05-15 07:01:25323 // Remove the extension from our list.
324 extensions_.erase(iter);
325
[email protected]631cf822009-05-15 07:01:25326 // Tell other services the extension is gone.
327 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
328 NotificationService::AllSources(),
329 Details<Extension>(extension));
330
[email protected]631cf822009-05-15 07:01:25331 delete extension;
332}
333
[email protected]9f1087e2009-06-15 17:29:32334void ExtensionsService::UnloadAllExtensions() {
335 ExtensionList::iterator iter;
336 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
337 // Tell other services the extension is gone.
338 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
339 NotificationService::AllSources(),
340 Details<Extension>(*iter));
341 delete *iter;
342 }
343 extensions_.clear();
344}
345
346void ExtensionsService::ReloadExtensions() {
347 UnloadAllExtensions();
348 LoadAllExtensions();
349}
350
351void ExtensionsService::GarbageCollectExtensions() {
[email protected]894bb502009-05-21 22:39:57352 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
[email protected]9f1087e2009-06-15 17:29:32353 &ExtensionsServiceBackend::GarbageCollectExtensions,
354 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30355}
356
[email protected]e72e8eb82009-06-18 17:21:51357void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13358 ready_ = true;
[email protected]e72e8eb82009-06-18 17:21:51359 NotificationService::current()->Notify(
360 NotificationType::EXTENSIONS_READY,
361 Source<ExtensionsService>(this),
362 NotificationService::NoDetails());
363}
364
[email protected]4a8d3272009-03-10 19:15:08365void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]9f1087e2009-06-15 17:29:32366 scoped_ptr<ExtensionList> cleanup(new_extensions);
367
[email protected]abe7a8942009-06-23 05:14:29368 // Filter out any extensions that shouldn't be loaded. Themes are always
369 // loaded, but other extensions are only loaded if the extensions system is
370 // enabled.
[email protected]a1257b12009-06-12 02:51:34371 ExtensionList enabled_extensions;
[email protected]86a274072009-06-11 02:06:45372 for (ExtensionList::iterator iter = new_extensions->begin();
[email protected]25b34332009-06-05 21:53:19373 iter != new_extensions->end(); ++iter) {
[email protected]abe7a8942009-06-23 05:14:29374 if (extensions_enabled() || (*iter)->IsTheme() ||
375 (*iter)->location() == Extension::EXTERNAL_REGISTRY) {
[email protected]9f1087e2009-06-15 17:29:32376 Extension* old = GetExtensionById((*iter)->id());
377 if (old) {
378 if ((*iter)->version()->CompareTo(*(old->version())) > 0) {
379 // To upgrade an extension in place, unload the old one and
380 // then load the new one.
381 // TODO(erikkay) issue 12399
382 UnloadExtension(old->id());
383 } else {
384 // We already have the extension of the same or older version.
385 LOG(WARNING) << "Duplicate extension load attempt: " << (*iter)->id();
386 delete *iter;
387 continue;
388 }
389 }
[email protected]86a274072009-06-11 02:06:45390 enabled_extensions.push_back(*iter);
[email protected]9f1087e2009-06-15 17:29:32391 extensions_.push_back(*iter);
[email protected]ba74f352009-06-11 18:54:45392 } else {
393 // Extensions that get enabled get added to extensions_ and deleted later.
394 // Anything skipped must be deleted now so we don't leak.
395 delete *iter;
396 }
[email protected]86a274072009-06-11 02:06:45397 }
398
[email protected]e72e8eb82009-06-18 17:21:51399 if (enabled_extensions.size()) {
400 NotificationService::current()->Notify(
401 NotificationType::EXTENSIONS_LOADED,
402 NotificationService::AllSources(),
403 Details<ExtensionList>(&enabled_extensions));
404 }
[email protected]6014d672008-12-05 00:38:25405}
406
[email protected]a57209872009-05-04 22:53:14407void ExtensionsService::OnExtensionInstalled(Extension* extension,
[email protected]fbcc40302009-06-12 20:45:45408 Extension::InstallType install_type) {
[email protected]e72e8eb82009-06-18 17:21:51409 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19410
[email protected]4a190632009-05-09 01:07:42411 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
412 // to apply it.
413 if (extension->IsTheme()) {
414 NotificationService::current()->Notify(
415 NotificationType::THEME_INSTALLED,
416 NotificationService::AllSources(),
417 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27418 } else {
419 NotificationService::current()->Notify(
420 NotificationType::EXTENSION_INSTALLED,
421 NotificationService::AllSources(),
422 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42423 }
424}
425
[email protected]fbcc40302009-06-12 20:45:45426void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]9f1087e2009-06-15 17:29:32427 Extension* extension = GetExtensionById(id);
[email protected]4a190632009-05-09 01:07:42428 if (extension && extension->IsTheme()) {
429 NotificationService::current()->Notify(
430 NotificationType::THEME_INSTALLED,
431 NotificationService::AllSources(),
432 Details<Extension>(extension));
433 }
[email protected]cc655912009-01-29 23:19:19434}
435
[email protected]9f1087e2009-06-15 17:29:32436Extension* ExtensionsService::GetExtensionById(const std::string& id) {
[email protected]ce5c4502009-05-06 16:46:11437 for (ExtensionList::const_iterator iter = extensions_.begin();
[email protected]4a190632009-05-09 01:07:42438 iter != extensions_.end(); ++iter) {
[email protected]ce5c4502009-05-06 16:46:11439 if ((*iter)->id() == id)
440 return *iter;
441 }
442 return NULL;
443}
444
[email protected]9f1087e2009-06-15 17:29:32445Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
446 std::string host = url.host();
447 return GetExtensionById(host);
448}
449
[email protected]a1257b12009-06-12 02:51:34450void ExtensionsService::ClearProvidersForTesting() {
451 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
452 &ExtensionsServiceBackend::ClearProvidersForTesting));
453}
454
455void ExtensionsService::SetProviderForTesting(
456 Extension::Location location, ExternalExtensionProvider* test_provider) {
457 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
458 &ExtensionsServiceBackend::SetProviderForTesting,
459 location, test_provider));
460}
461
[email protected]6014d672008-12-05 00:38:25462// ExtensionsServicesBackend
463
[email protected]894bb502009-05-21 22:39:57464ExtensionsServiceBackend::ExtensionsServiceBackend(
465 const FilePath& install_directory, ResourceDispatcherHost* rdh,
[email protected]abe7a8942009-06-23 05:14:29466 MessageLoop* frontend_loop, DictionaryValue* extension_prefs,
467 bool extensions_enabled)
[email protected]0c7bc4b2009-05-30 01:47:08468 : frontend_(NULL),
469 install_directory_(install_directory),
[email protected]894bb502009-05-21 22:39:57470 resource_dispatcher_host_(rdh),
[email protected]0c7bc4b2009-05-30 01:47:08471 alert_on_error_(false),
[email protected]abe7a8942009-06-23 05:14:29472 frontend_loop_(frontend_loop),
[email protected]36a784c2009-06-23 06:21:08473 extensions_enabled_(extensions_enabled) {
[email protected]a1257b12009-06-12 02:51:34474 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01475 linked_ptr<ExternalExtensionProvider>(
476 new ExternalPrefExtensionProvider(extension_prefs));
[email protected]a1257b12009-06-12 02:51:34477#if defined(OS_WIN)
478 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01479 linked_ptr<ExternalExtensionProvider>(
480 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34481#endif
482}
483
484ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57485}
486
[email protected]e72e8eb82009-06-18 17:21:51487void ExtensionsServiceBackend::LoadInstalledExtensions(
[email protected]25b34332009-06-05 21:53:19488 scoped_refptr<ExtensionsService> frontend,
[email protected]e72e8eb82009-06-18 17:21:51489 InstalledExtensions* installed) {
490 scoped_ptr<InstalledExtensions> cleanup(installed);
[email protected]b0beaa662009-02-26 00:04:15491 frontend_ = frontend;
492 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15493
[email protected]e72e8eb82009-06-18 17:21:51494 // Call LoadInstalledExtension for each extension |installed| knows about.
495 scoped_ptr<InstalledExtensions::Callback> callback(
496 NewCallback(this, &ExtensionsServiceBackend::LoadInstalledExtension));
497 installed->VisitInstalledExtensions(callback.get());
498
499 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
500 frontend_, &ExtensionsService::OnLoadedInstalledExtensions));
[email protected]9f1087e2009-06-15 17:29:32501}
[email protected]b0beaa662009-02-26 00:04:15502
[email protected]9f1087e2009-06-15 17:29:32503void ExtensionsServiceBackend::GarbageCollectExtensions(
504 scoped_refptr<ExtensionsService> frontend) {
505 frontend_ = frontend;
506 alert_on_error_ = false;
507
508 // Nothing to clean up if it doesn't exist.
509 if (!file_util::DirectoryExists(install_directory_))
510 return;
511
[email protected]f36fa4fb2009-06-19 18:23:50512 file_util::AbsolutePath(&install_directory_);
[email protected]540f91b2009-03-26 19:37:43513
[email protected]bf24d2c2009-02-24 23:07:45514 LOG(INFO) << "Loading installed extensions...";
515
[email protected]6014d672008-12-05 00:38:25516 // Find all child directories in the install directory and load their
517 // manifests. Post errors and results to the frontend.
[email protected]cc5da332009-03-04 08:02:51518 file_util::FileEnumerator enumerator(install_directory_,
[email protected]a1257b12009-06-12 02:51:34519 false, // Not recursive.
[email protected]6014d672008-12-05 00:38:25520 file_util::FileEnumerator::DIRECTORIES);
[email protected]cc5da332009-03-04 08:02:51521 FilePath extension_path;
522 for (extension_path = enumerator.Next(); !extension_path.value().empty();
523 extension_path = enumerator.Next()) {
524 std::string extension_id = WideToASCII(
525 extension_path.BaseName().ToWStringHack());
[email protected]5a2721f62009-06-13 07:08:20526
[email protected]96088fb2009-05-26 19:08:13527 // The utility process might be in the middle of unpacking an extension, so
528 // ignore the temp unpacking directory.
529 if (extension_id == kUnpackExtensionDir)
530 continue;
[email protected]894bb502009-05-21 22:39:57531
532 // If there is no Current Version file, just delete the directory and move
533 // on. This can legitimately happen when an uninstall does not complete, for
534 // example, when a plugin is in use at uninstall time.
535 FilePath current_version_path = extension_path.AppendASCII(
536 ExtensionsService::kCurrentVersionFileName);
537 if (!file_util::PathExists(current_version_path)) {
538 LOG(INFO) << "Deleting incomplete install for directory "
539 << WideToASCII(extension_path.ToWStringHack()) << ".";
[email protected]25b34332009-06-05 21:53:19540 file_util::Delete(extension_path, true); // Recursive.
[email protected]894bb502009-05-21 22:39:57541 continue;
542 }
543
[email protected]9f1087e2009-06-15 17:29:32544 // Ignore directories that aren't valid IDs.
545 if (!Extension::IdIsValid(extension_id)) {
546 LOG(WARNING) << "Invalid extension ID encountered in extensions "
547 "directory: " << extension_id;
548 // TODO(erikkay) delete these eventually too...
[email protected]cc5da332009-03-04 08:02:51549 continue;
550 }
551
[email protected]9f1087e2009-06-15 17:29:32552 // TODO(erikkay) check for extensions that aren't loaded?
[email protected]6014d672008-12-05 00:38:25553 }
[email protected]6014d672008-12-05 00:38:25554}
555
[email protected]b0beaa662009-02-26 00:04:15556void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57557 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15558 frontend_ = frontend;
559
560 // Explicit UI loads are always noisy.
561 alert_on_error_ = true;
562
[email protected]cc5da332009-03-04 08:02:51563 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50564 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45565
566 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51567 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45568
[email protected]5bfb1eb0a2009-04-08 18:33:30569 Extension* extension = LoadExtension(extension_path,
[email protected]a1257b12009-06-12 02:51:34570 Extension::LOAD,
[email protected]e72e8eb82009-06-18 17:21:51571 false); // Don't require id.
[email protected]0877fd92009-02-03 16:34:06572 if (extension) {
573 ExtensionList* extensions = new ExtensionList;
574 extensions->push_back(extension);
[email protected]b0beaa662009-02-26 00:04:15575 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06576 }
[email protected]0877fd92009-02-03 16:34:06577}
578
[email protected]e72e8eb82009-06-18 17:21:51579void ExtensionsServiceBackend::LoadInstalledExtension(
580 const std::string& id, const FilePath& path, Extension::Location location) {
581 if (CheckExternalUninstall(id, location)) {
582 // TODO(erikkay): Possibly defer this operation to avoid slowing initial
583 // load of extensions.
584 UninstallExtension(id);
585
586 // No error needs to be reported. The extension effectively doesn't exist.
587 return;
588 }
589
590 Extension* extension =
591 LoadExtension(FilePath(path), location, true); // Require id.
592
593 // TODO(erikkay) now we only report a single extension loaded at a time.
594 // Perhaps we should change the notifications to remove ExtensionList.
595 ExtensionList* extensions = new ExtensionList;
596 if (extension)
597 extensions->push_back(extension);
598 ReportExtensionsLoaded(extensions);
599}
600
[email protected]fbcc40302009-06-12 20:45:45601DictionaryValue* ExtensionsServiceBackend::ReadManifest(FilePath manifest_path,
602 std::string* error) {
603 JSONFileValueSerializer serializer(manifest_path);
604 scoped_ptr<Value> root(serializer.Deserialize(error));
605 if (!root.get())
606 return NULL;
607
608 if (!root->IsType(Value::TYPE_DICTIONARY)) {
609 *error = Extension::kInvalidManifestError;
610 return NULL;
611 }
612
613 return static_cast<DictionaryValue*>(root.release());
614}
615
[email protected]cc5da332009-03-04 08:02:51616Extension* ExtensionsServiceBackend::LoadExtension(
[email protected]a1257b12009-06-12 02:51:34617 const FilePath& extension_path,
618 Extension::Location location,
619 bool require_id) {
[email protected]0877fd92009-02-03 16:34:06620 FilePath manifest_path =
[email protected]cc5da332009-03-04 08:02:51621 extension_path.AppendASCII(Extension::kManifestFilename);
[email protected]0877fd92009-02-03 16:34:06622 if (!file_util::PathExists(manifest_path)) {
[email protected]cc5da332009-03-04 08:02:51623 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06624 return NULL;
625 }
626
[email protected]0877fd92009-02-03 16:34:06627 std::string error;
[email protected]fbcc40302009-06-12 20:45:45628 scoped_ptr<DictionaryValue> root(ReadManifest(manifest_path, &error));
[email protected]0877fd92009-02-03 16:34:06629 if (!root.get()) {
[email protected]cc5da332009-03-04 08:02:51630 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06631 return NULL;
632 }
633
[email protected]cc5da332009-03-04 08:02:51634 scoped_ptr<Extension> extension(new Extension(extension_path));
[email protected]fbcc40302009-06-12 20:45:45635 if (!extension->InitFromValue(*root.get(), require_id, &error)) {
[email protected]cc5da332009-03-04 08:02:51636 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06637 return NULL;
638 }
[email protected]8d6d9ff2009-02-20 08:14:39639
[email protected]a1257b12009-06-12 02:51:34640 extension->set_location(location);
[email protected]631cf822009-05-15 07:01:25641
[email protected]12198912009-06-05 03:41:22642 // Theme resource validation.
643 if (extension->IsTheme()) {
644 DictionaryValue* images_value = extension->GetThemeImages();
645 DictionaryValue::key_iterator iter = images_value->begin_keys();
646 while (iter != images_value->end_keys()) {
647 std::string val;
[email protected]25b34332009-06-05 21:53:19648 if (images_value->GetString(*iter , &val)) {
[email protected]12198912009-06-05 03:41:22649 FilePath image_path = extension->path().AppendASCII(val);
650 if (!file_util::PathExists(image_path)) {
651 ReportExtensionLoadError(extension_path,
652 StringPrintf("Could not load '%s' for theme.",
653 WideToUTF8(image_path.ToWStringHack()).c_str()));
654 return NULL;
655 }
656 }
657 ++iter;
658 }
659
660 // Themes cannot contain other extension types.
661 return extension.release();
662 }
663
[email protected]d24070e22009-05-21 19:26:59664 // Validate that claimed script resources actually exist.
[email protected]3cfbd0e2009-03-18 21:26:24665 for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
666 const UserScript& script = extension->content_scripts()[i];
667
668 for (size_t j = 0; j < script.js_scripts().size(); j++) {
669 const FilePath& path = script.js_scripts()[j].path();
670 if (!file_util::PathExists(path)) {
671 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59672 StringPrintf("Could not load '%s' for content script.",
673 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24674 return NULL;
675 }
676 }
677
678 for (size_t j = 0; j < script.css_scripts().size(); j++) {
679 const FilePath& path = script.css_scripts()[j].path();
680 if (!file_util::PathExists(path)) {
681 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59682 StringPrintf("Could not load '%s' for content script.",
683 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24684 return NULL;
685 }
[email protected]8d6d9ff2009-02-20 08:14:39686 }
687 }
688
[email protected]c533bb22009-06-03 19:06:11689 for (size_t i = 0; i < extension->plugins().size(); ++i) {
690 const Extension::PluginInfo& plugin = extension->plugins()[i];
691 if (!file_util::PathExists(plugin.path)) {
692 ReportExtensionLoadError(extension_path,
693 StringPrintf("Could not load '%s' for plugin.",
694 WideToUTF8(plugin.path.ToWStringHack()).c_str()));
695 return NULL;
696 }
697 }
698
[email protected]d24070e22009-05-21 19:26:59699 // Validate icon location for page actions.
700 const PageActionMap& page_actions = extension->page_actions();
701 for (PageActionMap::const_iterator i(page_actions.begin());
702 i != page_actions.end(); ++i) {
703 PageAction* page_action = i->second;
704 FilePath path = page_action->icon_path();
705 if (!file_util::PathExists(path)) {
706 ReportExtensionLoadError(extension_path,
707 StringPrintf("Could not load icon '%s' for page action.",
708 WideToUTF8(path.ToWStringHack()).c_str()));
709 return NULL;
710 }
711 }
712
[email protected]0877fd92009-02-03 16:34:06713 return extension.release();
714}
715
[email protected]6014d672008-12-05 00:38:25716void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51717 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15718 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51719 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00720 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19721 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18722 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25723}
724
725void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15726 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57727 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
728 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25729}
[email protected]cc655912009-01-29 23:19:19730
[email protected]b0beaa662009-02-26 00:04:15731bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
732 std::string* version_string) {
[email protected]18a12352009-01-31 01:33:28733 FilePath current_version =
[email protected]b0beaa662009-02-26 00:04:15734 dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
[email protected]18a12352009-01-31 01:33:28735 if (file_util::PathExists(current_version)) {
736 if (file_util::ReadFileToString(current_version, version_string)) {
737 TrimWhitespace(*version_string, TRIM_ALL, version_string);
738 return true;
739 }
740 }
741 return false;
742}
743
[email protected]fbcc40302009-06-12 20:45:45744Extension::InstallType ExtensionsServiceBackend::CompareToInstalledVersion(
745 const std::string& id,
[email protected]b0beaa662009-02-26 00:04:15746 const std::string& new_version_str,
[email protected]fbcc40302009-06-12 20:45:45747 std::string *current_version_str) {
748 CHECK(current_version_str);
749 FilePath dir(install_directory_.AppendASCII(id.c_str()));
750 if (!ReadCurrentVersion(dir, current_version_str))
751 return Extension::NEW_INSTALL;
752
[email protected]b0beaa662009-02-26 00:04:15753 scoped_ptr<Version> current_version(
[email protected]fbcc40302009-06-12 20:45:45754 Version::GetVersionFromString(*current_version_str));
[email protected]b0beaa662009-02-26 00:04:15755 scoped_ptr<Version> new_version(
[email protected]fbcc40302009-06-12 20:45:45756 Version::GetVersionFromString(new_version_str));
757 int comp = new_version->CompareTo(*current_version);
758 if (comp > 0)
759 return Extension::UPGRADE;
760 else if (comp == 0)
761 return Extension::REINSTALL;
762 else
763 return Extension::DOWNGRADE;
764}
765
766bool ExtensionsServiceBackend::NeedsReinstall(const std::string& id,
767 const std::string& current_version) {
768 // Verify that the directory actually exists.
769 // TODO(erikkay): A further step would be to verify that the extension
770 // has actually loaded successfully.
771 FilePath dir(install_directory_.AppendASCII(id.c_str()));
772 FilePath version_dir(dir.AppendASCII(current_version));
773 return !file_util::PathExists(version_dir);
[email protected]cc655912009-01-29 23:19:19774}
775
[email protected]f0a51fb52009-03-05 12:46:38776bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
[email protected]b0beaa662009-02-26 00:04:15777 const FilePath& dest_dir) {
[email protected]cc655912009-01-29 23:19:19778 if (file_util::PathExists(dest_dir)) {
779 // By the time we get here, it should be safe to assume that this directory
780 // is not currently in use (it's not the current active version).
781 if (!file_util::Delete(dest_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51782 ReportExtensionInstallError(source_dir,
[email protected]cc655912009-01-29 23:19:19783 "Can't delete existing version directory.");
784 return false;
785 }
786 } else {
787 FilePath parent = dest_dir.DirName();
788 if (!file_util::DirectoryExists(parent)) {
789 if (!file_util::CreateDirectory(parent)) {
[email protected]cc5da332009-03-04 08:02:51790 ReportExtensionInstallError(source_dir,
791 "Couldn't create extension directory.");
[email protected]cc655912009-01-29 23:19:19792 return false;
793 }
794 }
795 }
796 if (!file_util::Move(source_dir, dest_dir)) {
[email protected]cc5da332009-03-04 08:02:51797 ReportExtensionInstallError(source_dir,
798 "Couldn't move temporary directory.");
[email protected]cc655912009-01-29 23:19:19799 return false;
800 }
801
802 return true;
803}
804
[email protected]b0beaa662009-02-26 00:04:15805bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
806 std::string version) {
[email protected]cc655912009-01-29 23:19:19807 // Write out the new CurrentVersion file.
808 // <profile>/Extension/<name>/CurrentVersion
809 FilePath current_version =
810 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
811 FilePath current_version_old =
812 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
813 if (file_util::PathExists(current_version_old)) {
814 if (!file_util::Delete(current_version_old, false)) {
[email protected]cc5da332009-03-04 08:02:51815 ReportExtensionInstallError(dest_dir,
816 "Couldn't remove CurrentVersion_old file.");
[email protected]cc655912009-01-29 23:19:19817 return false;
818 }
819 }
820 if (file_util::PathExists(current_version)) {
821 if (!file_util::Move(current_version, current_version_old)) {
[email protected]cc5da332009-03-04 08:02:51822 ReportExtensionInstallError(dest_dir,
823 "Couldn't move CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19824 return false;
825 }
826 }
827 net::FileStream stream;
828 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
829 if (stream.Open(current_version, flags) != 0)
830 return false;
831 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
832 // Restore the old CurrentVersion.
833 if (file_util::PathExists(current_version_old)) {
834 if (!file_util::Move(current_version_old, current_version)) {
[email protected]f0a51fb52009-03-05 12:46:38835 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
[email protected]cc655912009-01-29 23:19:19836 " to " << current_version.value();
[email protected]b0beaa662009-02-26 00:04:15837
[email protected]cc655912009-01-29 23:19:19838 // TODO(erikkay): This is an ugly state to be in. Try harder?
839 }
840 }
[email protected]f0a51fb52009-03-05 12:46:38841 ReportExtensionInstallError(dest_dir,
[email protected]cc5da332009-03-04 08:02:51842 "Couldn't create CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19843 return false;
844 }
845 return true;
846}
847
[email protected]b0beaa662009-02-26 00:04:15848void ExtensionsServiceBackend::InstallExtension(
[email protected]894bb502009-05-21 22:39:57849 const FilePath& extension_path, scoped_refptr<ExtensionsService> frontend) {
[email protected]cc655912009-01-29 23:19:19850 LOG(INFO) << "Installing extension " << extension_path.value();
851
[email protected]b0beaa662009-02-26 00:04:15852 frontend_ = frontend;
[email protected]0e34d7892009-06-05 19:17:40853 alert_on_error_ = true;
[email protected]b0beaa662009-02-26 00:04:15854
[email protected]9f1087e2009-06-15 17:29:32855 InstallOrUpdateExtension(extension_path, std::string());
[email protected]b0beaa662009-02-26 00:04:15856}
857
[email protected]1fca1492009-05-15 22:23:43858void ExtensionsServiceBackend::InstallOrUpdateExtension(
[email protected]9f1087e2009-06-15 17:29:32859 const FilePath& extension_path, const std::string& expected_id) {
[email protected]fbcc40302009-06-12 20:45:45860 std::string actual_public_key;
861 if (!ValidateSignature(extension_path, &actual_public_key))
862 return; // Failures reported within ValidateSignature().
863
864 UnpackerClient* client = new UnpackerClient(
[email protected]9f1087e2009-06-15 17:29:32865 this, extension_path, actual_public_key, expected_id);
[email protected]1fca1492009-05-15 22:23:43866 client->Start();
867}
[email protected]cc655912009-01-29 23:19:19868
[email protected]fbcc40302009-06-12 20:45:45869bool ExtensionsServiceBackend::ValidateSignature(const FilePath& extension_path,
870 std::string* key_out) {
871 ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
872 if (!file.get()) {
873 ReportExtensionInstallError(extension_path, "Could not open file.");
874 return NULL;
875 }
876
877 // Read and verify the header.
878 ExtensionsService::ExtensionHeader header;
879 size_t len;
880
881 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
882 // appears that we don't have any endian/alignment aware serialization
883 // code in the code base. So for now, this assumes that we're running
884 // on a little endian machine with 4 byte alignment.
885 len = fread(&header, 1, sizeof(ExtensionsService::ExtensionHeader),
886 file.get());
887 if (len < sizeof(ExtensionsService::ExtensionHeader)) {
888 ReportExtensionInstallError(extension_path, kInvalidExtensionHeaderError);
889 return false;
890 }
891 if (strncmp(ExtensionsService::kExtensionHeaderMagic, header.magic,
892 sizeof(header.magic))) {
893 ReportExtensionInstallError(extension_path, kBadMagicNumberError);
894 return false;
895 }
896 if (header.version != ExtensionsService::kCurrentVersion) {
897 ReportExtensionInstallError(extension_path, kBadVersionNumberError);
898 return false;
899 }
900 if (header.key_size > ExtensionsService::kMaxPublicKeySize ||
901 header.signature_size > ExtensionsService::kMaxSignatureSize) {
902 ReportExtensionInstallError(extension_path, kBadHeaderSizeError);
903 return false;
904 }
905
906 std::vector<uint8> key;
907 key.resize(header.key_size);
908 len = fread(&key.front(), sizeof(uint8), header.key_size, file.get());
909 if (len < header.key_size) {
910 ReportExtensionInstallError(extension_path, kInvalidPublicKeyError);
911 return false;
912 }
913
914 std::vector<uint8> signature;
915 signature.resize(header.signature_size);
916 len = fread(&signature.front(), sizeof(uint8), header.signature_size,
917 file.get());
918 if (len < header.signature_size) {
919 ReportExtensionInstallError(extension_path, kInvalidSignatureError);
920 return false;
921 }
922
923 // Note: this structure is an ASN.1 which encodes the algorithm used
924 // with its parameters. This is defined in PKCS #1 v2.1 (RFC 3447).
925 // It is encoding: { OID sha1WithRSAEncryption PARAMETERS NULL }
926 // TODO(aa): This needs to be factored away someplace common.
927 const uint8 signature_algorithm[15] = {
928 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
929 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
930 };
931
932 base::SignatureVerifier verifier;
933 if (!verifier.VerifyInit(signature_algorithm,
934 sizeof(signature_algorithm),
935 &signature.front(),
936 signature.size(),
937 &key.front(),
938 key.size())) {
939 ReportExtensionInstallError(extension_path,
940 kSignatureVerificationInitFailed);
941 return false;
942 }
943
944 unsigned char buf[1 << 12];
945 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
946 verifier.VerifyUpdate(buf, len);
947
948 if (!verifier.VerifyFinal()) {
949 ReportExtensionInstallError(extension_path, kSignatureVerificationFailed);
950 return false;
951 }
952
953 net::Base64Encode(std::string(reinterpret_cast<char*>(&key.front()),
954 key.size()), key_out);
955 return true;
956}
957
[email protected]1fca1492009-05-15 22:23:43958void ExtensionsServiceBackend::OnExtensionUnpacked(
959 const FilePath& extension_path,
960 const FilePath& temp_extension_dir,
961 const std::string expected_id,
[email protected]902f7cd2009-05-22 19:02:19962 const DictionaryValue& manifest,
963 const std::vector< Tuple2<SkBitmap, FilePath> >& images) {
[email protected]1fca1492009-05-15 22:23:43964 Extension extension;
965 std::string error;
[email protected]902f7cd2009-05-22 19:02:19966 if (!extension.InitFromValue(manifest,
[email protected]1fca1492009-05-15 22:23:43967 true, // require ID
968 &error)) {
969 ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
970 return;
[email protected]0b344962009-03-31 04:21:45971 }
972
[email protected]abe7a8942009-06-23 05:14:29973 Extension::Location location = Extension::INTERNAL;
974 LookupExternalExtension(extension.id(), NULL, &location);
975
976 // We currently only allow themes and registry-installed extensions to be
977 // installed.
978 if (!extensions_enabled_ &&
979 !extension.IsTheme() &&
980 location != Extension::EXTERNAL_REGISTRY) {
[email protected]e2eb43112009-05-29 21:19:54981 ReportExtensionInstallError(extension_path,
[email protected]36a784c2009-06-23 06:21:08982 "Extensions are not enabled. Add --enable-extensions to the "
983 "command-line to enable extensions.\n\n"
984 "This is a temporary message and it will be removed when extensions "
985 "UI is finalized.");
[email protected]e2eb43112009-05-29 21:19:54986 return;
987 }
988
[email protected]0e34d7892009-06-05 19:17:40989#if defined(OS_WIN)
[email protected]36a784c2009-06-23 06:21:08990 // We don't show the install dialog for themes or external extensions.
[email protected]abe7a8942009-06-23 05:14:29991 if (!extension.IsTheme() &&
992 !Extension::IsExternalLocation(location) &&
[email protected]0e34d7892009-06-05 19:17:40993 frontend_->show_extensions_prompts() &&
[email protected]f90d0132009-06-15 20:40:50994 win_util::MessageBox(GetForegroundWindow(),
[email protected]0e34d7892009-06-05 19:17:40995 L"Are you sure you want to install this extension?\n\n"
996 L"This is a temporary message and it will be removed when extensions "
997 L"UI is finalized.",
998 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
[email protected]31e8a012009-06-06 08:13:34999 MB_OKCANCEL) != IDOK) {
[email protected]0e34d7892009-06-05 19:17:401000 ReportExtensionInstallError(extension_path,
1001 "User did not allow extension to be installed.");
1002 return;
1003 }
1004#endif
1005
[email protected]b0beaa662009-02-26 00:04:151006 // If an expected id was provided, make sure it matches.
[email protected]1fca1492009-05-15 22:23:431007 if (!expected_id.empty() && expected_id != extension.id()) {
[email protected]25b34332009-06-05 21:53:191008 std::string error_msg = "ID in new extension manifest (";
1009 error_msg += extension.id();
1010 error_msg += ") does not match expected ID (";
1011 error_msg += expected_id;
1012 error_msg += ")";
1013 ReportExtensionInstallError(extension_path, error_msg);
[email protected]1fca1492009-05-15 22:23:431014 return;
[email protected]cc655912009-01-29 23:19:191015 }
1016
1017 // <profile>/Extensions/<id>
[email protected]b0beaa662009-02-26 00:04:151018 FilePath dest_dir = install_directory_.AppendASCII(extension.id());
[email protected]cc655912009-01-29 23:19:191019 std::string version = extension.VersionString();
[email protected]b0beaa662009-02-26 00:04:151020 std::string current_version;
[email protected]fbcc40302009-06-12 20:45:451021 Extension::InstallType install_type =
1022 CompareToInstalledVersion(extension.id(), version, &current_version);
1023
1024 // Do not allow downgrade.
1025 if (install_type == Extension::DOWNGRADE) {
1026 ReportExtensionInstallError(extension_path,
1027 "Error: Attempt to downgrade extension from more recent version.");
1028 return;
1029 }
1030
1031 if (install_type == Extension::REINSTALL) {
1032 if (NeedsReinstall(extension.id(), current_version)) {
1033 // Treat corrupted existing installation as new install case.
1034 install_type = Extension::NEW_INSTALL;
1035 } else {
1036 // The client may use this as a signal (to switch themes, for instance).
1037 ReportExtensionOverinstallAttempted(extension.id());
[email protected]1fca1492009-05-15 22:23:431038 return;
[email protected]fbcc40302009-06-12 20:45:451039 }
[email protected]b0beaa662009-02-26 00:04:151040 }
[email protected]cc655912009-01-29 23:19:191041
[email protected]902f7cd2009-05-22 19:02:191042 // Write our parsed manifest back to disk, to ensure it doesn't contain an
1043 // exploitable bug that can be used to compromise the browser.
1044 std::string manifest_json;
1045 JSONStringValueSerializer serializer(&manifest_json);
1046 serializer.set_pretty_print(true);
1047 if (!serializer.Serialize(manifest)) {
1048 ReportExtensionInstallError(extension_path,
1049 "Error serializing manifest.json.");
1050 return;
1051 }
1052
1053 FilePath manifest_path =
1054 temp_extension_dir.AppendASCII(Extension::kManifestFilename);
1055 if (!file_util::WriteFile(manifest_path,
1056 manifest_json.data(), manifest_json.size())) {
1057 ReportExtensionInstallError(extension_path, "Error saving manifest.json.");
1058 return;
1059 }
1060
[email protected]facd7a7652009-06-05 23:15:021061 // Delete any images that may be used by the browser. We're going to write
1062 // out our own versions of the parsed images, and we want to make sure the
1063 // originals are gone for good.
1064 std::set<FilePath> image_paths = extension.GetBrowserImages();
1065 if (image_paths.size() != images.size()) {
1066 ReportExtensionInstallError(extension_path,
1067 "Decoded images don't match what's in the manifest.");
1068 return;
1069 }
1070
1071 for (std::set<FilePath>::iterator it = image_paths.begin();
1072 it != image_paths.end(); ++it) {
1073 if (!file_util::Delete(temp_extension_dir.Append(*it), false)) {
1074 ReportExtensionInstallError(extension_path,
1075 "Error removing old image file.");
1076 return;
1077 }
1078 }
1079
[email protected]902f7cd2009-05-22 19:02:191080 // Write our parsed images back to disk as well.
1081 for (size_t i = 0; i < images.size(); ++i) {
1082 const SkBitmap& image = images[i].a;
1083 FilePath path = temp_extension_dir.Append(images[i].b);
1084
1085 std::vector<unsigned char> image_data;
1086 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
1087 // though they may originally be .jpg, etc. Figure something out.
1088 // http://code.google.com/p/chromium/issues/detail?id=12459
1089 if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) {
1090 ReportExtensionInstallError(extension_path,
1091 "Error re-encoding theme image.");
1092 return;
1093 }
1094
1095 // Note: we're overwriting existing files that the utility process wrote,
1096 // so we can be sure the directory exists.
1097 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
1098 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
1099 ReportExtensionInstallError(extension_path, "Error saving theme image.");
1100 return;
1101 }
1102 }
1103
[email protected]cc655912009-01-29 23:19:191104 // <profile>/Extensions/<dir_name>/<version>
[email protected]1fca1492009-05-15 22:23:431105 FilePath version_dir = dest_dir.AppendASCII(version);
[email protected]902f7cd2009-05-22 19:02:191106
1107 // If anything fails after this, we want to delete the extension dir.
1108 ScopedTempDir scoped_version_dir;
1109 scoped_version_dir.Set(version_dir);
1110
[email protected]1fca1492009-05-15 22:23:431111 if (!InstallDirSafely(temp_extension_dir, version_dir))
1112 return;
[email protected]cc655912009-01-29 23:19:191113
[email protected]902f7cd2009-05-22 19:02:191114 if (!SetCurrentVersion(dest_dir, version))
[email protected]1fca1492009-05-15 22:23:431115 return;
[email protected]cc655912009-01-29 23:19:191116
[email protected]9f1087e2009-06-15 17:29:321117 Extension* loaded = LoadExtension(version_dir,
1118 location,
1119 true); // require id
1120 CHECK(loaded);
[email protected]1fca1492009-05-15 22:23:431121
[email protected]9f1087e2009-06-15 17:29:321122 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
1123 frontend_, &ExtensionsService::OnExtensionInstalled, loaded,
1124 install_type));
[email protected]894bb502009-05-21 22:39:571125
[email protected]9f1087e2009-06-15 17:29:321126 // Only one extension, but ReportExtensionsLoaded can handle multiple,
1127 // so we need to construct a list.
1128 scoped_ptr<ExtensionList> extensions(new ExtensionList);
1129 extensions->push_back(loaded);
[email protected]894bb502009-05-21 22:39:571130
[email protected]9f1087e2009-06-15 17:29:321131 // Hand off ownership of the loaded extensions to the frontend.
1132 ReportExtensionsLoaded(extensions.release());
[email protected]902f7cd2009-05-22 19:02:191133
1134 scoped_version_dir.Take();
[email protected]cc655912009-01-29 23:19:191135}
1136
1137void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:511138 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:151139
[email protected]cc655912009-01-29 23:19:191140 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:511141 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:191142 std::string message =
1143 StringPrintf("Could not install extension from '%s'. %s",
1144 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:181145 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]cc655912009-01-29 23:19:191146}
1147
[email protected]fbcc40302009-06-12 20:45:451148void ExtensionsServiceBackend::ReportExtensionOverinstallAttempted(
[email protected]4a190632009-05-09 01:07:421149 const std::string& id) {
[email protected]894bb502009-05-21 22:39:571150 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]fbcc40302009-06-12 20:45:451151 frontend_, &ExtensionsService::OnExtensionOverinstallAttempted, id));
[email protected]b0beaa662009-02-26 00:04:151152}
1153
[email protected]25b34332009-06-05 21:53:191154bool ExtensionsServiceBackend::ShouldSkipInstallingExtension(
1155 const std::set<std::string>& ids_to_ignore,
1156 const std::string& id) {
1157 if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
1158 LOG(INFO) << "Skipping uninstalled external extension " << id;
1159 return true;
1160 }
1161 return false;
1162}
1163
1164void ExtensionsServiceBackend::CheckVersionAndInstallExtension(
[email protected]a1257b12009-06-12 02:51:341165 const std::string& id, const Version* extension_version,
[email protected]9f1087e2009-06-15 17:29:321166 const FilePath& extension_path) {
[email protected]25b34332009-06-05 21:53:191167 if (ShouldInstall(id, extension_version))
[email protected]9f1087e2009-06-15 17:29:321168 InstallOrUpdateExtension(FilePath(extension_path), id);
[email protected]25b34332009-06-05 21:53:191169}
1170
[email protected]a1257b12009-06-12 02:51:341171bool ExtensionsServiceBackend::LookupExternalExtension(
1172 const std::string& id, Version** version, Extension::Location* location) {
1173 scoped_ptr<Version> extension_version;
1174 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1175 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011176 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341177 extension_version.reset(provider->RegisteredVersion(id, location));
1178 if (extension_version.get()) {
1179 if (version)
1180 *version = extension_version.release();
1181 return true;
1182 }
1183 }
1184 return false;
1185}
1186
[email protected]b0beaa662009-02-26 00:04:151187// Some extensions will autoupdate themselves externally from Chrome. These
1188// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191189// these, the extension will register its location in the the preferences file
1190// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151191// check that location for a .crx file, which it will then install locally if
1192// a new version is available.
1193void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571194 std::set<std::string> ids_to_ignore,
1195 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151196 // Note that this installation is intentionally silent (since it didn't
1197 // go through the front-end). Extensions that are registered in this
1198 // way are effectively considered 'pre-bundled', and so implicitly
1199 // trusted. In general, if something has HKLM or filesystem access,
1200 // they could install an extension manually themselves anyway.
1201 alert_on_error_ = false;
1202 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:151203
[email protected]a1257b12009-06-12 02:51:341204 // Ask each external extension provider to give us a call back for each
1205 // extension they know about. See OnExternalExtensionFound.
1206 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1207 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011208 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341209 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191210 }
[email protected]b0beaa662009-02-26 00:04:151211}
1212
[email protected]25b34332009-06-05 21:53:191213bool ExtensionsServiceBackend::CheckExternalUninstall(
[email protected]e72e8eb82009-06-18 17:21:511214 const std::string& id, Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341215 // Check if the providers know about this extension.
1216 ProviderMap::const_iterator i = external_extension_providers_.find(location);
1217 if (i != external_extension_providers_.end()) {
1218 scoped_ptr<Version> version;
1219 version.reset(i->second->RegisteredVersion(id, NULL));
1220 if (version.get())
1221 return false; // Yup, known extension, don't uninstall.
[email protected]e72e8eb82009-06-18 17:21:511222 } else {
1223 // Not from an external provider, so it's fine.
1224 return false;
[email protected]b0beaa662009-02-26 00:04:151225 }
[email protected]25b34332009-06-05 21:53:191226
[email protected]a1257b12009-06-12 02:51:341227 return true; // This is not a known extension, uninstall.
[email protected]b0beaa662009-02-26 00:04:151228}
1229
1230// Assumes that the extension isn't currently loaded or in use.
[email protected]631cf822009-05-15 07:01:251231void ExtensionsServiceBackend::UninstallExtension(
1232 const std::string& extension_id) {
1233 // First, delete the Current Version file. If the directory delete fails, then
1234 // at least the extension won't be loaded again.
1235 FilePath extension_directory = install_directory_.AppendASCII(extension_id);
1236
1237 if (!file_util::PathExists(extension_directory)) {
1238 LOG(WARNING) << "Asked to remove a non-existent extension " << extension_id;
[email protected]b0beaa662009-02-26 00:04:151239 return;
1240 }
[email protected]631cf822009-05-15 07:01:251241
1242 FilePath current_version_file = extension_directory.AppendASCII(
1243 ExtensionsService::kCurrentVersionFileName);
1244 if (!file_util::PathExists(current_version_file)) {
1245 LOG(WARNING) << "Extension " << extension_id
1246 << " does not have a Current Version file.";
1247 } else {
1248 if (!file_util::Delete(current_version_file, false)) {
1249 LOG(WARNING) << "Could not delete Current Version file for extension "
1250 << extension_id;
1251 return;
1252 }
1253 }
1254
[email protected]a1257b12009-06-12 02:51:341255 // OK, now try and delete the entire rest of the directory. One major place
[email protected]894bb502009-05-21 22:39:571256 // this can fail is if the extension contains a plugin (stupid plugins). It's
1257 // not a big deal though, because we'll notice next time we startup that the
1258 // Current Version file is gone and finish the delete then.
[email protected]631cf822009-05-15 07:01:251259 if (!file_util::Delete(extension_directory, true)) {
1260 LOG(WARNING) << "Could not delete directory for extension "
1261 << extension_id;
[email protected]b0beaa662009-02-26 00:04:151262 }
1263}
1264
[email protected]a1257b12009-06-12 02:51:341265void ExtensionsServiceBackend::ClearProvidersForTesting() {
[email protected]a1257b12009-06-12 02:51:341266 external_extension_providers_.clear();
1267}
1268
1269void ExtensionsServiceBackend::SetProviderForTesting(
1270 Extension::Location location,
1271 ExternalExtensionProvider* test_provider) {
1272 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011273 external_extension_providers_[location] =
1274 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341275}
1276
1277void ExtensionsServiceBackend::OnExternalExtensionFound(
1278 const std::string& id, const Version* version, const FilePath& path) {
[email protected]9f1087e2009-06-15 17:29:321279 CheckVersionAndInstallExtension(id, version, path);
[email protected]a1257b12009-06-12 02:51:341280}
1281
[email protected]b0beaa662009-02-26 00:04:151282bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
[email protected]a1257b12009-06-12 02:51:341283 const Version* version) {
[email protected]b0beaa662009-02-26 00:04:151284 std::string current_version;
[email protected]fbcc40302009-06-12 20:45:451285 Extension::InstallType install_type =
1286 CompareToInstalledVersion(id, version->GetString(), &current_version);
1287
1288 if (install_type == Extension::DOWNGRADE)
1289 return false;
1290
1291 return (install_type == Extension::UPGRADE ||
1292 install_type == Extension::NEW_INSTALL ||
1293 NeedsReinstall(id, current_version));
[email protected]cc655912009-01-29 23:19:191294}