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 29namespace errors = extension_manifest_errors; 30namespace keys = extension_manifest_keys; 31namespace filenames = extension_filenames; 32 33namespace { 34 35// Errors 36const char* kCouldNotCreateDirectoryError = 37 "Could not create directory for unzipping: "; 38const char* kCouldNotDecodeImageError = "Could not decode theme image."; 39const char* kCouldNotUnzipExtension = "Could not unzip extension."; 40const 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. 44const int kMaxImageCanvas = 4096 * 4096; 45 46SkBitmap 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 65bool 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 87ExtensionUnpacker::ExtensionUnpacker(const FilePath& extension_path) 88 : extension_path_(extension_path) { 89} 90 91ExtensionUnpacker::~ExtensionUnpacker() { 92} 93 94DictionaryValue* 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 118bool 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 146bool 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 213bool 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 228bool 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 244bool 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 257bool 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 270bool 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 287bool 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 323void ExtensionUnpacker::SetError(const std::string &error) { 324 error_message_ = error; 325} 326