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/browser/extensions/sandboxed_extension_unpacker.h" 6 7 #include <set> 8 9 #include "base/base64.h" 10 #include "base/file_util.h" 11 #include "base/file_util_proxy.h" 12 #include "base/memory/scoped_handle.h" 13 #include "base/message_loop.h" 14 #include "base/metrics/histogram.h" 15 #include "base/path_service.h" 16 #include "base/task.h" 17 #include "base/utf_string_conversions.h" // TODO(viettrungluu): delete me. 18 #include "crypto/signature_verifier.h" 19 #include "chrome/browser/extensions/extension_service.h" 20 #include "chrome/common/chrome_paths.h" 21 #include "chrome/common/chrome_switches.h" 22 #include "chrome/common/extensions/extension.h" 23 #include "chrome/common/extensions/extension_constants.h" 24 #include "chrome/common/extensions/extension_file_util.h" 25 #include "chrome/common/extensions/extension_l10n_util.h" 26 #include "chrome/common/extensions/extension_unpacker.h" 27 #include "content/browser/browser_thread.h" 28 #include "content/browser/renderer_host/resource_dispatcher_host.h" 29 #include "content/common/json_value_serializer.h" 30 #include "grit/generated_resources.h" 31 #include "third_party/skia/include/core/SkBitmap.h" 32 #include "ui/base/l10n/l10n_util.h" 33 #include "ui/gfx/codec/png_codec.h" 34 35 // The following macro makes histograms that record the length of paths 36 // in this file much easier to read. 37 // Windows has a short max path length. If the path length to a 38 // file being unpacked from a CRX exceeds the max length, we might 39 // fail to install. To see if this is happening, see how long the 40 // path to the temp unpack directory is. See crbug.com/69693 . 41 #define PATH_LENGTH_HISTOGRAM(name, path) \ 42 UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100) 43 44 const char SandboxedExtensionUnpacker::kExtensionHeaderMagic[] = "Cr24"; 45 46 SandboxedExtensionUnpacker::SandboxedExtensionUnpacker( 47 const FilePath& crx_path, 48 ResourceDispatcherHost* rdh, 49 SandboxedExtensionUnpackerClient* client) 50 : crx_path_(crx_path), 51 thread_identifier_(BrowserThread::ID_COUNT), 52 rdh_(rdh), client_(client), got_response_(false) { 53 } 54 55 bool SandboxedExtensionUnpacker::CreateTempDirectory() { 56 CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_identifier_)); 57 58 FilePath user_data_temp_dir = extension_file_util::GetUserDataTempDir(); 59 if (user_data_temp_dir.empty()) { 60 ReportFailure( 61 COULD_NOT_GET_TEMP_DIRECTORY, 62 l10n_util::GetStringFUTF8( 63 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 64 ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY"))); 65 return false; 66 } 67 68 if (!temp_dir_.CreateUniqueTempDirUnderPath(user_data_temp_dir)) { 69 ReportFailure( 70 COULD_NOT_CREATE_TEMP_DIRECTORY, 71 l10n_util::GetStringFUTF8( 72 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 73 ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY"))); 74 return false; 75 } 76 77 return true; 78 } 79 80 void SandboxedExtensionUnpacker::Start() { 81 // We assume that we are started on the thread that the client wants us to do 82 // file IO on. 83 CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_identifier_)); 84 85 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength", 86 crx_path_); 87 if (!CreateTempDirectory()) 88 return; // ReportFailure() already called. 89 90 // Initialize the path that will eventually contain the unpacked extension. 91 extension_root_ = temp_dir_.path().AppendASCII( 92 extension_filenames::kTempExtensionName); 93 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackUnpackedCrxPathLength", 94 extension_root_); 95 96 // Extract the public key and validate the package. 97 if (!ValidateSignature()) 98 return; // ValidateSignature() already reported the error. 99 100 // Copy the crx file into our working directory. 101 FilePath temp_crx_path = temp_dir_.path().Append(crx_path_.BaseName()); 102 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength", 103 temp_crx_path); 104 105 if (!file_util::CopyFile(crx_path_, temp_crx_path)) { 106 // Failed to copy extension file to temporary directory. 107 ReportFailure( 108 FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY, 109 l10n_util::GetStringFUTF8( 110 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 111 ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY"))); 112 return; 113 } 114 115 // If we are supposed to use a subprocess, kick off the subprocess. 116 // 117 // TODO(asargent) we shouldn't need to do this branch here - instead 118 // UtilityProcessHost should handle it for us. (http://crbug.com/19192) 119 bool use_utility_process = rdh_ && 120 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); 121 if (use_utility_process) { 122 // The utility process will have access to the directory passed to 123 // SandboxedExtensionUnpacker. That directory should not contain a 124 // symlink or NTFS reparse point. When the path is used, following 125 // the link/reparse point will cause file system access outside the 126 // sandbox path, and the sandbox will deny the operation. 127 FilePath link_free_crx_path; 128 if (!file_util::NormalizeFilePath(temp_crx_path, &link_free_crx_path)) { 129 LOG(ERROR) << "Could not get the normalized path of " 130 << temp_crx_path.value(); 131 ReportFailure( 132 COULD_NOT_GET_SANDBOX_FRIENDLY_PATH, 133 l10n_util::GetStringUTF8(IDS_EXTENSION_UNPACK_FAILED)); 134 return; 135 } 136 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength", 137 link_free_crx_path); 138 139 BrowserThread::PostTask( 140 BrowserThread::IO, FROM_HERE, 141 NewRunnableMethod( 142 this, 143 &SandboxedExtensionUnpacker::StartProcessOnIOThread, 144 link_free_crx_path)); 145 } else { 146 // Otherwise, unpack the extension in this process. 147 ExtensionUnpacker unpacker(temp_crx_path); 148 if (unpacker.Run() && unpacker.DumpImagesToFile() && 149 unpacker.DumpMessageCatalogsToFile()) { 150 OnUnpackExtensionSucceeded(*unpacker.parsed_manifest()); 151 } else { 152 OnUnpackExtensionFailed(unpacker.error_message()); 153 } 154 } 155 } 156 157 SandboxedExtensionUnpacker::~SandboxedExtensionUnpacker() { 158 base::FileUtilProxy::Delete( 159 BrowserThread::GetMessageLoopProxyForThread(thread_identifier_), 160 temp_dir_.Take(), 161 true, 162 NULL); 163 } 164 165 void SandboxedExtensionUnpacker::StartProcessOnIOThread( 166 const FilePath& temp_crx_path) { 167 UtilityProcessHost* host = new UtilityProcessHost(this, thread_identifier_); 168 host->StartExtensionUnpacker(temp_crx_path); 169 } 170 171 void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded( 172 const DictionaryValue& manifest) { 173 // Skip check for unittests. 174 if (thread_identifier_ != BrowserThread::ID_COUNT) 175 CHECK(BrowserThread::CurrentlyOn(thread_identifier_)); 176 got_response_ = true; 177 178 scoped_ptr<DictionaryValue> final_manifest(RewriteManifestFile(manifest)); 179 if (!final_manifest.get()) 180 return; 181 182 // Create an extension object that refers to the temporary location the 183 // extension was unpacked to. We use this until the extension is finally 184 // installed. For example, the install UI shows images from inside the 185 // extension. 186 187 // Localize manifest now, so confirm UI gets correct extension name. 188 std::string error; 189 if (!extension_l10n_util::LocalizeExtension(extension_root_, 190 final_manifest.get(), 191 &error)) { 192 ReportFailure( 193 COULD_NOT_LOCALIZE_EXTENSION, 194 l10n_util::GetStringFUTF8( 195 IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, 196 ASCIIToUTF16(error))); 197 return; 198 } 199 200 extension_ = Extension::Create( 201 extension_root_, 202 Extension::INTERNAL, 203 *final_manifest, 204 Extension::REQUIRE_KEY, 205 &error); 206 207 if (!extension_.get()) { 208 ReportFailure( 209 INVALID_MANIFEST, 210 std::string("Manifest is invalid: ") + error); 211 return; 212 } 213 214 if (!RewriteImageFiles()) 215 return; 216 217 if (!RewriteCatalogFiles()) 218 return; 219 220 ReportSuccess(); 221 } 222 223 void SandboxedExtensionUnpacker::OnUnpackExtensionFailed( 224 const std::string& error) { 225 CHECK(BrowserThread::CurrentlyOn(thread_identifier_)); 226 got_response_ = true; 227 ReportFailure( 228 UNPACKER_CLIENT_FAILED, 229 l10n_util::GetStringFUTF8( 230 IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, 231 ASCIIToUTF16(error))); 232 } 233 234 void SandboxedExtensionUnpacker::OnProcessCrashed(int exit_code) { 235 // Don't report crashes if they happen after we got a response. 236 if (got_response_) 237 return; 238 239 // Utility process crashed while trying to install. 240 ReportFailure( 241 UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL, 242 l10n_util::GetStringFUTF8( 243 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 244 ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL"))); 245 } 246 247 bool SandboxedExtensionUnpacker::ValidateSignature() { 248 ScopedStdioHandle file(file_util::OpenFile(crx_path_, "rb")); 249 250 if (!file.get()) { 251 // Could not open crx file for reading. 252 #if defined (OS_WIN) 253 // On windows, get the error code. 254 uint32 error_code = ::GetLastError(); 255 // TODO(skerner): Use this histogram to understand why so many 256 // windows users hit this error. crbug.com/69693 257 258 // Windows errors are unit32s, but all of likely errors are in 259 // [1, 1000]. See winerror.h for the meaning of specific values. 260 // Clip errors outside the expected range to a single extra value. 261 // If there are errors in that extra bucket, we will know to expand 262 // the range. 263 const uint32 kMaxErrorToSend = 1001; 264 error_code = std::min(error_code, kMaxErrorToSend); 265 UMA_HISTOGRAM_ENUMERATION("Extensions.ErrorCodeFromCrxOpen", 266 error_code, kMaxErrorToSend); 267 #endif 268 269 ReportFailure( 270 CRX_FILE_NOT_READABLE, 271 l10n_util::GetStringFUTF8( 272 IDS_EXTENSION_PACKAGE_ERROR_CODE, 273 ASCIIToUTF16("CRX_FILE_NOT_READABLE"))); 274 return false; 275 } 276 277 // Read and verify the header. 278 ExtensionHeader header; 279 size_t len; 280 281 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it 282 // appears that we don't have any endian/alignment aware serialization 283 // code in the code base. So for now, this assumes that we're running 284 // on a little endian machine with 4 byte alignment. 285 len = fread(&header, 1, sizeof(ExtensionHeader), 286 file.get()); 287 if (len < sizeof(ExtensionHeader)) { 288 // Invalid crx header 289 ReportFailure( 290 CRX_HEADER_INVALID, 291 l10n_util::GetStringFUTF8( 292 IDS_EXTENSION_PACKAGE_ERROR_CODE, 293 ASCIIToUTF16("CRX_HEADER_INVALID"))); 294 return false; 295 } 296 if (strncmp(kExtensionHeaderMagic, header.magic, 297 sizeof(header.magic))) { 298 // Bad magic number 299 ReportFailure( 300 CRX_MAGIC_NUMBER_INVALID, 301 l10n_util::GetStringFUTF8( 302 IDS_EXTENSION_PACKAGE_ERROR_CODE, 303 ASCIIToUTF16("CRX_MAGIC_NUMBER_INVALID"))); 304 return false; 305 } 306 if (header.version != kCurrentVersion) { 307 // Bad version numer 308 ReportFailure(CRX_VERSION_NUMBER_INVALID, 309 l10n_util::GetStringFUTF8( 310 IDS_EXTENSION_PACKAGE_ERROR_CODE, 311 ASCIIToUTF16("CRX_VERSION_NUMBER_INVALID"))); 312 return false; 313 } 314 if (header.key_size > kMaxPublicKeySize || 315 header.signature_size > kMaxSignatureSize) { 316 // Excessively large key or signature 317 ReportFailure( 318 CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE, 319 l10n_util::GetStringFUTF8( 320 IDS_EXTENSION_PACKAGE_ERROR_CODE, 321 ASCIIToUTF16("CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE"))); 322 return false; 323 } 324 if (header.key_size == 0) { 325 // Key length is zero 326 ReportFailure( 327 CRX_ZERO_KEY_LENGTH, 328 l10n_util::GetStringFUTF8( 329 IDS_EXTENSION_PACKAGE_ERROR_CODE, 330 ASCIIToUTF16("CRX_ZERO_KEY_LENGTH"))); 331 return false; 332 } 333 if (header.signature_size == 0) { 334 // Signature length is zero 335 ReportFailure( 336 CRX_ZERO_SIGNATURE_LENGTH, 337 l10n_util::GetStringFUTF8( 338 IDS_EXTENSION_PACKAGE_ERROR_CODE, 339 ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH"))); 340 return false; 341 } 342 343 std::vector<uint8> key; 344 key.resize(header.key_size); 345 len = fread(&key.front(), sizeof(uint8), header.key_size, file.get()); 346 if (len < header.key_size) { 347 // Invalid public key 348 ReportFailure( 349 CRX_PUBLIC_KEY_INVALID, 350 l10n_util::GetStringFUTF8( 351 IDS_EXTENSION_PACKAGE_ERROR_CODE, 352 ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID"))); 353 return false; 354 } 355 356 std::vector<uint8> signature; 357 signature.resize(header.signature_size); 358 len = fread(&signature.front(), sizeof(uint8), header.signature_size, 359 file.get()); 360 if (len < header.signature_size) { 361 // Invalid signature 362 ReportFailure( 363 CRX_SIGNATURE_INVALID, 364 l10n_util::GetStringFUTF8( 365 IDS_EXTENSION_PACKAGE_ERROR_CODE, 366 ASCIIToUTF16("CRX_SIGNATURE_INVALID"))); 367 return false; 368 } 369 370 crypto::SignatureVerifier verifier; 371 if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm, 372 sizeof(extension_misc::kSignatureAlgorithm), 373 &signature.front(), 374 signature.size(), 375 &key.front(), 376 key.size())) { 377 // Signature verification initialization failed. This is most likely 378 // caused by a public key in the wrong format (should encode algorithm). 379 ReportFailure( 380 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED, 381 l10n_util::GetStringFUTF8( 382 IDS_EXTENSION_PACKAGE_ERROR_CODE, 383 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED"))); 384 return false; 385 } 386 387 unsigned char buf[1 << 12]; 388 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0) 389 verifier.VerifyUpdate(buf, len); 390 391 if (!verifier.VerifyFinal()) { 392 // Signature verification failed 393 ReportFailure( 394 CRX_SIGNATURE_VERIFICATION_FAILED, 395 l10n_util::GetStringFUTF8( 396 IDS_EXTENSION_PACKAGE_ERROR_CODE, 397 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED"))); 398 return false; 399 } 400 401 base::Base64Encode(std::string(reinterpret_cast<char*>(&key.front()), 402 key.size()), &public_key_); 403 return true; 404 } 405 406 void SandboxedExtensionUnpacker::ReportFailure(FailureReason reason, 407 const std::string& error) { 408 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackFailure", 1); 409 UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason", 410 reason, NUM_FAILURE_REASONS); 411 412 client_->OnUnpackFailure(error); 413 } 414 415 void SandboxedExtensionUnpacker::ReportSuccess() { 416 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1); 417 418 // Client takes ownership of temporary directory and extension. 419 client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, extension_); 420 extension_ = NULL; 421 } 422 423 DictionaryValue* SandboxedExtensionUnpacker::RewriteManifestFile( 424 const DictionaryValue& manifest) { 425 // Add the public key extracted earlier to the parsed manifest and overwrite 426 // the original manifest. We do this to ensure the manifest doesn't contain an 427 // exploitable bug that could be used to compromise the browser. 428 scoped_ptr<DictionaryValue> final_manifest(manifest.DeepCopy()); 429 final_manifest->SetString(extension_manifest_keys::kPublicKey, public_key_); 430 431 std::string manifest_json; 432 JSONStringValueSerializer serializer(&manifest_json); 433 serializer.set_pretty_print(true); 434 if (!serializer.Serialize(*final_manifest)) { 435 // Error serializing manifest.json. 436 ReportFailure( 437 ERROR_SERIALIZING_MANIFEST_JSON, 438 l10n_util::GetStringFUTF8( 439 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 440 ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON"))); 441 return NULL; 442 } 443 444 FilePath manifest_path = 445 extension_root_.Append(Extension::kManifestFilename); 446 if (!file_util::WriteFile(manifest_path, 447 manifest_json.data(), manifest_json.size())) { 448 // Error saving manifest.json. 449 ReportFailure( 450 ERROR_SAVING_MANIFEST_JSON, 451 l10n_util::GetStringFUTF8( 452 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 453 ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON"))); 454 return NULL; 455 } 456 457 return final_manifest.release(); 458 } 459 460 bool SandboxedExtensionUnpacker::RewriteImageFiles() { 461 ExtensionUnpacker::DecodedImages images; 462 if (!ExtensionUnpacker::ReadImagesFromFile(temp_dir_.path(), &images)) { 463 // Couldn't read image data from disk. 464 ReportFailure( 465 COULD_NOT_READ_IMAGE_DATA_FROM_DISK, 466 l10n_util::GetStringFUTF8( 467 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 468 ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK"))); 469 return false; 470 } 471 472 // Delete any images that may be used by the browser. We're going to write 473 // out our own versions of the parsed images, and we want to make sure the 474 // originals are gone for good. 475 std::set<FilePath> image_paths = extension_->GetBrowserImages(); 476 if (image_paths.size() != images.size()) { 477 // Decoded images don't match what's in the manifest. 478 ReportFailure( 479 DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST, 480 l10n_util::GetStringFUTF8( 481 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 482 ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST"))); 483 return false; 484 } 485 486 for (std::set<FilePath>::iterator it = image_paths.begin(); 487 it != image_paths.end(); ++it) { 488 FilePath path = *it; 489 if (path.IsAbsolute() || path.ReferencesParent()) { 490 // Invalid path for browser image. 491 ReportFailure( 492 INVALID_PATH_FOR_BROWSER_IMAGE, 493 l10n_util::GetStringFUTF8( 494 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 495 ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE"))); 496 return false; 497 } 498 if (!file_util::Delete(extension_root_.Append(path), false)) { 499 // Error removing old image file. 500 ReportFailure( 501 ERROR_REMOVING_OLD_IMAGE_FILE, 502 l10n_util::GetStringFUTF8( 503 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 504 ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE"))); 505 return false; 506 } 507 } 508 509 // Write our parsed images back to disk as well. 510 for (size_t i = 0; i < images.size(); ++i) { 511 const SkBitmap& image = images[i].a; 512 FilePath path_suffix = images[i].b; 513 if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) { 514 // Invalid path for bitmap image. 515 ReportFailure( 516 INVALID_PATH_FOR_BITMAP_IMAGE, 517 l10n_util::GetStringFUTF8( 518 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 519 ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE"))); 520 return false; 521 } 522 FilePath path = extension_root_.Append(path_suffix); 523 524 std::vector<unsigned char> image_data; 525 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even 526 // though they may originally be .jpg, etc. Figure something out. 527 // http://code.google.com/p/chromium/issues/detail?id=12459 528 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) { 529 // Error re-encoding theme image. 530 ReportFailure( 531 ERROR_RE_ENCODING_THEME_IMAGE, 532 l10n_util::GetStringFUTF8( 533 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 534 ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE"))); 535 return false; 536 } 537 538 // Note: we're overwriting existing files that the utility process wrote, 539 // so we can be sure the directory exists. 540 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]); 541 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) { 542 // Error saving theme image. 543 ReportFailure( 544 ERROR_SAVING_THEME_IMAGE, 545 l10n_util::GetStringFUTF8( 546 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 547 ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE"))); 548 return false; 549 } 550 } 551 552 return true; 553 } 554 555 bool SandboxedExtensionUnpacker::RewriteCatalogFiles() { 556 DictionaryValue catalogs; 557 if (!ExtensionUnpacker::ReadMessageCatalogsFromFile(temp_dir_.path(), 558 &catalogs)) { 559 // Could not read catalog data from disk. 560 ReportFailure( 561 COULD_NOT_READ_CATALOG_DATA_FROM_DISK, 562 l10n_util::GetStringFUTF8( 563 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 564 ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK"))); 565 return false; 566 } 567 568 // Write our parsed catalogs back to disk. 569 for (DictionaryValue::key_iterator key_it = catalogs.begin_keys(); 570 key_it != catalogs.end_keys(); ++key_it) { 571 DictionaryValue* catalog; 572 if (!catalogs.GetDictionaryWithoutPathExpansion(*key_it, &catalog)) { 573 // Invalid catalog data. 574 ReportFailure( 575 INVALID_CATALOG_DATA, 576 l10n_util::GetStringFUTF8( 577 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 578 ASCIIToUTF16("INVALID_CATALOG_DATA"))); 579 return false; 580 } 581 582 // TODO(viettrungluu): Fix the |FilePath::FromWStringHack(UTF8ToWide())| 583 // hack and remove the corresponding #include. 584 FilePath relative_path = FilePath::FromWStringHack(UTF8ToWide(*key_it)); 585 relative_path = relative_path.Append(Extension::kMessagesFilename); 586 if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) { 587 // Invalid path for catalog. 588 ReportFailure( 589 INVALID_PATH_FOR_CATALOG, 590 l10n_util::GetStringFUTF8( 591 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 592 ASCIIToUTF16("INVALID_PATH_FOR_CATALOG"))); 593 return false; 594 } 595 FilePath path = extension_root_.Append(relative_path); 596 597 std::string catalog_json; 598 JSONStringValueSerializer serializer(&catalog_json); 599 serializer.set_pretty_print(true); 600 if (!serializer.Serialize(*catalog)) { 601 // Error serializing catalog. 602 ReportFailure( 603 ERROR_SERIALIZING_CATALOG, 604 l10n_util::GetStringFUTF8( 605 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 606 ASCIIToUTF16("ERROR_SERIALIZING_CATALOG"))); 607 return false; 608 } 609 610 // Note: we're overwriting existing files that the utility process read, 611 // so we can be sure the directory exists. 612 if (!file_util::WriteFile(path, 613 catalog_json.c_str(), 614 catalog_json.size())) { 615 // Error saving catalog. 616 ReportFailure( 617 ERROR_SAVING_CATALOG, 618 l10n_util::GetStringFUTF8( 619 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 620 ASCIIToUTF16("ERROR_SAVING_CATALOG"))); 621 return false; 622 } 623 } 624 625 return true; 626 } 627