// Copyright 2014 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/browser/extensions/extension_management_internal.h"

#include "base/logging.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/extensions/extension_management_constants.h"
#include "extensions/common/url_pattern_set.h"
#include "url/gurl.h"

namespace extensions {

namespace internal {

namespace {
const char kMalformedPreferenceWarning[] =
    "Malformed extension management preference.";
}  // namespace

IndividualSettings::IndividualSettings() {
  Reset();
}

// Initializes from default settings.
IndividualSettings::IndividualSettings(
    const IndividualSettings* default_settings) {
  installation_mode = default_settings->installation_mode;
  update_url = default_settings->installation_mode;
  blocked_permissions = default_settings->blocked_permissions;
  // We are not initializing |minimum_version_required| from |default_settings|
  // here since it's not applicable to default settings.
}

IndividualSettings::~IndividualSettings() {
}

bool IndividualSettings::Parse(const base::DictionaryValue* dict,
                               ParsingScope scope) {
  std::string installation_mode_str;
  if (dict->GetStringWithoutPathExpansion(schema_constants::kInstallationMode,
                                          &installation_mode_str)) {
    if (installation_mode_str == schema_constants::kAllowed) {
      installation_mode = ExtensionManagement::INSTALLATION_ALLOWED;
    } else if (installation_mode_str == schema_constants::kBlocked) {
      installation_mode = ExtensionManagement::INSTALLATION_BLOCKED;
    } else if (installation_mode_str == schema_constants::kForceInstalled) {
      installation_mode = ExtensionManagement::INSTALLATION_FORCED;
    } else if (installation_mode_str == schema_constants::kNormalInstalled) {
      installation_mode = ExtensionManagement::INSTALLATION_RECOMMENDED;
    } else {
      // Invalid value for 'installation_mode'.
      LOG(WARNING) << kMalformedPreferenceWarning;
      return false;
    }

    // Only proceed to fetch update url if force or recommended install mode
    // is set.
    if (installation_mode == ExtensionManagement::INSTALLATION_FORCED ||
        installation_mode == ExtensionManagement::INSTALLATION_RECOMMENDED) {
      if (scope != SCOPE_INDIVIDUAL) {
        // Only individual extensions are allowed to be automatically installed.
        LOG(WARNING) << kMalformedPreferenceWarning;
        return false;
      }
      std::string update_url_str;
      if (dict->GetStringWithoutPathExpansion(schema_constants::kUpdateUrl,
                                              &update_url_str) &&
          GURL(update_url_str).is_valid()) {
        update_url = update_url_str;
      } else {
        // No valid update URL for extension.
        LOG(WARNING) << kMalformedPreferenceWarning;
        return false;
      }
    }
  }

  // Parses the blocked permission settings.
  const base::ListValue* list_value = nullptr;
  base::string16 error;

  // If applicable, inherit from global block list and remove all explicitly
  // allowed permissions.
  if (scope != SCOPE_DEFAULT &&
      dict->GetListWithoutPathExpansion(schema_constants::kAllowedPermissions,
                                        &list_value)) {
    // It is assumed that Parse() is already called for SCOPE_DEFAULT and
    // settings specified for |this| is initialized by copying from default
    // settings, including the |blocked_permissions| setting here.
    // That is, |blocked_permissions| should be the default block permissions
    // list settings here.
    APIPermissionSet globally_blocked_permissions = blocked_permissions;
    APIPermissionSet explicitly_allowed_permissions;
    // Reuses code for parsing API permissions from manifest. But note that we
    // only support list of strings type.
    if (!APIPermissionSet::ParseFromJSON(
            list_value,
            APIPermissionSet::kDisallowInternalPermissions,
            &explicitly_allowed_permissions,
            &error,
            nullptr)) {
      // There might be unknown permissions, warn and just ignore them;
      LOG(WARNING) << error;
    }
    APIPermissionSet::Difference(globally_blocked_permissions,
                                 explicitly_allowed_permissions,
                                 &blocked_permissions);
  }

  // Then add all newly blocked permissions to the list.
  if (dict->GetListWithoutPathExpansion(schema_constants::kBlockedPermissions,
                                        &list_value)) {
    // The |blocked_permissions| might be the result of the routines above,
    // or remains the same as default block permissions settings.
    APIPermissionSet permissions_to_merge_from = blocked_permissions;
    APIPermissionSet permissions_parsed;
    if (!APIPermissionSet::ParseFromJSON(
            list_value,
            APIPermissionSet::kDisallowInternalPermissions,
            &permissions_parsed,
            &error,
            nullptr)) {
      LOG(WARNING) << error;
    }
    APIPermissionSet::Union(
        permissions_to_merge_from, permissions_parsed, &blocked_permissions);
  }

  // Parses the minimum version settings.
  std::string minimum_version_required_str;
  if (scope == SCOPE_INDIVIDUAL &&
      dict->GetStringWithoutPathExpansion(
          schema_constants::kMinimumVersionRequired,
          &minimum_version_required_str)) {
    scoped_ptr<base::Version> version(
        new Version(minimum_version_required_str));
    // We accept a general version string here. Note that count of components in
    // version string of extensions is limited to 4.
    if (!version->IsValid())
      LOG(WARNING) << kMalformedPreferenceWarning;
    else
      minimum_version_required = version.Pass();
  }

  return true;
}

void IndividualSettings::Reset() {
  installation_mode = ExtensionManagement::INSTALLATION_ALLOWED;
  update_url.clear();
  blocked_permissions.clear();
}

GlobalSettings::GlobalSettings() {
  Reset();
}

GlobalSettings::~GlobalSettings() {
}

void GlobalSettings::Reset() {
  has_restricted_install_sources = false;
  install_sources.ClearPatterns();
  has_restricted_allowed_types = false;
  allowed_types.clear();
}

}  // namespace internal

}  // namespace extensions