| // Copyright 2017 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/passwords/password_manager_porter.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/path_service.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/password_manager/password_store_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/chrome_select_file_policy.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/password_manager/core/browser/export/password_manager_exporter.h" |
| #include "components/password_manager/core/browser/password_store.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "content/public/browser/web_contents.h" |
| #include "net/base/filename_util.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "url/gurl.h" |
| |
| #if defined(OS_WIN) |
| #include "base/strings/string16.h" |
| #endif |
| |
| namespace { |
| |
| // The following are not used on Android due to the |SelectFileDialog| being |
| // unused. |
| #if !defined(OS_ANDROID) |
| const base::FilePath::CharType kFileExtension[] = FILE_PATH_LITERAL("csv"); |
| |
| // Returns the file extensions corresponding to supported formats. |
| // Inner vector indicates equivalent extensions. For example: |
| // { { "html", "htm" }, { "csv" } } |
| std::vector<std::vector<base::FilePath::StringType>> |
| GetSupportedFileExtensions() { |
| return std::vector<std::vector<base::FilePath::StringType>>( |
| 1, std::vector<base::FilePath::StringType>(1, kFileExtension)); |
| } |
| |
| // The default directory and filename when importing and exporting passwords. |
| base::FilePath GetDefaultFilepathForPasswordFile( |
| const base::FilePath::StringType& default_extension) { |
| base::FilePath default_path; |
| base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_path); |
| #if defined(OS_WIN) |
| base::string16 file_name = |
| l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_DEFAULT_EXPORT_FILENAME); |
| #else |
| std::string file_name = |
| l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_DEFAULT_EXPORT_FILENAME); |
| #endif |
| return default_path.Append(file_name).AddExtension(default_extension); |
| } |
| #endif |
| |
| // A helper class for reading the passwords that have been imported. |
| class PasswordImportConsumer { |
| public: |
| explicit PasswordImportConsumer(Profile* profile); |
| |
| void ConsumePassword(password_manager::PasswordImporter::Result result, |
| const std::vector<autofill::PasswordForm>& forms); |
| |
| private: |
| Profile* profile_; |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| DISALLOW_COPY_AND_ASSIGN(PasswordImportConsumer); |
| }; |
| |
| PasswordImportConsumer::PasswordImportConsumer(Profile* profile) |
| : profile_(profile) {} |
| |
| void PasswordImportConsumer::ConsumePassword( |
| password_manager::PasswordImporter::Result result, |
| const std::vector<autofill::PasswordForm>& forms) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| UMA_HISTOGRAM_ENUMERATION( |
| "PasswordManager.ImportPasswordFromCSVResult", result, |
| password_manager::PasswordImporter::NUM_IMPORT_RESULTS); |
| |
| if (result != password_manager::PasswordImporter::SUCCESS) |
| return; |
| |
| UMA_HISTOGRAM_COUNTS("PasswordManager.ImportedPasswordsPerUserInCSV", |
| forms.size()); |
| scoped_refptr<password_manager::PasswordStore> store( |
| PasswordStoreFactory::GetForProfile(profile_, |
| ServiceAccessType::EXPLICIT_ACCESS)); |
| if (store) { |
| for (const autofill::PasswordForm& form : forms) |
| store->AddLogin(form); |
| } |
| } |
| } // namespace |
| |
| PasswordManagerPorter::PasswordManagerPorter( |
| password_manager::CredentialProviderInterface* |
| credential_provider_interface, |
| ProgressCallback on_export_progress_callback) |
| : credential_provider_interface_(credential_provider_interface), |
| on_export_progress_callback_(on_export_progress_callback) {} |
| |
| PasswordManagerPorter::~PasswordManagerPorter() {} |
| |
| bool PasswordManagerPorter::Store() { |
| // In unittests a null WebContents means: "Abort creating the file Selector." |
| if (!web_contents_) |
| return true; |
| |
| if (exporter_ && exporter_->GetProgressStatus() == |
| password_manager::ExportProgressStatus::IN_PROGRESS) { |
| return false; |
| } |
| |
| // Set a new exporter for this request. |
| exporter_ = |
| exporter_for_testing_ |
| ? std::move(exporter_for_testing_) |
| : std::make_unique<password_manager::PasswordManagerExporter>( |
| credential_provider_interface_, on_export_progress_callback_); |
| |
| // Start serialising while the user selects a file. |
| exporter_->PreparePasswordsForExport(); |
| PresentFileSelector(web_contents_, |
| PasswordManagerPorter::Type::PASSWORD_EXPORT); |
| |
| return true; |
| } |
| |
| void PasswordManagerPorter::CancelStore() { |
| if (exporter_) |
| exporter_->Cancel(); |
| } |
| |
| password_manager::ExportProgressStatus |
| PasswordManagerPorter::GetExportProgressStatus() { |
| return exporter_ ? exporter_->GetProgressStatus() |
| : password_manager::ExportProgressStatus::NOT_STARTED; |
| } |
| |
| void PasswordManagerPorter::SetExporterForTesting( |
| std::unique_ptr<password_manager::PasswordManagerExporter> exporter) { |
| exporter_for_testing_ = std::move(exporter); |
| } |
| |
| void PasswordManagerPorter::Load() { |
| DCHECK(web_contents_); |
| PresentFileSelector(web_contents_, |
| PasswordManagerPorter::Type::PASSWORD_IMPORT); |
| } |
| |
| void PasswordManagerPorter::PresentFileSelector( |
| content::WebContents* web_contents, |
| Type type) { |
| // This method should never be called on Android (as there is no file selector), |
| // and the relevant IDS constants are not present for Android. |
| #if !defined(OS_ANDROID) |
| DCHECK(web_contents); |
| profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| |
| // Get the default file extension for password files. |
| ui::SelectFileDialog::FileTypeInfo file_type_info; |
| file_type_info.extensions = GetSupportedFileExtensions(); |
| DCHECK(!file_type_info.extensions.empty()); |
| DCHECK(!file_type_info.extensions[0].empty()); |
| file_type_info.include_all_files = true; |
| |
| // Present the file selector dialogue. |
| select_file_dialog_ = ui::SelectFileDialog::Create( |
| this, std::make_unique<ChromeSelectFilePolicy>(web_contents)); |
| |
| ui::SelectFileDialog::Type file_selector_mode = |
| ui::SelectFileDialog::SELECT_NONE; |
| unsigned title = 0; |
| switch (type) { |
| case PASSWORD_IMPORT: |
| file_selector_mode = ui::SelectFileDialog::SELECT_OPEN_FILE; |
| title = IDS_PASSWORD_MANAGER_IMPORT_DIALOG_TITLE; |
| break; |
| case PASSWORD_EXPORT: |
| file_selector_mode = ui::SelectFileDialog::SELECT_SAVEAS_FILE; |
| title = IDS_PASSWORD_MANAGER_EXPORT_DIALOG_TITLE; |
| break; |
| } |
| // Check that a valid action has been chosen. |
| DCHECK(file_selector_mode); |
| DCHECK(title); |
| |
| select_file_dialog_->SelectFile( |
| file_selector_mode, l10n_util::GetStringUTF16(title), |
| GetDefaultFilepathForPasswordFile(file_type_info.extensions[0][0]), |
| &file_type_info, 1, file_type_info.extensions[0][0], |
| web_contents->GetTopLevelNativeWindow(), reinterpret_cast<void*>(type)); |
| #endif |
| } |
| |
| void PasswordManagerPorter::FileSelected(const base::FilePath& path, |
| int index, |
| void* params) { |
| switch (reinterpret_cast<uintptr_t>(params)) { |
| case PASSWORD_IMPORT: |
| ImportPasswordsFromPath(path); |
| break; |
| case PASSWORD_EXPORT: |
| ExportPasswordsToPath(path); |
| break; |
| } |
| } |
| |
| void PasswordManagerPorter::FileSelectionCanceled(void* params) { |
| if (reinterpret_cast<uintptr_t>(params) == PASSWORD_EXPORT) { |
| exporter_->Cancel(); |
| } |
| } |
| |
| void PasswordManagerPorter::ImportPasswordsFromPath( |
| const base::FilePath& path) { |
| // Set up a |PasswordImportConsumer| to process each password entry. |
| std::unique_ptr<PasswordImportConsumer> form_consumer( |
| new PasswordImportConsumer(profile_)); |
| password_manager::PasswordImporter::Import( |
| path, base::Bind(&PasswordImportConsumer::ConsumePassword, |
| std::move(form_consumer))); |
| } |
| |
| void PasswordManagerPorter::ExportPasswordsToPath(const base::FilePath& path) { |
| exporter_->SetDestination(path); |
| } |