• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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