blob: caeaf6ebadb88f5196186c0378b97cef23feae0b [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
[email protected]c34381df2013-07-11 16:14:425#include "chrome/utility/extensions/unpacker.h"
[email protected]1fca1492009-05-15 22:23:436
[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]25a4c1c2013-06-08 04:53:3610#include "base/files/file_enumerator.h"
[email protected]ea1a3f62012-11-16 20:34:2311#include "base/files/scoped_temp_dir.h"
[email protected]3367f3a2012-09-01 02:40:0612#include "base/i18n/rtl.h"
[email protected]ffbec692012-02-26 20:26:4213#include "base/json/json_file_value_serializer.h"
[email protected]3b63f8f42011-03-28 01:54:1514#include "base/memory/scoped_handle.h"
[email protected]fc006cac2013-09-17 22:43:3115#include "base/safe_numerics.h"
[email protected]3c8a6b02013-06-11 00:49:4916#include "base/strings/string_util.h"
[email protected]12bfb612013-06-07 19:54:0217#include "base/strings/utf_string_conversions.h"
[email protected]34b99632011-01-01 01:01:0618#include "base/threading/thread.h"
[email protected]1fca1492009-05-15 22:23:4319#include "base/values.h"
[email protected]43c05d902013-07-10 21:27:0020#include "chrome/common/chrome_utility_messages.h"
[email protected]0273969d2013-01-30 22:16:3121#include "chrome/common/extensions/api/i18n/default_locale_handler.h"
[email protected]5b1a0e22009-05-26 19:00:5822#include "chrome/common/extensions/extension.h"
[email protected]7c927b62010-02-24 09:54:1323#include "chrome/common/extensions/extension_file_util.h"
[email protected]1acbb4b62010-03-09 17:52:2924#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]e689cf252013-06-26 18:21:1425#include "content/public/child/image_decoder_utils.h"
[email protected]19a5c7442011-10-21 20:00:4126#include "content/public/common/common_param_traits.h"
[email protected]993da5e2013-03-23 21:25:1627#include "extensions/common/constants.h"
[email protected]d42c1112013-08-22 19:36:3228#include "extensions/common/manifest.h"
[email protected]0c3c9732013-09-16 08:53:4129#include "extensions/common/manifest_constants.h"
[email protected]3367f3a2012-09-01 02:40:0630#include "grit/generated_resources.h"
[email protected]946d1b22009-07-22 23:57:2131#include "ipc/ipc_message_utils.h"
[email protected]e0785902011-05-19 23:34:1732#include "net/base/file_stream.h"
[email protected]902f7cd2009-05-22 19:02:1933#include "third_party/skia/include/core/SkBitmap.h"
[email protected]4170d3a2013-05-03 23:02:5734#include "third_party/zlib/google/zip.h"
[email protected]3367f3a2012-09-01 02:40:0635#include "ui/base/l10n/l10n_util.h"
[email protected]e689cf252013-06-26 18:21:1436#include "ui/gfx/size.h"
[email protected]1fca1492009-05-15 22:23:4337
[email protected]0c3c9732013-09-16 08:53:4138namespace extensions {
[email protected]9428edc2009-11-18 18:02:4739
[email protected]1fca1492009-05-15 22:23:4340namespace {
[email protected]6d377142010-03-17 20:36:0541
[email protected]0c3c9732013-09-16 08:53:4142namespace errors = manifest_errors;
43namespace keys = manifest_keys;
44
[email protected]fcfd12f2009-08-14 22:20:4645// A limit to stop us passing dangerously large canvases to the browser.
46const int kMaxImageCanvas = 4096 * 4096;
47
[email protected]a7329162013-02-07 19:21:4848SkBitmap DecodeImage(const base::FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:1949 // Read the file from disk.
50 std::string file_contents;
[email protected]7567484142013-07-11 17:36:0751 if (!base::PathExists(path) ||
[email protected]82f84b92013-08-30 18:23:5052 !base::ReadFileToString(path, &file_contents)) {
[email protected]902f7cd2009-05-22 19:02:1953 return SkBitmap();
54 }
55
56 // Decode the image using WebKit's image decoder.
57 const unsigned char* data =
58 reinterpret_cast<const unsigned char*>(file_contents.data());
[email protected]e689cf252013-06-26 18:21:1459 SkBitmap bitmap = content::DecodeImage(data,
60 gfx::Size(),
61 file_contents.length());
[email protected]fcfd12f2009-08-14 22:20:4662 Sk64 bitmap_size = bitmap.getSize64();
63 if (!bitmap_size.is32() || bitmap_size.get32() > kMaxImageCanvas)
64 return SkBitmap();
65 return bitmap;
[email protected]902f7cd2009-05-22 19:02:1966}
67
[email protected]a7329162013-02-07 19:21:4868bool PathContainsParentDirectory(const base::FilePath& path) {
69 const base::FilePath::StringType kSeparators(base::FilePath::kSeparators);
70 const base::FilePath::StringType kParentDirectory(
71 base::FilePath::kParentDirectory);
72 const size_t npos = base::FilePath::StringType::npos;
73 const base::FilePath::StringType& value = path.value();
[email protected]902f7cd2009-05-22 19:02:1974
75 for (size_t i = 0; i < value.length(); ) {
76 i = value.find(kParentDirectory, i);
77 if (i != npos) {
78 if ((i == 0 || kSeparators.find(value[i-1]) == npos) &&
79 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
80 return true;
81 }
82 ++i;
83 }
84 }
85
86 return false;
[email protected]1fca1492009-05-15 22:23:4387}
88
[email protected]fc006cac2013-09-17 22:43:3189bool WritePickle(const IPC::Message& pickle, const base::FilePath& dest_path) {
90 int size = base::checked_numeric_cast<int>(pickle.size());
91 const char* data = static_cast<const char*>(pickle.data());
92 int bytes_written = file_util::WriteFile(dest_path, data, size);
93 return (bytes_written == size);
94}
95
[email protected]3bb84992010-08-26 17:23:4696} // namespace
97
[email protected]43c05d902013-07-10 21:27:0098struct Unpacker::InternalData {
99 DecodedImages decoded_images;
100};
101
[email protected]a7329162013-02-07 19:21:48102Unpacker::Unpacker(const base::FilePath& extension_path,
[email protected]b3fe68d2012-07-16 19:14:39103 const std::string& extension_id,
[email protected]1d5e58b2013-01-31 08:41:40104 Manifest::Location location,
[email protected]b3fe68d2012-07-16 19:14:39105 int creation_flags)
[email protected]f5bf1842012-02-15 02:52:26106 : extension_path_(extension_path),
107 extension_id_(extension_id),
108 location_(location),
[email protected]fc38935a2011-10-31 23:53:28109 creation_flags_(creation_flags) {
[email protected]43c05d902013-07-10 21:27:00110 internal_data_.reset(new InternalData());
[email protected]3bb84992010-08-26 17:23:46111}
112
[email protected]b3fe68d2012-07-16 19:14:39113Unpacker::~Unpacker() {
[email protected]3bb84992010-08-26 17:23:46114}
115
[email protected]a371b4b2013-06-18 20:29:27116base::DictionaryValue* Unpacker::ReadManifest() {
[email protected]a7329162013-02-07 19:21:48117 base::FilePath manifest_path =
[email protected]993da5e2013-03-23 21:25:16118 temp_install_dir_.Append(kManifestFilename);
[email protected]7567484142013-07-11 17:36:07119 if (!base::PathExists(manifest_path)) {
[email protected]9428edc2009-11-18 18:02:47120 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19121 return NULL;
122 }
123
124 JSONFileValueSerializer serializer(manifest_path);
125 std::string error;
[email protected]a371b4b2013-06-18 20:29:27126 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
[email protected]902f7cd2009-05-22 19:02:19127 if (!root.get()) {
128 SetError(error);
129 return NULL;
130 }
131
[email protected]a371b4b2013-06-18 20:29:27132 if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
[email protected]9428edc2009-11-18 18:02:47133 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19134 return NULL;
135 }
136
[email protected]a371b4b2013-06-18 20:29:27137 return static_cast<base::DictionaryValue*>(root.release());
[email protected]902f7cd2009-05-22 19:02:19138}
139
[email protected]b3fe68d2012-07-16 19:14:39140bool Unpacker::ReadAllMessageCatalogs(const std::string& default_locale) {
[email protected]a7329162013-02-07 19:21:48141 base::FilePath locales_path =
[email protected]993da5e2013-03-23 21:25:16142 temp_install_dir_.Append(kLocaleFolder);
[email protected]9428edc2009-11-18 18:02:47143
[email protected]1acbb4b62010-03-09 17:52:29144 // Not all folders under _locales have to be valid locales.
[email protected]25a4c1c2013-06-08 04:53:36145 base::FileEnumerator locales(locales_path,
146 false,
147 base::FileEnumerator::DIRECTORIES);
[email protected]9428edc2009-11-18 18:02:47148
[email protected]1acbb4b62010-03-09 17:52:29149 std::set<std::string> all_locales;
150 extension_l10n_util::GetAllLocales(&all_locales);
[email protected]a7329162013-02-07 19:21:48151 base::FilePath locale_path;
[email protected]1acbb4b62010-03-09 17:52:29152 while (!(locale_path = locales.Next()).empty()) {
153 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
154 all_locales))
[email protected]9428edc2009-11-18 18:02:47155 continue;
156
[email protected]993da5e2013-03-23 21:25:16157 base::FilePath messages_path = locale_path.Append(kMessagesFilename);
[email protected]9428edc2009-11-18 18:02:47158
159 if (!ReadMessageCatalog(messages_path))
160 return false;
[email protected]1acbb4b62010-03-09 17:52:29161 }
[email protected]9428edc2009-11-18 18:02:47162
163 return true;
164}
165
[email protected]b3fe68d2012-07-16 19:14:39166bool Unpacker::Run() {
[email protected]26f025e2011-10-28 22:49:27167 DVLOG(1) << "Installing extension " << extension_path_.value();
[email protected]1fca1492009-05-15 22:23:43168
[email protected]3367f3a2012-09-01 02:40:06169 // <profile>/Extensions/CRX_INSTALL
[email protected]902f7cd2009-05-22 19:02:19170 temp_install_dir_ =
[email protected]b22c8af62013-07-23 23:17:02171 extension_path_.DirName().AppendASCII(kTempExtensionName);
[email protected]baedfdf2010-06-14 18:50:19172
[email protected]baedfdf2010-06-14 18:50:19173 if (!file_util::CreateDirectory(temp_install_dir_)) {
[email protected]3367f3a2012-09-01 02:40:06174 SetUTF16Error(
175 l10n_util::GetStringFUTF16(
176 IDS_EXTENSION_PACKAGE_DIRECTORY_ERROR,
177 base::i18n::GetDisplayStringInLTRDirectionality(
178 temp_install_dir_.LossyDisplayName())));
[email protected]1fca1492009-05-15 22:23:43179 return false;
180 }
181
[email protected]b3eb3dd22011-10-26 06:59:34182 if (!zip::Unzip(extension_path_, temp_install_dir_)) {
[email protected]3367f3a2012-09-01 02:40:06183 SetUTF16Error(l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
[email protected]1fca1492009-05-15 22:23:43184 return false;
185 }
186
[email protected]902f7cd2009-05-22 19:02:19187 // Parse the manifest.
188 parsed_manifest_.reset(ReadManifest());
[email protected]a999d672012-01-23 22:31:40189 if (!parsed_manifest_.get())
[email protected]902f7cd2009-05-22 19:02:19190 return false; // Error was already reported.
[email protected]af1277b2009-07-28 00:47:53191
[email protected]fbcc40302009-06-12 20:45:45192 std::string error;
[email protected]1e0f45a2012-06-13 00:31:06193 scoped_refptr<Extension> extension(Extension::Create(
[email protected]542258c2011-03-04 21:25:31194 temp_install_dir_,
[email protected]fc38935a2011-10-31 23:53:28195 location_,
[email protected]542258c2011-03-04 21:25:31196 *parsed_manifest_,
[email protected]fc38935a2011-10-31 23:53:28197 creation_flags_,
[email protected]f5bf1842012-02-15 02:52:26198 extension_id_,
[email protected]542258c2011-03-04 21:25:31199 &error));
[email protected]66e4eb32010-10-27 20:37:41200 if (!extension.get()) {
[email protected]902f7cd2009-05-22 19:02:19201 SetError(error);
202 return false;
203 }
[email protected]99872e32009-09-25 22:02:49204
[email protected]1d5e58b2013-01-31 08:41:40205 std::vector<InstallWarning> warnings;
[email protected]ab55c2b2012-06-01 23:55:03206 if (!extension_file_util::ValidateExtension(extension.get(),
207 &error, &warnings)) {
[email protected]99872e32009-09-25 22:02:49208 SetError(error);
209 return false;
210 }
[email protected]ab55c2b2012-06-01 23:55:03211 extension->AddInstallWarnings(warnings);
[email protected]99872e32009-09-25 22:02:49212
[email protected]902f7cd2009-05-22 19:02:19213 // Decode any images that the browser needs to display.
[email protected]72b49d42013-04-19 12:47:31214 std::set<base::FilePath> image_paths =
[email protected]5c6ac842013-06-02 23:37:03215 extension_file_util::GetBrowserImagePaths(extension.get());
[email protected]a7329162013-02-07 19:21:48216 for (std::set<base::FilePath>::iterator it = image_paths.begin();
[email protected]5c6ac842013-06-02 23:37:03217 it != image_paths.end();
218 ++it) {
[email protected]facd7a7652009-06-05 23:15:02219 if (!AddDecodedImage(*it))
[email protected]902f7cd2009-05-22 19:02:19220 return false; // Error was already reported.
221 }
222
[email protected]9428edc2009-11-18 18:02:47223 // Parse all message catalogs (if any).
[email protected]a371b4b2013-06-18 20:29:27224 parsed_catalogs_.reset(new base::DictionaryValue);
[email protected]5c6ac842013-06-02 23:37:03225 if (!LocaleInfo::GetDefaultLocale(extension.get()).empty()) {
226 if (!ReadAllMessageCatalogs(LocaleInfo::GetDefaultLocale(extension.get())))
[email protected]9428edc2009-11-18 18:02:47227 return false; // Error was already reported.
228 }
229
[email protected]902f7cd2009-05-22 19:02:19230 return true;
231}
232
[email protected]b3fe68d2012-07-16 19:14:39233bool Unpacker::DumpImagesToFile() {
[email protected]facd7a7652009-06-05 23:15:02234 IPC::Message pickle; // We use a Message so we can use WriteParam.
[email protected]43c05d902013-07-10 21:27:00235 IPC::WriteParam(&pickle, internal_data_->decoded_images);
[email protected]facd7a7652009-06-05 23:15:02236
[email protected]a7329162013-02-07 19:21:48237 base::FilePath path = extension_path_.DirName().AppendASCII(
[email protected]b22c8af62013-07-23 23:17:02238 kDecodedImagesFilename);
[email protected]fc006cac2013-09-17 22:43:31239 if (!WritePickle(pickle, path)) {
[email protected]facd7a7652009-06-05 23:15:02240 SetError("Could not write image data to disk.");
241 return false;
242 }
243
244 return true;
245}
246
[email protected]b3fe68d2012-07-16 19:14:39247bool Unpacker::DumpMessageCatalogsToFile() {
[email protected]6d377142010-03-17 20:36:05248 IPC::Message pickle;
249 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
250
[email protected]a7329162013-02-07 19:21:48251 base::FilePath path = extension_path_.DirName().AppendASCII(
[email protected]b22c8af62013-07-23 23:17:02252 kDecodedMessageCatalogsFilename);
[email protected]fc006cac2013-09-17 22:43:31253 if (!WritePickle(pickle, path)) {
[email protected]6d377142010-03-17 20:36:05254 SetError("Could not write message catalogs to disk.");
255 return false;
256 }
257
258 return true;
259}
260
[email protected]a7329162013-02-07 19:21:48261bool Unpacker::AddDecodedImage(const base::FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:19262 // Make sure it's not referencing a file outside the extension's subdir.
263 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
[email protected]3367f3a2012-09-01 02:40:06264 SetUTF16Error(
265 l10n_util::GetStringFUTF16(
266 IDS_EXTENSION_PACKAGE_IMAGE_PATH_ERROR,
267 base::i18n::GetDisplayStringInLTRDirectionality(
268 path.LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19269 return false;
270 }
271
272 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
273 if (image_bitmap.isNull()) {
[email protected]3367f3a2012-09-01 02:40:06274 SetUTF16Error(
275 l10n_util::GetStringFUTF16(
276 IDS_EXTENSION_PACKAGE_IMAGE_ERROR,
277 base::i18n::GetDisplayStringInLTRDirectionality(
278 path.BaseName().LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19279 return false;
280 }
281
[email protected]43c05d902013-07-10 21:27:00282 internal_data_->decoded_images.push_back(MakeTuple(image_bitmap, path));
[email protected]1fca1492009-05-15 22:23:43283 return true;
284}
285
[email protected]a7329162013-02-07 19:21:48286bool Unpacker::ReadMessageCatalog(const base::FilePath& message_path) {
[email protected]9428edc2009-11-18 18:02:47287 std::string error;
288 JSONFileValueSerializer serializer(message_path);
[email protected]a371b4b2013-06-18 20:29:27289 scoped_ptr<base::DictionaryValue> root(static_cast<base::DictionaryValue*>(
290 serializer.Deserialize(NULL, &error)));
[email protected]6d377142010-03-17 20:36:05291 if (!root.get()) {
[email protected]967d18b2011-03-02 22:22:07292 string16 messages_file = message_path.LossyDisplayName();
[email protected]9428edc2009-11-18 18:02:47293 if (error.empty()) {
294 // If file is missing, Deserialize will fail with empty error.
[email protected]93f10522010-10-31 16:27:48295 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
[email protected]967d18b2011-03-02 22:22:07296 UTF16ToUTF8(messages_file).c_str()));
[email protected]9428edc2009-11-18 18:02:47297 } else {
[email protected]967d18b2011-03-02 22:22:07298 SetError(base::StringPrintf("%s: %s",
299 UTF16ToUTF8(messages_file).c_str(),
[email protected]93f10522010-10-31 16:27:48300 error.c_str()));
[email protected]9428edc2009-11-18 18:02:47301 }
302 return false;
303 }
304
[email protected]a7329162013-02-07 19:21:48305 base::FilePath relative_path;
[email protected]9428edc2009-11-18 18:02:47306 // message_path was created from temp_install_dir. This should never fail.
[email protected]967d18b2011-03-02 22:22:07307 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path)) {
[email protected]9428edc2009-11-18 18:02:47308 NOTREACHED();
[email protected]967d18b2011-03-02 22:22:07309 return false;
310 }
[email protected]9428edc2009-11-18 18:02:47311
[email protected]967d18b2011-03-02 22:22:07312 std::string dir_name = relative_path.DirName().MaybeAsASCII();
313 if (dir_name.empty()) {
314 NOTREACHED();
315 return false;
316 }
317 parsed_catalogs_->Set(dir_name, root.release());
[email protected]9428edc2009-11-18 18:02:47318
319 return true;
320}
321
[email protected]b3fe68d2012-07-16 19:14:39322void Unpacker::SetError(const std::string &error) {
[email protected]3367f3a2012-09-01 02:40:06323 SetUTF16Error(UTF8ToUTF16(error));
324}
325
326void Unpacker::SetUTF16Error(const string16 &error) {
327 error_message_ = error;
[email protected]1fca1492009-05-15 22:23:43328}
[email protected]b3fe68d2012-07-16 19:14:39329
330} // namespace extensions