Home | History | Annotate | Download | only in ui
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/certificate_dialogs.h"
      6 
      7 
      8 #include <vector>
      9 
     10 #include "base/base64.h"
     11 #include "base/bind.h"
     12 #include "base/file_util.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "chrome/browser/ui/chrome_select_file_policy.h"
     16 #include "chrome/common/net/x509_certificate_model.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "grit/generated_resources.h"
     19 #include "net/base/filename_util.h"
     20 #include "ui/base/l10n/l10n_util.h"
     21 #include "ui/shell_dialogs/select_file_dialog.h"
     22 #include "url/gurl.h"
     23 
     24 using content::BrowserThread;
     25 using content::WebContents;
     26 
     27 namespace {
     28 
     29 void WriterCallback(const base::FilePath& path, const std::string& data) {
     30   int bytes_written = base::WriteFile(path, data.data(), data.size());
     31   if (bytes_written != static_cast<ssize_t>(data.size())) {
     32     LOG(ERROR) << "Writing " << path.value() << " ("
     33                << data.size() << "B) returned " << bytes_written;
     34   }
     35 }
     36 
     37 void WriteFileOnFileThread(const base::FilePath& path,
     38                            const std::string& data) {
     39   BrowserThread::PostTask(
     40       BrowserThread::FILE, FROM_HERE, base::Bind(&WriterCallback, path, data));
     41 }
     42 
     43 std::string WrapAt64(const std::string &str) {
     44   std::string result;
     45   for (size_t i = 0; i < str.size(); i += 64) {
     46     result.append(str, i, 64);  // Append clamps the len arg internally.
     47     result.append("\r\n");
     48   }
     49   return result;
     50 }
     51 
     52 std::string GetBase64String(net::X509Certificate::OSCertHandle cert) {
     53   std::string base64;
     54   base::Base64Encode(x509_certificate_model::GetDerString(cert), &base64);
     55   return "-----BEGIN CERTIFICATE-----\r\n" +
     56       WrapAt64(base64) +
     57       "-----END CERTIFICATE-----\r\n";
     58 }
     59 
     60 ////////////////////////////////////////////////////////////////////////////////
     61 // General utility functions.
     62 
     63 class Exporter : public ui::SelectFileDialog::Listener {
     64  public:
     65   Exporter(WebContents* web_contents, gfx::NativeWindow parent,
     66            net::X509Certificate::OSCertHandle cert);
     67   virtual ~Exporter();
     68 
     69   // SelectFileDialog::Listener implemenation.
     70   virtual void FileSelected(const base::FilePath& path,
     71                             int index, void* params) OVERRIDE;
     72   virtual void FileSelectionCanceled(void* params) OVERRIDE;
     73  private:
     74   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
     75 
     76   // The certificate hierarchy (leaf cert first).
     77   net::X509Certificate::OSCertHandles cert_chain_list_;
     78 };
     79 
     80 Exporter::Exporter(WebContents* web_contents,
     81                    gfx::NativeWindow parent,
     82                    net::X509Certificate::OSCertHandle cert)
     83     : select_file_dialog_(ui::SelectFileDialog::Create(
     84         this, new ChromeSelectFilePolicy(web_contents))) {
     85   x509_certificate_model::GetCertChainFromCert(cert, &cert_chain_list_);
     86 
     87   // TODO(mattm): should this default to some directory?
     88   // Maybe SavePackage::GetSaveDirPreference? (Except that it's private.)
     89   std::string cert_title = x509_certificate_model::GetTitle(cert);
     90   base::FilePath suggested_path =
     91       net::GenerateFileName(GURL::EmptyGURL(),  // url
     92                             std::string(),      // content_disposition
     93                             std::string(),      // referrer_charset
     94                             cert_title,         // suggested_name
     95                             std::string(),      // mime_type
     96                             "certificate");     // default_name
     97 
     98   ShowCertSelectFileDialog(select_file_dialog_.get(),
     99                            ui::SelectFileDialog::SELECT_SAVEAS_FILE,
    100                            suggested_path,
    101                            parent,
    102                            NULL);
    103 }
    104 
    105 Exporter::~Exporter() {
    106   // There may be pending file dialogs, we need to tell them that we've gone
    107   // away so they don't try and call back to us.
    108   if (select_file_dialog_.get())
    109     select_file_dialog_->ListenerDestroyed();
    110 
    111   x509_certificate_model::DestroyCertChain(&cert_chain_list_);
    112 }
    113 
    114 void Exporter::FileSelected(const base::FilePath& path, int index,
    115                             void* params) {
    116   std::string data;
    117   switch (index) {
    118     case 2:
    119       for (size_t i = 0; i < cert_chain_list_.size(); ++i)
    120         data += GetBase64String(cert_chain_list_[i]);
    121       break;
    122     case 3:
    123       data = x509_certificate_model::GetDerString(cert_chain_list_[0]);
    124       break;
    125     case 4:
    126       data = x509_certificate_model::GetCMSString(cert_chain_list_, 0, 1);
    127       break;
    128     case 5:
    129       data = x509_certificate_model::GetCMSString(
    130           cert_chain_list_, 0, cert_chain_list_.size());
    131       break;
    132     case 1:
    133     default:
    134       data = GetBase64String(cert_chain_list_[0]);
    135       break;
    136   }
    137 
    138   if (!data.empty())
    139     WriteFileOnFileThread(path, data);
    140 
    141   delete this;
    142 }
    143 
    144 void Exporter::FileSelectionCanceled(void* params) {
    145   delete this;
    146 }
    147 
    148 } // namespace
    149 
    150 void ShowCertSelectFileDialog(ui::SelectFileDialog* select_file_dialog,
    151                               ui::SelectFileDialog::Type type,
    152                               const base::FilePath& suggested_path,
    153                               gfx::NativeWindow parent,
    154                               void* params) {
    155   ui::SelectFileDialog::FileTypeInfo file_type_info;
    156   file_type_info.extensions.resize(5);
    157   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pem"));
    158   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt"));
    159   file_type_info.extension_description_overrides.push_back(
    160       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64));
    161   file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("pem"));
    162   file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("crt"));
    163   file_type_info.extension_description_overrides.push_back(
    164       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64_CHAIN));
    165   file_type_info.extensions[2].push_back(FILE_PATH_LITERAL("der"));
    166   file_type_info.extension_description_overrides.push_back(
    167       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_DER));
    168   file_type_info.extensions[3].push_back(FILE_PATH_LITERAL("p7c"));
    169   file_type_info.extension_description_overrides.push_back(
    170       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7));
    171   file_type_info.extensions[4].push_back(FILE_PATH_LITERAL("p7c"));
    172   file_type_info.extension_description_overrides.push_back(
    173       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7_CHAIN));
    174   file_type_info.include_all_files = true;
    175   select_file_dialog->SelectFile(
    176       type, base::string16(),
    177       suggested_path, &file_type_info,
    178       1,  // 1-based index for |file_type_info.extensions| to specify default.
    179       FILE_PATH_LITERAL("crt"),
    180       parent, params);
    181 }
    182 
    183 void ShowCertExportDialog(WebContents* web_contents,
    184                           gfx::NativeWindow parent,
    185                           net::X509Certificate::OSCertHandle cert) {
    186   new Exporter(web_contents, parent, cert);
    187 }
    188