blob: e270870f24f0ddcb7e26ef8f88d8ee7e73fcf6d7 [file] [log] [blame]
[email protected]99922662010-08-17 16:24:251// Copyright (c) 2010 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
7#include "base/file_util.h"
8#include "base/scoped_handle.h"
9#include "base/scoped_temp_dir.h"
10#include "base/string_util.h"
[email protected]1fca1492009-05-15 22:23:4311#include "base/thread.h"
[email protected]99922662010-08-17 16:24:2512#include "base/utf_string_conversions.h"
[email protected]1fca1492009-05-15 22:23:4313#include "base/values.h"
14#include "net/base/file_stream.h"
[email protected]946d1b22009-07-22 23:57:2115#include "chrome/common/common_param_traits.h"
[email protected]5b1a0e22009-05-26 19:00:5816#include "chrome/common/extensions/extension.h"
[email protected]cb691e82009-07-13 14:59:0117#include "chrome/common/extensions/extension_constants.h"
[email protected]7c927b62010-02-24 09:54:1318#include "chrome/common/extensions/extension_file_util.h"
[email protected]1acbb4b62010-03-09 17:52:2919#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]1fca1492009-05-15 22:23:4320#include "chrome/common/json_value_serializer.h"
21#include "chrome/common/notification_service.h"
[email protected]1fca1492009-05-15 22:23:4322#include "chrome/common/url_constants.h"
[email protected]facd7a7652009-06-05 23:15:0223#include "chrome/common/zip.h"
[email protected]946d1b22009-07-22 23:57:2124#include "ipc/ipc_message_utils.h"
[email protected]902f7cd2009-05-22 19:02:1925#include "third_party/skia/include/core/SkBitmap.h"
26#include "webkit/glue/image_decoder.h"
[email protected]1fca1492009-05-15 22:23:4327
[email protected]9428edc2009-11-18 18:02:4728namespace errors = extension_manifest_errors;
29namespace keys = extension_manifest_keys;
[email protected]b0b3abd92010-04-30 17:00:0930namespace filenames = extension_filenames;
[email protected]9428edc2009-11-18 18:02:4731
[email protected]1fca1492009-05-15 22:23:4332namespace {
[email protected]6d377142010-03-17 20:36:0533
[email protected]fbcc40302009-06-12 20:45:4534// Errors
35const char* kCouldNotCreateDirectoryError =
[email protected]2d6bda732010-04-06 01:51:4336 "Could not create directory for unzipping: ";
[email protected]fbcc40302009-06-12 20:45:4537const char* kCouldNotDecodeImageError = "Could not decode theme image.";
38const char* kCouldNotUnzipExtension = "Could not unzip extension.";
39const char* kPathNamesMustBeAbsoluteOrLocalError =
40 "Path names must not be absolute or contain '..'.";
[email protected]1fca1492009-05-15 22:23:4341
[email protected]fcfd12f2009-08-14 22:20:4642// A limit to stop us passing dangerously large canvases to the browser.
43const int kMaxImageCanvas = 4096 * 4096;
44
[email protected]3bb84992010-08-26 17:23:4645SkBitmap DecodeImage(const FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:1946 // Read the file from disk.
47 std::string file_contents;
48 if (!file_util::PathExists(path) ||
49 !file_util::ReadFileToString(path, &file_contents)) {
50 return SkBitmap();
51 }
52
53 // Decode the image using WebKit's image decoder.
54 const unsigned char* data =
55 reinterpret_cast<const unsigned char*>(file_contents.data());
56 webkit_glue::ImageDecoder decoder;
[email protected]fcfd12f2009-08-14 22:20:4657 SkBitmap bitmap = decoder.Decode(data, file_contents.length());
58 Sk64 bitmap_size = bitmap.getSize64();
59 if (!bitmap_size.is32() || bitmap_size.get32() > kMaxImageCanvas)
60 return SkBitmap();
61 return bitmap;
[email protected]902f7cd2009-05-22 19:02:1962}
63
[email protected]3bb84992010-08-26 17:23:4664bool PathContainsParentDirectory(const FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:1965 const FilePath::StringType kSeparators(FilePath::kSeparators);
66 const FilePath::StringType kParentDirectory(FilePath::kParentDirectory);
67 const size_t npos = FilePath::StringType::npos;
68 const FilePath::StringType& value = path.value();
69
70 for (size_t i = 0; i < value.length(); ) {
71 i = value.find(kParentDirectory, i);
72 if (i != npos) {
73 if ((i == 0 || kSeparators.find(value[i-1]) == npos) &&
74 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
75 return true;
76 }
77 ++i;
78 }
79 }
80
81 return false;
[email protected]1fca1492009-05-15 22:23:4382}
83
[email protected]3bb84992010-08-26 17:23:4684} // namespace
85
86ExtensionUnpacker::ExtensionUnpacker(const FilePath& extension_path)
87 : extension_path_(extension_path) {
88}
89
90ExtensionUnpacker::~ExtensionUnpacker() {
91}
92
[email protected]902f7cd2009-05-22 19:02:1993DictionaryValue* ExtensionUnpacker::ReadManifest() {
94 FilePath manifest_path =
[email protected]99efb7b12009-12-18 02:39:1695 temp_install_dir_.Append(Extension::kManifestFilename);
[email protected]902f7cd2009-05-22 19:02:1996 if (!file_util::PathExists(manifest_path)) {
[email protected]9428edc2009-11-18 18:02:4797 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:1998 return NULL;
99 }
100
101 JSONFileValueSerializer serializer(manifest_path);
102 std::string error;
[email protected]ba399672010-04-06 15:42:39103 scoped_ptr<Value> root(serializer.Deserialize(NULL, &error));
[email protected]902f7cd2009-05-22 19:02:19104 if (!root.get()) {
105 SetError(error);
106 return NULL;
107 }
108
109 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]9428edc2009-11-18 18:02:47110 SetError(errors::kInvalidManifest);
[email protected]902f7cd2009-05-22 19:02:19111 return NULL;
112 }
113
114 return static_cast<DictionaryValue*>(root.release());
115}
116
[email protected]9428edc2009-11-18 18:02:47117bool ExtensionUnpacker::ReadAllMessageCatalogs(
118 const std::string& default_locale) {
119 FilePath locales_path =
[email protected]99efb7b12009-12-18 02:39:16120 temp_install_dir_.Append(Extension::kLocaleFolder);
[email protected]9428edc2009-11-18 18:02:47121
[email protected]1acbb4b62010-03-09 17:52:29122 // Not all folders under _locales have to be valid locales.
[email protected]9428edc2009-11-18 18:02:47123 file_util::FileEnumerator locales(locales_path,
124 false,
125 file_util::FileEnumerator::DIRECTORIES);
126
[email protected]1acbb4b62010-03-09 17:52:29127 std::set<std::string> all_locales;
128 extension_l10n_util::GetAllLocales(&all_locales);
129 FilePath locale_path;
130 while (!(locale_path = locales.Next()).empty()) {
131 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
132 all_locales))
[email protected]9428edc2009-11-18 18:02:47133 continue;
134
135 FilePath messages_path =
[email protected]99efb7b12009-12-18 02:39:16136 locale_path.Append(Extension::kMessagesFilename);
[email protected]9428edc2009-11-18 18:02:47137
138 if (!ReadMessageCatalog(messages_path))
139 return false;
[email protected]1acbb4b62010-03-09 17:52:29140 }
[email protected]9428edc2009-11-18 18:02:47141
142 return true;
143}
144
[email protected]1fca1492009-05-15 22:23:43145bool ExtensionUnpacker::Run() {
[email protected]11c2688a2010-10-22 00:58:58146 VLOG(1) << "Installing extension " << extension_path_.value();
[email protected]1fca1492009-05-15 22:23:43147
[email protected]1fca1492009-05-15 22:23:43148 // <profile>/Extensions/INSTALL_TEMP/<version>
[email protected]902f7cd2009-05-22 19:02:19149 temp_install_dir_ =
[email protected]b0b3abd92010-04-30 17:00:09150 extension_path_.DirName().AppendASCII(filenames::kTempExtensionName);
[email protected]baedfdf2010-06-14 18:50:19151
[email protected]baedfdf2010-06-14 18:50:19152 if (!file_util::CreateDirectory(temp_install_dir_)) {
[email protected]075be5d2010-09-24 15:39:39153
154#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]4777bc562009-06-01 02:53:00164 if (!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(
180 temp_install_dir_, Extension::INVALID, *parsed_manifest_, false, &error));
181 if (!extension.get()) {
[email protected]902f7cd2009-05-22 19:02:19182 SetError(error);
183 return false;
184 }
[email protected]99872e32009-09-25 22:02:49185
[email protected]66e4eb32010-10-27 20:37:41186 if (!extension_file_util::ValidateExtension(extension.get(), &error)) {
[email protected]99872e32009-09-25 22:02:49187 SetError(error);
188 return false;
189 }
190
[email protected]902f7cd2009-05-22 19:02:19191 // Decode any images that the browser needs to display.
[email protected]66e4eb32010-10-27 20:37:41192 std::set<FilePath> image_paths = extension->GetBrowserImages();
[email protected]facd7a7652009-06-05 23:15:02193 for (std::set<FilePath>::iterator it = image_paths.begin();
194 it != image_paths.end(); ++it) {
195 if (!AddDecodedImage(*it))
[email protected]902f7cd2009-05-22 19:02:19196 return false; // Error was already reported.
197 }
198
[email protected]9428edc2009-11-18 18:02:47199 // Parse all message catalogs (if any).
200 parsed_catalogs_.reset(new DictionaryValue);
[email protected]66e4eb32010-10-27 20:37:41201 if (!extension->default_locale().empty()) {
202 if (!ReadAllMessageCatalogs(extension->default_locale()))
[email protected]9428edc2009-11-18 18:02:47203 return false; // Error was already reported.
204 }
205
[email protected]902f7cd2009-05-22 19:02:19206 return true;
207}
208
[email protected]facd7a7652009-06-05 23:15:02209bool ExtensionUnpacker::DumpImagesToFile() {
210 IPC::Message pickle; // We use a Message so we can use WriteParam.
211 IPC::WriteParam(&pickle, decoded_images_);
212
[email protected]b0b3abd92010-04-30 17:00:09213 FilePath path = extension_path_.DirName().AppendASCII(
214 filenames::kDecodedImagesFilename);
[email protected]facd7a7652009-06-05 23:15:02215 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
216 pickle.size())) {
217 SetError("Could not write image data to disk.");
218 return false;
219 }
220
221 return true;
222}
223
[email protected]6d377142010-03-17 20:36:05224bool ExtensionUnpacker::DumpMessageCatalogsToFile() {
225 IPC::Message pickle;
226 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
227
228 FilePath path = extension_path_.DirName().AppendASCII(
[email protected]b0b3abd92010-04-30 17:00:09229 filenames::kDecodedMessageCatalogsFilename);
[email protected]6d377142010-03-17 20:36:05230 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
231 pickle.size())) {
232 SetError("Could not write message catalogs to disk.");
233 return false;
234 }
235
236 return true;
237}
238
[email protected]facd7a7652009-06-05 23:15:02239// static
240bool ExtensionUnpacker::ReadImagesFromFile(const FilePath& extension_path,
241 DecodedImages* images) {
[email protected]b0b3abd92010-04-30 17:00:09242 FilePath path = extension_path.AppendASCII(filenames::kDecodedImagesFilename);
[email protected]facd7a7652009-06-05 23:15:02243 std::string file_str;
244 if (!file_util::ReadFileToString(path, &file_str))
245 return false;
246
247 IPC::Message pickle(file_str.data(), file_str.size());
248 void* iter = NULL;
249 return IPC::ReadParam(&pickle, &iter, images);
250}
251
[email protected]6d377142010-03-17 20:36:05252// static
253bool ExtensionUnpacker::ReadMessageCatalogsFromFile(
254 const FilePath& extension_path, DictionaryValue* catalogs) {
[email protected]b0b3abd92010-04-30 17:00:09255 FilePath path = extension_path.AppendASCII(
256 filenames::kDecodedMessageCatalogsFilename);
[email protected]6d377142010-03-17 20:36:05257 std::string file_str;
258 if (!file_util::ReadFileToString(path, &file_str))
259 return false;
260
261 IPC::Message pickle(file_str.data(), file_str.size());
262 void* iter = NULL;
263 return IPC::ReadParam(&pickle, &iter, catalogs);
264}
265
[email protected]902f7cd2009-05-22 19:02:19266bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) {
267 // Make sure it's not referencing a file outside the extension's subdir.
268 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
[email protected]fbcc40302009-06-12 20:45:45269 SetError(kPathNamesMustBeAbsoluteOrLocalError);
[email protected]902f7cd2009-05-22 19:02:19270 return false;
271 }
272
273 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
274 if (image_bitmap.isNull()) {
[email protected]fbcc40302009-06-12 20:45:45275 SetError(kCouldNotDecodeImageError);
[email protected]902f7cd2009-05-22 19:02:19276 return false;
277 }
278
279 decoded_images_.push_back(MakeTuple(image_bitmap, path));
[email protected]1fca1492009-05-15 22:23:43280 return true;
281}
282
[email protected]9428edc2009-11-18 18:02:47283bool ExtensionUnpacker::ReadMessageCatalog(const FilePath& message_path) {
284 std::string error;
285 JSONFileValueSerializer serializer(message_path);
[email protected]6d377142010-03-17 20:36:05286 scoped_ptr<DictionaryValue> root(
[email protected]ba399672010-04-06 15:42:39287 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, &error)));
[email protected]6d377142010-03-17 20:36:05288 if (!root.get()) {
[email protected]9428edc2009-11-18 18:02:47289 std::string messages_file = WideToASCII(message_path.ToWStringHack());
290 if (error.empty()) {
291 // If file is missing, Deserialize will fail with empty error.
[email protected]93f10522010-10-31 16:27:48292 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
293 messages_file.c_str()));
[email protected]9428edc2009-11-18 18:02:47294 } else {
[email protected]93f10522010-10-31 16:27:48295 SetError(base::StringPrintf("%s: %s", messages_file.c_str(),
296 error.c_str()));
[email protected]9428edc2009-11-18 18:02:47297 }
298 return false;
299 }
300
301 FilePath relative_path;
302 // message_path was created from temp_install_dir. This should never fail.
303 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path))
304 NOTREACHED();
305
[email protected]99922662010-08-17 16:24:25306 parsed_catalogs_->Set(WideToUTF8(relative_path.DirName().ToWStringHack()),
[email protected]6d377142010-03-17 20:36:05307 root.release());
[email protected]9428edc2009-11-18 18:02:47308
309 return true;
310}
311
[email protected]1fca1492009-05-15 22:23:43312void ExtensionUnpacker::SetError(const std::string &error) {
313 error_message_ = error;
314}