1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // 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 <set>
8
9 #include "base/file_util.h"
10 #include "base/memory/scoped_handle.h"
11 #include "base/memory/scoped_temp_dir.h"
12 #include "base/string_util.h"
13 #include "base/threading/thread.h"
14 #include "base/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "net/base/file_stream.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "chrome/common/extensions/extension_file_util.h"
20 #include "chrome/common/extensions/extension_l10n_util.h"
21 #include "chrome/common/url_constants.h"
22 #include "chrome/common/zip.h"
23 #include "content/common/common_param_traits.h"
24 #include "content/common/json_value_serializer.h"
25 #include "ipc/ipc_message_utils.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "webkit/glue/image_decoder.h"
28
29 namespace errors = extension_manifest_errors;
30 namespace keys = extension_manifest_keys;
31 namespace filenames = extension_filenames;
32
33 namespace {
34
35 // Errors
36 const char* kCouldNotCreateDirectoryError =
37 "Could not create directory for unzipping: ";
38 const char* kCouldNotDecodeImageError = "Could not decode theme image.";
39 const char* kCouldNotUnzipExtension = "Could not unzip extension.";
40 const char* kPathNamesMustBeAbsoluteOrLocalError =
41 "Path names must not be absolute or contain '..'.";
42
43 // A limit to stop us passing dangerously large canvases to the browser.
44 const int kMaxImageCanvas = 4096 * 4096;
45
DecodeImage(const FilePath & path)46 SkBitmap DecodeImage(const FilePath& path) {
47 // 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;
58 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;
63 }
64
PathContainsParentDirectory(const FilePath & path)65 bool PathContainsParentDirectory(const FilePath& path) {
66 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;
83 }
84
85 } // namespace
86
ExtensionUnpacker(const FilePath & extension_path)87 ExtensionUnpacker::ExtensionUnpacker(const FilePath& extension_path)
88 : extension_path_(extension_path) {
89 }
90
~ExtensionUnpacker()91 ExtensionUnpacker::~ExtensionUnpacker() {
92 }
93
ReadManifest()94 DictionaryValue* ExtensionUnpacker::ReadManifest() {
95 FilePath manifest_path =
96 temp_install_dir_.Append(Extension::kManifestFilename);
97 if (!file_util::PathExists(manifest_path)) {
98 SetError(errors::kInvalidManifest);
99 return NULL;
100 }
101
102 JSONFileValueSerializer serializer(manifest_path);
103 std::string error;
104 scoped_ptr<Value> root(serializer.Deserialize(NULL, &error));
105 if (!root.get()) {
106 SetError(error);
107 return NULL;
108 }
109
110 if (!root->IsType(Value::TYPE_DICTIONARY)) {
111 SetError(errors::kInvalidManifest);
112 return NULL;
113 }
114
115 return static_cast<DictionaryValue*>(root.release());
116 }
117
ReadAllMessageCatalogs(const std::string & default_locale)118 bool ExtensionUnpacker::ReadAllMessageCatalogs(
119 const std::string& default_locale) {
120 FilePath locales_path =
121 temp_install_dir_.Append(Extension::kLocaleFolder);
122
123 // Not all folders under _locales have to be valid locales.
124 file_util::FileEnumerator locales(locales_path,
125 false,
126 file_util::FileEnumerator::DIRECTORIES);
127
128 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))
134 continue;
135
136 FilePath messages_path =
137 locale_path.Append(Extension::kMessagesFilename);
138
139 if (!ReadMessageCatalog(messages_path))
140 return false;
141 }
142
143 return true;
144 }
145
Run()146 bool ExtensionUnpacker::Run() {
147 VLOG(1) << "Installing extension " << extension_path_.value();
148
149 // <profile>/Extensions/INSTALL_TEMP/<version>
150 temp_install_dir_ =
151 extension_path_.DirName().AppendASCII(filenames::kTempExtensionName);
152
153 if (!file_util::CreateDirectory(temp_install_dir_)) {
154 #if defined(OS_WIN)
155 std::string dir_string = WideToUTF8(temp_install_dir_.value());
156 #else
157 std::string dir_string = temp_install_dir_.value();
158 #endif
159
160 SetError(kCouldNotCreateDirectoryError + dir_string);
161 return false;
162 }
163
164 if (!Unzip(extension_path_, temp_install_dir_)) {
165 SetError(kCouldNotUnzipExtension);
166 return false;
167 }
168
169 // Parse the manifest.
170 parsed_manifest_.reset(ReadManifest());
171 if (!parsed_manifest_.get())
172 return false; // Error was already reported.
173
174 // NOTE: Since the unpacker doesn't have the extension's public_id, the
175 // 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.
178 std::string error;
179 scoped_refptr<Extension> extension(Extension::Create(
180 temp_install_dir_,
181 Extension::INVALID,
182 *parsed_manifest_,
183 Extension::NO_FLAGS,
184 &error));
185 if (!extension.get()) {
186 SetError(error);
187 return false;
188 }
189
190 if (!extension_file_util::ValidateExtension(extension.get(), &error)) {
191 SetError(error);
192 return false;
193 }
194
195 // Decode any images that the browser needs to display.
196 std::set<FilePath> image_paths = extension->GetBrowserImages();
197 for (std::set<FilePath>::iterator it = image_paths.begin();
198 it != image_paths.end(); ++it) {
199 if (!AddDecodedImage(*it))
200 return false; // Error was already reported.
201 }
202
203 // Parse all message catalogs (if any).
204 parsed_catalogs_.reset(new DictionaryValue);
205 if (!extension->default_locale().empty()) {
206 if (!ReadAllMessageCatalogs(extension->default_locale()))
207 return false; // Error was already reported.
208 }
209
210 return true;
211 }
212
DumpImagesToFile()213 bool ExtensionUnpacker::DumpImagesToFile() {
214 IPC::Message pickle; // We use a Message so we can use WriteParam.
215 IPC::WriteParam(&pickle, decoded_images_);
216
217 FilePath path = extension_path_.DirName().AppendASCII(
218 filenames::kDecodedImagesFilename);
219 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
DumpMessageCatalogsToFile()228 bool ExtensionUnpacker::DumpMessageCatalogsToFile() {
229 IPC::Message pickle;
230 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
231
232 FilePath path = extension_path_.DirName().AppendASCII(
233 filenames::kDecodedMessageCatalogsFilename);
234 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
243 // static
ReadImagesFromFile(const FilePath & extension_path,DecodedImages * images)244 bool ExtensionUnpacker::ReadImagesFromFile(const FilePath& extension_path,
245 DecodedImages* images) {
246 FilePath path = extension_path.AppendASCII(filenames::kDecodedImagesFilename);
247 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
256 // static
ReadMessageCatalogsFromFile(const FilePath & extension_path,DictionaryValue * catalogs)257 bool ExtensionUnpacker::ReadMessageCatalogsFromFile(
258 const FilePath& extension_path, DictionaryValue* catalogs) {
259 FilePath path = extension_path.AppendASCII(
260 filenames::kDecodedMessageCatalogsFilename);
261 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
AddDecodedImage(const FilePath & path)270 bool 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)) {
273 SetError(kPathNamesMustBeAbsoluteOrLocalError);
274 return false;
275 }
276
277 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
278 if (image_bitmap.isNull()) {
279 SetError(kCouldNotDecodeImageError);
280 return false;
281 }
282
283 decoded_images_.push_back(MakeTuple(image_bitmap, path));
284 return true;
285 }
286
ReadMessageCatalog(const FilePath & message_path)287 bool ExtensionUnpacker::ReadMessageCatalog(const FilePath& message_path) {
288 std::string error;
289 JSONFileValueSerializer serializer(message_path);
290 scoped_ptr<DictionaryValue> root(
291 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, &error)));
292 if (!root.get()) {
293 string16 messages_file = message_path.LossyDisplayName();
294 if (error.empty()) {
295 // If file is missing, Deserialize will fail with empty error.
296 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
297 UTF16ToUTF8(messages_file).c_str()));
298 } else {
299 SetError(base::StringPrintf("%s: %s",
300 UTF16ToUTF8(messages_file).c_str(),
301 error.c_str()));
302 }
303 return false;
304 }
305
306 FilePath relative_path;
307 // message_path was created from temp_install_dir. This should never fail.
308 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path)) {
309 NOTREACHED();
310 return false;
311 }
312
313 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());
319
320 return true;
321 }
322
SetError(const std::string & error)323 void ExtensionUnpacker::SetError(const std::string &error) {
324 error_message_ = error;
325 }
326