blob: 3c729a47c745d54338f97c27930af78364b53895 [file] [log] [blame]
[email protected]3b63f8f42011-03-28 01:54:151// Copyright (c) 2011 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]005bab02011-10-07 18:36:0010#include "base/json/json_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]cb691e82009-07-13 14:59:0118#include "chrome/common/extensions/extension_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
87ExtensionUnpacker::ExtensionUnpacker(const FilePath& extension_path)
88 : extension_path_(extension_path) {
89}
90
91ExtensionUnpacker::~ExtensionUnpacker() {
92}
93
[email protected]902f7cd2009-05-22 19:02:1994DictionaryValue* ExtensionUnpacker::ReadManifest() {
95 FilePath manifest_path =
[email protected]99efb7b12009-12-18 02:39:1696 temp_install_dir_.Append(Extension::kManifestFilename);
[email protected]902f7cd2009-05-22 19:02:1997 if (!file_util::PathExists(manifest_path)) {
[email protected]9428edc2009-11-18 18:02:4798 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:1999 return NULL;
100 }
101
102 JSONFileValueSerializer serializer(manifest_path);
103 std::string error;
[email protected]ba399672010-04-06 15:42:39104 scoped_ptr<Value> root(serializer.Deserialize(NULL, &error));
[email protected]902f7cd2009-05-22 19:02:19105 if (!root.get()) {
106 SetError(error);
107 return NULL;
108 }
109
110 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]9428edc2009-11-18 18:02:47111 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19112 return NULL;
113 }
114
115 return static_cast<DictionaryValue*>(root.release());
116}
117
[email protected]9428edc2009-11-18 18:02:47118bool ExtensionUnpacker::ReadAllMessageCatalogs(
119 const std::string& default_locale) {
120 FilePath locales_path =
[email protected]99efb7b12009-12-18 02:39:16121 temp_install_dir_.Append(Extension::kLocaleFolder);
[email protected]9428edc2009-11-18 18:02:47122
[email protected]1acbb4b62010-03-09 17:52:29123 // Not all folders under _locales have to be valid locales.
[email protected]9428edc2009-11-18 18:02:47124 file_util::FileEnumerator locales(locales_path,
125 false,
126 file_util::FileEnumerator::DIRECTORIES);
127
[email protected]1acbb4b62010-03-09 17:52:29128 std::set<std::string> all_locales;
129 extension_l10n_util::GetAllLocales(&all_locales);
130 FilePath locale_path;
131 while (!(locale_path = locales.Next()).empty()) {
132 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
133 all_locales))
[email protected]9428edc2009-11-18 18:02:47134 continue;
135
136 FilePath messages_path =
[email protected]99efb7b12009-12-18 02:39:16137 locale_path.Append(Extension::kMessagesFilename);
[email protected]9428edc2009-11-18 18:02:47138
139 if (!ReadMessageCatalog(messages_path))
140 return false;
[email protected]1acbb4b62010-03-09 17:52:29141 }
[email protected]9428edc2009-11-18 18:02:47142
143 return true;
144}
145
[email protected]1fca1492009-05-15 22:23:43146bool ExtensionUnpacker::Run() {
[email protected]26f025e2011-10-28 22:49:27147 DVLOG(1) << "Installing extension " << extension_path_.value();
[email protected]1fca1492009-05-15 22:23:43148
[email protected]1fca1492009-05-15 22:23:43149 // <profile>/Extensions/INSTALL_TEMP/<version>
[email protected]902f7cd2009-05-22 19:02:19150 temp_install_dir_ =
[email protected]b0b3abd92010-04-30 17:00:09151 extension_path_.DirName().AppendASCII(filenames::kTempExtensionName);
[email protected]baedfdf2010-06-14 18:50:19152
[email protected]baedfdf2010-06-14 18:50:19153 if (!file_util::CreateDirectory(temp_install_dir_)) {
[email protected]075be5d2010-09-24 15:39:39154#if defined(OS_WIN)
155 std::string dir_string = WideToUTF8(temp_install_dir_.value());
156#else
[email protected]2d6bda732010-04-06 01:51:43157 std::string dir_string = temp_install_dir_.value();
[email protected]075be5d2010-09-24 15:39:39158#endif
159
[email protected]2d6bda732010-04-06 01:51:43160 SetError(kCouldNotCreateDirectoryError + dir_string);
[email protected]1fca1492009-05-15 22:23:43161 return false;
162 }
163
[email protected]b3eb3dd22011-10-26 06:59:34164 if (!zip::Unzip(extension_path_, temp_install_dir_)) {
[email protected]fbcc40302009-06-12 20:45:45165 SetError(kCouldNotUnzipExtension);
[email protected]1fca1492009-05-15 22:23:43166 return false;
167 }
168
[email protected]902f7cd2009-05-22 19:02:19169 // Parse the manifest.
170 parsed_manifest_.reset(ReadManifest());
171 if (!parsed_manifest_.get())
172 return false; // Error was already reported.
[email protected]af1277b2009-07-28 00:47:53173
[email protected]075be5d2010-09-24 15:39:39174 // NOTE: Since the unpacker doesn't have the extension's public_id, the
[email protected]fbcc40302009-06-12 20:45:45175 // InitFromValue is allowed to generate a temporary id for the extension.
176 // ANY CODE THAT FOLLOWS SHOULD NOT DEPEND ON THE CORRECT ID OF THIS
177 // EXTENSION.
[email protected]fbcc40302009-06-12 20:45:45178 std::string error;
[email protected]66e4eb32010-10-27 20:37:41179 scoped_refptr<Extension> extension(Extension::Create(
[email protected]542258c2011-03-04 21:25:31180 temp_install_dir_,
181 Extension::INVALID,
182 *parsed_manifest_,
[email protected]83048a22011-03-29 00:14:13183 Extension::NO_FLAGS,
[email protected]542258c2011-03-04 21:25:31184 &error));
[email protected]66e4eb32010-10-27 20:37:41185 if (!extension.get()) {
[email protected]902f7cd2009-05-22 19:02:19186 SetError(error);
187 return false;
188 }
[email protected]99872e32009-09-25 22:02:49189
[email protected]66e4eb32010-10-27 20:37:41190 if (!extension_file_util::ValidateExtension(extension.get(), &error)) {
[email protected]99872e32009-09-25 22:02:49191 SetError(error);
192 return false;
193 }
194
[email protected]902f7cd2009-05-22 19:02:19195 // Decode any images that the browser needs to display.
[email protected]66e4eb32010-10-27 20:37:41196 std::set<FilePath> image_paths = extension->GetBrowserImages();
[email protected]facd7a7652009-06-05 23:15:02197 for (std::set<FilePath>::iterator it = image_paths.begin();
198 it != image_paths.end(); ++it) {
199 if (!AddDecodedImage(*it))
[email protected]902f7cd2009-05-22 19:02:19200 return false; // Error was already reported.
201 }
202
[email protected]9428edc2009-11-18 18:02:47203 // Parse all message catalogs (if any).
204 parsed_catalogs_.reset(new DictionaryValue);
[email protected]66e4eb32010-10-27 20:37:41205 if (!extension->default_locale().empty()) {
206 if (!ReadAllMessageCatalogs(extension->default_locale()))
[email protected]9428edc2009-11-18 18:02:47207 return false; // Error was already reported.
208 }
209
[email protected]902f7cd2009-05-22 19:02:19210 return true;
211}
212
[email protected]facd7a7652009-06-05 23:15:02213bool ExtensionUnpacker::DumpImagesToFile() {
214 IPC::Message pickle; // We use a Message so we can use WriteParam.
215 IPC::WriteParam(&pickle, decoded_images_);
216
[email protected]b0b3abd92010-04-30 17:00:09217 FilePath path = extension_path_.DirName().AppendASCII(
218 filenames::kDecodedImagesFilename);
[email protected]facd7a7652009-06-05 23:15:02219 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
220 pickle.size())) {
221 SetError("Could not write image data to disk.");
222 return false;
223 }
224
225 return true;
226}
227
[email protected]6d377142010-03-17 20:36:05228bool ExtensionUnpacker::DumpMessageCatalogsToFile() {
229 IPC::Message pickle;
230 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
231
232 FilePath path = extension_path_.DirName().AppendASCII(
[email protected]b0b3abd92010-04-30 17:00:09233 filenames::kDecodedMessageCatalogsFilename);
[email protected]6d377142010-03-17 20:36:05234 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
235 pickle.size())) {
236 SetError("Could not write message catalogs to disk.");
237 return false;
238 }
239
240 return true;
241}
242
[email protected]facd7a7652009-06-05 23:15:02243// static
244bool ExtensionUnpacker::ReadImagesFromFile(const FilePath& extension_path,
245 DecodedImages* images) {
[email protected]b0b3abd92010-04-30 17:00:09246 FilePath path = extension_path.AppendASCII(filenames::kDecodedImagesFilename);
[email protected]facd7a7652009-06-05 23:15:02247 std::string file_str;
248 if (!file_util::ReadFileToString(path, &file_str))
249 return false;
250
251 IPC::Message pickle(file_str.data(), file_str.size());
252 void* iter = NULL;
253 return IPC::ReadParam(&pickle, &iter, images);
254}
255
[email protected]6d377142010-03-17 20:36:05256// static
257bool ExtensionUnpacker::ReadMessageCatalogsFromFile(
258 const FilePath& extension_path, DictionaryValue* catalogs) {
[email protected]b0b3abd92010-04-30 17:00:09259 FilePath path = extension_path.AppendASCII(
260 filenames::kDecodedMessageCatalogsFilename);
[email protected]6d377142010-03-17 20:36:05261 std::string file_str;
262 if (!file_util::ReadFileToString(path, &file_str))
263 return false;
264
265 IPC::Message pickle(file_str.data(), file_str.size());
266 void* iter = NULL;
267 return IPC::ReadParam(&pickle, &iter, catalogs);
268}
269
[email protected]902f7cd2009-05-22 19:02:19270bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) {
271 // Make sure it's not referencing a file outside the extension's subdir.
272 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
[email protected]fbcc40302009-06-12 20:45:45273 SetError(kPathNamesMustBeAbsoluteOrLocalError);
[email protected]902f7cd2009-05-22 19:02:19274 return false;
275 }
276
277 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
278 if (image_bitmap.isNull()) {
[email protected]fbcc40302009-06-12 20:45:45279 SetError(kCouldNotDecodeImageError);
[email protected]902f7cd2009-05-22 19:02:19280 return false;
281 }
282
283 decoded_images_.push_back(MakeTuple(image_bitmap, path));
[email protected]1fca1492009-05-15 22:23:43284 return true;
285}
286
[email protected]9428edc2009-11-18 18:02:47287bool ExtensionUnpacker::ReadMessageCatalog(const FilePath& message_path) {
288 std::string error;
289 JSONFileValueSerializer serializer(message_path);
[email protected]6d377142010-03-17 20:36:05290 scoped_ptr<DictionaryValue> root(
[email protected]ba399672010-04-06 15:42:39291 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, &error)));
[email protected]6d377142010-03-17 20:36:05292 if (!root.get()) {
[email protected]967d18b2011-03-02 22:22:07293 string16 messages_file = message_path.LossyDisplayName();
[email protected]9428edc2009-11-18 18:02:47294 if (error.empty()) {
295 // If file is missing, Deserialize will fail with empty error.
[email protected]93f10522010-10-31 16:27:48296 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
[email protected]967d18b2011-03-02 22:22:07297 UTF16ToUTF8(messages_file).c_str()));
[email protected]9428edc2009-11-18 18:02:47298 } else {
[email protected]967d18b2011-03-02 22:22:07299 SetError(base::StringPrintf("%s: %s",
300 UTF16ToUTF8(messages_file).c_str(),
[email protected]93f10522010-10-31 16:27:48301 error.c_str()));
[email protected]9428edc2009-11-18 18:02:47302 }
303 return false;
304 }
305
306 FilePath relative_path;
307 // message_path was created from temp_install_dir. This should never fail.
[email protected]967d18b2011-03-02 22:22:07308 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path)) {
[email protected]9428edc2009-11-18 18:02:47309 NOTREACHED();
[email protected]967d18b2011-03-02 22:22:07310 return false;
311 }
[email protected]9428edc2009-11-18 18:02:47312
[email protected]967d18b2011-03-02 22:22:07313 std::string dir_name = relative_path.DirName().MaybeAsASCII();
314 if (dir_name.empty()) {
315 NOTREACHED();
316 return false;
317 }
318 parsed_catalogs_->Set(dir_name, root.release());
[email protected]9428edc2009-11-18 18:02:47319
320 return true;
321}
322
[email protected]1fca1492009-05-15 22:23:43323void ExtensionUnpacker::SetError(const std::string &error) {
324 error_message_ = error;
325}