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 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 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 87 ExtensionUnpacker::ExtensionUnpacker(const FilePath& extension_path) 88 : extension_path_(extension_path) { 89 } 90 91 ExtensionUnpacker::~ExtensionUnpacker() { 92 } 93 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 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 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 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 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 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 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 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 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 323 void ExtensionUnpacker::SetError(const std::string &error) { 324 error_message_ = error; 325 } 326