1 // Copyright 2013 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 "base/bind.h" 6 #include "base/file_util.h" 7 #include "base/files/file_enumerator.h" 8 #include "base/files/file_path.h" 9 #include "base/values.h" 10 #include "base/version.h" 11 // TODO(ddorwin): Find a better place for ReadManifest. 12 #include "chrome/browser/component_updater/component_unpacker.h" 13 #include "chrome/browser/component_updater/default_component_installer.h" 14 #include "content/public/browser/browser_thread.h" 15 16 namespace { 17 // Version "0" corresponds to no installed version. By the server's conventions, 18 // we represent it as a dotted quad. 19 const char kNullVersion[] = "0.0.0.0"; 20 } // namespace 21 22 ComponentInstallerTraits::~ComponentInstallerTraits() { 23 } 24 25 DefaultComponentInstaller::DefaultComponentInstaller( 26 scoped_ptr<ComponentInstallerTraits> installer_traits) 27 : current_version_(kNullVersion) { 28 installer_traits_ = installer_traits.Pass(); 29 } 30 31 DefaultComponentInstaller::~DefaultComponentInstaller() { 32 } 33 34 void DefaultComponentInstaller::Register(ComponentUpdateService* cus) { 35 if (!installer_traits_) { 36 NOTREACHED() << "A DefaultComponentInstaller has been created but " 37 << "has no installer traits."; 38 return; 39 } 40 content::BrowserThread::PostBlockingPoolTask( 41 FROM_HERE, 42 base::Bind(&DefaultComponentInstaller::StartRegistration, 43 base::Unretained(this), 44 cus)); 45 } 46 47 void DefaultComponentInstaller::OnUpdateError(int error) { 48 NOTREACHED() << "Component update error: " << error; 49 } 50 51 bool DefaultComponentInstaller::InstallHelper( 52 const base::DictionaryValue& manifest, 53 const base::FilePath& unpack_path, 54 const base::FilePath& install_path) { 55 if (!base::Move(unpack_path, install_path)) 56 return false; 57 if (!installer_traits_->OnCustomInstall(manifest, install_path)) 58 return false; 59 if (!installer_traits_->VerifyInstallation(install_path)) 60 return false; 61 return true; 62 } 63 64 bool DefaultComponentInstaller::Install(const base::DictionaryValue& manifest, 65 const base::FilePath& unpack_path) { 66 std::string manifest_version; 67 manifest.GetStringASCII("version", &manifest_version); 68 base::Version version(manifest_version.c_str()); 69 if (!version.IsValid()) 70 return false; 71 if (current_version_.CompareTo(version) > 0) 72 return false; 73 base::FilePath install_path = 74 installer_traits_->GetBaseDirectory().AppendASCII(version.GetString()); 75 if (base::PathExists(install_path)) { 76 if (!base::DeleteFile(install_path, true)) 77 return false; 78 } 79 if (!InstallHelper(manifest, unpack_path, install_path)) { 80 base::DeleteFile(install_path, true); 81 return false; 82 } 83 current_version_ = version; 84 // TODO(ddorwin): Change the parameter to scoped_ptr<base::DictionaryValue> 85 // so we can avoid this DeepCopy. 86 current_manifest_.reset(manifest.DeepCopy()); 87 scoped_ptr<base::DictionaryValue> manifest_copy( 88 current_manifest_->DeepCopy()); 89 content::BrowserThread::PostTask( 90 content::BrowserThread::UI, FROM_HERE, 91 base::Bind(&ComponentInstallerTraits::ComponentReady, 92 base::Unretained(installer_traits_.get()), 93 current_version_, 94 GetInstallDirectory(), 95 base::Passed(&manifest_copy))); 96 return true; 97 } 98 99 bool DefaultComponentInstaller::GetInstalledFile( 100 const std::string& file, 101 base::FilePath* installed_file) { 102 if (current_version_.Equals(base::Version(kNullVersion))) 103 return false; // No component has been installed yet. 104 105 *installed_file = 106 installer_traits_->GetBaseDirectory() 107 .AppendASCII(current_version_.GetString()).AppendASCII(file); 108 return true; 109 } 110 111 void DefaultComponentInstaller::StartRegistration( 112 ComponentUpdateService* cus) { 113 base::FilePath base_dir = installer_traits_->GetBaseDirectory(); 114 if (!base::PathExists(base_dir) && 115 !base::CreateDirectory(base_dir)) { 116 NOTREACHED() << "Could not create the base directory for " 117 << installer_traits_->GetName() << " (" 118 << base_dir.MaybeAsASCII() << ")."; 119 return; 120 } 121 122 base::FilePath latest_dir; 123 base::Version latest_version(kNullVersion); 124 std::vector<base::FilePath> older_dirs; 125 bool found = false; 126 base::FileEnumerator file_enumerator( 127 base_dir, false, base::FileEnumerator::DIRECTORIES); 128 for (base::FilePath path = file_enumerator.Next(); 129 !path.value().empty(); 130 path = file_enumerator.Next()) { 131 base::Version version(path.BaseName().MaybeAsASCII()); 132 if (!version.IsValid()) 133 continue; 134 if (found) { 135 if (version.CompareTo(latest_version) > 0) { 136 older_dirs.push_back(latest_dir); 137 latest_dir = path; 138 latest_version = version; 139 } else { 140 older_dirs.push_back(path); 141 } 142 } else { 143 latest_dir = path; 144 latest_version = version; 145 found = true; 146 } 147 } 148 149 if (found) { 150 current_version_ = latest_version; 151 // TODO(ddorwin): Remove these members and pass them directly to 152 // FinishRegistration(). 153 base::ReadFileToString(latest_dir.AppendASCII("manifest.fingerprint"), 154 ¤t_fingerprint_); 155 current_manifest_= ReadManifest(latest_dir); 156 if (!current_manifest_) { 157 DLOG(ERROR) << "Failed to read manifest for " 158 << installer_traits_->GetName() << " (" 159 << base_dir.MaybeAsASCII() << ")."; 160 return; 161 } 162 } 163 164 // Remove older versions of the component. None should be in use during 165 // browser startup. 166 for (std::vector<base::FilePath>::iterator iter = older_dirs.begin(); 167 iter != older_dirs.end(); ++iter) { 168 base::DeleteFile(*iter, true); 169 } 170 171 content::BrowserThread::PostTask( 172 content::BrowserThread::UI, FROM_HERE, 173 base::Bind(&DefaultComponentInstaller::FinishRegistration, 174 base::Unretained(this), 175 cus)); 176 } 177 178 base::FilePath DefaultComponentInstaller::GetInstallDirectory() { 179 return installer_traits_->GetBaseDirectory() 180 .AppendASCII(current_version_.GetString()); 181 } 182 183 void DefaultComponentInstaller::FinishRegistration( 184 ComponentUpdateService* cus) { 185 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 186 if (installer_traits_->CanAutoUpdate()) { 187 CrxComponent crx; 188 crx.name = installer_traits_->GetName(); 189 crx.installer = this; 190 crx.version = current_version_; 191 crx.fingerprint = current_fingerprint_; 192 installer_traits_->GetHash(&crx.pk_hash); 193 ComponentUpdateService::Status status = cus->RegisterComponent(crx); 194 if (status != ComponentUpdateService::kOk && 195 status != ComponentUpdateService::kReplaced) { 196 NOTREACHED() << "Component registration failed for " 197 << installer_traits_->GetName(); 198 return; 199 } 200 } 201 202 if (current_version_.CompareTo(base::Version(kNullVersion)) > 0) { 203 scoped_ptr<base::DictionaryValue> manifest_copy( 204 current_manifest_->DeepCopy()); 205 installer_traits_->ComponentReady( 206 current_version_, 207 GetInstallDirectory(), 208 manifest_copy.Pass()); 209 } 210 } 211