Home | History | Annotate | Download | only in component_updater
      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                            &current_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