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