blob: c469c086bcc4dfa150d03e42081f700592b3a88e [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]bdbc87c2009-01-25 05:08:5417#include "chrome/browser/extensions/user_script_master.h"
[email protected]6014d672008-12-05 00:38:2518#include "chrome/common/json_value_serializer.h"
[email protected]82891262008-12-24 00:21:2619#include "chrome/common/notification_service.h"
[email protected]cc655912009-01-29 23:19:1920#include "chrome/common/unzip.h"
[email protected]79db6232009-02-13 20:51:2021#if defined(OS_WIN)
22#include "chrome/common/win_util.h"
23#endif
[email protected]6014d672008-12-05 00:38:2524
25// ExtensionsService
26
[email protected]cc655912009-01-29 23:19:1927const char* ExtensionsService::kInstallDirectoryName = "Extensions";
28const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
29const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
30// Chromium Extension magic number
31static const char kExtensionFileMagic[] = "Cr24";
32
33struct ExtensionHeader {
34 char magic[sizeof(kExtensionFileMagic) - 1];
35 uint32 version;
36 size_t header_size;
37 size_t manifest_size;
38};
39
40const size_t kZipHashBytes = 32; // SHA-256
41const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
[email protected]6014d672008-12-05 00:38:2542
[email protected]bdbc87c2009-01-25 05:08:5443ExtensionsService::ExtensionsService(const FilePath& profile_directory,
44 UserScriptMaster* user_script_master)
[email protected]6014d672008-12-05 00:38:2545 : message_loop_(MessageLoop::current()),
46 backend_(new ExtensionsServiceBackend),
[email protected]cc655912009-01-29 23:19:1947 install_directory_(profile_directory.AppendASCII(kInstallDirectoryName)),
[email protected]bdbc87c2009-01-25 05:08:5448 user_script_master_(user_script_master) {
[email protected]6014d672008-12-05 00:38:2549}
50
51ExtensionsService::~ExtensionsService() {
52 for (ExtensionList::iterator iter = extensions_.begin();
53 iter != extensions_.end(); ++iter) {
54 delete *iter;
55 }
56}
57
58bool ExtensionsService::Init() {
59 // TODO(aa): This message loop should probably come from a backend
60 // interface, similar to how the message loop for the frontend comes
61 // from the frontend interface.
62 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
63 NewRunnableMethod(backend_.get(),
64 &ExtensionsServiceBackend::LoadExtensionsFromDirectory,
65 install_directory_,
66 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
67 // TODO(aa): Load extensions from other registered directories.
68
69 return true;
70}
71
72MessageLoop* ExtensionsService::GetMessageLoop() {
73 return message_loop_;
74}
75
[email protected]3cf4f0992009-02-03 23:00:3076void ExtensionsService::InstallExtension(const FilePath& extension_path) {
77 // TODO(aa): This message loop should probably come from a backend
78 // interface, similar to how the message loop for the frontend comes
79 // from the frontend interface.
80 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
81 NewRunnableMethod(backend_.get(),
82 &ExtensionsServiceBackend::InstallExtension,
83 extension_path,
84 install_directory_,
85 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
86}
87
88void ExtensionsService::LoadExtension(const FilePath& extension_path) {
89 // TODO(aa): This message loop should probably come from a backend
90 // interface, similar to how the message loop for the frontend comes
91 // from the frontend interface.
92 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
93 NewRunnableMethod(backend_.get(),
94 &ExtensionsServiceBackend::LoadSingleExtension,
95 extension_path,
96 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
97}
98
[email protected]6014d672008-12-05 00:38:2599void ExtensionsService::OnExtensionsLoadedFromDirectory(
[email protected]08816d0d2008-12-08 18:43:53100 ExtensionList* new_extensions) {
101 extensions_.insert(extensions_.end(), new_extensions->begin(),
102 new_extensions->end());
[email protected]6014d672008-12-05 00:38:25103
[email protected]bdbc87c2009-01-25 05:08:54104 // Tell UserScriptMaster about any scripts in the loaded extensions.
105 for (ExtensionList::iterator extension = extensions_.begin();
106 extension != extensions_.end(); ++extension) {
[email protected]34aa8dc2009-02-19 07:03:05107 const UserScriptList& scripts = (*extension)->content_scripts();
[email protected]bdbc87c2009-01-25 05:08:54108 for (UserScriptList::const_iterator script = scripts.begin();
109 script != scripts.end(); ++script) {
110 user_script_master_->AddLoneScript(*script);
111 }
112 }
113
[email protected]54cb3c92009-02-17 22:30:21114 // Tell UserScriptMaster to kick off the first scan.
[email protected]bdbc87c2009-01-25 05:08:54115 user_script_master_->StartScan();
116
[email protected]bfd04a62009-02-01 18:16:56117 NotificationService::current()->Notify(
118 NotificationType::EXTENSIONS_LOADED,
[email protected]82891262008-12-24 00:21:26119 NotificationService::AllSources(),
120 Details<ExtensionList>(new_extensions));
121
122 delete new_extensions;
[email protected]6014d672008-12-05 00:38:25123}
124
[email protected]3acbd422008-12-08 18:25:00125void ExtensionsService::OnExtensionLoadError(const std::string& error) {
126 // TODO(aa): Print the error message out somewhere better. I think we are
127 // going to need some sort of 'extension inspector'.
128 LOG(WARNING) << error;
[email protected]79db6232009-02-13 20:51:20129#if defined(OS_WIN)
130 win_util::MessageBox(NULL, UTF8ToWide(error),
131 L"Extension load error", MB_OK | MB_SETFOREGROUND);
132#endif
[email protected]6014d672008-12-05 00:38:25133}
134
[email protected]cc655912009-01-29 23:19:19135void ExtensionsService::OnExtensionInstallError(const std::string& error) {
136 // TODO(erikkay): Print the error message out somewhere better.
137 LOG(WARNING) << error;
[email protected]79db6232009-02-13 20:51:20138#if defined(OS_WIN)
139 win_util::MessageBox(NULL, UTF8ToWide(error),
140 L"Extension load error", MB_OK | MB_SETFOREGROUND);
141#endif
[email protected]cc655912009-01-29 23:19:19142}
143
144void ExtensionsService::OnExtensionInstalled(FilePath path) {
[email protected]bfd04a62009-02-01 18:16:56145 NotificationService::current()->Notify(
146 NotificationType::EXTENSION_INSTALLED,
[email protected]cc655912009-01-29 23:19:19147 NotificationService::AllSources(),
148 Details<FilePath>(&path));
149
[email protected]0877fd92009-02-03 16:34:06150 // Immediately try to load the extension.
151 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
152 NewRunnableMethod(backend_.get(),
153 &ExtensionsServiceBackend::LoadSingleExtension,
154 path,
155 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
[email protected]cc655912009-01-29 23:19:19156}
157
[email protected]6014d672008-12-05 00:38:25158
159// ExtensionsServicesBackend
160
161bool ExtensionsServiceBackend::LoadExtensionsFromDirectory(
[email protected]eab9b452009-01-23 20:48:59162 const FilePath& path_in,
[email protected]6014d672008-12-05 00:38:25163 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
[email protected]eab9b452009-01-23 20:48:59164 FilePath path = path_in;
165 if (!file_util::AbsolutePath(&path))
166 NOTREACHED();
167
[email protected]6014d672008-12-05 00:38:25168 // Find all child directories in the install directory and load their
169 // manifests. Post errors and results to the frontend.
170 scoped_ptr<ExtensionList> extensions(new ExtensionList);
[email protected]0b733222008-12-11 14:55:12171 file_util::FileEnumerator enumerator(path,
[email protected]cc655912009-01-29 23:19:19172 false, // not recursive
[email protected]6014d672008-12-05 00:38:25173 file_util::FileEnumerator::DIRECTORIES);
[email protected]0b733222008-12-11 14:55:12174 for (FilePath child_path = enumerator.Next(); !child_path.value().empty();
[email protected]6014d672008-12-05 00:38:25175 child_path = enumerator.Next()) {
[email protected]18a12352009-01-31 01:33:28176 std::string version_str;
[email protected]54cb3c92009-02-17 22:30:21177 if (!ReadCurrentVersion(child_path, &version_str)) {
178 ReportExtensionLoadError(frontend.get(), child_path, StringPrintf(
179 "Could not read '%s' file.",
180 ExtensionsService::kCurrentVersionFileName));
181 continue;
[email protected]18a12352009-01-31 01:33:28182 }
[email protected]6014d672008-12-05 00:38:25183
[email protected]54cb3c92009-02-17 22:30:21184 child_path = child_path.AppendASCII(version_str);
[email protected]0877fd92009-02-03 16:34:06185 Extension* extension = LoadExtension(child_path, frontend);
186 if (extension)
187 extensions->push_back(extension);
[email protected]6014d672008-12-05 00:38:25188 }
189
190 ReportExtensionsLoaded(frontend.get(), extensions.release());
191 return true;
192}
193
[email protected]0877fd92009-02-03 16:34:06194bool ExtensionsServiceBackend::LoadSingleExtension(
195 const FilePath& path_in,
196 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
197 FilePath path = path_in;
198 if (!file_util::AbsolutePath(&path))
199 NOTREACHED();
200 Extension* extension = LoadExtension(path, frontend);
201 if (extension) {
202 ExtensionList* extensions = new ExtensionList;
203 extensions->push_back(extension);
204 ReportExtensionsLoaded(frontend.get(), extensions);
205 return true;
206 }
207 return false;
208}
209
210Extension* ExtensionsServiceBackend::LoadExtension(
211 const FilePath& path,
212 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
213 FilePath manifest_path =
214 path.AppendASCII(Extension::kManifestFilename);
215 if (!file_util::PathExists(manifest_path)) {
216 ReportExtensionLoadError(frontend.get(), path,
217 Extension::kInvalidManifestError);
218 return NULL;
219 }
220
221 JSONFileValueSerializer serializer(manifest_path.ToWStringHack());
222 std::string error;
223 scoped_ptr<Value> root(serializer.Deserialize(&error));
224 if (!root.get()) {
225 ReportExtensionLoadError(frontend.get(), path,
226 error);
227 return NULL;
228 }
229
230 if (!root->IsType(Value::TYPE_DICTIONARY)) {
231 ReportExtensionLoadError(frontend.get(), path,
232 Extension::kInvalidManifestError);
233 return NULL;
234 }
235
236 scoped_ptr<Extension> extension(new Extension(path));
237 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
238 &error)) {
239 ReportExtensionLoadError(frontend.get(), path, error);
240 return NULL;
241 }
[email protected]8d6d9ff2009-02-20 08:14:39242
243 // Validate that claimed resources actually exist.
244 for (UserScriptList::const_iterator iter =
245 extension->content_scripts().begin();
246 iter != extension->content_scripts().end(); ++iter) {
247 if (!file_util::PathExists(iter->path())) {
248 ReportExtensionLoadError(frontend.get(), path, StringPrintf(
249 "Could not load content script '%s'.",
250 WideToUTF8(iter->path().ToWStringHack()).c_str()));
251 return NULL;
252 }
253 }
254
[email protected]0877fd92009-02-03 16:34:06255 return extension.release();
256}
257
[email protected]6014d672008-12-05 00:38:25258void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc655912009-01-29 23:19:19259 ExtensionsServiceFrontendInterface *frontend, const FilePath& path,
[email protected]3acbd422008-12-08 18:25:00260 const std::string &error) {
[email protected]cc655912009-01-29 23:19:19261 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
262 std::string path_str = WideToASCII(path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00263 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19264 path_str.c_str(), error.c_str());
[email protected]6014d672008-12-05 00:38:25265 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
266 frontend, &ExtensionsServiceFrontendInterface::OnExtensionLoadError,
[email protected]3acbd422008-12-08 18:25:00267 message));
[email protected]6014d672008-12-05 00:38:25268}
269
270void ExtensionsServiceBackend::ReportExtensionsLoaded(
271 ExtensionsServiceFrontendInterface *frontend, ExtensionList* extensions) {
272 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
273 frontend,
274 &ExtensionsServiceFrontendInterface::OnExtensionsLoadedFromDirectory,
275 extensions));
276}
[email protected]cc655912009-01-29 23:19:19277
278// The extension file format is a header, followed by the manifest, followed
279// by the zip file. The header is a magic number, a version, the size of the
280// header, and the size of the manifest. These ints are 4 byte little endian.
281DictionaryValue* ExtensionsServiceBackend::ReadManifest(
282 const FilePath& extension_path,
283 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
284 ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
285 if (!file.get()) {
286 ReportExtensionInstallError(frontend, extension_path,
287 "no such extension file");
288 return NULL;
289 }
290
291 // Read and verify the header.
292 ExtensionHeader header;
293 size_t len;
294 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
295 // appears that we don't have any endian/alignment aware serialization
296 // code in the code base. So for now, this assumes that we're running
297 // on a little endian machine with 4 byte alignment.
298 len = fread(&header, 1, sizeof(ExtensionHeader), file.get());
299 if (len < sizeof(ExtensionHeader)) {
300 ReportExtensionInstallError(frontend, extension_path,
301 "invalid extension header");
302 return NULL;
303 }
304 if (strncmp(kExtensionFileMagic, header.magic, sizeof(header.magic))) {
305 ReportExtensionInstallError(frontend, extension_path,
306 "bad magic number");
307 return NULL;
308 }
309 if (header.version != Extension::kExpectedFormatVersion) {
310 ReportExtensionInstallError(frontend, extension_path,
311 "bad version number");
312 return NULL;
313 }
314 if (header.header_size > sizeof(ExtensionHeader))
315 fseek(file.get(), header.header_size - sizeof(ExtensionHeader), SEEK_CUR);
316
317 char buf[1 << 16];
318 std::string manifest_str;
319 size_t read_size = std::min(sizeof(buf), header.manifest_size);
320 size_t remainder = header.manifest_size;
321 while ((len = fread(buf, 1, read_size, file.get())) > 0) {
322 manifest_str.append(buf, len);
323 if (len <= remainder)
324 break;
325 remainder -= len;
326 read_size = std::min(sizeof(buf), remainder);
327 }
328
329 // Verify the JSON
330 JSONStringValueSerializer json(manifest_str);
331 std::string error;
[email protected]4c7ca4b2009-02-04 00:53:08332 scoped_ptr<Value> val(json.Deserialize(&error));
333 if (!val.get()) {
[email protected]cc655912009-01-29 23:19:19334 ReportExtensionInstallError(frontend, extension_path, error);
335 return NULL;
336 }
337 if (!val->IsType(Value::TYPE_DICTIONARY)) {
338 ReportExtensionInstallError(frontend, extension_path,
339 "manifest isn't a JSON dictionary");
340 return NULL;
341 }
[email protected]4c7ca4b2009-02-04 00:53:08342 DictionaryValue* manifest = static_cast<DictionaryValue*>(val.get());
[email protected]cc655912009-01-29 23:19:19343 std::string zip_hash;
344 if (!manifest->GetString(Extension::kZipHashKey, &zip_hash)) {
345 ReportExtensionInstallError(frontend, extension_path,
346 "missing zip_hash key");
347 return NULL;
348 }
349 if (zip_hash.size() != kZipHashHexBytes) {
350 ReportExtensionInstallError(frontend, extension_path,
351 "invalid zip_hash key");
352 return NULL;
353 }
354
355 // Read the rest of the zip file and compute a hash to compare against
356 // what the manifest claims. Compute the hash incrementally since the
357 // zip file could be large.
358 const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
359 SHA256Context ctx;
360 SHA256_Begin(&ctx);
361 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
362 SHA256_Update(&ctx, ubuf, len);
363 uint8 hash[32];
364 SHA256_End(&ctx, hash, NULL, sizeof(hash));
365
366 std::vector<uint8> zip_hash_bytes;
367 if (!HexStringToBytes(zip_hash, &zip_hash_bytes)) {
368 ReportExtensionInstallError(frontend, extension_path,
369 "invalid zip_hash key");
370 return NULL;
371 }
372 if (zip_hash_bytes.size() != kZipHashBytes) {
373 ReportExtensionInstallError(frontend, extension_path,
374 "invalid zip_hash key");
375 return NULL;
376 }
377 for (size_t i = 0; i < kZipHashBytes; ++i) {
378 if (zip_hash_bytes[i] != hash[i]) {
379 ReportExtensionInstallError(frontend, extension_path,
380 "zip_hash key didn't match zip hash");
381 return NULL;
382 }
383 }
384
385 // TODO(erikkay): The manifest will also contain a signature of the hash
386 // (or perhaps the whole manifest) for authentication purposes.
387
[email protected]4c7ca4b2009-02-04 00:53:08388 // The caller owns val (now cast to manifest).
389 val.release();
[email protected]cc655912009-01-29 23:19:19390 return manifest;
391}
392
[email protected]18a12352009-01-31 01:33:28393bool ExtensionsServiceBackend::ReadCurrentVersion(
394 const FilePath& extension_path,
395 std::string* version_string) {
396 FilePath current_version =
397 extension_path.AppendASCII(ExtensionsService::kCurrentVersionFileName);
398 if (file_util::PathExists(current_version)) {
399 if (file_util::ReadFileToString(current_version, version_string)) {
400 TrimWhitespace(*version_string, TRIM_ALL, version_string);
401 return true;
402 }
403 }
404 return false;
405}
406
[email protected]cc655912009-01-29 23:19:19407bool ExtensionsServiceBackend::CheckCurrentVersion(
408 const FilePath& extension_path,
409 const std::string& version,
410 const FilePath& dest_dir,
411 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
[email protected]18a12352009-01-31 01:33:28412 std::string version_str;
413 if (ReadCurrentVersion(dest_dir, &version_str)) {
414 if (version_str == version) {
415 FilePath version_dir = dest_dir.AppendASCII(version_str);
416 if (file_util::PathExists(version_dir)) {
[email protected]cc655912009-01-29 23:19:19417 ReportExtensionInstallError(frontend, extension_path,
418 "Extension version already installed");
419 return false;
[email protected]18a12352009-01-31 01:33:28420 }
421 // If the existing version_dir doesn't exist, then we'll return true
422 // so that we attempt to repair the broken installation.
423 } else {
424 scoped_ptr<Version> cur_version(
425 Version::GetVersionFromString(version_str));
426 scoped_ptr<Version> new_version(
427 Version::GetVersionFromString(version));
428 if (cur_version->CompareTo(*new_version) >= 0) {
429 ReportExtensionInstallError(frontend, extension_path,
430 "More recent version of extension already installed");
431 return false;
[email protected]cc655912009-01-29 23:19:19432 }
433 }
434 }
435 return true;
436}
437
438bool ExtensionsServiceBackend::UnzipExtension(const FilePath& extension_path,
439 const FilePath& temp_dir,
440 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
441 // <profile>/Extensions/INSTALL_TEMP/<version>
442 if (!file_util::CreateDirectory(temp_dir)) {
443 ReportExtensionInstallError(frontend, extension_path,
444 "Couldn't create version directory.");
445 return false;
446 }
447 if (!Unzip(extension_path, temp_dir, NULL)) {
448 // Remove what we just installed.
449 file_util::Delete(temp_dir, true);
450 ReportExtensionInstallError(frontend, extension_path,
451 "Couldn't unzip extension.");
452 return false;
453 }
454 return true;
455}
456
457bool ExtensionsServiceBackend::InstallDirSafely(
458 const FilePath& extension_path,
459 const FilePath& source_dir,
460 const FilePath& dest_dir,
461 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
462
463 if (file_util::PathExists(dest_dir)) {
464 // By the time we get here, it should be safe to assume that this directory
465 // is not currently in use (it's not the current active version).
466 if (!file_util::Delete(dest_dir, true)) {
467 ReportExtensionInstallError(frontend, extension_path,
468 "Can't delete existing version directory.");
469 return false;
470 }
471 } else {
472 FilePath parent = dest_dir.DirName();
473 if (!file_util::DirectoryExists(parent)) {
474 if (!file_util::CreateDirectory(parent)) {
475 ReportExtensionInstallError(frontend, extension_path,
476 "Couldn't create extension directory.");
477 return false;
478 }
479 }
480 }
481 if (!file_util::Move(source_dir, dest_dir)) {
482 ReportExtensionInstallError(frontend, extension_path,
483 "Couldn't move temporary directory.");
484 return false;
485 }
486
487 return true;
488}
489
490bool ExtensionsServiceBackend::SetCurrentVersion(
491 const FilePath& extension_path,
492 const FilePath& dest_dir,
493 std::string version,
494 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
495 // Write out the new CurrentVersion file.
496 // <profile>/Extension/<name>/CurrentVersion
497 FilePath current_version =
498 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
499 FilePath current_version_old =
500 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
501 if (file_util::PathExists(current_version_old)) {
502 if (!file_util::Delete(current_version_old, false)) {
503 ReportExtensionInstallError(frontend, extension_path,
504 "Couldn't remove CurrentVersion_old file.");
505 return false;
506 }
507 }
508 if (file_util::PathExists(current_version)) {
509 if (!file_util::Move(current_version, current_version_old)) {
510 ReportExtensionInstallError(frontend, extension_path,
511 "Couldn't move CurrentVersion file.");
512 return false;
513 }
514 }
515 net::FileStream stream;
516 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
517 if (stream.Open(current_version, flags) != 0)
518 return false;
519 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
520 // Restore the old CurrentVersion.
521 if (file_util::PathExists(current_version_old)) {
522 if (!file_util::Move(current_version_old, current_version)) {
523 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
524 " to " << current_version.value();
525 // TODO(erikkay): This is an ugly state to be in. Try harder?
526 }
527 }
528 ReportExtensionInstallError(frontend, extension_path,
529 "Couldn't create CurrentVersion file.");
530 return false;
531 }
532 return true;
533}
534
535bool ExtensionsServiceBackend::InstallExtension(
536 const FilePath& extension_path,
537 const FilePath& install_dir,
538 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
539 LOG(INFO) << "Installing extension " << extension_path.value();
540
541 // <profile>/Extensions/INSTALL_TEMP
542 FilePath temp_dir = install_dir.AppendASCII(kTempExtensionName);
543 // Ensure we're starting with a clean slate.
544 if (file_util::PathExists(temp_dir)) {
545 if (!file_util::Delete(temp_dir, true)) {
546 ReportExtensionInstallError(frontend, extension_path,
547 "Couldn't delete existing temporary directory.");
548 return false;
549 }
550 }
551 ScopedTempDir scoped_temp;
552 scoped_temp.Set(temp_dir);
553 if (!scoped_temp.IsValid()) {
554 ReportExtensionInstallError(frontend, extension_path,
555 "Couldn't create temporary directory.");
556 return false;
557 }
558
559 // Read and verify the extension.
560 scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path, frontend));
561 if (!manifest.get()) {
562 // ReadManifest has already reported the extension error.
563 return false;
564 }
565 DictionaryValue* dict = manifest.get();
566 Extension extension;
567 std::string error;
568 if (!extension.InitFromValue(*dict, &error)) {
569 ReportExtensionInstallError(frontend, extension_path,
570 "Invalid extension manifest.");
571 return false;
572 }
573
574 // <profile>/Extensions/<id>
575 FilePath dest_dir = install_dir.AppendASCII(extension.id());
576 std::string version = extension.VersionString();
577 if (!CheckCurrentVersion(extension_path, version, dest_dir, frontend))
578 return false;
579
580 // <profile>/Extensions/INSTALL_TEMP/<version>
581 FilePath temp_version = temp_dir.AppendASCII(version);
582 if (!UnzipExtension(extension_path, temp_version, frontend))
583 return false;
584
585 // <profile>/Extensions/<dir_name>/<version>
586 FilePath version_dir = dest_dir.AppendASCII(version);
587 if (!InstallDirSafely(extension_path, temp_version, version_dir, frontend))
588 return false;
589
590 if (!SetCurrentVersion(extension_path, dest_dir, version, frontend)) {
591 if (!file_util::Delete(version_dir, true))
592 LOG(WARNING) << "Can't remove " << dest_dir.value();
593 return false;
594 }
595
596 ReportExtensionInstalled(frontend, dest_dir);
597 return true;
598}
599
600void ExtensionsServiceBackend::ReportExtensionInstallError(
601 ExtensionsServiceFrontendInterface *frontend, const FilePath& path,
602 const std::string &error) {
603 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
604 std::string path_str = WideToASCII(path.ToWStringHack());
605 std::string message =
606 StringPrintf("Could not install extension from '%s'. %s",
607 path_str.c_str(), error.c_str());
608 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
609 frontend, &ExtensionsServiceFrontendInterface::OnExtensionInstallError,
610 message));
611}
612
613void ExtensionsServiceBackend::ReportExtensionInstalled(
614 ExtensionsServiceFrontendInterface *frontend, FilePath path) {
615 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
616 frontend,
617 &ExtensionsServiceFrontendInterface::OnExtensionInstalled,
618 path));
619}