chromium/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/smb_client/smb_persisted_share_registry.h"

#include <utility>

#include "base/base64.h"
#include "base/check.h"
#include "base/logging.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/scoped_user_pref_update.h"

namespace ash::smb_client {
namespace {

constexpr char kShareUrlKey[] = "share_url";
constexpr char kDisplayNameKey[] = "display_name";
constexpr char kUsernameKey[] = "username";
constexpr char kWorkgroupKey[] = "workgroup";
constexpr char kUseKerberosKey[] = "use_kerberos";
constexpr char kPasswordSaltKey[] = "password_salt";

base::Value ShareToDict(const SmbShareInfo& share) {
  base::Value::Dict dict = base::Value::Dict()
                               .Set(kShareUrlKey, share.share_url().ToString())
                               .Set(kDisplayNameKey, share.display_name())
                               .Set(kUseKerberosKey, share.use_kerberos());
  if (!share.username().empty()) {
    dict.Set(kUsernameKey, share.username());
  }
  if (!share.workgroup().empty()) {
    dict.Set(kWorkgroupKey, share.workgroup());
  }
  if (!share.password_salt().empty()) {
    // Blob base::Values can't be stored in prefs, so store as a base64 encoded
    // string.
    dict.Set(kPasswordSaltKey, base::Base64Encode(share.password_salt()));
  }
  return base::Value(std::move(dict));
}

std::string GetStringValue(const base::Value::Dict& dict,
                           const std::string& key) {
  const std::string* value = dict.FindString(key);
  if (!value) {
    return {};
  }
  return *value;
}

std::vector<uint8_t> GetEncodedBinaryValue(const base::Value::Dict& dict,
                                           const std::string& key) {
  const std::string* encoded_value = dict.FindString(key);
  if (!encoded_value) {
    return {};
  }
  std::string decoded_value;
  if (!base::Base64Decode(*encoded_value, &decoded_value)) {
    LOG(ERROR) << "Unable to decode base64-encoded binary pref from key: "
               << key;
    return {};
  }
  return {decoded_value.begin(), decoded_value.end()};
}

std::optional<SmbShareInfo> DictToShare(const base::Value::Dict& dict) {
  std::string share_url = GetStringValue(dict, kShareUrlKey);
  if (share_url.empty()) {
    return {};
  }

  SmbUrl url(share_url);
  DCHECK(url.IsValid());
  SmbShareInfo info(url, GetStringValue(dict, kDisplayNameKey),
                    GetStringValue(dict, kUsernameKey),
                    GetStringValue(dict, kWorkgroupKey),
                    dict.FindBool(kUseKerberosKey).value_or(false),
                    GetEncodedBinaryValue(dict, kPasswordSaltKey));
  return std::make_optional(std::move(info));
}

}  // namespace

// static
void SmbPersistedShareRegistry::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterListPref(prefs::kNetworkFileSharesSavedShares);
}

SmbPersistedShareRegistry::SmbPersistedShareRegistry(Profile* profile)
    : profile_(profile) {}

void SmbPersistedShareRegistry::Save(const SmbShareInfo& share) {
  ScopedListPrefUpdate pref(profile_->GetPrefs(),
                            prefs::kNetworkFileSharesSavedShares);

  base::Value::List& share_list = pref.Get();
  for (base::Value& item : share_list) {
    if (GetStringValue(item.GetDict(), kShareUrlKey) ==
        share.share_url().ToString()) {
      item = ShareToDict(share);
      return;
    }
  }

  pref->Append(ShareToDict(share));
  return;
}

void SmbPersistedShareRegistry::Delete(const SmbUrl& share_url) {
  ScopedListPrefUpdate pref(profile_->GetPrefs(),
                            prefs::kNetworkFileSharesSavedShares);

  base::Value::List& list_update = pref.Get();
  for (auto it = list_update.begin(); it != list_update.end(); ++it) {
    if (GetStringValue(it->GetDict(), kShareUrlKey) == share_url.ToString()) {
      list_update.erase(it);
      return;
    }
  }
}

std::optional<SmbShareInfo> SmbPersistedShareRegistry::Get(
    const SmbUrl& share_url) const {
  const base::Value& pref =
      profile_->GetPrefs()->GetValue(prefs::kNetworkFileSharesSavedShares);

  for (const base::Value& entry : pref.GetList()) {
    if (GetStringValue(entry.GetDict(), kShareUrlKey) == share_url.ToString()) {
      return DictToShare(entry.GetDict());
    }
  }
  return {};
}

std::vector<SmbShareInfo> SmbPersistedShareRegistry::GetAll() const {
  const base::Value& pref =
      profile_->GetPrefs()->GetValue(prefs::kNetworkFileSharesSavedShares);

  std::vector<SmbShareInfo> shares;
  for (const auto& entry : pref.GetList()) {
    std::optional<SmbShareInfo> info = DictToShare(entry.GetDict());
    if (info) {
      shares.push_back(std::move(*info));
    }
  }
  return shares;
}

}  // namespace ash::smb_client