blob: 3620f38800b93d96d1041966442f25fc0cb3f681 [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
asargent9156f0292015-01-15 01:07:025#include "extensions/utility/unpacker.h"
[email protected]1fca1492009-05-15 22:23:436
avi2d124c02015-12-23 06:36:427#include <stddef.h>
8
meacerbd735062016-09-28 19:58:379#include <algorithm>
[email protected]ccea03c2010-12-17 03:31:5010#include <set>
tzikb2f765b2016-03-09 00:51:4511#include <tuple>
dchenge59eca1602015-12-18 17:48:0012#include <utility>
[email protected]ccea03c2010-12-17 03:31:5013
[email protected]25a4c1c2013-06-08 04:53:3614#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0415#include "base/files/file_util.h"
[email protected]ea1a3f62012-11-16 20:34:2316#include "base/files/scoped_temp_dir.h"
[email protected]3367f3a2012-09-01 02:40:0617#include "base/i18n/rtl.h"
[email protected]ffbec692012-02-26 20:26:4218#include "base/json/json_file_value_serializer.h"
[email protected]cb154062014-01-17 03:32:4019#include "base/numerics/safe_conversions.h"
[email protected]3c8a6b02013-06-11 00:49:4920#include "base/strings/string_util.h"
[email protected]12bfb612013-06-07 19:54:0221#include "base/strings/utf_string_conversions.h"
[email protected]34b99632011-01-01 01:01:0622#include "base/threading/thread.h"
[email protected]1fca1492009-05-15 22:23:4323#include "base/values.h"
[email protected]e689cf252013-06-26 18:21:1424#include "content/public/child/image_decoder_utils.h"
[email protected]993da5e2013-03-23 21:25:1625#include "extensions/common/constants.h"
[email protected]e4452d32013-11-15 23:07:4126#include "extensions/common/extension.h"
[email protected]6668e5d2014-04-08 23:32:5227#include "extensions/common/extension_l10n_util.h"
asargent9156f0292015-01-15 01:07:0228#include "extensions/common/extension_utility_messages.h"
rockotec1e64b2014-11-13 22:06:5129#include "extensions/common/extensions_client.h"
[email protected]85df9d12014-04-15 17:02:1430#include "extensions/common/file_util.h"
[email protected]d42c1112013-08-22 19:36:3231#include "extensions/common/manifest.h"
[email protected]0c3c9732013-09-16 08:53:4132#include "extensions/common/manifest_constants.h"
rockote5fd3d92014-11-13 00:21:2233#include "extensions/common/manifest_handlers/default_locale_handler.h"
asargent9156f0292015-01-15 01:07:0234#include "extensions/strings/grit/extensions_strings.h"
[email protected]946d1b22009-07-22 23:57:2135#include "ipc/ipc_message_utils.h"
[email protected]e0785902011-05-19 23:34:1736#include "net/base/file_stream.h"
[email protected]902f7cd2009-05-22 19:02:1937#include "third_party/skia/include/core/SkBitmap.h"
[email protected]3367f3a2012-09-01 02:40:0638#include "ui/base/l10n/l10n_util.h"
tfarinaebe974f02015-01-03 04:25:3239#include "ui/gfx/geometry/size.h"
[email protected]1fca1492009-05-15 22:23:4340
[email protected]0c3c9732013-09-16 08:53:4141namespace extensions {
[email protected]9428edc2009-11-18 18:02:4742
[email protected]1fca1492009-05-15 22:23:4343namespace {
[email protected]6d377142010-03-17 20:36:0544
[email protected]0c3c9732013-09-16 08:53:4145namespace errors = manifest_errors;
46namespace keys = manifest_keys;
47
[email protected]fcfd12f2009-08-14 22:20:4648// A limit to stop us passing dangerously large canvases to the browser.
49const int kMaxImageCanvas = 4096 * 4096;
50
meacerbd735062016-09-28 19:58:3751static const base::FilePath::CharType* kAllowedThemeFiletypes[] = {
52 FILE_PATH_LITERAL(".bmp"), FILE_PATH_LITERAL(".gif"),
53 FILE_PATH_LITERAL(".jpeg"), FILE_PATH_LITERAL(".jpg"),
54 FILE_PATH_LITERAL(".json"), FILE_PATH_LITERAL(".png"),
55 FILE_PATH_LITERAL(".webp")};
56
[email protected]a7329162013-02-07 19:21:4857SkBitmap DecodeImage(const base::FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:1958 // Read the file from disk.
59 std::string file_contents;
[email protected]7567484142013-07-11 17:36:0760 if (!base::PathExists(path) ||
[email protected]82f84b92013-08-30 18:23:5061 !base::ReadFileToString(path, &file_contents)) {
[email protected]902f7cd2009-05-22 19:02:1962 return SkBitmap();
63 }
64
65 // Decode the image using WebKit's image decoder.
66 const unsigned char* data =
67 reinterpret_cast<const unsigned char*>(file_contents.data());
asargent9156f0292015-01-15 01:07:0268 SkBitmap bitmap =
69 content::DecodeImage(data, gfx::Size(), file_contents.length());
[email protected]72ed0b8502014-01-03 18:53:0270 if (bitmap.computeSize64() > kMaxImageCanvas)
[email protected]fcfd12f2009-08-14 22:20:4671 return SkBitmap();
72 return bitmap;
[email protected]902f7cd2009-05-22 19:02:1973}
74
[email protected]a7329162013-02-07 19:21:4875bool PathContainsParentDirectory(const base::FilePath& path) {
76 const base::FilePath::StringType kSeparators(base::FilePath::kSeparators);
77 const base::FilePath::StringType kParentDirectory(
78 base::FilePath::kParentDirectory);
79 const size_t npos = base::FilePath::StringType::npos;
80 const base::FilePath::StringType& value = path.value();
[email protected]902f7cd2009-05-22 19:02:1981
asargent9156f0292015-01-15 01:07:0282 for (size_t i = 0; i < value.length();) {
[email protected]902f7cd2009-05-22 19:02:1983 i = value.find(kParentDirectory, i);
84 if (i != npos) {
asargent9156f0292015-01-15 01:07:0285 if ((i == 0 || kSeparators.find(value[i - 1]) == npos) &&
86 (i + 1 < value.length() || kSeparators.find(value[i + 1]) == npos)) {
[email protected]902f7cd2009-05-22 19:02:1987 return true;
88 }
89 ++i;
90 }
91 }
92
93 return false;
[email protected]1fca1492009-05-15 22:23:4394}
95
[email protected]fc006cac2013-09-17 22:43:3196bool WritePickle(const IPC::Message& pickle, const base::FilePath& dest_path) {
[email protected]cb154062014-01-17 03:32:4097 int size = base::checked_cast<int>(pickle.size());
[email protected]fc006cac2013-09-17 22:43:3198 const char* data = static_cast<const char*>(pickle.data());
[email protected]e5c2a22e2014-03-06 20:42:3099 int bytes_written = base::WriteFile(dest_path, data, size);
[email protected]fc006cac2013-09-17 22:43:31100 return (bytes_written == size);
101}
102
[email protected]3bb84992010-08-26 17:23:46103} // namespace
104
[email protected]43c05d902013-07-10 21:27:00105struct Unpacker::InternalData {
106 DecodedImages decoded_images;
107};
108
asargentcac815112015-06-08 18:52:50109Unpacker::Unpacker(const base::FilePath& working_dir,
110 const base::FilePath& extension_dir,
[email protected]b3fe68d2012-07-16 19:14:39111 const std::string& extension_id,
[email protected]1d5e58b2013-01-31 08:41:40112 Manifest::Location location,
[email protected]b3fe68d2012-07-16 19:14:39113 int creation_flags)
asargentcac815112015-06-08 18:52:50114 : working_dir_(working_dir),
115 extension_dir_(extension_dir),
[email protected]f5bf1842012-02-15 02:52:26116 extension_id_(extension_id),
117 location_(location),
[email protected]fc38935a2011-10-31 23:53:28118 creation_flags_(creation_flags) {
[email protected]43c05d902013-07-10 21:27:00119 internal_data_.reset(new InternalData());
[email protected]3bb84992010-08-26 17:23:46120}
121
[email protected]b3fe68d2012-07-16 19:14:39122Unpacker::~Unpacker() {
[email protected]3bb84992010-08-26 17:23:46123}
124
meacerb040d3a2016-09-09 03:20:20125// static
meacerbd735062016-09-28 19:58:37126bool Unpacker::ShouldExtractFile(bool is_theme,
127 const base::FilePath& file_path) {
128 if (is_theme) {
129 const base::FilePath::StringType extension =
130 base::ToLowerASCII(file_path.FinalExtension());
131 // Allow filenames with no extension.
132 if (extension.empty())
133 return true;
134 return std::find(kAllowedThemeFiletypes,
135 kAllowedThemeFiletypes + arraysize(kAllowedThemeFiletypes),
136 extension) !=
137 (kAllowedThemeFiletypes + arraysize(kAllowedThemeFiletypes));
138 }
meacerb040d3a2016-09-09 03:20:20139 return !base::FilePath::CompareEqualIgnoreCase(file_path.FinalExtension(),
140 FILE_PATH_LITERAL(".exe"));
141}
142
meacerbd735062016-09-28 19:58:37143// static
144bool Unpacker::IsManifestFile(const base::FilePath& file_path) {
145 CHECK(!file_path.IsAbsolute());
146 return base::FilePath::CompareEqualIgnoreCase(file_path.value(),
147 kManifestFilename);
148}
149
150// static
151std::unique_ptr<base::DictionaryValue> Unpacker::ReadManifest(
152 const base::FilePath& extension_dir,
153 std::string* error) {
154 DCHECK(error);
155 base::FilePath manifest_path = extension_dir.Append(kManifestFilename);
[email protected]7567484142013-07-11 17:36:07156 if (!base::PathExists(manifest_path)) {
meacerbd735062016-09-28 19:58:37157 *error = errors::kInvalidManifest;
158 return nullptr;
[email protected]902f7cd2009-05-22 19:02:19159 }
160
prashhir54a994502015-03-05 09:30:57161 JSONFileValueDeserializer deserializer(manifest_path);
meacerbd735062016-09-28 19:58:37162 std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, error);
[email protected]902f7cd2009-05-22 19:02:19163 if (!root.get()) {
meacerbd735062016-09-28 19:58:37164 return nullptr;
[email protected]902f7cd2009-05-22 19:02:19165 }
166
[email protected]a371b4b2013-06-18 20:29:27167 if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
meacerbd735062016-09-28 19:58:37168 *error = errors::kInvalidManifest;
169 return nullptr;
[email protected]902f7cd2009-05-22 19:02:19170 }
171
dchenge59eca1602015-12-18 17:48:00172 return base::DictionaryValue::From(std::move(root));
[email protected]902f7cd2009-05-22 19:02:19173}
174
[email protected]b3fe68d2012-07-16 19:14:39175bool Unpacker::ReadAllMessageCatalogs(const std::string& default_locale) {
asargentcac815112015-06-08 18:52:50176 base::FilePath locales_path = extension_dir_.Append(kLocaleFolder);
[email protected]9428edc2009-11-18 18:02:47177
[email protected]1acbb4b62010-03-09 17:52:29178 // Not all folders under _locales have to be valid locales.
asargent9156f0292015-01-15 01:07:02179 base::FileEnumerator locales(locales_path, false,
[email protected]25a4c1c2013-06-08 04:53:36180 base::FileEnumerator::DIRECTORIES);
[email protected]9428edc2009-11-18 18:02:47181
[email protected]1acbb4b62010-03-09 17:52:29182 std::set<std::string> all_locales;
183 extension_l10n_util::GetAllLocales(&all_locales);
[email protected]a7329162013-02-07 19:21:48184 base::FilePath locale_path;
[email protected]1acbb4b62010-03-09 17:52:29185 while (!(locale_path = locales.Next()).empty()) {
186 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
187 all_locales))
[email protected]9428edc2009-11-18 18:02:47188 continue;
189
[email protected]993da5e2013-03-23 21:25:16190 base::FilePath messages_path = locale_path.Append(kMessagesFilename);
[email protected]9428edc2009-11-18 18:02:47191
192 if (!ReadMessageCatalog(messages_path))
193 return false;
[email protected]1acbb4b62010-03-09 17:52:29194 }
[email protected]9428edc2009-11-18 18:02:47195
196 return true;
197}
198
[email protected]b3fe68d2012-07-16 19:14:39199bool Unpacker::Run() {
[email protected]902f7cd2009-05-22 19:02:19200 // Parse the manifest.
dschuyler3fe4bfc2016-09-28 01:11:29201 std::string error;
meacerbd735062016-09-28 19:58:37202 parsed_manifest_ = ReadManifest(extension_dir_, &error);
203 if (!parsed_manifest_.get()) {
204 SetError(error);
205 return false;
206 }
207
asargent9156f0292015-01-15 01:07:02208 scoped_refptr<Extension> extension(
asargentcac815112015-06-08 18:52:50209 Extension::Create(extension_dir_, location_, *parsed_manifest_,
asargent9156f0292015-01-15 01:07:02210 creation_flags_, extension_id_, &error));
[email protected]66e4eb32010-10-27 20:37:41211 if (!extension.get()) {
[email protected]902f7cd2009-05-22 19:02:19212 SetError(error);
213 return false;
214 }
[email protected]99872e32009-09-25 22:02:49215
[email protected]1d5e58b2013-01-31 08:41:40216 std::vector<InstallWarning> warnings;
[email protected]85df9d12014-04-15 17:02:14217 if (!file_util::ValidateExtension(extension.get(), &error, &warnings)) {
[email protected]99872e32009-09-25 22:02:49218 SetError(error);
219 return false;
220 }
[email protected]ab55c2b2012-06-01 23:55:03221 extension->AddInstallWarnings(warnings);
[email protected]99872e32009-09-25 22:02:49222
[email protected]902f7cd2009-05-22 19:02:19223 // Decode any images that the browser needs to display.
[email protected]72b49d42013-04-19 12:47:31224 std::set<base::FilePath> image_paths =
rockotec1e64b2014-11-13 22:06:51225 ExtensionsClient::Get()->GetBrowserImagePaths(extension.get());
estade82eaf582015-12-29 17:51:31226 for (const base::FilePath& path : image_paths) {
227 if (!AddDecodedImage(path))
[email protected]902f7cd2009-05-22 19:02:19228 return false; // Error was already reported.
229 }
230
[email protected]9428edc2009-11-18 18:02:47231 // Parse all message catalogs (if any).
[email protected]a371b4b2013-06-18 20:29:27232 parsed_catalogs_.reset(new base::DictionaryValue);
[email protected]5c6ac842013-06-02 23:37:03233 if (!LocaleInfo::GetDefaultLocale(extension.get()).empty()) {
234 if (!ReadAllMessageCatalogs(LocaleInfo::GetDefaultLocale(extension.get())))
[email protected]9428edc2009-11-18 18:02:47235 return false; // Error was already reported.
236 }
237
asargentcac815112015-06-08 18:52:50238 return DumpImagesToFile() && DumpMessageCatalogsToFile();
[email protected]902f7cd2009-05-22 19:02:19239}
240
[email protected]b3fe68d2012-07-16 19:14:39241bool Unpacker::DumpImagesToFile() {
[email protected]facd7a7652009-06-05 23:15:02242 IPC::Message pickle; // We use a Message so we can use WriteParam.
[email protected]43c05d902013-07-10 21:27:00243 IPC::WriteParam(&pickle, internal_data_->decoded_images);
[email protected]facd7a7652009-06-05 23:15:02244
asargentcac815112015-06-08 18:52:50245 base::FilePath path = working_dir_.AppendASCII(kDecodedImagesFilename);
[email protected]fc006cac2013-09-17 22:43:31246 if (!WritePickle(pickle, path)) {
[email protected]facd7a7652009-06-05 23:15:02247 SetError("Could not write image data to disk.");
248 return false;
249 }
250
251 return true;
252}
253
[email protected]b3fe68d2012-07-16 19:14:39254bool Unpacker::DumpMessageCatalogsToFile() {
[email protected]6d377142010-03-17 20:36:05255 IPC::Message pickle;
256 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
257
asargent9156f0292015-01-15 01:07:02258 base::FilePath path =
asargentcac815112015-06-08 18:52:50259 working_dir_.AppendASCII(kDecodedMessageCatalogsFilename);
[email protected]fc006cac2013-09-17 22:43:31260 if (!WritePickle(pickle, path)) {
[email protected]6d377142010-03-17 20:36:05261 SetError("Could not write message catalogs to disk.");
262 return false;
263 }
264
265 return true;
266}
267
[email protected]a7329162013-02-07 19:21:48268bool Unpacker::AddDecodedImage(const base::FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:19269 // Make sure it's not referencing a file outside the extension's subdir.
270 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
asargent9156f0292015-01-15 01:07:02271 SetUTF16Error(l10n_util::GetStringFUTF16(
272 IDS_EXTENSION_PACKAGE_IMAGE_PATH_ERROR,
273 base::i18n::GetDisplayStringInLTRDirectionality(
274 path.LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19275 return false;
276 }
277
asargentcac815112015-06-08 18:52:50278 SkBitmap image_bitmap = DecodeImage(extension_dir_.Append(path));
[email protected]902f7cd2009-05-22 19:02:19279 if (image_bitmap.isNull()) {
asargent9156f0292015-01-15 01:07:02280 SetUTF16Error(l10n_util::GetStringFUTF16(
281 IDS_EXTENSION_PACKAGE_IMAGE_ERROR,
282 base::i18n::GetDisplayStringInLTRDirectionality(
283 path.BaseName().LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19284 return false;
285 }
286
tzikb2f765b2016-03-09 00:51:45287 internal_data_->decoded_images.push_back(std::make_tuple(image_bitmap, path));
[email protected]1fca1492009-05-15 22:23:43288 return true;
289}
290
[email protected]a7329162013-02-07 19:21:48291bool Unpacker::ReadMessageCatalog(const base::FilePath& message_path) {
[email protected]9428edc2009-11-18 18:02:47292 std::string error;
prashhir54a994502015-03-05 09:30:57293 JSONFileValueDeserializer deserializer(message_path);
dcheng4a7e93b2016-04-23 00:27:16294 std::unique_ptr<base::DictionaryValue> root =
olli.raulaba045252015-10-16 06:16:40295 base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
[email protected]6d377142010-03-17 20:36:05296 if (!root.get()) {
[email protected]05aab99c2013-12-20 09:03:15297 base::string16 messages_file = message_path.LossyDisplayName();
[email protected]9428edc2009-11-18 18:02:47298 if (error.empty()) {
299 // If file is missing, Deserialize will fail with empty error.
[email protected]93f10522010-10-31 16:27:48300 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
[email protected]c91b9d42013-12-25 00:53:42301 base::UTF16ToUTF8(messages_file).c_str()));
[email protected]9428edc2009-11-18 18:02:47302 } else {
asargent9156f0292015-01-15 01:07:02303 SetError(base::StringPrintf(
304 "%s: %s", base::UTF16ToUTF8(messages_file).c_str(), error.c_str()));
[email protected]9428edc2009-11-18 18:02:47305 }
306 return false;
307 }
308
[email protected]a7329162013-02-07 19:21:48309 base::FilePath relative_path;
[email protected]9428edc2009-11-18 18:02:47310 // message_path was created from temp_install_dir. This should never fail.
asargentcac815112015-06-08 18:52:50311 if (!extension_dir_.AppendRelativePath(message_path, &relative_path)) {
[email protected]9428edc2009-11-18 18:02:47312 NOTREACHED();
[email protected]967d18b2011-03-02 22:22:07313 return false;
314 }
[email protected]9428edc2009-11-18 18:02:47315
[email protected]967d18b2011-03-02 22:22:07316 std::string dir_name = relative_path.DirName().MaybeAsASCII();
317 if (dir_name.empty()) {
318 NOTREACHED();
319 return false;
320 }
321 parsed_catalogs_->Set(dir_name, root.release());
[email protected]9428edc2009-11-18 18:02:47322
323 return true;
324}
325
asargent9156f0292015-01-15 01:07:02326void Unpacker::SetError(const std::string& error) {
[email protected]c91b9d42013-12-25 00:53:42327 SetUTF16Error(base::UTF8ToUTF16(error));
[email protected]3367f3a2012-09-01 02:40:06328}
329
[email protected]05aab99c2013-12-20 09:03:15330void Unpacker::SetUTF16Error(const base::string16& error) {
[email protected]3367f3a2012-09-01 02:40:06331 error_message_ = error;
[email protected]1fca1492009-05-15 22:23:43332}
[email protected]b3fe68d2012-07-16 19:14:39333
334} // namespace extensions