// Copyright (c) 2011 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. // TODO(mattm): this isn't gtk specific, it shouldn't be under the gtk dir #include "chrome/browser/ui/gtk/certificate_dialogs.h" #include <vector> #include "base/base64.h" #include "base/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/task.h" #include "chrome/common/net/x509_certificate_model.h" #include "content/browser/browser_thread.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" namespace { //////////////////////////////////////////////////////////////////////////////// // General utility functions. class Writer : public Task { public: Writer(const FilePath& path, const std::string& data) : path_(path), data_(data) { } virtual void Run() { int bytes_written = file_util::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; } } private: FilePath path_; std::string data_; }; void WriteFileOnFileThread(const FilePath& path, const std::string& data) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, new Writer(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 base64; if (!base::Base64Encode( x509_certificate_model::GetDerString(cert), &base64)) { LOG(ERROR) << "base64 encoding error"; return ""; } return "-----BEGIN CERTIFICATE-----\r\n" + WrapAt64(base64) + "-----END CERTIFICATE-----\r\n"; } //////////////////////////////////////////////////////////////////////////////// // General utility functions. class Exporter : public SelectFileDialog::Listener { public: Exporter(TabContents* tab_contents, gfx::NativeWindow parent, net::X509Certificate::OSCertHandle cert); ~Exporter(); // SelectFileDialog::Listener implemenation. virtual void FileSelected(const FilePath& path, int index, void* params); virtual void FileSelectionCanceled(void* params); private: scoped_refptr<SelectFileDialog> select_file_dialog_; // The certificate hierarchy (leaf cert first). net::X509Certificate::OSCertHandles cert_chain_list_; }; Exporter::Exporter(TabContents* tab_contents, gfx::NativeWindow parent, net::X509Certificate::OSCertHandle cert) : select_file_dialog_(SelectFileDialog::Create(this)) { x509_certificate_model::GetCertChainFromCert(cert, &cert_chain_list_); // TODO(mattm): should this default to some directory? // Maybe SavePackage::GetSaveDirPreference? (Except that it's private.) FilePath suggested_path("certificate"); std::string cert_title = x509_certificate_model::GetTitle(cert); if (!cert_title.empty()) suggested_path = FilePath(cert_title); ShowCertSelectFileDialog(select_file_dialog_.get(), SelectFileDialog::SELECT_SAVEAS_FILE, suggested_path, tab_contents, 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(); x509_certificate_model::DestroyCertChain(&cert_chain_list_); } void Exporter::FileSelected(const 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: data = x509_certificate_model::GetDerString(cert_chain_list_[0]); 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(SelectFileDialog* select_file_dialog, SelectFileDialog::Type type, const FilePath& suggested_path, TabContents* tab_contents, gfx::NativeWindow parent, void* params) { 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, string16(), suggested_path, &file_type_info, 1, FILE_PATH_LITERAL("crt"), tab_contents, parent, params); } void ShowCertExportDialog(TabContents* tab_contents, gfx::NativeWindow parent, net::X509Certificate::OSCertHandle cert) { new Exporter(tab_contents, parent, cert); }