| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/extensions_service.h" | 5 #include "chrome/browser/extensions/extensions_service.h" |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/gfx/png_encoder.h" |
| 8 #include "base/scoped_handle.h" | 9 #include "base/scoped_handle.h" |
| 9 #include "base/scoped_temp_dir.h" | 10 #include "base/scoped_temp_dir.h" |
| 10 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 11 #include "base/third_party/nss/blapi.h" | 12 #include "base/third_party/nss/blapi.h" |
| 12 #include "base/third_party/nss/sha256.h" | 13 #include "base/third_party/nss/sha256.h" |
| 13 #include "base/thread.h" | 14 #include "base/thread.h" |
| 14 #include "base/values.h" | 15 #include "base/values.h" |
| 15 #include "net/base/file_stream.h" | 16 #include "net/base/file_stream.h" |
| 16 #include "chrome/browser/browser.h" | 17 #include "chrome/browser/browser.h" |
| 17 #include "chrome/browser/browser_list.h" | 18 #include "chrome/browser/browser_list.h" |
| 18 #include "chrome/browser/browser_process.h" | 19 #include "chrome/browser/browser_process.h" |
| 19 #include "chrome/browser/chrome_thread.h" | 20 #include "chrome/browser/chrome_thread.h" |
| 20 #include "chrome/browser/extensions/extension.h" | 21 #include "chrome/browser/extensions/extension.h" |
| 21 #include "chrome/browser/extensions/extension_browser_event_router.h" | 22 #include "chrome/browser/extensions/extension_browser_event_router.h" |
| 22 #include "chrome/browser/extensions/extension_error_reporter.h" | 23 #include "chrome/browser/extensions/extension_error_reporter.h" |
| 23 #include "chrome/browser/extensions/extension_process_manager.h" | 24 #include "chrome/browser/extensions/extension_process_manager.h" |
| 24 #include "chrome/browser/profile.h" | 25 #include "chrome/browser/profile.h" |
| 25 #include "chrome/browser/utility_process_host.h" | 26 #include "chrome/browser/utility_process_host.h" |
| 26 #include "chrome/common/extensions/extension_unpacker.h" | 27 #include "chrome/common/extensions/extension_unpacker.h" |
| 27 #include "chrome/common/json_value_serializer.h" | 28 #include "chrome/common/json_value_serializer.h" |
| 28 #include "chrome/common/notification_service.h" | 29 #include "chrome/common/notification_service.h" |
| 29 #include "chrome/common/pref_service.h" | 30 #include "chrome/common/pref_service.h" |
| 30 #include "chrome/common/unzip.h" | 31 #include "chrome/common/unzip.h" |
| 31 #include "chrome/common/url_constants.h" | 32 #include "chrome/common/url_constants.h" |
| 33 #include "third_party/skia/include/core/SkBitmap.h" |
| 32 | 34 |
| 33 #if defined(OS_WIN) | 35 #if defined(OS_WIN) |
| 34 #include "base/registry.h" | 36 #include "base/registry.h" |
| 35 #endif | 37 #endif |
| 36 | 38 |
| 37 // ExtensionsService | 39 // ExtensionsService |
| 38 | 40 |
| 39 const char* ExtensionsService::kInstallDirectoryName = "Extensions"; | 41 const char* ExtensionsService::kInstallDirectoryName = "Extensions"; |
| 40 const char* ExtensionsService::kCurrentVersionFileName = "Current Version"; | 42 const char* ExtensionsService::kCurrentVersionFileName = "Current Version"; |
| 41 const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL"; | 43 const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL"; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 | 124 |
| 123 if (backend_->resource_dispatcher_host_) { | 125 if (backend_->resource_dispatcher_host_) { |
| 124 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE, | 126 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE, |
| 125 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread, | 127 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread, |
| 126 backend_->resource_dispatcher_host_, | 128 backend_->resource_dispatcher_host_, |
| 127 MessageLoop::current())); | 129 MessageLoop::current())); |
| 128 } else { | 130 } else { |
| 129 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're | 131 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're |
| 130 // in a unit test and run the unpacker directly in-process. | 132 // in a unit test and run the unpacker directly in-process. |
| 131 ExtensionUnpacker unpacker(temp_extension_path_); | 133 ExtensionUnpacker unpacker(temp_extension_path_); |
| 132 bool success = unpacker.Run(); | 134 if (unpacker.Run()) { |
| 133 OnUnpackExtensionReply(success, unpacker.error_message()); | 135 OnUnpackExtensionSucceeded(*unpacker.parsed_manifest(), |
| 136 unpacker.decoded_images()); |
| 137 } else { |
| 138 OnUnpackExtensionFailed(unpacker.error_message()); |
| 139 } |
| 134 } | 140 } |
| 135 } | 141 } |
| 136 | 142 |
| 137 private: | 143 private: |
| 138 // UtilityProcessHost::Client | 144 // UtilityProcessHost::Client |
| 139 virtual void OnProcessCrashed() { | 145 virtual void OnProcessCrashed() { |
| 140 OnUnpackExtensionReply(false, "Chrome crashed while trying to install"); | 146 OnUnpackExtensionFailed("Chrome crashed while trying to install"); |
| 141 } | 147 } |
| 142 | 148 |
| 143 virtual void OnUnpackExtensionReply(bool success, | 149 virtual void OnUnpackExtensionSucceeded( |
| 144 const std::string& error_message) { | 150 const DictionaryValue& manifest, |
| 145 if (success) { | 151 const std::vector< Tuple2<SkBitmap, FilePath> >& images) { |
| 146 // The extension was unpacked to the temp dir inside our unpacking dir. | 152 // The extension was unpacked to the temp dir inside our unpacking dir. |
| 147 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII( | 153 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII( |
| 148 ExtensionsServiceBackend::kTempExtensionName); | 154 ExtensionsServiceBackend::kTempExtensionName); |
| 149 backend_->OnExtensionUnpacked(extension_path_, extension_dir, | 155 backend_->OnExtensionUnpacked(extension_path_, extension_dir, |
| 150 expected_id_, from_external_); | 156 expected_id_, from_external_, |
| 151 } else { | 157 manifest, images); |
| 152 backend_->ReportExtensionInstallError(extension_path_, error_message); | |
| 153 } | |
| 154 Cleanup(); | 158 Cleanup(); |
| 155 Release(); // balanced in Run() | 159 } |
| 160 |
| 161 virtual void OnUnpackExtensionFailed(const std::string& error_message) { |
| 162 backend_->ReportExtensionInstallError(extension_path_, error_message); |
| 163 Cleanup(); |
| 156 } | 164 } |
| 157 | 165 |
| 158 // Cleans up our temp directory. | 166 // Cleans up our temp directory. |
| 159 void Cleanup() { | 167 void Cleanup() { |
| 160 file_util::Delete(temp_extension_path_.DirName(), true); | 168 file_util::Delete(temp_extension_path_.DirName(), true); |
| 169 Release(); // balanced in Run() |
| 161 } | 170 } |
| 162 | 171 |
| 163 // Starts the utility process that unpacks our extension. | 172 // Starts the utility process that unpacks our extension. |
| 164 void StartProcessOnIOThread(ResourceDispatcherHost* rdh, | 173 void StartProcessOnIOThread(ResourceDispatcherHost* rdh, |
| 165 MessageLoop* file_loop) { | 174 MessageLoop* file_loop) { |
| 166 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop); | 175 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop); |
| 167 host->StartExtensionUnpacker(temp_extension_path_); | 176 host->StartExtensionUnpacker(temp_extension_path_); |
| 168 } | 177 } |
| 169 | 178 |
| 170 scoped_refptr<ExtensionsServiceBackend> backend_; | 179 scoped_refptr<ExtensionsServiceBackend> backend_; |
| (...skipping 634 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 805 bool from_external) { | 814 bool from_external) { |
| 806 UnpackerClient* client = | 815 UnpackerClient* client = |
| 807 new UnpackerClient(this, extension_path, expected_id, from_external); | 816 new UnpackerClient(this, extension_path, expected_id, from_external); |
| 808 client->Start(); | 817 client->Start(); |
| 809 } | 818 } |
| 810 | 819 |
| 811 void ExtensionsServiceBackend::OnExtensionUnpacked( | 820 void ExtensionsServiceBackend::OnExtensionUnpacked( |
| 812 const FilePath& extension_path, | 821 const FilePath& extension_path, |
| 813 const FilePath& temp_extension_dir, | 822 const FilePath& temp_extension_dir, |
| 814 const std::string expected_id, | 823 const std::string expected_id, |
| 815 bool from_external) { | 824 bool from_external, |
| 816 // TODO(mpcomplete): the utility process should pass up a parsed manifest that | 825 const DictionaryValue& manifest, |
| 817 // we rewrite in the browser. | 826 const std::vector< Tuple2<SkBitmap, FilePath> >& images) { |
| 818 // Bug http://code.google.com/p/chromium/issues/detail?id=11680 | |
| 819 scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path)); | |
| 820 if (!manifest.get()) { | |
| 821 // ReadManifest has already reported the extension error. | |
| 822 return; | |
| 823 } | |
| 824 | |
| 825 Extension extension; | 827 Extension extension; |
| 826 std::string error; | 828 std::string error; |
| 827 if (!extension.InitFromValue(*manifest, | 829 if (!extension.InitFromValue(manifest, |
| 828 true, // require ID | 830 true, // require ID |
| 829 &error)) { | 831 &error)) { |
| 830 ReportExtensionInstallError(extension_path, "Invalid extension manifest."); | 832 ReportExtensionInstallError(extension_path, "Invalid extension manifest."); |
| 831 return; | 833 return; |
| 832 } | 834 } |
| 833 | 835 |
| 834 // If an expected id was provided, make sure it matches. | 836 // If an expected id was provided, make sure it matches. |
| 835 if (!expected_id.empty() && expected_id != extension.id()) { | 837 if (!expected_id.empty() && expected_id != extension.id()) { |
| 836 ReportExtensionInstallError(extension_path, | 838 ReportExtensionInstallError(extension_path, |
| 837 "ID in new extension manifest does not match expected ID."); | 839 "ID in new extension manifest does not match expected ID."); |
| 838 return; | 840 return; |
| 839 } | 841 } |
| 840 | 842 |
| 841 // <profile>/Extensions/<id> | 843 // <profile>/Extensions/<id> |
| 842 FilePath dest_dir = install_directory_.AppendASCII(extension.id()); | 844 FilePath dest_dir = install_directory_.AppendASCII(extension.id()); |
| 843 std::string version = extension.VersionString(); | 845 std::string version = extension.VersionString(); |
| 844 std::string current_version; | 846 std::string current_version; |
| 845 bool was_update = false; | 847 bool was_update = false; |
| 846 if (ReadCurrentVersion(dest_dir, ¤t_version)) { | 848 if (ReadCurrentVersion(dest_dir, ¤t_version)) { |
| 847 if (!CheckCurrentVersion(version, current_version, dest_dir)) | 849 if (!CheckCurrentVersion(version, current_version, dest_dir)) |
| 848 return; | 850 return; |
| 849 was_update = true; | 851 was_update = true; |
| 850 } | 852 } |
| 851 | 853 |
| 854 // Write our parsed manifest back to disk, to ensure it doesn't contain an |
| 855 // exploitable bug that can be used to compromise the browser. |
| 856 std::string manifest_json; |
| 857 JSONStringValueSerializer serializer(&manifest_json); |
| 858 serializer.set_pretty_print(true); |
| 859 if (!serializer.Serialize(manifest)) { |
| 860 ReportExtensionInstallError(extension_path, |
| 861 "Error serializing manifest.json."); |
| 862 return; |
| 863 } |
| 864 |
| 865 FilePath manifest_path = |
| 866 temp_extension_dir.AppendASCII(Extension::kManifestFilename); |
| 867 if (!file_util::WriteFile(manifest_path, |
| 868 manifest_json.data(), manifest_json.size())) { |
| 869 ReportExtensionInstallError(extension_path, "Error saving manifest.json."); |
| 870 return; |
| 871 } |
| 872 |
| 873 // Write our parsed images back to disk as well. |
| 874 for (size_t i = 0; i < images.size(); ++i) { |
| 875 const SkBitmap& image = images[i].a; |
| 876 FilePath path = temp_extension_dir.Append(images[i].b); |
| 877 |
| 878 std::vector<unsigned char> image_data; |
| 879 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even |
| 880 // though they may originally be .jpg, etc. Figure something out. |
| 881 // http://code.google.com/p/chromium/issues/detail?id=12459 |
| 882 if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) { |
| 883 ReportExtensionInstallError(extension_path, |
| 884 "Error re-encoding theme image."); |
| 885 return; |
| 886 } |
| 887 |
| 888 // Note: we're overwriting existing files that the utility process wrote, |
| 889 // so we can be sure the directory exists. |
| 890 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]); |
| 891 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) { |
| 892 ReportExtensionInstallError(extension_path, "Error saving theme image."); |
| 893 return; |
| 894 } |
| 895 } |
| 896 |
| 852 // <profile>/Extensions/<dir_name>/<version> | 897 // <profile>/Extensions/<dir_name>/<version> |
| 853 FilePath version_dir = dest_dir.AppendASCII(version); | 898 FilePath version_dir = dest_dir.AppendASCII(version); |
| 899 |
| 900 // If anything fails after this, we want to delete the extension dir. |
| 901 ScopedTempDir scoped_version_dir; |
| 902 scoped_version_dir.Set(version_dir); |
| 903 |
| 854 if (!InstallDirSafely(temp_extension_dir, version_dir)) | 904 if (!InstallDirSafely(temp_extension_dir, version_dir)) |
| 855 return; | 905 return; |
| 856 | 906 |
| 857 if (!SetCurrentVersion(dest_dir, version)) { | 907 if (!SetCurrentVersion(dest_dir, version)) |
| 858 if (!file_util::Delete(version_dir, true)) | |
| 859 LOG(WARNING) << "Can't remove " << dest_dir.value(); | |
| 860 return; | 908 return; |
| 861 } | |
| 862 | 909 |
| 863 // To mark that this extension was installed from an external source, create a | 910 // To mark that this extension was installed from an external source, create a |
| 864 // zero-length file. At load time, this is used to indicate that the | 911 // zero-length file. At load time, this is used to indicate that the |
| 865 // extension should be uninstalled. | 912 // extension should be uninstalled. |
| 866 // TODO(erikkay): move this into per-extension config storage when it appears. | 913 // TODO(erikkay): move this into per-extension config storage when it appears. |
| 867 if (from_external) { | 914 if (from_external) { |
| 868 FilePath marker = version_dir.AppendASCII(kExternalInstallFile); | 915 FilePath marker = version_dir.AppendASCII(kExternalInstallFile); |
| 869 file_util::WriteFile(marker, NULL, 0); | 916 file_util::WriteFile(marker, NULL, 0); |
| 870 } | 917 } |
| 871 | 918 |
| 872 // Load the extension immediately and then report installation success. We | 919 // Load the extension immediately and then report installation success. We |
| 873 // don't load extensions for external installs because external installation | 920 // don't load extensions for external installs because external installation |
| 874 // occurs before the normal startup so we just let startup pick them up. We | 921 // occurs before the normal startup so we just let startup pick them up. We |
| 875 // don't notify installation because there is no UI or external install so | 922 // don't notify installation because there is no UI for external install so |
| 876 // there is nobody to notify. | 923 // there is nobody to notify. |
| 877 if (!from_external) { | 924 if (!from_external) { |
| 878 Extension* extension = LoadExtension(version_dir, true); // require id | 925 Extension* extension = LoadExtension(version_dir, true); // require id |
| 879 CHECK(extension); | 926 CHECK(extension); |
| 880 | 927 |
| 881 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 928 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
| 882 frontend_, &ExtensionsService::OnExtensionInstalled, extension, | 929 frontend_, &ExtensionsService::OnExtensionInstalled, extension, |
| 883 was_update)); | 930 was_update)); |
| 884 | 931 |
| 885 // Only one extension, but ReportExtensionsLoaded can handle multiple, | 932 // Only one extension, but ReportExtensionsLoaded can handle multiple, |
| 886 // so we need to construct a list. | 933 // so we need to construct a list. |
| 887 scoped_ptr<ExtensionList> extensions(new ExtensionList); | 934 scoped_ptr<ExtensionList> extensions(new ExtensionList); |
| 888 extensions->push_back(extension); | 935 extensions->push_back(extension); |
| 889 LOG(INFO) << "Done."; | 936 LOG(INFO) << "Done."; |
| 890 // Hand off ownership of the loaded extensions to the frontend. | 937 // Hand off ownership of the loaded extensions to the frontend. |
| 891 ReportExtensionsLoaded(extensions.release()); | 938 ReportExtensionsLoaded(extensions.release()); |
| 892 } | 939 } |
| 940 |
| 941 scoped_version_dir.Take(); |
| 893 } | 942 } |
| 894 | 943 |
| 895 void ExtensionsServiceBackend::ReportExtensionInstallError( | 944 void ExtensionsServiceBackend::ReportExtensionInstallError( |
| 896 const FilePath& extension_path, const std::string &error) { | 945 const FilePath& extension_path, const std::string &error) { |
| 897 | 946 |
| 898 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux. | 947 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux. |
| 899 std::string path_str = WideToASCII(extension_path.ToWStringHack()); | 948 std::string path_str = WideToASCII(extension_path.ToWStringHack()); |
| 900 std::string message = | 949 std::string message = |
| 901 StringPrintf("Could not install extension from '%s'. %s", | 950 StringPrintf("Could not install extension from '%s'. %s", |
| 902 path_str.c_str(), error.c_str()); | 951 path_str.c_str(), error.c_str()); |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1030 | 1079 |
| 1031 bool ExtensionsServiceBackend::ShouldInstall(const std::string& id, | 1080 bool ExtensionsServiceBackend::ShouldInstall(const std::string& id, |
| 1032 const std::string& version) { | 1081 const std::string& version) { |
| 1033 FilePath dir(install_directory_.AppendASCII(id.c_str())); | 1082 FilePath dir(install_directory_.AppendASCII(id.c_str())); |
| 1034 std::string current_version; | 1083 std::string current_version; |
| 1035 if (ReadCurrentVersion(dir, ¤t_version)) { | 1084 if (ReadCurrentVersion(dir, ¤t_version)) { |
| 1036 return CheckCurrentVersion(version, current_version, dir); | 1085 return CheckCurrentVersion(version, current_version, dir); |
| 1037 } | 1086 } |
| 1038 return true; | 1087 return true; |
| 1039 } | 1088 } |
| OLD | NEW |