blob: 7915de16cadcf1b2924e42fa6fedfe9b1fd057c4 [file] [log] [blame]
[email protected]a999d672012-01-23 22:31:401// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]1fca1492009-05-15 22:23:432// 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/common/extensions/extension_unpacker.h"
6
[email protected]ccea03c2010-12-17 03:31:507#include <set>
8
[email protected]1fca1492009-05-15 22:23:439#include "base/file_util.h"
[email protected]ffbec692012-02-26 20:26:4210#include "base/json/json_file_value_serializer.h"
[email protected]3b63f8f42011-03-28 01:54:1511#include "base/memory/scoped_handle.h"
[email protected]e0785902011-05-19 23:34:1712#include "base/scoped_temp_dir.h"
[email protected]1fca1492009-05-15 22:23:4313#include "base/string_util.h"
[email protected]34b99632011-01-01 01:01:0614#include "base/threading/thread.h"
[email protected]99922662010-08-17 16:24:2515#include "base/utf_string_conversions.h"
[email protected]1fca1492009-05-15 22:23:4316#include "base/values.h"
[email protected]5b1a0e22009-05-26 19:00:5817#include "chrome/common/extensions/extension.h"
[email protected]a52c0e92012-03-23 06:02:2418#include "chrome/common/extensions/extension_manifest_constants.h"
[email protected]7c927b62010-02-24 09:54:1319#include "chrome/common/extensions/extension_file_util.h"
[email protected]1acbb4b62010-03-09 17:52:2920#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]1fca1492009-05-15 22:23:4321#include "chrome/common/url_constants.h"
[email protected]facd7a7652009-06-05 23:15:0222#include "chrome/common/zip.h"
[email protected]19a5c7442011-10-21 20:00:4123#include "content/public/common/common_param_traits.h"
[email protected]946d1b22009-07-22 23:57:2124#include "ipc/ipc_message_utils.h"
[email protected]e0785902011-05-19 23:34:1725#include "net/base/file_stream.h"
[email protected]902f7cd2009-05-22 19:02:1926#include "third_party/skia/include/core/SkBitmap.h"
27#include "webkit/glue/image_decoder.h"
[email protected]1fca1492009-05-15 22:23:4328
[email protected]9428edc2009-11-18 18:02:4729namespace errors = extension_manifest_errors;
30namespace keys = extension_manifest_keys;
[email protected]b0b3abd92010-04-30 17:00:0931namespace filenames = extension_filenames;
[email protected]9428edc2009-11-18 18:02:4732
[email protected]1fca1492009-05-15 22:23:4333namespace {
[email protected]6d377142010-03-17 20:36:0534
[email protected]fbcc40302009-06-12 20:45:4535// Errors
36const char* kCouldNotCreateDirectoryError =
[email protected]2d6bda732010-04-06 01:51:4337 "Could not create directory for unzipping: ";
[email protected]fbcc40302009-06-12 20:45:4538const char* kCouldNotDecodeImageError = "Could not decode theme image.";
39const char* kCouldNotUnzipExtension = "Could not unzip extension.";
40const char* kPathNamesMustBeAbsoluteOrLocalError =
41 "Path names must not be absolute or contain '..'.";
[email protected]1fca1492009-05-15 22:23:4342
[email protected]fcfd12f2009-08-14 22:20:4643// A limit to stop us passing dangerously large canvases to the browser.
44const int kMaxImageCanvas = 4096 * 4096;
45
[email protected]3bb84992010-08-26 17:23:4646SkBitmap DecodeImage(const FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:1947 // Read the file from disk.
48 std::string file_contents;
49 if (!file_util::PathExists(path) ||
50 !file_util::ReadFileToString(path, &file_contents)) {
51 return SkBitmap();
52 }
53
54 // Decode the image using WebKit's image decoder.
55 const unsigned char* data =
56 reinterpret_cast<const unsigned char*>(file_contents.data());
57 webkit_glue::ImageDecoder decoder;
[email protected]fcfd12f2009-08-14 22:20:4658 SkBitmap bitmap = decoder.Decode(data, file_contents.length());
59 Sk64 bitmap_size = bitmap.getSize64();
60 if (!bitmap_size.is32() || bitmap_size.get32() > kMaxImageCanvas)
61 return SkBitmap();
62 return bitmap;
[email protected]902f7cd2009-05-22 19:02:1963}
64
[email protected]3bb84992010-08-26 17:23:4665bool PathContainsParentDirectory(const FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:1966 const FilePath::StringType kSeparators(FilePath::kSeparators);
67 const FilePath::StringType kParentDirectory(FilePath::kParentDirectory);
68 const size_t npos = FilePath::StringType::npos;
69 const FilePath::StringType& value = path.value();
70
71 for (size_t i = 0; i < value.length(); ) {
72 i = value.find(kParentDirectory, i);
73 if (i != npos) {
74 if ((i == 0 || kSeparators.find(value[i-1]) == npos) &&
75 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
76 return true;
77 }
78 ++i;
79 }
80 }
81
82 return false;
[email protected]1fca1492009-05-15 22:23:4383}
84
[email protected]3bb84992010-08-26 17:23:4685} // namespace
86
[email protected]fc38935a2011-10-31 23:53:2887ExtensionUnpacker::ExtensionUnpacker(const FilePath& extension_path,
[email protected]f5bf1842012-02-15 02:52:2688 const std::string& extension_id,
[email protected]1c321ee2012-05-21 03:02:3489 extensions::Extension::Location location,
[email protected]fc38935a2011-10-31 23:53:2890 int creation_flags)
[email protected]f5bf1842012-02-15 02:52:2691 : extension_path_(extension_path),
92 extension_id_(extension_id),
93 location_(location),
[email protected]fc38935a2011-10-31 23:53:2894 creation_flags_(creation_flags) {
[email protected]3bb84992010-08-26 17:23:4695}
96
97ExtensionUnpacker::~ExtensionUnpacker() {
98}
99
[email protected]902f7cd2009-05-22 19:02:19100DictionaryValue* ExtensionUnpacker::ReadManifest() {
101 FilePath manifest_path =
[email protected]1c321ee2012-05-21 03:02:34102 temp_install_dir_.Append(extensions::Extension::kManifestFilename);
[email protected]902f7cd2009-05-22 19:02:19103 if (!file_util::PathExists(manifest_path)) {
[email protected]9428edc2009-11-18 18:02:47104 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19105 return NULL;
106 }
107
108 JSONFileValueSerializer serializer(manifest_path);
109 std::string error;
[email protected]ba399672010-04-06 15:42:39110 scoped_ptr<Value> root(serializer.Deserialize(NULL, &error));
[email protected]902f7cd2009-05-22 19:02:19111 if (!root.get()) {
112 SetError(error);
113 return NULL;
114 }
115
116 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]9428edc2009-11-18 18:02:47117 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19118 return NULL;
119 }
120
121 return static_cast<DictionaryValue*>(root.release());
122}
123
[email protected]9428edc2009-11-18 18:02:47124bool ExtensionUnpacker::ReadAllMessageCatalogs(
125 const std::string& default_locale) {
126 FilePath locales_path =
[email protected]1c321ee2012-05-21 03:02:34127 temp_install_dir_.Append(extensions::Extension::kLocaleFolder);
[email protected]9428edc2009-11-18 18:02:47128
[email protected]1acbb4b62010-03-09 17:52:29129 // Not all folders under _locales have to be valid locales.
[email protected]9428edc2009-11-18 18:02:47130 file_util::FileEnumerator locales(locales_path,
131 false,
132 file_util::FileEnumerator::DIRECTORIES);
133
[email protected]1acbb4b62010-03-09 17:52:29134 std::set<std::string> all_locales;
135 extension_l10n_util::GetAllLocales(&all_locales);
136 FilePath locale_path;
137 while (!(locale_path = locales.Next()).empty()) {
138 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
139 all_locales))
[email protected]9428edc2009-11-18 18:02:47140 continue;
141
142 FilePath messages_path =
[email protected]1c321ee2012-05-21 03:02:34143 locale_path.Append(extensions::Extension::kMessagesFilename);
[email protected]9428edc2009-11-18 18:02:47144
145 if (!ReadMessageCatalog(messages_path))
146 return false;
[email protected]1acbb4b62010-03-09 17:52:29147 }
[email protected]9428edc2009-11-18 18:02:47148
149 return true;
150}
151
[email protected]1fca1492009-05-15 22:23:43152bool ExtensionUnpacker::Run() {
[email protected]26f025e2011-10-28 22:49:27153 DVLOG(1) << "Installing extension " << extension_path_.value();
[email protected]1fca1492009-05-15 22:23:43154
[email protected]1fca1492009-05-15 22:23:43155 // <profile>/Extensions/INSTALL_TEMP/<version>
[email protected]902f7cd2009-05-22 19:02:19156 temp_install_dir_ =
[email protected]a999d672012-01-23 22:31:40157 extension_path_.DirName().AppendASCII(filenames::kTempExtensionName);
[email protected]baedfdf2010-06-14 18:50:19158
[email protected]baedfdf2010-06-14 18:50:19159 if (!file_util::CreateDirectory(temp_install_dir_)) {
[email protected]075be5d2010-09-24 15:39:39160#if defined(OS_WIN)
161 std::string dir_string = WideToUTF8(temp_install_dir_.value());
162#else
[email protected]2d6bda732010-04-06 01:51:43163 std::string dir_string = temp_install_dir_.value();
[email protected]075be5d2010-09-24 15:39:39164#endif
165
[email protected]2d6bda732010-04-06 01:51:43166 SetError(kCouldNotCreateDirectoryError + dir_string);
[email protected]1fca1492009-05-15 22:23:43167 return false;
168 }
169
[email protected]b3eb3dd22011-10-26 06:59:34170 if (!zip::Unzip(extension_path_, temp_install_dir_)) {
[email protected]fbcc40302009-06-12 20:45:45171 SetError(kCouldNotUnzipExtension);
[email protected]1fca1492009-05-15 22:23:43172 return false;
173 }
174
[email protected]902f7cd2009-05-22 19:02:19175 // Parse the manifest.
176 parsed_manifest_.reset(ReadManifest());
[email protected]a999d672012-01-23 22:31:40177 if (!parsed_manifest_.get())
[email protected]902f7cd2009-05-22 19:02:19178 return false; // Error was already reported.
[email protected]af1277b2009-07-28 00:47:53179
[email protected]fbcc40302009-06-12 20:45:45180 std::string error;
[email protected]1c321ee2012-05-21 03:02:34181 scoped_refptr<extensions::Extension> extension(extensions::Extension::Create(
[email protected]542258c2011-03-04 21:25:31182 temp_install_dir_,
[email protected]fc38935a2011-10-31 23:53:28183 location_,
[email protected]542258c2011-03-04 21:25:31184 *parsed_manifest_,
[email protected]fc38935a2011-10-31 23:53:28185 creation_flags_,
[email protected]f5bf1842012-02-15 02:52:26186 extension_id_,
[email protected]542258c2011-03-04 21:25:31187 &error));
[email protected]66e4eb32010-10-27 20:37:41188 if (!extension.get()) {
[email protected]902f7cd2009-05-22 19:02:19189 SetError(error);
190 return false;
191 }
[email protected]99872e32009-09-25 22:02:49192
[email protected]ab55c2b2012-06-01 23:55:03193 std::vector<std::string> warnings;
194 if (!extension_file_util::ValidateExtension(extension.get(),
195 &error, &warnings)) {
[email protected]99872e32009-09-25 22:02:49196 SetError(error);
197 return false;
198 }
[email protected]ab55c2b2012-06-01 23:55:03199 extension->AddInstallWarnings(warnings);
[email protected]99872e32009-09-25 22:02:49200
[email protected]902f7cd2009-05-22 19:02:19201 // Decode any images that the browser needs to display.
[email protected]66e4eb32010-10-27 20:37:41202 std::set<FilePath> image_paths = extension->GetBrowserImages();
[email protected]facd7a7652009-06-05 23:15:02203 for (std::set<FilePath>::iterator it = image_paths.begin();
204 it != image_paths.end(); ++it) {
205 if (!AddDecodedImage(*it))
[email protected]902f7cd2009-05-22 19:02:19206 return false; // Error was already reported.
207 }
208
[email protected]9428edc2009-11-18 18:02:47209 // Parse all message catalogs (if any).
210 parsed_catalogs_.reset(new DictionaryValue);
[email protected]66e4eb32010-10-27 20:37:41211 if (!extension->default_locale().empty()) {
212 if (!ReadAllMessageCatalogs(extension->default_locale()))
[email protected]9428edc2009-11-18 18:02:47213 return false; // Error was already reported.
214 }
215
[email protected]902f7cd2009-05-22 19:02:19216 return true;
217}
218
[email protected]facd7a7652009-06-05 23:15:02219bool ExtensionUnpacker::DumpImagesToFile() {
220 IPC::Message pickle; // We use a Message so we can use WriteParam.
221 IPC::WriteParam(&pickle, decoded_images_);
222
[email protected]b0b3abd92010-04-30 17:00:09223 FilePath path = extension_path_.DirName().AppendASCII(
224 filenames::kDecodedImagesFilename);
[email protected]facd7a7652009-06-05 23:15:02225 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
226 pickle.size())) {
227 SetError("Could not write image data to disk.");
228 return false;
229 }
230
231 return true;
232}
233
[email protected]6d377142010-03-17 20:36:05234bool ExtensionUnpacker::DumpMessageCatalogsToFile() {
235 IPC::Message pickle;
236 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
237
238 FilePath path = extension_path_.DirName().AppendASCII(
[email protected]b0b3abd92010-04-30 17:00:09239 filenames::kDecodedMessageCatalogsFilename);
[email protected]6d377142010-03-17 20:36:05240 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
241 pickle.size())) {
242 SetError("Could not write message catalogs to disk.");
243 return false;
244 }
245
246 return true;
247}
248
[email protected]facd7a7652009-06-05 23:15:02249// static
250bool ExtensionUnpacker::ReadImagesFromFile(const FilePath& extension_path,
251 DecodedImages* images) {
[email protected]b0b3abd92010-04-30 17:00:09252 FilePath path = extension_path.AppendASCII(filenames::kDecodedImagesFilename);
[email protected]facd7a7652009-06-05 23:15:02253 std::string file_str;
254 if (!file_util::ReadFileToString(path, &file_str))
255 return false;
256
257 IPC::Message pickle(file_str.data(), file_str.size());
[email protected]ce208f872012-03-07 20:42:56258 PickleIterator iter(pickle);
[email protected]facd7a7652009-06-05 23:15:02259 return IPC::ReadParam(&pickle, &iter, images);
260}
261
[email protected]6d377142010-03-17 20:36:05262// static
263bool ExtensionUnpacker::ReadMessageCatalogsFromFile(
264 const FilePath& extension_path, DictionaryValue* catalogs) {
[email protected]b0b3abd92010-04-30 17:00:09265 FilePath path = extension_path.AppendASCII(
266 filenames::kDecodedMessageCatalogsFilename);
[email protected]6d377142010-03-17 20:36:05267 std::string file_str;
268 if (!file_util::ReadFileToString(path, &file_str))
269 return false;
270
271 IPC::Message pickle(file_str.data(), file_str.size());
[email protected]ce208f872012-03-07 20:42:56272 PickleIterator iter(pickle);
[email protected]6d377142010-03-17 20:36:05273 return IPC::ReadParam(&pickle, &iter, catalogs);
274}
275
[email protected]902f7cd2009-05-22 19:02:19276bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) {
277 // Make sure it's not referencing a file outside the extension's subdir.
278 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
[email protected]fbcc40302009-06-12 20:45:45279 SetError(kPathNamesMustBeAbsoluteOrLocalError);
[email protected]902f7cd2009-05-22 19:02:19280 return false;
281 }
282
283 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
284 if (image_bitmap.isNull()) {
[email protected]fbcc40302009-06-12 20:45:45285 SetError(kCouldNotDecodeImageError);
[email protected]902f7cd2009-05-22 19:02:19286 return false;
287 }
288
289 decoded_images_.push_back(MakeTuple(image_bitmap, path));
[email protected]1fca1492009-05-15 22:23:43290 return true;
291}
292
[email protected]9428edc2009-11-18 18:02:47293bool ExtensionUnpacker::ReadMessageCatalog(const FilePath& message_path) {
294 std::string error;
295 JSONFileValueSerializer serializer(message_path);
[email protected]6d377142010-03-17 20:36:05296 scoped_ptr<DictionaryValue> root(
[email protected]ba399672010-04-06 15:42:39297 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, &error)));
[email protected]6d377142010-03-17 20:36:05298 if (!root.get()) {
[email protected]967d18b2011-03-02 22:22:07299 string16 messages_file = message_path.LossyDisplayName();
[email protected]9428edc2009-11-18 18:02:47300 if (error.empty()) {
301 // If file is missing, Deserialize will fail with empty error.
[email protected]93f10522010-10-31 16:27:48302 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
[email protected]967d18b2011-03-02 22:22:07303 UTF16ToUTF8(messages_file).c_str()));
[email protected]9428edc2009-11-18 18:02:47304 } else {
[email protected]967d18b2011-03-02 22:22:07305 SetError(base::StringPrintf("%s: %s",
306 UTF16ToUTF8(messages_file).c_str(),
[email protected]93f10522010-10-31 16:27:48307 error.c_str()));
[email protected]9428edc2009-11-18 18:02:47308 }
309 return false;
310 }
311
312 FilePath relative_path;
313 // message_path was created from temp_install_dir. This should never fail.
[email protected]967d18b2011-03-02 22:22:07314 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path)) {
[email protected]9428edc2009-11-18 18:02:47315 NOTREACHED();
[email protected]967d18b2011-03-02 22:22:07316 return false;
317 }
[email protected]9428edc2009-11-18 18:02:47318
[email protected]967d18b2011-03-02 22:22:07319 std::string dir_name = relative_path.DirName().MaybeAsASCII();
320 if (dir_name.empty()) {
321 NOTREACHED();
322 return false;
323 }
324 parsed_catalogs_->Set(dir_name, root.release());
[email protected]9428edc2009-11-18 18:02:47325
326 return true;
327}
328
[email protected]1fca1492009-05-15 22:23:43329void ExtensionUnpacker::SetError(const std::string &error) {
[email protected]fc670822011-12-17 09:33:49330 error_message_ = UTF8ToUTF16(error);
[email protected]1fca1492009-05-15 22:23:43331}