1 // Copyright (c) 2012 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/utility/extensions/unpacker.h" 6 7 #include <set> 8 9 #include "base/file_util.h" 10 #include "base/files/file_enumerator.h" 11 #include "base/files/scoped_temp_dir.h" 12 #include "base/i18n/rtl.h" 13 #include "base/json/json_file_value_serializer.h" 14 #include "base/memory/scoped_handle.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/threading/thread.h" 18 #include "base/values.h" 19 #include "chrome/common/chrome_utility_messages.h" 20 #include "chrome/common/extensions/api/i18n/default_locale_handler.h" 21 #include "chrome/common/extensions/extension.h" 22 #include "chrome/common/extensions/extension_file_util.h" 23 #include "chrome/common/extensions/extension_l10n_util.h" 24 #include "chrome/common/extensions/extension_manifest_constants.h" 25 #include "chrome/common/extensions/manifest.h" 26 #include "content/public/child/image_decoder_utils.h" 27 #include "content/public/common/common_param_traits.h" 28 #include "extensions/common/constants.h" 29 #include "grit/generated_resources.h" 30 #include "ipc/ipc_message_utils.h" 31 #include "net/base/file_stream.h" 32 #include "third_party/skia/include/core/SkBitmap.h" 33 #include "third_party/zlib/google/zip.h" 34 #include "ui/base/l10n/l10n_util.h" 35 #include "ui/gfx/size.h" 36 37 namespace errors = extension_manifest_errors; 38 namespace keys = extension_manifest_keys; 39 40 namespace { 41 42 // A limit to stop us passing dangerously large canvases to the browser. 43 const int kMaxImageCanvas = 4096 * 4096; 44 45 SkBitmap DecodeImage(const base::FilePath& path) { 46 // Read the file from disk. 47 std::string file_contents; 48 if (!base::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 SkBitmap bitmap = content::DecodeImage(data, 57 gfx::Size(), 58 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 65 bool PathContainsParentDirectory(const base::FilePath& path) { 66 const base::FilePath::StringType kSeparators(base::FilePath::kSeparators); 67 const base::FilePath::StringType kParentDirectory( 68 base::FilePath::kParentDirectory); 69 const size_t npos = base::FilePath::StringType::npos; 70 const base::FilePath::StringType& value = path.value(); 71 72 for (size_t i = 0; i < value.length(); ) { 73 i = value.find(kParentDirectory, i); 74 if (i != npos) { 75 if ((i == 0 || kSeparators.find(value[i-1]) == npos) && 76 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) { 77 return true; 78 } 79 ++i; 80 } 81 } 82 83 return false; 84 } 85 86 } // namespace 87 88 namespace extensions { 89 90 struct Unpacker::InternalData { 91 DecodedImages decoded_images; 92 }; 93 94 Unpacker::Unpacker(const base::FilePath& extension_path, 95 const std::string& extension_id, 96 Manifest::Location location, 97 int creation_flags) 98 : extension_path_(extension_path), 99 extension_id_(extension_id), 100 location_(location), 101 creation_flags_(creation_flags) { 102 internal_data_.reset(new InternalData()); 103 } 104 105 Unpacker::~Unpacker() { 106 } 107 108 base::DictionaryValue* Unpacker::ReadManifest() { 109 base::FilePath manifest_path = 110 temp_install_dir_.Append(kManifestFilename); 111 if (!base::PathExists(manifest_path)) { 112 SetError(errors::kInvalidManifest); 113 return NULL; 114 } 115 116 JSONFileValueSerializer serializer(manifest_path); 117 std::string error; 118 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); 119 if (!root.get()) { 120 SetError(error); 121 return NULL; 122 } 123 124 if (!root->IsType(base::Value::TYPE_DICTIONARY)) { 125 SetError(errors::kInvalidManifest); 126 return NULL; 127 } 128 129 return static_cast<base::DictionaryValue*>(root.release()); 130 } 131 132 bool Unpacker::ReadAllMessageCatalogs(const std::string& default_locale) { 133 base::FilePath locales_path = 134 temp_install_dir_.Append(kLocaleFolder); 135 136 // Not all folders under _locales have to be valid locales. 137 base::FileEnumerator locales(locales_path, 138 false, 139 base::FileEnumerator::DIRECTORIES); 140 141 std::set<std::string> all_locales; 142 extension_l10n_util::GetAllLocales(&all_locales); 143 base::FilePath locale_path; 144 while (!(locale_path = locales.Next()).empty()) { 145 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path, 146 all_locales)) 147 continue; 148 149 base::FilePath messages_path = locale_path.Append(kMessagesFilename); 150 151 if (!ReadMessageCatalog(messages_path)) 152 return false; 153 } 154 155 return true; 156 } 157 158 bool Unpacker::Run() { 159 DVLOG(1) << "Installing extension " << extension_path_.value(); 160 161 // <profile>/Extensions/CRX_INSTALL 162 temp_install_dir_ = 163 extension_path_.DirName().AppendASCII(kTempExtensionName); 164 165 if (!file_util::CreateDirectory(temp_install_dir_)) { 166 SetUTF16Error( 167 l10n_util::GetStringFUTF16( 168 IDS_EXTENSION_PACKAGE_DIRECTORY_ERROR, 169 base::i18n::GetDisplayStringInLTRDirectionality( 170 temp_install_dir_.LossyDisplayName()))); 171 return false; 172 } 173 174 if (!zip::Unzip(extension_path_, temp_install_dir_)) { 175 SetUTF16Error(l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR)); 176 return false; 177 } 178 179 // Parse the manifest. 180 parsed_manifest_.reset(ReadManifest()); 181 if (!parsed_manifest_.get()) 182 return false; // Error was already reported. 183 184 std::string error; 185 scoped_refptr<Extension> extension(Extension::Create( 186 temp_install_dir_, 187 location_, 188 *parsed_manifest_, 189 creation_flags_, 190 extension_id_, 191 &error)); 192 if (!extension.get()) { 193 SetError(error); 194 return false; 195 } 196 197 std::vector<InstallWarning> warnings; 198 if (!extension_file_util::ValidateExtension(extension.get(), 199 &error, &warnings)) { 200 SetError(error); 201 return false; 202 } 203 extension->AddInstallWarnings(warnings); 204 205 // Decode any images that the browser needs to display. 206 std::set<base::FilePath> image_paths = 207 extension_file_util::GetBrowserImagePaths(extension.get()); 208 for (std::set<base::FilePath>::iterator it = image_paths.begin(); 209 it != image_paths.end(); 210 ++it) { 211 if (!AddDecodedImage(*it)) 212 return false; // Error was already reported. 213 } 214 215 // Parse all message catalogs (if any). 216 parsed_catalogs_.reset(new base::DictionaryValue); 217 if (!LocaleInfo::GetDefaultLocale(extension.get()).empty()) { 218 if (!ReadAllMessageCatalogs(LocaleInfo::GetDefaultLocale(extension.get()))) 219 return false; // Error was already reported. 220 } 221 222 return true; 223 } 224 225 bool Unpacker::DumpImagesToFile() { 226 IPC::Message pickle; // We use a Message so we can use WriteParam. 227 IPC::WriteParam(&pickle, internal_data_->decoded_images); 228 229 base::FilePath path = extension_path_.DirName().AppendASCII( 230 kDecodedImagesFilename); 231 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()), 232 pickle.size())) { 233 SetError("Could not write image data to disk."); 234 return false; 235 } 236 237 return true; 238 } 239 240 bool Unpacker::DumpMessageCatalogsToFile() { 241 IPC::Message pickle; 242 IPC::WriteParam(&pickle, *parsed_catalogs_.get()); 243 244 base::FilePath path = extension_path_.DirName().AppendASCII( 245 kDecodedMessageCatalogsFilename); 246 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()), 247 pickle.size())) { 248 SetError("Could not write message catalogs to disk."); 249 return false; 250 } 251 252 return true; 253 } 254 255 bool Unpacker::AddDecodedImage(const base::FilePath& path) { 256 // Make sure it's not referencing a file outside the extension's subdir. 257 if (path.IsAbsolute() || PathContainsParentDirectory(path)) { 258 SetUTF16Error( 259 l10n_util::GetStringFUTF16( 260 IDS_EXTENSION_PACKAGE_IMAGE_PATH_ERROR, 261 base::i18n::GetDisplayStringInLTRDirectionality( 262 path.LossyDisplayName()))); 263 return false; 264 } 265 266 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path)); 267 if (image_bitmap.isNull()) { 268 SetUTF16Error( 269 l10n_util::GetStringFUTF16( 270 IDS_EXTENSION_PACKAGE_IMAGE_ERROR, 271 base::i18n::GetDisplayStringInLTRDirectionality( 272 path.BaseName().LossyDisplayName()))); 273 return false; 274 } 275 276 internal_data_->decoded_images.push_back(MakeTuple(image_bitmap, path)); 277 return true; 278 } 279 280 bool Unpacker::ReadMessageCatalog(const base::FilePath& message_path) { 281 std::string error; 282 JSONFileValueSerializer serializer(message_path); 283 scoped_ptr<base::DictionaryValue> root(static_cast<base::DictionaryValue*>( 284 serializer.Deserialize(NULL, &error))); 285 if (!root.get()) { 286 string16 messages_file = message_path.LossyDisplayName(); 287 if (error.empty()) { 288 // If file is missing, Deserialize will fail with empty error. 289 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing, 290 UTF16ToUTF8(messages_file).c_str())); 291 } else { 292 SetError(base::StringPrintf("%s: %s", 293 UTF16ToUTF8(messages_file).c_str(), 294 error.c_str())); 295 } 296 return false; 297 } 298 299 base::FilePath relative_path; 300 // message_path was created from temp_install_dir. This should never fail. 301 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path)) { 302 NOTREACHED(); 303 return false; 304 } 305 306 std::string dir_name = relative_path.DirName().MaybeAsASCII(); 307 if (dir_name.empty()) { 308 NOTREACHED(); 309 return false; 310 } 311 parsed_catalogs_->Set(dir_name, root.release()); 312 313 return true; 314 } 315 316 void Unpacker::SetError(const std::string &error) { 317 SetUTF16Error(UTF8ToUTF16(error)); 318 } 319 320 void Unpacker::SetUTF16Error(const string16 &error) { 321 error_message_ = error; 322 } 323 324 } // namespace extensions 325