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