// Copyright (c) 2009 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/page_info_model.h"

#include "app/l10n_util.h"
#include "base/i18n/time_formatting.h"
#include "chrome/browser/cert_store.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/ssl/ssl_manager.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "grit/generated_resources.h"
#include "net/base/cert_status_flags.h"
#include "net/base/x509_certificate.h"

namespace {
  // Returns a name that can be used to represent the issuer.  It tries in this
  // order CN, O and OU and returns the first non-empty one found.
  std::string GetIssuerName(const net::X509Certificate::Principal& issuer) {
    if (!issuer.common_name.empty())
      return issuer.common_name;
    if (!issuer.organization_names.empty())
      return issuer.organization_names[0];
    if (!issuer.organization_unit_names.empty())
      return issuer.organization_unit_names[0];

    return std::string();
  }
}

PageInfoModel::PageInfoModel(Profile* profile,
                             const GURL& url,
                             const NavigationEntry::SSLStatus& ssl,
                             bool show_history,
                             PageInfoModelObserver* observer)
    : observer_(observer) {
  bool state = true;
  string16 head_line;
  string16 description;
  scoped_refptr<net::X509Certificate> cert;

  // Identity section.
  string16 subject_name(UTF8ToUTF16(url.host()));
  bool empty_subject_name = false;
  if (subject_name.empty()) {
    subject_name.assign(
        l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
    empty_subject_name = true;
  }
  if (ssl.cert_id() &&
      CertStore::GetSharedInstance()->RetrieveCert(ssl.cert_id(), &cert) &&
      !net::IsCertStatusError(ssl.cert_status())) {
    // OK HTTPS page.
    if ((ssl.cert_status() & net::CERT_STATUS_IS_EV) != 0) {
      DCHECK(!cert->subject().organization_names.empty());
      head_line =
          l10n_util::GetStringFUTF16(IDS_PAGE_INFO_EV_IDENTITY_TITLE,
              UTF8ToUTF16(cert->subject().organization_names[0]),
              UTF8ToUTF16(url.host()));
      // An EV Cert is required to have a city (localityName) and country but
      // state is "if any".
      DCHECK(!cert->subject().locality_name.empty());
      DCHECK(!cert->subject().country_name.empty());
      string16 locality;
      if (!cert->subject().state_or_province_name.empty()) {
        locality = l10n_util::GetStringFUTF16(
            IDS_PAGEINFO_ADDRESS,
            UTF8ToUTF16(cert->subject().locality_name),
            UTF8ToUTF16(cert->subject().state_or_province_name),
            UTF8ToUTF16(cert->subject().country_name));
      } else {
        locality = l10n_util::GetStringFUTF16(
            IDS_PAGEINFO_PARTIAL_ADDRESS,
            UTF8ToUTF16(cert->subject().locality_name),
            UTF8ToUTF16(cert->subject().country_name));
      }
      DCHECK(!cert->subject().organization_names.empty());
      description.assign(l10n_util::GetStringFUTF16(
          IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV,
          UTF8ToUTF16(cert->subject().organization_names[0]),
          locality,
          UTF8ToUTF16(GetIssuerName(cert->issuer()))));
    } else {
      // Non EV OK HTTPS.
      if (empty_subject_name)
        head_line.clear();  // Don't display any title.
      else
        head_line.assign(subject_name);
      string16 issuer_name(UTF8ToUTF16(GetIssuerName(cert->issuer())));
      if (issuer_name.empty()) {
        issuer_name.assign(l10n_util::GetStringUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
      } else {
        description.assign(l10n_util::GetStringFUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name));
      }
    }
  } else {
    // Bad HTTPS.
    description.assign(l10n_util::GetStringUTF16(
        IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY));
    state = false;
  }
  sections_.push_back(SectionInfo(
      state,
      l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE),
      head_line,
      description));

  // Connection section.
  // We consider anything less than 80 bits encryption to be weak encryption.
  // TODO(wtc): Bug 1198735: report mixed/unsafe content for unencrypted and
  // weakly encrypted connections.
  state = true;
  head_line.clear();
  description.clear();
  if (ssl.security_bits() <= 0) {
    state = false;
    description.assign(
        l10n_util::GetStringFUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT,
            subject_name));
  } else if (ssl.security_bits() < 80) {
    state = false;
    description.assign(
        l10n_util::GetStringFUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_WEAK_ENCRYPTION_CONNECTION_TEXT,
            subject_name));
  } else {
    description.assign(
        l10n_util::GetStringFUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_CONNECTION_TEXT,
            subject_name,
            IntToString16(ssl.security_bits())));
    if (ssl.has_mixed_content()) {
      state = false;
      description.assign(
          l10n_util::GetStringFUTF16(
              IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK,
              description,
              l10n_util::GetStringUTF16(
                  IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_MIXED_CONTENT_WARNING)));
    } else if (ssl.has_unsafe_content()) {
      state = false;
      description.assign(
          l10n_util::GetStringFUTF16(
              IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK,
              description,
              l10n_util::GetStringUTF16(
                  IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_BAD_HTTPS_WARNING)));
    }
  }
  sections_.push_back(SectionInfo(
      state,
      l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE),
      head_line,
      description));

  // Request the number of visits.
  HistoryService* history = profile->GetHistoryService(
      Profile::EXPLICIT_ACCESS);
  if (show_history && history) {
    history->GetVisitCountToHost(
        url,
        &request_consumer_,
        NewCallback(this, &PageInfoModel::OnGotVisitCountToHost));
  }
}

int PageInfoModel::GetSectionCount() {
  return sections_.size();
}

PageInfoModel::SectionInfo PageInfoModel::GetSectionInfo(int index) {
  DCHECK(index < static_cast<int>(sections_.size()));
  return sections_[index];
}

void PageInfoModel::OnGotVisitCountToHost(HistoryService::Handle handle,
                                          bool found_visits,
                                          int count,
                                          base::Time first_visit) {
  if (!found_visits) {
    // This indicates an error, such as the page wasn't http/https; do nothing.
    return;
  }

  bool visited_before_today = false;
  if (count) {
    base::Time today = base::Time::Now().LocalMidnight();
    base::Time first_visit_midnight = first_visit.LocalMidnight();
    visited_before_today = (first_visit_midnight < today);
  }

  if (!visited_before_today) {
    sections_.push_back(SectionInfo(
        false,
        l10n_util::GetStringUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_PERSONAL_HISTORY_TITLE),
        string16(),
        l10n_util::GetStringUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_FIRST_VISITED_TODAY)));
  } else {
    sections_.push_back(SectionInfo(
        true,
        l10n_util::GetStringUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_PERSONAL_HISTORY_TITLE),
        string16(),
        l10n_util::GetStringFUTF16(
            IDS_PAGE_INFO_SECURITY_TAB_VISITED_BEFORE_TODAY,
            WideToUTF16(base::TimeFormatShortDate(first_visit)))));
  }
  observer_->ModelChanged();
}

// static
void PageInfoModel::RegisterPrefs(PrefService* prefs) {
  prefs->RegisterDictionaryPref(prefs::kPageInfoWindowPlacement);
}