Skeleton app installer for Chrome Updater.

BUG=967689

Change-Id: I8547f5c8b047dc1ddffc99fd576eb67bf817e22f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1630519
Reviewed-by: Joshua Pawlicki <[email protected]>
Reviewed-by: Devlin <[email protected]>
Commit-Queue: Sorin Jianu <[email protected]>
Cr-Commit-Position: refs/heads/master@{#664020}
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 186a2dbd..1d13162 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -31,6 +31,8 @@
       "crash_client.h",
       "crash_reporter.cc",
       "crash_reporter.h",
+      "installer.cc",
+      "installer.h",
       "patcher.cc",
       "patcher.h",
       "prefs.cc",
diff --git a/chrome/updater/DEPS b/chrome/updater/DEPS
index 843c0ac..5aa062e 100644
--- a/chrome/updater/DEPS
+++ b/chrome/updater/DEPS
@@ -1,11 +1,12 @@
 include_rules = [
   "+components/crash/core/common",
+  "+components/crx_file",
   "+components/prefs",
   "+components/services/patch",
   "+components/services/unzip",
   "+components/update_client",
   "+components/version_info",
   "+courgette",
-  "+third_party/zlib/google",
   "+third_party/crashpad",
+  "+third_party/zlib/google",
 ]
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc
new file mode 100644
index 0000000..8b2807a
--- /dev/null
+++ b/chrome/updater/installer.cc
@@ -0,0 +1,185 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/installer.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/util.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+
+namespace updater {
+
+namespace {
+
+// Version "0" corresponds to no installed version.
+const char kNullVersion[] = "0.0.0.0";
+
+}  // namespace
+
+Installer::InstallInfo::InstallInfo() : version(kNullVersion) {}
+Installer::InstallInfo::~InstallInfo() = default;
+
+Installer::Installer(const std::vector<uint8_t>& pk_hash)
+    : pk_hash_(pk_hash),
+      crx_id_(update_client::GetCrxIdFromPublicKeyHash(pk_hash)),
+      install_info_(std::make_unique<InstallInfo>()) {}
+
+Installer::~Installer() = default;
+
+update_client::CrxComponent Installer::MakeCrxComponent() {
+  update_client::CrxComponent component;
+  component.installer = scoped_refptr<Installer>(this);
+  component.requires_network_encryption = false;
+  component.crx_format_requirement =
+      crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF;
+  component.pk_hash = pk_hash_;
+  component.name = crx_id_;
+  component.version = install_info_->version;
+  component.fingerprint = install_info_->fingerprint;
+  return component;
+}
+
+void Installer::FindInstallOfApp() {
+  VLOG(1) << __func__ << " for " << crx_id_;
+
+  base::FilePath root_install_path;
+  if (!GetProductDataDirectory(&root_install_path)) {
+    install_info_ = std::make_unique<InstallInfo>();
+    return;
+  }
+  root_install_path = root_install_path.AppendASCII(crx_id_);
+  if (!base::PathExists(root_install_path)) {
+    install_info_ = std::make_unique<InstallInfo>();
+    return;
+  }
+
+  base::Version latest_version(kNullVersion);
+  base::FilePath latest_path;
+  std::vector<base::FilePath> older_paths;
+  base::FileEnumerator file_enumerator(root_install_path, false,
+                                       base::FileEnumerator::DIRECTORIES);
+  for (auto path = file_enumerator.Next(); !path.value().empty();
+       path = file_enumerator.Next()) {
+    const base::Version version(path.BaseName().MaybeAsASCII());
+
+    // Ignore folders that don't have valid version names.
+    if (!version.IsValid())
+      continue;
+
+    // The |version| not newer than the latest found version is marked for
+    // removal. |kNullVersion| is also removed.
+    if (version.CompareTo(latest_version) <= 0) {
+      older_paths.push_back(path);
+      continue;
+    }
+
+    // New valid |version| folder found.
+    if (!latest_path.empty())
+      older_paths.push_back(latest_path);
+
+    latest_version = version;
+    latest_path = path;
+  }
+
+  install_info_->version = latest_version;
+  install_info_->install_dir = latest_path;
+  install_info_->manifest = update_client::ReadManifest(latest_path);
+  base::ReadFileToString(latest_path.AppendASCII("manifest.fingerprint"),
+                         &install_info_->fingerprint);
+
+  for (const auto& older_path : older_paths)
+    base::DeleteFile(older_path, true);
+}
+
+Installer::Result Installer::InstallHelper(const base::FilePath& unpack_path) {
+  auto local_manifest = update_client::ReadManifest(unpack_path);
+  if (!local_manifest)
+    return Result(update_client::InstallError::BAD_MANIFEST);
+
+  std::string version_ascii;
+  local_manifest->GetStringASCII("version", &version_ascii);
+  const base::Version manifest_version(version_ascii);
+
+  VLOG(1) << "Installed version=" << install_info_->version.GetString()
+          << ", installing version=" << manifest_version.GetString();
+
+  if (!manifest_version.IsValid())
+    return Result(update_client::InstallError::INVALID_VERSION);
+
+  if (install_info_->version.CompareTo(manifest_version) > 0)
+    return Result(update_client::InstallError::VERSION_NOT_UPGRADED);
+
+  base::FilePath root_install_path;
+  if (!GetProductDataDirectory(&root_install_path))
+    return Result(update_client::InstallError::NO_DIR_COMPONENT_USER);
+
+  root_install_path = root_install_path.AppendASCII(crx_id_);
+  if (!base::CreateDirectory(root_install_path)) {
+    return Result(
+        static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) +
+        kCustomInstallErrorCreateAppInstallDirectory);
+  }
+
+  const auto versioned_install_path =
+      root_install_path.AppendASCII(manifest_version.GetString());
+  if (base::PathExists(versioned_install_path)) {
+    if (!base::DeleteFile(versioned_install_path, true))
+      return Result(update_client::InstallError::CLEAN_INSTALL_DIR_FAILED);
+  }
+
+  VLOG(1) << "Install_path=" << versioned_install_path.AsUTF8Unsafe();
+
+  if (!base::Move(unpack_path, versioned_install_path)) {
+    PLOG(ERROR) << "Move failed.";
+    base::DeleteFile(versioned_install_path, true);
+    return Result(update_client::InstallError::MOVE_FILES_ERROR);
+  }
+
+  DCHECK(!base::PathExists(unpack_path));
+  DCHECK(base::PathExists(versioned_install_path));
+
+  install_info_->manifest = std::move(local_manifest);
+  install_info_->version = manifest_version;
+  install_info_->install_dir = versioned_install_path;
+  base::ReadFileToString(
+      versioned_install_path.AppendASCII("manifest.fingerprint"),
+      &install_info_->fingerprint);
+
+  return Result(update_client::InstallError::NONE);
+}
+
+void Installer::OnUpdateError(int error) {
+  LOG(ERROR) << "updater error: " << error << " for " << crx_id_;
+}
+
+void Installer::Install(const base::FilePath& unpack_path,
+                        const std::string& public_key,
+                        Callback callback) {
+  std::unique_ptr<base::DictionaryValue> manifest;
+  base::Version version;
+  base::FilePath install_path;
+
+  const auto result = InstallHelper(unpack_path);
+  base::DeleteFile(unpack_path, true);
+  std::move(callback).Run(result);
+}
+
+bool Installer::GetInstalledFile(const std::string& file,
+                                 base::FilePath* installed_file) {
+  return false;
+}
+
+bool Installer::Uninstall() {
+  return false;
+}
+
+}  // namespace updater
diff --git a/chrome/updater/installer.h b/chrome/updater/installer.h
new file mode 100644
index 0000000..ef97e84
--- /dev/null
+++ b/chrome/updater/installer.h
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_INSTALLER_H_
+#define CHROME_UPDATER_INSTALLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "components/update_client/update_client.h"
+
+namespace updater {
+
+class Installer final : public update_client::CrxInstaller {
+ public:
+  struct InstallInfo {
+    InstallInfo();
+    ~InstallInfo();
+
+    base::FilePath install_dir;
+    base::Version version;
+    std::string fingerprint;
+    std::unique_ptr<base::DictionaryValue> manifest;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(InstallInfo);
+  };
+
+  explicit Installer(const std::vector<uint8_t>& pk_hash);
+
+  const std::string crx_id() const { return crx_id_; }
+
+  // Finds the highest version install of the app, and updates the install
+  // info for this installer instance.
+  void FindInstallOfApp();
+
+  // Returns a CrxComponent instance that describes the current install
+  // state of the app.
+  update_client::CrxComponent MakeCrxComponent();
+
+ private:
+  ~Installer() override;
+
+  // Overrides from update_client::CrxInstaller.
+  void OnUpdateError(int error) override;
+  void Install(const base::FilePath& unpack_path,
+               const std::string& public_key,
+               Callback callback) override;
+  bool GetInstalledFile(const std::string& file,
+                        base::FilePath* installed_file) override;
+  bool Uninstall() override;
+
+  Result InstallHelper(const base::FilePath& unpack_path);
+
+  const std::vector<uint8_t> pk_hash_;
+  const std::string crx_id_;
+  std::unique_ptr<InstallInfo> install_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(Installer);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_INSTALLER_H_
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index ef1a9d4..0aae5e93 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/updater.h"
 
+#include <stdint.h>
+
 #include <iterator>
 #include <memory>
 #include <string>
@@ -11,7 +13,7 @@
 #include <vector>
 
 #include "base/at_exit.h"
-#include "base/callback_forward.h"
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
@@ -30,6 +32,7 @@
 #include "chrome/updater/configurator.h"
 #include "chrome/updater/crash_client.h"
 #include "chrome/updater/crash_reporter.h"
+#include "chrome/updater/installer.h"
 #include "chrome/updater/updater_constants.h"
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util.h"
@@ -68,12 +71,16 @@
 
   // Overrides for update_client::UpdateClient::Observer.
   void OnEvent(Events event, const std::string& id) override {
-    update_client::CrxUpdateItem item;
-    update_client_->GetCrxUpdateState(id, &item);
+    update_client_->GetCrxUpdateState(id, &crx_update_item_);
+  }
+
+  const update_client::CrxUpdateItem& crx_update_item() const {
+    return crx_update_item_;
   }
 
  private:
   scoped_refptr<update_client::UpdateClient> update_client_;
+  update_client::CrxUpdateItem crx_update_item_;
   DISALLOW_COPY_AND_ASSIGN(Observer);
 };
 
@@ -100,11 +107,11 @@
       "process_type");
   crash_key_process_type.Set("updater");
 
