blob: 04ccd13877612d9530596d8044e715d43746f391 [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
7#include "base/file_util.h"
[email protected]cc655912009-01-29 23:19:198#include "base/scoped_handle.h"
9#include "base/scoped_temp_dir.h"
[email protected]6014d672008-12-05 00:38:2510#include "base/string_util.h"
[email protected]cc655912009-01-29 23:19:1911#include "base/third_party/nss/blapi.h"
12#include "base/third_party/nss/sha256.h"
[email protected]6014d672008-12-05 00:38:2513#include "base/thread.h"
[email protected]cc655912009-01-29 23:19:1914#include "base/values.h"
15#include "net/base/file_stream.h"
[email protected]a57209872009-05-04 22:53:1416#include "chrome/browser/browser.h"
17#include "chrome/browser/browser_list.h"
[email protected]6014d672008-12-05 00:38:2518#include "chrome/browser/browser_process.h"
[email protected]1fca1492009-05-15 22:23:4319#include "chrome/browser/chrome_thread.h"
[email protected]69f1be82009-04-16 22:27:2120#include "chrome/browser/extensions/extension.h"
[email protected]b68d5ed2009-04-16 02:41:2821#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]bb28e062009-02-27 17:19:1822#include "chrome/browser/extensions/extension_error_reporter.h"
[email protected]481e1a42009-05-06 20:56:0523#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]81e63782009-02-27 19:35:0924#include "chrome/browser/profile.h"
[email protected]1fca1492009-05-15 22:23:4325#include "chrome/browser/utility_process_host.h"
26#include "chrome/common/extensions/extension_unpacker.h"
[email protected]6014d672008-12-05 00:38:2527#include "chrome/common/json_value_serializer.h"
[email protected]82891262008-12-24 00:21:2628#include "chrome/common/notification_service.h"
[email protected]894bb502009-05-21 22:39:5729#include "chrome/common/pref_service.h"
[email protected]cc655912009-01-29 23:19:1930#include "chrome/common/unzip.h"
[email protected]a57209872009-05-04 22:53:1431#include "chrome/common/url_constants.h"
[email protected]c64631652009-04-29 22:24:3132
[email protected]79db6232009-02-13 20:51:2033#if defined(OS_WIN)
[email protected]b0beaa662009-02-26 00:04:1534#include "base/registry.h"
[email protected]79db6232009-02-13 20:51:2035#endif
[email protected]6014d672008-12-05 00:38:2536
37// ExtensionsService
38
[email protected]cc655912009-01-29 23:19:1939const char* ExtensionsService::kInstallDirectoryName = "Extensions";
40const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
41const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
[email protected]b0beaa662009-02-26 00:04:1542
43namespace {
[email protected]cc655912009-01-29 23:19:1944// Chromium Extension magic number
[email protected]b0beaa662009-02-26 00:04:1545const char kExtensionFileMagic[] = "Cr24";
[email protected]cc655912009-01-29 23:19:1946
47struct ExtensionHeader {
48 char magic[sizeof(kExtensionFileMagic) - 1];
49 uint32 version;
50 size_t header_size;
51 size_t manifest_size;
52};
53
54const size_t kZipHashBytes = 32; // SHA-256
55const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
[email protected]6014d672008-12-05 00:38:2556
[email protected]894bb502009-05-21 22:39:5757// A preference that keeps track of external extensions the user has
58// uninstalled.
59const wchar_t kUninstalledExternalPref[] =
60 L"extensions.uninstalled_external_ids";
[email protected]b0beaa662009-02-26 00:04:1561
62// Registry key where registry defined extension installers live.
[email protected]894bb502009-05-21 22:39:5763// TODO(port): Assuming this becomes a similar key into the appropriate
64// platform system.
65const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";
66
67#if defined(OS_WIN)
[email protected]b0beaa662009-02-26 00:04:1568
69// Registry value of of that key that defines the path to the .crx file.
70const wchar_t kRegistryExtensionPath[] = L"path";
71
72// Registry value of that key that defines the current version of the .crx file.
73const wchar_t kRegistryExtensionVersion[] = L"version";
74
75#endif
76
77// A marker file to indicate that an extension was installed from an external
78// source.
79const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
[email protected]0b344962009-03-31 04:21:4580
[email protected]1fca1492009-05-15 22:23:4381// A temporary subdirectory where we unpack extensions.
82const char* kUnpackExtensionDir = "TEMP_UNPACK";
83
[email protected]0b344962009-03-31 04:21:4584// The version of the extension package that this code understands.
85const uint32 kExpectedVersion = 1;
[email protected]b0beaa662009-02-26 00:04:1586}
87
[email protected]1fca1492009-05-15 22:23:4388// This class coordinates an extension unpack task which is run in a separate
89// process. Results are sent back to this class, which we route to the
90// ExtensionServiceBackend.
91class ExtensionsServiceBackend::UnpackerClient
92 : public UtilityProcessHost::Client {
93 public:
94 UnpackerClient(ExtensionsServiceBackend* backend,
95 const FilePath& extension_path,
96 const std::string& expected_id,
97 bool from_external)
98 : backend_(backend), extension_path_(extension_path),
99 expected_id_(expected_id), from_external_(from_external) {
100 }
101
102 // Starts the unpack task. We call back to the backend when the task is done,
103 // or a problem occurs.
104 void Start() {
105 AddRef(); // balanced in OnUnpackExtensionReply()
106
107 // TODO(mpcomplete): handle multiple installs
108 FilePath temp_dir = backend_->install_directory_.AppendASCII(
109 kUnpackExtensionDir);
110 if (!file_util::CreateDirectory(temp_dir)) {
111 backend_->ReportExtensionInstallError(extension_path_,
112 "Failed to create temporary directory.");
113 return;
114 }
115
116 temp_extension_path_ = temp_dir.Append(extension_path_.BaseName());
117 if (!file_util::CopyFile(extension_path_, temp_extension_path_)) {
118 backend_->ReportExtensionInstallError(extension_path_,
119 "Failed to copy extension file to temporary directory.");
120 return;
121 }
122
123 if (backend_->resource_dispatcher_host_) {
124 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE,
125 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread,
126 backend_->resource_dispatcher_host_,
127 MessageLoop::current()));
128 } else {
129 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're
130 // in a unit test and run the unpacker directly in-process.
131 ExtensionUnpacker unpacker(temp_extension_path_);
132 bool success = unpacker.Run();
133 OnUnpackExtensionReply(success, unpacker.error_message());
134 }
135 }
136
137 private:
138 // UtilityProcessHost::Client
139 virtual void OnProcessCrashed() {
140 OnUnpackExtensionReply(false, "Chrome crashed while trying to install");
141 }
142
143 virtual void OnUnpackExtensionReply(bool success,
144 const std::string& error_message) {
145 if (success) {
146 // The extension was unpacked to the temp dir inside our unpacking dir.
147 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII(
148 ExtensionsServiceBackend::kTempExtensionName);
149 backend_->OnExtensionUnpacked(extension_path_, extension_dir,
150 expected_id_, from_external_);
151 } else {
152 backend_->ReportExtensionInstallError(extension_path_, error_message);
153 }
154 Cleanup();
155 Release(); // balanced in Run()
156 }
157
158 // Cleans up our temp directory.
159 void Cleanup() {
160 file_util::Delete(temp_extension_path_.DirName(), true);
161 }
162
163 // Starts the utility process that unpacks our extension.
164 void StartProcessOnIOThread(ResourceDispatcherHost* rdh,
165 MessageLoop* file_loop) {
166 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop);
167 host->StartExtensionUnpacker(temp_extension_path_);
168 }
169
170 scoped_refptr<ExtensionsServiceBackend> backend_;
171
172 // The path to the crx file that we're installing.
173 FilePath extension_path_;
174
175 // The path to the copy of the crx file in the temporary directory where we're
176 // unpacking it.
177 FilePath temp_extension_path_;
178
179 // The ID we expect this extension to have, if any.
180 std::string expected_id_;
181
182 // True if this is being installed from an external source.
183 bool from_external_;
184};
185
[email protected]81e63782009-02-27 19:35:09186ExtensionsService::ExtensionsService(Profile* profile,
[email protected]894bb502009-05-21 22:39:57187 MessageLoop* frontend_loop,
188 MessageLoop* backend_loop,
189 const std::string& registry_path)
190 : prefs_(profile->GetPrefs()),
191 backend_loop_(backend_loop),
[email protected]81e63782009-02-27 19:35:09192 install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
[email protected]1fca1492009-05-15 22:23:43193 backend_(new ExtensionsServiceBackend(
[email protected]894bb502009-05-21 22:39:57194 install_directory_, g_browser_process->resource_dispatcher_host(),
195 frontend_loop, registry_path)) {
196 prefs_->RegisterListPref(kUninstalledExternalPref);
[email protected]6014d672008-12-05 00:38:25197}
198
199ExtensionsService::~ExtensionsService() {
200 for (ExtensionList::iterator iter = extensions_.begin();
201 iter != extensions_.end(); ++iter) {
202 delete *iter;
203 }
204}
205
206bool ExtensionsService::Init() {
[email protected]b68d5ed2009-04-16 02:41:28207 // Start up the extension event routers.
208 ExtensionBrowserEventRouter::GetInstance()->Init();
209
[email protected]b0beaa662009-02-26 00:04:15210#if defined(OS_WIN)
[email protected]894bb502009-05-21 22:39:57211
212 std::set<std::string> uninstalled_external_ids;
213 const ListValue* list = prefs_->GetList(kUninstalledExternalPref);
214 if (list) {
215 for (size_t i = 0; i < list->GetSize(); ++i) {
216 std::string val;
217 bool ok = list->GetString(i, &val);
218 DCHECK(ok);
219 DCHECK(uninstalled_external_ids.find(val) ==
220 uninstalled_external_ids.end());
221 uninstalled_external_ids.insert(val);
222 }
223 }
224
[email protected]b0beaa662009-02-26 00:04:15225 // TODO(erikkay): Should we monitor the registry during run as well?
[email protected]894bb502009-05-21 22:39:57226 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
227 &ExtensionsServiceBackend::CheckForExternalUpdates,
228 uninstalled_external_ids, scoped_refptr<ExtensionsService>(this)));
229#else
230
231 // TODO(port)
232
[email protected]b0beaa662009-02-26 00:04:15233#endif
234
[email protected]894bb502009-05-21 22:39:57235 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
236 &ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory,
237 scoped_refptr<ExtensionsService>(this)));
[email protected]6014d672008-12-05 00:38:25238
239 return true;
240}
241
[email protected]3cf4f0992009-02-03 23:00:30242void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]894bb502009-05-21 22:39:57243 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
244 &ExtensionsServiceBackend::InstallExtension,
245 extension_path,
246 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30247}
248
[email protected]631cf822009-05-15 07:01:25249void ExtensionsService::UninstallExtension(const std::string& extension_id) {
250 Extension* extension = NULL;
251
252 ExtensionList::iterator iter;
253 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
254 if ((*iter)->id() == extension_id) {
255 extension = *iter;
256 break;
257 }
258 }
259
[email protected]894bb502009-05-21 22:39:57260 // Callers should not send us nonexistant extensions.
261 CHECK(extension);
262
[email protected]631cf822009-05-15 07:01:25263 // Remove the extension from our list.
264 extensions_.erase(iter);
265
[email protected]631cf822009-05-15 07:01:25266 // Tell other services the extension is gone.
267 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
268 NotificationService::AllSources(),
269 Details<Extension>(extension));
270
[email protected]894bb502009-05-21 22:39:57271 // For external extensions, we save a preference reminding ourself not to try
272 // and install the extension anymore.
273 if (extension->location() == Extension::EXTERNAL) {
274 ListValue* list = prefs_->GetMutableList(kUninstalledExternalPref);
275 list->Append(Value::CreateStringValue(extension->id()));
276 prefs_->ScheduleSavePersistentPrefs();
277 }
278
279 // Tell the backend to start deleting installed extensions on the file thread.
280 if (extension->location() == Extension::INTERNAL ||
281 extension->location() == Extension::EXTERNAL) {
282 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
283 &ExtensionsServiceBackend::UninstallExtension, extension_id));
284 }
[email protected]631cf822009-05-15 07:01:25285
286 delete extension;
287}
288
[email protected]3cf4f0992009-02-03 23:00:30289void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]894bb502009-05-21 22:39:57290 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
291 &ExtensionsServiceBackend::LoadSingleExtension,
292 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30293}
294
[email protected]4a8d3272009-03-10 19:15:08295void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]08816d0d2008-12-08 18:43:53296 extensions_.insert(extensions_.end(), new_extensions->begin(),
297 new_extensions->end());
[email protected]bfd04a62009-02-01 18:16:56298 NotificationService::current()->Notify(
299 NotificationType::EXTENSIONS_LOADED,
[email protected]82891262008-12-24 00:21:26300 NotificationService::AllSources(),
301 Details<ExtensionList>(new_extensions));
[email protected]82891262008-12-24 00:21:26302 delete new_extensions;
[email protected]6014d672008-12-05 00:38:25303}
304
[email protected]a57209872009-05-04 22:53:14305void ExtensionsService::OnExtensionInstalled(Extension* extension,
306 bool update) {
[email protected]bfd04a62009-02-01 18:16:56307 NotificationService::current()->Notify(
308 NotificationType::EXTENSION_INSTALLED,
[email protected]cc655912009-01-29 23:19:19309 NotificationService::AllSources(),
[email protected]a57209872009-05-04 22:53:14310 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42311
312 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
313 // to apply it.
314 if (extension->IsTheme()) {
315 NotificationService::current()->Notify(
316 NotificationType::THEME_INSTALLED,
317 NotificationService::AllSources(),
318 Details<Extension>(extension));
319 }
320}
321
322void ExtensionsService::OnExtensionVersionReinstalled(const std::string& id) {
323 Extension* extension = GetExtensionByID(id);
324 if (extension && extension->IsTheme()) {
325 NotificationService::current()->Notify(
326 NotificationType::THEME_INSTALLED,
327 NotificationService::AllSources(),
328 Details<Extension>(extension));
329 }
[email protected]cc655912009-01-29 23:19:19330}
331
[email protected]ce5c4502009-05-06 16:46:11332Extension* ExtensionsService::GetExtensionByID(std::string id) {
333 for (ExtensionList::const_iterator iter = extensions_.begin();
[email protected]4a190632009-05-09 01:07:42334 iter != extensions_.end(); ++iter) {
[email protected]ce5c4502009-05-06 16:46:11335 if ((*iter)->id() == id)
336 return *iter;
337 }
338 return NULL;
339}
340
[email protected]6014d672008-12-05 00:38:25341
342// ExtensionsServicesBackend
343
[email protected]894bb502009-05-21 22:39:57344ExtensionsServiceBackend::ExtensionsServiceBackend(
345 const FilePath& install_directory, ResourceDispatcherHost* rdh,
346 MessageLoop* frontend_loop, const std::string& registry_path)
347 : install_directory_(install_directory),
348 resource_dispatcher_host_(rdh),
349 frontend_loop_(frontend_loop),
350 registry_path_(registry_path) {
351 // Default the registry path if unspecified.
352 if (registry_path_.empty()) {
353 registry_path_ = kRegistryExtensions;
354 }
355}
356
[email protected]cc5da332009-03-04 08:02:51357void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
[email protected]894bb502009-05-21 22:39:57358 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15359 frontend_ = frontend;
360 alert_on_error_ = false;
[email protected]05eb0fa2009-02-21 00:05:48361
[email protected]540f91b2009-03-26 19:37:43362#if defined(OS_WIN)
363 // On POSIX, AbsolutePath() calls realpath() which returns NULL if
364 // it does not exist. Instead we absolute-ify after creation in
365 // case that is needed.
366 // TODO(port): does this really need to happen before
367 // CreateDirectory() on Windows?
[email protected]cc5da332009-03-04 08:02:51368 if (!file_util::AbsolutePath(&install_directory_))
[email protected]eab9b452009-01-23 20:48:59369 NOTREACHED();
[email protected]540f91b2009-03-26 19:37:43370#endif
[email protected]eab9b452009-01-23 20:48:59371
[email protected]b0beaa662009-02-26 00:04:15372 scoped_ptr<ExtensionList> extensions(new ExtensionList);
373
374 // Create the <Profile>/Extensions directory if it doesn't exist.
[email protected]cc5da332009-03-04 08:02:51375 if (!file_util::DirectoryExists(install_directory_)) {
376 file_util::CreateDirectory(install_directory_);
[email protected]b0beaa662009-02-26 00:04:15377 LOG(INFO) << "Created Extensions directory. No extensions to install.";
378 ReportExtensionsLoaded(extensions.release());
379 return;
380 }
381
[email protected]540f91b2009-03-26 19:37:43382#if !defined(OS_WIN)
383 if (!file_util::AbsolutePath(&install_directory_))
384 NOTREACHED();
385#endif
386
[email protected]bf24d2c2009-02-24 23:07:45387 LOG(INFO) << "Loading installed extensions...";
388
[email protected]6014d672008-12-05 00:38:25389 // Find all child directories in the install directory and load their
390 // manifests. Post errors and results to the frontend.
[email protected]cc5da332009-03-04 08:02:51391 file_util::FileEnumerator enumerator(install_directory_,
[email protected]cc655912009-01-29 23:19:19392 false, // not recursive
[email protected]6014d672008-12-05 00:38:25393 file_util::FileEnumerator::DIRECTORIES);
[email protected]cc5da332009-03-04 08:02:51394 FilePath extension_path;
395 for (extension_path = enumerator.Next(); !extension_path.value().empty();
396 extension_path = enumerator.Next()) {
397 std::string extension_id = WideToASCII(
398 extension_path.BaseName().ToWStringHack());
[email protected]894bb502009-05-21 22:39:57399
400 // If there is no Current Version file, just delete the directory and move
401 // on. This can legitimately happen when an uninstall does not complete, for
402 // example, when a plugin is in use at uninstall time.
403 FilePath current_version_path = extension_path.AppendASCII(
404 ExtensionsService::kCurrentVersionFileName);
405 if (!file_util::PathExists(current_version_path)) {
406 LOG(INFO) << "Deleting incomplete install for directory "
407 << WideToASCII(extension_path.ToWStringHack()) << ".";
408 file_util::Delete(extension_path, true); // recursive;
409 continue;
410 }
411
412 std::string current_version;
413 if (!ReadCurrentVersion(extension_path, &current_version))
414 continue;
415
416 FilePath version_path = extension_path.AppendASCII(current_version);
417 if (CheckExternalUninstall(version_path, extension_id)) {
[email protected]cc5da332009-03-04 08:02:51418 // TODO(erikkay): Possibly defer this operation to avoid slowing initial
419 // load of extensions.
[email protected]631cf822009-05-15 07:01:25420 UninstallExtension(extension_id);
[email protected]cc5da332009-03-04 08:02:51421
422 // No error needs to be reported. The extension effectively doesn't
423 // exist.
424 continue;
425 }
426
[email protected]894bb502009-05-21 22:39:57427 Extension* extension = LoadExtension(version_path, true); // require id
[email protected]0877fd92009-02-03 16:34:06428 if (extension)
429 extensions->push_back(extension);
[email protected]6014d672008-12-05 00:38:25430 }
431
[email protected]bf24d2c2009-02-24 23:07:45432 LOG(INFO) << "Done.";
[email protected]b0beaa662009-02-26 00:04:15433 ReportExtensionsLoaded(extensions.release());
[email protected]6014d672008-12-05 00:38:25434}
435
[email protected]b0beaa662009-02-26 00:04:15436void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57437 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15438 frontend_ = frontend;
439
440 // Explicit UI loads are always noisy.
441 alert_on_error_ = true;
442
[email protected]cc5da332009-03-04 08:02:51443 FilePath extension_path = path_in;
444 if (!file_util::AbsolutePath(&extension_path))
[email protected]0877fd92009-02-03 16:34:06445 NOTREACHED();
[email protected]bf24d2c2009-02-24 23:07:45446
447 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51448 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45449
[email protected]5bfb1eb0a2009-04-08 18:33:30450 Extension* extension = LoadExtension(extension_path,
451 false); // don't require ID
[email protected]0877fd92009-02-03 16:34:06452 if (extension) {
[email protected]631cf822009-05-15 07:01:25453 extension->set_location(Extension::LOAD);
[email protected]0877fd92009-02-03 16:34:06454 ExtensionList* extensions = new ExtensionList;
455 extensions->push_back(extension);
[email protected]b0beaa662009-02-26 00:04:15456 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06457 }
[email protected]0877fd92009-02-03 16:34:06458}
459
[email protected]cc5da332009-03-04 08:02:51460Extension* ExtensionsServiceBackend::LoadExtension(
[email protected]5bfb1eb0a2009-04-08 18:33:30461 const FilePath& extension_path, bool require_id) {
[email protected]0877fd92009-02-03 16:34:06462 FilePath manifest_path =
[email protected]cc5da332009-03-04 08:02:51463 extension_path.AppendASCII(Extension::kManifestFilename);
[email protected]0877fd92009-02-03 16:34:06464 if (!file_util::PathExists(manifest_path)) {
[email protected]cc5da332009-03-04 08:02:51465 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06466 return NULL;
467 }
468
[email protected]1635bc12009-04-03 17:18:27469 JSONFileValueSerializer serializer(manifest_path);
[email protected]0877fd92009-02-03 16:34:06470 std::string error;
471 scoped_ptr<Value> root(serializer.Deserialize(&error));
472 if (!root.get()) {
[email protected]cc5da332009-03-04 08:02:51473 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06474 return NULL;
475 }
476
477 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]cc5da332009-03-04 08:02:51478 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06479 return NULL;
480 }
481
[email protected]cc5da332009-03-04 08:02:51482 scoped_ptr<Extension> extension(new Extension(extension_path));
[email protected]0877fd92009-02-03 16:34:06483 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
[email protected]5bfb1eb0a2009-04-08 18:33:30484 require_id, &error)) {
[email protected]cc5da332009-03-04 08:02:51485 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06486 return NULL;
487 }
[email protected]8d6d9ff2009-02-20 08:14:39488
[email protected]631cf822009-05-15 07:01:25489 FilePath external_marker = extension_path.AppendASCII(kExternalInstallFile);
490 if (file_util::PathExists(external_marker))
491 extension->set_location(Extension::EXTERNAL);
492 else
493 extension->set_location(Extension::INTERNAL);
494
[email protected]6706963472009-05-09 02:19:19495 // TODO(glen): Add theme resource validation here. http://crbug.com/11678
[email protected]d24070e22009-05-21 19:26:59496 // Validate that claimed script resources actually exist.
[email protected]3cfbd0e2009-03-18 21:26:24497 for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
498 const UserScript& script = extension->content_scripts()[i];
499
500 for (size_t j = 0; j < script.js_scripts().size(); j++) {
501 const FilePath& path = script.js_scripts()[j].path();
502 if (!file_util::PathExists(path)) {
503 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59504 StringPrintf("Could not load '%s' for content script.",
505 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24506 return NULL;
507 }
508 }
509
510 for (size_t j = 0; j < script.css_scripts().size(); j++) {
511 const FilePath& path = script.css_scripts()[j].path();
512 if (!file_util::PathExists(path)) {
513 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59514 StringPrintf("Could not load '%s' for content script.",
515 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24516 return NULL;
517 }
[email protected]8d6d9ff2009-02-20 08:14:39518 }
519 }
520
[email protected]d24070e22009-05-21 19:26:59521 // Validate icon location for page actions.
522 const PageActionMap& page_actions = extension->page_actions();
523 for (PageActionMap::const_iterator i(page_actions.begin());
524 i != page_actions.end(); ++i) {
525 PageAction* page_action = i->second;
526 FilePath path = page_action->icon_path();
527 if (!file_util::PathExists(path)) {
528 ReportExtensionLoadError(extension_path,
529 StringPrintf("Could not load icon '%s' for page action.",
530 WideToUTF8(path.ToWStringHack()).c_str()));
531 return NULL;
532 }
533 }
534
[email protected]0877fd92009-02-03 16:34:06535 return extension.release();
536}
537
[email protected]6014d672008-12-05 00:38:25538void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51539 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15540 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51541 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00542 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19543 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18544 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25545}
546
547void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15548 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57549 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
550 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25551}
[email protected]cc655912009-01-29 23:19:19552
553// The extension file format is a header, followed by the manifest, followed
554// by the zip file. The header is a magic number, a version, the size of the
555// header, and the size of the manifest. These ints are 4 byte little endian.
[email protected]cc5da332009-03-04 08:02:51556DictionaryValue* ExtensionsServiceBackend::ReadManifest(
557 const FilePath& extension_path) {
558 ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
[email protected]cc655912009-01-29 23:19:19559 if (!file.get()) {
[email protected]cc5da332009-03-04 08:02:51560 ReportExtensionInstallError(extension_path, "no such extension file");
[email protected]cc655912009-01-29 23:19:19561 return NULL;
562 }
563
564 // Read and verify the header.
565 ExtensionHeader header;
566 size_t len;
[email protected]b0beaa662009-02-26 00:04:15567
[email protected]cc655912009-01-29 23:19:19568 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
569 // appears that we don't have any endian/alignment aware serialization
570 // code in the code base. So for now, this assumes that we're running
571 // on a little endian machine with 4 byte alignment.
572 len = fread(&header, 1, sizeof(ExtensionHeader), file.get());
573 if (len < sizeof(ExtensionHeader)) {
[email protected]cc5da332009-03-04 08:02:51574 ReportExtensionInstallError(extension_path, "invalid extension header");
[email protected]cc655912009-01-29 23:19:19575 return NULL;
576 }
577 if (strncmp(kExtensionFileMagic, header.magic, sizeof(header.magic))) {
[email protected]cc5da332009-03-04 08:02:51578 ReportExtensionInstallError(extension_path, "bad magic number");
[email protected]cc655912009-01-29 23:19:19579 return NULL;
580 }
[email protected]0b344962009-03-31 04:21:45581 if (header.version != kExpectedVersion) {
[email protected]cc5da332009-03-04 08:02:51582 ReportExtensionInstallError(extension_path, "bad version number");
[email protected]cc655912009-01-29 23:19:19583 return NULL;
584 }
585 if (header.header_size > sizeof(ExtensionHeader))
586 fseek(file.get(), header.header_size - sizeof(ExtensionHeader), SEEK_CUR);
[email protected]f0a51fb52009-03-05 12:46:38587
[email protected]cc655912009-01-29 23:19:19588 char buf[1 << 16];
589 std::string manifest_str;
590 size_t read_size = std::min(sizeof(buf), header.manifest_size);
591 size_t remainder = header.manifest_size;
592 while ((len = fread(buf, 1, read_size, file.get())) > 0) {
593 manifest_str.append(buf, len);
594 if (len <= remainder)
595 break;
596 remainder -= len;
597 read_size = std::min(sizeof(buf), remainder);
598 }
599
600 // Verify the JSON
601 JSONStringValueSerializer json(manifest_str);
602 std::string error;
[email protected]4c7ca4b2009-02-04 00:53:08603 scoped_ptr<Value> val(json.Deserialize(&error));
604 if (!val.get()) {
[email protected]cc5da332009-03-04 08:02:51605 ReportExtensionInstallError(extension_path, error);
[email protected]cc655912009-01-29 23:19:19606 return NULL;
607 }
608 if (!val->IsType(Value::TYPE_DICTIONARY)) {
[email protected]cc5da332009-03-04 08:02:51609 ReportExtensionInstallError(extension_path,
610 "manifest isn't a JSON dictionary");
[email protected]cc655912009-01-29 23:19:19611 return NULL;
612 }
[email protected]4c7ca4b2009-02-04 00:53:08613 DictionaryValue* manifest = static_cast<DictionaryValue*>(val.get());
[email protected]b0beaa662009-02-26 00:04:15614
615 // Check the version before proceeding. Although we verify the version
616 // again later, checking it here allows us to skip some potentially expensive
617 // work.
618 std::string id;
[email protected]8e50b602009-03-03 22:59:43619 if (!manifest->GetString(Extension::kIdKey, &id)) {
[email protected]cc5da332009-03-04 08:02:51620 ReportExtensionInstallError(extension_path, "missing id key");
[email protected]b0beaa662009-02-26 00:04:15621 return NULL;
622 }
623 FilePath dest_dir = install_directory_.AppendASCII(id.c_str());
624 if (file_util::PathExists(dest_dir)) {
625 std::string version;
[email protected]8e50b602009-03-03 22:59:43626 if (!manifest->GetString(Extension::kVersionKey, &version)) {
[email protected]cc5da332009-03-04 08:02:51627 ReportExtensionInstallError(extension_path, "missing version key");
[email protected]b0beaa662009-02-26 00:04:15628 return NULL;
629 }
630 std::string current_version;
631 if (ReadCurrentVersion(dest_dir, &current_version)) {
632 if (!CheckCurrentVersion(version, current_version, dest_dir))
633 return NULL;
634 }
635 }
636
[email protected]cc655912009-01-29 23:19:19637 std::string zip_hash;
[email protected]8e50b602009-03-03 22:59:43638 if (!manifest->GetString(Extension::kZipHashKey, &zip_hash)) {
[email protected]cc5da332009-03-04 08:02:51639 ReportExtensionInstallError(extension_path, "missing zip_hash key");
[email protected]cc655912009-01-29 23:19:19640 return NULL;
641 }
642 if (zip_hash.size() != kZipHashHexBytes) {
[email protected]cc5da332009-03-04 08:02:51643 ReportExtensionInstallError(extension_path, "invalid zip_hash key");
[email protected]cc655912009-01-29 23:19:19644 return NULL;
645 }
646
647 // Read the rest of the zip file and compute a hash to compare against
648 // what the manifest claims. Compute the hash incrementally since the
649 // zip file could be large.
650 const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
651 SHA256Context ctx;
652 SHA256_Begin(&ctx);
653 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
654 SHA256_Update(&ctx, ubuf, len);
655 uint8 hash[32];
656 SHA256_End(&ctx, hash, NULL, sizeof(hash));
657
658 std::vector<uint8> zip_hash_bytes;
659 if (!HexStringToBytes(zip_hash, &zip_hash_bytes)) {
[email protected]cc5da332009-03-04 08:02:51660 ReportExtensionInstallError(extension_path, "invalid zip_hash key");
[email protected]cc655912009-01-29 23:19:19661 return NULL;
662 }
663 if (zip_hash_bytes.size() != kZipHashBytes) {
[email protected]cc5da332009-03-04 08:02:51664 ReportExtensionInstallError(extension_path, "invalid zip_hash key");
[email protected]cc655912009-01-29 23:19:19665 return NULL;
666 }
667 for (size_t i = 0; i < kZipHashBytes; ++i) {
668 if (zip_hash_bytes[i] != hash[i]) {
[email protected]cc5da332009-03-04 08:02:51669 ReportExtensionInstallError(extension_path,
670 "zip_hash key didn't match zip hash");
[email protected]cc655912009-01-29 23:19:19671 return NULL;
672 }
673 }
674
675 // TODO(erikkay): The manifest will also contain a signature of the hash
676 // (or perhaps the whole manifest) for authentication purposes.
677
[email protected]4c7ca4b2009-02-04 00:53:08678 // The caller owns val (now cast to manifest).
679 val.release();
[email protected]cc655912009-01-29 23:19:19680 return manifest;
681}
682
[email protected]b0beaa662009-02-26 00:04:15683bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
684 std::string* version_string) {
[email protected]18a12352009-01-31 01:33:28685 FilePath current_version =
[email protected]b0beaa662009-02-26 00:04:15686 dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
[email protected]18a12352009-01-31 01:33:28687 if (file_util::PathExists(current_version)) {
688 if (file_util::ReadFileToString(current_version, version_string)) {
689 TrimWhitespace(*version_string, TRIM_ALL, version_string);
690 return true;
691 }
692 }
693 return false;
694}
695
[email protected]cc655912009-01-29 23:19:19696bool ExtensionsServiceBackend::CheckCurrentVersion(
[email protected]b0beaa662009-02-26 00:04:15697 const std::string& new_version_str,
698 const std::string& current_version_str,
699 const FilePath& dest_dir) {
700 scoped_ptr<Version> current_version(
701 Version::GetVersionFromString(current_version_str));
702 scoped_ptr<Version> new_version(
703 Version::GetVersionFromString(new_version_str));
704 if (current_version->CompareTo(*new_version) >= 0) {
[email protected]b0beaa662009-02-26 00:04:15705 // Verify that the directory actually exists. If it doesn't we'll return
706 // true so that the install code will repair the broken installation.
707 // TODO(erikkay): A further step would be to verify that the extension
708 // has actually loaded successfully.
709 FilePath version_dir = dest_dir.AppendASCII(current_version_str);
710 if (file_util::PathExists(version_dir)) {
[email protected]4a190632009-05-09 01:07:42711 std::string id = WideToASCII(dest_dir.BaseName().ToWStringHack());
712 StringToLowerASCII(&id);
713 ReportExtensionVersionReinstalled(id);
[email protected]b0beaa662009-02-26 00:04:15714 return false;
[email protected]cc655912009-01-29 23:19:19715 }
716 }
717 return true;
718}
719
[email protected]f0a51fb52009-03-05 12:46:38720bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
[email protected]b0beaa662009-02-26 00:04:15721 const FilePath& dest_dir) {
[email protected]cc655912009-01-29 23:19:19722 if (file_util::PathExists(dest_dir)) {
723 // By the time we get here, it should be safe to assume that this directory
724 // is not currently in use (it's not the current active version).
725 if (!file_util::Delete(dest_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51726 ReportExtensionInstallError(source_dir,
[email protected]cc655912009-01-29 23:19:19727 "Can't delete existing version directory.");
728 return false;
729 }
730 } else {
731 FilePath parent = dest_dir.DirName();
732 if (!file_util::DirectoryExists(parent)) {
733 if (!file_util::CreateDirectory(parent)) {
[email protected]cc5da332009-03-04 08:02:51734 ReportExtensionInstallError(source_dir,
735 "Couldn't create extension directory.");
[email protected]cc655912009-01-29 23:19:19736 return false;
737 }
738 }
739 }
740 if (!file_util::Move(source_dir, dest_dir)) {
[email protected]cc5da332009-03-04 08:02:51741 ReportExtensionInstallError(source_dir,
742 "Couldn't move temporary directory.");
[email protected]cc655912009-01-29 23:19:19743 return false;
744 }
745
746 return true;
747}
748
[email protected]b0beaa662009-02-26 00:04:15749bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
750 std::string version) {
[email protected]cc655912009-01-29 23:19:19751 // Write out the new CurrentVersion file.
752 // <profile>/Extension/<name>/CurrentVersion
753 FilePath current_version =
754 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
755 FilePath current_version_old =
756 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
757 if (file_util::PathExists(current_version_old)) {
758 if (!file_util::Delete(current_version_old, false)) {
[email protected]cc5da332009-03-04 08:02:51759 ReportExtensionInstallError(dest_dir,
760 "Couldn't remove CurrentVersion_old file.");
[email protected]cc655912009-01-29 23:19:19761 return false;
762 }
763 }
764 if (file_util::PathExists(current_version)) {
765 if (!file_util::Move(current_version, current_version_old)) {
[email protected]cc5da332009-03-04 08:02:51766 ReportExtensionInstallError(dest_dir,
767 "Couldn't move CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19768 return false;
769 }
770 }
771 net::FileStream stream;
772 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
773 if (stream.Open(current_version, flags) != 0)
774 return false;
775 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
776 // Restore the old CurrentVersion.
777 if (file_util::PathExists(current_version_old)) {
778 if (!file_util::Move(current_version_old, current_version)) {
[email protected]f0a51fb52009-03-05 12:46:38779 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
[email protected]cc655912009-01-29 23:19:19780 " to " << current_version.value();
[email protected]b0beaa662009-02-26 00:04:15781
[email protected]cc655912009-01-29 23:19:19782 // TODO(erikkay): This is an ugly state to be in. Try harder?
783 }
784 }
[email protected]f0a51fb52009-03-05 12:46:38785 ReportExtensionInstallError(dest_dir,
[email protected]cc5da332009-03-04 08:02:51786 "Couldn't create CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19787 return false;
788 }
789 return true;
790}
791
[email protected]b0beaa662009-02-26 00:04:15792void ExtensionsServiceBackend::InstallExtension(
[email protected]894bb502009-05-21 22:39:57793 const FilePath& extension_path, scoped_refptr<ExtensionsService> frontend) {
[email protected]cc655912009-01-29 23:19:19794 LOG(INFO) << "Installing extension " << extension_path.value();
795
[email protected]b0beaa662009-02-26 00:04:15796 frontend_ = frontend;
[email protected]cc5da332009-03-04 08:02:51797 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15798
[email protected]1fca1492009-05-15 22:23:43799 bool from_external = false;
800 InstallOrUpdateExtension(extension_path, std::string(), from_external);
[email protected]b0beaa662009-02-26 00:04:15801}
802
[email protected]1fca1492009-05-15 22:23:43803void ExtensionsServiceBackend::InstallOrUpdateExtension(
804 const FilePath& extension_path, const std::string& expected_id,
805 bool from_external) {
806 UnpackerClient* client =
807 new UnpackerClient(this, extension_path, expected_id, from_external);
808 client->Start();
809}
[email protected]cc655912009-01-29 23:19:19810
[email protected]1fca1492009-05-15 22:23:43811void ExtensionsServiceBackend::OnExtensionUnpacked(
812 const FilePath& extension_path,
813 const FilePath& temp_extension_dir,
814 const std::string expected_id,
815 bool from_external) {
816 // TODO(mpcomplete): the utility process should pass up a parsed manifest that
817 // we rewrite in the browser.
818 // Bug http://code.google.com/p/chromium/issues/detail?id=11680
819 scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path));
[email protected]cc655912009-01-29 23:19:19820 if (!manifest.get()) {
821 // ReadManifest has already reported the extension error.
[email protected]1fca1492009-05-15 22:23:43822 return;
[email protected]b0beaa662009-02-26 00:04:15823 }
824
[email protected]1fca1492009-05-15 22:23:43825 Extension extension;
826 std::string error;
827 if (!extension.InitFromValue(*manifest,
828 true, // require ID
829 &error)) {
830 ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
831 return;
[email protected]0b344962009-03-31 04:21:45832 }
833
[email protected]b0beaa662009-02-26 00:04:15834 // If an expected id was provided, make sure it matches.
[email protected]1fca1492009-05-15 22:23:43835 if (!expected_id.empty() && expected_id != extension.id()) {
836 ReportExtensionInstallError(extension_path,
[email protected]b0beaa662009-02-26 00:04:15837 "ID in new extension manifest does not match expected ID.");
[email protected]1fca1492009-05-15 22:23:43838 return;
[email protected]cc655912009-01-29 23:19:19839 }
840
841 // <profile>/Extensions/<id>
[email protected]b0beaa662009-02-26 00:04:15842 FilePath dest_dir = install_directory_.AppendASCII(extension.id());
[email protected]cc655912009-01-29 23:19:19843 std::string version = extension.VersionString();
[email protected]b0beaa662009-02-26 00:04:15844 std::string current_version;
[email protected]1fca1492009-05-15 22:23:43845 bool was_update = false;
[email protected]b0beaa662009-02-26 00:04:15846 if (ReadCurrentVersion(dest_dir, &current_version)) {
847 if (!CheckCurrentVersion(version, current_version, dest_dir))
[email protected]1fca1492009-05-15 22:23:43848 return;
849 was_update = true;
[email protected]b0beaa662009-02-26 00:04:15850 }
[email protected]cc655912009-01-29 23:19:19851
852 // <profile>/Extensions/<dir_name>/<version>
[email protected]1fca1492009-05-15 22:23:43853 FilePath version_dir = dest_dir.AppendASCII(version);
854 if (!InstallDirSafely(temp_extension_dir, version_dir))
855 return;
[email protected]cc655912009-01-29 23:19:19856
[email protected]b0beaa662009-02-26 00:04:15857 if (!SetCurrentVersion(dest_dir, version)) {
[email protected]1fca1492009-05-15 22:23:43858 if (!file_util::Delete(version_dir, true))
[email protected]cc655912009-01-29 23:19:19859 LOG(WARNING) << "Can't remove " << dest_dir.value();
[email protected]1fca1492009-05-15 22:23:43860 return;
[email protected]cc655912009-01-29 23:19:19861 }
862
[email protected]1fca1492009-05-15 22:23:43863 // To mark that this extension was installed from an external source, create a
864 // zero-length file. At load time, this is used to indicate that the
865 // extension should be uninstalled.
866 // TODO(erikkay): move this into per-extension config storage when it appears.
867 if (from_external) {
868 FilePath marker = version_dir.AppendASCII(kExternalInstallFile);
869 file_util::WriteFile(marker, NULL, 0);
870 }
871
[email protected]894bb502009-05-21 22:39:57872 // Load the extension immediately and then report installation success. We
873 // don't load extensions for external installs because external installation
874 // occurs before the normal startup so we just let startup pick them up. We
875 // don't notify installation because there is no UI or external install so
876 // there is nobody to notify.
877 if (!from_external) {
878 Extension* extension = LoadExtension(version_dir, true); // require id
879 CHECK(extension);
880
881 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
882 frontend_, &ExtensionsService::OnExtensionInstalled, extension,
883 was_update));
884
885 // Only one extension, but ReportExtensionsLoaded can handle multiple,
886 // so we need to construct a list.
887 scoped_ptr<ExtensionList> extensions(new ExtensionList);
888 extensions->push_back(extension);
889 LOG(INFO) << "Done.";
890 // Hand off ownership of the loaded extensions to the frontend.
891 ReportExtensionsLoaded(extensions.release());
892 }
[email protected]cc655912009-01-29 23:19:19893}
894
895void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:51896 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15897
[email protected]cc655912009-01-29 23:19:19898 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51899 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:19900 std::string message =
901 StringPrintf("Could not install extension from '%s'. %s",
902 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18903 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]cc655912009-01-29 23:19:19904}
905
[email protected]4a190632009-05-09 01:07:42906void ExtensionsServiceBackend::ReportExtensionVersionReinstalled(
907 const std::string& id) {
[email protected]894bb502009-05-21 22:39:57908 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
909 frontend_, &ExtensionsService::OnExtensionVersionReinstalled, id));
[email protected]b0beaa662009-02-26 00:04:15910}
911
912// Some extensions will autoupdate themselves externally from Chrome. These
913// are typically part of some larger client application package. To support
914// these, the extension will register its location in the registry on Windows
915// (TODO(port): what about on other platforms?) and this code will periodically
916// check that location for a .crx file, which it will then install locally if
917// a new version is available.
918void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57919 std::set<std::string> ids_to_ignore,
920 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15921
922 // Note that this installation is intentionally silent (since it didn't
923 // go through the front-end). Extensions that are registered in this
924 // way are effectively considered 'pre-bundled', and so implicitly
925 // trusted. In general, if something has HKLM or filesystem access,
926 // they could install an extension manually themselves anyway.
927 alert_on_error_ = false;
928 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15929
930#if defined(OS_WIN)
[email protected]631cf822009-05-15 07:01:25931 // TODO(port): Pull this out into an interface. That will also allow us to
932 // test the behavior of external extensions.
[email protected]b0beaa662009-02-26 00:04:15933 HKEY reg_root = HKEY_LOCAL_MACHINE;
[email protected]894bb502009-05-21 22:39:57934 RegistryKeyIterator iterator(reg_root, ASCIIToWide(registry_path_).c_str());
[email protected]b0beaa662009-02-26 00:04:15935 while (iterator.Valid()) {
[email protected]894bb502009-05-21 22:39:57936 // Fold
937 std::string id = StringToLowerASCII(WideToASCII(iterator.Name()));
938 if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
939 LOG(INFO) << "Skipping uninstalled external extension " << id;
940 ++iterator;
941 continue;
942 }
943
[email protected]b0beaa662009-02-26 00:04:15944 RegKey key;
[email protected]894bb502009-05-21 22:39:57945 std::wstring key_path = ASCIIToWide(registry_path_);
[email protected]b0beaa662009-02-26 00:04:15946 key_path.append(L"\\");
947 key_path.append(iterator.Name());
948 if (key.Open(reg_root, key_path.c_str())) {
949 std::wstring extension_path;
950 if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
[email protected]b0beaa662009-02-26 00:04:15951 std::wstring extension_version;
952 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
[email protected]cc5da332009-03-04 08:02:51953 if (ShouldInstall(id, WideToASCII(extension_version))) {
[email protected]1fca1492009-05-15 22:23:43954 bool from_external = true;
955 InstallOrUpdateExtension(FilePath(extension_path), id,
956 from_external);
[email protected]cc5da332009-03-04 08:02:51957 }
[email protected]b0beaa662009-02-26 00:04:15958 } else {
[email protected]b0beaa662009-02-26 00:04:15959 // TODO(erikkay): find a way to get this into about:extensions
960 LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
961 " for key " << key_path;
962 }
963 } else {
[email protected]b0beaa662009-02-26 00:04:15964 // TODO(erikkay): find a way to get this into about:extensions
965 LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
966 " for key " << key_path;
967 }
968 }
969 ++iterator;
970 }
971#else
972 NOTREACHED();
973#endif
974}
975
[email protected]894bb502009-05-21 22:39:57976bool ExtensionsServiceBackend::CheckExternalUninstall(const FilePath& version_path,
[email protected]b0beaa662009-02-26 00:04:15977 const std::string& id) {
[email protected]894bb502009-05-21 22:39:57978 FilePath external_file = version_path.AppendASCII(kExternalInstallFile);
[email protected]b0beaa662009-02-26 00:04:15979 if (file_util::PathExists(external_file)) {
980#if defined(OS_WIN)
981 HKEY reg_root = HKEY_LOCAL_MACHINE;
982 RegKey key;
[email protected]894bb502009-05-21 22:39:57983 std::wstring key_path = ASCIIToWide(registry_path_);
[email protected]b0beaa662009-02-26 00:04:15984 key_path.append(L"\\");
985 key_path.append(ASCIIToWide(id));
986
987 // If the key doesn't exist, then we should uninstall.
988 return !key.Open(reg_root, key_path.c_str());
989#else
[email protected]894bb502009-05-21 22:39:57990 // TODO(port)
[email protected]b0beaa662009-02-26 00:04:15991#endif
992 }
993 return false;
994}
995
996// Assumes that the extension isn't currently loaded or in use.
[email protected]631cf822009-05-15 07:01:25997void ExtensionsServiceBackend::UninstallExtension(
998 const std::string& extension_id) {
999 // First, delete the Current Version file. If the directory delete fails, then
1000 // at least the extension won't be loaded again.
1001 FilePath extension_directory = install_directory_.AppendASCII(extension_id);
1002
1003 if (!file_util::PathExists(extension_directory)) {
1004 LOG(WARNING) << "Asked to remove a non-existent extension " << extension_id;
[email protected]b0beaa662009-02-26 00:04:151005 return;
1006 }
[email protected]631cf822009-05-15 07:01:251007
1008 FilePath current_version_file = extension_directory.AppendASCII(
1009 ExtensionsService::kCurrentVersionFileName);
1010 if (!file_util::PathExists(current_version_file)) {
1011 LOG(WARNING) << "Extension " << extension_id
1012 << " does not have a Current Version file.";
1013 } else {
1014 if (!file_util::Delete(current_version_file, false)) {
1015 LOG(WARNING) << "Could not delete Current Version file for extension "
1016 << extension_id;
1017 return;
1018 }
1019 }
1020
[email protected]894bb502009-05-21 22:39:571021 // Ok, now try and delete the entire rest of the directory. One major place
1022 // this can fail is if the extension contains a plugin (stupid plugins). It's
1023 // not a big deal though, because we'll notice next time we startup that the
1024 // Current Version file is gone and finish the delete then.
[email protected]631cf822009-05-15 07:01:251025 if (!file_util::Delete(extension_directory, true)) {
1026 LOG(WARNING) << "Could not delete directory for extension "
1027 << extension_id;
[email protected]b0beaa662009-02-26 00:04:151028 }
1029}
1030
1031bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
1032 const std::string& version) {
1033 FilePath dir(install_directory_.AppendASCII(id.c_str()));
1034 std::string current_version;
1035 if (ReadCurrentVersion(dir, &current_version)) {
[email protected]894bb502009-05-21 22:39:571036 return CheckCurrentVersion(version, current_version, dir);
[email protected]b0beaa662009-02-26 00:04:151037 }
1038 return true;
[email protected]cc655912009-01-29 23:19:191039}