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