普通文本  |  215行  |  7.91 KB

// Copyright (c) 2012 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/ui/certificate_dialogs.h"

#include <algorithm>
#include <vector>

#include "base/base64.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/common/net/x509_certificate_model.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/filename_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "url/gurl.h"

using content::BrowserThread;
using content::WebContents;

namespace {

void WriterCallback(const base::FilePath& path, const std::string& data) {
  int bytes_written = base::WriteFile(path, data.data(), data.size());
  if (bytes_written != static_cast<ssize_t>(data.size())) {
    LOG(ERROR) << "Writing " << path.value() << " ("
               << data.size() << "B) returned " << bytes_written;
  }
}

void WriteFileOnFileThread(const base::FilePath& path,
                           const std::string& data) {
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE, base::Bind(&WriterCallback, path, data));
}

std::string WrapAt64(const std::string &str) {
  std::string result;
  for (size_t i = 0; i < str.size(); i += 64) {
    result.append(str, i, 64);  // Append clamps the len arg internally.
    result.append("\r\n");
  }
  return result;
}

std::string GetBase64String(net::X509Certificate::OSCertHandle cert) {
  std::string der_cert;
  if (!net::X509Certificate::GetDEREncoded(cert, &der_cert))
    return std::string();
  std::string base64;
  base::Base64Encode(der_cert, &base64);
  return "-----BEGIN CERTIFICATE-----\r\n" +
      WrapAt64(base64) +
      "-----END CERTIFICATE-----\r\n";
}

////////////////////////////////////////////////////////////////////////////////
// General utility functions.

class Exporter : public ui::SelectFileDialog::Listener {
 public:
  Exporter(WebContents* web_contents,
           gfx::NativeWindow parent,
           net::X509Certificate::OSCertHandles::iterator certs_begin,
           net::X509Certificate::OSCertHandles::iterator certs_end);
  virtual ~Exporter();

  // SelectFileDialog::Listener implemenation.
  virtual void FileSelected(const base::FilePath& path,
                            int index, void* params) OVERRIDE;
  virtual void FileSelectionCanceled(void* params) OVERRIDE;
 private:
  scoped_refptr<ui::SelectFileDialog> select_file_dialog_;

  // The certificate hierarchy (leaf cert first).
  net::X509Certificate::OSCertHandles cert_chain_list_;
};

Exporter::Exporter(WebContents* web_contents,
                   gfx::NativeWindow parent,
                   net::X509Certificate::OSCertHandles::iterator certs_begin,
                   net::X509Certificate::OSCertHandles::iterator certs_end)
    : select_file_dialog_(ui::SelectFileDialog::Create(
          this,
          new ChromeSelectFilePolicy(web_contents))) {
  DCHECK(certs_begin != certs_end);
  for (net::X509Certificate::OSCertHandles::iterator i = certs_begin;
       i != certs_end;
       ++i) {
    cert_chain_list_.push_back(net::X509Certificate::DupOSCertHandle(*i));
  }

  // TODO(mattm): should this default to some directory?
  // Maybe SavePackage::GetSaveDirPreference? (Except that it's private.)
  std::string cert_title = x509_certificate_model::GetTitle(*certs_begin);
  base::FilePath suggested_path =
      net::GenerateFileName(GURL::EmptyGURL(),  // url
                            std::string(),      // content_disposition
                            std::string(),      // referrer_charset
                            cert_title,         // suggested_name
                            std::string(),      // mime_type
                            "certificate");     // default_name

  ShowCertSelectFileDialog(select_file_dialog_.get(),
                           ui::SelectFileDialog::SELECT_SAVEAS_FILE,
                           suggested_path,
                           parent,
                           NULL);
}

Exporter::~Exporter() {
  // There may be pending file dialogs, we need to tell them that we've gone
  // away so they don't try and call back to us.
  if (select_file_dialog_.get())
    select_file_dialog_->ListenerDestroyed();

  std::for_each(cert_chain_list_.begin(),
                cert_chain_list_.end(),
                &net::X509Certificate::FreeOSCertHandle);
}

void Exporter::FileSelected(const base::FilePath& path, int index,
                            void* params) {
  std::string data;
  switch (index) {
    case 2:
      for (size_t i = 0; i < cert_chain_list_.size(); ++i)
        data += GetBase64String(cert_chain_list_[i]);
      break;
    case 3:
      net::X509Certificate::GetDEREncoded(cert_chain_list_[0], &data);
      break;
    case 4:
      data = x509_certificate_model::GetCMSString(cert_chain_list_, 0, 1);
      break;
    case 5:
      data = x509_certificate_model::GetCMSString(
          cert_chain_list_, 0, cert_chain_list_.size());
      break;
    case 1:
    default:
      data = GetBase64String(cert_chain_list_[0]);
      break;
  }

  if (!data.empty())
    WriteFileOnFileThread(path, data);

  delete this;
}

void Exporter::FileSelectionCanceled(void* params) {
  delete this;
}

} // namespace

void ShowCertSelectFileDialog(ui::SelectFileDialog* select_file_dialog,
                              ui::SelectFileDialog::Type type,
                              const base::FilePath& suggested_path,
                              gfx::NativeWindow parent,
                              void* params) {
  ui::SelectFileDialog::FileTypeInfo file_type_info;
  file_type_info.extensions.resize(5);
  file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pem"));
  file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt"));
  file_type_info.extension_description_overrides.push_back(
      l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64));
  file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("pem"));
  file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("crt"));
  file_type_info.extension_description_overrides.push_back(
      l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64_CHAIN));
  file_type_info.extensions[2].push_back(FILE_PATH_LITERAL("der"));
  file_type_info.extension_description_overrides.push_back(
      l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_DER));
  file_type_info.extensions[3].push_back(FILE_PATH_LITERAL("p7c"));
  file_type_info.extension_description_overrides.push_back(
      l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7));
  file_type_info.extensions[4].push_back(FILE_PATH_LITERAL("p7c"));
  file_type_info.extension_description_overrides.push_back(
      l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7_CHAIN));
  file_type_info.include_all_files = true;
  select_file_dialog->SelectFile(
      type, base::string16(),
      suggested_path, &file_type_info,
      1,  // 1-based index for |file_type_info.extensions| to specify default.
      FILE_PATH_LITERAL("crt"),
      parent, params);
}

void ShowCertExportDialog(WebContents* web_contents,
                          gfx::NativeWindow parent,
                          const scoped_refptr<net::X509Certificate>& cert) {
  net::X509Certificate::OSCertHandles cert_chain;
  cert_chain.push_back(cert->os_cert_handle());
  const net::X509Certificate::OSCertHandles& certs =
      cert->GetIntermediateCertificates();
  cert_chain.insert(cert_chain.end(), certs.begin(), certs.end());
  new Exporter(web_contents, parent, cert_chain.begin(), cert_chain.end());
}

void ShowCertExportDialog(
    content::WebContents* web_contents,
    gfx::NativeWindow parent,
    net::X509Certificate::OSCertHandles::iterator certs_begin,
    net::X509Certificate::OSCertHandles::iterator certs_end) {
  new Exporter(web_contents, parent, certs_begin, certs_end);
}