-  if (CrashClient::GetInstance()->InitializeCrashReporting()) {
+  if (CrashClient::GetInstance()->InitializeCrashReporting())
     VLOG(1) << "Crash reporting initialized.";
-  } else {
+  else
     VLOG(1) << "Crash reporting is not available.";
-  }
+
   StartCrashReporter(UPDATER_VERSION_STRING);
 
   ThreadPoolStart();
@@ -138,12 +145,16 @@
     return *ptr;
   }
 
+  auto installer = base::MakeRefCounted<Installer>(
+      std::vector<uint8_t>(std::cbegin(mimo_hash), std::cend(mimo_hash)));
+  installer->FindInstallOfApp();
+  const auto component = installer->MakeCrxComponent();
+
   base::MessageLoopForUI message_loop;
   base::RunLoop runloop;
   DCHECK(base::ThreadTaskRunnerHandle::IsSet());
 
   auto config = base::MakeRefCounted<Configurator>();
-
   {
     base::ScopedDisallowBlocking no_blocking_allowed;
 
@@ -152,20 +163,17 @@
     Observer observer(update_client);
     update_client->AddObserver(&observer);
 
-    const std::vector<std::string> ids = {"mimojjlkmoijpicakmndhoigimigcmbb"};
+    const std::vector<std::string> ids = {installer->crx_id()};
     update_client->Update(
         ids,
         base::BindOnce(
-            [](const std::vector<std::string>& ids)
+            [](const update_client::CrxComponent& component,
+               const std::vector<std::string>& ids)
                 -> std::vector<base::Optional<update_client::CrxComponent>> {
-              update_client::CrxComponent component;
-              component.name = "mimo";
-              component.pk_hash.assign(std::begin(mimo_hash),
-                                       std::end(mimo_hash));
-              component.version = base::Version("0.0");
-              component.requires_network_encryption = false;
+              DCHECK_EQ(1u, ids.size());
               return {component};
-            }),
+            },
+            component),
         true,
         base::BindOnce(
             [](base::OnceClosure closure, update_client::Error error) {
@@ -176,6 +184,21 @@
 
     runloop.Run();
 
+    const auto& update_item = observer.crx_update_item();
+    switch (update_item.state) {
+      case update_client::ComponentState::kUpdated:
+        VLOG(1) << "Update success.";
+        break;
+      case update_client::ComponentState::kUpToDate:
+        VLOG(1) << "No updates.";
+        break;
+      case update_client::ComponentState::kUpdateError:
+        VLOG(1) << "Updater error: " << update_item.error_code << ".";
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
     update_client->RemoveObserver(&observer);
     update_client = nullptr;
   }
diff --git a/chrome/updater/updater_constants.h b/chrome/updater/updater_constants.h
index 4b3704b..235cddd 100644
--- a/chrome/updater/updater_constants.h
+++ b/chrome/updater/updater_constants.h
@@ -31,6 +31,11 @@
 extern const char kCrashUploadURL[];
 extern const char kCrashStagingUploadURL[];
 
+// Errors.
+//
+// The install directory for the application could not be created.
+const int kCustomInstallErrorCreateAppInstallDirectory = 0;
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_UPDATER_CONSTANTS_H_