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