blob: 2e655d10cfc22eae444ab793de13d450d41a1832 [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]894bb502009-05-21 22:39:57214 MessageLoop* frontend_loop,
[email protected]a1257b12009-06-12 02:51:34215 MessageLoop* backend_loop)
[email protected]e72e8eb82009-06-18 17:21:51216 : extension_prefs_(new ExtensionPrefs(profile->GetPrefs())),
[email protected]894bb502009-05-21 22:39:57217 backend_loop_(backend_loop),
[email protected]81e63782009-02-27 19:35:09218 install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
[email protected]e2eb43112009-05-29 21:19:54219 extensions_enabled_(
220 CommandLine::ForCurrentProcess()->
221 HasSwitch(switches::kEnableExtensions)),
[email protected]a1257b12009-06-12 02:51:34222 show_extensions_prompts_(true) {
[email protected]a1257b12009-06-12 02:51:34223 // We pass ownership of this object to the Backend.
[email protected]e72e8eb82009-06-18 17:21:51224 DictionaryValue* extensions = extension_prefs_->CopyCurrentExtensions();
[email protected]a1257b12009-06-12 02:51:34225 backend_ = new ExtensionsServiceBackend(
226 install_directory_, g_browser_process->resource_dispatcher_host(),
[email protected]e72e8eb82009-06-18 17:21:51227 frontend_loop, extensions);
[email protected]6014d672008-12-05 00:38:25228}
229
230ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32231 UnloadAllExtensions();
[email protected]6014d672008-12-05 00:38:25232}
233
[email protected]9f1087e2009-06-15 17:29:32234void ExtensionsService::Init() {
235 DCHECK(extensions_.size() == 0);
236
[email protected]b68d5ed2009-04-16 02:41:28237 // Start up the extension event routers.
238 ExtensionBrowserEventRouter::GetInstance()->Init();
239
[email protected]9f1087e2009-06-15 17:29:32240 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57241
[email protected]9f1087e2009-06-15 17:29:32242 // TODO(erikkay) this should probably be deferred to a future point
243 // rather than running immediately at startup.
244 CheckForUpdates();
[email protected]894bb502009-05-21 22:39:57245
[email protected]9f1087e2009-06-15 17:29:32246 // TODO(erikkay) this should probably be deferred as well.
247 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25248}
249
[email protected]3cf4f0992009-02-03 23:00:30250void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]894bb502009-05-21 22:39:57251 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
252 &ExtensionsServiceBackend::InstallExtension,
253 extension_path,
254 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30255}
256
[email protected]631cf822009-05-15 07:01:25257void ExtensionsService::UninstallExtension(const std::string& extension_id) {
[email protected]9f1087e2009-06-15 17:29:32258 Extension* extension = GetExtensionById(extension_id);
[email protected]631cf822009-05-15 07:01:25259
[email protected]9f1087e2009-06-15 17:29:32260 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51261 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32262
[email protected]e72e8eb82009-06-18 17:21:51263 extension_prefs_->OnExtensionUninstalled(extension);
[email protected]9f1087e2009-06-15 17:29:32264
265 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51266 if (Extension::LOAD != extension->location()) {
[email protected]9f1087e2009-06-15 17:29:32267 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
268 &ExtensionsServiceBackend::UninstallExtension, extension_id));
269 }
270
271 UnloadExtension(extension_id);
272}
273
274void ExtensionsService::LoadExtension(const FilePath& extension_path) {
275 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
276 &ExtensionsServiceBackend::LoadSingleExtension,
277 extension_path, scoped_refptr<ExtensionsService>(this)));
278}
279
280void ExtensionsService::LoadAllExtensions() {
[email protected]e72e8eb82009-06-18 17:21:51281 // Load the previously installed extensions.
282 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
283 &ExtensionsServiceBackend::LoadInstalledExtensions,
284 scoped_refptr<ExtensionsService>(this),
285 new InstalledExtensions(extension_prefs_.get())));
[email protected]9f1087e2009-06-15 17:29:32286}
287
288void ExtensionsService::CheckForUpdates() {
289 // This installs or updates externally provided extensions.
290 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51291 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]9f1087e2009-06-15 17:29:32292 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
293 &ExtensionsServiceBackend::CheckForExternalUpdates,
294 killed_extensions,
295 scoped_refptr<ExtensionsService>(this)));
296}
297
298void ExtensionsService::UnloadExtension(const std::string& extension_id) {
299 Extension* extension = NULL;
[email protected]631cf822009-05-15 07:01:25300 ExtensionList::iterator iter;
301 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
302 if ((*iter)->id() == extension_id) {
303 extension = *iter;
304 break;
305 }
306 }
307
[email protected]894bb502009-05-21 22:39:57308 // Callers should not send us nonexistant extensions.
309 CHECK(extension);
310
[email protected]631cf822009-05-15 07:01:25311 // Remove the extension from our list.
312 extensions_.erase(iter);
313
[email protected]631cf822009-05-15 07:01:25314 // Tell other services the extension is gone.
315 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
316 NotificationService::AllSources(),
317 Details<Extension>(extension));
318
[email protected]631cf822009-05-15 07:01:25319 delete extension;
320}
321
[email protected]9f1087e2009-06-15 17:29:32322void ExtensionsService::UnloadAllExtensions() {
323 ExtensionList::iterator iter;
324 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
325 // Tell other services the extension is gone.
326 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
327 NotificationService::AllSources(),
328 Details<Extension>(*iter));
329 delete *iter;
330 }
331 extensions_.clear();
332}
333
334void ExtensionsService::ReloadExtensions() {
335 UnloadAllExtensions();
336 LoadAllExtensions();
337}
338
339void ExtensionsService::GarbageCollectExtensions() {
[email protected]894bb502009-05-21 22:39:57340 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
[email protected]9f1087e2009-06-15 17:29:32341 &ExtensionsServiceBackend::GarbageCollectExtensions,
342 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30343}
344
[email protected]e72e8eb82009-06-18 17:21:51345void ExtensionsService::OnLoadedInstalledExtensions() {
346 NotificationService::current()->Notify(
347 NotificationType::EXTENSIONS_READY,
348 Source<ExtensionsService>(this),
349 NotificationService::NoDetails());
350}
351
[email protected]4a8d3272009-03-10 19:15:08352void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]9f1087e2009-06-15 17:29:32353 scoped_ptr<ExtensionList> cleanup(new_extensions);
354
[email protected]86a274072009-06-11 02:06:45355 // Filter out any extensions we don't want to enable. Themes are always
356 // enabled, but other extensions are only loaded if --enable-extensions is
357 // present.
[email protected]a1257b12009-06-12 02:51:34358 ExtensionList enabled_extensions;
[email protected]86a274072009-06-11 02:06:45359 for (ExtensionList::iterator iter = new_extensions->begin();
[email protected]25b34332009-06-05 21:53:19360 iter != new_extensions->end(); ++iter) {
[email protected]ba74f352009-06-11 18:54:45361 if (extensions_enabled() || (*iter)->IsTheme()) {
[email protected]9f1087e2009-06-15 17:29:32362 Extension* old = GetExtensionById((*iter)->id());
363 if (old) {
364 if ((*iter)->version()->CompareTo(*(old->version())) > 0) {
365 // To upgrade an extension in place, unload the old one and
366 // then load the new one.
367 // TODO(erikkay) issue 12399
368 UnloadExtension(old->id());
369 } else {
370 // We already have the extension of the same or older version.
371 LOG(WARNING) << "Duplicate extension load attempt: " << (*iter)->id();
372 delete *iter;
373 continue;
374 }
375 }
[email protected]86a274072009-06-11 02:06:45376 enabled_extensions.push_back(*iter);
[email protected]9f1087e2009-06-15 17:29:32377 extensions_.push_back(*iter);
[email protected]ba74f352009-06-11 18:54:45378 } else {
379 // Extensions that get enabled get added to extensions_ and deleted later.
380 // Anything skipped must be deleted now so we don't leak.
381 delete *iter;
382 }
[email protected]86a274072009-06-11 02:06:45383 }
384
[email protected]e72e8eb82009-06-18 17:21:51385 if (enabled_extensions.size()) {
386 NotificationService::current()->Notify(
387 NotificationType::EXTENSIONS_LOADED,
388 NotificationService::AllSources(),
389 Details<ExtensionList>(&enabled_extensions));
390 }
[email protected]6014d672008-12-05 00:38:25391}
392
[email protected]a57209872009-05-04 22:53:14393void ExtensionsService::OnExtensionInstalled(Extension* extension,
[email protected]fbcc40302009-06-12 20:45:45394 Extension::InstallType install_type) {
[email protected]e72e8eb82009-06-18 17:21:51395 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19396
[email protected]4a190632009-05-09 01:07:42397 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
398 // to apply it.
399 if (extension->IsTheme()) {
400 NotificationService::current()->Notify(
401 NotificationType::THEME_INSTALLED,
402 NotificationService::AllSources(),
403 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27404 } else {
405 NotificationService::current()->Notify(
406 NotificationType::EXTENSION_INSTALLED,
407 NotificationService::AllSources(),
408 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42409 }
410}
411
[email protected]fbcc40302009-06-12 20:45:45412void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]9f1087e2009-06-15 17:29:32413 Extension* extension = GetExtensionById(id);
[email protected]4a190632009-05-09 01:07:42414 if (extension && extension->IsTheme()) {
415 NotificationService::current()->Notify(
416 NotificationType::THEME_INSTALLED,
417 NotificationService::AllSources(),
418 Details<Extension>(extension));
419 }
[email protected]cc655912009-01-29 23:19:19420}
421
[email protected]9f1087e2009-06-15 17:29:32422Extension* ExtensionsService::GetExtensionById(const std::string& id) {
[email protected]ce5c4502009-05-06 16:46:11423 for (ExtensionList::const_iterator iter = extensions_.begin();
[email protected]4a190632009-05-09 01:07:42424 iter != extensions_.end(); ++iter) {
[email protected]ce5c4502009-05-06 16:46:11425 if ((*iter)->id() == id)
426 return *iter;
427 }
428 return NULL;
429}
430
[email protected]9f1087e2009-06-15 17:29:32431Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
432 std::string host = url.host();
433 return GetExtensionById(host);
434}
435
[email protected]a1257b12009-06-12 02:51:34436void ExtensionsService::ClearProvidersForTesting() {
437 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
438 &ExtensionsServiceBackend::ClearProvidersForTesting));
439}
440
441void ExtensionsService::SetProviderForTesting(
442 Extension::Location location, ExternalExtensionProvider* test_provider) {
443 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
444 &ExtensionsServiceBackend::SetProviderForTesting,
445 location, test_provider));
446}
447
[email protected]6014d672008-12-05 00:38:25448// ExtensionsServicesBackend
449
[email protected]894bb502009-05-21 22:39:57450ExtensionsServiceBackend::ExtensionsServiceBackend(
451 const FilePath& install_directory, ResourceDispatcherHost* rdh,
[email protected]a1257b12009-06-12 02:51:34452 MessageLoop* frontend_loop, DictionaryValue* extension_prefs)
[email protected]0c7bc4b2009-05-30 01:47:08453 : frontend_(NULL),
454 install_directory_(install_directory),
[email protected]894bb502009-05-21 22:39:57455 resource_dispatcher_host_(rdh),
[email protected]0c7bc4b2009-05-30 01:47:08456 alert_on_error_(false),
[email protected]a1257b12009-06-12 02:51:34457 frontend_loop_(frontend_loop) {
458 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01459 linked_ptr<ExternalExtensionProvider>(
460 new ExternalPrefExtensionProvider(extension_prefs));
[email protected]a1257b12009-06-12 02:51:34461#if defined(OS_WIN)
462 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01463 linked_ptr<ExternalExtensionProvider>(
464 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34465#endif
466}
467
468ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57469}
470
[email protected]e72e8eb82009-06-18 17:21:51471void ExtensionsServiceBackend::LoadInstalledExtensions(
[email protected]25b34332009-06-05 21:53:19472 scoped_refptr<ExtensionsService> frontend,
[email protected]e72e8eb82009-06-18 17:21:51473 InstalledExtensions* installed) {
474 scoped_ptr<InstalledExtensions> cleanup(installed);
[email protected]b0beaa662009-02-26 00:04:15475 frontend_ = frontend;
476 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15477
[email protected]e72e8eb82009-06-18 17:21:51478 // Call LoadInstalledExtension for each extension |installed| knows about.
479 scoped_ptr<InstalledExtensions::Callback> callback(
480 NewCallback(this, &ExtensionsServiceBackend::LoadInstalledExtension));
481 installed->VisitInstalledExtensions(callback.get());
482
483 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
484 frontend_, &ExtensionsService::OnLoadedInstalledExtensions));
[email protected]9f1087e2009-06-15 17:29:32485}
[email protected]b0beaa662009-02-26 00:04:15486
[email protected]9f1087e2009-06-15 17:29:32487void ExtensionsServiceBackend::GarbageCollectExtensions(
488 scoped_refptr<ExtensionsService> frontend) {
489 frontend_ = frontend;
490 alert_on_error_ = false;
491
492 // Nothing to clean up if it doesn't exist.
493 if (!file_util::DirectoryExists(install_directory_))
494 return;
495
[email protected]540f91b2009-03-26 19:37:43496 if (!file_util::AbsolutePath(&install_directory_))
497 NOTREACHED();
[email protected]540f91b2009-03-26 19:37:43498
[email protected]bf24d2c2009-02-24 23:07:45499 LOG(INFO) << "Loading installed extensions...";
500
[email protected]6014d672008-12-05 00:38:25501 // Find all child directories in the install directory and load their
502 // manifests. Post errors and results to the frontend.
[email protected]cc5da332009-03-04 08:02:51503 file_util::FileEnumerator enumerator(install_directory_,
[email protected]a1257b12009-06-12 02:51:34504 false, // Not recursive.
[email protected]6014d672008-12-05 00:38:25505 file_util::FileEnumerator::DIRECTORIES);
[email protected]cc5da332009-03-04 08:02:51506 FilePath extension_path;
507 for (extension_path = enumerator.Next(); !extension_path.value().empty();
508 extension_path = enumerator.Next()) {
509 std::string extension_id = WideToASCII(
510 extension_path.BaseName().ToWStringHack());
[email protected]5a2721f62009-06-13 07:08:20511
[email protected]96088fb2009-05-26 19:08:13512 // The utility process might be in the middle of unpacking an extension, so
513 // ignore the temp unpacking directory.
514 if (extension_id == kUnpackExtensionDir)
515 continue;
[email protected]894bb502009-05-21 22:39:57516
517 // If there is no Current Version file, just delete the directory and move
518 // on. This can legitimately happen when an uninstall does not complete, for
519 // example, when a plugin is in use at uninstall time.
520 FilePath current_version_path = extension_path.AppendASCII(
521 ExtensionsService::kCurrentVersionFileName);
522 if (!file_util::PathExists(current_version_path)) {
523 LOG(INFO) << "Deleting incomplete install for directory "
524 << WideToASCII(extension_path.ToWStringHack()) << ".";
[email protected]25b34332009-06-05 21:53:19525 file_util::Delete(extension_path, true); // Recursive.
[email protected]894bb502009-05-21 22:39:57526 continue;
527 }
528
[email protected]9f1087e2009-06-15 17:29:32529 // Ignore directories that aren't valid IDs.
530 if (!Extension::IdIsValid(extension_id)) {
531 LOG(WARNING) << "Invalid extension ID encountered in extensions "
532 "directory: " << extension_id;
533 // TODO(erikkay) delete these eventually too...
[email protected]cc5da332009-03-04 08:02:51534 continue;
535 }
536
[email protected]9f1087e2009-06-15 17:29:32537 // TODO(erikkay) check for extensions that aren't loaded?
[email protected]6014d672008-12-05 00:38:25538 }
[email protected]6014d672008-12-05 00:38:25539}
540
[email protected]b0beaa662009-02-26 00:04:15541void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57542 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15543 frontend_ = frontend;
544
545 // Explicit UI loads are always noisy.
546 alert_on_error_ = true;
547
[email protected]cc5da332009-03-04 08:02:51548 FilePath extension_path = path_in;
549 if (!file_util::AbsolutePath(&extension_path))
[email protected]0877fd92009-02-03 16:34:06550 NOTREACHED();
[email protected]bf24d2c2009-02-24 23:07:45551
552 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51553 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45554
[email protected]5bfb1eb0a2009-04-08 18:33:30555 Extension* extension = LoadExtension(extension_path,
[email protected]a1257b12009-06-12 02:51:34556 Extension::LOAD,
[email protected]e72e8eb82009-06-18 17:21:51557 false); // Don't require id.
[email protected]0877fd92009-02-03 16:34:06558 if (extension) {
559 ExtensionList* extensions = new ExtensionList;
560 extensions->push_back(extension);
[email protected]b0beaa662009-02-26 00:04:15561 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06562 }
[email protected]0877fd92009-02-03 16:34:06563}
564
[email protected]e72e8eb82009-06-18 17:21:51565void ExtensionsServiceBackend::LoadInstalledExtension(
566 const std::string& id, const FilePath& path, Extension::Location location) {
567 if (CheckExternalUninstall(id, location)) {
568 // TODO(erikkay): Possibly defer this operation to avoid slowing initial
569 // load of extensions.
570 UninstallExtension(id);
571
572 // No error needs to be reported. The extension effectively doesn't exist.
573 return;
574 }
575
576 Extension* extension =
577 LoadExtension(FilePath(path), location, true); // Require id.
578
579 // TODO(erikkay) now we only report a single extension loaded at a time.
580 // Perhaps we should change the notifications to remove ExtensionList.
581 ExtensionList* extensions = new ExtensionList;
582 if (extension)
583 extensions->push_back(extension);
584 ReportExtensionsLoaded(extensions);
585}
586
[email protected]fbcc40302009-06-12 20:45:45587DictionaryValue* ExtensionsServiceBackend::ReadManifest(FilePath manifest_path,
588 std::string* error) {
589 JSONFileValueSerializer serializer(manifest_path);
590 scoped_ptr<Value> root(serializer.Deserialize(error));
591 if (!root.get())
592 return NULL;
593
594 if (!root->IsType(Value::TYPE_DICTIONARY)) {
595 *error = Extension::kInvalidManifestError;
596 return NULL;
597 }
598
599 return static_cast<DictionaryValue*>(root.release());
600}
601
[email protected]cc5da332009-03-04 08:02:51602Extension* ExtensionsServiceBackend::LoadExtension(
[email protected]a1257b12009-06-12 02:51:34603 const FilePath& extension_path,
604 Extension::Location location,
605 bool require_id) {
[email protected]0877fd92009-02-03 16:34:06606 FilePath manifest_path =
[email protected]cc5da332009-03-04 08:02:51607 extension_path.AppendASCII(Extension::kManifestFilename);
[email protected]0877fd92009-02-03 16:34:06608 if (!file_util::PathExists(manifest_path)) {
[email protected]cc5da332009-03-04 08:02:51609 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06610 return NULL;
611 }
612
[email protected]0877fd92009-02-03 16:34:06613 std::string error;
[email protected]fbcc40302009-06-12 20:45:45614 scoped_ptr<DictionaryValue> root(ReadManifest(manifest_path, &error));
[email protected]0877fd92009-02-03 16:34:06615 if (!root.get()) {
[email protected]cc5da332009-03-04 08:02:51616 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06617 return NULL;
618 }
619
[email protected]cc5da332009-03-04 08:02:51620 scoped_ptr<Extension> extension(new Extension(extension_path));
[email protected]fbcc40302009-06-12 20:45:45621 if (!extension->InitFromValue(*root.get(), require_id, &error)) {
[email protected]cc5da332009-03-04 08:02:51622 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06623 return NULL;
624 }
[email protected]8d6d9ff2009-02-20 08:14:39625
[email protected]a1257b12009-06-12 02:51:34626 extension->set_location(location);
[email protected]631cf822009-05-15 07:01:25627
[email protected]12198912009-06-05 03:41:22628 // Theme resource validation.
629 if (extension->IsTheme()) {
630 DictionaryValue* images_value = extension->GetThemeImages();
631 DictionaryValue::key_iterator iter = images_value->begin_keys();
632 while (iter != images_value->end_keys()) {
633 std::string val;
[email protected]25b34332009-06-05 21:53:19634 if (images_value->GetString(*iter , &val)) {
[email protected]12198912009-06-05 03:41:22635 FilePath image_path = extension->path().AppendASCII(val);
636 if (!file_util::PathExists(image_path)) {
637 ReportExtensionLoadError(extension_path,
638 StringPrintf("Could not load '%s' for theme.",
639 WideToUTF8(image_path.ToWStringHack()).c_str()));
640 return NULL;
641 }
642 }
643 ++iter;
644 }
645
646 // Themes cannot contain other extension types.
647 return extension.release();
648 }
649
[email protected]d24070e22009-05-21 19:26:59650 // Validate that claimed script resources actually exist.
[email protected]3cfbd0e2009-03-18 21:26:24651 for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
652 const UserScript& script = extension->content_scripts()[i];
653
654 for (size_t j = 0; j < script.js_scripts().size(); j++) {
655 const FilePath& path = script.js_scripts()[j].path();
656 if (!file_util::PathExists(path)) {
657 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59658 StringPrintf("Could not load '%s' for content script.",
659 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24660 return NULL;
661 }
662 }
663
664 for (size_t j = 0; j < script.css_scripts().size(); j++) {
665 const FilePath& path = script.css_scripts()[j].path();
666 if (!file_util::PathExists(path)) {
667 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59668 StringPrintf("Could not load '%s' for content script.",
669 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24670 return NULL;
671 }
[email protected]8d6d9ff2009-02-20 08:14:39672 }
673 }
674
[email protected]c533bb22009-06-03 19:06:11675 for (size_t i = 0; i < extension->plugins().size(); ++i) {
676 const Extension::PluginInfo& plugin = extension->plugins()[i];
677 if (!file_util::PathExists(plugin.path)) {
678 ReportExtensionLoadError(extension_path,
679 StringPrintf("Could not load '%s' for plugin.",
680 WideToUTF8(plugin.path.ToWStringHack()).c_str()));
681 return NULL;
682 }
683 }
684
[email protected]d24070e22009-05-21 19:26:59685 // Validate icon location for page actions.
686 const PageActionMap& page_actions = extension->page_actions();
687 for (PageActionMap::const_iterator i(page_actions.begin());
688 i != page_actions.end(); ++i) {
689 PageAction* page_action = i->second;
690 FilePath path = page_action->icon_path();
691 if (!file_util::PathExists(path)) {
692 ReportExtensionLoadError(extension_path,
693 StringPrintf("Could not load icon '%s' for page action.",
694 WideToUTF8(path.ToWStringHack()).c_str()));
695 return NULL;
696 }
697 }
698
[email protected]0877fd92009-02-03 16:34:06699 return extension.release();
700}
701
[email protected]6014d672008-12-05 00:38:25702void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51703 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15704 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51705 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00706 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19707 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18708 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25709}
710
711void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15712 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57713 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
714 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25715}
[email protected]cc655912009-01-29 23:19:19716
[email protected]b0beaa662009-02-26 00:04:15717bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
718 std::string* version_string) {
[email protected]18a12352009-01-31 01:33:28719 FilePath current_version =
[email protected]b0beaa662009-02-26 00:04:15720 dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
[email protected]18a12352009-01-31 01:33:28721 if (file_util::PathExists(current_version)) {
722 if (file_util::ReadFileToString(current_version, version_string)) {
723 TrimWhitespace(*version_string, TRIM_ALL, version_string);
724 return true;
725 }
726 }
727 return false;
728}
729
[email protected]fbcc40302009-06-12 20:45:45730Extension::InstallType ExtensionsServiceBackend::CompareToInstalledVersion(
731 const std::string& id,
[email protected]b0beaa662009-02-26 00:04:15732 const std::string& new_version_str,
[email protected]fbcc40302009-06-12 20:45:45733 std::string *current_version_str) {
734 CHECK(current_version_str);
735 FilePath dir(install_directory_.AppendASCII(id.c_str()));
736 if (!ReadCurrentVersion(dir, current_version_str))
737 return Extension::NEW_INSTALL;
738
[email protected]b0beaa662009-02-26 00:04:15739 scoped_ptr<Version> current_version(
[email protected]fbcc40302009-06-12 20:45:45740 Version::GetVersionFromString(*current_version_str));
[email protected]b0beaa662009-02-26 00:04:15741 scoped_ptr<Version> new_version(
[email protected]fbcc40302009-06-12 20:45:45742 Version::GetVersionFromString(new_version_str));
743 int comp = new_version->CompareTo(*current_version);
744 if (comp > 0)
745 return Extension::UPGRADE;
746 else if (comp == 0)
747 return Extension::REINSTALL;
748 else
749 return Extension::DOWNGRADE;
750}
751
752bool ExtensionsServiceBackend::NeedsReinstall(const std::string& id,
753 const std::string& current_version) {
754 // Verify that the directory actually exists.
755 // TODO(erikkay): A further step would be to verify that the extension
756 // has actually loaded successfully.
757 FilePath dir(install_directory_.AppendASCII(id.c_str()));
758 FilePath version_dir(dir.AppendASCII(current_version));
759 return !file_util::PathExists(version_dir);
[email protected]cc655912009-01-29 23:19:19760}
761
[email protected]f0a51fb52009-03-05 12:46:38762bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
[email protected]b0beaa662009-02-26 00:04:15763 const FilePath& dest_dir) {
[email protected]cc655912009-01-29 23:19:19764 if (file_util::PathExists(dest_dir)) {
765 // By the time we get here, it should be safe to assume that this directory
766 // is not currently in use (it's not the current active version).
767 if (!file_util::Delete(dest_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51768 ReportExtensionInstallError(source_dir,
[email protected]cc655912009-01-29 23:19:19769 "Can't delete existing version directory.");
770 return false;
771 }
772 } else {
773 FilePath parent = dest_dir.DirName();
774 if (!file_util::DirectoryExists(parent)) {
775 if (!file_util::CreateDirectory(parent)) {
[email protected]cc5da332009-03-04 08:02:51776 ReportExtensionInstallError(source_dir,
777 "Couldn't create extension directory.");
[email protected]cc655912009-01-29 23:19:19778 return false;
779 }
780 }
781 }
782 if (!file_util::Move(source_dir, dest_dir)) {
[email protected]cc5da332009-03-04 08:02:51783 ReportExtensionInstallError(source_dir,
784 "Couldn't move temporary directory.");
[email protected]cc655912009-01-29 23:19:19785 return false;
786 }
787
788 return true;
789}
790
[email protected]b0beaa662009-02-26 00:04:15791bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
792 std::string version) {
[email protected]cc655912009-01-29 23:19:19793 // Write out the new CurrentVersion file.
794 // <profile>/Extension/<name>/CurrentVersion
795 FilePath current_version =
796 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
797 FilePath current_version_old =
798 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
799 if (file_util::PathExists(current_version_old)) {
800 if (!file_util::Delete(current_version_old, false)) {
[email protected]cc5da332009-03-04 08:02:51801 ReportExtensionInstallError(dest_dir,
802 "Couldn't remove CurrentVersion_old file.");
[email protected]cc655912009-01-29 23:19:19803 return false;
804 }
805 }
806 if (file_util::PathExists(current_version)) {
807 if (!file_util::Move(current_version, current_version_old)) {
[email protected]cc5da332009-03-04 08:02:51808 ReportExtensionInstallError(dest_dir,
809 "Couldn't move CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19810 return false;
811 }
812 }
813 net::FileStream stream;
814 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
815 if (stream.Open(current_version, flags) != 0)
816 return false;
817 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
818 // Restore the old CurrentVersion.
819 if (file_util::PathExists(current_version_old)) {
820 if (!file_util::Move(current_version_old, current_version)) {
[email protected]f0a51fb52009-03-05 12:46:38821 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
[email protected]cc655912009-01-29 23:19:19822 " to " << current_version.value();
[email protected]b0beaa662009-02-26 00:04:15823
[email protected]cc655912009-01-29 23:19:19824 // TODO(erikkay): This is an ugly state to be in. Try harder?
825 }
826 }
[email protected]f0a51fb52009-03-05 12:46:38827 ReportExtensionInstallError(dest_dir,
[email protected]cc5da332009-03-04 08:02:51828 "Couldn't create CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19829 return false;
830 }
831 return true;
832}
833
[email protected]b0beaa662009-02-26 00:04:15834void ExtensionsServiceBackend::InstallExtension(
[email protected]894bb502009-05-21 22:39:57835 const FilePath& extension_path, scoped_refptr<ExtensionsService> frontend) {
[email protected]cc655912009-01-29 23:19:19836 LOG(INFO) << "Installing extension " << extension_path.value();
837
[email protected]b0beaa662009-02-26 00:04:15838 frontend_ = frontend;
[email protected]0e34d7892009-06-05 19:17:40839 alert_on_error_ = true;
[email protected]b0beaa662009-02-26 00:04:15840
[email protected]9f1087e2009-06-15 17:29:32841 InstallOrUpdateExtension(extension_path, std::string());
[email protected]b0beaa662009-02-26 00:04:15842}
843
[email protected]1fca1492009-05-15 22:23:43844void ExtensionsServiceBackend::InstallOrUpdateExtension(
[email protected]9f1087e2009-06-15 17:29:32845 const FilePath& extension_path, const std::string& expected_id) {
[email protected]fbcc40302009-06-12 20:45:45846 std::string actual_public_key;
847 if (!ValidateSignature(extension_path, &actual_public_key))
848 return; // Failures reported within ValidateSignature().
849
850 UnpackerClient* client = new UnpackerClient(
[email protected]9f1087e2009-06-15 17:29:32851 this, extension_path, actual_public_key, expected_id);
[email protected]1fca1492009-05-15 22:23:43852 client->Start();
853}
[email protected]cc655912009-01-29 23:19:19854
[email protected]fbcc40302009-06-12 20:45:45855bool ExtensionsServiceBackend::ValidateSignature(const FilePath& extension_path,
856 std::string* key_out) {
857 ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
858 if (!file.get()) {
859 ReportExtensionInstallError(extension_path, "Could not open file.");
860 return NULL;
861 }
862
863 // Read and verify the header.
864 ExtensionsService::ExtensionHeader header;
865 size_t len;
866
867 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
868 // appears that we don't have any endian/alignment aware serialization
869 // code in the code base. So for now, this assumes that we're running
870 // on a little endian machine with 4 byte alignment.
871 len = fread(&header, 1, sizeof(ExtensionsService::ExtensionHeader),
872 file.get());
873 if (len < sizeof(ExtensionsService::ExtensionHeader)) {
874 ReportExtensionInstallError(extension_path, kInvalidExtensionHeaderError);
875 return false;
876 }
877 if (strncmp(ExtensionsService::kExtensionHeaderMagic, header.magic,
878 sizeof(header.magic))) {
879 ReportExtensionInstallError(extension_path, kBadMagicNumberError);
880 return false;
881 }
882 if (header.version != ExtensionsService::kCurrentVersion) {
883 ReportExtensionInstallError(extension_path, kBadVersionNumberError);
884 return false;
885 }
886 if (header.key_size > ExtensionsService::kMaxPublicKeySize ||
887 header.signature_size > ExtensionsService::kMaxSignatureSize) {
888 ReportExtensionInstallError(extension_path, kBadHeaderSizeError);
889 return false;
890 }
891
892 std::vector<uint8> key;
893 key.resize(header.key_size);
894 len = fread(&key.front(), sizeof(uint8), header.key_size, file.get());
895 if (len < header.key_size) {
896 ReportExtensionInstallError(extension_path, kInvalidPublicKeyError);
897 return false;
898 }
899
900 std::vector<uint8> signature;
901 signature.resize(header.signature_size);
902 len = fread(&signature.front(), sizeof(uint8), header.signature_size,
903 file.get());
904 if (len < header.signature_size) {
905 ReportExtensionInstallError(extension_path, kInvalidSignatureError);
906 return false;
907 }
908
909 // Note: this structure is an ASN.1 which encodes the algorithm used
910 // with its parameters. This is defined in PKCS #1 v2.1 (RFC 3447).
911 // It is encoding: { OID sha1WithRSAEncryption PARAMETERS NULL }
912 // TODO(aa): This needs to be factored away someplace common.
913 const uint8 signature_algorithm[15] = {
914 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
915 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
916 };
917
918 base::SignatureVerifier verifier;
919 if (!verifier.VerifyInit(signature_algorithm,
920 sizeof(signature_algorithm),
921 &signature.front(),
922 signature.size(),
923 &key.front(),
924 key.size())) {
925 ReportExtensionInstallError(extension_path,
926 kSignatureVerificationInitFailed);
927 return false;
928 }
929
930 unsigned char buf[1 << 12];
931 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
932 verifier.VerifyUpdate(buf, len);
933
934 if (!verifier.VerifyFinal()) {
935 ReportExtensionInstallError(extension_path, kSignatureVerificationFailed);
936 return false;
937 }
938
939 net::Base64Encode(std::string(reinterpret_cast<char*>(&key.front()),
940 key.size()), key_out);
941 return true;
942}
943
[email protected]1fca1492009-05-15 22:23:43944void ExtensionsServiceBackend::OnExtensionUnpacked(
945 const FilePath& extension_path,
946 const FilePath& temp_extension_dir,
947 const std::string expected_id,
[email protected]902f7cd2009-05-22 19:02:19948 const DictionaryValue& manifest,
949 const std::vector< Tuple2<SkBitmap, FilePath> >& images) {
[email protected]1fca1492009-05-15 22:23:43950 Extension extension;
951 std::string error;
[email protected]902f7cd2009-05-22 19:02:19952 if (!extension.InitFromValue(manifest,
[email protected]1fca1492009-05-15 22:23:43953 true, // require ID
954 &error)) {
955 ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
956 return;
[email protected]0b344962009-03-31 04:21:45957 }
958
[email protected]e2eb43112009-05-29 21:19:54959 if (!frontend_->extensions_enabled() && !extension.IsTheme()) {
960#if defined(OS_WIN)
[email protected]0e34d7892009-06-05 19:17:40961 if (frontend_->show_extensions_prompts()) {
[email protected]f90d0132009-06-15 20:40:50962 win_util::MessageBox(GetForegroundWindow(),
[email protected]e2eb43112009-05-29 21:19:54963 L"Extensions are not enabled. Add --enable-extensions to the "
964 L"command-line to enable extensions.\n\n"
965 L"This is a temporary message and it will be removed when extensions "
966 L"UI is finalized.",
967 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(), MB_OK);
968 }
969#endif
970 ReportExtensionInstallError(extension_path,
971 "Extensions are not enabled.");
972 return;
973 }
974
[email protected]9f1087e2009-06-15 17:29:32975 Extension::Location location = Extension::INTERNAL;
976 LookupExternalExtension(extension.id(), NULL, &location);
[email protected]0e34d7892009-06-05 19:17:40977#if defined(OS_WIN)
[email protected]9f1087e2009-06-15 17:29:32978 bool from_external = Extension::IsExternalLocation(location);
979
[email protected]a1257b12009-06-12 02:51:34980 if (!extension.IsTheme() && !from_external &&
[email protected]0e34d7892009-06-05 19:17:40981 frontend_->show_extensions_prompts() &&
[email protected]f90d0132009-06-15 20:40:50982 win_util::MessageBox(GetForegroundWindow(),
[email protected]0e34d7892009-06-05 19:17:40983 L"Are you sure you want to install this extension?\n\n"
984 L"This is a temporary message and it will be removed when extensions "
985 L"UI is finalized.",
986 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
[email protected]31e8a012009-06-06 08:13:34987 MB_OKCANCEL) != IDOK) {
[email protected]0e34d7892009-06-05 19:17:40988 ReportExtensionInstallError(extension_path,
989 "User did not allow extension to be installed.");
990 return;
991 }
992#endif
993
[email protected]b0beaa662009-02-26 00:04:15994 // If an expected id was provided, make sure it matches.
[email protected]1fca1492009-05-15 22:23:43995 if (!expected_id.empty() && expected_id != extension.id()) {
[email protected]25b34332009-06-05 21:53:19996 std::string error_msg = "ID in new extension manifest (";
997 error_msg += extension.id();
998 error_msg += ") does not match expected ID (";
999 error_msg += expected_id;
1000 error_msg += ")";
1001 ReportExtensionInstallError(extension_path, error_msg);
[email protected]1fca1492009-05-15 22:23:431002 return;
[email protected]cc655912009-01-29 23:19:191003 }
1004
1005 // <profile>/Extensions/<id>
[email protected]b0beaa662009-02-26 00:04:151006 FilePath dest_dir = install_directory_.AppendASCII(extension.id());
[email protected]cc655912009-01-29 23:19:191007 std::string version = extension.VersionString();
[email protected]b0beaa662009-02-26 00:04:151008 std::string current_version;
[email protected]fbcc40302009-06-12 20:45:451009 Extension::InstallType install_type =
1010 CompareToInstalledVersion(extension.id(), version, &current_version);
1011
1012 // Do not allow downgrade.
1013 if (install_type == Extension::DOWNGRADE) {
1014 ReportExtensionInstallError(extension_path,
1015 "Error: Attempt to downgrade extension from more recent version.");
1016 return;
1017 }
1018
1019 if (install_type == Extension::REINSTALL) {
1020 if (NeedsReinstall(extension.id(), current_version)) {
1021 // Treat corrupted existing installation as new install case.
1022 install_type = Extension::NEW_INSTALL;
1023 } else {
1024 // The client may use this as a signal (to switch themes, for instance).
1025 ReportExtensionOverinstallAttempted(extension.id());
[email protected]1fca1492009-05-15 22:23:431026 return;
[email protected]fbcc40302009-06-12 20:45:451027 }
[email protected]b0beaa662009-02-26 00:04:151028 }
[email protected]cc655912009-01-29 23:19:191029
[email protected]902f7cd2009-05-22 19:02:191030 // Write our parsed manifest back to disk, to ensure it doesn't contain an
1031 // exploitable bug that can be used to compromise the browser.
1032 std::string manifest_json;
1033 JSONStringValueSerializer serializer(&manifest_json);
1034 serializer.set_pretty_print(true);
1035 if (!serializer.Serialize(manifest)) {
1036 ReportExtensionInstallError(extension_path,
1037 "Error serializing manifest.json.");
1038 return;
1039 }
1040
1041 FilePath manifest_path =
1042 temp_extension_dir.AppendASCII(Extension::kManifestFilename);
1043 if (!file_util::WriteFile(manifest_path,
1044 manifest_json.data(), manifest_json.size())) {
1045 ReportExtensionInstallError(extension_path, "Error saving manifest.json.");
1046 return;
1047 }
1048
[email protected]facd7a7652009-06-05 23:15:021049 // Delete any images that may be used by the browser. We're going to write
1050 // out our own versions of the parsed images, and we want to make sure the
1051 // originals are gone for good.
1052 std::set<FilePath> image_paths = extension.GetBrowserImages();
1053 if (image_paths.size() != images.size()) {
1054 ReportExtensionInstallError(extension_path,
1055 "Decoded images don't match what's in the manifest.");
1056 return;
1057 }
1058
1059 for (std::set<FilePath>::iterator it = image_paths.begin();
1060 it != image_paths.end(); ++it) {
1061 if (!file_util::Delete(temp_extension_dir.Append(*it), false)) {
1062 ReportExtensionInstallError(extension_path,
1063 "Error removing old image file.");
1064 return;
1065 }
1066 }
1067
[email protected]902f7cd2009-05-22 19:02:191068 // Write our parsed images back to disk as well.
1069 for (size_t i = 0; i < images.size(); ++i) {
1070 const SkBitmap& image = images[i].a;
1071 FilePath path = temp_extension_dir.Append(images[i].b);
1072
1073 std::vector<unsigned char> image_data;
1074 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
1075 // though they may originally be .jpg, etc. Figure something out.
1076 // http://code.google.com/p/chromium/issues/detail?id=12459
1077 if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) {
1078 ReportExtensionInstallError(extension_path,
1079 "Error re-encoding theme image.");
1080 return;
1081 }
1082
1083 // Note: we're overwriting existing files that the utility process wrote,
1084 // so we can be sure the directory exists.
1085 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
1086 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
1087 ReportExtensionInstallError(extension_path, "Error saving theme image.");
1088 return;
1089 }
1090 }
1091
[email protected]cc655912009-01-29 23:19:191092 // <profile>/Extensions/<dir_name>/<version>
[email protected]1fca1492009-05-15 22:23:431093 FilePath version_dir = dest_dir.AppendASCII(version);
[email protected]902f7cd2009-05-22 19:02:191094
1095 // If anything fails after this, we want to delete the extension dir.
1096 ScopedTempDir scoped_version_dir;
1097 scoped_version_dir.Set(version_dir);
1098
[email protected]1fca1492009-05-15 22:23:431099 if (!InstallDirSafely(temp_extension_dir, version_dir))
1100 return;
[email protected]cc655912009-01-29 23:19:191101
[email protected]902f7cd2009-05-22 19:02:191102 if (!SetCurrentVersion(dest_dir, version))
[email protected]1fca1492009-05-15 22:23:431103 return;
[email protected]cc655912009-01-29 23:19:191104
[email protected]9f1087e2009-06-15 17:29:321105 Extension* loaded = LoadExtension(version_dir,
1106 location,
1107 true); // require id
1108 CHECK(loaded);
[email protected]1fca1492009-05-15 22:23:431109
[email protected]9f1087e2009-06-15 17:29:321110 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
1111 frontend_, &ExtensionsService::OnExtensionInstalled, loaded,
1112 install_type));
[email protected]894bb502009-05-21 22:39:571113
[email protected]9f1087e2009-06-15 17:29:321114 // Only one extension, but ReportExtensionsLoaded can handle multiple,
1115 // so we need to construct a list.
1116 scoped_ptr<ExtensionList> extensions(new ExtensionList);
1117 extensions->push_back(loaded);
[email protected]894bb502009-05-21 22:39:571118
[email protected]9f1087e2009-06-15 17:29:321119 // Hand off ownership of the loaded extensions to the frontend.
1120 ReportExtensionsLoaded(extensions.release());
[email protected]902f7cd2009-05-22 19:02:191121
1122 scoped_version_dir.Take();
[email protected]cc655912009-01-29 23:19:191123}
1124
1125void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:511126 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:151127
[email protected]cc655912009-01-29 23:19:191128 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:511129 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:191130 std::string message =
1131 StringPrintf("Could not install extension from '%s'. %s",
1132 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:181133 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]cc655912009-01-29 23:19:191134}
1135
[email protected]fbcc40302009-06-12 20:45:451136void ExtensionsServiceBackend::ReportExtensionOverinstallAttempted(
[email protected]4a190632009-05-09 01:07:421137 const std::string& id) {
[email protected]894bb502009-05-21 22:39:571138 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]fbcc40302009-06-12 20:45:451139 frontend_, &ExtensionsService::OnExtensionOverinstallAttempted, id));
[email protected]b0beaa662009-02-26 00:04:151140}
1141
[email protected]25b34332009-06-05 21:53:191142bool ExtensionsServiceBackend::ShouldSkipInstallingExtension(
1143 const std::set<std::string>& ids_to_ignore,
1144 const std::string& id) {
1145 if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
1146 LOG(INFO) << "Skipping uninstalled external extension " << id;
1147 return true;
1148 }
1149 return false;
1150}
1151
1152void ExtensionsServiceBackend::CheckVersionAndInstallExtension(
[email protected]a1257b12009-06-12 02:51:341153 const std::string& id, const Version* extension_version,
[email protected]9f1087e2009-06-15 17:29:321154 const FilePath& extension_path) {
[email protected]25b34332009-06-05 21:53:191155 if (ShouldInstall(id, extension_version))
[email protected]9f1087e2009-06-15 17:29:321156 InstallOrUpdateExtension(FilePath(extension_path), id);
[email protected]25b34332009-06-05 21:53:191157}
1158
[email protected]a1257b12009-06-12 02:51:341159bool ExtensionsServiceBackend::LookupExternalExtension(
1160 const std::string& id, Version** version, Extension::Location* location) {
1161 scoped_ptr<Version> extension_version;
1162 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1163 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011164 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341165 extension_version.reset(provider->RegisteredVersion(id, location));
1166 if (extension_version.get()) {
1167 if (version)
1168 *version = extension_version.release();
1169 return true;
1170 }
1171 }
1172 return false;
1173}
1174
[email protected]b0beaa662009-02-26 00:04:151175// Some extensions will autoupdate themselves externally from Chrome. These
1176// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191177// these, the extension will register its location in the the preferences file
1178// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151179// check that location for a .crx file, which it will then install locally if
1180// a new version is available.
1181void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571182 std::set<std::string> ids_to_ignore,
1183 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151184 // Note that this installation is intentionally silent (since it didn't
1185 // go through the front-end). Extensions that are registered in this
1186 // way are effectively considered 'pre-bundled', and so implicitly
1187 // trusted. In general, if something has HKLM or filesystem access,
1188 // they could install an extension manually themselves anyway.
1189 alert_on_error_ = false;
1190 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:151191
[email protected]a1257b12009-06-12 02:51:341192 // Ask each external extension provider to give us a call back for each
1193 // extension they know about. See OnExternalExtensionFound.
1194 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1195 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011196 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341197 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191198 }
[email protected]b0beaa662009-02-26 00:04:151199}
1200
[email protected]25b34332009-06-05 21:53:191201bool ExtensionsServiceBackend::CheckExternalUninstall(
[email protected]e72e8eb82009-06-18 17:21:511202 const std::string& id, Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341203 // Check if the providers know about this extension.
1204 ProviderMap::const_iterator i = external_extension_providers_.find(location);
1205 if (i != external_extension_providers_.end()) {
1206 scoped_ptr<Version> version;
1207 version.reset(i->second->RegisteredVersion(id, NULL));
1208 if (version.get())
1209 return false; // Yup, known extension, don't uninstall.
[email protected]e72e8eb82009-06-18 17:21:511210 } else {
1211 // Not from an external provider, so it's fine.
1212 return false;
[email protected]b0beaa662009-02-26 00:04:151213 }
[email protected]25b34332009-06-05 21:53:191214
[email protected]a1257b12009-06-12 02:51:341215 return true; // This is not a known extension, uninstall.
[email protected]b0beaa662009-02-26 00:04:151216}
1217
1218// Assumes that the extension isn't currently loaded or in use.
[email protected]631cf822009-05-15 07:01:251219void ExtensionsServiceBackend::UninstallExtension(
1220 const std::string& extension_id) {
1221 // First, delete the Current Version file. If the directory delete fails, then
1222 // at least the extension won't be loaded again.
1223 FilePath extension_directory = install_directory_.AppendASCII(extension_id);
1224
1225 if (!file_util::PathExists(extension_directory)) {
1226 LOG(WARNING) << "Asked to remove a non-existent extension " << extension_id;
[email protected]b0beaa662009-02-26 00:04:151227 return;
1228 }
[email protected]631cf822009-05-15 07:01:251229
1230 FilePath current_version_file = extension_directory.AppendASCII(
1231 ExtensionsService::kCurrentVersionFileName);
1232 if (!file_util::PathExists(current_version_file)) {
1233 LOG(WARNING) << "Extension " << extension_id
1234 << " does not have a Current Version file.";
1235 } else {
1236 if (!file_util::Delete(current_version_file, false)) {
1237 LOG(WARNING) << "Could not delete Current Version file for extension "
1238 << extension_id;
1239 return;
1240 }
1241 }
1242
[email protected]a1257b12009-06-12 02:51:341243 // OK, now try and delete the entire rest of the directory. One major place
[email protected]894bb502009-05-21 22:39:571244 // this can fail is if the extension contains a plugin (stupid plugins). It's
1245 // not a big deal though, because we'll notice next time we startup that the
1246 // Current Version file is gone and finish the delete then.
[email protected]631cf822009-05-15 07:01:251247 if (!file_util::Delete(extension_directory, true)) {
1248 LOG(WARNING) << "Could not delete directory for extension "
1249 << extension_id;
[email protected]b0beaa662009-02-26 00:04:151250 }
1251}
1252
[email protected]a1257b12009-06-12 02:51:341253void ExtensionsServiceBackend::ClearProvidersForTesting() {
[email protected]a1257b12009-06-12 02:51:341254 external_extension_providers_.clear();
1255}
1256
1257void ExtensionsServiceBackend::SetProviderForTesting(
1258 Extension::Location location,
1259 ExternalExtensionProvider* test_provider) {
1260 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011261 external_extension_providers_[location] =
1262 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341263}
1264
1265void ExtensionsServiceBackend::OnExternalExtensionFound(
1266 const std::string& id, const Version* version, const FilePath& path) {
[email protected]9f1087e2009-06-15 17:29:321267 CheckVersionAndInstallExtension(id, version, path);
[email protected]a1257b12009-06-12 02:51:341268}
1269
[email protected]b0beaa662009-02-26 00:04:151270bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
[email protected]a1257b12009-06-12 02:51:341271 const Version* version) {
[email protected]b0beaa662009-02-26 00:04:151272 std::string current_version;
[email protected]fbcc40302009-06-12 20:45:451273 Extension::InstallType install_type =
1274 CompareToInstalledVersion(id, version->GetString(), &current_version);
1275
1276 if (install_type == Extension::DOWNGRADE)
1277 return false;
1278
1279 return (install_type == Extension::UPGRADE ||
1280 install_type == Extension::NEW_INSTALL ||
1281 NeedsReinstall(id, current_version));
[email protected]cc655912009-01-29 23:19:191282}