blob: 9a92d90fbaa9fb21cf64e7253e5328ba0a924252 [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]25a4c1c2013-06-08 04:53:369#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0410#include "base/files/file_util.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]cb154062014-01-17 03:32:4014#include "base/numerics/safe_conversions.h"
[email protected]3c8a6b02013-06-11 00:49:4915#include "base/strings/string_util.h"
[email protected]12bfb612013-06-07 19:54:0216#include "base/strings/utf_string_conversions.h"
[email protected]34b99632011-01-01 01:01:0617#include "base/threading/thread.h"
[email protected]1fca1492009-05-15 22:23:4318#include "base/values.h"
[email protected]43c05d902013-07-10 21:27:0019#include "chrome/common/chrome_utility_messages.h"
[email protected]5ec0d4f2014-08-13 08:28:3020#include "chrome/grit/generated_resources.h"
[email protected]e689cf252013-06-26 18:21:1421#include "content/public/child/image_decoder_utils.h"
[email protected]19a5c7442011-10-21 20:00:4122#include "content/public/common/common_param_traits.h"
[email protected]993da5e2013-03-23 21:25:1623#include "extensions/common/constants.h"
[email protected]e4452d32013-11-15 23:07:4124#include "extensions/common/extension.h"
[email protected]6668e5d2014-04-08 23:32:5225#include "extensions/common/extension_l10n_util.h"
rockotec1e64b2014-11-13 22:06:5126#include "extensions/common/extensions_client.h"
[email protected]85df9d12014-04-15 17:02:1427#include "extensions/common/file_util.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"
rockote5fd3d92014-11-13 00:21:2230#include "extensions/common/manifest_handlers/default_locale_handler.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]72ed0b8502014-01-03 18:53:0262 if (bitmap.computeSize64() > kMaxImageCanvas)
[email protected]fcfd12f2009-08-14 22:20:4663 return SkBitmap();
64 return bitmap;
[email protected]902f7cd2009-05-22 19:02:1965}
66
[email protected]a7329162013-02-07 19:21:4867bool PathContainsParentDirectory(const base::FilePath& path) {
68 const base::FilePath::StringType kSeparators(base::FilePath::kSeparators);
69 const base::FilePath::StringType kParentDirectory(
70 base::FilePath::kParentDirectory);
71 const size_t npos = base::FilePath::StringType::npos;
72 const base::FilePath::StringType& value = path.value();
[email protected]902f7cd2009-05-22 19:02:1973
74 for (size_t i = 0; i < value.length(); ) {
75 i = value.find(kParentDirectory, i);
76 if (i != npos) {
77 if ((i == 0 || kSeparators.find(value[i-1]) == npos) &&
78 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
79 return true;
80 }
81 ++i;
82 }
83 }
84
85 return false;
[email protected]1fca1492009-05-15 22:23:4386}
87
[email protected]fc006cac2013-09-17 22:43:3188bool WritePickle(const IPC::Message& pickle, const base::FilePath& dest_path) {
[email protected]cb154062014-01-17 03:32:4089 int size = base::checked_cast<int>(pickle.size());
[email protected]fc006cac2013-09-17 22:43:3190 const char* data = static_cast<const char*>(pickle.data());
[email protected]e5c2a22e2014-03-06 20:42:3091 int bytes_written = base::WriteFile(dest_path, data, size);
[email protected]fc006cac2013-09-17 22:43:3192 return (bytes_written == size);
93}
94
[email protected]3bb84992010-08-26 17:23:4695} // namespace
96
[email protected]43c05d902013-07-10 21:27:0097struct Unpacker::InternalData {
98 DecodedImages decoded_images;
99};
100
[email protected]a7329162013-02-07 19:21:48101Unpacker::Unpacker(const base::FilePath& extension_path,
[email protected]b3fe68d2012-07-16 19:14:39102 const std::string& extension_id,
[email protected]1d5e58b2013-01-31 08:41:40103 Manifest::Location location,
[email protected]b3fe68d2012-07-16 19:14:39104 int creation_flags)
[email protected]f5bf1842012-02-15 02:52:26105 : extension_path_(extension_path),
106 extension_id_(extension_id),
107 location_(location),
[email protected]fc38935a2011-10-31 23:53:28108 creation_flags_(creation_flags) {
[email protected]43c05d902013-07-10 21:27:00109 internal_data_.reset(new InternalData());
[email protected]3bb84992010-08-26 17:23:46110}
111
[email protected]b3fe68d2012-07-16 19:14:39112Unpacker::~Unpacker() {
[email protected]3bb84992010-08-26 17:23:46113}
114
[email protected]a371b4b2013-06-18 20:29:27115base::DictionaryValue* Unpacker::ReadManifest() {
[email protected]a7329162013-02-07 19:21:48116 base::FilePath manifest_path =
[email protected]993da5e2013-03-23 21:25:16117 temp_install_dir_.Append(kManifestFilename);
[email protected]7567484142013-07-11 17:36:07118 if (!base::PathExists(manifest_path)) {
[email protected]9428edc2009-11-18 18:02:47119 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19120 return NULL;
121 }
122
123 JSONFileValueSerializer serializer(manifest_path);
124 std::string error;
[email protected]a371b4b2013-06-18 20:29:27125 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
[email protected]902f7cd2009-05-22 19:02:19126 if (!root.get()) {
127 SetError(error);
128 return NULL;
129 }
130
[email protected]a371b4b2013-06-18 20:29:27131 if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
[email protected]9428edc2009-11-18 18:02:47132 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19133 return NULL;
134 }
135
[email protected]a371b4b2013-06-18 20:29:27136 return static_cast<base::DictionaryValue*>(root.release());
[email protected]902f7cd2009-05-22 19:02:19137}
138
[email protected]b3fe68d2012-07-16 19:14:39139bool Unpacker::ReadAllMessageCatalogs(const std::string& default_locale) {
[email protected]a7329162013-02-07 19:21:48140 base::FilePath locales_path =
[email protected]993da5e2013-03-23 21:25:16141 temp_install_dir_.Append(kLocaleFolder);
[email protected]9428edc2009-11-18 18:02:47142
[email protected]1acbb4b62010-03-09 17:52:29143 // Not all folders under _locales have to be valid locales.
[email protected]25a4c1c2013-06-08 04:53:36144 base::FileEnumerator locales(locales_path,
145 false,
146 base::FileEnumerator::DIRECTORIES);
[email protected]9428edc2009-11-18 18:02:47147
[email protected]1acbb4b62010-03-09 17:52:29148 std::set<std::string> all_locales;
149 extension_l10n_util::GetAllLocales(&all_locales);
[email protected]a7329162013-02-07 19:21:48150 base::FilePath locale_path;
[email protected]1acbb4b62010-03-09 17:52:29151 while (!(locale_path = locales.Next()).empty()) {
152 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
153 all_locales))
[email protected]9428edc2009-11-18 18:02:47154 continue;
155
[email protected]993da5e2013-03-23 21:25:16156 base::FilePath messages_path = locale_path.Append(kMessagesFilename);
[email protected]9428edc2009-11-18 18:02:47157
158 if (!ReadMessageCatalog(messages_path))
159 return false;
[email protected]1acbb4b62010-03-09 17:52:29160 }
[email protected]9428edc2009-11-18 18:02:47161
162 return true;
163}
164
[email protected]b3fe68d2012-07-16 19:14:39165bool Unpacker::Run() {
[email protected]26f025e2011-10-28 22:49:27166 DVLOG(1) << "Installing extension " << extension_path_.value();
[email protected]1fca1492009-05-15 22:23:43167
[email protected]3367f3a2012-09-01 02:40:06168 // <profile>/Extensions/CRX_INSTALL
[email protected]902f7cd2009-05-22 19:02:19169 temp_install_dir_ =
[email protected]b22c8af62013-07-23 23:17:02170 extension_path_.DirName().AppendASCII(kTempExtensionName);
[email protected]baedfdf2010-06-14 18:50:19171
[email protected]426d1c92013-12-03 20:08:54172 if (!base::CreateDirectory(temp_install_dir_)) {
[email protected]3367f3a2012-09-01 02:40:06173 SetUTF16Error(
174 l10n_util::GetStringFUTF16(
175 IDS_EXTENSION_PACKAGE_DIRECTORY_ERROR,
176 base::i18n::GetDisplayStringInLTRDirectionality(
177 temp_install_dir_.LossyDisplayName())));
[email protected]1fca1492009-05-15 22:23:43178 return false;
179 }
180
[email protected]b3eb3dd22011-10-26 06:59:34181 if (!zip::Unzip(extension_path_, temp_install_dir_)) {
[email protected]3367f3a2012-09-01 02:40:06182 SetUTF16Error(l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
[email protected]1fca1492009-05-15 22:23:43183 return false;
184 }
185
[email protected]902f7cd2009-05-22 19:02:19186 // Parse the manifest.
187 parsed_manifest_.reset(ReadManifest());
[email protected]a999d672012-01-23 22:31:40188 if (!parsed_manifest_.get())
[email protected]902f7cd2009-05-22 19:02:19189 return false; // Error was already reported.
[email protected]af1277b2009-07-28 00:47:53190
[email protected]fbcc40302009-06-12 20:45:45191 std::string error;
[email protected]1e0f45a2012-06-13 00:31:06192 scoped_refptr<Extension> extension(Extension::Create(
[email protected]542258c2011-03-04 21:25:31193 temp_install_dir_,
[email protected]fc38935a2011-10-31 23:53:28194 location_,
[email protected]542258c2011-03-04 21:25:31195 *parsed_manifest_,
[email protected]fc38935a2011-10-31 23:53:28196 creation_flags_,
[email protected]f5bf1842012-02-15 02:52:26197 extension_id_,
[email protected]542258c2011-03-04 21:25:31198 &error));
[email protected]66e4eb32010-10-27 20:37:41199 if (!extension.get()) {
[email protected]902f7cd2009-05-22 19:02:19200 SetError(error);
201 return false;
202 }
[email protected]99872e32009-09-25 22:02:49203
[email protected]1d5e58b2013-01-31 08:41:40204 std::vector<InstallWarning> warnings;
[email protected]85df9d12014-04-15 17:02:14205 if (!file_util::ValidateExtension(extension.get(), &error, &warnings)) {
[email protected]99872e32009-09-25 22:02:49206 SetError(error);
207 return false;
208 }
[email protected]ab55c2b2012-06-01 23:55:03209 extension->AddInstallWarnings(warnings);
[email protected]99872e32009-09-25 22:02:49210
[email protected]902f7cd2009-05-22 19:02:19211 // Decode any images that the browser needs to display.
[email protected]72b49d42013-04-19 12:47:31212 std::set<base::FilePath> image_paths =
rockotec1e64b2014-11-13 22:06:51213 ExtensionsClient::Get()->GetBrowserImagePaths(extension.get());
[email protected]a7329162013-02-07 19:21:48214 for (std::set<base::FilePath>::iterator it = image_paths.begin();
[email protected]5c6ac842013-06-02 23:37:03215 it != image_paths.end();
216 ++it) {
[email protected]facd7a7652009-06-05 23:15:02217 if (!AddDecodedImage(*it))
[email protected]902f7cd2009-05-22 19:02:19218 return false; // Error was already reported.
219 }
220
[email protected]9428edc2009-11-18 18:02:47221 // Parse all message catalogs (if any).
[email protected]a371b4b2013-06-18 20:29:27222 parsed_catalogs_.reset(new base::DictionaryValue);
[email protected]5c6ac842013-06-02 23:37:03223 if (!LocaleInfo::GetDefaultLocale(extension.get()).empty()) {
224 if (!ReadAllMessageCatalogs(LocaleInfo::GetDefaultLocale(extension.get())))
[email protected]9428edc2009-11-18 18:02:47225 return false; // Error was already reported.
226 }
227
[email protected]902f7cd2009-05-22 19:02:19228 return true;
229}
230
[email protected]b3fe68d2012-07-16 19:14:39231bool Unpacker::DumpImagesToFile() {
[email protected]facd7a7652009-06-05 23:15:02232 IPC::Message pickle; // We use a Message so we can use WriteParam.
[email protected]43c05d902013-07-10 21:27:00233 IPC::WriteParam(&pickle, internal_data_->decoded_images);
[email protected]facd7a7652009-06-05 23:15:02234
[email protected]a7329162013-02-07 19:21:48235 base::FilePath path = extension_path_.DirName().AppendASCII(
[email protected]b22c8af62013-07-23 23:17:02236 kDecodedImagesFilename);
[email protected]fc006cac2013-09-17 22:43:31237 if (!WritePickle(pickle, path)) {
[email protected]facd7a7652009-06-05 23:15:02238 SetError("Could not write image data to disk.");
239 return false;
240 }
241
242 return true;
243}
244
[email protected]b3fe68d2012-07-16 19:14:39245bool Unpacker::DumpMessageCatalogsToFile() {
[email protected]6d377142010-03-17 20:36:05246 IPC::Message pickle;
247 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
248
[email protected]a7329162013-02-07 19:21:48249 base::FilePath path = extension_path_.DirName().AppendASCII(
[email protected]b22c8af62013-07-23 23:17:02250 kDecodedMessageCatalogsFilename);
[email protected]fc006cac2013-09-17 22:43:31251 if (!WritePickle(pickle, path)) {
[email protected]6d377142010-03-17 20:36:05252 SetError("Could not write message catalogs to disk.");
253 return false;
254 }
255
256 return true;
257}
258
[email protected]a7329162013-02-07 19:21:48259bool Unpacker::AddDecodedImage(const base::FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:19260 // Make sure it's not referencing a file outside the extension's subdir.
261 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
[email protected]3367f3a2012-09-01 02:40:06262 SetUTF16Error(
263 l10n_util::GetStringFUTF16(
264 IDS_EXTENSION_PACKAGE_IMAGE_PATH_ERROR,
265 base::i18n::GetDisplayStringInLTRDirectionality(
266 path.LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19267 return false;
268 }
269
270 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
271 if (image_bitmap.isNull()) {
[email protected]3367f3a2012-09-01 02:40:06272 SetUTF16Error(
273 l10n_util::GetStringFUTF16(
274 IDS_EXTENSION_PACKAGE_IMAGE_ERROR,
275 base::i18n::GetDisplayStringInLTRDirectionality(
276 path.BaseName().LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19277 return false;
278 }
279
[email protected]43c05d902013-07-10 21:27:00280 internal_data_->decoded_images.push_back(MakeTuple(image_bitmap, path));
[email protected]1fca1492009-05-15 22:23:43281 return true;
282}
283
[email protected]a7329162013-02-07 19:21:48284bool Unpacker::ReadMessageCatalog(const base::FilePath& message_path) {
[email protected]9428edc2009-11-18 18:02:47285 std::string error;
286 JSONFileValueSerializer serializer(message_path);
[email protected]a371b4b2013-06-18 20:29:27287 scoped_ptr<base::DictionaryValue> root(static_cast<base::DictionaryValue*>(
288 serializer.Deserialize(NULL, &error)));
[email protected]6d377142010-03-17 20:36:05289 if (!root.get()) {
[email protected]05aab99c2013-12-20 09:03:15290 base::string16 messages_file = message_path.LossyDisplayName();
[email protected]9428edc2009-11-18 18:02:47291 if (error.empty()) {
292 // If file is missing, Deserialize will fail with empty error.
[email protected]93f10522010-10-31 16:27:48293 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
[email protected]c91b9d42013-12-25 00:53:42294 base::UTF16ToUTF8(messages_file).c_str()));
[email protected]9428edc2009-11-18 18:02:47295 } else {
[email protected]967d18b2011-03-02 22:22:07296 SetError(base::StringPrintf("%s: %s",
[email protected]c91b9d42013-12-25 00:53:42297 base::UTF16ToUTF8(messages_file).c_str(),
[email protected]93f10522010-10-31 16:27:48298 error.c_str()));
[email protected]9428edc2009-11-18 18:02:47299 }
300 return false;
301 }
302
[email protected]a7329162013-02-07 19:21:48303 base::FilePath relative_path;
[email protected]9428edc2009-11-18 18:02:47304 // message_path was created from temp_install_dir. This should never fail.
[email protected]967d18b2011-03-02 22:22:07305 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path)) {
[email protected]9428edc2009-11-18 18:02:47306 NOTREACHED();
[email protected]967d18b2011-03-02 22:22:07307 return false;
308 }
[email protected]9428edc2009-11-18 18:02:47309
[email protected]967d18b2011-03-02 22:22:07310 std::string dir_name = relative_path.DirName().MaybeAsASCII();
311 if (dir_name.empty()) {
312 NOTREACHED();
313 return false;
314 }
315 parsed_catalogs_->Set(dir_name, root.release());
[email protected]9428edc2009-11-18 18:02:47316
317 return true;
318}
319
[email protected]b3fe68d2012-07-16 19:14:39320void Unpacker::SetError(const std::string &error) {
[email protected]c91b9d42013-12-25 00:53:42321 SetUTF16Error(base::UTF8ToUTF16(error));
[email protected]3367f3a2012-09-01 02:40:06322}
323
[email protected]05aab99c2013-12-20 09:03:15324void Unpacker::SetUTF16Error(const base::string16& error) {
[email protected]3367f3a2012-09-01 02:40:06325 error_message_ = error;
[email protected]1fca1492009-05-15 22:23:43326}
[email protected]b3fe68d2012-07-16 19:14:39327
328} // namespace extensions