blob: b645dfe0cdcb8c6fee78f2551a113746b3e29f12 [file] [log] [blame]
[email protected]6014d672008-12-05 00:38:251// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/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]6014d672008-12-05 00:38:2516#include "chrome/browser/browser_process.h"
[email protected]69f1be82009-04-16 22:27:2117#include "chrome/browser/browsing_instance.h"
18#include "chrome/browser/extensions/extension.h"
[email protected]b68d5ed2009-04-16 02:41:2819#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]bb28e062009-02-27 17:19:1820#include "chrome/browser/extensions/extension_error_reporter.h"
[email protected]bdbc87c2009-01-25 05:08:5421#include "chrome/browser/extensions/user_script_master.h"
[email protected]81e63782009-02-27 19:35:0922#include "chrome/browser/extensions/extension_view.h"
[email protected]367230c52009-02-21 01:44:3023#include "chrome/browser/plugin_service.h"
[email protected]81e63782009-02-27 19:35:0924#include "chrome/browser/profile.h"
[email protected]69f1be82009-04-16 22:27:2125#include "chrome/browser/tab_contents/site_instance.h"
[email protected]6014d672008-12-05 00:38:2526#include "chrome/common/json_value_serializer.h"
[email protected]82891262008-12-24 00:21:2627#include "chrome/common/notification_service.h"
[email protected]cc655912009-01-29 23:19:1928#include "chrome/common/unzip.h"
[email protected]b0beaa662009-02-26 00:04:1529
[email protected]79db6232009-02-13 20:51:2030#if defined(OS_WIN)
[email protected]b0beaa662009-02-26 00:04:1531#include "base/registry.h"
[email protected]79db6232009-02-13 20:51:2032#endif
[email protected]6014d672008-12-05 00:38:2533
34// ExtensionsService
35
[email protected]cc655912009-01-29 23:19:1936const char* ExtensionsService::kInstallDirectoryName = "Extensions";
37const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
38const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
[email protected]b0beaa662009-02-26 00:04:1539
40namespace {
[email protected]cc655912009-01-29 23:19:1941// Chromium Extension magic number
[email protected]b0beaa662009-02-26 00:04:1542const char kExtensionFileMagic[] = "Cr24";
[email protected]cc655912009-01-29 23:19:1943
44struct ExtensionHeader {
45 char magic[sizeof(kExtensionFileMagic) - 1];
46 uint32 version;
47 size_t header_size;
48 size_t manifest_size;
49};
50
51const size_t kZipHashBytes = 32; // SHA-256
52const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
[email protected]6014d672008-12-05 00:38:2553
[email protected]b0beaa662009-02-26 00:04:1554#if defined(OS_WIN)
55
56// Registry key where registry defined extension installers live.
57const wchar_t kRegistryExtensions[] = L"Software\\Google\\Chrome\\Extensions";
58
59// Registry value of of that key that defines the path to the .crx file.
60const wchar_t kRegistryExtensionPath[] = L"path";
61
62// Registry value of that key that defines the current version of the .crx file.
63const wchar_t kRegistryExtensionVersion[] = L"version";
64
65#endif
66
67// A marker file to indicate that an extension was installed from an external
68// source.
69const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
[email protected]0b344962009-03-31 04:21:4570
71// The version of the extension package that this code understands.
72const uint32 kExpectedVersion = 1;
[email protected]b0beaa662009-02-26 00:04:1573}
74
[email protected]81e63782009-02-27 19:35:0975ExtensionsService::ExtensionsService(Profile* profile,
[email protected]bdbc87c2009-01-25 05:08:5476 UserScriptMaster* user_script_master)
[email protected]6014d672008-12-05 00:38:2577 : message_loop_(MessageLoop::current()),
[email protected]81e63782009-02-27 19:35:0978 install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
[email protected]cc5da332009-03-04 08:02:5179 backend_(new ExtensionsServiceBackend(install_directory_)),
[email protected]69f1be82009-04-16 22:27:2180 user_script_master_(user_script_master),
81 browsing_instance_(new BrowsingInstance(profile)) {
[email protected]6014d672008-12-05 00:38:2582}
83
84ExtensionsService::~ExtensionsService() {
85 for (ExtensionList::iterator iter = extensions_.begin();
86 iter != extensions_.end(); ++iter) {
87 delete *iter;
88 }
89}
90
91bool ExtensionsService::Init() {
[email protected]b68d5ed2009-04-16 02:41:2892 // Start up the extension event routers.
93 ExtensionBrowserEventRouter::GetInstance()->Init();
94
[email protected]b0beaa662009-02-26 00:04:1595#if defined(OS_WIN)
[email protected]b0beaa662009-02-26 00:04:1596 // TODO(port): ExtensionsServiceBackend::CheckForExternalUpdates depends on
97 // the Windows registry.
98 // TODO(erikkay): Should we monitor the registry during run as well?
99 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
100 NewRunnableMethod(backend_.get(),
101 &ExtensionsServiceBackend::CheckForExternalUpdates,
[email protected]b0beaa662009-02-26 00:04:15102 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
103#endif
104
[email protected]6014d672008-12-05 00:38:25105 // TODO(aa): This message loop should probably come from a backend
106 // interface, similar to how the message loop for the frontend comes
107 // from the frontend interface.
108 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
109 NewRunnableMethod(backend_.get(),
[email protected]cc5da332009-03-04 08:02:51110 &ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory,
[email protected]6014d672008-12-05 00:38:25111 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
[email protected]6014d672008-12-05 00:38:25112
113 return true;
114}
115
[email protected]6014d672008-12-05 00:38:25116MessageLoop* ExtensionsService::GetMessageLoop() {
117 return message_loop_;
118}
119
[email protected]3cf4f0992009-02-03 23:00:30120void ExtensionsService::InstallExtension(const FilePath& extension_path) {
121 // TODO(aa): This message loop should probably come from a backend
122 // interface, similar to how the message loop for the frontend comes
123 // from the frontend interface.
124 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
125 NewRunnableMethod(backend_.get(),
126 &ExtensionsServiceBackend::InstallExtension,
127 extension_path,
[email protected]3cf4f0992009-02-03 23:00:30128 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
129}
130
131void ExtensionsService::LoadExtension(const FilePath& extension_path) {
132 // TODO(aa): This message loop should probably come from a backend
133 // interface, similar to how the message loop for the frontend comes
134 // from the frontend interface.
135 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
136 NewRunnableMethod(backend_.get(),
137 &ExtensionsServiceBackend::LoadSingleExtension,
138 extension_path,
139 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
140}
141
[email protected]4a8d3272009-03-10 19:15:08142void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]08816d0d2008-12-08 18:43:53143 extensions_.insert(extensions_.end(), new_extensions->begin(),
144 new_extensions->end());
[email protected]6014d672008-12-05 00:38:25145
[email protected]367230c52009-02-21 01:44:30146 // TODO: Fix race here. A page could need a user script on startup, before
147 // the user script is loaded. We need to freeze the renderer in that case.
148 // TODO(mpcomplete): We also need to force a renderer to refresh its cache of
149 // the plugin list when we inject user scripts, since it could have a stale
150 // version by the time extensions are loaded.
151
[email protected]bdbc87c2009-01-25 05:08:54152 for (ExtensionList::iterator extension = extensions_.begin();
153 extension != extensions_.end(); ++extension) {
[email protected]367230c52009-02-21 01:44:30154 // Tell NPAPI about any plugins in the loaded extensions.
155 if (!(*extension)->plugins_dir().empty()) {
156 PluginService::GetInstance()->AddExtraPluginDir(
157 (*extension)->plugins_dir());
158 }
159
160 // Tell UserScriptMaster about any scripts in the loaded extensions.
[email protected]34aa8dc2009-02-19 07:03:05161 const UserScriptList& scripts = (*extension)->content_scripts();
[email protected]bdbc87c2009-01-25 05:08:54162 for (UserScriptList::const_iterator script = scripts.begin();
163 script != scripts.end(); ++script) {
164 user_script_master_->AddLoneScript(*script);
165 }
166 }
167
[email protected]cc5da332009-03-04 08:02:51168 // Since user scripts may have changed, tell UserScriptMaster to kick off
169 // a scan.
[email protected]bdbc87c2009-01-25 05:08:54170 user_script_master_->StartScan();
171
[email protected]bfd04a62009-02-01 18:16:56172 NotificationService::current()->Notify(
173 NotificationType::EXTENSIONS_LOADED,
[email protected]82891262008-12-24 00:21:26174 NotificationService::AllSources(),
175 Details<ExtensionList>(new_extensions));
176
177 delete new_extensions;
[email protected]6014d672008-12-05 00:38:25178}
179
[email protected]b0beaa662009-02-26 00:04:15180void ExtensionsService::OnExtensionInstalled(FilePath path, bool update) {
[email protected]bfd04a62009-02-01 18:16:56181 NotificationService::current()->Notify(
182 NotificationType::EXTENSION_INSTALLED,
[email protected]cc655912009-01-29 23:19:19183 NotificationService::AllSources(),
184 Details<FilePath>(&path));
185
[email protected]b0beaa662009-02-26 00:04:15186 // TODO(erikkay): Update UI if appropriate.
[email protected]cc655912009-01-29 23:19:19187}
188
[email protected]69f1be82009-04-16 22:27:21189ExtensionView* ExtensionsService::CreateView(Extension* extension,
190 const GURL& url,
191 Browser* browser) {
192 return new ExtensionView(extension, url, GetSiteInstanceForURL(url), browser);
193}
194
195SiteInstance* ExtensionsService::GetSiteInstanceForURL(const GURL& url) {
196 return browsing_instance_->GetSiteInstanceForURL(url);
197}
198
[email protected]6014d672008-12-05 00:38:25199
200// ExtensionsServicesBackend
201
[email protected]cc5da332009-03-04 08:02:51202void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
[email protected]6014d672008-12-05 00:38:25203 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
[email protected]b0beaa662009-02-26 00:04:15204 frontend_ = frontend;
205 alert_on_error_ = false;
[email protected]05eb0fa2009-02-21 00:05:48206
[email protected]540f91b2009-03-26 19:37:43207#if defined(OS_WIN)
208 // On POSIX, AbsolutePath() calls realpath() which returns NULL if
209 // it does not exist. Instead we absolute-ify after creation in
210 // case that is needed.
211 // TODO(port): does this really need to happen before
212 // CreateDirectory() on Windows?
[email protected]cc5da332009-03-04 08:02:51213 if (!file_util::AbsolutePath(&install_directory_))
[email protected]eab9b452009-01-23 20:48:59214 NOTREACHED();
[email protected]540f91b2009-03-26 19:37:43215#endif
[email protected]eab9b452009-01-23 20:48:59216
[email protected]b0beaa662009-02-26 00:04:15217 scoped_ptr<ExtensionList> extensions(new ExtensionList);
218
219 // Create the <Profile>/Extensions directory if it doesn't exist.
[email protected]cc5da332009-03-04 08:02:51220 if (!file_util::DirectoryExists(install_directory_)) {
221 file_util::CreateDirectory(install_directory_);
[email protected]b0beaa662009-02-26 00:04:15222 LOG(INFO) << "Created Extensions directory. No extensions to install.";
223 ReportExtensionsLoaded(extensions.release());
224 return;
225 }
226
[email protected]540f91b2009-03-26 19:37:43227#if !defined(OS_WIN)
228 if (!file_util::AbsolutePath(&install_directory_))
229 NOTREACHED();
230#endif
231
[email protected]bf24d2c2009-02-24 23:07:45232 LOG(INFO) << "Loading installed extensions...";
233
[email protected]6014d672008-12-05 00:38:25234 // Find all child directories in the install directory and load their
235 // manifests. Post errors and results to the frontend.
[email protected]cc5da332009-03-04 08:02:51236 file_util::FileEnumerator enumerator(install_directory_,
[email protected]cc655912009-01-29 23:19:19237 false, // not recursive
[email protected]6014d672008-12-05 00:38:25238 file_util::FileEnumerator::DIRECTORIES);
[email protected]cc5da332009-03-04 08:02:51239 FilePath extension_path;
240 for (extension_path = enumerator.Next(); !extension_path.value().empty();
241 extension_path = enumerator.Next()) {
242 std::string extension_id = WideToASCII(
243 extension_path.BaseName().ToWStringHack());
244 if (CheckExternalUninstall(extension_path, extension_id)) {
245 // TODO(erikkay): Possibly defer this operation to avoid slowing initial
246 // load of extensions.
247 UninstallExtension(extension_path);
248
249 // No error needs to be reported. The extension effectively doesn't
250 // exist.
251 continue;
252 }
253
254 Extension* extension = LoadExtensionCurrentVersion(extension_path);
[email protected]0877fd92009-02-03 16:34:06255 if (extension)
256 extensions->push_back(extension);
[email protected]6014d672008-12-05 00:38:25257 }
258
[email protected]bf24d2c2009-02-24 23:07:45259 LOG(INFO) << "Done.";
[email protected]b0beaa662009-02-26 00:04:15260 ReportExtensionsLoaded(extensions.release());
[email protected]6014d672008-12-05 00:38:25261}
262
[email protected]b0beaa662009-02-26 00:04:15263void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]0877fd92009-02-03 16:34:06264 const FilePath& path_in,
265 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
[email protected]b0beaa662009-02-26 00:04:15266 frontend_ = frontend;
267
268 // Explicit UI loads are always noisy.
269 alert_on_error_ = true;
270
[email protected]cc5da332009-03-04 08:02:51271 FilePath extension_path = path_in;
272 if (!file_util::AbsolutePath(&extension_path))
[email protected]0877fd92009-02-03 16:34:06273 NOTREACHED();
[email protected]bf24d2c2009-02-24 23:07:45274
275 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51276 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45277
[email protected]5bfb1eb0a2009-04-08 18:33:30278 Extension* extension = LoadExtension(extension_path,
279 false); // don't require ID
[email protected]0877fd92009-02-03 16:34:06280 if (extension) {
281 ExtensionList* extensions = new ExtensionList;
282 extensions->push_back(extension);
[email protected]b0beaa662009-02-26 00:04:15283 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06284 }
[email protected]0877fd92009-02-03 16:34:06285}
286
[email protected]cc5da332009-03-04 08:02:51287Extension* ExtensionsServiceBackend::LoadExtensionCurrentVersion(
288 const FilePath& extension_path) {
[email protected]b0beaa662009-02-26 00:04:15289 std::string version_str;
[email protected]cc5da332009-03-04 08:02:51290 if (!ReadCurrentVersion(extension_path, &version_str)) {
291 ReportExtensionLoadError(extension_path,
292 StringPrintf("Could not read '%s' file.",
293 ExtensionsService::kCurrentVersionFileName));
[email protected]b0beaa662009-02-26 00:04:15294 return NULL;
295 }
296
297 LOG(INFO) << " " <<
[email protected]cc5da332009-03-04 08:02:51298 WideToASCII(extension_path.BaseName().ToWStringHack()) <<
[email protected]b0beaa662009-02-26 00:04:15299 " version: " << version_str;
300
[email protected]5bfb1eb0a2009-04-08 18:33:30301 return LoadExtension(extension_path.AppendASCII(version_str),
302 true); // require id
[email protected]b0beaa662009-02-26 00:04:15303}
304
[email protected]cc5da332009-03-04 08:02:51305Extension* ExtensionsServiceBackend::LoadExtension(
[email protected]5bfb1eb0a2009-04-08 18:33:30306 const FilePath& extension_path, bool require_id) {
[email protected]0877fd92009-02-03 16:34:06307 FilePath manifest_path =
[email protected]cc5da332009-03-04 08:02:51308 extension_path.AppendASCII(Extension::kManifestFilename);
[email protected]0877fd92009-02-03 16:34:06309 if (!file_util::PathExists(manifest_path)) {
[email protected]cc5da332009-03-04 08:02:51310 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06311 return NULL;
312 }
313
[email protected]1635bc12009-04-03 17:18:27314 JSONFileValueSerializer serializer(manifest_path);
[email protected]0877fd92009-02-03 16:34:06315 std::string error;
316 scoped_ptr<Value> root(serializer.Deserialize(&error));
317 if (!root.get()) {
[email protected]cc5da332009-03-04 08:02:51318 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06319 return NULL;
320 }
321
322 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]cc5da332009-03-04 08:02:51323 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06324 return NULL;
325 }
326
[email protected]cc5da332009-03-04 08:02:51327 scoped_ptr<Extension> extension(new Extension(extension_path));
[email protected]0877fd92009-02-03 16:34:06328 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
[email protected]5bfb1eb0a2009-04-08 18:33:30329 require_id, &error)) {
[email protected]cc5da332009-03-04 08:02:51330 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06331 return NULL;
332 }
[email protected]8d6d9ff2009-02-20 08:14:39333
334 // Validate that claimed resources actually exist.
[email protected]3cfbd0e2009-03-18 21:26:24335 for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
336 const UserScript& script = extension->content_scripts()[i];
337
338 for (size_t j = 0; j < script.js_scripts().size(); j++) {
339 const FilePath& path = script.js_scripts()[j].path();
340 if (!file_util::PathExists(path)) {
341 ReportExtensionLoadError(extension_path,
342 StringPrintf("Could not load '%s' for content script.",
343 WideToUTF8(path.ToWStringHack()).c_str()));
344 return NULL;
345 }
346 }
347
348 for (size_t j = 0; j < script.css_scripts().size(); j++) {
349 const FilePath& path = script.css_scripts()[j].path();
350 if (!file_util::PathExists(path)) {
351 ReportExtensionLoadError(extension_path,
352 StringPrintf("Could not load '%s' for content script.",
353 WideToUTF8(path.ToWStringHack()).c_str()));
354 return NULL;
355 }
[email protected]8d6d9ff2009-02-20 08:14:39356 }
357 }
358
[email protected]0877fd92009-02-03 16:34:06359 return extension.release();
360}
361
[email protected]6014d672008-12-05 00:38:25362void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51363 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15364 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51365 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00366 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19367 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18368 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25369}
370
371void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15372 ExtensionList* extensions) {
373 frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
374 frontend_,
[email protected]cc5da332009-03-04 08:02:51375 &ExtensionsServiceFrontendInterface::OnExtensionsLoaded,
[email protected]6014d672008-12-05 00:38:25376 extensions));
377}
[email protected]cc655912009-01-29 23:19:19378
379// The extension file format is a header, followed by the manifest, followed
380// by the zip file. The header is a magic number, a version, the size of the
381// header, and the size of the manifest. These ints are 4 byte little endian.
[email protected]cc5da332009-03-04 08:02:51382DictionaryValue* ExtensionsServiceBackend::ReadManifest(
383 const FilePath& extension_path) {
384 ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
[email protected]cc655912009-01-29 23:19:19385 if (!file.get()) {
[email protected]cc5da332009-03-04 08:02:51386 ReportExtensionInstallError(extension_path, "no such extension file");
[email protected]cc655912009-01-29 23:19:19387 return NULL;
388 }
389
390 // Read and verify the header.
391 ExtensionHeader header;
392 size_t len;
[email protected]b0beaa662009-02-26 00:04:15393
[email protected]cc655912009-01-29 23:19:19394 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
395 // appears that we don't have any endian/alignment aware serialization
396 // code in the code base. So for now, this assumes that we're running
397 // on a little endian machine with 4 byte alignment.
398 len = fread(&header, 1, sizeof(ExtensionHeader), file.get());
399 if (len < sizeof(ExtensionHeader)) {
[email protected]cc5da332009-03-04 08:02:51400 ReportExtensionInstallError(extension_path, "invalid extension header");
[email protected]cc655912009-01-29 23:19:19401 return NULL;
402 }
403 if (strncmp(kExtensionFileMagic, header.magic, sizeof(header.magic))) {
[email protected]cc5da332009-03-04 08:02:51404 ReportExtensionInstallError(extension_path, "bad magic number");
[email protected]cc655912009-01-29 23:19:19405 return NULL;
406 }
[email protected]0b344962009-03-31 04:21:45407 if (header.version != kExpectedVersion) {
[email protected]cc5da332009-03-04 08:02:51408 ReportExtensionInstallError(extension_path, "bad version number");
[email protected]cc655912009-01-29 23:19:19409 return NULL;
410 }
411 if (header.header_size > sizeof(ExtensionHeader))
412 fseek(file.get(), header.header_size - sizeof(ExtensionHeader), SEEK_CUR);
[email protected]f0a51fb52009-03-05 12:46:38413
[email protected]cc655912009-01-29 23:19:19414 char buf[1 << 16];
415 std::string manifest_str;
416 size_t read_size = std::min(sizeof(buf), header.manifest_size);
417 size_t remainder = header.manifest_size;
418 while ((len = fread(buf, 1, read_size, file.get())) > 0) {
419 manifest_str.append(buf, len);
420 if (len <= remainder)
421 break;
422 remainder -= len;
423 read_size = std::min(sizeof(buf), remainder);
424 }
425
426 // Verify the JSON
427 JSONStringValueSerializer json(manifest_str);
428 std::string error;
[email protected]4c7ca4b2009-02-04 00:53:08429 scoped_ptr<Value> val(json.Deserialize(&error));
430 if (!val.get()) {
[email protected]cc5da332009-03-04 08:02:51431 ReportExtensionInstallError(extension_path, error);
[email protected]cc655912009-01-29 23:19:19432 return NULL;
433 }
434 if (!val->IsType(Value::TYPE_DICTIONARY)) {
[email protected]cc5da332009-03-04 08:02:51435 ReportExtensionInstallError(extension_path,
436 "manifest isn't a JSON dictionary");
[email protected]cc655912009-01-29 23:19:19437 return NULL;
438 }
[email protected]4c7ca4b2009-02-04 00:53:08439 DictionaryValue* manifest = static_cast<DictionaryValue*>(val.get());
[email protected]b0beaa662009-02-26 00:04:15440
441 // Check the version before proceeding. Although we verify the version
442 // again later, checking it here allows us to skip some potentially expensive
443 // work.
444 std::string id;
[email protected]8e50b602009-03-03 22:59:43445 if (!manifest->GetString(Extension::kIdKey, &id)) {
[email protected]cc5da332009-03-04 08:02:51446 ReportExtensionInstallError(extension_path, "missing id key");
[email protected]b0beaa662009-02-26 00:04:15447 return NULL;
448 }
449 FilePath dest_dir = install_directory_.AppendASCII(id.c_str());
450 if (file_util::PathExists(dest_dir)) {
451 std::string version;
[email protected]8e50b602009-03-03 22:59:43452 if (!manifest->GetString(Extension::kVersionKey, &version)) {
[email protected]cc5da332009-03-04 08:02:51453 ReportExtensionInstallError(extension_path, "missing version key");
[email protected]b0beaa662009-02-26 00:04:15454 return NULL;
455 }
456 std::string current_version;
457 if (ReadCurrentVersion(dest_dir, &current_version)) {
458 if (!CheckCurrentVersion(version, current_version, dest_dir))
459 return NULL;
460 }
461 }
462
[email protected]cc655912009-01-29 23:19:19463 std::string zip_hash;
[email protected]8e50b602009-03-03 22:59:43464 if (!manifest->GetString(Extension::kZipHashKey, &zip_hash)) {
[email protected]cc5da332009-03-04 08:02:51465 ReportExtensionInstallError(extension_path, "missing zip_hash key");
[email protected]cc655912009-01-29 23:19:19466 return NULL;
467 }
468 if (zip_hash.size() != kZipHashHexBytes) {
[email protected]cc5da332009-03-04 08:02:51469 ReportExtensionInstallError(extension_path, "invalid zip_hash key");
[email protected]cc655912009-01-29 23:19:19470 return NULL;
471 }
472
473 // Read the rest of the zip file and compute a hash to compare against
474 // what the manifest claims. Compute the hash incrementally since the
475 // zip file could be large.
476 const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
477 SHA256Context ctx;
478 SHA256_Begin(&ctx);
479 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
480 SHA256_Update(&ctx, ubuf, len);
481 uint8 hash[32];
482 SHA256_End(&ctx, hash, NULL, sizeof(hash));
483
484 std::vector<uint8> zip_hash_bytes;
485 if (!HexStringToBytes(zip_hash, &zip_hash_bytes)) {
[email protected]cc5da332009-03-04 08:02:51486 ReportExtensionInstallError(extension_path, "invalid zip_hash key");
[email protected]cc655912009-01-29 23:19:19487 return NULL;
488 }
489 if (zip_hash_bytes.size() != kZipHashBytes) {
[email protected]cc5da332009-03-04 08:02:51490 ReportExtensionInstallError(extension_path, "invalid zip_hash key");
[email protected]cc655912009-01-29 23:19:19491 return NULL;
492 }
493 for (size_t i = 0; i < kZipHashBytes; ++i) {
494 if (zip_hash_bytes[i] != hash[i]) {
[email protected]cc5da332009-03-04 08:02:51495 ReportExtensionInstallError(extension_path,
496 "zip_hash key didn't match zip hash");
[email protected]cc655912009-01-29 23:19:19497 return NULL;
498 }
499 }
500
501 // TODO(erikkay): The manifest will also contain a signature of the hash
502 // (or perhaps the whole manifest) for authentication purposes.
503
[email protected]4c7ca4b2009-02-04 00:53:08504 // The caller owns val (now cast to manifest).
505 val.release();
[email protected]cc655912009-01-29 23:19:19506 return manifest;
507}
508
[email protected]b0beaa662009-02-26 00:04:15509bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
510 std::string* version_string) {
[email protected]18a12352009-01-31 01:33:28511 FilePath current_version =
[email protected]b0beaa662009-02-26 00:04:15512 dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
[email protected]18a12352009-01-31 01:33:28513 if (file_util::PathExists(current_version)) {
514 if (file_util::ReadFileToString(current_version, version_string)) {
515 TrimWhitespace(*version_string, TRIM_ALL, version_string);
516 return true;
517 }
518 }
519 return false;
520}
521
[email protected]cc655912009-01-29 23:19:19522bool ExtensionsServiceBackend::CheckCurrentVersion(
[email protected]b0beaa662009-02-26 00:04:15523 const std::string& new_version_str,
524 const std::string& current_version_str,
525 const FilePath& dest_dir) {
526 scoped_ptr<Version> current_version(
527 Version::GetVersionFromString(current_version_str));
528 scoped_ptr<Version> new_version(
529 Version::GetVersionFromString(new_version_str));
530 if (current_version->CompareTo(*new_version) >= 0) {
[email protected]b0beaa662009-02-26 00:04:15531 // Verify that the directory actually exists. If it doesn't we'll return
532 // true so that the install code will repair the broken installation.
533 // TODO(erikkay): A further step would be to verify that the extension
534 // has actually loaded successfully.
535 FilePath version_dir = dest_dir.AppendASCII(current_version_str);
536 if (file_util::PathExists(version_dir)) {
[email protected]f0a51fb52009-03-05 12:46:38537 ReportExtensionInstallError(dest_dir,
[email protected]b0beaa662009-02-26 00:04:15538 "Existing version is already up to date.");
539 return false;
[email protected]cc655912009-01-29 23:19:19540 }
541 }
542 return true;
543}
544
[email protected]f0a51fb52009-03-05 12:46:38545bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
[email protected]b0beaa662009-02-26 00:04:15546 const FilePath& dest_dir) {
[email protected]cc655912009-01-29 23:19:19547
548 if (file_util::PathExists(dest_dir)) {
[email protected]cc655912009-01-29 23:19:19549 // By the time we get here, it should be safe to assume that this directory
550 // is not currently in use (it's not the current active version).
551 if (!file_util::Delete(dest_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51552 ReportExtensionInstallError(source_dir,
[email protected]cc655912009-01-29 23:19:19553 "Can't delete existing version directory.");
554 return false;
555 }
556 } else {
557 FilePath parent = dest_dir.DirName();
558 if (!file_util::DirectoryExists(parent)) {
559 if (!file_util::CreateDirectory(parent)) {
[email protected]cc5da332009-03-04 08:02:51560 ReportExtensionInstallError(source_dir,
561 "Couldn't create extension directory.");
[email protected]cc655912009-01-29 23:19:19562 return false;
563 }
564 }
565 }
566 if (!file_util::Move(source_dir, dest_dir)) {
[email protected]cc5da332009-03-04 08:02:51567 ReportExtensionInstallError(source_dir,
568 "Couldn't move temporary directory.");
[email protected]cc655912009-01-29 23:19:19569 return false;
570 }
571
572 return true;
573}
574
[email protected]b0beaa662009-02-26 00:04:15575bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
576 std::string version) {
[email protected]cc655912009-01-29 23:19:19577 // Write out the new CurrentVersion file.
578 // <profile>/Extension/<name>/CurrentVersion
579 FilePath current_version =
580 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
581 FilePath current_version_old =
582 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
583 if (file_util::PathExists(current_version_old)) {
584 if (!file_util::Delete(current_version_old, false)) {
[email protected]cc5da332009-03-04 08:02:51585 ReportExtensionInstallError(dest_dir,
586 "Couldn't remove CurrentVersion_old file.");
[email protected]cc655912009-01-29 23:19:19587 return false;
588 }
589 }
590 if (file_util::PathExists(current_version)) {
591 if (!file_util::Move(current_version, current_version_old)) {
[email protected]cc5da332009-03-04 08:02:51592 ReportExtensionInstallError(dest_dir,
593 "Couldn't move CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19594 return false;
595 }
596 }
597 net::FileStream stream;
598 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
599 if (stream.Open(current_version, flags) != 0)
600 return false;
601 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
[email protected]b0beaa662009-02-26 00:04:15602
[email protected]cc655912009-01-29 23:19:19603 // Restore the old CurrentVersion.
604 if (file_util::PathExists(current_version_old)) {
605 if (!file_util::Move(current_version_old, current_version)) {
[email protected]f0a51fb52009-03-05 12:46:38606 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
[email protected]cc655912009-01-29 23:19:19607 " to " << current_version.value();
[email protected]b0beaa662009-02-26 00:04:15608
[email protected]cc655912009-01-29 23:19:19609 // TODO(erikkay): This is an ugly state to be in. Try harder?
610 }
611 }
[email protected]f0a51fb52009-03-05 12:46:38612 ReportExtensionInstallError(dest_dir,
[email protected]cc5da332009-03-04 08:02:51613 "Couldn't create CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19614 return false;
615 }
616 return true;
617}
618
[email protected]b0beaa662009-02-26 00:04:15619void ExtensionsServiceBackend::InstallExtension(
[email protected]cc655912009-01-29 23:19:19620 const FilePath& extension_path,
[email protected]cc655912009-01-29 23:19:19621 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
622 LOG(INFO) << "Installing extension " << extension_path.value();
623
[email protected]b0beaa662009-02-26 00:04:15624 frontend_ = frontend;
[email protected]cc5da332009-03-04 08:02:51625 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15626
[email protected]cc5da332009-03-04 08:02:51627 bool was_update = false;
628 FilePath destination_path;
629 if (InstallOrUpdateExtension(extension_path,
630 std::string(), // no expected id
631 &destination_path, &was_update))
632 ReportExtensionInstalled(destination_path.DirName(), was_update);
[email protected]b0beaa662009-02-26 00:04:15633}
634
[email protected]cc5da332009-03-04 08:02:51635bool ExtensionsServiceBackend::InstallOrUpdateExtension(
636 const FilePath& source_file, const std::string& expected_id,
637 FilePath* version_dir, bool* was_update) {
638 if (was_update)
639 *was_update = false;
[email protected]cc655912009-01-29 23:19:19640
641 // Read and verify the extension.
[email protected]cc5da332009-03-04 08:02:51642 scoped_ptr<DictionaryValue> manifest(ReadManifest(source_file));
[email protected]cc655912009-01-29 23:19:19643 if (!manifest.get()) {
[email protected]cc655912009-01-29 23:19:19644 // ReadManifest has already reported the extension error.
[email protected]cc5da332009-03-04 08:02:51645 return false;
[email protected]cc655912009-01-29 23:19:19646 }
647 DictionaryValue* dict = manifest.get();
648 Extension extension;
649 std::string error;
[email protected]5bfb1eb0a2009-04-08 18:33:30650 if (!extension.InitFromValue(*dict,
651 true, // require ID
652 &error)) {
[email protected]cc5da332009-03-04 08:02:51653 ReportExtensionInstallError(source_file,
654 "Invalid extension manifest.");
655 return false;
[email protected]b0beaa662009-02-26 00:04:15656 }
657
[email protected]0b344962009-03-31 04:21:45658 // ID is required for installed extensions.
659 if (extension.id().empty()) {
660 ReportExtensionInstallError(source_file, "Required value 'id' is missing.");
661 return false;
662 }
663
[email protected]b0beaa662009-02-26 00:04:15664 // If an expected id was provided, make sure it matches.
665 if (expected_id.length() && expected_id != extension.id()) {
[email protected]f0a51fb52009-03-05 12:46:38666 ReportExtensionInstallError(source_file,
[email protected]b0beaa662009-02-26 00:04:15667 "ID in new extension manifest does not match expected ID.");
[email protected]cc5da332009-03-04 08:02:51668 return false;
[email protected]cc655912009-01-29 23:19:19669 }
670
671 // <profile>/Extensions/<id>
[email protected]b0beaa662009-02-26 00:04:15672 FilePath dest_dir = install_directory_.AppendASCII(extension.id());
[email protected]cc655912009-01-29 23:19:19673 std::string version = extension.VersionString();
[email protected]b0beaa662009-02-26 00:04:15674 std::string current_version;
675 if (ReadCurrentVersion(dest_dir, &current_version)) {
676 if (!CheckCurrentVersion(version, current_version, dest_dir))
[email protected]cc5da332009-03-04 08:02:51677 return false;
678 if (was_update)
679 *was_update = true;
[email protected]b0beaa662009-02-26 00:04:15680 }
681
682 // <profile>/Extensions/INSTALL_TEMP
683 FilePath temp_dir = install_directory_.AppendASCII(kTempExtensionName);
684
685 // Ensure we're starting with a clean slate.
686 if (file_util::PathExists(temp_dir)) {
687 if (!file_util::Delete(temp_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51688 ReportExtensionInstallError(source_file,
[email protected]b0beaa662009-02-26 00:04:15689 "Couldn't delete existing temporary directory.");
[email protected]cc5da332009-03-04 08:02:51690 return false;
[email protected]b0beaa662009-02-26 00:04:15691 }
692 }
693 ScopedTempDir scoped_temp;
694 scoped_temp.Set(temp_dir);
695 if (!scoped_temp.IsValid()) {
[email protected]f0a51fb52009-03-05 12:46:38696 ReportExtensionInstallError(source_file,
[email protected]cc5da332009-03-04 08:02:51697 "Couldn't create temporary directory.");
698 return false;
[email protected]b0beaa662009-02-26 00:04:15699 }
[email protected]cc655912009-01-29 23:19:19700
701 // <profile>/Extensions/INSTALL_TEMP/<version>
702 FilePath temp_version = temp_dir.AppendASCII(version);
[email protected]b0beaa662009-02-26 00:04:15703 file_util::CreateDirectory(temp_version);
[email protected]cc5da332009-03-04 08:02:51704 if (!Unzip(source_file, temp_version, NULL)) {
705 ReportExtensionInstallError(source_file, "Couldn't unzip extension.");
706 return false;
[email protected]b0beaa662009-02-26 00:04:15707 }
[email protected]cc655912009-01-29 23:19:19708
709 // <profile>/Extensions/<dir_name>/<version>
[email protected]cc5da332009-03-04 08:02:51710 *version_dir = dest_dir.AppendASCII(version);
711 if (!InstallDirSafely(temp_version, *version_dir))
712 return false;
[email protected]cc655912009-01-29 23:19:19713
[email protected]b0beaa662009-02-26 00:04:15714 if (!SetCurrentVersion(dest_dir, version)) {
[email protected]cc5da332009-03-04 08:02:51715 if (!file_util::Delete(*version_dir, true))
[email protected]cc655912009-01-29 23:19:19716 LOG(WARNING) << "Can't remove " << dest_dir.value();
[email protected]cc5da332009-03-04 08:02:51717 return false;
[email protected]cc655912009-01-29 23:19:19718 }
719
[email protected]cc5da332009-03-04 08:02:51720 return true;
[email protected]cc655912009-01-29 23:19:19721}
722
723void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:51724 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15725
[email protected]cc655912009-01-29 23:19:19726 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51727 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:19728 std::string message =
729 StringPrintf("Could not install extension from '%s'. %s",
730 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18731 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]cc655912009-01-29 23:19:19732}
733
734void ExtensionsServiceBackend::ReportExtensionInstalled(
[email protected]cc5da332009-03-04 08:02:51735 const FilePath& path, bool update) {
[email protected]b0beaa662009-02-26 00:04:15736 frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
737 frontend_,
[email protected]cc655912009-01-29 23:19:19738 &ExtensionsServiceFrontendInterface::OnExtensionInstalled,
[email protected]b0beaa662009-02-26 00:04:15739 path,
740 update));
741
742 // After it's installed, load it right away with the same settings.
[email protected]252cc252009-02-26 18:07:50743 LOG(INFO) << "Loading extension " << path.value();
[email protected]cc5da332009-03-04 08:02:51744 Extension* extension = LoadExtensionCurrentVersion(path);
[email protected]252cc252009-02-26 18:07:50745 if (extension) {
746 // Only one extension, but ReportExtensionsLoaded can handle multiple,
747 // so we need to construct a list.
748 scoped_ptr<ExtensionList> extensions(new ExtensionList);
749 extensions->push_back(extension);
750 LOG(INFO) << "Done.";
751 // Hand off ownership of the loaded extensions to the frontend.
752 ReportExtensionsLoaded(extensions.release());
753 }
[email protected]b0beaa662009-02-26 00:04:15754}
755
756// Some extensions will autoupdate themselves externally from Chrome. These
757// are typically part of some larger client application package. To support
758// these, the extension will register its location in the registry on Windows
759// (TODO(port): what about on other platforms?) and this code will periodically
760// check that location for a .crx file, which it will then install locally if
761// a new version is available.
762void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]b0beaa662009-02-26 00:04:15763 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
764
765 // Note that this installation is intentionally silent (since it didn't
766 // go through the front-end). Extensions that are registered in this
767 // way are effectively considered 'pre-bundled', and so implicitly
768 // trusted. In general, if something has HKLM or filesystem access,
769 // they could install an extension manually themselves anyway.
770 alert_on_error_ = false;
771 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15772
773#if defined(OS_WIN)
774 HKEY reg_root = HKEY_LOCAL_MACHINE;
775 RegistryKeyIterator iterator(reg_root, kRegistryExtensions);
776 while (iterator.Valid()) {
777 RegKey key;
778 std::wstring key_path = kRegistryExtensions;
779 key_path.append(L"\\");
780 key_path.append(iterator.Name());
781 if (key.Open(reg_root, key_path.c_str())) {
782 std::wstring extension_path;
783 if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
784 std::string id = WideToASCII(iterator.Name());
[email protected]b0beaa662009-02-26 00:04:15785 std::wstring extension_version;
786 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
[email protected]cc5da332009-03-04 08:02:51787 if (ShouldInstall(id, WideToASCII(extension_version))) {
788 FilePath version_dir;
789 if (InstallOrUpdateExtension(FilePath(extension_path), id,
790 &version_dir, NULL)) {
791 // To mark that this extension was installed from an external
792 // source, create a zero-length file. At load time, this is used
793 // to indicate that the extension should be uninstalled.
794 // TODO(erikkay): move this into per-extension config storage when
795 // it appears.
796 FilePath marker = version_dir.AppendASCII(
797 kExternalInstallFile);
798 file_util::WriteFile(marker, NULL, 0);
799 }
800 }
[email protected]b0beaa662009-02-26 00:04:15801 } else {
[email protected]b0beaa662009-02-26 00:04:15802 // TODO(erikkay): find a way to get this into about:extensions
803 LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
804 " for key " << key_path;
805 }
806 } else {
[email protected]b0beaa662009-02-26 00:04:15807 // TODO(erikkay): find a way to get this into about:extensions
808 LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
809 " for key " << key_path;
810 }
811 }
812 ++iterator;
813 }
814#else
815 NOTREACHED();
816#endif
817}
818
819bool ExtensionsServiceBackend::CheckExternalUninstall(const FilePath& path,
820 const std::string& id) {
821 FilePath external_file = path.AppendASCII(kExternalInstallFile);
822 if (file_util::PathExists(external_file)) {
823#if defined(OS_WIN)
824 HKEY reg_root = HKEY_LOCAL_MACHINE;
825 RegKey key;
826 std::wstring key_path = kRegistryExtensions;
827 key_path.append(L"\\");
828 key_path.append(ASCIIToWide(id));
829
830 // If the key doesn't exist, then we should uninstall.
831 return !key.Open(reg_root, key_path.c_str());
832#else
833 NOTREACHED();
834#endif
835 }
836 return false;
837}
838
839// Assumes that the extension isn't currently loaded or in use.
840void ExtensionsServiceBackend::UninstallExtension(const FilePath& path) {
841 FilePath parent = path.DirName();
842 FilePath version =
843 parent.AppendASCII(ExtensionsService::kCurrentVersionFileName);
844 bool version_exists = file_util::PathExists(version);
845 DCHECK(version_exists);
846 if (!version_exists) {
847 LOG(WARNING) << "Asked to uninstall bogus extension dir " << parent.value();
848 return;
849 }
850 if (!file_util::Delete(parent, true)) {
851 LOG(WARNING) << "Failed to delete " << parent.value();
852 }
853}
854
855bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
856 const std::string& version) {
857 FilePath dir(install_directory_.AppendASCII(id.c_str()));
858 std::string current_version;
859 if (ReadCurrentVersion(dir, &current_version)) {
860 return !CheckCurrentVersion(version, current_version, dir);
861 }
862 return true;
[email protected]cc655912009-01-29 23:19:19863}