diff --git a/atom/browser/api/atom_api_importer.cc b/atom/browser/api/atom_api_importer.cc new file mode 100644 index 0000000000..4b77d73110 --- /dev/null +++ b/atom/browser/api/atom_api_importer.cc @@ -0,0 +1,241 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_importer.h" + +#include +#include + +#include "atom/common/native_mate_converters/callback.h" +#include "atom/common/native_mate_converters/file_path_converter.h" +#include "atom/common/native_mate_converters/string16_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" +#include "atom/common/node_includes.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" +#include "brave/browser/brave_content_browser_client.h" +#include "chrome/browser/importer/external_process_importer_host.h" +#include "chrome/browser/importer/importer_list.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/browser_thread.h" +#include "native_mate/dictionary.h" +#include "native_mate/object_template_builder.h" + +using content::BrowserThread; + +namespace importer { +void ShowImportLockDialog(gfx::NativeWindow parent, + const base::Callback& callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, false)); +} +} + +namespace atom { + +namespace api { + +Importer::Importer(v8::Isolate* isolate) + : importer_host_(NULL), + import_did_succeed_(false) { + Init(isolate); + profile_writer_ = new ProfileWriter(NULL); +} + +Importer::~Importer() { + if (importer_host_) + importer_host_->set_observer(NULL); +} + +void Importer::InitializeImporter() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + importer_list_.reset(new ImporterList()); + importer_list_->DetectSourceProfiles( + brave::BraveContentBrowserClient::Get()->GetApplicationLocale(), + true, // include_interactive_profiles? + base::Bind(&Importer::InitializePage, base::Unretained(this))); + profile_writer_->Initialize(this); +} + +void Importer::ImportData(const base::DictionaryValue& data) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + std::string index; + bool history, favorites, passwords, search, homepage, autofill_form_data, + cookies; + + int browser_index = -1; + if (!data.GetString("index", &index) || + !base::StringToInt(index, &browser_index)) { + NOTREACHED(); + return; + } + + uint16_t selected_items = importer::NONE; + if (data.GetBoolean("history", &history) && history == true) { + selected_items |= importer::HISTORY; + } + if (data.GetBoolean("favorites", &favorites) && favorites == true) { + selected_items |= importer::FAVORITES; + } + if (data.GetBoolean("passwords", &passwords) && passwords == true) { + selected_items |= importer::PASSWORDS; + } + if (data.GetBoolean("search", &search) && search == true) { + selected_items |= importer::SEARCH_ENGINES; + } + if (data.GetBoolean("homepage", &homepage) && homepage == true) { + selected_items |= importer::HOME_PAGE; + } + if (data.GetBoolean("autofill-autofill_form_data", &autofill_form_data) && + autofill_form_data == true) { + selected_items |= importer::AUTOFILL_FORM_DATA; + } + if (data.GetBoolean("cookies", &cookies) && cookies == true) { + selected_items |= importer::COOKIES; + } + + const importer::SourceProfile& source_profile = + importer_list_->GetSourceProfileAt(browser_index); + uint16_t supported_items = source_profile.services_supported; + + uint16_t imported_items = (selected_items & supported_items); + if (imported_items) { + StartImport(source_profile, imported_items); + } else { + LOG(WARNING) << "There were no settings to import from '" + << source_profile.importer_name << "'."; + } +} + +void Importer::ImportHTML(const base::FilePath& path) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + importer::SourceProfile source_profile; + source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE; + source_profile.source_path = path; + + StartImport(source_profile, importer::FAVORITES); +} + +void Importer::StartImport( + const importer::SourceProfile& source_profile, + uint16_t imported_items) { + // If another import is already ongoing, let it finish silently. + if (importer_host_) + importer_host_->set_observer(NULL); + + import_did_succeed_ = false; + + importer_host_ = new ExternalProcessImporterHost(); + importer_host_->set_observer(this); + importer_host_->StartImportSettings(source_profile, NULL, + imported_items, + profile_writer_.get()); +} + +void Importer::InitializePage() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + base::ListValue browser_profiles; + for (size_t i = 0; i < importer_list_->count(); ++i) { + const importer::SourceProfile& source_profile = + importer_list_->GetSourceProfileAt(i); + uint16_t browser_services = source_profile.services_supported; + + base::DictionaryValue* browser_profile = new base::DictionaryValue(); + + browser_profile->SetString("name", source_profile.importer_name); + browser_profile->SetInteger("type", source_profile.importer_type); + browser_profile->SetInteger("index", i); + browser_profile->SetBoolean("history", + (browser_services & importer::HISTORY) != 0); + browser_profile->SetBoolean("favorites", + (browser_services & importer::FAVORITES) != 0); + browser_profile->SetBoolean("passwords", + (browser_services & importer::PASSWORDS) != 0); + browser_profile->SetBoolean("search", + (browser_services & importer::SEARCH_ENGINES) != 0); + browser_profile->SetBoolean("homepage", + (browser_services & importer::HOME_PAGE) != 0); + browser_profile->SetBoolean("autofill-form-data", + (browser_services & importer::AUTOFILL_FORM_DATA) != 0); + browser_profile->SetBoolean("cookies", + (browser_services & importer::COOKIES) != 0); + + browser_profiles.Append(browser_profile); + } + Emit("update-supported-browsers", browser_profiles); +} + +// static +mate::Handle Importer::Create(v8::Isolate* isolate) { + return mate::CreateHandle(isolate, new Importer(isolate)); +} + +void Importer::ImportStarted() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +void Importer::ImportItemStarted(importer::ImportItem item) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // TODO(csilv): show progress detail in the web view. +} + +void Importer::ImportItemEnded(importer::ImportItem item) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // TODO(csilv): show progress detail in the web view. + import_did_succeed_ = true; +} + +void Importer::ImportEnded() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + importer_host_->set_observer(NULL); + importer_host_ = NULL; + + if (import_did_succeed_) { + // web_ui()->CallJavascriptFunction("ImportDataOverlay.confirmSuccess"); + } else { + /* + base::FundamentalValue state(false); + web_ui()->CallJavascriptFunction("ImportDataOverlay.setImportingState", + state); + web_ui()->CallJavascriptFunction("ImportDataOverlay.dismiss"); + */ + } +} + +// static +void Importer::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + prototype->SetClassName(mate::StringToV8(isolate, "Importer")); + mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) + .SetMethod("initialize", &Importer::InitializeImporter) + .SetMethod("importData", &Importer::ImportData) + .SetMethod("importHTML", &Importer::ImportHTML); +} + +} // namespace api + +} // namespace atom + +namespace { + +using atom::api::Importer; + +void Initialize(v8::Local exports, v8::Local unused, + v8::Local context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.Set("importer", Importer::Create(isolate)); + dict.Set("Importer", Importer::GetConstructor(isolate)->GetFunction()); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_importer, Initialize); diff --git a/atom/browser/api/atom_api_importer.h b/atom/browser/api/atom_api_importer.h new file mode 100644 index 0000000000..090a8085db --- /dev/null +++ b/atom/browser/api/atom_api_importer.h @@ -0,0 +1,72 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_IMPORTER_H_ +#define ATOM_BROWSER_API_ATOM_API_IMPORTER_H_ + +#include +#include + +#include "atom/browser/api/event_emitter.h" +#include "atom/browser/api/trackable_object.h" +#include "base/callback.h" +#include "base/values.h" +#include "chrome/browser/importer/importer_progress_observer.h" +#include "chrome/common/importer/importer_data_types.h" +#include "native_mate/handle.h" + +class ExternalProcessImporterHost; +class ImporterList; +class ProfileWriter; + +namespace atom { + +namespace api { + +class Importer: public mate::EventEmitter, + public importer::ImporterProgressObserver { + public: + static mate::Handle Create(v8::Isolate* isolate); + + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + protected: + explicit Importer(v8::Isolate* isolate); + ~Importer() override; + + private: + void InitializeImporter(); + void InitializePage(); + + void ImportData(const base::DictionaryValue& data); + void ImportHTML(const base::FilePath& path); + void StartImport(const importer::SourceProfile& source_profile, + uint16_t imported_items); + + // importer::ImporterProgressObserver: + void ImportStarted() override; + void ImportItemStarted(importer::ImportItem item) override; + void ImportItemEnded(importer::ImportItem item) override; + void ImportEnded() override; + + // If non-null it means importing is in progress. ImporterHost takes care + // of deleting itself when import is complete. + ExternalProcessImporterHost* importer_host_; // weak + + std::unique_ptr importer_list_; + + scoped_refptr profile_writer_; + + bool import_did_succeed_; + + DISALLOW_COPY_AND_ASSIGN(Importer); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_IMPORTER_H_ diff --git a/atom/browser/importer/profile_writer.cc b/atom/browser/importer/profile_writer.cc new file mode 100644 index 0000000000..952c2ba1e8 --- /dev/null +++ b/atom/browser/importer/profile_writer.cc @@ -0,0 +1,511 @@ +// 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/importer/profile_writer.h" + +#include + +#include +#include +#include + +#include "atom/browser/api/atom_api_importer.h" +#include "atom/common/importer/imported_cookie_entry.h" +#include "atom/common/native_mate_converters/string16_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread.h" +#include "build/build_config.h" +// #include "chrome/browser/bookmarks/bookmark_model_factory.h" +#include "chrome/browser/chrome_notification_types.h" +// #include "chrome/browser/favicon/favicon_service_factory.h" +// #include "chrome/browser/history/history_service_factory.h" +// #include "chrome/browser/password_manager/password_store_factory.h" +#include "chrome/browser/profiles/profile.h" +// #include "chrome/browser/search_engines/template_url_service_factory.h" +// #include "chrome/browser/web_data_service_factory.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/pref_names.h" +#include "components/autofill/core/browser/webdata/autofill_entry.h" +#include "components/autofill/core/common/password_form.h" +// #include +// "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/bookmarks/browser/bookmark_model.h" +// #include "components/bookmarks/common/bookmark_pref_names.h" +// #include "components/favicon/core/favicon_service.h" +#include "components/history/core/browser/history_service.h" +// #include "components/password_manager/core/browser/password_store.h" +#include "components/prefs/pref_service.h" +#include "components/search_engines/template_url.h" +#include "components/search_engines/template_url_service.h" + +#if defined(OS_WIN) +// #include "chrome/browser/web_data_service_factory.h" +// #include "components/password_manager/core/browser/webdata/\ +// password_web_data_service_win.h" +#endif +/* +using bookmarks::BookmarkModel; +using bookmarks::BookmarkNode; + +namespace { + +// Generates a unique folder name. If |folder_name| is not unique, then this +// repeatedly tests for '|folder_name| + (i)' until a unique name is found. +base::string16 GenerateUniqueFolderName(BookmarkModel* model, + const base::string16& folder_name) { + // Build a set containing the bookmark bar folder names. + std::set existing_folder_names; + const BookmarkNode* bookmark_bar = model->bookmark_bar_node(); + for (int i = 0; i < bookmark_bar->child_count(); ++i) { + const BookmarkNode* node = bookmark_bar->GetChild(i); + if (node->is_folder()) + existing_folder_names.insert(node->GetTitle()); + } + + // If the given name is unique, use it. + if (existing_folder_names.find(folder_name) == existing_folder_names.end()) + return folder_name; + + // Otherwise iterate until we find a unique name. + for (size_t i = 1; i <= existing_folder_names.size(); ++i) { + base::string16 name = folder_name + base::ASCIIToUTF16(" (") + + base::SizeTToString16(i) + base::ASCIIToUTF16(")"); + if (existing_folder_names.find(name) == existing_folder_names.end()) + return name; + } + + NOTREACHED(); + return folder_name; +} + +// Shows the bookmarks toolbar. +void ShowBookmarkBar(Profile* profile) { + //profile->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true); +} + +} // namespace +*/ + +ProfileWriter::ProfileWriter(Profile* profile) : profile_(profile), + importer_(nullptr) {} + +bool ProfileWriter::BookmarkModelIsLoaded() const { + // return BookmarkModelFactory::GetForProfile(profile_)->loaded(); + return true; +} + +bool ProfileWriter::TemplateURLServiceIsLoaded() const { + // return TemplateURLServiceFactory::GetForProfile(profile_)->loaded(); + return true; +} + +void ProfileWriter::AddPasswordForm(const autofill::PasswordForm& form) { + /* + PasswordStoreFactory::GetForProfile( + profile_, ServiceAccessType::EXPLICIT_ACCESS)->AddLogin(form); + */ + if (importer_) { + base::DictionaryValue imported_form; + imported_form.SetString("signon_realm", form.signon_realm); + imported_form.SetString("username_value", form.username_value); + imported_form.SetString("password_value", form.password_value); + + importer_->Emit("add-password-form", imported_form); + } +} + +#if defined(OS_WIN) +void ProfileWriter::AddIE7PasswordInfo(const IE7PasswordInfo& info) { + /* + WebDataServiceFactory::GetPasswordWebDataForProfile( + profile_, ServiceAccessType::EXPLICIT_ACCESS)->AddIE7Login(info); + */ +} +#endif + +void ProfileWriter::AddHistoryPage(const history::URLRows& page, + history::VisitSource visit_source) { + /* + HistoryServiceFactory::GetForProfile(profile_, + ServiceAccessType::EXPLICIT_ACCESS) + ->AddPagesWithDetails(page, visit_source); + */ + if (importer_) { + base::ListValue history_list; + for (const history::URLRow& row : page) { + base::DictionaryValue* history = new base::DictionaryValue(); + history->SetString("title", row.title()); + history->SetString("url", row.url().possibly_invalid_spec()); + history->SetInteger("visit_count", row.visit_count()); + history->SetInteger("last_visit", row.last_visit().ToDoubleT()); + history_list.Append(history); + } + importer_->Emit("add-history-page", history_list, + (unsigned int) visit_source); + } +} + +void ProfileWriter::AddHomepage(const GURL& home_page) { + /* + DCHECK(profile_); + PrefService* prefs = profile_->GetPrefs(); + // NOTE: We set the kHomePage value, but keep the NewTab page as the homepage. + const PrefService::Preference* pref = prefs->FindPreference(prefs::kHomePage); + if (pref && !pref->IsManaged()) { + prefs->SetString(prefs::kHomePage, home_page.spec()); + } + */ + if (importer_) { + importer_->Emit("add-homepage", home_page.possibly_invalid_spec()); + } +} + +void ProfileWriter::AddBookmarks( + const std::vector& bookmarks, + const base::string16& top_level_folder_name) { + /* + if (bookmarks.empty()) + return; + + BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); + DCHECK(model->loaded()); + + // If the bookmark bar is currently empty, we should import directly to it. + // Otherwise, we should import everything to a subfolder. + const BookmarkNode* bookmark_bar = model->bookmark_bar_node(); + bool import_to_top_level = bookmark_bar->empty(); + + // Reorder bookmarks so that the toolbar entries come first. + std::vector toolbar_bookmarks; + std::vector reordered_bookmarks; + for (std::vector::const_iterator it = + bookmarks.begin(); + it != bookmarks.end(); ++it) { + if (it->in_toolbar) + toolbar_bookmarks.push_back(*it); + else + reordered_bookmarks.push_back(*it); + } + reordered_bookmarks.insert(reordered_bookmarks.begin(), + toolbar_bookmarks.begin(), + toolbar_bookmarks.end()); + + // If the user currently has no bookmarks in the bookmark bar, make sure that + // at least some of the imported bookmarks end up there. Otherwise, we'll end + // up with just a single folder containing the imported bookmarks, which makes + // for unnecessary nesting. + bool add_all_to_top_level = import_to_top_level && toolbar_bookmarks.empty(); + + model->BeginExtensiveChanges(); + + std::set folders_added_to; + const BookmarkNode* top_level_folder = NULL; + for (std::vector::const_iterator bookmark = + reordered_bookmarks.begin(); + bookmark != reordered_bookmarks.end(); ++bookmark) { + // Disregard any bookmarks with invalid urls. + if (!bookmark->is_folder && !bookmark->url.is_valid()) + continue; + + const BookmarkNode* parent = NULL; + if (import_to_top_level && (add_all_to_top_level || bookmark->in_toolbar)) { + // Add directly to the bookmarks bar. + parent = bookmark_bar; + } else { + // Add to a folder that will contain all the imported bookmarks not added + // to the bar. The first time we do so, create the folder. + if (!top_level_folder) { + base::string16 name = + GenerateUniqueFolderName(model,top_level_folder_name); + top_level_folder = model->AddFolder(bookmark_bar, + bookmark_bar->child_count(), + name); + } + parent = top_level_folder; + } + + // Ensure any enclosing folders are present in the model. The bookmark's + // enclosing folder structure should be + // path[0] > path[1] > ... > path[size() - 1] + for (std::vector::const_iterator folder_name = + bookmark->path.begin(); + folder_name != bookmark->path.end(); ++folder_name) { + if (bookmark->in_toolbar && parent == bookmark_bar && + folder_name == bookmark->path.begin()) { + // If we're importing directly to the bookmarks bar, skip over the + // folder named "Bookmarks Toolbar" (or any non-Firefox equivalent). + continue; + } + + const BookmarkNode* child = NULL; + for (int index = 0; index < parent->child_count(); ++index) { + const BookmarkNode* node = parent->GetChild(index); + if (node->is_folder() && node->GetTitle() == *folder_name) { + child = node; + break; + } + } + if (!child) + child = model->AddFolder(parent, parent->child_count(), *folder_name); + parent = child; + } + + folders_added_to.insert(parent); + if (bookmark->is_folder) { + model->AddFolder(parent, parent->child_count(), bookmark->title); + } else { + model->AddURLWithCreationTimeAndMetaInfo(parent, + parent->child_count(), + bookmark->title, + bookmark->url, + bookmark->creation_time, + NULL); + } + } + + // In order to keep the imported-to folders from appearing in the 'recently + // added to' combobox, reset their modified times. + for (std::set::const_iterator i = + folders_added_to.begin(); + i != folders_added_to.end(); ++i) { + model->ResetDateFolderModified(*i); + } + + model->EndExtensiveChanges(); + + // If the user was previously using a toolbar, we should show the bar. + if (import_to_top_level && !add_all_to_top_level) + ShowBookmarkBar(profile_); + */ + if (importer_) { + base::ListValue imported_bookmarks; + for (const ImportedBookmarkEntry& bookmark : bookmarks) { + base::DictionaryValue* imported_bookmark = new base::DictionaryValue(); + imported_bookmark->SetBoolean("in_toolbar", bookmark.in_toolbar); + imported_bookmark->SetBoolean("is_folder", bookmark.is_folder); + imported_bookmark->SetString("url", bookmark.url.possibly_invalid_spec()); + imported_bookmark->SetString("title", bookmark.title); + imported_bookmark->SetInteger("creation_time", + bookmark.creation_time.ToDoubleT()); + base::ListValue* paths = new base::ListValue(); + for (const base::string16& path : bookmark.path) { + paths->AppendString(path); + } + imported_bookmark->Set("path", paths); + imported_bookmarks.Append(imported_bookmark); + } + importer_->Emit("add-bookmarks", imported_bookmarks, top_level_folder_name); + } +} + +void ProfileWriter::AddFavicons( + const favicon_base::FaviconUsageDataList& favicons) { + /* + FaviconServiceFactory::GetForProfile(profile_, + ServiceAccessType::EXPLICIT_ACCESS) + ->SetImportedFavicons(favicons); + */ + if (importer_) { + base::ListValue imported_favicons; + for (const favicon_base::FaviconUsageData& favicon : favicons) { + base::DictionaryValue* imported_favicon = new base::DictionaryValue(); + imported_favicon->SetString("favicon_url", + favicon.favicon_url.possibly_invalid_spec()); + /* + imported_favicon->SetString("png_data", + std::string(favicon.png_data.begin(), + favicon.png_data.end())); + */ + std::set::iterator it; + base::ListValue* urls = new base::ListValue(); + for (it = favicon.urls.begin(); it != favicon.urls.end(); ++it) { + urls->AppendString(it->possibly_invalid_spec()); + } + imported_favicon->Set("urls", urls); + imported_favicons.Append(imported_favicon); + } + importer_->Emit("add-favicons", imported_favicons); + } +} + +/* +typedef std::map HostPathMap; + +// Returns the key for the map built by BuildHostPathMap. If url_string is not +// a valid URL, an empty string is returned, otherwise host+path is returned. +static std::string HostPathKeyForURL(const GURL& url) { + return url.is_valid() ? url.host() + url.path() : std::string(); +} + +// Builds the key to use in HostPathMap for the specified TemplateURL. Returns +// an empty string if a host+path can't be generated for the TemplateURL. +// If an empty string is returned, the TemplateURL should not be added to +// HostPathMap. +// +// If |try_url_if_invalid| is true, and |t_url| isn't valid, a string is built +// from the raw TemplateURL string. Use a value of true for |try_url_if_invalid| +// when checking imported URLs as the imported URL may not be valid yet may +// match the host+path of one of the default URLs. This is used to catch the +// case of IE using an invalid OSDD URL for Live Search, yet the host+path +// matches our prepopulate data. IE's URL for Live Search is something like +// 'http://...{Language}...'. As {Language} is not a valid OSDD parameter value +// the TemplateURL is invalid. +static std::string BuildHostPathKey(const TemplateURL* t_url, + const SearchTermsData& search_terms_data, + bool try_url_if_invalid) { + if (try_url_if_invalid && !t_url->url_ref().IsValid(search_terms_data)) + return HostPathKeyForURL(GURL(t_url->url())); + + if (t_url->url_ref().SupportsReplacement(search_terms_data)) { + return HostPathKeyForURL(GURL( + t_url->url_ref().ReplaceSearchTerms( + TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("x")), + search_terms_data))); + } + return std::string(); +} + +// Builds a set that contains an entry of the host+path for each TemplateURL in +// the TemplateURLService that has a valid search url. +static void BuildHostPathMap(TemplateURLService* model, + HostPathMap* host_path_map) { + TemplateURLService::TemplateURLVector template_urls = + model->GetTemplateURLs(); + for (size_t i = 0; i < template_urls.size(); ++i) { + const std::string host_path = BuildHostPathKey( + template_urls[i], model->search_terms_data(), false); + if (!host_path.empty()) { + const TemplateURL* existing_turl = (*host_path_map)[host_path]; + if (!existing_turl || + (template_urls[i]->show_in_default_list() && + !existing_turl->show_in_default_list())) { + // If there are multiple TemplateURLs with the same host+path, favor + // those shown in the default list. If there are multiple potential + // defaults, favor the first one, which should be the more commonly used + // one. + (*host_path_map)[host_path] = template_urls[i]; + } + } // else case, TemplateURL doesn't have a search url, doesn't support + // replacement, or doesn't have valid GURL. Ignore it. + } +} +*/ + +void ProfileWriter::AddKeywords(ScopedVector template_urls, + bool unique_on_host_and_path) { + /* + TemplateURLService* model = + TemplateURLServiceFactory::GetForProfile(profile_); + HostPathMap host_path_map; + if (unique_on_host_and_path) + BuildHostPathMap(model, &host_path_map); + + for (ScopedVector::iterator i = template_urls.begin(); + i != template_urls.end(); ++i) { + // TemplateURLService requires keywords to be unique. If there is already a + // TemplateURL with this keyword, don't import it again. + if (model->GetTemplateURLForKeyword((*i)->keyword()) != NULL) + continue; + + // For search engines if there is already a keyword with the same + // host+path, we don't import it. This is done to avoid both duplicate + // search providers (such as two Googles, or two Yahoos) as well as making + // sure the search engines we provide aren't replaced by those from the + // imported browser. + if (unique_on_host_and_path && + (host_path_map.find(BuildHostPathKey( + *i, model->search_terms_data(), true)) != host_path_map.end())) + continue; + + // Only add valid TemplateURLs to the model. + if ((*i)->url_ref().IsValid(model->search_terms_data())) { + model->Add(*i); // Takes ownership. + *i = NULL; // Prevent the vector from deleting *i later. + } + } + */ + if (importer_) { + base::ListValue imported_template_urls; + for (ScopedVector::iterator i = template_urls.begin(); + i != template_urls.end(); ++i) { + base::DictionaryValue* imported_template_url = + new base::DictionaryValue(); + imported_template_url->SetString("short_name", (*i)->short_name()); + imported_template_url->SetString("keyword", (*i)->keyword()); + imported_template_url->SetString("url", (*i)->url()); + imported_template_url->SetString("favicon_url", + (*i)->favicon_url().possibly_invalid_spec()); + imported_template_url->SetString("suggestions_url", + (*i)->suggestions_url()); + + imported_template_urls.Append(imported_template_url); + } + importer_->Emit("add-keywords", imported_template_urls, + unique_on_host_and_path); + } +} + +void ProfileWriter::AddAutofillFormDataEntries( + const std::vector& autofill_entries) { + /* + scoped_refptr web_data_service = + WebDataServiceFactory::GetAutofillWebDataForProfile( + profile_, ServiceAccessType::EXPLICIT_ACCESS); + if (web_data_service.get()) + web_data_service->UpdateAutofillEntries(autofill_entries); + */ + if (importer_) { + base::ListValue imported_autofill_entries; + for (const autofill::AutofillEntry& autofill_entry : autofill_entries) { + base::DictionaryValue* imported_autofill_entry = + new base::DictionaryValue(); + imported_autofill_entry->SetString("name", autofill_entry.key().name()); + imported_autofill_entry->SetString("value", autofill_entry.key().value()); + imported_autofill_entries.Append(imported_autofill_entry); + } + importer_->Emit("add-autofill-form-data-entries", + imported_autofill_entries); + } +} + +void ProfileWriter::AddCookies( + const std::vector& cookies) { + if (importer_) { + base::ListValue imported_cookies; + for (const ImportedCookieEntry& cookie_entry : cookies) { + base::DictionaryValue* cookie = new base::DictionaryValue(); + base::string16 url; + if (cookie_entry.httponly) { + url.append(base::UTF8ToUTF16("http://")); + url.append(cookie_entry.host); + } else { + url.append(base::UTF8ToUTF16("https://")); + url.append(cookie_entry.host); + } + cookie->SetString("url", url); + cookie->SetString("domain", cookie_entry.domain); + cookie->SetString("name", cookie_entry.name); + cookie->SetString("value", cookie_entry.value); + cookie->SetString("path", cookie_entry.path); + cookie->SetInteger("expiry_date", cookie_entry.expiry_date.ToDoubleT()); + cookie->SetBoolean("secure", cookie_entry.secure); + cookie->SetBoolean("httponly", cookie_entry.httponly); + imported_cookies.Append(cookie); + } + importer_->Emit("add-cookies", imported_cookies); + } +} + +void ProfileWriter::Initialize(atom::api::Importer* importer) { + importer_ = importer; +} + +void ProfileWriter::ShowWarningDialog() { + importer_->Emit("show-warning-dialog"); +} + +ProfileWriter::~ProfileWriter() {} diff --git a/atom/common/importer/chrome_importer_utils.cc b/atom/common/importer/chrome_importer_utils.cc new file mode 100644 index 0000000000..8edb5c5caa --- /dev/null +++ b/atom/common/importer/chrome_importer_utils.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/importer/chrome_importer_utils.h" + +#include + +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/values.h" +#include "chrome/common/importer/importer_data_types.h" + +base::ListValue* GetChromeSourceProfiles( + const base::FilePath& user_data_folder) { + base::ListValue* profiles = new base::ListValue(); + base::FilePath local_state_path = + user_data_folder.Append( + base::FilePath::StringType(FILE_PATH_LITERAL("Local State"))); + if (!base::PathExists(local_state_path)) { + base::DictionaryValue* entry = new base::DictionaryValue(); + entry->SetString("id", "Default"); + entry->SetString("name", "Default"); + profiles->Append(entry); + } else { + std::string local_state_content; + base::ReadFileToString(local_state_path, &local_state_content); + std::unique_ptr local_state = + base::JSONReader::Read(local_state_content); + const base::DictionaryValue* local_state_dict; + const base::DictionaryValue* profile_dict; + const base::DictionaryValue* info_cache; + if (!local_state || !local_state->GetAsDictionary(&local_state_dict)) + return profiles; + + if (local_state_dict->GetDictionary("profile", &profile_dict)) { + if (profile_dict->GetDictionary("info_cache", &info_cache)) { + for (base::DictionaryValue::Iterator it(*info_cache); + !it.IsAtEnd(); it.Advance()) { + const base::DictionaryValue* profile; + if (!it.value().GetAsDictionary(&profile)) + continue; + std::string name; + profile->GetString("name", &name); + base::DictionaryValue* entry = new base::DictionaryValue(); + entry->SetString("id", it.key()); + entry->SetString("name", name); + profiles->Append(entry); + } + } + } + } + return profiles; +} + +bool ChromeImporterCanImport(const base::FilePath& profile, + uint16_t* services_supported) { + DCHECK(services_supported); + *services_supported = importer::NONE; + + base::FilePath bookmarks = + profile.Append(base::FilePath::StringType(FILE_PATH_LITERAL("Bookmarks"))); + base::FilePath history = + profile.Append(base::FilePath::StringType(FILE_PATH_LITERAL("History"))); + base::FilePath cookies = + profile.Append(base::FilePath::StringType(FILE_PATH_LITERAL("Cookies"))); + + if (base::PathExists(bookmarks)) + *services_supported |= importer::FAVORITES; + if (base::PathExists(history)) + *services_supported |= importer::HISTORY; + if (base::PathExists(cookies)) + *services_supported |= importer::COOKIES; + + return *services_supported != importer::NONE; +} + +base::DictionaryValue* GetChromeResources( + const base::FilePath& profile) { + base::DictionaryValue* resource = new base::DictionaryValue(); + return resource; +} diff --git a/atom/common/importer/chrome_importer_utils.h b/atom/common/importer/chrome_importer_utils.h new file mode 100644 index 0000000000..7859c5ce20 --- /dev/null +++ b/atom/common/importer/chrome_importer_utils.h @@ -0,0 +1,29 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_IMPORTER_CHROME_IMPORTER_UTILS_H_ +#define ATOM_COMMON_IMPORTER_CHROME_IMPORTER_UTILS_H_ + +#include + +#include + +namespace base { +class DictionaryValue; +class FilePath; +class ListValue; +} + +base::FilePath GetChromeUserDataFolder(); + +base::ListValue* GetChromeSourceProfiles( + const base::FilePath& user_data_folder); + +bool ChromeImporterCanImport(const base::FilePath& profile, + uint16_t* services_supported); + +base::DictionaryValue* GetChromeResources( + const base::FilePath& profile); + +#endif // ATOM_COMMON_IMPORTER_CHROME_IMPORTER_UTILS_H_ diff --git a/atom/common/importer/chrome_importer_utils_linux.cc b/atom/common/importer/chrome_importer_utils_linux.cc new file mode 100644 index 0000000000..7d50f523c2 --- /dev/null +++ b/atom/common/importer/chrome_importer_utils_linux.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/importer/chrome_importer_utils.h" + +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" + +base::FilePath GetChromeUserDataFolder() { + base::FilePath result; + if (!PathService::Get(base::DIR_HOME, &result)) + return base::FilePath(); + + result = result.Append(".config"); + result = result.Append("google-chrome"); + + return result; +} diff --git a/atom/common/importer/chrome_importer_utils_mac.mm b/atom/common/importer/chrome_importer_utils_mac.mm new file mode 100644 index 0000000000..07cad02300 --- /dev/null +++ b/atom/common/importer/chrome_importer_utils_mac.mm @@ -0,0 +1,16 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include +#include + +#include "atom/common/importer/chrome_importer_utils.h" + +#include "base/files/file_util.h" +#include "base/mac/foundation_util.h" + +base::FilePath GetChromeUserDataFolder() { + base::FilePath result = base::mac::GetUserLibraryPath(); + return result.Append("Application Support/Google/Chrome"); +} diff --git a/atom/common/importer/chrome_importer_utils_win.cc b/atom/common/importer/chrome_importer_utils_win.cc new file mode 100644 index 0000000000..af4fbb0d92 --- /dev/null +++ b/atom/common/importer/chrome_importer_utils_win.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/importer/chrome_importer_utils.h" + +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/strings/string16.h" + +base::FilePath GetChromeUserDataFolder() { + base::FilePath result; + if (!PathService::Get(base::DIR_LOCAL_APP_DATA, &result)) + return base::FilePath(); + + result = result.AppendASCII("Google"); + result = result.AppendASCII("Chrome"); + result = result.AppendASCII("User Data"); + + return result; +} diff --git a/atom/common/importer/imported_cookie_entry.cc b/atom/common/importer/imported_cookie_entry.cc new file mode 100644 index 0000000000..d0b5148d0f --- /dev/null +++ b/atom/common/importer/imported_cookie_entry.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/importer/imported_cookie_entry.h" + +ImportedCookieEntry::ImportedCookieEntry() { +} + +ImportedCookieEntry::~ImportedCookieEntry() { +} diff --git a/atom/common/importer/imported_cookie_entry.h b/atom/common/importer/imported_cookie_entry.h new file mode 100644 index 0000000000..bbf2f23f95 --- /dev/null +++ b/atom/common/importer/imported_cookie_entry.h @@ -0,0 +1,32 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_IMPORTER_IMPORTED_COOKIE_ENTRY_H_ +#define ATOM_COMMON_IMPORTER_IMPORTED_COOKIE_ENTRY_H_ + +#include "base/strings/string16.h" +#include "base/time/time.h" + +struct ImportedCookieEntry { + ImportedCookieEntry(); + ~ImportedCookieEntry(); + + base::string16 domain; + + base::string16 name; + + base::string16 value; + + base::string16 host; + + base::string16 path; + + base::Time expiry_date; + + bool secure; + + bool httponly; +}; + +#endif // ATOM_COMMON_IMPORTER_IMPORTED_COOKIE_ENTRY_H_ diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index d37a57a328..90780c5a79 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -38,6 +38,7 @@ REFERENCE_MODULE(atom_browser_dialog); REFERENCE_MODULE(atom_browser_debugger); REFERENCE_MODULE(atom_browser_desktop_capturer); REFERENCE_MODULE(atom_browser_download_item); +REFERENCE_MODULE(atom_browser_importer); REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_power_save_blocker); diff --git a/atom/utility/atom_content_utility_client.cc b/atom/utility/atom_content_utility_client.cc index 12f2f7adf8..bf8c91c9b4 100644 --- a/atom/utility/atom_content_utility_client.cc +++ b/atom/utility/atom_content_utility_client.cc @@ -9,6 +9,7 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "chrome/common/chrome_utility_messages.h" +#include "chrome/utility/profile_import_handler.h" #include "chrome/utility/utility_message_handler.h" #include "content/public/common/content_switches.h" #include "content/public/utility/utility_thread.h" @@ -39,6 +40,7 @@ int64_t AtomContentUtilityClient::max_ipc_message_size_ = AtomContentUtilityClient::AtomContentUtilityClient() : filter_messages_(false) { + handlers_.push_back(new ProfileImportHandler()); #if defined(OS_WIN) handlers_.push_back(new printing::PrintingHandlerWin()); #endif diff --git a/atom/utility/importer/chrome_importer.cc b/atom/utility/importer/chrome_importer.cc new file mode 100644 index 0000000000..b2a586cf76 --- /dev/null +++ b/atom/utility/importer/chrome_importer.cc @@ -0,0 +1,223 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/utility/importer/chrome_importer.h" + +#include + +#include "atom/common/importer/imported_cookie_entry.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_bridge.h" +#include "chrome/common/importer/importer_url_row.h" +#include "sql/connection.h" +#include "sql/statement.h" +#include "url/gurl.h" + +ChromeImporter::ChromeImporter() { +} + +ChromeImporter::~ChromeImporter() { +} + +void ChromeImporter::StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) { + bridge_ = bridge; + source_path_ = source_profile.source_path; + + // The order here is important! + bridge_->NotifyStarted(); + + if ((items & importer::HISTORY) && !cancelled()) { + bridge_->NotifyItemStarted(importer::HISTORY); + ImportHistory(); + bridge_->NotifyItemEnded(importer::HISTORY); + } + + if ((items & importer::FAVORITES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::FAVORITES); + ImportBookmarks(); + bridge_->NotifyItemEnded(importer::FAVORITES); + } + + if ((items & importer::COOKIES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::COOKIES); + ImportCookies(); + bridge_->NotifyItemEnded(importer::COOKIES); + } + bridge_->NotifyEnded(); +} + +void ChromeImporter::ImportHistory() { + base::FilePath history_path = + source_path_.Append( + base::FilePath::StringType(FILE_PATH_LITERAL("History"))); + if (!base::PathExists(history_path)) + return; + + sql::Connection db; + if (!db.Open(history_path)) + return; + + const char query[] = + "SELECT url, title, last_visit_time, typed_count, visit_count " + "FROM urls WHERE hidden = 0"; + + sql::Statement s(db.GetUniqueStatement(query)); + + std::vector rows; + while (s.Step() && !cancelled()) { + GURL url(s.ColumnString(0)); + + ImporterURLRow row(url); + row.title = s.ColumnString16(1); + row.last_visit = + base::Time::FromDoubleT(chromeTimeToDouble((s.ColumnInt64(2)))); + row.hidden = false; + row.typed_count = s.ColumnInt(3); + row.visit_count = s.ColumnInt(4); + + rows.push_back(row); + } + + if (!rows.empty() && !cancelled()) + bridge_->SetHistoryItems(rows, importer::VISIT_SOURCE_CHROME_IMPORTED); +} + +void ChromeImporter::ImportBookmarks() { + std::string bookmarks_content; + base::FilePath bookmarks_path = + source_path_.Append( + base::FilePath::StringType(FILE_PATH_LITERAL("Bookmarks"))); + base::ReadFileToString(bookmarks_path, &bookmarks_content); + std::unique_ptr bookmarks_json = + base::JSONReader::Read(bookmarks_content); + const base::DictionaryValue* bookmark_dict; + if (!bookmarks_json || !bookmarks_json->GetAsDictionary(&bookmark_dict)) + return; + std::vector bookmarks; + const base::DictionaryValue* roots; + const base::DictionaryValue* bookmark_bar; + const base::DictionaryValue* other; + if (bookmark_dict->GetDictionary("roots", &roots)) { + // Importing bookmark bar items + if (roots->GetDictionary("bookmark_bar", &bookmark_bar)) { + std::vector path; + base::string16 name; + bookmark_bar->GetString("name", &name); + + path.push_back(name); + RecursiveReadBookmarksFolder(bookmark_bar, path, true, &bookmarks); + } + // Importing other items + if (roots->GetDictionary("other", &other)) { + std::vector path; + base::string16 name; + other->GetString("name", &name); + + path.push_back(name); + RecursiveReadBookmarksFolder(other, path, false, &bookmarks); + } + } + // Write into profile. + if (!bookmarks.empty() && !cancelled()) { + const base::string16& first_folder_name = + base::UTF8ToUTF16("Imported from Chrome"); + bridge_->AddBookmarks(bookmarks, first_folder_name); + } +} + +void ChromeImporter::ImportCookies() { + base::FilePath cookies_path = + source_path_.Append( + base::FilePath::StringType(FILE_PATH_LITERAL("Cookies"))); + if (!base::PathExists(cookies_path)) + return; + + sql::Connection db; + if (!db.Open(cookies_path)) + return; + + const char query[] = + "SELECT host_key, name, value, path, expires_utc, secure, httponly, " + "encrypted_value FROM cookies WHERE length(encrypted_value) = 0"; + + sql::Statement s(db.GetUniqueStatement(query)); + + std::vector cookies; + while (s.Step() && !cancelled()) { + ImportedCookieEntry cookie; + base::string16 host(base::UTF8ToUTF16("*")); + host.append(s.ColumnString16(0)); + cookie.domain = s.ColumnString16(0); + cookie.name = s.ColumnString16(1); + cookie.value = s.ColumnString16(2); + cookie.host = host; + cookie.path = s.ColumnString16(3); + cookie.expiry_date = + base::Time::FromDoubleT(chromeTimeToDouble((s.ColumnInt64(4)))); + cookie.secure = s.ColumnBool(5); + cookie.httponly = s.ColumnBool(6); + + cookies.push_back(cookie); + } + + if (!cookies.empty() && !cancelled()) + bridge_->SetCookies(cookies); +} + +void ChromeImporter::RecursiveReadBookmarksFolder( + const base::DictionaryValue* folder, + const std::vector& parent_path, + bool is_in_toolbar, + std::vector* bookmarks) { + const base::ListValue* children; + if (folder->GetList("children", &children)) { + for (const base::Value* value : *children) { + const base::DictionaryValue* dict; + if (!value->GetAsDictionary(&dict)) + continue; + std::string date_added, type, url; + base::string16 name; + dict->GetString("date_added", &date_added); + dict->GetString("name", &name); + dict->GetString("type", &type); + dict->GetString("url", &url); + ImportedBookmarkEntry entry; + if (type == "folder") { + entry.in_toolbar = is_in_toolbar; + entry.is_folder = true; + entry.url = GURL(); + entry.path = parent_path; + entry.title = name; + entry.creation_time = + base::Time::FromDoubleT(chromeTimeToDouble(std::stol(date_added))); + bookmarks->push_back(entry); + + std::vector path = parent_path; + path.push_back(name); + RecursiveReadBookmarksFolder(dict, path, false, bookmarks); + } else if (type == "url") { + entry.in_toolbar = is_in_toolbar; + entry.is_folder = false; + entry.url = GURL(url); + entry.path = parent_path; + entry.title = name; + entry.creation_time = + base::Time::FromDoubleT(chromeTimeToDouble(std::stol(date_added))); + bookmarks->push_back(entry); + } + } + } +} + +double ChromeImporter::chromeTimeToDouble(int64_t time) { + return ((time * 10 - 0x19DB1DED53E8000) / 10000) / 1000; +} diff --git a/atom/utility/importer/chrome_importer.h b/atom/utility/importer/chrome_importer.h new file mode 100644 index 0000000000..be96680b78 --- /dev/null +++ b/atom/utility/importer/chrome_importer.h @@ -0,0 +1,53 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_UTILITY_IMPORTER_CHROME_IMPORTER_H_ +#define ATOM_UTILITY_IMPORTER_CHROME_IMPORTER_H_ + +#include + +#include + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "build/build_config.h" +#include "chrome/utility/importer/importer.h" + +struct ImportedBookmarkEntry; + +namespace base { +class DictionaryValue; +} + +class ChromeImporter : public Importer { + public: + ChromeImporter(); + + // Importer: + void StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) override; + + private: + ~ChromeImporter() override; + + void ImportBookmarks(); + void ImportHistory(); + void ImportCookies(); + + void RecursiveReadBookmarksFolder( + const base::DictionaryValue* folder, + const std::vector& parent_path, + bool is_in_toolbar, + std::vector* bookmarks); + + double chromeTimeToDouble(int64_t time); + + base::FilePath source_path_; + + DISALLOW_COPY_AND_ASSIGN(ChromeImporter); +}; + +#endif // ATOM_UTILITY_IMPORTER_CHROME_IMPORTER_H_ diff --git a/chromium_src/chrome/browser/importer/external_process_importer_client.cc b/chromium_src/chrome/browser/importer/external_process_importer_client.cc new file mode 100644 index 0000000000..950c84866e --- /dev/null +++ b/chromium_src/chrome/browser/importer/external_process_importer_client.cc @@ -0,0 +1,384 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add SetCookies. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/browser/importer/external_process_importer_client.h" + +#include "base/bind.h" +#include "base/strings/string_number_conversions.h" +#include "build/build_config.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/importer/external_process_importer_host.h" +#include "chrome/browser/importer/in_process_importer_bridge.h" +#include "chrome/common/importer/firefox_importer_utils.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/profile_import_process_messages.h" +#include "chrome/grit/generated_resources.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/utility_process_host.h" +#include "grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" + +using content::BrowserThread; +using content::UtilityProcessHost; + +ExternalProcessImporterClient::ExternalProcessImporterClient( + base::WeakPtr importer_host, + const importer::SourceProfile& source_profile, + uint16_t items, + InProcessImporterBridge* bridge) + : total_bookmarks_count_(0), + total_history_rows_count_(0), + total_favicons_count_(0), + process_importer_host_(importer_host), + source_profile_(source_profile), + items_(items), + bridge_(bridge), + cancelled_(false) { + process_importer_host_->NotifyImportStarted(); +} + +void ExternalProcessImporterClient::Start() { + AddRef(); // balanced in Cleanup. + BrowserThread::ID thread_id; + CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_id)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&ExternalProcessImporterClient::StartProcessOnIOThread, + this, + thread_id)); +} + +void ExternalProcessImporterClient::Cancel() { + if (cancelled_) + return; + + cancelled_ = true; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind( + &ExternalProcessImporterClient::CancelImportProcessOnIOThread, + this)); + Release(); +} + +void ExternalProcessImporterClient::OnProcessCrashed(int exit_code) { + DLOG(ERROR) << __FUNCTION__; + if (cancelled_) + return; + + // If the host is still around, cancel the import; otherwise it means the + // import was already cancelled or completed and this message can be dropped. + if (process_importer_host_.get()) + process_importer_host_->Cancel(); +} + +bool ExternalProcessImporterClient::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ExternalProcessImporterClient, message) + // Notification messages about the state of the import process. + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_Import_Started, + OnImportStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_Import_Finished, + OnImportFinished) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_ImportItem_Started, + OnImportItemStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_ImportItem_Finished, + OnImportItemFinished) + // Data messages containing items to be written to the user profile. + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyHistoryImportStart, + OnHistoryImportStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyHistoryImportGroup, + OnHistoryImportGroup) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyHomePageImportReady, + OnHomePageImportReady) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyBookmarksImportStart, + OnBookmarksImportStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyBookmarksImportGroup, + OnBookmarksImportGroup) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyFaviconsImportStart, + OnFaviconsImportStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyFaviconsImportGroup, + OnFaviconsImportGroup) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyPasswordFormReady, + OnPasswordFormImportReady) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyKeywordsReady, + OnKeywordsImportReady) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyFirefoxSearchEngData, + OnFirefoxSearchEngineDataReceived) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_AutofillFormDataImportStart, + OnAutofillFormDataImportStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_AutofillFormDataImportGroup, + OnAutofillFormDataImportGroup) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyCookiesImportStart, + OnCookiesImportStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyCookiesImportGroup, + OnCookiesImportGroup) +#if defined(OS_WIN) + IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyIE7PasswordInfo, + OnIE7PasswordReceived) +#endif + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void ExternalProcessImporterClient::OnImportStart() { + if (cancelled_) + return; + + bridge_->NotifyStarted(); +} + +void ExternalProcessImporterClient::OnImportFinished( + bool succeeded, const std::string& error_msg) { + if (cancelled_) + return; + + if (!succeeded) + LOG(WARNING) << "Import failed. Error: " << error_msg; + Cleanup(); +} + +void ExternalProcessImporterClient::OnImportItemStart(int item_data) { + if (cancelled_) + return; + + bridge_->NotifyItemStarted(static_cast(item_data)); +} + +void ExternalProcessImporterClient::OnImportItemFinished(int item_data) { + if (cancelled_) + return; + + importer::ImportItem import_item = + static_cast(item_data); + bridge_->NotifyItemEnded(import_item); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&ExternalProcessImporterClient::NotifyItemFinishedOnIOThread, + this, + import_item)); +} + +void ExternalProcessImporterClient::OnHistoryImportStart( + size_t total_history_rows_count) { + if (cancelled_) + return; + + total_history_rows_count_ = total_history_rows_count; + history_rows_.reserve(total_history_rows_count); +} + +void ExternalProcessImporterClient::OnHistoryImportGroup( + const std::vector& history_rows_group, + int visit_source) { + if (cancelled_) + return; + + history_rows_.insert(history_rows_.end(), history_rows_group.begin(), + history_rows_group.end()); + if (history_rows_.size() >= total_history_rows_count_) + bridge_->SetHistoryItems(history_rows_, + static_cast(visit_source)); +} + +void ExternalProcessImporterClient::OnHomePageImportReady( + const GURL& home_page) { + if (cancelled_) + return; + + bridge_->AddHomePage(home_page); +} + +void ExternalProcessImporterClient::OnBookmarksImportStart( + const base::string16& first_folder_name, + size_t total_bookmarks_count) { + if (cancelled_) + return; + + bookmarks_first_folder_name_ = first_folder_name; + total_bookmarks_count_ = total_bookmarks_count; + bookmarks_.reserve(total_bookmarks_count); +} + +void ExternalProcessImporterClient::OnBookmarksImportGroup( + const std::vector& bookmarks_group) { + if (cancelled_) + return; + + // Collect sets of bookmarks from importer process until we have reached + // total_bookmarks_count_: + bookmarks_.insert(bookmarks_.end(), bookmarks_group.begin(), + bookmarks_group.end()); + if (bookmarks_.size() >= total_bookmarks_count_) + bridge_->AddBookmarks(bookmarks_, bookmarks_first_folder_name_); +} + +void ExternalProcessImporterClient::OnFaviconsImportStart( + size_t total_favicons_count) { + if (cancelled_) + return; + + total_favicons_count_ = total_favicons_count; + favicons_.reserve(total_favicons_count); +} + +void ExternalProcessImporterClient::OnFaviconsImportGroup( + const favicon_base::FaviconUsageDataList& favicons_group) { + if (cancelled_) + return; + + favicons_.insert(favicons_.end(), favicons_group.begin(), + favicons_group.end()); + if (favicons_.size() >= total_favicons_count_) + bridge_->SetFavicons(favicons_); +} + +void ExternalProcessImporterClient::OnPasswordFormImportReady( + const autofill::PasswordForm& form) { + if (cancelled_) + return; + + bridge_->SetPasswordForm(form); +} + +void ExternalProcessImporterClient::OnKeywordsImportReady( + const std::vector& search_engines, + bool unique_on_host_and_path) { + if (cancelled_) + return; + bridge_->SetKeywords(search_engines, unique_on_host_and_path); +} + +void ExternalProcessImporterClient::OnFirefoxSearchEngineDataReceived( + const std::vector search_engine_data) { + if (cancelled_) + return; + bridge_->SetFirefoxSearchEnginesXMLData(search_engine_data); +} + +void ExternalProcessImporterClient::OnAutofillFormDataImportStart( + size_t total_autofill_form_data_entry_count) { + if (cancelled_) + return; + + total_autofill_form_data_entry_count_ = total_autofill_form_data_entry_count; + autofill_form_data_.reserve(total_autofill_form_data_entry_count); +} + +void ExternalProcessImporterClient::OnAutofillFormDataImportGroup( + const std::vector& + autofill_form_data_entry_group) { + if (cancelled_) + return; + + autofill_form_data_.insert(autofill_form_data_.end(), + autofill_form_data_entry_group.begin(), + autofill_form_data_entry_group.end()); + if (autofill_form_data_.size() >= total_autofill_form_data_entry_count_) + bridge_->SetAutofillFormData(autofill_form_data_); +} +void ExternalProcessImporterClient::OnCookiesImportStart( + size_t total_cookies_count) { + if (cancelled_) + return; + + total_cookies_count_ = total_cookies_count; + cookies_.reserve(total_cookies_count); +} +void ExternalProcessImporterClient::OnCookiesImportGroup( + const std::vector& cookies_group) { + if (cancelled_) + return; + + cookies_.insert(cookies_.end(), cookies_group.begin(), + cookies_group.end()); + if (cookies_.size() >= total_cookies_count_) + bridge_->SetCookies(cookies_); +} + +#if defined(OS_WIN) +void ExternalProcessImporterClient::OnIE7PasswordReceived( + const importer::ImporterIE7PasswordInfo& importer_password_info) { + if (cancelled_) + return; + bridge_->AddIE7PasswordInfo(importer_password_info); +} +#endif + +ExternalProcessImporterClient::~ExternalProcessImporterClient() {} + +void ExternalProcessImporterClient::Cleanup() { + if (cancelled_) + return; + + if (process_importer_host_.get()) + process_importer_host_->NotifyImportEnded(); + Release(); +} + +void ExternalProcessImporterClient::CancelImportProcessOnIOThread() { + if (utility_process_host_.get()) + utility_process_host_->Send(new ProfileImportProcessMsg_CancelImport()); +} + +void ExternalProcessImporterClient::NotifyItemFinishedOnIOThread( + importer::ImportItem import_item) { + utility_process_host_->Send( + new ProfileImportProcessMsg_ReportImportItemFinished(import_item)); +} + +void ExternalProcessImporterClient::StartProcessOnIOThread( + BrowserThread::ID thread_id) { + utility_process_host_ = UtilityProcessHost::Create( + this, BrowserThread::GetMessageLoopProxyForThread(thread_id).get()) + ->AsWeakPtr(); + utility_process_host_->SetName(l10n_util::GetStringUTF16( + IDS_UTILITY_PROCESS_PROFILE_IMPORTER_NAME)); + utility_process_host_->DisableSandbox(); + +#if defined(OS_MACOSX) + base::EnvironmentMap env; + std::string dylib_path = GetFirefoxDylibPath().value(); + if (!dylib_path.empty()) + env["DYLD_FALLBACK_LIBRARY_PATH"] = dylib_path; + utility_process_host_->SetEnv(env); +#endif + + // Dictionary of all localized strings that could be needed by the importer + // in the external process. + base::DictionaryValue localized_strings; + localized_strings.SetString( + base::IntToString(IDS_BOOKMARK_GROUP), + l10n_util::GetStringUTF8(IDS_BOOKMARK_GROUP)); + localized_strings.SetString( + base::IntToString(IDS_BOOKMARK_GROUP_FROM_FIREFOX), + l10n_util::GetStringUTF8(IDS_BOOKMARK_GROUP_FROM_FIREFOX)); + localized_strings.SetString( + base::IntToString(IDS_BOOKMARK_GROUP_FROM_SAFARI), + l10n_util::GetStringUTF8(IDS_BOOKMARK_GROUP_FROM_SAFARI)); + localized_strings.SetString( + base::IntToString(IDS_IMPORT_FROM_FIREFOX), + l10n_util::GetStringUTF8(IDS_IMPORT_FROM_FIREFOX)); + localized_strings.SetString( + base::IntToString(IDS_IMPORT_FROM_ICEWEASEL), + l10n_util::GetStringUTF8(IDS_IMPORT_FROM_ICEWEASEL)); + localized_strings.SetString( + base::IntToString(IDS_IMPORT_FROM_SAFARI), + l10n_util::GetStringUTF8(IDS_IMPORT_FROM_SAFARI)); + localized_strings.SetString( + base::IntToString(IDS_BOOKMARK_BAR_FOLDER_NAME), + l10n_util::GetStringUTF8(IDS_BOOKMARK_BAR_FOLDER_NAME)); + + utility_process_host_->Send(new ProfileImportProcessMsg_StartImport( + source_profile_, items_, localized_strings)); +} diff --git a/chromium_src/chrome/browser/importer/external_process_importer_client.h b/chromium_src/chrome/browser/importer/external_process_importer_client.h new file mode 100644 index 0000000000..6f6a50ba76 --- /dev/null +++ b/chromium_src/chrome/browser/importer/external_process_importer_client.h @@ -0,0 +1,182 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add SetCookies. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_BROWSER_IMPORTER_EXTERNAL_PROCESS_IMPORTER_CLIENT_H_ +#define CHROME_BROWSER_IMPORTER_EXTERNAL_PROCESS_IMPORTER_CLIENT_H_ + +#include +#include + +#include +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" +#include "build/build_config.h" +#include "chrome/common/importer/importer_autofill_form_data_entry.h" +#include "chrome/common/importer/importer_data_types.h" +#include "chrome/common/importer/importer_url_row.h" +#include "components/favicon_base/favicon_usage_data.h" +#include "components/history/core/browser/history_types.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/utility_process_host_client.h" + +class ExternalProcessImporterHost; +struct ImportedBookmarkEntry; +class InProcessImporterBridge; + +namespace autofill { +struct PasswordForm; +} + +struct ImportedCookieEntry; + +namespace content{ +class UtilityProcessHost; +} + +namespace importer { +#if defined(OS_WIN) +struct ImporterIE7PasswordInfo; +#endif +struct ImporterAutofillFormDataEntry; +struct SearchEngineInfo; +} + +// This class is the client for the out of process profile importing. It +// collects notifications from this process host and feeds data back to the +// importer host, who actually does the writing. +class ExternalProcessImporterClient : public content::UtilityProcessHostClient { + public: + ExternalProcessImporterClient( + base::WeakPtr importer_host, + const importer::SourceProfile& source_profile, + uint16_t items, + InProcessImporterBridge* bridge); + + // Launches the task to start the external process. + void Start(); + + // Called by the ExternalProcessImporterHost on import cancel. + void Cancel(); + + // UtilityProcessHostClient implementation: + void OnProcessCrashed(int exit_code) override; + bool OnMessageReceived(const IPC::Message& message) override; + + // Message handlers + void OnImportStart(); + void OnImportFinished(bool succeeded, const std::string& error_msg); + void OnImportItemStart(int item); + void OnImportItemFinished(int item); + void OnHistoryImportStart(size_t total_history_rows_count); + void OnHistoryImportGroup( + const std::vector& history_rows_group, + int visit_source); + void OnHomePageImportReady(const GURL& home_page); + void OnBookmarksImportStart(const base::string16& first_folder_name, + size_t total_bookmarks_count); + void OnBookmarksImportGroup( + const std::vector& bookmarks_group); + void OnFaviconsImportStart(size_t total_favicons_count); + void OnFaviconsImportGroup( + const favicon_base::FaviconUsageDataList& favicons_group); + void OnPasswordFormImportReady(const autofill::PasswordForm& form); + void OnKeywordsImportReady( + const std::vector& search_engines, + bool unique_on_host_and_path); + void OnFirefoxSearchEngineDataReceived( + const std::vector search_engine_data); + void OnAutofillFormDataImportStart( + size_t total_autofill_form_data_entry_count); + void OnAutofillFormDataImportGroup(const std::vector< + ImporterAutofillFormDataEntry>& autofill_form_data_entry_group); + void OnCookiesImportStart(size_t total_cookies_count); + void OnCookiesImportGroup( + const std::vector& cookies_group); +#if defined(OS_WIN) + void OnIE7PasswordReceived( + const importer::ImporterIE7PasswordInfo& importer_password_info); +#endif + + protected: + ~ExternalProcessImporterClient() override; + + private: + // Notifies the importerhost that import has finished, and calls Release(). + void Cleanup(); + + // Cancel import process on IO thread. + void CancelImportProcessOnIOThread(); + + // Report item completely downloaded on IO thread. + void NotifyItemFinishedOnIOThread(importer::ImportItem import_item); + + // Creates a new UtilityProcessHost, which launches the import process. + void StartProcessOnIOThread(content::BrowserThread::ID thread_id); + + // These variables store data being collected from the importer until the + // entire group has been collected and is ready to be written to the profile. + std::vector history_rows_; + std::vector bookmarks_; + favicon_base::FaviconUsageDataList favicons_; + std::vector autofill_form_data_; + std::vector cookies_; + + // Usually some variation on IDS_BOOKMARK_GROUP_...; the name of the folder + // under which imported bookmarks will be placed. + base::string16 bookmarks_first_folder_name_; + + // Total number of bookmarks to import. + size_t total_bookmarks_count_; + + // Total number of history items to import. + size_t total_history_rows_count_; + + // Total number of favicons to import. + size_t total_favicons_count_; + + // Total number of autofill form data entries to import. + size_t total_autofill_form_data_entry_count_; + + // Total number of cookies to import. + size_t total_cookies_count_; + + // Notifications received from the ProfileImportProcessHost are passed back + // to process_importer_host_, which calls the ProfileWriter to record the + // import data. When the import process is done, process_importer_host_ + // deletes itself. This is a weak ptr so that any messages received after + // the host has deleted itself are ignored (e.g., it's possible to receive + // OnProcessCrashed() after NotifyImportEnded()). + base::WeakPtr process_importer_host_; + + // Handles sending messages to the external process. Deletes itself when + // the external process dies (see + // BrowserChildProcessHost::OnChildDisconnected). + base::WeakPtr utility_process_host_; + + // Data to be passed from the importer host to the external importer. + importer::SourceProfile source_profile_; + uint16_t items_; + + // Takes import data coming over IPC and delivers it to be written by the + // ProfileWriter. + scoped_refptr bridge_; + + // True if import process has been cancelled. + bool cancelled_; + + DISALLOW_COPY_AND_ASSIGN(ExternalProcessImporterClient); +}; + +#endif // CHROME_BROWSER_IMPORTER_EXTERNAL_PROCESS_IMPORTER_CLIENT_H_ diff --git a/chromium_src/chrome/browser/importer/external_process_importer_host.cc b/chromium_src/chrome/browser/importer/external_process_importer_host.cc new file mode 100644 index 0000000000..3ebf7fc51a --- /dev/null +++ b/chromium_src/chrome/browser/importer/external_process_importer_host.cc @@ -0,0 +1,224 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * some functionality is removed. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/browser/importer/external_process_importer_host.h" + +#include "base/bind.h" +//#include "chrome/browser/bookmarks/bookmark_model_factory.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/importer/external_process_importer_client.h" +#include "chrome/browser/importer/firefox_profile_lock.h" +#include "chrome/browser/importer/importer_lock_dialog.h" +#include "chrome/browser/importer/importer_progress_observer.h" +#include "chrome/browser/importer/in_process_importer_bridge.h" +//#include "chrome/browser/search_engines/template_url_service_factory.h" +//#include "components/bookmarks/browser/bookmark_model.h" +//#include "components/search_engines/template_url_service.h" +#include "content/public/browser/browser_thread.h" + +//using bookmarks::BookmarkModel; +using content::BrowserThread; + +ExternalProcessImporterHost::ExternalProcessImporterHost() + : headless_(false), + parent_window_(NULL), + observer_(NULL), + profile_(NULL), + //waiting_for_bookmarkbar_model_(false), + //installed_bookmark_observer_(false), + is_source_readable_(true), + client_(NULL), + items_(0), + cancelled_(false), + weak_ptr_factory_(this) { +} + +void ExternalProcessImporterHost::Cancel() { + cancelled_ = true; + // There is only a |client_| if the import was started. + if (client_) + client_->Cancel(); + NotifyImportEnded(); // Tells the observer that we're done, and deletes us. +} + +void ExternalProcessImporterHost::StartImportSettings( + const importer::SourceProfile& source_profile, + Profile* target_profile, + uint16_t items, + ProfileWriter* writer) { + // We really only support importing from one host at a time. + DCHECK(!profile_); + DCHECK(target_profile); + + profile_ = target_profile; + writer_ = writer; + source_profile_ = source_profile; + items_ = items; + + if (!CheckForFirefoxLock(source_profile)) { + Cancel(); + return; + } + + CheckForLoadedModels(items); + + LaunchImportIfReady(); +} + +void ExternalProcessImporterHost::NotifyImportStarted() { + if (observer_) + observer_->ImportStarted(); +} + +void ExternalProcessImporterHost::NotifyImportItemStarted( + importer::ImportItem item) { + if (observer_) + observer_->ImportItemStarted(item); +} + +void ExternalProcessImporterHost::NotifyImportItemEnded( + importer::ImportItem item) { + if (observer_) + observer_->ImportItemEnded(item); +} + +void ExternalProcessImporterHost::NotifyImportEnded() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + firefox_lock_.reset(); + if (observer_) + observer_->ImportEnded(); + delete this; +} + +ExternalProcessImporterHost::~ExternalProcessImporterHost() { + /* + if (installed_bookmark_observer_) { + DCHECK(profile_); + //BookmarkModelFactory::GetForProfile(profile_)->RemoveObserver(this); + }*/ +} + +void ExternalProcessImporterHost::LaunchImportIfReady() { + //if (waiting_for_bookmarkbar_model_ || template_service_subscription_.get() || + if(!is_source_readable_ || cancelled_) + return; + + // This is the in-process half of the bridge, which catches data from the IPC + // pipe and feeds it to the ProfileWriter. The external process half of the + // bridge lives in the external process (see ProfileImportThread). + // The ExternalProcessImporterClient created in the next line owns the bridge, + // and will delete it. + InProcessImporterBridge* bridge = + new InProcessImporterBridge(writer_.get(), + weak_ptr_factory_.GetWeakPtr()); + client_ = new ExternalProcessImporterClient( + weak_ptr_factory_.GetWeakPtr(), source_profile_, items_, bridge); + client_->Start(); +} +/* +void ExternalProcessImporterHost::BookmarkModelLoaded(BookmarkModel* model, + bool ids_reassigned) { + DCHECK(model->loaded()); + model->RemoveObserver(this); + waiting_for_bookmarkbar_model_ = false; + installed_bookmark_observer_ = false; + + LaunchImportIfReady(); +} + +void ExternalProcessImporterHost::BookmarkModelBeingDeleted( + BookmarkModel* model) { + installed_bookmark_observer_ = false; +} + +void ExternalProcessImporterHost::BookmarkModelChanged() { +} +void ExternalProcessImporterHost::OnTemplateURLServiceLoaded() { + //template_service_subscription_.reset(); + LaunchImportIfReady(); +} +*/ + +void ExternalProcessImporterHost::ShowWarningDialog() { + DCHECK(!headless_); + importer::ShowImportLockDialog( + parent_window_, + base::Bind(&ExternalProcessImporterHost::OnImportLockDialogEnd, + weak_ptr_factory_.GetWeakPtr())); + writer_->ShowWarningDialog(); +} + +void ExternalProcessImporterHost::OnImportLockDialogEnd(bool is_continue) { + if (is_continue) { + // User chose to continue, then we check the lock again to make + // sure that Firefox has been closed. Try to import the settings + // if successful. Otherwise, show a warning dialog. + firefox_lock_->Lock(); + if (firefox_lock_->HasAcquired()) { + is_source_readable_ = true; + LaunchImportIfReady(); + } else { + ShowWarningDialog(); + } + } else { + NotifyImportEnded(); + } +} + +bool ExternalProcessImporterHost::CheckForFirefoxLock( + const importer::SourceProfile& source_profile) { + if (source_profile.importer_type != importer::TYPE_FIREFOX) + return true; + + DCHECK(!firefox_lock_.get()); + firefox_lock_.reset(new FirefoxProfileLock(source_profile.source_path)); + if (firefox_lock_->HasAcquired()) + return true; + + // If fail to acquire the lock, we set the source unreadable and + // show a warning dialog, unless running without UI (in which case the import + // must be aborted). + is_source_readable_ = false; + if (headless_) + return false; + + ShowWarningDialog(); + return true; +} + +void ExternalProcessImporterHost::CheckForLoadedModels(uint16_t items) { + // A target profile must be loaded by StartImportSettings(). + DCHECK(profile_); + + // BookmarkModel should be loaded before adding IE favorites. So we observe + // the BookmarkModel if needed, and start the task after it has been loaded. + /* + if ((items & importer::FAVORITES) && !writer_->BookmarkModelIsLoaded()) { + BookmarkModelFactory::GetForProfile(profile_)->AddObserver(this); + waiting_for_bookmarkbar_model_ = true; + installed_bookmark_observer_ = true; + } + + // Observes the TemplateURLService if needed to import search engines from the + // other browser. We also check to see if we're importing bookmarks because + // we can import bookmark keywords from Firefox as search engines. + if ((items & importer::SEARCH_ENGINES) || (items & importer::FAVORITES)) { + if (!writer_->TemplateURLServiceIsLoaded()) { + TemplateURLService* model = + TemplateURLServiceFactory::GetForProfile(profile_); + template_service_subscription_ = model->RegisterOnLoadedCallback( + base::Bind(&ExternalProcessImporterHost::OnTemplateURLServiceLoaded, + weak_ptr_factory_.GetWeakPtr())); + model->Load(); + } + } + */ +} diff --git a/chromium_src/chrome/browser/importer/external_process_importer_host.h b/chromium_src/chrome/browser/importer/external_process_importer_host.h new file mode 100644 index 0000000000..e71a0a4345 --- /dev/null +++ b/chromium_src/chrome/browser/importer/external_process_importer_host.h @@ -0,0 +1,180 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * some functionality is removed. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_BROWSER_IMPORTER_EXTERNAL_PROCESS_IMPORTER_HOST_H_ +#define CHROME_BROWSER_IMPORTER_EXTERNAL_PROCESS_IMPORTER_HOST_H_ + +#include + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/importer/importer_progress_observer.h" +#include "chrome/browser/importer/profile_writer.h" +#include "chrome/common/importer/importer_data_types.h" +//#include "components/bookmarks/browser/base_bookmark_model_observer.h" +//#include "components/search_engines/template_url_service.h" +#include "ui/gfx/native_widget_types.h" + +class ExternalProcessImporterClient; +class FirefoxProfileLock; +class Importer; +class Profile; + +namespace importer { +struct SourceProfile; +} + +// This class manages the import process. It creates the in-process half of the +// importer bridge and the external process importer client. +class ExternalProcessImporterHost { + //: public bookmarks::BaseBookmarkModelObserver { + public: + ExternalProcessImporterHost(); + + void Cancel(); + + // Starts the process of importing the settings and data depending on what the + // user selected. + // |source_profile| - importer profile to import. + // |target_profile| - profile to import into. + // |items| - specifies which data to import (bitmask of importer::ImportItem). + // |writer| - called to actually write data back to the profile. + virtual void StartImportSettings( + const importer::SourceProfile& source_profile, + Profile* target_profile, + uint16_t items, + ProfileWriter* writer); + + // When in headless mode, the importer will not show any warning dialog if + // a user action is required (e.g., Firefox profile is locked and user should + // close Firefox to continue) and the outcome is as if the user had canceled + // the import operation. + void set_headless() { headless_ = true; } + bool is_headless() const { return headless_; } + + void set_parent_window(gfx::NativeWindow parent_window) { + parent_window_ = parent_window; + } + + void set_observer(importer::ImporterProgressObserver* observer) { + observer_ = observer; + } + + // A series of functions invoked at the start, during and end of the import + // process. The middle functions are notifications that the a harvesting of a + // particular source of data (specified by |item|) is under way. + void NotifyImportStarted(); + void NotifyImportItemStarted(importer::ImportItem item); + void NotifyImportItemEnded(importer::ImportItem item); + void NotifyImportEnded(); + + private: + // ExternalProcessImporterHost deletes itself in OnImportEnded(). + //~ExternalProcessImporterHost() override; + virtual ~ExternalProcessImporterHost(); + + // Launches the utility process that starts the import task, unless bookmark + // or template model are not yet loaded. If load is not detected, this method + // will be called when the loading observer sees that model loading is + // complete. + virtual void LaunchImportIfReady(); + + // bookmarks::BaseBookmarkModelObserver: + /* + void BookmarkModelLoaded(bookmarks::BookmarkModel* model, + bool ids_reassigned) override; + void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override; + void BookmarkModelChanged() override; + + // Called when TemplateURLService has been loaded. + void OnTemplateURLServiceLoaded(); + */ + + // ShowWarningDialog() asks user to close the application that is owning the + // lock. They can retry or skip the importing process. + // This method should not be called if the importer is in headless mode. + void ShowWarningDialog(); + + // This is called when when user ends the lock dialog by clicking on either + // the "Skip" or "Continue" buttons. |is_continue| is true when user clicked + // the "Continue" button. + void OnImportLockDialogEnd(bool is_continue); + + // Make sure that Firefox isn't running, if import browser is Firefox. Show + // to the user a dialog that notifies that is necessary to close Firefox + // prior to continue. + // |source_profile| - importer profile to import. + // Returns false iff import should be aborted. + bool CheckForFirefoxLock(const importer::SourceProfile& source_profile); + + // Make sure BookmarkModel and TemplateURLService are loaded before import + // process starts, if bookmarks and/or search engines are among the items + // which are to be imported. + void CheckForLoadedModels(uint16_t items); + + // True if UI is not to be shown. + bool headless_; + + // Parent window that we pass to the import lock dialog (i.e, the Firefox + // warning dialog). + gfx::NativeWindow parent_window_; + + // The observer that we need to notify about changes in the import process. + importer::ImporterProgressObserver* observer_; + + // Firefox profile lock. + std::unique_ptr firefox_lock_; + + // Profile we're importing from. + Profile* profile_; + + // True if we're waiting for the model to finish loading. + //bool waiting_for_bookmarkbar_model_; + + // May contain a Subscription waiting for the TemplateURLService to finish + // loading. + /* + std::unique_ptr + template_service_subscription_; + */ + + // Have we installed a listener on the bookmark model? + //bool installed_bookmark_observer_; + + // True if source profile is readable. + bool is_source_readable_; + + // Writes data from the importer back to the profile. + scoped_refptr writer_; + + // Used to pass notifications from the browser side to the external process. + ExternalProcessImporterClient* client_; + + // Information about a profile needed for importing. + importer::SourceProfile source_profile_; + + // Bitmask of items to be imported (see importer::ImportItem enum). + uint16_t items_; + + // True if the import process has been cancelled. + bool cancelled_; + + // Vends weak pointers for the importer to call us back. + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ExternalProcessImporterHost); +}; + +#endif // CHROME_BROWSER_IMPORTER_EXTERNAL_PROCESS_IMPORTER_HOST_H_ diff --git a/chromium_src/chrome/browser/importer/firefox_profile_lock.cc b/chromium_src/chrome/browser/importer/firefox_profile_lock.cc new file mode 100644 index 0000000000..a5afafcf00 --- /dev/null +++ b/chromium_src/chrome/browser/importer/firefox_profile_lock.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2010 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/importer/firefox_profile_lock.h" + +#include "base/files/file_path.h" +#include "base/threading/thread_restrictions.h" +#include "build/build_config.h" + +// This class is based on Firefox code in: +// profile/dirserviceprovider/src/nsProfileLock.cpp +// The license block is: + +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Conrad Carlen +* Brendan Eich +* Colin Blake +* Javier Pedemonte +* Mats Palmgren +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +// static +#if defined(OS_MACOSX) +const base::FilePath::CharType* FirefoxProfileLock::kLockFileName = + FILE_PATH_LITERAL(".parentlock"); +const base::FilePath::CharType* FirefoxProfileLock::kOldLockFileName = + FILE_PATH_LITERAL("parent.lock"); +#elif defined(OS_POSIX) +// http://www.google.com/codesearch/p?hl=en#e_ObwTAVPyo/profile/dirserviceprovider/src/nsProfileLock.cpp&l=433 +const base::FilePath::CharType* FirefoxProfileLock::kLockFileName = + FILE_PATH_LITERAL(".parentlock"); +const base::FilePath::CharType* FirefoxProfileLock::kOldLockFileName = + FILE_PATH_LITERAL("lock"); +#else +const base::FilePath::CharType* FirefoxProfileLock::kLockFileName = + FILE_PATH_LITERAL("parent.lock"); +#endif + +FirefoxProfileLock::FirefoxProfileLock(const base::FilePath& path) { + Init(); + lock_file_ = path.Append(kLockFileName); + Lock(); +} + +FirefoxProfileLock::~FirefoxProfileLock() { + // Because this destructor happens in first run on the profile import thread, + // with no UI to jank, it's ok to allow deletion of the lock here. + base::ThreadRestrictions::ScopedAllowIO allow_io; + Unlock(); +} diff --git a/chromium_src/chrome/browser/importer/firefox_profile_lock.h b/chromium_src/chrome/browser/importer/firefox_profile_lock.h new file mode 100644 index 0000000000..b35d86db74 --- /dev/null +++ b/chromium_src/chrome/browser/importer/firefox_profile_lock.h @@ -0,0 +1,112 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_IMPORTER_FIREFOX_PROFILE_LOCK_H__ +#define CHROME_BROWSER_IMPORTER_FIREFOX_PROFILE_LOCK_H__ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" + +// Firefox is designed to allow only one application to access its +// profile at the same time. +// Reference: +// http://kb.mozillazine.org/Profile_in_use +// +// This class is based on Firefox code in: +// profile/dirserviceprovider/src/nsProfileLock.cpp +// The license block is: + +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Conrad Carlen +* Brendan Eich +* Colin Blake +* Javier Pedemonte +* Mats Palmgren +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +class FirefoxProfileLock { + public: + explicit FirefoxProfileLock(const base::FilePath& path); + ~FirefoxProfileLock(); + + // Locks and releases the profile. + void Lock(); + void Unlock(); + + // Returns true if we lock the profile successfully. + bool HasAcquired(); + + private: + FRIEND_TEST_ALL_PREFIXES(FirefoxProfileLockTest, ProfileLock); + FRIEND_TEST_ALL_PREFIXES(FirefoxProfileLockTest, ProfileLockOrphaned); + + static const base::FilePath::CharType* kLockFileName; + static const base::FilePath::CharType* kOldLockFileName; + + void Init(); + + // Full path of the lock file in the profile folder. + base::FilePath lock_file_; + + // The handle of the lock file. +#if defined(OS_WIN) + HANDLE lock_handle_; +#elif defined(OS_POSIX) + int lock_fd_; + + // On Posix systems Firefox apparently first tries to put a fcntl lock + // on a file and if that fails, it does a regular exculsive open on another + // file. This variable contains the location of this other file. + base::FilePath old_lock_file_; + + // Method that tries to put a fcntl lock on file specified by |lock_file_|. + // Returns false if lock is already held by another process. true in all + // other cases. + bool LockWithFcntl(); +#endif + + DISALLOW_COPY_AND_ASSIGN(FirefoxProfileLock); +}; + +#endif // CHROME_BROWSER_IMPORTER_FIREFOX_PROFILE_LOCK_H__ diff --git a/chromium_src/chrome/browser/importer/firefox_profile_lock_posix.cc b/chromium_src/chrome/browser/importer/firefox_profile_lock_posix.cc new file mode 100644 index 0000000000..7631299713 --- /dev/null +++ b/chromium_src/chrome/browser/importer/firefox_profile_lock_posix.cc @@ -0,0 +1,124 @@ +// Copyright (c) 2009 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/importer/firefox_profile_lock.h" + +#include +#include +#include +#include + +#include "base/files/file_util.h" + +// This class is based on Firefox code in: +// profile/dirserviceprovider/src/nsProfileLock.cpp +// The license block is: + +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Conrad Carlen +* Brendan Eich +* Colin Blake +* Javier Pedemonte +* Mats Palmgren +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +void FirefoxProfileLock::Init() { + lock_fd_ = -1; +} + +void FirefoxProfileLock::Lock() { + if (HasAcquired()) + return; + + bool fcntl_lock = LockWithFcntl(); + if (!fcntl_lock) { + return; + } else if (!HasAcquired()) { + old_lock_file_ = lock_file_.DirName().Append(kOldLockFileName); + lock_fd_ = open(old_lock_file_.value().c_str(), O_CREAT | O_EXCL, 0644); + } +} + +void FirefoxProfileLock::Unlock() { + if (!HasAcquired()) + return; + close(lock_fd_); + lock_fd_ = -1; + base::DeleteFile(old_lock_file_, false); +} + +bool FirefoxProfileLock::HasAcquired() { + return (lock_fd_ >= 0); +} + +// This function tries to lock Firefox profile using fcntl(). The return +// value of this function together with HasAcquired() tells the current status +// of lock. +// if return == false: Another process has lock to the profile. +// if return == true && HasAcquired() == true: successfully acquired the lock. +// if return == false && HasAcquired() == false: Failed to acquire lock due +// to some error (so that we can try alternate method of profile lock). +bool FirefoxProfileLock::LockWithFcntl() { + lock_fd_ = open(lock_file_.value().c_str(), O_WRONLY | O_CREAT | O_TRUNC, + 0666); + if (lock_fd_ == -1) + return true; + + struct flock lock; + lock.l_start = 0; + lock.l_len = 0; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_pid = 0; + + struct flock testlock = lock; + if (fcntl(lock_fd_, F_GETLK, &testlock) == -1) { + close(lock_fd_); + lock_fd_ = -1; + return true; + } else if (fcntl(lock_fd_, F_SETLK, &lock) == -1) { + close(lock_fd_); + lock_fd_ = -1; + if (errno == EAGAIN || errno == EACCES) + return false; + else + return true; + } else { + // We have the lock. + return true; + } +} diff --git a/chromium_src/chrome/browser/importer/firefox_profile_lock_win.cc b/chromium_src/chrome/browser/importer/firefox_profile_lock_win.cc new file mode 100644 index 0000000000..e0c46d97bf --- /dev/null +++ b/chromium_src/chrome/browser/importer/firefox_profile_lock_win.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2008 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/importer/firefox_profile_lock.h" + +#include + +// This class is based on Firefox code in: +// profile/dirserviceprovider/src/nsProfileLock.cpp +// The license block is: + +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is mozilla.org code. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 2002 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Conrad Carlen +* Brendan Eich +* Colin Blake +* Javier Pedemonte +* Mats Palmgren +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +void FirefoxProfileLock::Init() { + lock_handle_ = INVALID_HANDLE_VALUE; +} + +void FirefoxProfileLock::Lock() { + if (HasAcquired()) + return; + lock_handle_ = CreateFile(lock_file_.value().c_str(), + GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, + FILE_FLAG_DELETE_ON_CLOSE, NULL); +} + +void FirefoxProfileLock::Unlock() { + if (!HasAcquired()) + return; + CloseHandle(lock_handle_); + lock_handle_ = INVALID_HANDLE_VALUE; +} + +bool FirefoxProfileLock::HasAcquired() { + return (lock_handle_ != INVALID_HANDLE_VALUE); +} diff --git a/chromium_src/chrome/browser/importer/importer_list.cc b/chromium_src/chrome/browser/importer/importer_list.cc new file mode 100644 index 0000000000..477b95712b --- /dev/null +++ b/chromium_src/chrome/browser/importer/importer_list.cc @@ -0,0 +1,257 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * some functionality is removed. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/browser/importer/importer_list.h" + +#include + +#include "atom/common/importer/chrome_importer_utils.h" +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "build/build_config.h" +//#include "chrome/browser/shell_integration.h" +#include "chrome/common/importer/firefox_importer_utils.h" +#include "chrome/common/importer/importer_bridge.h" +#include "chrome/common/importer/importer_data_types.h" +#include "chrome/grit/generated_resources.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/l10n/l10n_util.h" + +#if defined(OS_MACOSX) +#include + +#include "base/mac/foundation_util.h" +#include "chrome/common/importer/safari_importer_utils.h" +#endif + +#if defined(OS_WIN) +#include "chrome/common/importer/edge_importer_utils_win.h" +#endif + +using content::BrowserThread; + +namespace shell_integration { + bool IsFirefoxDefaultBrowser() { + return false; + } +} + +namespace { + +#if defined(OS_WIN) +void DetectIEProfiles(std::vector* profiles) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + // IE always exists and doesn't have multiple profiles. + importer::SourceProfile ie; + // ie.importer_name = l10n_util::GetStringUTF16(IDS_IMPORT_FROM_IE); + ie.importer_name = base::UTF8ToUTF16("Microsoft Internet Explorer"); + ie.importer_type = importer::TYPE_IE; + ie.services_supported = importer::HISTORY | importer::FAVORITES | + importer::COOKIES | importer::PASSWORDS | + importer::SEARCH_ENGINES; + profiles->push_back(ie); +} + +void DetectEdgeProfiles(std::vector* profiles) { + importer::SourceProfile edge; + // edge.importer_name = l10n_util::GetStringUTF16(IDS_IMPORT_FROM_EDGE); + edge.importer_name = base::UTF8ToUTF16("Microsoft Edge"); + edge.importer_type = importer::TYPE_EDGE; + edge.services_supported = importer::FAVORITES; + edge.source_path = importer::GetEdgeDataFilePath(); + profiles->push_back(edge); +} + +void DetectBuiltinWindowsProfiles( + std::vector* profiles) { + // Make the assumption on Windows 10 that Edge exists and is probably default. + if (importer::EdgeImporterCanImport()) + DetectEdgeProfiles(profiles); + DetectIEProfiles(profiles); +} + +#endif // defined(OS_WIN) + +#if defined(OS_MACOSX) +void DetectSafariProfiles(std::vector* profiles) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + uint16_t items = importer::NONE; + if (!SafariImporterCanImport(base::mac::GetUserLibraryPath(), &items)) + return; + + importer::SourceProfile safari; + // safari.importer_name = l10n_util::GetStringUTF16(IDS_IMPORT_FROM_SAFARI); + safari.importer_name = base::UTF8ToUTF16("Safari"); + safari.importer_type = importer::TYPE_SAFARI; + safari.services_supported = items; + profiles->push_back(safari); +} +#endif // defined(OS_MACOSX) + +// |locale|: The application locale used for lookups in Firefox's +// locale-specific search engines feature (see firefox_importer.cc for +// details). +void DetectFirefoxProfiles(const std::string locale, + std::vector* profiles) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + base::FilePath profile_path = GetFirefoxProfilePath(); + if (profile_path.empty()) + return; + + // Detects which version of Firefox is installed. + importer::ImporterType firefox_type; + base::FilePath app_path; + int version = 0; +#if defined(OS_WIN) + version = GetCurrentFirefoxMajorVersionFromRegistry(); +#endif + if (version < 2) + GetFirefoxVersionAndPathFromProfile(profile_path, &version, &app_path); + + if (version >= 3) { + firefox_type = importer::TYPE_FIREFOX; + } else { + // Ignores old versions of firefox. + return; + } + + importer::SourceProfile firefox; + firefox.importer_name = GetFirefoxImporterName(app_path); + firefox.importer_type = firefox_type; + firefox.source_path = profile_path; +#if defined(OS_WIN) + firefox.app_path = GetFirefoxInstallPathFromRegistry(); +#endif + if (firefox.app_path.empty()) + firefox.app_path = app_path; + firefox.services_supported = importer::HISTORY | importer::FAVORITES | + importer::PASSWORDS | importer::SEARCH_ENGINES | + importer::AUTOFILL_FORM_DATA | importer::COOKIES; + firefox.locale = locale; + profiles->push_back(firefox); +} + +void DetectChromeProfiles(std::vector* profiles) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + base::FilePath user_data_folder = GetChromeUserDataFolder(); + base::ListValue* chrome_profiles = GetChromeSourceProfiles(user_data_folder); + for (const base::Value* value : *chrome_profiles) { + const base::DictionaryValue* dict; + if (!value->GetAsDictionary(&dict)) + continue; + uint16_t items = importer::NONE; + std::string profile; + std::string name; + dict->GetString("id", &profile); + dict->GetString("name", &name); + if (!ChromeImporterCanImport(user_data_folder.Append( + base::FilePath::StringType(profile.begin(), profile.end())), &items)) + continue; + importer::SourceProfile chrome; + std::string importer_name("Chrome "); + importer_name.append(name); + chrome.importer_name = base::UTF8ToUTF16(importer_name); + chrome.importer_type = importer::TYPE_CHROME; + chrome.services_supported = items; + chrome.source_path = + user_data_folder.Append( + base::FilePath::StringType(profile.begin(), profile.end())); + profiles->push_back(chrome); + } + delete chrome_profiles; +} + +std::vector DetectSourceProfilesWorker( + const std::string& locale, + bool include_interactive_profiles) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + + std::vector profiles; + + // The first run import will automatically take settings from the first + // profile detected, which should be the user's current default. +#if defined(OS_WIN) + if (shell_integration::IsFirefoxDefaultBrowser()) { + DetectFirefoxProfiles(locale, &profiles); + DetectBuiltinWindowsProfiles(&profiles); + } else { + DetectBuiltinWindowsProfiles(&profiles); + DetectFirefoxProfiles(locale, &profiles); + DetectChromeProfiles(&profiles); + } +#elif defined(OS_MACOSX) + if (shell_integration::IsFirefoxDefaultBrowser()) { + DetectFirefoxProfiles(locale, &profiles); + DetectSafariProfiles(&profiles); + } else { + DetectSafariProfiles(&profiles); + DetectFirefoxProfiles(locale, &profiles); + DetectChromeProfiles(&profiles); + } +#else + DetectFirefoxProfiles(locale, &profiles); + DetectChromeProfiles(&profiles); +#endif + if (include_interactive_profiles) { + importer::SourceProfile bookmarks_profile; + bookmarks_profile.importer_name = + // l10n_util::GetStringUTF16(IDS_IMPORT_FROM_BOOKMARKS_HTML_FILE); + base::UTF8ToUTF16("Bookmarks HTML File"); + bookmarks_profile.importer_type = importer::TYPE_BOOKMARKS_FILE; + bookmarks_profile.services_supported = importer::FAVORITES; + profiles.push_back(bookmarks_profile); + } + + return profiles; +} + +} // namespace + +ImporterList::ImporterList() + : weak_ptr_factory_(this) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +ImporterList::~ImporterList() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +void ImporterList::DetectSourceProfiles( + const std::string& locale, + bool include_interactive_profiles, + const base::Closure& profiles_loaded_callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&DetectSourceProfilesWorker, + locale, + include_interactive_profiles), + base::Bind(&ImporterList::SourceProfilesLoaded, + weak_ptr_factory_.GetWeakPtr(), + profiles_loaded_callback)); +} + +const importer::SourceProfile& ImporterList::GetSourceProfileAt( + size_t index) const { + DCHECK_LT(index, count()); + return source_profiles_[index]; +} + +void ImporterList::SourceProfilesLoaded( + const base::Closure& profiles_loaded_callback, + const std::vector& profiles) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + source_profiles_.assign(profiles.begin(), profiles.end()); + profiles_loaded_callback.Run(); +} diff --git a/chromium_src/chrome/browser/importer/importer_list.h b/chromium_src/chrome/browser/importer/importer_list.h new file mode 100644 index 0000000000..b7a245232b --- /dev/null +++ b/chromium_src/chrome/browser/importer/importer_list.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef CHROME_BROWSER_IMPORTER_IMPORTER_LIST_H_ +#define CHROME_BROWSER_IMPORTER_IMPORTER_LIST_H_ + +#include + +#include +#include + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" +#include "chrome/common/importer/importer_data_types.h" + +// ImporterList detects installed browsers and profiles via +// DetectSourceProfilesWorker(). ImporterList lives on the UI thread. +class ImporterList { + public: + ImporterList(); + ~ImporterList(); + + // Detects the installed browsers and their associated profiles, then stores + // their information in a list to be accessed via count() and + // GetSourceProfileAt(). The detection runs asynchronously. + // + // |locale|: As in DetectSourceProfilesWorker(). + // |include_interactive_profiles|: True to include source profiles that + // require user interaction to read. + // |profiles_loaded_callback|: Assuming this ImporterList instance is still + // alive, run the callback when the source profile detection finishes. + void DetectSourceProfiles(const std::string& locale, + bool include_interactive_profiles, + const base::Closure& profiles_loaded_callback); + + // Returns the number of different source profiles you can import from. + size_t count() const { return source_profiles_.size(); } + + // Returns the SourceProfile at |index|. The profiles are ordered such that + // the profile at index 0 is the likely default browser. The SourceProfile + // should be passed to ImporterHost::StartImportSettings(). + const importer::SourceProfile& GetSourceProfileAt(size_t index) const; + + private: + // Called when the source profiles are loaded. Copies the loaded profiles + // in |profiles| and calls |profiles_loaded_callback|. + void SourceProfilesLoaded( + const base::Closure& profiles_loaded_callback, + const std::vector& profiles); + + // The list of profiles with the default one first. + std::vector source_profiles_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ImporterList); +}; + +#endif // CHROME_BROWSER_IMPORTER_IMPORTER_LIST_H_ diff --git a/chromium_src/chrome/browser/importer/importer_lock_dialog.h b/chromium_src/chrome/browser/importer/importer_lock_dialog.h new file mode 100644 index 0000000000..53b4427313 --- /dev/null +++ b/chromium_src/chrome/browser/importer/importer_lock_dialog.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef CHROME_BROWSER_IMPORTER_IMPORTER_LOCK_DIALOG_H_ +#define CHROME_BROWSER_IMPORTER_IMPORTER_LOCK_DIALOG_H_ + +#include "base/callback_forward.h" +#include "ui/gfx/native_widget_types.h" + +namespace importer { + +// This function is called by an ImporterHost, and presents the Firefox profile +// warning dialog. After closing the dialog, the ImportHost receives a callback +// with the message either to skip the import, or to continue the process. +void ShowImportLockDialog(gfx::NativeWindow parent, + const base::Callback& callback); + +} // namespace importer + +#endif // CHROME_BROWSER_IMPORTER_IMPORTER_LOCK_DIALOG_H_ diff --git a/chromium_src/chrome/browser/importer/importer_progress_observer.h b/chromium_src/chrome/browser/importer/importer_progress_observer.h new file mode 100644 index 0000000000..b033af88e5 --- /dev/null +++ b/chromium_src/chrome/browser/importer/importer_progress_observer.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef CHROME_BROWSER_IMPORTER_IMPORTER_PROGRESS_OBSERVER_H_ +#define CHROME_BROWSER_IMPORTER_IMPORTER_PROGRESS_OBSERVER_H_ + +#include "chrome/common/importer/importer_data_types.h" + +namespace importer { + +// Objects implement this interface when they wish to be notified of events +// during the import process. +class ImporterProgressObserver { + public: + // Invoked when the import begins. + virtual void ImportStarted() = 0; + + // Invoked when data for the specified item is about to be collected. + virtual void ImportItemStarted(ImportItem item) = 0; + + // Invoked when data for the specified item has been collected from the + // source profile and is now ready for further processing. + virtual void ImportItemEnded(ImportItem item) = 0; + + // Invoked when the source profile has been imported. + virtual void ImportEnded() = 0; + + protected: + virtual ~ImporterProgressObserver() {} +}; + +} // namespace importer + +#endif // CHROME_BROWSER_IMPORTER_IMPORTER_PROGRESS_OBSERVER_H_ diff --git a/chromium_src/chrome/browser/importer/importer_uma.cc b/chromium_src/chrome/browser/importer/importer_uma.cc new file mode 100644 index 0000000000..cab2cb6f34 --- /dev/null +++ b/chromium_src/chrome/browser/importer/importer_uma.cc @@ -0,0 +1,87 @@ +// Copyright 2013 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. + +/** + * This is not a straight copy from chromium src, in particular + * add IMPORTER_METRICS_CHROME for chrome. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "build/build_config.h" +#include "chrome/browser/importer/importer_uma.h" + +namespace importer { + +namespace { + +// The enum used to register importer use. +enum ImporterTypeMetrics { + IMPORTER_METRICS_UNKNOWN = 0, +#if defined(OS_WIN) + IMPORTER_METRICS_IE = 1, +#endif + IMPORTER_METRICS_FIREFOX2 = 2, // obsolete + IMPORTER_METRICS_FIREFOX3 = 3, +#if defined(OS_MACOSX) + IMPORTER_METRICS_SAFARI = 4, +#endif + IMPORTER_METRICS_GOOGLE_TOOLBAR5 = 5, // obsolete + IMPORTER_METRICS_BOOKMARKS_FILE = 6, +#if defined(OS_WIN) + IMPORTER_METRICS_EDGE = 7, +#endif + IMPORTER_METRICS_CHROME = 8, + + // Insert new values here. Never remove any existing values, as this enum is + // used to bucket a UMA histogram, and removing values breaks that. + IMPORTER_METRICS_SIZE +}; + +} // namespace + +void LogImporterUseToMetrics(const std::string& metric_postfix, + ImporterType type) { + ImporterTypeMetrics metrics_type = IMPORTER_METRICS_UNKNOWN; + switch (type) { + case TYPE_UNKNOWN: + metrics_type = IMPORTER_METRICS_UNKNOWN; + break; +#if defined(OS_WIN) + case TYPE_IE: + metrics_type = IMPORTER_METRICS_IE; + break; + case TYPE_EDGE: + metrics_type = IMPORTER_METRICS_EDGE; + break; +#endif + case TYPE_FIREFOX: + metrics_type = IMPORTER_METRICS_FIREFOX3; + break; +#if defined(OS_MACOSX) + case TYPE_SAFARI: + metrics_type = IMPORTER_METRICS_SAFARI; + break; +#endif + case TYPE_BOOKMARKS_FILE: + metrics_type = IMPORTER_METRICS_BOOKMARKS_FILE; + break; + case TYPE_CHROME: + metrics_type = IMPORTER_METRICS_CHROME; + break; + } + + // Note: This leaks memory, which is the expected behavior as the factory + // creates and owns the histogram. + base::HistogramBase* histogram = + base::LinearHistogram::FactoryGet( + "Import.ImporterType." + metric_postfix, + 1, + IMPORTER_METRICS_SIZE, + IMPORTER_METRICS_SIZE + 1, + base::HistogramBase::kUmaTargetedHistogramFlag); + histogram->Add(metrics_type); +} + +} // namespace importer diff --git a/chromium_src/chrome/browser/importer/importer_uma.h b/chromium_src/chrome/browser/importer/importer_uma.h new file mode 100644 index 0000000000..c71e6c9b82 --- /dev/null +++ b/chromium_src/chrome/browser/importer/importer_uma.h @@ -0,0 +1,23 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_IMPORTER_IMPORTER_UMA_H_ +#define CHROME_BROWSER_IMPORTER_IMPORTER_UMA_H_ + +#include +#include "base/metrics/histogram.h" +#include "chrome/common/importer/importer_type.h" + +namespace importer { + +// Logs to UMA that an Importer of the specified |type| was used. Uses +// |metric_postfix| to split by entry point. Note: Values passed via +// |metric_postfix| require a matching "Import.ImporterType.|metric_postfix|" +// entry in tools/metrics/histograms/histograms.xml. +void LogImporterUseToMetrics(const std::string& metric_prefix, + ImporterType type); + +} // namespace importer + +#endif // CHROME_BROWSER_IMPORTER_IMPORTER_UMA_H_ diff --git a/chromium_src/chrome/browser/importer/in_process_importer_bridge.cc b/chromium_src/chrome/browser/importer/in_process_importer_bridge.cc new file mode 100644 index 0000000000..370af344ed --- /dev/null +++ b/chromium_src/chrome/browser/importer/in_process_importer_bridge.cc @@ -0,0 +1,326 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * some functionality is removed. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/browser/importer/in_process_importer_bridge.h" + +#include + +#include "atom/common/importer/imported_cookie_entry.h" +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "chrome/browser/importer/external_process_importer_host.h" +// #include "chrome/browser/search_engines/ui_thread_search_terms_data.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_autofill_form_data_entry.h" +#include "components/autofill/core/browser/webdata/autofill_entry.h" +#include "components/autofill/core/common/password_form.h" +#include "components/favicon_base/favicon_usage_data.h" +#include "components/search_engines/template_url.h" +#include "components/search_engines/template_url_parser.h" +#include "components/search_engines/template_url_prepopulate_data.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/l10n/l10n_util.h" + +#if defined(OS_WIN) +#include "components/os_crypt/ie7_password_win.h" +#endif + +#include + +namespace { + +history::URLRows ConvertImporterURLRowsToHistoryURLRows( + const std::vector& rows) { + history::URLRows converted; + converted.reserve(rows.size()); + for (std::vector::const_iterator it = rows.begin(); + it != rows.end(); ++it) { + history::URLRow row(it->url); + row.set_title(it->title); + row.set_visit_count(it->visit_count); + row.set_typed_count(it->typed_count); + row.set_last_visit(it->last_visit); + row.set_hidden(it->hidden); + converted.push_back(row); + } + return converted; +} + +history::VisitSource ConvertImporterVisitSourceToHistoryVisitSource( + importer::VisitSource visit_source) { + switch (visit_source) { + case importer::VISIT_SOURCE_BROWSED: + return history::SOURCE_BROWSED; + case importer::VISIT_SOURCE_FIREFOX_IMPORTED: + return history::SOURCE_FIREFOX_IMPORTED; + case importer::VISIT_SOURCE_IE_IMPORTED: + return history::SOURCE_IE_IMPORTED; + case importer::VISIT_SOURCE_SAFARI_IMPORTED: + return history::SOURCE_SAFARI_IMPORTED; + case importer::VISIT_SOURCE_CHROME_IMPORTED: + // TODO(Anthony): pull components/history/core/browser/history_types.h + return history::SOURCE_SYNCED; + } + NOTREACHED(); + return history::SOURCE_SYNCED; +} + +} // namespace + +using content::BrowserThread; + +namespace { + +// FirefoxURLParameterFilter is used to remove parameter mentioning Firefox from +// the search URL when importing search engines. +class FirefoxURLParameterFilter : public TemplateURLParser::ParameterFilter { + public: + FirefoxURLParameterFilter() {} + ~FirefoxURLParameterFilter() override {} + + // TemplateURLParser::ParameterFilter method. + bool KeepParameter(const std::string& key, + const std::string& value) override { + std::string low_value = base::ToLowerASCII(value); + if (low_value.find("mozilla") != std::string::npos || + low_value.find("firefox") != std::string::npos || + low_value.find("moz:") != std::string::npos) { + return false; + } + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(FirefoxURLParameterFilter); +}; + +// Attempts to create a TemplateURL from the provided data. |title| is optional. +// If TemplateURL creation fails, returns NULL. +// This function transfers ownership of the created TemplateURL to the caller. +TemplateURL* CreateTemplateURL(const base::string16& url, + const base::string16& keyword, + const base::string16& title) { + if (url.empty() || keyword.empty()) + return NULL; + TemplateURLData data; + data.SetKeyword(keyword); + // We set short name by using the title if it exists. + // Otherwise, we use the shortcut. + data.SetShortName(title.empty() ? keyword : title); + data.SetURL(TemplateURLRef::DisplayURLToURLRef(url)); + return new TemplateURL(data); +} + +// Parses the OpenSearch XML files in |xml_files| and populates |search_engines| +// with the resulting TemplateURLs. +void ParseSearchEnginesFromFirefoxXMLData( + const std::vector& xml_data, + std::vector* search_engines) { + DCHECK(search_engines); +/* + typedef std::map SearchEnginesMap; + SearchEnginesMap search_engine_for_url; + FirefoxURLParameterFilter param_filter; + // The first XML file represents the default search engine in Firefox 3, so we + // need to keep it on top of the list. + SearchEnginesMap::const_iterator default_turl = search_engine_for_url.end(); + for (std::vector::const_iterator xml_iter = + xml_data.begin(); xml_iter != xml_data.end(); ++xml_iter) { + TemplateURL* template_url = TemplateURLParser::Parse( + UIThreadSearchTermsData(NULL), true, + xml_iter->data(), xml_iter->length(), ¶m_filter); + if (template_url) { + SearchEnginesMap::iterator iter = + search_engine_for_url.find(template_url->url()); + if (iter == search_engine_for_url.end()) { + iter = search_engine_for_url.insert( + std::make_pair(template_url->url(), template_url)).first; + } else { + // We have already found a search engine with the same URL. We give + // priority to the latest one found, as GetSearchEnginesXMLFiles() + // returns a vector with first Firefox default search engines and then + // the user's ones. We want to give priority to the user ones. + delete iter->second; + iter->second = template_url; + } + if (default_turl == search_engine_for_url.end()) + default_turl = iter; + } + } + + // Put the results in the |search_engines| vector. + for (SearchEnginesMap::iterator t_iter = search_engine_for_url.begin(); + t_iter != search_engine_for_url.end(); ++t_iter) { + if (t_iter == default_turl) + search_engines->insert(search_engines->begin(), default_turl->second); + else + search_engines->push_back(t_iter->second); + } + */ +} + +} // namespace + +InProcessImporterBridge::InProcessImporterBridge( + ProfileWriter* writer, + base::WeakPtr host) : writer_(writer), + host_(host) { +} + +void InProcessImporterBridge::AddBookmarks( + const std::vector& bookmarks, + const base::string16& first_folder_name) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddBookmarks, writer_, bookmarks, + first_folder_name)); +} + +void InProcessImporterBridge::AddHomePage(const GURL& home_page) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddHomepage, writer_, home_page)); +} + +#if defined(OS_WIN) +void InProcessImporterBridge::AddIE7PasswordInfo( + const importer::ImporterIE7PasswordInfo& password_info) { + IE7PasswordInfo ie7_password_info; + ie7_password_info.url_hash = password_info.url_hash; + ie7_password_info.encrypted_data = password_info.encrypted_data; + ie7_password_info.date_created = password_info.date_created; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddIE7PasswordInfo, writer_, + ie7_password_info)); +} +#endif // OS_WIN + +void InProcessImporterBridge::SetFavicons( + const favicon_base::FaviconUsageDataList& favicons) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddFavicons, writer_, favicons)); +} + +void InProcessImporterBridge::SetHistoryItems( + const std::vector& rows, + importer::VisitSource visit_source) { + history::URLRows converted_rows = + ConvertImporterURLRowsToHistoryURLRows(rows); + history::VisitSource converted_visit_source = + ConvertImporterVisitSourceToHistoryVisitSource(visit_source); + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(&ProfileWriter::AddHistoryPage, + writer_, + converted_rows, + converted_visit_source)); +} + +void InProcessImporterBridge::SetKeywords( + const std::vector& search_engines, + bool unique_on_host_and_path) { + ScopedVector owned_template_urls; + for (const auto& search_engine : search_engines) { + TemplateURL* owned_template_url = + CreateTemplateURL(search_engine.url, + search_engine.keyword, + search_engine.display_name); + if (owned_template_url) + owned_template_urls.push_back(owned_template_url); + } + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddKeywords, writer_, + base::Passed(&owned_template_urls), unique_on_host_and_path)); +} + +void InProcessImporterBridge::SetFirefoxSearchEnginesXMLData( + const std::vector& search_engine_data) { + std::vector search_engines; + ParseSearchEnginesFromFirefoxXMLData(search_engine_data, &search_engines); + + ScopedVector owned_template_urls; + std::copy(search_engines.begin(), search_engines.end(), + std::back_inserter(owned_template_urls)); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddKeywords, writer_, + base::Passed(&owned_template_urls), true)); +} + +void InProcessImporterBridge::SetPasswordForm( + const autofill::PasswordForm& form) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddPasswordForm, writer_, form)); +} + +void InProcessImporterBridge::SetAutofillFormData( + const std::vector& entries) { + std::vector autofill_entries; + for (size_t i = 0; i < entries.size(); ++i) { + autofill_entries.push_back(autofill::AutofillEntry( + autofill::AutofillKey(entries[i].name, entries[i].value), + entries[i].first_used, + entries[i].last_used)); + } + + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(&ProfileWriter::AddAutofillFormDataEntries, + writer_, + autofill_entries)); +} + +void InProcessImporterBridge::SetCookies( + const std::vector& cookies) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ProfileWriter::AddCookies, writer_, cookies)); +} + +void InProcessImporterBridge::NotifyStarted() { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ExternalProcessImporterHost::NotifyImportStarted, host_)); +} + +void InProcessImporterBridge::NotifyItemStarted(importer::ImportItem item) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ExternalProcessImporterHost::NotifyImportItemStarted, + host_, item)); +} + +void InProcessImporterBridge::NotifyItemEnded(importer::ImportItem item) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ExternalProcessImporterHost::NotifyImportItemEnded, + host_, item)); +} + +void InProcessImporterBridge::NotifyEnded() { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ExternalProcessImporterHost::NotifyImportEnded, host_)); +} + +base::string16 InProcessImporterBridge::GetLocalizedString(int message_id) { + return l10n_util::GetStringUTF16(message_id); +} + +InProcessImporterBridge::~InProcessImporterBridge() {} diff --git a/chromium_src/chrome/browser/importer/in_process_importer_bridge.h b/chromium_src/chrome/browser/importer/in_process_importer_bridge.h new file mode 100644 index 0000000000..77d0fa84a8 --- /dev/null +++ b/chromium_src/chrome/browser/importer/in_process_importer_bridge.h @@ -0,0 +1,92 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add SetCookies. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_BROWSER_IMPORTER_IN_PROCESS_IMPORTER_BRIDGE_H_ +#define CHROME_BROWSER_IMPORTER_IN_PROCESS_IMPORTER_BRIDGE_H_ + +#include +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "build/build_config.h" +#include "chrome/browser/importer/profile_writer.h" +#include "chrome/common/importer/importer_bridge.h" + +class GURL; +struct ImportedBookmarkEntry; +struct FaviconUsageData; +class ExternalProcessImporterHost; + +struct ImportedCookieEntry; + +namespace importer { +#if defined(OS_WIN) +struct ImporterIE7PasswordInfo; +#endif +struct ImporterURlRow; +struct SearchEngineInfo; +} + +class InProcessImporterBridge : public ImporterBridge { + public: + InProcessImporterBridge(ProfileWriter* writer, + base::WeakPtr host); + + // Begin ImporterBridge implementation: + void AddBookmarks(const std::vector& bookmarks, + const base::string16& first_folder_name) override; + + void AddHomePage(const GURL& home_page) override; + +#if defined(OS_WIN) + void AddIE7PasswordInfo( + const importer::ImporterIE7PasswordInfo& password_info) override; +#endif + + void SetFavicons(const favicon_base::FaviconUsageDataList& favicons) override; + + void SetHistoryItems(const std::vector& rows, + importer::VisitSource visit_source) override; + + void SetKeywords( + const std::vector& search_engines, + bool unique_on_host_and_path) override; + + void SetFirefoxSearchEnginesXMLData( + const std::vector& search_engine_data) override; + + void SetPasswordForm(const autofill::PasswordForm& form) override; + + void SetAutofillFormData( + const std::vector& entries) override; + + void SetCookies(const std::vector& cookies) override; + + void NotifyStarted() override; + void NotifyItemStarted(importer::ImportItem item) override; + void NotifyItemEnded(importer::ImportItem item) override; + void NotifyEnded() override; + + base::string16 GetLocalizedString(int message_id) override; + // End ImporterBridge implementation. + + private: + ~InProcessImporterBridge() override; + + ProfileWriter* const writer_; // weak + const base::WeakPtr host_; + + DISALLOW_COPY_AND_ASSIGN(InProcessImporterBridge); +}; + +#endif // CHROME_BROWSER_IMPORTER_IN_PROCESS_IMPORTER_BRIDGE_H_ diff --git a/chromium_src/chrome/browser/importer/profile_writer.h b/chromium_src/chrome/browser/importer/profile_writer.h new file mode 100644 index 0000000000..d0072f45ae --- /dev/null +++ b/chromium_src/chrome/browser/importer/profile_writer.h @@ -0,0 +1,132 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * some functionality is removed and we add electron + * importer delegate to emit event. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_BROWSER_IMPORTER_PROFILE_WRITER_H_ +#define CHROME_BROWSER_IMPORTER_PROFILE_WRITER_H_ + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_vector.h" +#include "base/strings/string16.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "components/favicon_base/favicon_usage_data.h" +#include "components/history/core/browser/history_types.h" +#include "url/gurl.h" + +struct ImportedBookmarkEntry; +class Profile; +class TemplateURL; + +namespace atom { +namespace api { +class Importer; +} +} + +namespace autofill { +struct PasswordForm; +class AutofillEntry; +} + +struct ImportedCookieEntry; + +#if defined(OS_WIN) +struct IE7PasswordInfo; +#endif + +// ProfileWriter encapsulates profile for writing entries into it. +// This object must be invoked on UI thread. +class ProfileWriter : public base::RefCountedThreadSafe { + public: + explicit ProfileWriter(Profile* profile); + + // These functions return true if the corresponding model has been loaded. + // If the models haven't been loaded, the importer waits to run until they've + // completed. + virtual bool BookmarkModelIsLoaded() const; + virtual bool TemplateURLServiceIsLoaded() const; + + // Helper methods for adding data to local stores. + virtual void AddPasswordForm(const autofill::PasswordForm& form); + +#if defined(OS_WIN) + virtual void AddIE7PasswordInfo(const IE7PasswordInfo& info); +#endif + + virtual void AddHistoryPage(const history::URLRows& page, + history::VisitSource visit_source); + + virtual void AddHomepage(const GURL& homepage); + + // Adds the |bookmarks| to the bookmark model. + // + // (a) If the bookmarks bar is empty: + // (i) If |bookmarks| includes at least one bookmark that was originally + // located in a toolbar, all such bookmarks are imported directly to + // the toolbar; any other bookmarks are imported to a subfolder in + // the toolbar. + // (i) If |bookmarks| includes no bookmarks that were originally located + // in a toolbar, all bookmarks are imported directly to the toolbar. + // (b) If the bookmarks bar is not empty, all bookmarks are imported to a + // subfolder in the toolbar. + // + // In either case, if a subfolder is created, the name will be the value of + // |top_level_folder_name|, unless a folder with this name already exists. + // If a folder with this name already exists, then the name is uniquified. + // For example, if |first_folder_name| is 'Imported from IE' and a folder with + // the name 'Imported from IE' already exists in the bookmarks toolbar, then + // we will instead create a subfolder named 'Imported from IE (1)'. + virtual void AddBookmarks( + const std::vector& bookmarks, + const base::string16& top_level_folder_name); + + virtual void AddFavicons(const favicon_base::FaviconUsageDataList& favicons); + + // Adds the TemplateURLs in |template_urls| to the local store. The local + // store becomes the owner of the TemplateURLs. Some TemplateURLs in + // |template_urls| may conflict (same keyword or same host name in the URL) + // with existing TemplateURLs in the local store, in which case the existing + // ones take precedence and the duplicates in |template_urls| are deleted. + // If |unique_on_host_and_path| is true, a TemplateURL is only added if there + // is not an existing TemplateURL that has a replaceable search url with the + // same host+path combination. + virtual void AddKeywords(ScopedVector template_urls, + bool unique_on_host_and_path); + + // Adds the imported autofill entries to the autofill database. + virtual void AddAutofillFormDataEntries( + const std::vector& autofill_entries); + + virtual void AddCookies(const std::vector& cookies); + + void Initialize(atom::api::Importer* importer); + + void ShowWarningDialog(); + + protected: + friend class base::RefCountedThreadSafe; + + virtual ~ProfileWriter(); + + private: + Profile* const profile_; + + // Importer instance of Brave + atom::api::Importer* importer_; + + DISALLOW_COPY_AND_ASSIGN(ProfileWriter); +}; + +#endif // CHROME_BROWSER_IMPORTER_PROFILE_WRITER_H_ diff --git a/chromium_src/chrome/common/common_param_traits_macros.h b/chromium_src/chrome/common/common_param_traits_macros.h new file mode 100644 index 0000000000..d5b75a4b22 --- /dev/null +++ b/chromium_src/chrome/common/common_param_traits_macros.h @@ -0,0 +1,16 @@ +// 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. + +// Singly or multiply-included shared traits file depending upon circumstances. +// This allows the use of IPC serialization macros in more than one IPC message +// file. +#ifndef CHROME_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ +#define CHROME_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ + +#include "components/content_settings/core/common/content_settings.h" +#include "ipc/ipc_message_macros.h" + +IPC_ENUM_TRAITS_MAX_VALUE(ContentSetting, CONTENT_SETTING_NUM_SETTINGS - 1) + +#endif // CHROME_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ diff --git a/chromium_src/chrome/common/importer/edge_importer_utils_win.cc b/chromium_src/chrome/common/importer/edge_importer_utils_win.cc new file mode 100644 index 0000000000..c2abd64b2d --- /dev/null +++ b/chromium_src/chrome/common/importer/edge_importer_utils_win.cc @@ -0,0 +1,87 @@ +// Copyright 2015 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/common/importer/edge_importer_utils_win.h" + +#include + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/win/registry.h" +#include "base/win/windows_version.h" +#include "chrome/common/importer/importer_test_registry_overrider_win.h" + +namespace { + +const base::char16 kEdgeSettingsMainKey[] = L"MicrosoftEdge\\Main"; + +const base::char16 kEdgePackageName[] = + L"microsoft.microsoftedge_8wekyb3d8bbwe"; + +// We assume at the moment that the package name never changes for Edge. +base::string16 GetEdgePackageName() { + return kEdgePackageName; +} + +base::string16 GetEdgeRegistryKey(const base::string16& key_name) { + base::string16 registry_key = + L"Software\\Classes\\Local Settings\\" + L"Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\" + L"Storage\\"; + registry_key += GetEdgePackageName(); + registry_key += L"\\"; + registry_key += key_name; + return registry_key; +} + +base::string16 GetPotentiallyOverridenEdgeKey( + const base::string16& desired_key_path) { + base::string16 test_registry_override( + ImporterTestRegistryOverrider::GetTestRegistryOverride()); + return test_registry_override.empty() ? GetEdgeRegistryKey(desired_key_path) + : test_registry_override; +} + +} // namespace + +namespace importer { + +base::string16 GetEdgeSettingsKey() { + return GetPotentiallyOverridenEdgeKey(kEdgeSettingsMainKey); +} + +base::FilePath GetEdgeDataFilePath() { + wchar_t buffer[MAX_PATH]; + if (::SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, + buffer) != S_OK) + return base::FilePath(); + + base::FilePath base_path(buffer); + base::string16 rel_path = L"Packages\\"; + rel_path += GetEdgePackageName(); + rel_path += L"\\AC\\MicrosoftEdge\\User\\Default"; + return base_path.Append(rel_path); +} + +bool IsEdgeFavoritesLegacyMode() { + base::win::RegKey key(HKEY_CURRENT_USER, GetEdgeSettingsKey().c_str(), + KEY_READ); + DWORD ese_enabled = 0; + // Check whether Edge is using the new Extensible Store Engine (ESE) format + // for its favorites. + if (key.ReadValueDW(L"FavoritesESEEnabled", &ese_enabled) == ERROR_SUCCESS) + return !ese_enabled; + return true; +} + +bool EdgeImporterCanImport() { + base::File::Info file_info; + if (base::win::GetVersion() < base::win::VERSION_WIN10) + return false; + return base::GetFileInfo(GetEdgeDataFilePath(), &file_info) && + file_info.is_directory; +} + +} // namespace importer diff --git a/chromium_src/chrome/common/importer/edge_importer_utils_win.h b/chromium_src/chrome/common/importer/edge_importer_utils_win.h new file mode 100644 index 0000000000..4d54be7cdc --- /dev/null +++ b/chromium_src/chrome/common/importer/edge_importer_utils_win.h @@ -0,0 +1,28 @@ +// Copyright 2015 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. + +#ifndef CHROME_COMMON_IMPORTER_EDGE_IMPORTER_UTILS_WIN_H_ +#define CHROME_COMMON_IMPORTER_EDGE_IMPORTER_UTILS_WIN_H_ + +#include "base/files/file_path.h" +#include "base/strings/string16.h" + +namespace importer { + +// Returns the key to be used in HKCU to look for Edge's settings. +// Overridable by tests via ImporterTestRegistryOverrider. +base::string16 GetEdgeSettingsKey(); + +// Returns the data path for the Edge browser. Returns an empty path on error. +base::FilePath GetEdgeDataFilePath(); + +// Returns true if Edge favorites is currently in legacy (pre-Edge 13) mode. +bool IsEdgeFavoritesLegacyMode(); + +// Returns true if the Edge browser is installed and available for import. +bool EdgeImporterCanImport(); + +} // namespace importer + +#endif // CHROME_COMMON_IMPORTER_EDGE_IMPORTER_UTILS_WIN_H_ diff --git a/chromium_src/chrome/common/importer/firefox_importer_utils.cc b/chromium_src/chrome/common/importer/firefox_importer_utils.cc new file mode 100644 index 0000000000..48e5d380fc --- /dev/null +++ b/chromium_src/chrome/common/importer/firefox_importer_utils.cc @@ -0,0 +1,338 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * some functionality is removed. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/common/importer/firefox_importer_utils.h" + +#include + +#include +#include +#include + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/common/ini_parser.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/gurl.h" + +namespace { + +// Retrieves the file system path of the profile name. +base::FilePath GetProfilePath(const base::DictionaryValue& root, + const std::string& profile_name) { + base::string16 path16; + std::string is_relative; + if (!root.GetStringASCII(profile_name + ".IsRelative", &is_relative) || + !root.GetString(profile_name + ".Path", &path16)) + return base::FilePath(); + +#if defined(OS_WIN) + base::ReplaceSubstringsAfterOffset( + &path16, 0, base::ASCIIToUTF16("/"), base::ASCIIToUTF16("\\")); +#endif + base::FilePath path = base::FilePath::FromUTF16Unsafe(path16); + + // IsRelative=1 means the folder path would be relative to the + // path of profiles.ini. IsRelative=0 refers to a custom profile + // location. + if (is_relative == "1") + path = GetProfilesINI().DirName().Append(path); + + return path; +} + +// Checks if the named profile is the default profile. +bool IsDefaultProfile(const base::DictionaryValue& root, + const std::string& profile_name) { + std::string is_default; + root.GetStringASCII(profile_name + ".Default", &is_default); + return is_default == "1"; +} + +} // namespace + +base::FilePath GetFirefoxProfilePath() { + base::FilePath ini_file = GetProfilesINI(); + std::string content; + base::ReadFileToString(ini_file, &content); + DictionaryValueINIParser ini_parser; + ini_parser.Parse(content); + return GetFirefoxProfilePathFromDictionary(ini_parser.root()); +} + +base::FilePath GetFirefoxProfilePathFromDictionary( + const base::DictionaryValue& root) { + std::vector profiles; + for (int i = 0; ; ++i) { + std::string current_profile = base::StringPrintf("Profile%d", i); + if (root.HasKey(current_profile)) { + profiles.push_back(current_profile); + } else { + // Profiles are continuously numbered. So we exit when we can't + // find the i-th one. + break; + } + } + + if (profiles.empty()) + return base::FilePath(); + + // When multiple profiles exist, the path to the default profile is returned, + // since the other profiles are used mostly by developers for testing. + for (std::vector::const_iterator it = profiles.begin(); + it != profiles.end(); ++it) + if (IsDefaultProfile(root, *it)) + return GetProfilePath(root, *it); + + // If no default profile is found, the path to Profile0 will be returned. + return GetProfilePath(root, profiles.front()); +} + +#if defined(OS_MACOSX) +// Find the "*.app" component of the path and build up from there. +// The resulting path will be .../Firefox.app/Contents/MacOS. +// We do this because we don't trust LastAppDir to always be +// this particular path, without any subdirs, and we want to make +// our assumption about Firefox's root being in that path explicit. +bool ComposeMacAppPath(const std::string& path_from_file, + base::FilePath* output) { + base::FilePath path(path_from_file); + typedef std::vector ComponentVector; + ComponentVector path_components; + path.GetComponents(&path_components); + if (path_components.empty()) + return false; + // The first path component is special because it may be absolute. Calling + // Append with an absolute path component will trigger an assert, so we + // must handle it differently and initialize output with it. + *output = base::FilePath(path_components[0]); + // Append next path components untill we find the *.app component. When we do, + // append Contents/MacOS. + for (size_t i = 1; i < path_components.size(); ++i) { + *output = output->Append(path_components[i]); + if (base::EndsWith(path_components[i], ".app", + base::CompareCase::SENSITIVE)) { + *output = output->Append("Contents"); + *output = output->Append("MacOS"); + return true; + } + } + LOG(ERROR) << path_from_file << " doesn't look like a valid Firefox " + << "installation path: missing /*.app/ directory."; + return false; +} +#endif // OS_MACOSX + +bool GetFirefoxVersionAndPathFromProfile(const base::FilePath& profile_path, + int* version, + base::FilePath* app_path) { + bool ret = false; + base::FilePath compatibility_file = + profile_path.AppendASCII("compatibility.ini"); + std::string content; + base::ReadFileToString(compatibility_file, &content); + base::ReplaceSubstringsAfterOffset(&content, 0, "\r\n", "\n"); + + for (const std::string& line : base::SplitString( + content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { + if (line.empty() || line[0] == '#' || line[0] == ';') + continue; + size_t equal = line.find('='); + if (equal != std::string::npos) { + std::string key = line.substr(0, equal); + if (key == "LastVersion") { + base::StringToInt(line.substr(equal + 1), version); + ret = true; + } else if (key == "LastAppDir") { + // TODO(evanm): If the path in question isn't convertible to + // UTF-8, what does Firefox do? If it puts raw bytes in the + // file, we could go straight from bytes -> filepath; + // otherwise, we're out of luck here. +#if defined(OS_MACOSX) + // Extract path from "LastAppDir=/actual/path" + size_t separator_pos = line.find_first_of('='); + const std::string& path_from_ini = line.substr(separator_pos + 1); + if (!ComposeMacAppPath(path_from_ini, app_path)) + return false; +#else // !OS_MACOSX + *app_path = base::FilePath::FromUTF8Unsafe(line.substr(equal + 1)); +#endif // OS_MACOSX + } + } + } + return ret; +} + +bool ReadPrefFile(const base::FilePath& path, std::string* content) { + if (content == NULL) + return false; + + base::ReadFileToString(path, content); + + if (content->empty()) { + LOG(WARNING) << "Firefox preference file " << path.value() << " is empty."; + return false; + } + + return true; +} + +std::string ReadBrowserConfigProp(const base::FilePath& app_path, + const std::string& pref_key) { + std::string content; + if (!ReadPrefFile(app_path.AppendASCII("browserconfig.properties"), &content)) + return std::string(); + + // This file has the syntax: key=value. + size_t prop_index = content.find(pref_key + "="); + if (prop_index == std::string::npos) + return std::string(); + + size_t start = prop_index + pref_key.length(); + size_t stop = std::string::npos; + if (start != std::string::npos) + stop = content.find("\n", start + 1); + + if (start == std::string::npos || + stop == std::string::npos || (start == stop)) { + LOG(WARNING) << "Firefox property " << pref_key << " could not be parsed."; + return std::string(); + } + + return content.substr(start + 1, stop - start - 1); +} + +std::string ReadPrefsJsValue(const base::FilePath& profile_path, + const std::string& pref_key) { + std::string content; + if (!ReadPrefFile(profile_path.AppendASCII("prefs.js"), &content)) + return std::string(); + + return GetPrefsJsValue(content, pref_key); +} + +GURL GetHomepage(const base::FilePath& profile_path) { + std::string home_page_list = + ReadPrefsJsValue(profile_path, "browser.startup.homepage"); + + size_t seperator = home_page_list.find_first_of('|'); + if (seperator == std::string::npos) + return GURL(home_page_list); + + return GURL(home_page_list.substr(0, seperator)); +} + +bool IsDefaultHomepage(const GURL& homepage, const base::FilePath& app_path) { + if (!homepage.is_valid()) + return false; + + std::string default_homepages = + ReadBrowserConfigProp(app_path, "browser.startup.homepage"); + + size_t seperator = default_homepages.find_first_of('|'); + if (seperator == std::string::npos) + return homepage.spec() == GURL(default_homepages).spec(); + + // Crack the string into separate homepage urls. + for (const std::string& url : base::SplitString( + default_homepages, "|", + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { + if (homepage.spec() == GURL(url).spec()) + return true; + } + + return false; +} + +std::string GetPrefsJsValue(const std::string& content, + const std::string& pref_key) { + // This file has the syntax: user_pref("key", value); + std::string search_for = std::string("user_pref(\"") + pref_key + + std::string("\", "); + size_t prop_index = content.find(search_for); + if (prop_index == std::string::npos) + return std::string(); + + size_t start = prop_index + search_for.length(); + size_t stop = std::string::npos; + if (start != std::string::npos) { + // Stop at the last ')' on this line. + stop = content.find("\n", start + 1); + stop = content.rfind(")", stop); + } + + if (start == std::string::npos || stop == std::string::npos || + stop < start) { + LOG(WARNING) << "Firefox property " << pref_key << " could not be parsed."; + return std::string(); + } + + // String values have double quotes we don't need to return to the caller. + if (content[start] == '\"' && content[stop - 1] == '\"') { + ++start; + --stop; + } + + return content.substr(start, stop - start); +} + +// The branding name is obtained from the application.ini file from the Firefox +// application directory. A sample application.ini file is the following: +// [App] +// Vendor=Mozilla +// Name=Iceweasel +// Profile=mozilla/firefox +// Version=3.5.16 +// BuildID=20120421070307 +// Copyright=Copyright (c) 1998 - 2010 mozilla.org +// ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384} +// ......................................... +// In this example the function returns "Iceweasel" (or a localized equivalent). +base::string16 GetFirefoxImporterName(const base::FilePath& app_path) { + const base::FilePath app_ini_file = app_path.AppendASCII("application.ini"); + std::string branding_name; + if (base::PathExists(app_ini_file)) { + std::string content; + base::ReadFileToString(app_ini_file, &content); + + const std::string name_attr("Name="); + bool in_app_section = false; + for (const base::StringPiece& line : base::SplitStringPiece( + content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { + if (line == "[App]") { + in_app_section = true; + } else if (in_app_section) { + if (line.find(name_attr) == 0) { + line.substr(name_attr.size()).CopyToString(&branding_name); + break; + } else if (line.length() > 0 && line[0] == '[') { + // No longer in the [App] section. + break; + } + } + } + } + + branding_name = base::ToLowerASCII(branding_name); + if (branding_name.find("iceweasel") != std::string::npos) + // return l10n_util::GetStringUTF16(IDS_IMPORT_FROM_ICEWEASEL); + return base::UTF8ToUTF16("Iceweasel"); + // return l10n_util::GetStringUTF16(IDS_IMPORT_FROM_FIREFOX); + return base::UTF8ToUTF16("Mozilla Firefox"); +} diff --git a/chromium_src/chrome/common/importer/firefox_importer_utils.h b/chromium_src/chrome/common/importer/firefox_importer_utils.h new file mode 100644 index 0000000000..d5ef46a055 --- /dev/null +++ b/chromium_src/chrome/common/importer/firefox_importer_utils.h @@ -0,0 +1,93 @@ +// 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. + +#ifndef CHROME_COMMON_IMPORTER_FIREFOX_IMPORTER_UTILS_H_ +#define CHROME_COMMON_IMPORTER_FIREFOX_IMPORTER_UTILS_H_ + +#include +#include + +#include "base/strings/string16.h" +#include "build/build_config.h" + +class GURL; +class TemplateURL; + +namespace base { +class DictionaryValue; +class FilePath; +} + +#if defined(OS_WIN) +// Detects which version of Firefox is installed from registry. Returns its +// major version, and drops the minor version. Returns 0 if failed. If there are +// indicators of both Firefox 2 and Firefox 3 it is biased to return the biggest +// version. +int GetCurrentFirefoxMajorVersionFromRegistry(); + +// Detects where Firefox lives. Returns an empty path if Firefox is not +// installed. +base::FilePath GetFirefoxInstallPathFromRegistry(); +#endif // OS_WIN + +#if defined(OS_MACOSX) +// Get the directory in which the Firefox .dylibs live, we need to load these +// in order to decoded FF profile passwords. +// The Path is usuall FF App Bundle/Contents/Mac OS/ +// Returns empty path on failure. +base::FilePath GetFirefoxDylibPath(); +#endif // OS_MACOSX + +// Returns the path to the Firefox profile. +base::FilePath GetFirefoxProfilePath(); + +// Returns the path to the Firefox profile, using a custom dictionary. +// Exposed for testing. +base::FilePath GetFirefoxProfilePathFromDictionary( + const base::DictionaryValue& root); + +// Detects version of Firefox and installation path for the given Firefox +// profile. +bool GetFirefoxVersionAndPathFromProfile(const base::FilePath& profile_path, + int* version, + base::FilePath* app_path); + +// Gets the full path of the profiles.ini file. This file records the profiles +// that can be used by Firefox. Returns an empty path if failed. +base::FilePath GetProfilesINI(); + +// Parses the profile.ini file, and stores its information in |root|. +// This file is a plain-text file. Key/value pairs are stored one per line, and +// they are separated in different sections. For example: +// [General] +// StartWithLastProfile=1 +// +// [Profile0] +// Name=default +// IsRelative=1 +// Path=Profiles/abcdefeg.default +// We set "[value]" in path "
.". For example, the path +// "Genenral.StartWithLastProfile" has the value "1". +void ParseProfileINI(const base::FilePath& file, base::DictionaryValue* root); + +// Returns the home page set in Firefox in a particular profile. +GURL GetHomepage(const base::FilePath& profile_path); + +// Checks to see if this home page is a default home page, as specified by +// the resource file browserconfig.properties in the Firefox application +// directory. +bool IsDefaultHomepage(const GURL& homepage, const base::FilePath& app_path); + +// Parses the value of a particular firefox preference from a string that is the +// contents of the prefs file. +std::string GetPrefsJsValue(const std::string& prefs, + const std::string& pref_key); + +// Returns the localized Firefox branding name. +// This is useful to differentiate between Firefox and Iceweasel. +// If anything goes wrong while trying to obtain the branding name, +// the function assumes it's Firefox. +base::string16 GetFirefoxImporterName(const base::FilePath& app_path); + +#endif // CHROME_COMMON_IMPORTER_FIREFOX_IMPORTER_UTILS_H_ diff --git a/chromium_src/chrome/common/importer/firefox_importer_utils_linux.cc b/chromium_src/chrome/common/importer/firefox_importer_utils_linux.cc new file mode 100644 index 0000000000..73ff81e73e --- /dev/null +++ b/chromium_src/chrome/common/importer/firefox_importer_utils_linux.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2009 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/common/importer/firefox_importer_utils.h" + +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" + +base::FilePath GetProfilesINI() { + base::FilePath ini_file; + // The default location of the profile folder containing user data is + // under user HOME directory in .mozilla/firefox folder on Linux. + base::FilePath home; + PathService::Get(base::DIR_HOME, &home); + if (!home.empty()) { + ini_file = home.Append(".mozilla/firefox/profiles.ini"); + } + if (base::PathExists(ini_file)) + return ini_file; + + return base::FilePath(); +} diff --git a/chromium_src/chrome/common/importer/firefox_importer_utils_mac.mm b/chromium_src/chrome/common/importer/firefox_importer_utils_mac.mm new file mode 100644 index 0000000000..f5da8c005f --- /dev/null +++ b/chromium_src/chrome/common/importer/firefox_importer_utils_mac.mm @@ -0,0 +1,45 @@ +// 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. + +#include +#include + +#include "chrome/common/importer/firefox_importer_utils.h" + +#include "base/files/file_util.h" +#include "base/mac/foundation_util.h" +#include "base/path_service.h" + +base::FilePath GetProfilesINI() { + base::FilePath app_data_path; + if (!PathService::Get(base::DIR_APP_DATA, &app_data_path)) { + return base::FilePath(); + } + base::FilePath ini_file = + app_data_path.Append("Firefox").Append("profiles.ini"); + if (!base::PathExists(ini_file)) { + return base::FilePath(); + } + return ini_file; +} + +base::FilePath GetFirefoxDylibPath() { + CFURLRef appURL = nil; + if (LSFindApplicationForInfo(kLSUnknownCreator, + CFSTR("org.mozilla.firefox"), + NULL, + NULL, + &appURL) != noErr) { + return base::FilePath(); + } + NSBundle *ff_bundle = + [NSBundle bundleWithPath:[base::mac::CFToNSCast(appURL) path]]; + CFRelease(appURL); + NSString *ff_library_path = + [[ff_bundle executablePath] stringByDeletingLastPathComponent]; + char buf[MAXPATHLEN]; + if (![ff_library_path getFileSystemRepresentation:buf maxLength:sizeof(buf)]) + return base::FilePath(); + return base::FilePath(buf); +} diff --git a/chromium_src/chrome/common/importer/firefox_importer_utils_win.cc b/chromium_src/chrome/common/importer/firefox_importer_utils_win.cc new file mode 100644 index 0000000000..47486d11c7 --- /dev/null +++ b/chromium_src/chrome/common/importer/firefox_importer_utils_win.cc @@ -0,0 +1,81 @@ +// 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/common/importer/firefox_importer_utils.h" + +#include + +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/strings/string16.h" +#include "base/win/registry.h" + +// NOTE: Keep these in order since we need test all those paths according +// to priority. For example. One machine has multiple users. One non-admin +// user installs Firefox 2, which causes there is a Firefox2 entry under HKCU. +// One admin user installs Firefox 3, which causes there is a Firefox 3 entry +// under HKLM. So when the non-admin user log in, we should deal with Firefox 2 +// related data instead of Firefox 3. +static const HKEY kFireFoxRegistryPaths[] = { + HKEY_CURRENT_USER, + HKEY_LOCAL_MACHINE +}; + +static const wchar_t* kFirefoxPath = L"Software\\Mozilla\\Mozilla Firefox"; +static const wchar_t* kCurrentVersion = L"CurrentVersion"; + +int GetCurrentFirefoxMajorVersionFromRegistry() { + TCHAR ver_buffer[128]; + DWORD ver_buffer_length = sizeof(ver_buffer); + int highest_version = 0; + // When installing Firefox with admin account, the product keys will be + // written under HKLM\Mozilla. Otherwise it the keys will be written under + // HKCU\Mozilla. + for (const HKEY kFireFoxRegistryPath : kFireFoxRegistryPaths) { + base::win::RegKey reg_key(kFireFoxRegistryPath, kFirefoxPath, KEY_READ); + + LONG result = reg_key.ReadValue(kCurrentVersion, ver_buffer, + &ver_buffer_length, NULL); + if (result != ERROR_SUCCESS) + continue; + highest_version = std::max(highest_version, _wtoi(ver_buffer)); + } + return highest_version; +} + +base::FilePath GetFirefoxInstallPathFromRegistry() { + // Detects the path that Firefox is installed in. + base::string16 registry_path = kFirefoxPath; + wchar_t buffer[MAX_PATH]; + DWORD buffer_length = sizeof(buffer); + base::win::RegKey reg_key(HKEY_LOCAL_MACHINE, registry_path.c_str(), + KEY_READ); + LONG result = reg_key.ReadValue(kCurrentVersion, buffer, + &buffer_length, NULL); + if (result != ERROR_SUCCESS) + return base::FilePath(); + + registry_path += L"\\" + base::string16(buffer) + L"\\Main"; + buffer_length = sizeof(buffer); + base::win::RegKey reg_key_directory(HKEY_LOCAL_MACHINE, + registry_path.c_str(), KEY_READ); + result = reg_key_directory.ReadValue(L"Install Directory", buffer, + &buffer_length, NULL); + + return (result != ERROR_SUCCESS) ? base::FilePath() : base::FilePath(buffer); +} + +base::FilePath GetProfilesINI() { + base::FilePath ini_file; + // The default location of the profile folder containing user data is + // under the "Application Data" folder in Windows XP, Vista, and 7. + if (!PathService::Get(base::DIR_APP_DATA, &ini_file)) + return base::FilePath(); + + ini_file = ini_file.AppendASCII("Mozilla"); + ini_file = ini_file.AppendASCII("Firefox"); + ini_file = ini_file.AppendASCII("profiles.ini"); + + return base::PathExists(ini_file) ? ini_file : base::FilePath(); +} diff --git a/chromium_src/chrome/common/importer/ie_importer_utils_win.cc b/chromium_src/chrome/common/importer/ie_importer_utils_win.cc new file mode 100644 index 0000000000..726e5b0166 --- /dev/null +++ b/chromium_src/chrome/common/importer/ie_importer_utils_win.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2013 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/common/importer/ie_importer_utils_win.h" + +#include "chrome/common/importer/importer_test_registry_overrider_win.h" + +namespace { + +const base::char16 kIEFavoritesOrderKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\" + L"MenuOrder\\Favorites"; + +const base::char16 kIEStorage2Key[] = + L"Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2"; + +const base::char16 kIESettingsMainKey[] = + L"Software\\Microsoft\\Internet Explorer\\Main"; + +base::string16 GetPotentiallyOverridenIEKey( + const base::string16& desired_key_path) { + base::string16 test_reg_override( + ImporterTestRegistryOverrider::GetTestRegistryOverride()); + return test_reg_override.empty() ? desired_key_path : test_reg_override; +} + +} // namespace + +namespace importer { + +base::string16 GetIEFavoritesOrderKey() { + // Return kIEFavoritesOrderKey unless an override has been set for tests. + return GetPotentiallyOverridenIEKey(kIEFavoritesOrderKey); +} + +base::string16 GetIE7PasswordsKey() { + // Return kIEStorage2Key unless an override has been set for tests. + return GetPotentiallyOverridenIEKey(kIEStorage2Key); +} + +base::string16 GetIESettingsKey() { + // Return kIESettingsMainKey unless an override has been set for tests. + return GetPotentiallyOverridenIEKey(kIESettingsMainKey); +} + +} // namespace importer + diff --git a/chromium_src/chrome/common/importer/ie_importer_utils_win.h b/chromium_src/chrome/common/importer/ie_importer_utils_win.h new file mode 100644 index 0000000000..8e43bbaee4 --- /dev/null +++ b/chromium_src/chrome/common/importer/ie_importer_utils_win.h @@ -0,0 +1,26 @@ +// Copyright (c) 2013 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. + +#ifndef CHROME_COMMON_IMPORTER_IE_IMPORTER_UTILS_WIN_H_ +#define CHROME_COMMON_IMPORTER_IE_IMPORTER_UTILS_WIN_H_ + +#include "base/strings/string16.h" + +namespace importer { + +// Returns the key to be used in HKCU to look for IE's favorites order blob. +// Overridable by tests via ImporterTestRegistryOverrider. +base::string16 GetIEFavoritesOrderKey(); + +// Returns the key to be used in HKCU to look for IE7 passwords. +// Overridable by tests via ImporterTestRegistryOverrider. +base::string16 GetIE7PasswordsKey(); + +// Returns the key to be used in HKCU to look for IE settings. +// Overridable by tests via ImporterTestRegistryOverrider. +base::string16 GetIESettingsKey(); + +} // namespace importer + +#endif // CHROME_COMMON_IMPORTER_IE_IMPORTER_UTILS_WIN_H_ diff --git a/chromium_src/chrome/common/importer/imported_bookmark_entry.cc b/chromium_src/chrome/common/importer/imported_bookmark_entry.cc new file mode 100644 index 0000000000..a82e1cd85c --- /dev/null +++ b/chromium_src/chrome/common/importer/imported_bookmark_entry.cc @@ -0,0 +1,24 @@ +// Copyright 2013 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/common/importer/imported_bookmark_entry.h" + +ImportedBookmarkEntry::ImportedBookmarkEntry() + : in_toolbar(false), + is_folder(false) {} + +ImportedBookmarkEntry::ImportedBookmarkEntry( + const ImportedBookmarkEntry& other) = default; + +ImportedBookmarkEntry::~ImportedBookmarkEntry() {} + +bool ImportedBookmarkEntry::operator==( + const ImportedBookmarkEntry& other) const { + return (in_toolbar == other.in_toolbar && + is_folder == other.is_folder && + url == other.url && + path == other.path && + title == other.title && + creation_time == other.creation_time); +} diff --git a/chromium_src/chrome/common/importer/imported_bookmark_entry.h b/chromium_src/chrome/common/importer/imported_bookmark_entry.h new file mode 100644 index 0000000000..abb9ea0ba3 --- /dev/null +++ b/chromium_src/chrome/common/importer/imported_bookmark_entry.h @@ -0,0 +1,29 @@ +// Copyright 2013 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. + +#ifndef CHROME_COMMON_IMPORTER_IMPORTED_BOOKMARK_ENTRY_H_ +#define CHROME_COMMON_IMPORTER_IMPORTED_BOOKMARK_ENTRY_H_ + +#include + +#include "base/strings/string16.h" +#include "base/time/time.h" +#include "url/gurl.h" + +struct ImportedBookmarkEntry { + ImportedBookmarkEntry(); + ImportedBookmarkEntry(const ImportedBookmarkEntry& other); + ~ImportedBookmarkEntry(); + + bool operator==(const ImportedBookmarkEntry& other) const; + + bool in_toolbar; + bool is_folder; + GURL url; + std::vector path; + base::string16 title; + base::Time creation_time; +}; + +#endif // CHROME_COMMON_IMPORTER_IMPORTED_BOOKMARK_ENTRY_H_ diff --git a/chromium_src/chrome/common/importer/importer_autofill_form_data_entry.cc b/chromium_src/chrome/common/importer/importer_autofill_form_data_entry.cc new file mode 100644 index 0000000000..5dd4c5a374 --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_autofill_form_data_entry.cc @@ -0,0 +1,11 @@ +// Copyright 2014 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/common/importer/importer_autofill_form_data_entry.h" + +ImporterAutofillFormDataEntry::ImporterAutofillFormDataEntry() : times_used(0) { +} + +ImporterAutofillFormDataEntry::~ImporterAutofillFormDataEntry() { +} diff --git a/chromium_src/chrome/common/importer/importer_autofill_form_data_entry.h b/chromium_src/chrome/common/importer/importer_autofill_form_data_entry.h new file mode 100644 index 0000000000..af35195ae4 --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_autofill_form_data_entry.h @@ -0,0 +1,33 @@ +// Copyright 2014 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. + +#ifndef CHROME_COMMON_IMPORTER_IMPORTER_AUTOFILL_FORM_DATA_ENTRY_H_ +#define CHROME_COMMON_IMPORTER_IMPORTER_AUTOFILL_FORM_DATA_ENTRY_H_ + +#include "base/strings/string16.h" +#include "base/time/time.h" + +// Used as the target for importing form history from other browsers' profiles +// in the utility process. +struct ImporterAutofillFormDataEntry { + ImporterAutofillFormDataEntry(); + ~ImporterAutofillFormDataEntry(); + + // Name of input element. + base::string16 name; + + // Value of input element. + base::string16 value; + + // Number of times this name-value pair has been used. + int times_used; + + // The date of the first time when this name-value pair was used. + base::Time first_used; + + // The date of the last time when this name-value pair was used. + base::Time last_used; +}; + +#endif // CHROME_COMMON_IMPORTER_IMPORTER_AUTOFILL_FORM_DATA_ENTRY_H_ diff --git a/chromium_src/chrome/common/importer/importer_bridge.cc b/chromium_src/chrome/common/importer/importer_bridge.cc new file mode 100644 index 0000000000..15b7f745cf --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_bridge.cc @@ -0,0 +1,9 @@ +// 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. + +#include "chrome/common/importer/importer_bridge.h" + +ImporterBridge::ImporterBridge() {} + +ImporterBridge::~ImporterBridge() {} diff --git a/chromium_src/chrome/common/importer/importer_bridge.h b/chromium_src/chrome/common/importer/importer_bridge.h new file mode 100644 index 0000000000..0992868ad1 --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_bridge.h @@ -0,0 +1,109 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add SetCookies. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_COMMON_IMPORTER_IMPORTER_BRIDGE_H_ +#define CHROME_COMMON_IMPORTER_IMPORTER_BRIDGE_H_ + +#include +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "build/build_config.h" +#include "chrome/common/importer/importer_data_types.h" +#include "chrome/common/importer/importer_url_row.h" +#include "components/favicon_base/favicon_usage_data.h" + +class GURL; +struct ImportedBookmarkEntry; +struct ImporterAutofillFormDataEntry; + +namespace autofill { +struct PasswordForm; +} + +struct ImportedCookieEntry; + +namespace importer { +#if defined(OS_WIN) +struct ImporterIE7PasswordInfo; +#endif +struct ImporterURlRow; +struct SearchEngineInfo; +} + +class ImporterBridge : public base::RefCountedThreadSafe { + public: + ImporterBridge(); + + virtual void AddBookmarks( + const std::vector& bookmarks, + const base::string16& first_folder_name) = 0; + + virtual void AddHomePage(const GURL& home_page) = 0; + +#if defined(OS_WIN) + virtual void AddIE7PasswordInfo( + const importer::ImporterIE7PasswordInfo& password_info) = 0; +#endif + + virtual void SetFavicons( + const favicon_base::FaviconUsageDataList& favicons) = 0; + + virtual void SetHistoryItems(const std::vector& rows, + importer::VisitSource visit_source) = 0; + + virtual void SetKeywords( + const std::vector& search_engines, + bool unique_on_host_and_path) = 0; + + // The search_engine_data vector contains XML data retrieved from the Firefox + // profile and its sqlite db. + virtual void SetFirefoxSearchEnginesXMLData( + const std::vector& search_engine_data) = 0; + + virtual void SetPasswordForm(const autofill::PasswordForm& form) = 0; + + virtual void SetAutofillFormData( + const std::vector& entries) = 0; + + virtual void SetCookies(const std::vector& cookies) = 0; + + // Notifies the coordinator that the import operation has begun. + virtual void NotifyStarted() = 0; + + // Notifies the coordinator that the collection of data for the specified + // item has begun. + virtual void NotifyItemStarted(importer::ImportItem item) = 0; + + // Notifies the coordinator that the collection of data for the specified + // item has completed. + virtual void NotifyItemEnded(importer::ImportItem item) = 0; + + // Notifies the coordinator that the entire import operation has completed. + virtual void NotifyEnded() = 0; + + // For InProcessImporters this calls l10n_util. For ExternalProcessImporters + // this calls the set of strings we've ported over to the external process. + // It's good to avoid having to create a separate ResourceBundle for the + // external import process, since the importer only needs a few strings. + virtual base::string16 GetLocalizedString(int message_id) = 0; + + protected: + friend class base::RefCountedThreadSafe; + + virtual ~ImporterBridge(); + + DISALLOW_COPY_AND_ASSIGN(ImporterBridge); +}; + +#endif // CHROME_COMMON_IMPORTER_IMPORTER_BRIDGE_H_ diff --git a/chromium_src/chrome/common/importer/importer_data_types.cc b/chromium_src/chrome/common/importer/importer_data_types.cc new file mode 100644 index 0000000000..7a02114c8a --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_data_types.cc @@ -0,0 +1,28 @@ +// Copyright 2013 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 "build/build_config.h" +#include "chrome/common/importer/importer_data_types.h" + +namespace importer { + +SourceProfile::SourceProfile() + : importer_type(TYPE_UNKNOWN), + services_supported(0) { +} + +SourceProfile::SourceProfile(const SourceProfile& other) = default; + +SourceProfile::~SourceProfile() { +} + +#if defined(OS_WIN) +ImporterIE7PasswordInfo::ImporterIE7PasswordInfo() { +} + +ImporterIE7PasswordInfo::~ImporterIE7PasswordInfo() { +} +#endif + +} // namespace importer diff --git a/chromium_src/chrome/common/importer/importer_data_types.h b/chromium_src/chrome/common/importer/importer_data_types.h new file mode 100644 index 0000000000..c4e307c23a --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_data_types.h @@ -0,0 +1,98 @@ +// Copyright 2013 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. +// +/** + * This is not a straight copy from chromium src, in particular + * we add VISIT_SOURCE_CHROME_IMPORTED. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_COMMON_IMPORTER_IMPORTER_DATA_TYPES_H_ +#define CHROME_COMMON_IMPORTER_IMPORTER_DATA_TYPES_H_ + +#include + +#include +#include + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "chrome/common/importer/importer_type.h" +#include "url/gurl.h" + +// Types needed for importing data from other browsers and the Google Toolbar. +namespace importer { + +// An enumeration of the type of data that can be imported. +enum ImportItem { + NONE = 0, + HISTORY = 1 << 0, + FAVORITES = 1 << 1, + COOKIES = 1 << 2, // Not supported yet. + PASSWORDS = 1 << 3, + SEARCH_ENGINES = 1 << 4, + HOME_PAGE = 1 << 5, + AUTOFILL_FORM_DATA = 1 << 6, + ALL = (1 << 7) - 1 // All the bits should be 1, hence the -1. +}; + +// Information about a profile needed by an importer to do import work. +struct SourceProfile { + SourceProfile(); + SourceProfile(const SourceProfile& other); + ~SourceProfile(); + + base::string16 importer_name; + ImporterType importer_type; + base::FilePath source_path; + base::FilePath app_path; + uint16_t services_supported; // Bitmask of ImportItem. + // The application locale. Stored because we can only access it from the UI + // thread on the browser process. This is only used by the Firefox importer. + std::string locale; +}; + +// Contains information needed for importing search engine urls. +struct SearchEngineInfo { + // |url| is a string instead of a GURL since it may not actually be a valid + // GURL directly (e.g. for "http://%s.com"). + base::string16 url; + base::string16 keyword; + base::string16 display_name; +}; + +#if defined(OS_WIN) +// Contains the information read from the IE7/IE8 Storage2 key in the registry. +struct ImporterIE7PasswordInfo { + ImporterIE7PasswordInfo(); + ~ImporterIE7PasswordInfo(); + + // Hash of the url. + std::wstring url_hash; + + // Encrypted data containing the username, password and some more + // undocumented fields. + std::vector encrypted_data; + + // When the login was imported. + base::Time date_created; +}; +#endif + +// Mapped to history::VisitSource after import in the browser. +enum VisitSource { + VISIT_SOURCE_BROWSED = 0, + VISIT_SOURCE_FIREFOX_IMPORTED = 1, + VISIT_SOURCE_IE_IMPORTED = 2, + VISIT_SOURCE_SAFARI_IMPORTED = 3, + VISIT_SOURCE_CHROME_IMPORTED = 4, +}; + +} // namespace importer + +#endif // CHROME_COMMON_IMPORTER_IMPORTER_DATA_TYPES_H_ diff --git a/chromium_src/chrome/common/importer/importer_test_registry_overrider_win.cc b/chromium_src/chrome/common/importer/importer_test_registry_overrider_win.cc new file mode 100644 index 0000000000..0f88196825 --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_test_registry_overrider_win.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2013 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/common/importer/importer_test_registry_overrider_win.h" + +#include + +#include +#include + +#include "base/environment.h" +#include "base/guid.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/registry.h" + +namespace { + +// The key to which a random subkey will be appended. This key itself will never +// be deleted. +const wchar_t kTestHKCUOverrideKeyPrefix[] = L"SOFTWARE\\Chromium Unit Tests\\"; +const char kTestHKCUOverrideEnvironmentVariable[] = + "IE_IMPORTER_TEST_OVERRIDE_HKCU"; + +// Reads the environment variable set by a previous call to +// SetTestRegistryOverride() into |key| if it exists and |key| is not NULL. +// Returns true if the variable was successfully read. +bool GetTestKeyFromEnvironment(base::string16* key) { + std::unique_ptr env(base::Environment::Create()); + std::string value; + bool result = env->GetVar(kTestHKCUOverrideEnvironmentVariable, &value); + if (result) + *key = base::UTF8ToUTF16(value); + return result; +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// ImporterTestRegistryOverrider, public: + +ImporterTestRegistryOverrider::ImporterTestRegistryOverrider() + : temporary_key_(kTestHKCUOverrideKeyPrefix + + base::UTF8ToUTF16(base::GenerateGUID())) { + DCHECK(!GetTestKeyFromEnvironment(NULL)); + + std::unique_ptr env(base::Environment::Create()); + bool success = env->SetVar(kTestHKCUOverrideEnvironmentVariable, + base::UTF16ToUTF8(temporary_key_)); + DCHECK(success); +} + +ImporterTestRegistryOverrider::~ImporterTestRegistryOverrider() { + base::win::RegKey reg_key(HKEY_CURRENT_USER, temporary_key_.c_str(), + KEY_ALL_ACCESS); + DCHECK(reg_key.Valid()); + reg_key.DeleteKey(L""); + + std::unique_ptr env(base::Environment::Create()); + bool success = env->UnSetVar(kTestHKCUOverrideEnvironmentVariable); + DCHECK(success); +} + +// static +base::string16 ImporterTestRegistryOverrider::GetTestRegistryOverride() { + base::string16 key; + if (!GetTestKeyFromEnvironment(&key)) + return base::string16(); + return key; +} diff --git a/chromium_src/chrome/common/importer/importer_test_registry_overrider_win.h b/chromium_src/chrome/common/importer/importer_test_registry_overrider_win.h new file mode 100644 index 0000000000..f814382ca3 --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_test_registry_overrider_win.h @@ -0,0 +1,34 @@ +// Copyright (c) 2013 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. + +#ifndef CHROME_COMMON_IMPORTER_IMPORTER_TEST_REGISTRY_OVERRIDER_WIN_H_ +#define CHROME_COMMON_IMPORTER_IMPORTER_TEST_REGISTRY_OVERRIDER_WIN_H_ + +#include "base/macros.h" +#include "base/strings/string16.h" + +// A helper class to let tests generate a random registry key to be used in +// HKEY_CURRENT_USER in tests. After the key has been generated by constructing +// an ImporterTestRegistryOverrider, consumers in this process (or in any +// child processes created after the key has been generated) can obtain the key +// via GetTestRegistryOverride(). ImporterTestRegistryOverrider will delete +// the temporary key upon being deleted itself. Only one +// ImporterTestRegistryOverrider should live at once in a given process +// hiearchy. +class ImporterTestRegistryOverrider { + public: + ImporterTestRegistryOverrider(); + ~ImporterTestRegistryOverrider(); + + // Returns a test key if one was chosen and set by a call to + // SetTestRegistryOverride(); returns the empty string if none. + static base::string16 GetTestRegistryOverride(); + + private: + base::string16 temporary_key_; + + DISALLOW_COPY_AND_ASSIGN(ImporterTestRegistryOverrider); +}; + +#endif // CHROME_COMMON_IMPORTER_IMPORTER_TEST_REGISTRY_OVERRIDER_WIN_H_ diff --git a/chromium_src/chrome/common/importer/importer_type.h b/chromium_src/chrome/common/importer/importer_type.h new file mode 100644 index 0000000000..800a7ad060 --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_type.h @@ -0,0 +1,45 @@ +// Copyright 2013 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. + +/** + * This is not a straight copy from chromium src, in particular + * add TYPE_CHROME for chrome profile. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_COMMON_IMPORTER_IMPORTER_TYPE_H_ +#define CHROME_COMMON_IMPORTER_IMPORTER_TYPE_H_ + +#include "build/build_config.h" + +namespace importer { + +// An enumeration of the type of importers that we support to import +// settings and data from (browsers, google toolbar and a bookmarks html file). +// NOTE: Numbers added so that data can be reliably cast to ints and passed +// across IPC. +enum ImporterType { + TYPE_UNKNOWN = -1, +#if defined(OS_WIN) + TYPE_IE = 0, +#endif + // Value 1 was the (now deleted) Firefox 2 profile importer. + // We use it for chrome profile now. + TYPE_CHROME = 1, + TYPE_FIREFOX = 2, +#if defined(OS_MACOSX) + TYPE_SAFARI = 3, +#endif + // Value 4 was the (now deleted) Google Toolbar importer. + TYPE_BOOKMARKS_FILE = 5, // Identifies a 'bookmarks.html' file. +#if defined(OS_WIN) + TYPE_EDGE = 6, +#endif +}; + +} // namespace importer + + +#endif // CHROME_COMMON_IMPORTER_IMPORTER_TYPE_H_ diff --git a/chromium_src/chrome/common/importer/importer_url_row.cc b/chromium_src/chrome/common/importer/importer_url_row.cc new file mode 100644 index 0000000000..26a3414a6c --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_url_row.cc @@ -0,0 +1,21 @@ +// Copyright 2013 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/common/importer/importer_url_row.h" + +ImporterURLRow::ImporterURLRow() + : visit_count(0), + typed_count(0), + hidden(false) { +} + +ImporterURLRow::ImporterURLRow(const GURL& url) + : url(url), + visit_count(0), + typed_count(0), + hidden(false) { +} + +ImporterURLRow::ImporterURLRow(const ImporterURLRow& other) = default; + diff --git a/chromium_src/chrome/common/importer/importer_url_row.h b/chromium_src/chrome/common/importer/importer_url_row.h new file mode 100644 index 0000000000..f7e0a6dd8e --- /dev/null +++ b/chromium_src/chrome/common/importer/importer_url_row.h @@ -0,0 +1,39 @@ +// Copyright 2013 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. + +#ifndef CHROME_COMMON_IMPORTER_IMPORTER_URL_ROW_H_ +#define CHROME_COMMON_IMPORTER_IMPORTER_URL_ROW_H_ + +#include "base/strings/string16.h" +#include "base/time/time.h" +#include "url/gurl.h" + +// Used as the target for importing history URLs from other browser's profiles +// in the utility process. Converted to history::URLRow after being passed via +// IPC to the browser. +struct ImporterURLRow { + public: + ImporterURLRow(); + explicit ImporterURLRow(const GURL& url); + ImporterURLRow(const ImporterURLRow& other); + + GURL url; + base::string16 title; + + // Total number of times this URL has been visited. + int visit_count; + + // Number of times this URL has been manually entered in the URL bar. + int typed_count; + + // The date of the last visit of this URL, which saves us from having to + // loop up in the visit table for things like autocomplete and expiration. + base::Time last_visit; + + // Indicates this entry should now be shown in typical UI or queries, this + // is usually for subframes. + bool hidden; +}; + +#endif // CHROME_COMMON_IMPORTER_IMPORTER_URL_ROW_H_ diff --git a/chromium_src/chrome/common/importer/profile_import_process_messages.cc b/chromium_src/chrome/common/importer/profile_import_process_messages.cc new file mode 100644 index 0000000000..b585c24fad --- /dev/null +++ b/chromium_src/chrome/common/importer/profile_import_process_messages.cc @@ -0,0 +1,39 @@ +// Copyright 2013 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. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "chrome/common/importer/profile_import_process_messages.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "chrome/common/importer/profile_import_process_messages.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "chrome/common/importer/profile_import_process_messages.h" + +// Generate param traits size methods. +#include "ipc/param_traits_size_macros.h" +namespace IPC { +#include "chrome/common/importer/profile_import_process_messages.h" +} + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "chrome/common/importer/profile_import_process_messages.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "chrome/common/importer/profile_import_process_messages.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "chrome/common/importer/profile_import_process_messages.h" +} // namespace IPC diff --git a/chromium_src/chrome/common/importer/profile_import_process_messages.h b/chromium_src/chrome/common/importer/profile_import_process_messages.h new file mode 100644 index 0000000000..962e1344b8 --- /dev/null +++ b/chromium_src/chrome/common/importer/profile_import_process_messages.h @@ -0,0 +1,119 @@ +// Copyright 2013 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add NotifyCookiesImportStart and NotifyCookiesImportGroup. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +// Multiply-included message file, no traditonal include guard. +#include +#include + +#include "atom/common/importer/imported_cookie_entry.h" +#include "base/strings/string16.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/common/common_param_traits_macros.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_autofill_form_data_entry.h" +#include "chrome/common/importer/importer_data_types.h" +#include "chrome/common/importer/importer_url_row.h" +#include "chrome/common/importer/profile_import_process_param_traits_macros.h" +#include "components/autofill/content/common/autofill_param_traits_macros.h" +#include "components/autofill/core/common/password_form.h" +#include "components/favicon_base/favicon_usage_data.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" +#include "url/ipc/url_param_traits.h" + +// Force multiple inclusion of the param traits file to generate all methods. +#undef CHROME_COMMON_IMPORTER_PROFILE_IMPORT_PROCESS_PARAM_TRAITS_MACROS_H_ + +#define IPC_MESSAGE_START ProfileImportMsgStart + +//----------------------------------------------------------------------------- +// ProfileImportProcess messages +// These are messages sent from the browser to the profile import process. +IPC_MESSAGE_CONTROL3(ProfileImportProcessMsg_StartImport, + importer::SourceProfile, + int /* Bitmask of items to import. */, + base::DictionaryValue /* Localized strings. */) + +IPC_MESSAGE_CONTROL0(ProfileImportProcessMsg_CancelImport) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessMsg_ReportImportItemFinished, + int /* ImportItem */) + +//--------------------------------------------------------------------------- +// ProfileImportProcessHost messages +// These are messages sent from the profile import process to the browser. +// These messages send information about the status of the import and +// individual import tasks. +IPC_MESSAGE_CONTROL0(ProfileImportProcessHostMsg_Import_Started) + +IPC_MESSAGE_CONTROL2(ProfileImportProcessHostMsg_Import_Finished, + bool /* was import successful? */, + std::string /* error message, if any */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_ImportItem_Started, + int /* ImportItem */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_ImportItem_Finished, + int /* ImportItem */) + +// These messages send data from the external importer process back to +// the process host so it can be written to the profile. +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyHistoryImportStart, + int /* total number of ImporterURLRow items */) + +IPC_MESSAGE_CONTROL2(ProfileImportProcessHostMsg_NotifyHistoryImportGroup, + std::vector, + int /* the source of URLs as in history::VisitSource.*/ + /* To simplify IPC call, pass as an integer */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyHomePageImportReady, + GURL /* GURL of home page */) + +IPC_MESSAGE_CONTROL2(ProfileImportProcessHostMsg_NotifyBookmarksImportStart, + base::string16 /* first folder name */, + int /* total number of bookmarks */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyBookmarksImportGroup, + std::vector) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyFaviconsImportStart, + int /* total number of favicons */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyFaviconsImportGroup, + favicon_base::FaviconUsageDataList) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyPasswordFormReady, + autofill::PasswordForm) + +IPC_MESSAGE_CONTROL2(ProfileImportProcessHostMsg_NotifyKeywordsReady, + std::vector, // search_engines + bool /* unique on host and path */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyFirefoxSearchEngData, + std::vector) // search_engine_data + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_AutofillFormDataImportStart, + int /* total number of entries to be imported */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_AutofillFormDataImportGroup, + std::vector) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyCookiesImportStart, + int /* total number of cookies */) + +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyCookiesImportGroup, + std::vector) + +#if defined(OS_WIN) +IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyIE7PasswordInfo, + importer::ImporterIE7PasswordInfo) // password_info +#endif diff --git a/chromium_src/chrome/common/importer/profile_import_process_param_traits_macros.h b/chromium_src/chrome/common/importer/profile_import_process_param_traits_macros.h new file mode 100644 index 0000000000..0ab66a7c5e --- /dev/null +++ b/chromium_src/chrome/common/importer/profile_import_process_param_traits_macros.h @@ -0,0 +1,112 @@ +// Copyright 2014 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add traits for ImportedCookieEntry. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +// Singly or Multiply-included shared traits file depending on circumstances. +// This allows the use of IPC serialization macros in more than one IPC message +// file. +#ifndef CHROME_COMMON_IMPORTER_PROFILE_IMPORT_PROCESS_PARAM_TRAITS_MACROS_H_ +#define CHROME_COMMON_IMPORTER_PROFILE_IMPORT_PROCESS_PARAM_TRAITS_MACROS_H_ + +#include +#include + +#include "atom/common/importer/imported_cookie_entry.h" +#include "base/strings/string16.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/common/common_param_traits_macros.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_autofill_form_data_entry.h" +#include "chrome/common/importer/importer_data_types.h" +#include "chrome/common/importer/importer_url_row.h" +#include "components/autofill/content/common/autofill_param_traits_macros.h" +#include "components/autofill/core/common/password_form.h" +#include "components/favicon_base/favicon_usage_data.h" +#include "content/public/common/common_param_traits.h" +#include "ipc/ipc_message_macros.h" + +#if defined(OS_WIN) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(importer::ImporterType, + importer::TYPE_UNKNOWN, + importer::TYPE_EDGE) +#else +IPC_ENUM_TRAITS_MIN_MAX_VALUE(importer::ImporterType, + importer::TYPE_UNKNOWN, + importer::TYPE_BOOKMARKS_FILE) +#endif + +IPC_STRUCT_TRAITS_BEGIN(importer::SourceProfile) + IPC_STRUCT_TRAITS_MEMBER(importer_name) + IPC_STRUCT_TRAITS_MEMBER(importer_type) + IPC_STRUCT_TRAITS_MEMBER(source_path) + IPC_STRUCT_TRAITS_MEMBER(app_path) + IPC_STRUCT_TRAITS_MEMBER(services_supported) + IPC_STRUCT_TRAITS_MEMBER(locale) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(ImporterURLRow) + IPC_STRUCT_TRAITS_MEMBER(url) + IPC_STRUCT_TRAITS_MEMBER(title) + IPC_STRUCT_TRAITS_MEMBER(visit_count) + IPC_STRUCT_TRAITS_MEMBER(typed_count) + IPC_STRUCT_TRAITS_MEMBER(last_visit) + IPC_STRUCT_TRAITS_MEMBER(hidden) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(ImportedBookmarkEntry) + IPC_STRUCT_TRAITS_MEMBER(in_toolbar) + IPC_STRUCT_TRAITS_MEMBER(is_folder) + IPC_STRUCT_TRAITS_MEMBER(url) + IPC_STRUCT_TRAITS_MEMBER(path) + IPC_STRUCT_TRAITS_MEMBER(title) + IPC_STRUCT_TRAITS_MEMBER(creation_time) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(favicon_base::FaviconUsageData) + IPC_STRUCT_TRAITS_MEMBER(favicon_url) + IPC_STRUCT_TRAITS_MEMBER(png_data) + IPC_STRUCT_TRAITS_MEMBER(urls) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(importer::SearchEngineInfo) + IPC_STRUCT_TRAITS_MEMBER(url) + IPC_STRUCT_TRAITS_MEMBER(keyword) + IPC_STRUCT_TRAITS_MEMBER(display_name) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(ImporterAutofillFormDataEntry) + IPC_STRUCT_TRAITS_MEMBER(name) + IPC_STRUCT_TRAITS_MEMBER(value) + IPC_STRUCT_TRAITS_MEMBER(times_used) + IPC_STRUCT_TRAITS_MEMBER(first_used) + IPC_STRUCT_TRAITS_MEMBER(last_used) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(ImportedCookieEntry) + IPC_STRUCT_TRAITS_MEMBER(domain) + IPC_STRUCT_TRAITS_MEMBER(name) + IPC_STRUCT_TRAITS_MEMBER(value) + IPC_STRUCT_TRAITS_MEMBER(host) + IPC_STRUCT_TRAITS_MEMBER(path) + IPC_STRUCT_TRAITS_MEMBER(expiry_date) + IPC_STRUCT_TRAITS_MEMBER(secure) + IPC_STRUCT_TRAITS_MEMBER(httponly) +IPC_STRUCT_TRAITS_END() + +#if defined(OS_WIN) +IPC_STRUCT_TRAITS_BEGIN(importer::ImporterIE7PasswordInfo) + IPC_STRUCT_TRAITS_MEMBER(url_hash) + IPC_STRUCT_TRAITS_MEMBER(encrypted_data) + IPC_STRUCT_TRAITS_MEMBER(date_created) +IPC_STRUCT_TRAITS_END() +#endif + +#endif // CHROME_COMMON_IMPORTER_PROFILE_IMPORT_PROCESS_PARAM_TRAITS_MACROS_H_ diff --git a/chromium_src/chrome/common/importer/pstore_declarations.h b/chromium_src/chrome/common/importer/pstore_declarations.h new file mode 100644 index 0000000000..e06ab6c776 --- /dev/null +++ b/chromium_src/chrome/common/importer/pstore_declarations.h @@ -0,0 +1,182 @@ +// Copyright 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. + +#ifndef CHROME_COMMON_IMPORTER_PSTORE_DECLARATIONS_H_ +#define CHROME_COMMON_IMPORTER_PSTORE_DECLARATIONS_H_ + +#ifdef __PSTORE_H__ +#error Should not include pstore.h and this file simultaneously. +#endif + +#include + +// pstore.h is no longer shipped in the Windows 8 SDK. Define a minimal set +// here. + +// These types are referenced in interfaces we use, but our code does not use +// refer to these types, so simply make them opaque. +class IEnumPStoreTypes; +struct PST_ACCESSRULESET; +struct PST_PROMPTINFO; +struct PST_PROVIDERINFO; +struct PST_TYPEINFO; + +EXTERN_C const IID IID_IPStore; +EXTERN_C const IID IID_IEnumPStoreItems; + +typedef DWORD PST_KEY; +typedef DWORD PST_ACCESSMODE; +#define PST_E_OK _HRESULT_TYPEDEF_(0x00000000L) + +interface IEnumPStoreItems : public IUnknown +{ + public: + virtual HRESULT STDMETHODCALLTYPE Next( + DWORD celt, + LPWSTR __RPC_FAR *rgelt, + DWORD __RPC_FAR *pceltFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE Skip(DWORD celt) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reset(void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + IEnumPStoreItems __RPC_FAR *__RPC_FAR *ppenum) = 0; +}; + +interface IPStore : public IUnknown +{ + public: + virtual HRESULT STDMETHODCALLTYPE GetInfo( + PST_PROVIDERINFO* __RPC_FAR *ppProperties) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProvParam( + DWORD dwParam, + DWORD __RPC_FAR *pcbData, + BYTE __RPC_FAR *__RPC_FAR *ppbData, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProvParam( + DWORD dwParam, + DWORD cbData, + BYTE __RPC_FAR *pbData, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateType( + PST_KEY Key, + const GUID __RPC_FAR *pType, + PST_TYPEINFO* pInfo, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetTypeInfo( + PST_KEY Key, + const GUID __RPC_FAR *pType, + PST_TYPEINFO* __RPC_FAR *ppInfo, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteType( + PST_KEY Key, + const GUID __RPC_FAR *pType, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateSubtype( + PST_KEY Key, + const GUID __RPC_FAR *pType, + const GUID __RPC_FAR *pSubtype, + PST_TYPEINFO* pInfo, + PST_ACCESSRULESET* pRules, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSubtypeInfo( + PST_KEY Key, + const GUID __RPC_FAR *pType, + const GUID __RPC_FAR *pSubtype, + PST_TYPEINFO* __RPC_FAR *ppInfo, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteSubtype( + PST_KEY Key, + const GUID __RPC_FAR *pType, + const GUID __RPC_FAR *pSubtype, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReadAccessRuleset( + PST_KEY Key, + const GUID __RPC_FAR *pType, + const GUID __RPC_FAR *pSubtype, + PST_ACCESSRULESET* __RPC_FAR *ppRules, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE WriteAccessRuleset( + PST_KEY Key, + const GUID __RPC_FAR *pType, + const GUID __RPC_FAR *pSubtype, + PST_ACCESSRULESET* pRules, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumTypes( + PST_KEY Key, + DWORD dwFlags, + IEnumPStoreTypes __RPC_FAR *__RPC_FAR *ppenum) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumSubtypes( + PST_KEY Key, + const GUID __RPC_FAR *pType, + DWORD dwFlags, + IEnumPStoreTypes __RPC_FAR *__RPC_FAR *ppenum) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteItem( + PST_KEY Key, + const GUID __RPC_FAR *pItemType, + const GUID __RPC_FAR *pItemSubtype, + LPCWSTR szItemName, + PST_PROMPTINFO* pPromptInfo, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReadItem( + PST_KEY Key, + const GUID __RPC_FAR *pItemType, + const GUID __RPC_FAR *pItemSubtype, + LPCWSTR szItemName, + DWORD __RPC_FAR *pcbData, + BYTE __RPC_FAR *__RPC_FAR *ppbData, + PST_PROMPTINFO* pPromptInfo, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE WriteItem( + PST_KEY Key, + const GUID __RPC_FAR *pItemType, + const GUID __RPC_FAR *pItemSubtype, + LPCWSTR szItemName, + DWORD cbData, + BYTE __RPC_FAR *pbData, + PST_PROMPTINFO* pPromptInfo, + DWORD dwDefaultConfirmationStyle, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE OpenItem( + PST_KEY Key, + const GUID __RPC_FAR *pItemType, + const GUID __RPC_FAR *pItemSubtype, + LPCWSTR szItemName, + PST_ACCESSMODE ModeFlags, + PST_PROMPTINFO* pPromptInfo, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE CloseItem( + PST_KEY Key, + const GUID __RPC_FAR *pItemType, + const GUID __RPC_FAR *pItemSubtype, + LPCWSTR szItemName, + DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumItems( + PST_KEY Key, + const GUID __RPC_FAR *pItemType, + const GUID __RPC_FAR *pItemSubtype, + DWORD dwFlags, + IEnumPStoreItems __RPC_FAR *__RPC_FAR *ppenum) = 0; +}; + +#endif // CHROME_COMMON_IMPORTER_PSTORE_DECLARATIONS_H_ diff --git a/chromium_src/chrome/common/importer/safari_importer_utils.h b/chromium_src/chrome/common/importer/safari_importer_utils.h new file mode 100644 index 0000000000..6885251a62 --- /dev/null +++ b/chromium_src/chrome/common/importer/safari_importer_utils.h @@ -0,0 +1,22 @@ +// Copyright 2013 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. + +#ifndef CHROME_COMMON_IMPORTER_SAFARI_IMPORTER_UTILS_H_ +#define CHROME_COMMON_IMPORTER_SAFARI_IMPORTER_UTILS_H_ + +#include + +namespace base { +class FilePath; +} + +// Does this user account have a Safari Profile and if so, what items +// are supported? +// in: library_dir - ~/Library or a standin for testing purposes. +// out: services_supported - the service supported for import. +// Returns true if we can import the Safari profile. +bool SafariImporterCanImport(const base::FilePath& library_dir, + uint16_t* services_supported); + +#endif // CHROME_COMMON_IMPORTER_SAFARI_IMPORTER_UTILS_H_ diff --git a/chromium_src/chrome/common/importer/safari_importer_utils.mm b/chromium_src/chrome/common/importer/safari_importer_utils.mm new file mode 100644 index 0000000000..b69506abba --- /dev/null +++ b/chromium_src/chrome/common/importer/safari_importer_utils.mm @@ -0,0 +1,29 @@ +// Copyright 2013 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/common/importer/safari_importer_utils.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "chrome/common/importer/importer_data_types.h" + +bool SafariImporterCanImport(const base::FilePath& library_dir, + uint16_t* services_supported) { + DCHECK(services_supported); + *services_supported = importer::NONE; + + // Import features are toggled by the following: + // bookmarks import: existence of ~/Library/Safari/Bookmarks.plist file. + // history import: existence of ~/Library/Safari/History.plist file. + base::FilePath safari_dir = library_dir.Append("Safari"); + base::FilePath bookmarks_path = safari_dir.Append("Bookmarks.plist"); + base::FilePath history_path = safari_dir.Append("History.plist"); + + if (base::PathExists(bookmarks_path)) + *services_supported |= importer::FAVORITES; + if (base::PathExists(history_path)) + *services_supported |= importer::HISTORY; + + return *services_supported != importer::NONE; +} diff --git a/chromium_src/chrome/common/ini_parser.cc b/chromium_src/chrome/common/ini_parser.cc new file mode 100644 index 0000000000..91f2d7ad02 --- /dev/null +++ b/chromium_src/chrome/common/ini_parser.cc @@ -0,0 +1,64 @@ +// Copyright 2014 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/common/ini_parser.h" + +#include + +#include "base/logging.h" +#include "base/strings/string_tokenizer.h" + +INIParser::INIParser() : used_(false) {} + +INIParser::~INIParser() {} + +void INIParser::Parse(const std::string& content) { + DCHECK(!used_); + used_ = true; + base::StringTokenizer tokenizer(content, "\r\n"); + + std::string current_section; + while (tokenizer.GetNext()) { + std::string line = tokenizer.token(); + if (line.empty()) { + // Skips the empty line. + continue; + } + if (line[0] == '#' || line[0] == ';') { + // This line is a comment. + continue; + } + if (line[0] == '[') { + // It is a section header. + current_section = line.substr(1); + size_t end = current_section.rfind(']'); + if (end != std::string::npos) + current_section.erase(end); + } else { + std::string key, value; + size_t equal = line.find('='); + if (equal != std::string::npos) { + key = line.substr(0, equal); + value = line.substr(equal + 1); + HandleTriplet(current_section, key, value); + } + } + } +} + +DictionaryValueINIParser::DictionaryValueINIParser() {} + +DictionaryValueINIParser::~DictionaryValueINIParser() {} + +void DictionaryValueINIParser::HandleTriplet(const std::string& section, + const std::string& key, + const std::string& value) { + + // Checks whether the section and key contain a '.' character. + // Those sections and keys break DictionaryValue's path format when not + // using the *WithoutPathExpansion methods. + if (section.find('.') == std::string::npos && + key.find('.') == std::string::npos) + root_.SetString(section + "." + key, value); +} diff --git a/chromium_src/chrome/common/ini_parser.h b/chromium_src/chrome/common/ini_parser.h new file mode 100644 index 0000000000..2f8ef16079 --- /dev/null +++ b/chromium_src/chrome/common/ini_parser.h @@ -0,0 +1,64 @@ +// Copyright 2014 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. + +#ifndef CHROME_COMMON_INI_PARSER_H_ +#define CHROME_COMMON_INI_PARSER_H_ + +#include + +#include "base/macros.h" +#include "base/values.h" + +// Parses INI files in a string. Users should in inherit from this class. +// This is a very basic INI parser with these characteristics: +// - Ignores blank lines. +// - Ignores comment lines beginning with '#' or ';'. +// - Duplicate key names in the same section will simply cause repeated calls +// to HandleTriplet with the same |section| and |key| parameters. +// - No escape characters supported. +// - Global properties result in calls to HandleTriplet with an empty string in +// the |section| argument. +// - Section headers begin with a '[' character. It is recommended, but +// not required to close the header bracket with a ']' character. All +// characters after a closing ']' character is ignored. +// - Key value pairs are indicated with an '=' character. Whitespace is not +// ignored. Quoting is not supported. Everything before the first '=' +// is considered the |key|, and everything after is the |value|. +class INIParser { + public: + INIParser(); + virtual ~INIParser(); + + // May only be called once per instance. + void Parse(const std::string& content); + + private: + virtual void HandleTriplet(const std::string& section, + const std::string& key, + const std::string& value) = 0; + + bool used_; +}; + +// Parsed values are stored as strings at the "section.key" path. Triplets with +// |section| or |key| parameters containing '.' are ignored. +class DictionaryValueINIParser : public INIParser { + public: + DictionaryValueINIParser(); + ~DictionaryValueINIParser() override; + + const base::DictionaryValue& root() const { return root_; } + + private: + // INIParser implementation. + void HandleTriplet(const std::string& section, + const std::string& key, + const std::string& value) override; + + base::DictionaryValue root_; + + DISALLOW_COPY_AND_ASSIGN(DictionaryValueINIParser); +}; + +#endif // CHROME_COMMON_INI_PARSER_H_ diff --git a/chromium_src/chrome/utility/importer/bookmark_html_reader.cc b/chromium_src/chrome/utility/importer/bookmark_html_reader.cc new file mode 100644 index 0000000000..f23183c072 --- /dev/null +++ b/chromium_src/chrome/utility/importer/bookmark_html_reader.cc @@ -0,0 +1,502 @@ +// Copyright 2013 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/utility/importer/bookmark_html_reader.h" + +#include +#include + +#include "base/callback.h" +#include "base/files/file_util.h" +#include "base/i18n/icu_string_conversions.h" +#include "base/macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/utility/importer/favicon_reencode.h" +#include "components/search_engines/search_terms_data.h" +#include "components/search_engines/template_url.h" +#include "net/base/data_url.h" +#include "net/base/escape.h" +#include "url/gurl.h" +#include "url/url_constants.h" + +namespace { + +// Fetches the given |attribute| value from the |attribute_list|. Returns true +// if successful, and |value| will contain the value. +bool GetAttribute(const std::string& attribute_list, + const std::string& attribute, + std::string* value) { + const char kQuote[] = "\""; + + size_t begin = attribute_list.find(attribute + "=" + kQuote); + if (begin == std::string::npos) + return false; // Can't find the attribute. + + begin += attribute.size() + 2; + size_t end = begin + 1; + + while (end < attribute_list.size()) { + if (attribute_list[end] == '"' && + attribute_list[end - 1] != '\\') { + break; + } + end++; + } + + if (end == attribute_list.size()) + return false; // The value is not quoted. + + *value = attribute_list.substr(begin, end - begin); + return true; +} + +// Given the URL of a page and a favicon data URL, adds an appropriate record +// to the given favicon usage vector. +void DataURLToFaviconUsage(const GURL& link_url, + const GURL& favicon_data, + favicon_base::FaviconUsageDataList* favicons) { + if (!link_url.is_valid() || !favicon_data.is_valid() || + !favicon_data.SchemeIs(url::kDataScheme)) + return; + + // Parse the data URL. + std::string mime_type, char_set, data; + if (!net::DataURL::Parse(favicon_data, &mime_type, &char_set, &data) || + data.empty()) + return; + + favicon_base::FaviconUsageData usage; + if (!importer::ReencodeFavicon( + reinterpret_cast(&data[0]), + data.size(), &usage.png_data)) + return; // Unable to decode. + + // We need to make up a URL for the favicon. We use a version of the page's + // URL so that we can be sure it will not collide. + usage.favicon_url = GURL(std::string("made-up-favicon:") + link_url.spec()); + + // We only have one URL per favicon for Firefox 2 bookmarks. + usage.urls.insert(link_url); + + favicons->push_back(usage); +} + +} // namespace + +namespace bookmark_html_reader { + +static std::string stripDt(const std::string& lineDt) { + // Remove "
" if the line starts with "
". This may not occur if + // "
" was on the previous line. Liberally accept entries that do not + // have an opening "
" at all. + std::string line = lineDt; + static const char kDtTag[] = "
"; + if (base::StartsWith(line, kDtTag, + base::CompareCase::INSENSITIVE_ASCII)) { + line.erase(0, arraysize(kDtTag) - 1); + base::TrimString(line, " ", &line); + } + return line; +} + +void ImportBookmarksFile( + const base::Callback& cancellation_callback, + const base::Callback& valid_url_callback, + const base::FilePath& file_path, + std::vector* bookmarks, + std::vector* search_engines, + favicon_base::FaviconUsageDataList* favicons) { + std::string content; + base::ReadFileToString(file_path, &content); + std::vector lines = base::SplitString( + content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + + base::string16 last_folder; + bool last_folder_on_toolbar = false; + bool last_folder_is_empty = true; + bool has_subfolder = false; + base::Time last_folder_add_date; + std::vector path; + size_t toolbar_folder_index = 0; + std::string charset = "UTF-8"; // If no charset is specified, assume utf-8. + for (size_t i = 0; + i < lines.size() && + (cancellation_callback.is_null() || !cancellation_callback.Run()); + ++i) { + std::string line; + base::TrimString(lines[i], " ", &line); + + // Remove "
" if |line| starts with it. "
" is the bookmark entries + // separator in Firefox that Chrome does not support. Note that there can be + // multiple "
" tags at the beginning of a single line. + // See http://crbug.com/257474. + static const char kHrTag[] = "
"; + while (base::StartsWith(line, kHrTag, + base::CompareCase::INSENSITIVE_ASCII)) { + line.erase(0, arraysize(kHrTag) - 1); + base::TrimString(line, " ", &line); + } + + // Get the encoding of the bookmark file. + if (internal::ParseCharsetFromLine(line, &charset)) + continue; + + // Get the folder name. + if (internal::ParseFolderNameFromLine(line, + charset, + &last_folder, + &last_folder_on_toolbar, + &last_folder_add_date)) { + continue; + } + + // Get the bookmark entry. + base::string16 title; + base::string16 shortcut; + GURL url, favicon; + base::Time add_date; + base::string16 post_data; + bool is_bookmark; + // TODO(jcampan): http://b/issue?id=1196285 we do not support POST based + // keywords yet. + is_bookmark = + internal::ParseBookmarkFromLine(line, charset, &title, + &url, &favicon, &shortcut, + &add_date, &post_data) || + internal::ParseMinimumBookmarkFromLine(line, charset, &title, &url); + + // If bookmark contains a valid replaceable url and a keyword then import + // it as search engine. + std::string search_engine_url; + if (is_bookmark && post_data.empty() && + CanImportURLAsSearchEngine(url, &search_engine_url) && + !shortcut.empty()) { + importer::SearchEngineInfo search_engine_info; + search_engine_info.url.assign(base::UTF8ToUTF16(search_engine_url)); + search_engine_info.keyword = shortcut; + search_engine_info.display_name = title; + search_engines->push_back(search_engine_info); + continue; + } + + if (is_bookmark) + last_folder_is_empty = false; + + if (is_bookmark && + post_data.empty() && + (valid_url_callback.is_null() || valid_url_callback.Run(url))) { + if (toolbar_folder_index > path.size() && !path.empty()) { + NOTREACHED(); // error in parsing. + break; + } + + ImportedBookmarkEntry entry; + entry.creation_time = add_date; + entry.url = url; + entry.title = title; + + if (toolbar_folder_index) { + // The toolbar folder should be at the top level. + entry.in_toolbar = true; + entry.path.assign(path.begin() + toolbar_folder_index - 1, path.end()); + } else { + // Add this bookmark to the list of |bookmarks|. + if (!has_subfolder && !last_folder.empty()) { + path.push_back(last_folder); + last_folder.clear(); + } + entry.path.assign(path.begin(), path.end()); + } + bookmarks->push_back(entry); + + // Save the favicon. DataURLToFaviconUsage will handle the case where + // there is no favicon. + if (favicons) + DataURLToFaviconUsage(url, favicon, favicons); + + continue; + } + + // Bookmarks in sub-folder are encapsulated with
tag. + if (base::StartsWith(line, "
", base::CompareCase::INSENSITIVE_ASCII)) { + has_subfolder = true; + if (!last_folder.empty()) { + path.push_back(last_folder); + last_folder.clear(); + } + if (last_folder_on_toolbar && !toolbar_folder_index) + toolbar_folder_index = path.size(); + + // Mark next folder empty as initial state. + last_folder_is_empty = true; + } else if (base::StartsWith(line, "
", + base::CompareCase::INSENSITIVE_ASCII)) { + if (path.empty()) + break; // Mismatch
. + + base::string16 folder_title = path.back(); + path.pop_back(); + + if (last_folder_is_empty) { + // Empty folder should be added explicitly. + ImportedBookmarkEntry entry; + entry.is_folder = true; + entry.creation_time = last_folder_add_date; + entry.title = folder_title; + if (toolbar_folder_index) { + // The toolbar folder should be at the top level. + // Make sure we don't add the toolbar folder itself if it is empty. + if (toolbar_folder_index <= path.size()) { + entry.in_toolbar = true; + entry.path.assign(path.begin() + toolbar_folder_index - 1, + path.end()); + bookmarks->push_back(entry); + } + } else { + // Add this folder to the list of |bookmarks|. + entry.path.assign(path.begin(), path.end()); + bookmarks->push_back(entry); + } + + // Parent folder include current one, so it's not empty. + last_folder_is_empty = false; + } + + if (toolbar_folder_index > path.size()) + toolbar_folder_index = 0; + } + } +} + +bool CanImportURLAsSearchEngine(const GURL& url, + std::string* search_engine_url) { + std::string url_spec = url.possibly_invalid_spec(); + + if (url_spec.empty()) + return false; + + url_spec = net::UnescapeURLComponent( + url_spec, net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS); + + // Replace replacement terms ("%s") in |url_spec| with {searchTerms}. + url_spec = + TemplateURLRef::DisplayURLToURLRef(base::UTF8ToUTF16(url_spec)); + + TemplateURLData data; + data.SetURL(url_spec); + *search_engine_url = url_spec; + return TemplateURL(data).SupportsReplacement(SearchTermsData()); +} + +namespace internal { + +bool ParseCharsetFromLine(const std::string& line, std::string* charset) { + const char kCharset[] = "charset="; + if (base::StartsWith(line, "', end) + 1; + // If no end tag or start tag is broken, we skip to find the folder name. + if (end == std::string::npos || tag_end < arraysize(kFolderOpen)) + return false; + + base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), + base::OnStringConversionError::SKIP, folder_name); + *folder_name = net::UnescapeForHTML(*folder_name); + + std::string attribute_list = line.substr(arraysize(kFolderOpen), + tag_end - arraysize(kFolderOpen) - 1); + std::string value; + + // Add date + if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { + int64_t time; + base::StringToInt64(value, &time); + // Upper bound it at 32 bits. + if (0 < time && time < (1LL << 32)) + *add_date = base::Time::FromTimeT(time); + } + + if (GetAttribute(attribute_list, kToolbarFolderAttribute, &value) && + base::LowerCaseEqualsASCII(value, "true")) + *is_toolbar_folder = true; + else + *is_toolbar_folder = false; + + return true; +} + +bool ParseBookmarkFromLine(const std::string& lineDt, + const std::string& charset, + base::string16* title, + GURL* url, + GURL* favicon, + base::string16* shortcut, + base::Time* add_date, + base::string16* post_data) { + const char kItemOpen[] = "clear(); + *url = GURL(); + *favicon = GURL(); + shortcut->clear(); + post_data->clear(); + *add_date = base::Time(); + + if (!base::StartsWith(line, kItemOpen, base::CompareCase::SENSITIVE)) + return false; + + size_t end = line.find(kItemClose); + size_t tag_end = line.rfind('>', end) + 1; + if (end == std::string::npos || tag_end < arraysize(kItemOpen)) + return false; // No end tag or start tag is broken. + + std::string attribute_list = line.substr(arraysize(kItemOpen), + tag_end - arraysize(kItemOpen) - 1); + + // We don't import Live Bookmark folders, which is Firefox's RSS reading + // feature, since the user never necessarily bookmarked them and we don't + // have this feature to update their contents. + std::string value; + if (GetAttribute(attribute_list, kFeedURLAttribute, &value)) + return false; + + // Title + base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), + base::OnStringConversionError::SKIP, title); + *title = net::UnescapeForHTML(*title); + + // URL + if (GetAttribute(attribute_list, kHrefAttribute, &value)) { + base::string16 url16; + base::CodepageToUTF16(value, charset.c_str(), + base::OnStringConversionError::SKIP, &url16); + url16 = net::UnescapeForHTML(url16); + + *url = GURL(url16); + } + + // Favicon + if (GetAttribute(attribute_list, kIconAttribute, &value)) + *favicon = GURL(value); + + // Keyword + if (GetAttribute(attribute_list, kShortcutURLAttribute, &value)) { + base::CodepageToUTF16(value, charset.c_str(), + base::OnStringConversionError::SKIP, shortcut); + *shortcut = net::UnescapeForHTML(*shortcut); + } + + // Add date + if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { + int64_t time; + base::StringToInt64(value, &time); + // Upper bound it at 32 bits. + if (0 < time && time < (1LL << 32)) + *add_date = base::Time::FromTimeT(time); + } + + // Post data. + if (GetAttribute(attribute_list, kPostDataAttribute, &value)) { + base::CodepageToUTF16(value, charset.c_str(), + base::OnStringConversionError::SKIP, post_data); + *post_data = net::UnescapeForHTML(*post_data); + } + + return true; +} + +bool ParseMinimumBookmarkFromLine(const std::string& lineDt, + const std::string& charset, + base::string16* title, + GURL* url) { + const char kItemOpen[] = "clear(); + *url = GURL(); + + // Case-insensitive check of open tag. + if (!base::StartsWith(line, kItemOpen, base::CompareCase::INSENSITIVE_ASCII)) + return false; + + // Find any close tag. + size_t end = line.find(kItemClose); + size_t tag_end = line.rfind('>', end) + 1; + if (end == std::string::npos || tag_end < arraysize(kItemOpen)) + return false; // No end tag or start tag is broken. + + std::string attribute_list = line.substr(arraysize(kItemOpen), + tag_end - arraysize(kItemOpen) - 1); + + // Title + base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), + base::OnStringConversionError::SKIP, title); + *title = net::UnescapeForHTML(*title); + + // URL + std::string value; + if (GetAttribute(attribute_list, kHrefAttributeUpper, &value) || + GetAttribute(attribute_list, kHrefAttributeLower, &value)) { + if (charset.length() != 0) { + base::string16 url16; + base::CodepageToUTF16(value, charset.c_str(), + base::OnStringConversionError::SKIP, &url16); + url16 = net::UnescapeForHTML(url16); + + *url = GURL(url16); + } else { + *url = GURL(value); + } + } + + return true; +} + +} // namespace internal + +} // namespace bookmark_html_reader diff --git a/chromium_src/chrome/utility/importer/bookmark_html_reader.h b/chromium_src/chrome/utility/importer/bookmark_html_reader.h new file mode 100644 index 0000000000..8150347cef --- /dev/null +++ b/chromium_src/chrome/utility/importer/bookmark_html_reader.h @@ -0,0 +1,108 @@ +// Copyright 2013 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. + +#ifndef CHROME_UTILITY_IMPORTER_BOOKMARK_HTML_READER_H_ +#define CHROME_UTILITY_IMPORTER_BOOKMARK_HTML_READER_H_ + +#include +#include + +#include "base/callback_forward.h" +#include "base/strings/string16.h" +#include "chrome/common/importer/importer_data_types.h" +#include "components/favicon_base/favicon_usage_data.h" + +class GURL; +struct ImportedBookmarkEntry; + +namespace base { +class FilePath; +class Time; +} + +namespace bookmark_html_reader { + +// Imports the bookmarks from the specified file. +// +// |cancellation_callback| is polled to query if the import should be cancelled; +// if it returns |true| at any time the import will be cancelled. If +// |cancellation_callback| is a null callback the import will run to completion. +// +// |valid_url_callback| is called to determine if a specified URL is valid for +// import; it returns |true| if it is. If |valid_url_callback| is a null +// callback, all URLs are considered to be valid. +// +// |file_path| is the path of the file on disk to import. +// +// |bookmarks| is a pointer to a vector, which is filled with the imported +// bookmarks. It may not be NULL. +// +// |search_engines| is a pointer to a vector, which is filled with the imported +// search engines. +// +// |favicons| is a pointer to a vector, which is filled with the favicons of +// imported bookmarks. It may be NULL, in which case favicons are not imported. +void ImportBookmarksFile( + const base::Callback& cancellation_callback, + const base::Callback& valid_url_callback, + const base::FilePath& file_path, + std::vector* bookmarks, + std::vector* search_engines, + favicon_base::FaviconUsageDataList* favicons); + +// Returns true if |url| should be imported as a search engine, i.e. because it +// has replacement terms. Chrome treats such bookmarks as search engines rather +// than true bookmarks. +bool CanImportURLAsSearchEngine(const GURL& url, + std::string* search_engine_url); + +namespace internal { + +// The file format that BookmarkHTMLReader parses starts with a heading +// tag, which contains its title. All bookmarks and sub-folders follow, +// bracketed by a
tag: +//

title

+//

+// ... container ... +//

+// And a bookmark is presented by a tag: +//

name +// Reference: http://kb.mozillazine.org/Bookmarks.html + +bool ParseCharsetFromLine(const std::string& line, + std::string* charset); +bool ParseFolderNameFromLine(const std::string& line, + const std::string& charset, + base::string16* folder_name, + bool* is_toolbar_folder, + base::Time* add_date); +// See above, this will also put the data: URL of the favicon into |*favicon| +// if there is a favicon given. |post_data| is set for POST base keywords to +// the contents of the actual POST (with %s for the search term). +bool ParseBookmarkFromLine(const std::string& line, + const std::string& charset, + base::string16* title, + GURL* url, + GURL* favicon, + base::string16* shortcut, + base::Time* add_date, + base::string16* post_data); +// Save bookmarks imported from browsers with Firefox 2 compatible bookmark +// systems such as Epiphany. This bookmark format is the same as that of the +// basic Firefox 2 bookmark, but it misses additional properties and uses +// lower-case tag: +// ...

Bookmarks

+//
name
+//
name
+//
+bool ParseMinimumBookmarkFromLine(const std::string& line, + const std::string& charset, + base::string16* title, + GURL* url); + +} // namespace internal + +} // namespace bookmark_html_reader + +#endif // CHROME_UTILITY_IMPORTER_BOOKMARK_HTML_READER_H_ diff --git a/chromium_src/chrome/utility/importer/bookmarks_file_importer.cc b/chromium_src/chrome/utility/importer/bookmarks_file_importer.cc new file mode 100644 index 0000000000..b943ca220d --- /dev/null +++ b/chromium_src/chrome/utility/importer/bookmarks_file_importer.cc @@ -0,0 +1,130 @@ +// Copyright 2013 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. + +/** + * This is not a straight copy from chromium src, in particular + * we remove some functionality. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/utility/importer/bookmarks_file_importer.h" + +#include + +#include "base/bind.h" +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_bridge.h" +#include "chrome/common/importer/importer_data_types.h" +// #include "chrome/common/url_constants.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/utility/importer/bookmark_html_reader.h" +#include "components/favicon_base/favicon_usage_data.h" +// #include "components/url_formatter/url_fixer.h" +#include "content/public/common/url_constants.h" + +namespace { + +bool IsImporterCancelled(BookmarksFileImporter* importer) { + return importer->cancelled(); +} + +} // namespace + +namespace internal { + +// Returns true if |url| has a valid scheme that we allow to import. We +// filter out the URL with a unsupported scheme. +bool CanImportURL(const GURL& url) { + // The URL is not valid. + if (!url.is_valid()) + return false; + + // Filter out the URLs with unsupported schemes. + const char* const kInvalidSchemes[] = {"wyciwyg", "place"}; + for (size_t i = 0; i < arraysize(kInvalidSchemes); ++i) { + if (url.SchemeIs(kInvalidSchemes[i])) + return false; + } + + // Check if |url| is about:blank. + if (url == GURL(url::kAboutBlankURL)) + return true; + + /* + // If |url| starts with chrome:// or about:, check if it's one of the URLs + // that we support. + if (url.SchemeIs(content::kChromeUIScheme) || + url.SchemeIs(url::kAboutScheme)) { + if (url.host() == chrome::kChromeUIUberHost || + url.host() == chrome::kChromeUIAboutHost) + return true; + + GURL fixed_url(url_formatter::FixupURL(url.spec(), std::string())); + for (size_t i = 0; i < chrome::kNumberOfChromeHostURLs; ++i) { + if (fixed_url.DomainIs(chrome::kChromeHostURLs[i])) + return true; + } + + for (int i = 0; i < chrome::kNumberOfChromeDebugURLs; ++i) { + if (fixed_url == GURL(chrome::kChromeDebugURLs[i])) + return true; + } + + // If url has either chrome:// or about: schemes but wasn't found in the + // above lists, it means we don't support it, so we don't allow the user + // to import it. + return false; + } + */ + + // Otherwise, we assume the url has a valid (importable) scheme. + return true; +} + +} // namespace internal + +BookmarksFileImporter::BookmarksFileImporter() {} + +BookmarksFileImporter::~BookmarksFileImporter() {} + +void BookmarksFileImporter::StartImport( + const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) { + // The only thing this importer can import is a bookmarks file, aka + // "favorites". + DCHECK_EQ(importer::FAVORITES, items); + + bridge->NotifyStarted(); + bridge->NotifyItemStarted(importer::FAVORITES); + + std::vector bookmarks; + std::vector search_engines; + favicon_base::FaviconUsageDataList favicons; + + bookmark_html_reader::ImportBookmarksFile( + base::Bind(IsImporterCancelled, base::Unretained(this)), + base::Bind(internal::CanImportURL), + source_profile.source_path, + &bookmarks, + &search_engines, + &favicons); + + if (!bookmarks.empty() && !cancelled()) { + base::string16 first_folder_name = + // bridge->GetLocalizedString(IDS_BOOKMARK_GROUP); + base::UTF8ToUTF16("Imported from HTML"); + bridge->AddBookmarks(bookmarks, first_folder_name); + } + if (!search_engines.empty()) + bridge->SetKeywords(search_engines, false); + if (!favicons.empty()) + bridge->SetFavicons(favicons); + + bridge->NotifyItemEnded(importer::FAVORITES); + bridge->NotifyEnded(); +} diff --git a/chromium_src/chrome/utility/importer/bookmarks_file_importer.h b/chromium_src/chrome/utility/importer/bookmarks_file_importer.h new file mode 100644 index 0000000000..ce66ee1369 --- /dev/null +++ b/chromium_src/chrome/utility/importer/bookmarks_file_importer.h @@ -0,0 +1,29 @@ +// Copyright 2013 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. + +#ifndef CHROME_UTILITY_IMPORTER_BOOKMARKS_FILE_IMPORTER_H_ +#define CHROME_UTILITY_IMPORTER_BOOKMARKS_FILE_IMPORTER_H_ + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "chrome/utility/importer/importer.h" + +// Importer for bookmarks files. +class BookmarksFileImporter : public Importer { + public: + BookmarksFileImporter(); + + void StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) override; + + private: + ~BookmarksFileImporter() override; + + DISALLOW_COPY_AND_ASSIGN(BookmarksFileImporter); +}; + +#endif // CHROME_UTILITY_IMPORTER_BOOKMARKS_FILE_IMPORTER_H_ diff --git a/chromium_src/chrome/utility/importer/edge_database_reader_win.cc b/chromium_src/chrome/utility/importer/edge_database_reader_win.cc new file mode 100644 index 0000000000..dd5d45515a --- /dev/null +++ b/chromium_src/chrome/utility/importer/edge_database_reader_win.cc @@ -0,0 +1,261 @@ +// Copyright 2015 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/utility/importer/edge_database_reader_win.h" + +#include +#include +#include + +#include + +#include "base/memory/ptr_util.h" + +namespace { + +// This is an arbitary size chosen for the database error message buffer. +const size_t kErrorMessageSize = 1024; +// This is the page size of the Edge data. It's unlikely to change. +const JET_API_PTR kEdgeDatabasePageSize = 8192; +// This is the code page value for a Unicode (UCS-2) column. +const unsigned short kJetUnicodeCodePage = 1200; + +template +bool ValidateAndConvertValueGeneric(const JET_COLTYP match_column_type, + const JET_COLTYP column_type, + const std::vector& column_data, + T* value) { + if ((column_type == match_column_type) && (column_data.size() == sizeof(T))) { + memcpy(value, &column_data[0], sizeof(T)); + return true; + } + return false; +} + +bool ValidateAndConvertValue(const JET_COLTYP column_type, + const std::vector& column_data, + bool* value) { + if ((column_type == JET_coltypBit) && (column_data.size() == 1)) { + *value = (column_data[0] & 1) == 1; + return true; + } + return false; +} + +bool ValidateAndConvertValue(const JET_COLTYP column_type, + const std::vector& column_data, + base::string16* value) { + if ((column_type == JET_coltypLongText) && + ((column_data.size() % sizeof(base::char16)) == 0)) { + base::string16& value_ref = *value; + size_t char_length = column_data.size() / sizeof(base::char16); + value_ref.resize(char_length); + memcpy(&value_ref[0], &column_data[0], column_data.size()); + // Remove any trailing NUL characters. + while (char_length > 0) { + if (value_ref[char_length - 1]) + break; + char_length--; + } + value_ref.resize(char_length); + return true; + } + return false; +} + +bool ValidateAndConvertValue(const JET_COLTYP column_type, + const std::vector& column_data, + GUID* value) { + return ValidateAndConvertValueGeneric(JET_coltypGUID, column_type, + column_data, value); +} + +bool ValidateAndConvertValue(const JET_COLTYP column_type, + const std::vector& column_data, + int32_t* value) { + return ValidateAndConvertValueGeneric(JET_coltypLong, column_type, + column_data, value); +} + +bool ValidateAndConvertValue(const JET_COLTYP column_type, + const std::vector& column_data, + int64_t* value) { + return ValidateAndConvertValueGeneric(JET_coltypLongLong, column_type, + column_data, value); +} + +bool ValidateAndConvertValue(const JET_COLTYP column_type, + const std::vector& column_data, + FILETIME* value) { + return ValidateAndConvertValueGeneric(JET_coltypLongLong, column_type, + column_data, value); +} + +bool ValidateAndConvertValue(const JET_COLTYP column_type, + const std::vector& column_data, + uint32_t* value) { + return ValidateAndConvertValueGeneric(JET_coltypUnsignedLong, column_type, + column_data, value); +} + +} // namespace + +base::string16 EdgeErrorObject::GetErrorMessage() const { + WCHAR error_message[kErrorMessageSize] = {}; + JET_API_PTR err = last_error_; + JET_ERR result = JetGetSystemParameter(JET_instanceNil, JET_sesidNil, + JET_paramErrorToString, &err, + error_message, sizeof(error_message)); + if (result != JET_errSuccess) + return L""; + + return error_message; +} + +bool EdgeErrorObject::SetLastError(JET_ERR error) { + last_error_ = error; + return error == JET_errSuccess; +} + +EdgeDatabaseTableEnumerator::EdgeDatabaseTableEnumerator( + const base::string16& table_name, + JET_SESID session_id, + JET_TABLEID table_id) + : table_id_(table_id), table_name_(table_name), session_id_(session_id) {} + +EdgeDatabaseTableEnumerator::~EdgeDatabaseTableEnumerator() { + if (table_id_ != JET_tableidNil) + JetCloseTable(session_id_, table_id_); +} + +bool EdgeDatabaseTableEnumerator::Reset() { + return SetLastError(JetMove(session_id_, table_id_, JET_MoveFirst, 0)); +} + +bool EdgeDatabaseTableEnumerator::Next() { + return SetLastError(JetMove(session_id_, table_id_, JET_MoveNext, 0)); +} + +template +bool EdgeDatabaseTableEnumerator::RetrieveColumn( + const base::string16& column_name, + T* value) { + const JET_COLUMNBASE& column_base = GetColumnByName(column_name); + if (column_base.cbMax == 0) { + SetLastError(JET_errColumnNotFound); + return false; + } + if (column_base.coltyp == JET_coltypLongText && + column_base.cp != kJetUnicodeCodePage) { + SetLastError(JET_errInvalidColumnType); + return false; + } + std::vector column_data(column_base.cbMax); + unsigned long actual_size = 0; + JET_ERR err = JetRetrieveColumn(session_id_, table_id_, column_base.columnid, + &column_data[0], column_data.size(), + &actual_size, 0, nullptr); + SetLastError(err); + if (err != JET_errSuccess && err != JET_wrnColumnNull) { + return false; + } + + if (err == JET_errSuccess) { + column_data.resize(actual_size); + if (!ValidateAndConvertValue(column_base.coltyp, column_data, value)) { + SetLastError(JET_errInvalidColumnType); + return false; + } + } else { + *value = T(); + } + + return true; +} + +// Explicitly instantiate implementations of RetrieveColumn for various types. +template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, + bool*); +template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, + FILETIME*); +template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, + GUID*); +template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, + int32_t*); +template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, + int64_t*); +template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, + base::string16*); +template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, + uint32_t*); + +const JET_COLUMNBASE& EdgeDatabaseTableEnumerator::GetColumnByName( + const base::string16& column_name) { + auto found_col = columns_by_name_.find(column_name); + if (found_col == columns_by_name_.end()) { + JET_COLUMNBASE column_base = {}; + column_base.cbStruct = sizeof(JET_COLUMNBASE); + if (!SetLastError(JetGetTableColumnInfo( + session_id_, table_id_, column_name.c_str(), &column_base, + sizeof(column_base), JET_ColInfoBase))) { + // 0 indicates an invalid column. + column_base.cbMax = 0; + } + columns_by_name_[column_name] = column_base; + found_col = columns_by_name_.find(column_name); + } + return found_col->second; +} + +EdgeDatabaseReader::~EdgeDatabaseReader() { + // We don't need to collect other ID handles, terminating instance + // is enough to shut the entire session down. + if (instance_id_ != JET_instanceNil) + JetTerm(instance_id_); +} + +bool EdgeDatabaseReader::OpenDatabase(const base::string16& database_file) { + if (IsOpen()) { + SetLastError(JET_errOneDatabasePerSession); + return false; + } + if (!SetLastError(JetSetSystemParameter(nullptr, JET_sesidNil, + JET_paramDatabasePageSize, + kEdgeDatabasePageSize, nullptr))) + return false; + if (!SetLastError(JetCreateInstance(&instance_id_, L"EdgeDataImporter"))) + return false; + if (!SetLastError(JetSetSystemParameter(&instance_id_, JET_sesidNil, + JET_paramRecovery, 0, L"Off"))) + return false; + if (!SetLastError(JetInit(&instance_id_))) + return false; + if (!SetLastError( + JetBeginSession(instance_id_, &session_id_, nullptr, nullptr))) + return false; + if (!SetLastError(JetAttachDatabase2(session_id_, database_file.c_str(), 0, + JET_bitDbReadOnly))) + return false; + if (!SetLastError(JetOpenDatabase(session_id_, database_file.c_str(), nullptr, + &db_id_, JET_bitDbReadOnly))) + return false; + return true; +} + +std::unique_ptr +EdgeDatabaseReader::OpenTableEnumerator(const base::string16& table_name) { + JET_TABLEID table_id; + + if (!IsOpen()) { + SetLastError(JET_errDatabaseNotFound); + return nullptr; + } + + if (!SetLastError(JetOpenTable(session_id_, db_id_, table_name.c_str(), + nullptr, 0, JET_bitTableReadOnly, &table_id))) + return nullptr; + + return base::WrapUnique( + new EdgeDatabaseTableEnumerator(table_name, session_id_, table_id)); +} diff --git a/chromium_src/chrome/utility/importer/edge_database_reader_win.h b/chromium_src/chrome/utility/importer/edge_database_reader_win.h new file mode 100644 index 0000000000..623918a315 --- /dev/null +++ b/chromium_src/chrome/utility/importer/edge_database_reader_win.h @@ -0,0 +1,95 @@ +// Copyright 2015 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. + +#ifndef CHROME_UTILITY_IMPORTER_EDGE_DATABASE_READER_WIN_H_ +#define CHROME_UTILITY_IMPORTER_EDGE_DATABASE_READER_WIN_H_ + +#define JET_UNICODE +#include +#undef JET_UNICODE + +#include +#include + +#include "base/macros.h" +#include "base/strings/string16.h" + +class EdgeErrorObject { + public: + EdgeErrorObject() : last_error_(JET_errSuccess) {} + + // Get the last error converted to a descriptive string. + base::string16 GetErrorMessage() const; + // Get the last error value. + JET_ERR last_error() const { return last_error_; } + + protected: + // This function returns true if the passed error parameter is equal + // to JET_errSuccess + bool SetLastError(JET_ERR error); + + private: + JET_ERR last_error_; + + DISALLOW_COPY_AND_ASSIGN(EdgeErrorObject); +}; + +class EdgeDatabaseTableEnumerator : public EdgeErrorObject { + public: + EdgeDatabaseTableEnumerator(const base::string16& table_name, + JET_SESID session_id, + JET_TABLEID table_id); + + ~EdgeDatabaseTableEnumerator(); + + const base::string16& table_name() { return table_name_; } + + // Reset the enumerator to the start of the table. Returns true if successful. + bool Reset(); + // Move to the next row in the table. Returns false on error or no more rows. + bool Next(); + + // Retrieve a column's data value. If a NULL is encountered in the column the + // default value for the template type is placed in |value|. + template + bool RetrieveColumn(const base::string16& column_name, T* value); + + private: + const JET_COLUMNBASE& GetColumnByName(const base::string16& column_name); + + std::map columns_by_name_; + JET_TABLEID table_id_; + base::string16 table_name_; + JET_SESID session_id_; + + DISALLOW_COPY_AND_ASSIGN(EdgeDatabaseTableEnumerator); +}; + +class EdgeDatabaseReader : public EdgeErrorObject { + public: + EdgeDatabaseReader() + : db_id_(JET_dbidNil), + instance_id_(JET_instanceNil), + session_id_(JET_sesidNil) {} + + ~EdgeDatabaseReader(); + + // Open the database from a file path. Returns true on success. + bool OpenDatabase(const base::string16& database_file); + + // Open a row enumerator for a specified table. Returns a nullptr on error. + std::unique_ptr OpenTableEnumerator( + const base::string16& table_name); + + private: + bool IsOpen() { return instance_id_ != JET_instanceNil; } + + JET_DBID db_id_; + JET_INSTANCE instance_id_; + JET_SESID session_id_; + + DISALLOW_COPY_AND_ASSIGN(EdgeDatabaseReader); +}; + +#endif // CHROME_UTILITY_IMPORTER_EDGE_DATABASE_READER_WIN_H_ diff --git a/chromium_src/chrome/utility/importer/edge_importer_win.cc b/chromium_src/chrome/utility/importer/edge_importer_win.cc new file mode 100644 index 0000000000..d8e84c455e --- /dev/null +++ b/chromium_src/chrome/utility/importer/edge_importer_win.cc @@ -0,0 +1,297 @@ +// Copyright 2015 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. + +/** + * This is not a straight copy from chromium src, in particular + * we remove some functionality. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/utility/importer/edge_importer_win.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "base/win/windows_version.h" +#include "chrome/common/importer/edge_importer_utils_win.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_bridge.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/utility/importer/edge_database_reader_win.h" +#include "chrome/utility/importer/favicon_reencode.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/gurl.h" + +namespace { + +// Toolbar favorites are placed under this special folder name. +const base::char16 kFavoritesBarTitle[] = L"_Favorites_Bar_"; +const base::char16 kSpartanDatabaseFile[] = L"spartan.edb"; + +struct EdgeFavoriteEntry { + EdgeFavoriteEntry() + : is_folder(false), + order_number(0), + item_id(GUID_NULL), + parent_id(GUID_NULL) {} + + base::string16 title; + GURL url; + base::FilePath favicon_file; + bool is_folder; + int64_t order_number; + base::Time date_updated; + GUID item_id; + GUID parent_id; + + std::vector children; + + ImportedBookmarkEntry ToBookmarkEntry( + bool in_toolbar, + const std::vector& path) const { + ImportedBookmarkEntry entry; + entry.in_toolbar = in_toolbar; + entry.is_folder = is_folder; + entry.url = url; + entry.path = path; + entry.title = title; + entry.creation_time = date_updated; + return entry; + } +}; + +struct EdgeFavoriteEntryComparator { + bool operator()(const EdgeFavoriteEntry* lhs, + const EdgeFavoriteEntry* rhs) const { + return std::tie(lhs->order_number, lhs->title) < + std::tie(rhs->order_number, rhs->title); + } +}; + +// The name of the database file is spartan.edb, however it isn't clear how +// the intermediate path between the DataStore and the database is generated. +// Therefore we just do a simple recursive search until we find a matching name. +base::FilePath FindSpartanDatabase(const base::FilePath& profile_path) { + base::FilePath data_path = + profile_path.empty() ? importer::GetEdgeDataFilePath() : profile_path; + if (data_path.empty()) + return base::FilePath(); + + base::FileEnumerator enumerator(data_path.Append(L"DataStore\\Data"), true, + base::FileEnumerator::FILES); + base::FilePath path = enumerator.Next(); + while (!path.empty()) { + if (base::EqualsCaseInsensitiveASCII(path.BaseName().value(), + kSpartanDatabaseFile)) + return path; + path = enumerator.Next(); + } + return base::FilePath(); +} + +struct GuidComparator { + bool operator()(const GUID& a, const GUID& b) const { + return memcmp(&a, &b, sizeof(a)) < 0; + } +}; + +bool ReadFaviconData(const base::FilePath& file, + std::vector* data) { + std::string image_data; + if (!base::ReadFileToString(file, &image_data)) + return false; + + const unsigned char* ptr = + reinterpret_cast(image_data.c_str()); + return importer::ReencodeFavicon(ptr, image_data.size(), data); +} + +void BuildBookmarkEntries(const EdgeFavoriteEntry& current_entry, + bool is_toolbar, + std::vector* bookmarks, + favicon_base::FaviconUsageDataList* favicons, + std::vector* path) { + for (const EdgeFavoriteEntry* entry : current_entry.children) { + if (entry->is_folder) { + // If the favorites bar then load all children as toolbar items. + if (base::EqualsCaseInsensitiveASCII(entry->title, kFavoritesBarTitle)) { + // Replace name with Links similar to IE. + path->push_back(L"Links"); + BuildBookmarkEntries(*entry, true, bookmarks, favicons, path); + path->pop_back(); + } else { + path->push_back(entry->title); + BuildBookmarkEntries(*entry, is_toolbar, bookmarks, favicons, path); + path->pop_back(); + } + } else { + bookmarks->push_back(entry->ToBookmarkEntry(is_toolbar, *path)); + favicon_base::FaviconUsageData favicon; + if (entry->url.is_valid() && !entry->favicon_file.empty() && + ReadFaviconData(entry->favicon_file, &favicon.png_data)) { + // As the database doesn't provide us a favicon URL we'll fake one. + GURL::Replacements path_replace; + path_replace.SetPathStr("/favicon.ico"); + favicon.favicon_url = + entry->url.GetWithEmptyPath().ReplaceComponents(path_replace); + favicon.urls.insert(entry->url); + favicons->push_back(favicon); + } + } + } +} + +} // namespace + +EdgeImporter::EdgeImporter() {} + +void EdgeImporter::StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) { + bridge_ = bridge; + bridge_->NotifyStarted(); + source_path_ = source_profile.source_path; + + if ((items & importer::FAVORITES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::FAVORITES); + ImportFavorites(); + bridge_->NotifyItemEnded(importer::FAVORITES); + } + bridge_->NotifyEnded(); +} + +EdgeImporter::~EdgeImporter() {} + +void EdgeImporter::ImportFavorites() { + std::vector bookmarks; + favicon_base::FaviconUsageDataList favicons; + ParseFavoritesDatabase(&bookmarks, &favicons); + + if (!bookmarks.empty() && !cancelled()) { + const base::string16& first_folder_name = + // l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_EDGE); + base::UTF8ToUTF16("Imported from Edge"); + bridge_->AddBookmarks(bookmarks, first_folder_name); + } + if (!favicons.empty() && !cancelled()) + bridge_->SetFavicons(favicons); +} + +// From Edge 13 (released with Windows 10 TH2), Favorites are stored in a JET +// database within the Edge local storage. The import uses the ESE library to +// open and read the data file. The data is stored in a Favorites table with +// the following schema. +// Column Name Column Type +// ------------------------------------------ +// DateUpdated LongLong - FILETIME +// FaviconFile LongText - Relative path +// HashedUrl ULong +// IsDeleted Bit +// IsFolder Bit +// ItemId Guid +// OrderNumber LongLong +// ParentId Guid +// RoamDisabled Bit +// RowId Long +// Title LongText +// URL LongText +void EdgeImporter::ParseFavoritesDatabase( + std::vector* bookmarks, + favicon_base::FaviconUsageDataList* favicons) { + base::FilePath database_path = FindSpartanDatabase(source_path_); + if (database_path.empty()) + return; + + EdgeDatabaseReader database; + if (!database.OpenDatabase(database_path.value())) { + DVLOG(1) << "Error opening database " << database.GetErrorMessage(); + return; + } + + std::unique_ptr enumerator = + database.OpenTableEnumerator(L"Favorites"); + if (!enumerator) { + DVLOG(1) << "Error opening database table " << database.GetErrorMessage(); + return; + } + + if (!enumerator->Reset()) + return; + + std::map database_entries; + base::FilePath favicon_base = + source_path_.empty() ? importer::GetEdgeDataFilePath() : source_path_; + favicon_base = favicon_base.Append(L"DataStore"); + + do { + EdgeFavoriteEntry entry; + bool is_deleted = false; + if (!enumerator->RetrieveColumn(L"IsDeleted", &is_deleted)) + continue; + if (is_deleted) + continue; + if (!enumerator->RetrieveColumn(L"IsFolder", &entry.is_folder)) + continue; + base::string16 url; + if (!enumerator->RetrieveColumn(L"URL", &url)) + continue; + entry.url = GURL(url); + if (!entry.is_folder && !entry.url.is_valid()) + continue; + if (!enumerator->RetrieveColumn(L"Title", &entry.title)) + continue; + base::string16 favicon_file; + if (!enumerator->RetrieveColumn(L"FaviconFile", &favicon_file)) + continue; + if (!favicon_file.empty()) + entry.favicon_file = favicon_base.Append(favicon_file); + if (!enumerator->RetrieveColumn(L"ParentId", &entry.parent_id)) + continue; + if (!enumerator->RetrieveColumn(L"ItemId", &entry.item_id)) + continue; + if (!enumerator->RetrieveColumn(L"OrderNumber", &entry.order_number)) + continue; + FILETIME data_updated; + if (!enumerator->RetrieveColumn(L"DateUpdated", &data_updated)) + continue; + entry.date_updated = base::Time::FromFileTime(data_updated); + database_entries[entry.item_id] = entry; + } while (enumerator->Next() && !cancelled()); + + // Build simple tree. + EdgeFavoriteEntry root_entry; + for (auto& entry : database_entries) { + auto found_parent = database_entries.find(entry.second.parent_id); + if (found_parent == database_entries.end() || + !found_parent->second.is_folder) { + root_entry.children.push_back(&entry.second); + } else { + found_parent->second.children.push_back(&entry.second); + } + } + // With tree built sort the children of each node including the root. + std::sort(root_entry.children.begin(), root_entry.children.end(), + EdgeFavoriteEntryComparator()); + for (auto& entry : database_entries) { + std::sort(entry.second.children.begin(), entry.second.children.end(), + EdgeFavoriteEntryComparator()); + } + std::vector path; + BuildBookmarkEntries(root_entry, false, bookmarks, favicons, &path); +} diff --git a/chromium_src/chrome/utility/importer/edge_importer_win.h b/chromium_src/chrome/utility/importer/edge_importer_win.h new file mode 100644 index 0000000000..c93c38a05d --- /dev/null +++ b/chromium_src/chrome/utility/importer/edge_importer_win.h @@ -0,0 +1,45 @@ +// Copyright 2015 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. + +#ifndef CHROME_UTILITY_IMPORTER_EDGE_IMPORTER_WIN_H_ +#define CHROME_UTILITY_IMPORTER_EDGE_IMPORTER_WIN_H_ + +#include + +#include + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "chrome/utility/importer/importer.h" +#include "components/favicon_base/favicon_usage_data.h" + +struct ImportedBookmarkEntry; + +class EdgeImporter : public Importer { + public: + EdgeImporter(); + + // Importer: + void StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) override; + + private: + ~EdgeImporter() override; + + void ImportFavorites(); + // This function will read the favorites from the spartan database storing + // the bookmark items in |bookmarks| and favicon information in |favicons|. + void ParseFavoritesDatabase(std::vector* bookmarks, + favicon_base::FaviconUsageDataList* favicons); + + // Edge does not have source path. It's used in unit tests only for providing + // a fake source for the spartan database location. + base::FilePath source_path_; + + DISALLOW_COPY_AND_ASSIGN(EdgeImporter); +}; + +#endif // CHROME_UTILITY_IMPORTER_EDGE_IMPORTER_WIN_H_ diff --git a/chromium_src/chrome/utility/importer/external_process_importer_bridge.cc b/chromium_src/chrome/utility/importer/external_process_importer_bridge.cc new file mode 100644 index 0000000000..c27d2c792a --- /dev/null +++ b/chromium_src/chrome/utility/importer/external_process_importer_bridge.cc @@ -0,0 +1,241 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add SetCookies. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/utility/importer/external_process_importer_bridge.h" + +#include "base/bind.h" +#include "base/debug/dump_without_crashing.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task_runner.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_data_types.h" +#include "chrome/common/importer/profile_import_process_messages.h" +#include "components/autofill/core/common/password_form.h" +#include "ipc/ipc_sender.h" + +namespace { + +// Rather than sending all import items over IPC at once we chunk them into +// separate requests. This avoids the case of a large import causing +// oversized IPC messages. +const int kNumBookmarksToSend = 100; +const int kNumHistoryRowsToSend = 100; +const int kNumFaviconsToSend = 100; +const int kNumAutofillFormDataToSend = 100; +const int kNumCookiesToSend = 100; + +} // namespace + +ExternalProcessImporterBridge::ExternalProcessImporterBridge( + const base::DictionaryValue& localized_strings, + IPC::Sender* sender, + base::TaskRunner* task_runner) + : sender_(sender), + task_runner_(task_runner) { + // Bridge needs to make its own copy because OS 10.6 autoreleases the + // localized_strings value that is passed in (see http://crbug.com/46003 ). + localized_strings_.reset(localized_strings.DeepCopy()); +} + +void ExternalProcessImporterBridge::AddBookmarks( + const std::vector& bookmarks, + const base::string16& first_folder_name) { + Send(new ProfileImportProcessHostMsg_NotifyBookmarksImportStart( + first_folder_name, bookmarks.size())); + + // |bookmarks_left| is required for the checks below as Windows has a + // Debug bounds-check which prevents pushing an iterator beyond its end() + // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|). + int bookmarks_left = bookmarks.end() - bookmarks.begin(); + for (std::vector::const_iterator it = + bookmarks.begin(); it < bookmarks.end();) { + std::vector bookmark_group; + std::vector::const_iterator end_group = + it + std::min(bookmarks_left, kNumBookmarksToSend); + bookmark_group.assign(it, end_group); + + Send(new ProfileImportProcessHostMsg_NotifyBookmarksImportGroup( + bookmark_group)); + bookmarks_left -= end_group - it; + it = end_group; + } + DCHECK_EQ(0, bookmarks_left); +} + +void ExternalProcessImporterBridge::AddHomePage(const GURL& home_page) { + Send(new ProfileImportProcessHostMsg_NotifyHomePageImportReady(home_page)); +} + +#if defined(OS_WIN) +void ExternalProcessImporterBridge::AddIE7PasswordInfo( + const importer::ImporterIE7PasswordInfo& password_info) { + Send(new ProfileImportProcessHostMsg_NotifyIE7PasswordInfo(password_info)); +} +#endif + +void ExternalProcessImporterBridge::SetFavicons( + const favicon_base::FaviconUsageDataList& favicons) { + Send(new ProfileImportProcessHostMsg_NotifyFaviconsImportStart( + favicons.size())); + + // |favicons_left| is required for the checks below as Windows has a + // Debug bounds-check which prevents pushing an iterator beyond its end() + // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|). + int favicons_left = favicons.end() - favicons.begin(); + for (favicon_base::FaviconUsageDataList::const_iterator it = favicons.begin(); + it < favicons.end();) { + favicon_base::FaviconUsageDataList favicons_group; + favicon_base::FaviconUsageDataList::const_iterator end_group = + it + std::min(favicons_left, kNumFaviconsToSend); + favicons_group.assign(it, end_group); + + Send(new ProfileImportProcessHostMsg_NotifyFaviconsImportGroup( + favicons_group)); + favicons_left -= end_group - it; + it = end_group; + } + DCHECK_EQ(0, favicons_left); +} + +void ExternalProcessImporterBridge::SetHistoryItems( + const std::vector& rows, + importer::VisitSource visit_source) { + Send(new ProfileImportProcessHostMsg_NotifyHistoryImportStart(rows.size())); + + // |rows_left| is required for the checks below as Windows has a + // Debug bounds-check which prevents pushing an iterator beyond its end() + // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|). + int rows_left = rows.end() - rows.begin(); + for (std::vector::const_iterator it = rows.begin(); + it < rows.end();) { + std::vector row_group; + std::vector::const_iterator end_group = + it + std::min(rows_left, kNumHistoryRowsToSend); + row_group.assign(it, end_group); + + Send(new ProfileImportProcessHostMsg_NotifyHistoryImportGroup( + row_group, visit_source)); + rows_left -= end_group - it; + it = end_group; + } + DCHECK_EQ(0, rows_left); +} + +void ExternalProcessImporterBridge::SetKeywords( + const std::vector& search_engines, + bool unique_on_host_and_path) { + Send(new ProfileImportProcessHostMsg_NotifyKeywordsReady( + search_engines, unique_on_host_and_path)); +} + +void ExternalProcessImporterBridge::SetFirefoxSearchEnginesXMLData( + const std::vector& search_engine_data) { + Send(new ProfileImportProcessHostMsg_NotifyFirefoxSearchEngData( + search_engine_data)); +} + +void ExternalProcessImporterBridge::SetPasswordForm( + const autofill::PasswordForm& form) { + Send(new ProfileImportProcessHostMsg_NotifyPasswordFormReady(form)); +} + +void ExternalProcessImporterBridge::SetAutofillFormData( + const std::vector& entries) { + Send(new ProfileImportProcessHostMsg_AutofillFormDataImportStart( + entries.size())); + + // |autofill_form_data_entries_left| is required for the checks below as + // Windows has a Debug bounds-check which prevents pushing an iterator beyond + // its end() (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == + // s.end()|). + int autofill_form_data_entries_left = entries.end() - entries.begin(); + for (std::vector::const_iterator it = + entries.begin(); + it < entries.end();) { + std::vector autofill_form_data_entry_group; + std::vector::const_iterator end_group = + it + + std::min(autofill_form_data_entries_left, kNumAutofillFormDataToSend); + autofill_form_data_entry_group.assign(it, end_group); + + Send(new ProfileImportProcessHostMsg_AutofillFormDataImportGroup( + autofill_form_data_entry_group)); + autofill_form_data_entries_left -= end_group - it; + it = end_group; + } + DCHECK_EQ(0, autofill_form_data_entries_left); +} + +void ExternalProcessImporterBridge::SetCookies( + const std::vector& cookies) { + Send(new ProfileImportProcessHostMsg_NotifyCookiesImportStart( + cookies.size())); + + // |cookies_left| is required for the checks below as Windows has a + // Debug bounds-check which prevents pushing an iterator beyond its end() + // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|). + int cookies_left = cookies.end() - cookies.begin(); + for (std::vector::const_iterator it = + cookies.begin(); it < cookies.end();) { + std::vector cookies_group; + std::vector::const_iterator end_group = + it + std::min(cookies_left, kNumCookiesToSend); + cookies_group.assign(it, end_group); + + Send(new ProfileImportProcessHostMsg_NotifyCookiesImportGroup( + cookies_group)); + cookies_left -= end_group - it; + it = end_group; + } + DCHECK_EQ(0, cookies_left); +} + +void ExternalProcessImporterBridge::NotifyStarted() { + Send(new ProfileImportProcessHostMsg_Import_Started()); +} + +void ExternalProcessImporterBridge::NotifyItemStarted( + importer::ImportItem item) { + Send(new ProfileImportProcessHostMsg_ImportItem_Started(item)); +} + +void ExternalProcessImporterBridge::NotifyItemEnded(importer::ImportItem item) { + Send(new ProfileImportProcessHostMsg_ImportItem_Finished(item)); +} + +void ExternalProcessImporterBridge::NotifyEnded() { + // The internal process detects import end when all items have been received. +} + +base::string16 ExternalProcessImporterBridge::GetLocalizedString( + int message_id) { + base::string16 message; + localized_strings_->GetString(base::IntToString(message_id), &message); + return message; +} + +ExternalProcessImporterBridge::~ExternalProcessImporterBridge() {} + +void ExternalProcessImporterBridge::Send(IPC::Message* message) { + task_runner_->PostTask( + FROM_HERE, + base::Bind(&ExternalProcessImporterBridge::SendInternal, + this, message)); +} + +void ExternalProcessImporterBridge::SendInternal(IPC::Message* message) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + sender_->Send(message); +} diff --git a/chromium_src/chrome/utility/importer/external_process_importer_bridge.h b/chromium_src/chrome/utility/importer/external_process_importer_bridge.h new file mode 100644 index 0000000000..bd4626293a --- /dev/null +++ b/chromium_src/chrome/utility/importer/external_process_importer_bridge.h @@ -0,0 +1,112 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add SetCookies. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_UTILITY_IMPORTER_EXTERNAL_PROCESS_IMPORTER_BRIDGE_H_ +#define CHROME_UTILITY_IMPORTER_EXTERNAL_PROCESS_IMPORTER_BRIDGE_H_ + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "build/build_config.h" +#include "chrome/common/importer/importer_bridge.h" +#include "components/favicon_base/favicon_usage_data.h" + +class GURL; +struct ImportedBookmarkEntry; + +namespace base { +class DictionaryValue; +class TaskRunner; +} + +namespace importer { +#if defined(OS_WIN) +struct ImporterIE7PasswordInfo; +#endif +struct ImporterURLRow; +struct SearchEngineInfo; +} + +namespace IPC { +class Message; +class Sender; +} + +// When the importer is run in an external process, the bridge is effectively +// split in half by the IPC infrastructure. The external bridge receives data +// and notifications from the importer, and sends it across IPC. The +// internal bridge gathers the data from the IPC host and writes it to the +// profile. +class ExternalProcessImporterBridge : public ImporterBridge { + public: + ExternalProcessImporterBridge( + const base::DictionaryValue& localized_strings, + IPC::Sender* sender, + base::TaskRunner* task_runner); + + // Begin ImporterBridge implementation: + void AddBookmarks(const std::vector& bookmarks, + const base::string16& first_folder_name) override; + + void AddHomePage(const GURL& home_page) override; + +#if defined(OS_WIN) + void AddIE7PasswordInfo( + const importer::ImporterIE7PasswordInfo& password_info) override; +#endif + + void SetFavicons(const favicon_base::FaviconUsageDataList& favicons) override; + + void SetHistoryItems(const std::vector& rows, + importer::VisitSource visit_source) override; + + void SetKeywords( + const std::vector& search_engines, + bool unique_on_host_and_path) override; + + void SetFirefoxSearchEnginesXMLData( + const std::vector& seach_engine_data) override; + + void SetPasswordForm(const autofill::PasswordForm& form) override; + + void SetAutofillFormData( + const std::vector& entries) override; + + void SetCookies(const std::vector& cookies) override; + + void NotifyStarted() override; + void NotifyItemStarted(importer::ImportItem item) override; + void NotifyItemEnded(importer::ImportItem item) override; + void NotifyEnded() override; + + base::string16 GetLocalizedString(int message_id) override; + // End ImporterBridge implementation. + + private: + ~ExternalProcessImporterBridge() override; + + void Send(IPC::Message* message); + void SendInternal(IPC::Message* message); + + // Holds strings needed by the external importer because the resource + // bundle isn't available to the external process. + std::unique_ptr localized_strings_; + + IPC::Sender* sender_; + scoped_refptr task_runner_; + + DISALLOW_COPY_AND_ASSIGN(ExternalProcessImporterBridge); +}; + +#endif // CHROME_UTILITY_IMPORTER_EXTERNAL_PROCESS_IMPORTER_BRIDGE_H_ diff --git a/chromium_src/chrome/utility/importer/favicon_reencode.cc b/chromium_src/chrome/utility/importer/favicon_reencode.cc new file mode 100644 index 0000000000..f20555b086 --- /dev/null +++ b/chromium_src/chrome/utility/importer/favicon_reencode.cc @@ -0,0 +1,42 @@ +// Copyright 2013 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/utility/importer/favicon_reencode.h" + +#include "content/public/child/image_decoder_utils.h" +#include "skia/ext/image_operations.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/favicon_size.h" +#include "ui/gfx/geometry/size.h" + +namespace importer { + +bool ReencodeFavicon(const unsigned char* src_data, + size_t src_len, + std::vector* png_data) { + // Decode the favicon using WebKit's image decoder. + SkBitmap decoded = content::DecodeImage( + src_data, + gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize), + src_len); + if (decoded.empty()) + return false; // Unable to decode. + + if (decoded.width() != gfx::kFaviconSize || + decoded.height() != gfx::kFaviconSize) { + // The bitmap is not the correct size, re-sample. + int new_width = decoded.width(); + int new_height = decoded.height(); + gfx::CalculateFaviconTargetSize(&new_width, &new_height); + decoded = skia::ImageOperations::Resize( + decoded, skia::ImageOperations::RESIZE_LANCZOS3, new_width, new_height); + } + + // Encode our bitmap as a PNG. + gfx::PNGCodec::EncodeBGRASkBitmap(decoded, false, png_data); + return true; +} + +} // namespace importer diff --git a/chromium_src/chrome/utility/importer/favicon_reencode.h b/chromium_src/chrome/utility/importer/favicon_reencode.h new file mode 100644 index 0000000000..9b19f8e893 --- /dev/null +++ b/chromium_src/chrome/utility/importer/favicon_reencode.h @@ -0,0 +1,24 @@ +// Copyright 2013 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. + +#ifndef CHROME_UTILITY_IMPORTER_FAVICON_REENCODE_H_ +#define CHROME_UTILITY_IMPORTER_FAVICON_REENCODE_H_ + +#include + +#include + + +namespace importer { + +// Given raw image data, decodes the icon, re-sampling to the correct size as +// necessary, and re-encodes as PNG data in the given output vector. Returns +// true on success. +bool ReencodeFavicon(const unsigned char* src_data, + size_t src_len, + std::vector* png_data); + +} // namespace importer + +#endif // CHROME_UTILITY_IMPORTER_FAVICON_REENCODE_H_ diff --git a/chromium_src/chrome/utility/importer/firefox_importer.cc b/chromium_src/chrome/utility/importer/firefox_importer.cc new file mode 100644 index 0000000000..7989ea9110 --- /dev/null +++ b/chromium_src/chrome/utility/importer/firefox_importer.cc @@ -0,0 +1,859 @@ +// Copyright 2013 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add ImportCookie and fix broken ImportBookmarks. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/utility/importer/firefox_importer.h" + +#include +#include + +#include "atom/common/importer/imported_cookie_entry.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "chrome/common/importer/firefox_importer_utils.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_autofill_form_data_entry.h" +#include "chrome/common/importer/importer_bridge.h" +#include "chrome/common/importer/importer_url_row.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/utility/importer/bookmark_html_reader.h" +#include "chrome/utility/importer/favicon_reencode.h" +#include "chrome/utility/importer/nss_decryptor.h" +#include "components/autofill/core/common/password_form.h" +#include "sql/connection.h" +#include "sql/statement.h" +#include "url/gurl.h" + +namespace { + +// Original definition is in http://mxr.mozilla.org/firefox/source/toolkit/ +// components/places/public/nsINavBookmarksService.idl +enum BookmarkItemType { + TYPE_BOOKMARK = 1, + TYPE_FOLDER = 2, + TYPE_SEPARATOR = 3, + TYPE_DYNAMIC_CONTAINER = 4 +}; + +// Loads the default bookmarks in the Firefox installed at |app_path|, +// and stores their locations in |urls|. +void LoadDefaultBookmarks(const base::FilePath& app_path, + std::set* urls) { + base::FilePath file = app_path.AppendASCII("defaults") + .AppendASCII("profile") + .AppendASCII("bookmarks.html"); + urls->clear(); + + std::vector bookmarks; + std::vector search_engines; + bookmark_html_reader::ImportBookmarksFile(base::Callback(), + base::Callback(), + file, + &bookmarks, + &search_engines, + NULL); + for (size_t i = 0; i < bookmarks.size(); ++i) + urls->insert(bookmarks[i].url); +} + +// Returns true if |url| has a valid scheme that we allow to import. We +// filter out the URL with a unsupported scheme. +bool CanImportURL(const GURL& url) { + // The URL is not valid. + if (!url.is_valid()) + return false; + + // Filter out the URLs with unsupported schemes. + const char* const kInvalidSchemes[] = {"wyciwyg", "place", "about", "chrome"}; + for (size_t i = 0; i < arraysize(kInvalidSchemes); ++i) { + if (url.SchemeIs(kInvalidSchemes[i])) + return false; + } + + return true; +} + +} // namespace + +struct FirefoxImporter::BookmarkItem { + int parent; + int id; + GURL url; + base::string16 title; + BookmarkItemType type; + std::string keyword; + base::Time date_added; + int64_t favicon; + bool empty_folder; +}; + +FirefoxImporter::FirefoxImporter() { +} + +FirefoxImporter::~FirefoxImporter() { +} + +void FirefoxImporter::StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) { + bridge_ = bridge; + source_path_ = source_profile.source_path; + app_path_ = source_profile.app_path; + +#if defined(OS_POSIX) + locale_ = source_profile.locale; +#endif + + // The order here is important! + bridge_->NotifyStarted(); + if ((items & importer::HOME_PAGE) && !cancelled()) { + bridge_->NotifyItemStarted(importer::HOME_PAGE); + ImportHomepage(); // Doesn't have a UI item. + bridge_->NotifyItemEnded(importer::HOME_PAGE); + } + + // Note history should be imported before bookmarks because bookmark import + // will also import favicons and we store favicon for a URL only if the URL + // exist in history or bookmarks. + if ((items & importer::HISTORY) && !cancelled()) { + bridge_->NotifyItemStarted(importer::HISTORY); + ImportHistory(); + bridge_->NotifyItemEnded(importer::HISTORY); + } + + if ((items & importer::FAVORITES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::FAVORITES); + ImportBookmarks(); + bridge_->NotifyItemEnded(importer::FAVORITES); + } + if ((items & importer::SEARCH_ENGINES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::SEARCH_ENGINES); + ImportSearchEngines(); + bridge_->NotifyItemEnded(importer::SEARCH_ENGINES); + } + if ((items & importer::PASSWORDS) && !cancelled()) { + bridge_->NotifyItemStarted(importer::PASSWORDS); + ImportPasswords(); + bridge_->NotifyItemEnded(importer::PASSWORDS); + } + if ((items & importer::AUTOFILL_FORM_DATA) && !cancelled()) { + bridge_->NotifyItemStarted(importer::AUTOFILL_FORM_DATA); + ImportAutofillFormData(); + bridge_->NotifyItemEnded(importer::AUTOFILL_FORM_DATA); + } + if ((items & importer::COOKIES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::COOKIES); + ImportCookies(); + bridge_->NotifyItemEnded(importer::COOKIES); + } + bridge_->NotifyEnded(); +} + +void FirefoxImporter::ImportHistory() { + base::FilePath file = source_path_.AppendASCII("places.sqlite"); + if (!base::PathExists(file)) + return; + + sql::Connection db; + if (!db.Open(file)) + return; + + // |visit_type| represent the transition type of URLs (typed, click, + // redirect, bookmark, etc.) We eliminate some URLs like sub-frames and + // redirects, since we don't want them to appear in history. + // Firefox transition types are defined in: + // toolkit/components/places/public/nsINavHistoryService.idl + const char query[] = + "SELECT h.url, h.title, h.visit_count, " + "h.hidden, h.typed, v.visit_date " + "FROM moz_places h JOIN moz_historyvisits v " + "ON h.id = v.place_id " + "WHERE v.visit_type <= 3"; + + sql::Statement s(db.GetUniqueStatement(query)); + + std::vector rows; + while (s.Step() && !cancelled()) { + GURL url(s.ColumnString(0)); + + // Filter out unwanted URLs. + if (!CanImportURL(url)) + continue; + + ImporterURLRow row(url); + row.title = s.ColumnString16(1); + row.visit_count = s.ColumnInt(2); + row.hidden = s.ColumnInt(3) == 1; + row.typed_count = s.ColumnInt(4); + row.last_visit = base::Time::FromTimeT(s.ColumnInt64(5)/1000000); + + rows.push_back(row); + } + + if (!rows.empty() && !cancelled()) + bridge_->SetHistoryItems(rows, importer::VISIT_SOURCE_FIREFOX_IMPORTED); +} + +void FirefoxImporter::ImportBookmarks() { + base::FilePath file = source_path_.AppendASCII("places.sqlite"); + if (!base::PathExists(file)) + return; + + sql::Connection db; + if (!db.Open(file)) + return; + + // Get the bookmark folders that we are interested in. + int toolbar_folder_id = -1; + int menu_folder_id = -1; + int unsorted_folder_id = -1; + LoadRootNodeID(&db, &toolbar_folder_id, &menu_folder_id, &unsorted_folder_id); + + // Load livemark IDs. + std::set livemark_id; + LoadLivemarkIDs(&db, &livemark_id); + + // Load the default bookmarks. + std::set default_urls; + LoadDefaultBookmarks(app_path_, &default_urls); + + BookmarkList list; + GetTopBookmarkFolder(&db, toolbar_folder_id, &list); + GetTopBookmarkFolder(&db, menu_folder_id, &list); + GetTopBookmarkFolder(&db, unsorted_folder_id, &list); + size_t count = list.size(); + for (size_t i = 0; i < count; ++i) + GetWholeBookmarkFolder(&db, &list, i, NULL); + + std::vector bookmarks; + std::vector search_engines; + FaviconMap favicon_map; + + // TODO(jcampan): http://b/issue?id=1196285 we do not support POST based + // keywords yet. We won't include them in the list. + std::set post_keyword_ids; + const char query[] = + "SELECT b.id FROM moz_bookmarks b " + "INNER JOIN moz_items_annos ia ON ia.item_id = b.id " + "INNER JOIN moz_anno_attributes aa ON ia.anno_attribute_id = aa.id " + "WHERE aa.name = 'bookmarkProperties/POSTData'"; + sql::Statement s(db.GetUniqueStatement(query)); + + if (!s.is_valid()) + return; + + while (s.Step() && !cancelled()) + post_keyword_ids.insert(s.ColumnInt(0)); + + for (size_t i = 0; i < list.size(); ++i) { + BookmarkItem* item = list[i]; + + // Folders are added implicitly on adding children, so we only explicitly + // add empty folders. + if (item->type != TYPE_BOOKMARK && + ((item->type != TYPE_FOLDER) || !item->empty_folder)) + continue; + + if (CanImportURL(item->url)) { + // Skip the default bookmarks and unwanted URLs. + if (default_urls.find(item->url) != default_urls.end() || + post_keyword_ids.find(item->id) != post_keyword_ids.end()) + continue; + + // Find the bookmark path by tracing their links to parent folders. + std::vector path; + BookmarkItem* child = item; + bool found_path = false; + bool is_in_toolbar = false; + while (child->parent >= 0) { + BookmarkItem* parent = list[child->parent]; + if (livemark_id.find(parent->id) != livemark_id.end()) { + // Don't import live bookmarks. + break; + } + + if (parent->id != menu_folder_id) { + // To avoid excessive nesting, omit the name for the bookmarks menu + // folder. + path.insert(path.begin(), parent->title); + } + + if (parent->id == toolbar_folder_id) + is_in_toolbar = true; + + if (parent->id == toolbar_folder_id || + parent->id == menu_folder_id || + parent->id == unsorted_folder_id) { + // We've reached a root node, hooray! + found_path = true; + break; + } + + child = parent; + } + + if (!found_path) + continue; + + ImportedBookmarkEntry entry; + entry.creation_time = item->date_added; + entry.title = item->title; + entry.url = item->url; + entry.path = path; + entry.in_toolbar = is_in_toolbar; + entry.is_folder = item->type == TYPE_FOLDER; + + bookmarks.push_back(entry); + } + + if (item->type == TYPE_BOOKMARK) { + if (item->favicon) + favicon_map[item->favicon].insert(item->url); + + // Import this bookmark as a search engine if it has a keyword and its URL + // is usable as a search engine URL. (Even if the URL doesn't allow + // substitution, importing as a "search engine" allows users to trigger + // the bookmark by entering its keyword in the omnibox.) + if (item->keyword.empty()) + continue; + importer::SearchEngineInfo search_engine_info; + std::string search_engine_url; + if (item->url.is_valid()) + search_engine_info.url = base::UTF8ToUTF16(item->url.spec()); + else if (bookmark_html_reader::CanImportURLAsSearchEngine( + item->url, + &search_engine_url)) + search_engine_info.url = base::UTF8ToUTF16(search_engine_url); + else + continue; + search_engine_info.keyword = base::UTF8ToUTF16(item->keyword); + search_engine_info.display_name = item->title; + search_engines.push_back(search_engine_info); + } + } + + STLDeleteElements(&list); + + // Write into profile. + if (!bookmarks.empty() && !cancelled()) { + const base::string16& first_folder_name = + // bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_FIREFOX); + base::UTF8ToUTF16("Imported from Firefox"); + bridge_->AddBookmarks(bookmarks, first_folder_name); + } + if (!search_engines.empty() && !cancelled()) { + bridge_->SetKeywords(search_engines, false); + } + if (!favicon_map.empty() && !cancelled()) { + favicon_base::FaviconUsageDataList favicons; + LoadFavicons(&db, favicon_map, &favicons); + bridge_->SetFavicons(favicons); + } +} + +void FirefoxImporter::ImportPasswords() { + // Initializes NSS3. + NSSDecryptor decryptor; + if (!decryptor.Init(source_path_, source_path_) && + !decryptor.Init(app_path_, source_path_)) { + return; + } + + std::vector forms; + base::FilePath source_path = source_path_; + const base::FilePath sqlite_file = source_path.AppendASCII("signons.sqlite"); + const base::FilePath json_file = source_path.AppendASCII("logins.json"); + const base::FilePath signon3_file = source_path.AppendASCII("signons3.txt"); + const base::FilePath signon2_file = source_path.AppendASCII("signons2.txt"); + if (base::PathExists(json_file)) { + // Since Firefox 32, passwords are in logins.json. + decryptor.ReadAndParseLogins(json_file, &forms); + } else if (base::PathExists(sqlite_file)) { + // Since Firefox 3.1, passwords are in signons.sqlite db. + decryptor.ReadAndParseSignons(sqlite_file, &forms); + } else if (base::PathExists(signon3_file)) { + // Firefox 3.0 uses signons3.txt to store the passwords. + decryptor.ParseSignons(signon3_file, &forms); + } else { + decryptor.ParseSignons(signon2_file, &forms); + } + + if (!cancelled()) { + for (size_t i = 0; i < forms.size(); ++i) { + if (!forms[i].username_value.empty() || + !forms[i].password_value.empty() || + forms[i].blacklisted_by_user) { + bridge_->SetPasswordForm(forms[i]); + } + } + } +} + +void FirefoxImporter::ImportSearchEngines() { + std::vector search_engine_data; + GetSearchEnginesXMLData(&search_engine_data); + + bridge_->SetFirefoxSearchEnginesXMLData(search_engine_data); +} + +void FirefoxImporter::ImportHomepage() { + GURL home_page = GetHomepage(source_path_); + if (home_page.is_valid() && !IsDefaultHomepage(home_page, app_path_)) { + bridge_->AddHomePage(home_page); + } +} + +void FirefoxImporter::ImportAutofillFormData() { + base::FilePath file = source_path_.AppendASCII("formhistory.sqlite"); + if (!base::PathExists(file)) + return; + + sql::Connection db; + if (!db.Open(file)) + return; + + const char query[] = + "SELECT fieldname, value, timesUsed, firstUsed, lastUsed FROM " + "moz_formhistory"; + + sql::Statement s(db.GetUniqueStatement(query)); + + std::vector form_entries; + while (s.Step() && !cancelled()) { + ImporterAutofillFormDataEntry form_entry; + form_entry.name = s.ColumnString16(0); + form_entry.value = s.ColumnString16(1); + form_entry.times_used = s.ColumnInt(2); + form_entry.first_used = base::Time::FromTimeT(s.ColumnInt64(3) / 1000000); + form_entry.last_used = base::Time::FromTimeT(s.ColumnInt64(4) / 1000000); + + // Don't import search bar history. + if (base::UTF16ToUTF8(form_entry.name) == "searchbar-history") + continue; + + form_entries.push_back(form_entry); + } + + if (!form_entries.empty() && !cancelled()) + bridge_->SetAutofillFormData(form_entries); +} + +void FirefoxImporter::GetSearchEnginesXMLData( + std::vector* search_engine_data) { + base::FilePath file = source_path_.AppendASCII("search.sqlite"); + if (!base::PathExists(file)) { + // Since Firefox 3.5, search engines are no longer stored in search.sqlite. + // Instead, search.json is used for storing search engines. + GetSearchEnginesXMLDataFromJSON(search_engine_data); + return; + } + + sql::Connection db; + if (!db.Open(file)) + return; + + const char query[] = + "SELECT engineid FROM engine_data " + "WHERE engineid NOT IN " + "(SELECT engineid FROM engine_data " + "WHERE name='hidden') " + "ORDER BY value ASC"; + + sql::Statement s(db.GetUniqueStatement(query)); + if (!s.is_valid()) + return; + + const base::FilePath searchplugins_path(FILE_PATH_LITERAL("searchplugins")); + // Search engine definitions are XMLs stored in two directories. Default + // engines are in the app directory (app_path_) and custom engines are + // in the profile directory (source_path_). + + // Since Firefox 21, app_path_ engines are in 'browser' subdirectory: + base::FilePath app_path = + app_path_.AppendASCII("browser").Append(searchplugins_path); + if (!base::PathExists(app_path)) { + // This might be an older Firefox, try old location without the 'browser' + // path component: + app_path = app_path_.Append(searchplugins_path); + } + + base::FilePath profile_path = source_path_.Append(searchplugins_path); + + // Firefox doesn't store a search engine in its sqlite database unless the + // user has added a engine. So we get search engines from sqlite db as well + // as from the file system. + if (s.Step()) { + const std::string kAppPrefix("[app]/"); + const std::string kProfilePrefix("[profile]/"); + do { + base::FilePath file; + std::string engine(s.ColumnString(0)); + + // The string contains [app]/.xml or [profile]/.xml where + // the [app] and [profile] need to be replaced with the actual app or + // profile path. + size_t index = engine.find(kAppPrefix); + if (index != std::string::npos) { + // Remove '[app]/'. + file = app_path.AppendASCII(engine.substr(index + kAppPrefix.length())); + } else if ((index = engine.find(kProfilePrefix)) != std::string::npos) { + // Remove '[profile]/'. + file = profile_path.AppendASCII( + engine.substr(index + kProfilePrefix.length())); + } else { + // Looks like absolute path to the file. + file = base::FilePath::FromUTF8Unsafe(engine); + } + std::string file_data; + base::ReadFileToString(file, &file_data); + search_engine_data->push_back(file_data); + } while (s.Step() && !cancelled()); + } + +#if defined(OS_POSIX) + // Ubuntu-flavored Firefox supports locale-specific search engines via + // locale-named subdirectories. They fall back to en-US. + // See http://crbug.com/53899 + // TODO(jshin): we need to make sure our locale code matches that of + // Firefox. + DCHECK(!locale_.empty()); + base::FilePath locale_app_path = app_path.AppendASCII(locale_); + base::FilePath default_locale_app_path = app_path.AppendASCII("en-US"); + if (base::DirectoryExists(locale_app_path)) + app_path = locale_app_path; + else if (base::DirectoryExists(default_locale_app_path)) + app_path = default_locale_app_path; +#endif + + // Get search engine definition from file system. + base::FileEnumerator engines(app_path, false, base::FileEnumerator::FILES); + for (base::FilePath engine_path = engines.Next(); + !engine_path.value().empty(); engine_path = engines.Next()) { + std::string file_data; + base::ReadFileToString(file, &file_data); + search_engine_data->push_back(file_data); + } +} + +void FirefoxImporter::ImportCookies() { + base::FilePath file = source_path_.AppendASCII("cookies.sqlite"); + if (!base::PathExists(file)) + return; + + sql::Connection db; + if (!db.Open(file)) + return; + + const char query[] = + "SELECT baseDomain, name, value, host, path, expiry, isSecure, " + "isHttpOnly FROM moz_cookies"; + + sql::Statement s(db.GetUniqueStatement(query)); + + std::vector cookies; + while (s.Step() && !cancelled()) { + ImportedCookieEntry cookie; + base::string16 domain(base::UTF8ToUTF16(".")); + domain.append(s.ColumnString16(0)); + base::string16 host; + if (s.ColumnString16(3)[0] == '.') { + host.append(base::UTF8ToUTF16("*")); + host.append(s.ColumnString16(3)); + } else { + host = s.ColumnString16(3); + } + cookie.domain = domain; + cookie.name = s.ColumnString16(1); + cookie.value = s.ColumnString16(2); + cookie.host = host; + cookie.path = s.ColumnString16(4); + cookie.expiry_date = + base::Time::FromDoubleT(s.ColumnInt64(5)); + cookie.secure = s.ColumnBool(6); + cookie.httponly = s.ColumnBool(7); + + cookies.push_back(cookie); + } + + if (!cookies.empty() && !cancelled()) + bridge_->SetCookies(cookies); +} + +void FirefoxImporter::GetSearchEnginesXMLDataFromJSON( + std::vector* search_engine_data) { + // search-metadata.json contains keywords for search engines. This + // file exists only if the user has set keywords for search engines. + base::FilePath search_metadata_json_file = + source_path_.AppendASCII("search-metadata.json"); + JSONFileValueDeserializer metadata_deserializer(search_metadata_json_file); + std::unique_ptr metadata_root = + metadata_deserializer.Deserialize(NULL, NULL); + const base::DictionaryValue* search_metadata_root = NULL; + if (metadata_root) + metadata_root->GetAsDictionary(&search_metadata_root); + + // search.json contains information about search engines to import. + base::FilePath search_json_file = source_path_.AppendASCII("search.json"); + if (!base::PathExists(search_json_file)) + return; + + JSONFileValueDeserializer deserializer(search_json_file); + std::unique_ptr root = deserializer.Deserialize(NULL, NULL); + const base::DictionaryValue* search_root = NULL; + if (!root || !root->GetAsDictionary(&search_root)) + return; + + const std::string kDirectories("directories"); + const base::DictionaryValue* search_directories = NULL; + if (!search_root->GetDictionary(kDirectories, &search_directories)) + return; + + // Dictionary |search_directories| contains a list of search engines + // (default and installed). The list can be found from key + // of the dictionary. Key is a grandchild of key . + // However, key parent's key is dynamic which depends on + // operating systems. For example, + // Ubuntu (for default search engine): + // /usr/lib/firefox/distribution/searchplugins/locale/en-US + // Ubuntu (for installed search engines): + // /home//.mozilla/firefox/lcd50n4n.default/searchplugins + // Windows (for default search engine): + // C:\\Program Files (x86)\\Mozilla Firefox\\browser\\searchplugins + // Therefore, it needs to be retrieved by searching. + + for (base::DictionaryValue::Iterator it(*search_directories); !it.IsAtEnd(); + it.Advance()) { + // The key of |it| may contains dot (.) which cannot be used as + // for retrieving . Hence, it is needed to get |it| as dictionary. + // The resulted dictionary can be used for retrieving . + const std::string kEngines("engines"); + const base::DictionaryValue* search_directory = NULL; + if (!it.value().GetAsDictionary(&search_directory)) + continue; + + const base::ListValue* search_engines = NULL; + if (!search_directory->GetList(kEngines, &search_engines)) + continue; + + const std::string kFilePath("filePath"); + const std::string kHidden("_hidden"); + for (size_t i = 0; i < search_engines->GetSize(); ++i) { + const base::DictionaryValue* engine_info = NULL; + if (!search_engines->GetDictionary(i, &engine_info)) + continue; + + bool is_hidden = false; + std::string file_path; + if (!engine_info->GetBoolean(kHidden, &is_hidden) || + !engine_info->GetString(kFilePath, &file_path)) + continue; + + if (!is_hidden) { + const std::string kAppPrefix("[app]/"); + const std::string kProfilePrefix("[profile]/"); + base::FilePath xml_file = base::FilePath::FromUTF8Unsafe(file_path); + + // If |file_path| contains [app] or [profile] then they need to be + // replaced with the actual app or profile path. + size_t index = file_path.find(kAppPrefix); + if (index != std::string::npos) { + // Replace '[app]/' with actual app path. + xml_file = app_path_.AppendASCII("searchplugins").AppendASCII( + file_path.substr(index + kAppPrefix.length())); + } else if ((index = file_path.find(kProfilePrefix)) != + std::string::npos) { + // Replace '[profile]/' with actual profile path. + xml_file = source_path_.AppendASCII("searchplugins").AppendASCII( + file_path.substr(index + kProfilePrefix.length())); + } + + std::string file_data; + base::ReadFileToString(xml_file, &file_data); + + // If a keyword is mentioned for this search engine, then add + // it to the XML string as an element and use this updated + // string. + const base::DictionaryValue* search_xml_path = NULL; + if (search_metadata_root && search_metadata_root->HasKey(file_path) && + search_metadata_root->GetDictionaryWithoutPathExpansion( + file_path, &search_xml_path)) { + std::string alias; + search_xml_path->GetString("alias", &alias); + + // Add element as the last child element. + size_t end_of_parent = file_data.find(""); + if (end_of_parent != std::string::npos && !alias.empty()) + file_data.insert(end_of_parent, "" + alias + " \n"); + } + search_engine_data->push_back(file_data); + } + } + } +} + +void FirefoxImporter::LoadRootNodeID(sql::Connection* db, + int* toolbar_folder_id, + int* menu_folder_id, + int* unsorted_folder_id) { + /* + static const char kToolbarFolderName[] = "toolbar"; + static const char kMenuFolderName[] = "menu"; + static const char kUnsortedFolderName[] = "unfiled"; + */ + static const char kToolbarFolderName[] = "Bookmarks Toolbar"; + static const char kMenuFolderName[] = "Bookmarks Menu"; + static const char kUnsortedFolderName[] = "Other Bookmarks"; + + // const char query[] = "SELECT root_name, folder_id FROM moz_bookmarks_roots"; + const char query[] = "SELECT b.title, b.id FROM moz_bookmarks b " + "WHERE b.type = 2"; + sql::Statement s(db->GetUniqueStatement(query)); + + while (s.Step()) { + std::string folder = s.ColumnString(0); + int id = s.ColumnInt(1); + if (folder == kToolbarFolderName) + *toolbar_folder_id = id; + else if (folder == kMenuFolderName) + *menu_folder_id = id; + else if (folder == kUnsortedFolderName) + *unsorted_folder_id = id; + } +} + +void FirefoxImporter::LoadLivemarkIDs(sql::Connection* db, + std::set* livemark) { + static const char kFeedAnnotation[] = "livemark/feedURI"; + livemark->clear(); + + const char query[] = + "SELECT b.item_id " + "FROM moz_anno_attributes a " + "JOIN moz_items_annos b ON a.id = b.anno_attribute_id " + "WHERE a.name = ? "; + sql::Statement s(db->GetUniqueStatement(query)); + s.BindString(0, kFeedAnnotation); + + while (s.Step() && !cancelled()) + livemark->insert(s.ColumnInt(0)); +} + +void FirefoxImporter::GetTopBookmarkFolder(sql::Connection* db, + int folder_id, + BookmarkList* list) { + const char query[] = + "SELECT b.title " + "FROM moz_bookmarks b " + "WHERE b.type = 2 AND b.id = ? " + "ORDER BY b.position"; + sql::Statement s(db->GetUniqueStatement(query)); + s.BindInt(0, folder_id); + + if (s.Step()) { + BookmarkItem* item = new BookmarkItem; + item->parent = -1; // The top level folder has no parent. + item->id = folder_id; + item->title = s.ColumnString16(0); + item->type = TYPE_FOLDER; + item->favicon = 0; + item->empty_folder = true; + list->push_back(item); + } +} + +void FirefoxImporter::GetWholeBookmarkFolder(sql::Connection* db, + BookmarkList* list, + size_t position, + bool* empty_folder) { + if (position >= list->size()) { + NOTREACHED(); + return; + } + + const char query[] = + "SELECT b.id, h.url, COALESCE(b.title, h.title), " + "b.type, k.keyword, b.dateAdded, h.favicon_id " + "FROM moz_bookmarks b " + "LEFT JOIN moz_places h ON b.fk = h.id " + "LEFT JOIN moz_keywords k ON k.id = b.keyword_id " + "WHERE b.type IN (1,2) AND b.parent = ? " + "ORDER BY b.position"; + sql::Statement s(db->GetUniqueStatement(query)); + s.BindInt(0, (*list)[position]->id); + + BookmarkList temp_list; + while (s.Step()) { + BookmarkItem* item = new BookmarkItem; + item->parent = static_cast(position); + item->id = s.ColumnInt(0); + item->url = GURL(s.ColumnString(1)); + item->title = s.ColumnString16(2); + item->type = static_cast(s.ColumnInt(3)); + item->keyword = s.ColumnString(4); + item->date_added = base::Time::FromTimeT(s.ColumnInt64(5)/1000000); + item->favicon = s.ColumnInt64(6); + item->empty_folder = true; + + temp_list.push_back(item); + if (empty_folder != NULL) + *empty_folder = false; + } + + // Appends all items to the list. + for (BookmarkList::iterator i = temp_list.begin(); + i != temp_list.end(); ++i) { + list->push_back(*i); + // Recursive add bookmarks in sub-folders. + if ((*i)->type == TYPE_FOLDER) + GetWholeBookmarkFolder(db, list, list->size() - 1, &(*i)->empty_folder); + } +} + +void FirefoxImporter::LoadFavicons( + sql::Connection* db, + const FaviconMap& favicon_map, + favicon_base::FaviconUsageDataList* favicons) { + const char query[] = "SELECT url, data FROM moz_favicons WHERE id=?"; + sql::Statement s(db->GetUniqueStatement(query)); + + if (!s.is_valid()) + return; + + for (FaviconMap::const_iterator i = favicon_map.begin(); + i != favicon_map.end(); ++i) { + s.BindInt64(0, i->first); + if (s.Step()) { + favicon_base::FaviconUsageData usage; + + usage.favicon_url = GURL(s.ColumnString(0)); + if (!usage.favicon_url.is_valid()) + continue; // Don't bother importing favicons with invalid URLs. + + std::vector data; + s.ColumnBlobAsVector(1, &data); + if (data.empty()) + continue; // Data definitely invalid. + + if (!importer::ReencodeFavicon(&data[0], data.size(), &usage.png_data)) + continue; // Unable to decode. + + usage.urls = i->second; + favicons->push_back(usage); + } + s.Reset(true); + } +} diff --git a/chromium_src/chrome/utility/importer/firefox_importer.h b/chromium_src/chrome/utility/importer/firefox_importer.h new file mode 100644 index 0000000000..00055f8fc1 --- /dev/null +++ b/chromium_src/chrome/utility/importer/firefox_importer.h @@ -0,0 +1,104 @@ +// Copyright 2013 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add ImportCookie and fix broken ImportBookmarks. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#ifndef CHROME_UTILITY_IMPORTER_FIREFOX_IMPORTER_H_ +#define CHROME_UTILITY_IMPORTER_FIREFOX_IMPORTER_H_ + +#include +#include + +#include +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "build/build_config.h" +#include "chrome/utility/importer/importer.h" +#include "components/favicon_base/favicon_usage_data.h" + +class GURL; + +namespace sql { +class Connection; +} + +// Importer for Mozilla Firefox 3 and later. +// Firefox stores its persistent information in a system called places. +// http://wiki.mozilla.org/Places +class FirefoxImporter : public Importer { + public: + FirefoxImporter(); + + // Importer: + void StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) override; + + private: + typedef std::map> FaviconMap; + + ~FirefoxImporter() override; + + void ImportBookmarks(); + void ImportPasswords(); + void ImportHistory(); + void ImportSearchEngines(); + // Import the user's home page, unless it is set to default home page as + // defined in browserconfig.properties. + void ImportHomepage(); + void ImportAutofillFormData(); + void ImportCookies(); + void GetSearchEnginesXMLData(std::vector* search_engine_data); + void GetSearchEnginesXMLDataFromJSON( + std::vector* search_engine_data); + + // The struct stores the information about a bookmark item. + struct BookmarkItem; + typedef std::vector BookmarkList; + + // Gets the specific IDs of bookmark root node from |db|. + void LoadRootNodeID(sql::Connection* db, int* toolbar_folder_id, + int* menu_folder_id, int* unsorted_folder_id); + + // Loads all livemark IDs from database |db|. + void LoadLivemarkIDs(sql::Connection* db, std::set* livemark); + + // Gets the bookmark folder with given ID, and adds the entry in |list| + // if successful. + void GetTopBookmarkFolder(sql::Connection* db, + int folder_id, + BookmarkList* list); + + // Loads all children of the given folder, and appends them to the |list|. + void GetWholeBookmarkFolder(sql::Connection* db, BookmarkList* list, + size_t position, bool* empty_folder); + + // Loads the favicons given in the map from the database, loads the data, + // and converts it into FaviconUsage structures. + void LoadFavicons(sql::Connection* db, + const FaviconMap& favicon_map, + favicon_base::FaviconUsageDataList* favicons); + + base::FilePath source_path_; + base::FilePath app_path_; + +#if defined(OS_POSIX) + // Stored because we can only access it from the UI thread. + std::string locale_; +#endif + + DISALLOW_COPY_AND_ASSIGN(FirefoxImporter); +}; + +#endif // CHROME_UTILITY_IMPORTER_FIREFOX_IMPORTER_H_ diff --git a/chromium_src/chrome/utility/importer/ie_importer_win.cc b/chromium_src/chrome/utility/importer/ie_importer_win.cc new file mode 100644 index 0000000000..4dc3371477 --- /dev/null +++ b/chromium_src/chrome/utility/importer/ie_importer_win.cc @@ -0,0 +1,938 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * we remove some functionality. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/utility/importer/ie_importer_win.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "base/win/registry.h" +#include "base/win/scoped_co_mem.h" +#include "base/win/scoped_comptr.h" +#include "base/win/scoped_handle.h" +#include "base/win/scoped_propvariant.h" +#include "base/win/windows_version.h" +#include "chrome/common/importer/edge_importer_utils_win.h" +#include "chrome/common/importer/ie_importer_utils_win.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_bridge.h" +#include "chrome/common/importer/importer_data_types.h" +#include "chrome/common/importer/importer_url_row.h" +#include "chrome/common/importer/pstore_declarations.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/utility/importer/favicon_reencode.h" +#include "components/autofill/core/common/password_form.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/gurl.h" +#include "url/url_constants.h" + +namespace { + +// Registry key paths from which we import IE settings. +const base::char16 kSearchScopePath[] = + L"Software\\Microsoft\\Internet Explorer\\SearchScopes"; +const base::char16 kIEVersionKey[] = + L"Software\\Microsoft\\Internet Explorer"; +const base::char16 kIEToolbarKey[] = + L"Software\\Microsoft\\Internet Explorer\\Toolbar"; + +// NTFS stream name of favicon image data. +const base::char16 kFaviconStreamName[] = L":favicon:$DATA"; + +// A struct that hosts the information of AutoComplete data in PStore. +struct AutoCompleteInfo { + base::string16 key; + std::vector data; + bool is_url; +}; + +// Gets the creation time of the given file or directory. +base::Time GetFileCreationTime(const base::string16& file) { + base::Time creation_time; + base::win::ScopedHandle file_handle( + CreateFile(file.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL)); + FILETIME creation_filetime; + if (!file_handle.IsValid()) + return creation_time; + if (GetFileTime(file_handle.Get(), &creation_filetime, NULL, NULL)) + creation_time = base::Time::FromFileTime(creation_filetime); + return creation_time; +} + +// Safely read an object of type T from a raw sequence of bytes. +template +bool BinaryRead(T* data, size_t offset, const std::vector& blob) { + if (offset + sizeof(T) > blob.size()) + return false; + memcpy(data, &blob[offset], sizeof(T)); + return true; +} + +// Safely read an ITEMIDLIST from a raw sequence of bytes. +// +// An ITEMIDLIST is a list of SHITEMIDs, terminated by a SHITEMID with +// .cb = 0. Here, before simply casting &blob[offset] to LPITEMIDLIST, +// we verify that the list structure is not overrunning the boundary of +// the binary blob. +LPCITEMIDLIST BinaryReadItemIDList(size_t offset, + size_t idlist_size, + const std::vector& blob) { + size_t head = 0; + while (true) { + // Use a USHORT instead of SHITEMID to avoid buffer over read. + USHORT id_cb; + if (head >= idlist_size || !BinaryRead(&id_cb, offset + head, blob)) + return NULL; + if (id_cb == 0) + break; + head += id_cb; + } + return reinterpret_cast(&blob[offset]); +} + +// Compares the two bookmarks in the order of IE's Favorites menu. +// Returns true if rhs should come later than lhs (lhs < rhs). +struct IEOrderBookmarkComparator { + bool operator()(const ImportedBookmarkEntry& lhs, + const ImportedBookmarkEntry& rhs) const { + static const uint32_t kNotSorted = 0xfffffffb; // IE uses this magic value. + base::FilePath lhs_prefix; + base::FilePath rhs_prefix; + for (size_t i = 0; i <= lhs.path.size() && i <= rhs.path.size(); ++i) { + const base::FilePath::StringType lhs_i = + (i < lhs.path.size() ? lhs.path[i] : lhs.title + L".url"); + const base::FilePath::StringType rhs_i = + (i < rhs.path.size() ? rhs.path[i] : rhs.title + L".url"); + lhs_prefix = lhs_prefix.Append(lhs_i); + rhs_prefix = rhs_prefix.Append(rhs_i); + if (lhs_i == rhs_i) + continue; + // The first path element that differs between the two. + std::map::const_iterator lhs_iter = + sort_index_->find(lhs_prefix); + std::map::const_iterator rhs_iter = + sort_index_->find(rhs_prefix); + uint32_t lhs_sort_index = + (lhs_iter == sort_index_->end() ? kNotSorted : lhs_iter->second); + uint32_t rhs_sort_index = + (rhs_iter == sort_index_->end() ? kNotSorted : rhs_iter->second); + if (lhs_sort_index != rhs_sort_index) + return lhs_sort_index < rhs_sort_index; + // If they have the same sort order, sort alphabetically. + return lhs_i < rhs_i; + } + return lhs.path.size() < rhs.path.size(); + } + const std::map* sort_index_; +}; + +// IE stores the order of the Favorites menu in registry under: +// HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites. +// The folder hierarchy of Favorites menu is directly mapped to the key +// hierarchy in the registry. +// +// If the order of the items in a folder is customized by user, the order is +// recorded in the REG_BINARY value named "Order" of the corresponding key. +// The content of the "Order" value is a raw binary dump of an array of the +// following data structure +// struct { +// uint32_t size; // Note that ITEMIDLIST is variably-sized. +// uint32_t sort_index; // 0 means this is the first item, 1 the second, +// ... +// ITEMIDLIST item_id; +// }; +// where each item_id should correspond to a favorites link file (*.url) in +// the current folder. +// gcc, in its infinite wisdom, only allows WARN_UNUSED_RESULT for prototypes. +// Clang, to be compatible with gcc, warns if WARN_UNUSED_RESULT is used in a +// non-gcc compatible manner (-Wgcc-compat). So even though gcc isn't used to +// build on Windows, declare some prototypes anyway to satisfy Clang's gcc +// compatibility warnings. +bool ParseFavoritesOrderBlob(const Importer* importer, + const std::vector& blob, + const base::FilePath& path, + std::map* sort_index) + WARN_UNUSED_RESULT; +bool ParseFavoritesOrderBlob(const Importer* importer, + const std::vector& blob, + const base::FilePath& path, + std::map* sort_index) { + static const int kItemCountOffset = 16; + static const int kItemListStartOffset = 20; + + // Read the number of items. + uint32_t item_count = 0; + if (!BinaryRead(&item_count, kItemCountOffset, blob)) + return false; + + // Traverse over the items. + size_t base_offset = kItemListStartOffset; + for (uint32_t i = 0; i < item_count && !importer->cancelled(); ++i) { + static const int kSizeOffset = 0; + static const int kSortIndexOffset = 4; + static const int kItemIDListOffset = 8; + + // Read the size (number of bytes) of the current item. + uint32_t item_size = 0; + if (!BinaryRead(&item_size, base_offset + kSizeOffset, blob) || + base_offset + item_size <= base_offset || // checking overflow + base_offset + item_size > blob.size()) + return false; + + // Read the sort index of the current item. + uint32_t item_sort_index = 0; + if (!BinaryRead(&item_sort_index, base_offset + kSortIndexOffset, blob)) + return false; + + // Read the file name from the ITEMIDLIST structure. + LPCITEMIDLIST idlist = BinaryReadItemIDList( + base_offset + kItemIDListOffset, item_size - kItemIDListOffset, blob); + TCHAR item_filename[MAX_PATH]; + if (!idlist || !SHGetPathFromIDList(idlist, item_filename)) + return false; + base::FilePath item_relative_path = + path.Append(base::FilePath(item_filename).BaseName()); + + // Record the retrieved information and go to the next item. + sort_index->insert(std::make_pair(item_relative_path, item_sort_index)); + base_offset += item_size; + } + return true; +} + +bool ParseFavoritesOrderRegistryTree( + const Importer* importer, + const base::win::RegKey& key, + const base::FilePath& path, + std::map* sort_index) WARN_UNUSED_RESULT; +bool ParseFavoritesOrderRegistryTree( + const Importer* importer, + const base::win::RegKey& key, + const base::FilePath& path, + std::map* sort_index) { + // Parse the order information of the current folder. + DWORD blob_length = 0; + if (key.ReadValue(L"Order", NULL, &blob_length, NULL) == ERROR_SUCCESS) { + std::vector blob(blob_length); + if (blob_length > 0 && + key.ReadValue(L"Order", reinterpret_cast(&blob[0]), + &blob_length, NULL) == ERROR_SUCCESS) { + if (!ParseFavoritesOrderBlob(importer, blob, path, sort_index)) + return false; + } + } + + // Recursively parse subfolders. + for (base::win::RegistryKeyIterator child(key.Handle(), L""); + child.Valid() && !importer->cancelled(); + ++child) { + base::win::RegKey subkey(key.Handle(), child.Name(), KEY_READ); + if (subkey.Valid()) { + base::FilePath subpath(path.Append(child.Name())); + if (!ParseFavoritesOrderRegistryTree(importer, subkey, subpath, + sort_index)) { + return false; + } + } + } + return true; +} + +bool ParseFavoritesOrderInfo(const Importer* importer, + std::map* sort_index) + WARN_UNUSED_RESULT; +bool ParseFavoritesOrderInfo(const Importer* importer, + std::map* sort_index) { + base::string16 key_path(importer::GetIEFavoritesOrderKey()); + base::win::RegKey key(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ); + if (!key.Valid()) + return false; + return ParseFavoritesOrderRegistryTree(importer, key, base::FilePath(), + sort_index); +} + +// Reads the sort order from registry. If failed, we don't touch the list +// and use the default (alphabetical) order. +void SortBookmarksInIEOrder( + const Importer* importer, + std::vector* bookmarks) { + std::map sort_index; + if (!ParseFavoritesOrderInfo(importer, &sort_index)) + return; + IEOrderBookmarkComparator compare = {&sort_index}; + std::sort(bookmarks->begin(), bookmarks->end(), compare); +} + +// Reads an internet shortcut (*.url) |file| and returns a COM object +// representing it. +bool LoadInternetShortcut( + const base::string16& file, + base::win::ScopedComPtr* shortcut) { + base::win::ScopedComPtr url_locator; + if (FAILED(url_locator.CreateInstance(CLSID_InternetShortcut, NULL, + CLSCTX_INPROC_SERVER))) + return false; + + base::win::ScopedComPtr persist_file; + if (FAILED(persist_file.QueryFrom(url_locator.get()))) + return false; + + // Loads the Internet Shortcut from persistent storage. + if (FAILED(persist_file->Load(file.c_str(), STGM_READ))) + return false; + + std::swap(url_locator, *shortcut); + return true; +} + +// Reads the URL stored in the internet shortcut. +GURL ReadURLFromInternetShortcut(IUniformResourceLocator* url_locator) { + base::win::ScopedCoMem url; + // GetURL can return S_FALSE (FAILED(S_FALSE) is false) when url == NULL. + return (FAILED(url_locator->GetURL(&url)) || !url) ? + GURL() : GURL(url.get()); +} + +// Reads the URL of the favicon of the internet shortcut. +GURL ReadFaviconURLFromInternetShortcut(IUniformResourceLocator* url_locator) { + base::win::ScopedComPtr property_set_storage; + if (FAILED(property_set_storage.QueryFrom(url_locator))) + return GURL(); + + base::win::ScopedComPtr property_storage; + if (FAILED(property_set_storage->Open(FMTID_Intshcut, STGM_READ, + property_storage.Receive()))) { + return GURL(); + } + + PROPSPEC properties[] = {{PRSPEC_PROPID, {PID_IS_ICONFILE}}}; + // ReadMultiple takes a non-const array of PROPVARIANTs, but since this code + // only needs an array of size 1: a non-const pointer to |output| is + // equivalent. + base::win::ScopedPropVariant output; + // ReadMultiple can return S_FALSE (FAILED(S_FALSE) is false) when the + // property is not found, in which case output[0].vt is set to VT_EMPTY. + if (FAILED(property_storage->ReadMultiple(1, properties, output.Receive())) || + output.get().vt != VT_LPWSTR) + return GURL(); + return GURL(output.get().pwszVal); +} + +// Reads the favicon imaga data in an NTFS alternate data stream. This is where +// IE7 and above store the data. +bool ReadFaviconDataFromInternetShortcut(const base::string16& file, + std::string* data) { + return base::ReadFileToString( + base::FilePath(file + kFaviconStreamName), data); +} + +// Reads the favicon imaga data in the Internet cache. IE6 doesn't hold the data +// explicitly, but it might be found in the cache. +bool ReadFaviconDataFromCache(const GURL& favicon_url, std::string* data) { + std::wstring url_wstring(base::UTF8ToWide(favicon_url.spec())); + DWORD info_size = 0; + GetUrlCacheEntryInfoEx(url_wstring.c_str(), NULL, &info_size, NULL, NULL, + NULL, 0); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return false; + + std::vector buf(info_size); + INTERNET_CACHE_ENTRY_INFO* cache = + reinterpret_cast(&buf[0]); + if (!GetUrlCacheEntryInfoEx(url_wstring.c_str(), cache, &info_size, NULL, + NULL, NULL, 0)) { + return false; + } + return base::ReadFileToString(base::FilePath(cache->lpszLocalFileName), data); +} + +// Reads the binary image data of favicon of an internet shortcut file |file|. +// |favicon_url| read by ReadFaviconURLFromInternetShortcut is also needed to +// examine the IE cache. +bool ReadReencodedFaviconData(const base::string16& file, + const GURL& favicon_url, + std::vector* data) { + std::string image_data; + if (!ReadFaviconDataFromInternetShortcut(file, &image_data) && + !ReadFaviconDataFromCache(favicon_url, &image_data)) { + return false; + } + + const unsigned char* ptr = + reinterpret_cast(image_data.c_str()); + return importer::ReencodeFavicon(ptr, image_data.size(), data); +} + +// Loads favicon image data and registers to |favicon_map|. +void UpdateFaviconMap( + const base::string16& url_file, + const GURL& url, + IUniformResourceLocator* url_locator, + std::map* favicon_map) { + GURL favicon_url = ReadFaviconURLFromInternetShortcut(url_locator); + if (!favicon_url.is_valid()) + return; + + std::map::iterator it = + favicon_map->find(favicon_url); + if (it != favicon_map->end()) { + // Known favicon URL. + it->second.urls.insert(url); + } else { + // New favicon URL. Read the image data and store. + favicon_base::FaviconUsageData usage; + if (ReadReencodedFaviconData(url_file, favicon_url, &usage.png_data)) { + usage.favicon_url = favicon_url; + usage.urls.insert(url); + favicon_map->insert(std::make_pair(favicon_url, usage)); + } + } +} + +} // namespace + +// static +// {E161255A-37C3-11D2-BCAA-00C04fD929DB} +const GUID IEImporter::kPStoreAutocompleteGUID = { + 0xe161255a, 0x37c3, 0x11d2, + { 0xbc, 0xaa, 0x00, 0xc0, 0x4f, 0xd9, 0x29, 0xdb } +}; +// {A79029D6-753E-4e27-B807-3D46AB1545DF} +const GUID IEImporter::kUnittestGUID = { + 0xa79029d6, 0x753e, 0x4e27, + { 0xb8, 0x7, 0x3d, 0x46, 0xab, 0x15, 0x45, 0xdf } +}; + +IEImporter::IEImporter() : edge_import_mode_(false) {} + +void IEImporter::StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) { + edge_import_mode_ = source_profile.importer_type == importer::TYPE_EDGE; + bridge_ = bridge; + + if (edge_import_mode_) { + // When using for Edge imports we only support Favorites. + DCHECK_EQ(items, importer::FAVORITES); + // As coming from untrusted source ensure items is correct. + items = importer::FAVORITES; + } + source_path_ = source_profile.source_path; + + bridge_->NotifyStarted(); + + if ((items & importer::HOME_PAGE) && !cancelled()) { + bridge_->NotifyItemStarted(importer::HOME_PAGE); + ImportHomepage(); // Doesn't have a UI item. + bridge_->NotifyItemEnded(importer::HOME_PAGE); + } + // The order here is important! + if ((items & importer::HISTORY) && !cancelled()) { + bridge_->NotifyItemStarted(importer::HISTORY); + ImportHistory(); + bridge_->NotifyItemEnded(importer::HISTORY); + } + if ((items & importer::FAVORITES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::FAVORITES); + ImportFavorites(); + bridge_->NotifyItemEnded(importer::FAVORITES); + } + if ((items & importer::SEARCH_ENGINES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::SEARCH_ENGINES); + ImportSearchEngines(); + bridge_->NotifyItemEnded(importer::SEARCH_ENGINES); + } + if ((items & importer::PASSWORDS) && !cancelled()) { + bridge_->NotifyItemStarted(importer::PASSWORDS); + // Always import IE6 passwords. + ImportPasswordsIE6(); + + if (CurrentIEVersion() >= 7) + ImportPasswordsIE7(); + bridge_->NotifyItemEnded(importer::PASSWORDS); + } + bridge_->NotifyEnded(); +} + +IEImporter::~IEImporter() { +} + +void IEImporter::ImportFavorites() { + FavoritesInfo info; + if (!GetFavoritesInfo(&info)) + return; + + BookmarkVector bookmarks; + favicon_base::FaviconUsageDataList favicons; + ParseFavoritesFolder(info, &bookmarks, &favicons); + + if (!bookmarks.empty() && !cancelled()) { + const base::string16& first_folder_name = + edge_import_mode_ + /* + ? l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_EDGE) + : l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_IE); + */ + ? base::UTF8ToUTF16("Imported from Edge") + : base::UTF8ToUTF16("Imported from IE"); + + bridge_->AddBookmarks(bookmarks, first_folder_name); + } + if (!favicons.empty() && !cancelled()) + bridge_->SetFavicons(favicons); +} + +void IEImporter::ImportHistory() { + const std::string kSchemes[] = {url::kHttpScheme, + url::kHttpsScheme, + url::kFtpScheme, + url::kFileScheme}; + int total_schemes = arraysize(kSchemes); + + base::win::ScopedComPtr url_history_stg2; + if (FAILED(url_history_stg2.CreateInstance(CLSID_CUrlHistory, NULL, + CLSCTX_INPROC_SERVER))) { + return; + } + base::win::ScopedComPtr enum_url; + if (SUCCEEDED(url_history_stg2->EnumUrls(enum_url.Receive()))) { + std::vector rows; + STATURL stat_url; + + // IEnumSTATURL::Next() doesn't fill STATURL::dwFlags by default. Need to + // call IEnumSTATURL::SetFilter() with STATURL_QUERYFLAG_TOPLEVEL flag to + // specify how STATURL structure will be filled. + // The first argument of IEnumSTATURL::SetFilter() specifies the URL prefix + // that is used by IEnumSTATURL::Next() for filtering items by URL. + // So need to pass an empty string here to get all history items. + enum_url->SetFilter(L"", STATURL_QUERYFLAG_TOPLEVEL); + while (!cancelled() && + enum_url->Next(1, &stat_url, NULL) == S_OK) { + base::string16 url_string; + if (stat_url.pwcsUrl) { + url_string = stat_url.pwcsUrl; + CoTaskMemFree(stat_url.pwcsUrl); + } + base::string16 title_string; + if (stat_url.pwcsTitle) { + title_string = stat_url.pwcsTitle; + CoTaskMemFree(stat_url.pwcsTitle); + } + + GURL url(url_string); + // Skips the URLs that are invalid or have other schemes. + if (!url.is_valid() || + (std::find(kSchemes, kSchemes + total_schemes, url.scheme()) == + kSchemes + total_schemes)) + continue; + + ImporterURLRow row(url); + row.title = title_string; + row.last_visit = base::Time::FromFileTime(stat_url.ftLastVisited); + if (stat_url.dwFlags == STATURLFLAG_ISTOPLEVEL) { + row.visit_count = 1; + row.hidden = false; + } else { + // dwFlags should only contain the STATURLFLAG_ISTOPLEVEL bit per + // the filter set above. + DCHECK(!stat_url.dwFlags); + row.hidden = true; + } + + rows.push_back(row); + } + + if (!rows.empty() && !cancelled()) { + bridge_->SetHistoryItems(rows, importer::VISIT_SOURCE_IE_IMPORTED); + } + } +} + +void IEImporter::ImportPasswordsIE6() { + GUID AutocompleteGUID = kPStoreAutocompleteGUID; + if (!source_path_.empty()) { + // We supply a fake GUID for testting. + AutocompleteGUID = kUnittestGUID; + } + + // The PStoreCreateInstance function retrieves an interface pointer + // to a storage provider. But this function has no associated import + // library or header file, we must call it using the LoadLibrary() + // and GetProcAddress() functions. + typedef HRESULT (WINAPI *PStoreCreateFunc)(IPStore**, DWORD, DWORD, DWORD); + HMODULE pstorec_dll = LoadLibrary(L"pstorec.dll"); + if (!pstorec_dll) + return; + PStoreCreateFunc PStoreCreateInstance = + (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance"); + if (!PStoreCreateInstance) { + FreeLibrary(pstorec_dll); + return; + } + + base::win::ScopedComPtr pstore; + HRESULT result = PStoreCreateInstance(pstore.Receive(), 0, 0, 0); + if (result != S_OK) { + FreeLibrary(pstorec_dll); + return; + } + + std::vector ac_list; + + // Enumerates AutoComplete items in the protected database. + base::win::ScopedComPtr item; + result = pstore->EnumItems(0, &AutocompleteGUID, + &AutocompleteGUID, 0, item.Receive()); + if (result != PST_E_OK) { + pstore.Release(); + FreeLibrary(pstorec_dll); + return; + } + + wchar_t* item_name; + while (!cancelled() && SUCCEEDED(item->Next(1, &item_name, 0))) { + DWORD length = 0; + unsigned char* buffer = NULL; + result = pstore->ReadItem(0, &AutocompleteGUID, &AutocompleteGUID, + item_name, &length, &buffer, NULL, 0); + if (SUCCEEDED(result)) { + AutoCompleteInfo ac; + ac.key = item_name; + base::string16 data; + data.insert(0, reinterpret_cast(buffer), + length / sizeof(wchar_t)); + + // The key name is always ended with ":StringData". + const wchar_t kDataSuffix[] = L":StringData"; + size_t i = ac.key.rfind(kDataSuffix); + if (i != base::string16::npos && ac.key.substr(i) == kDataSuffix) { + ac.key.erase(i); + ac.is_url = (ac.key.find(L"://") != base::string16::npos); + ac_list.push_back(ac); + ac_list[ac_list.size() - 1].data = base::SplitString( + data, base::string16(1, '\0'), + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + } + CoTaskMemFree(buffer); + } + CoTaskMemFree(item_name); + } + // Releases them before unload the dll. + item.Release(); + pstore.Release(); + FreeLibrary(pstorec_dll); + + size_t i; + for (i = 0; i < ac_list.size(); i++) { + if (!ac_list[i].is_url || ac_list[i].data.size() < 2) + continue; + + GURL url(ac_list[i].key.c_str()); + if (!(base::LowerCaseEqualsASCII(url.scheme(), url::kHttpScheme) || + base::LowerCaseEqualsASCII(url.scheme(), url::kHttpsScheme))) { + continue; + } + + autofill::PasswordForm form; + GURL::Replacements rp; + rp.ClearUsername(); + rp.ClearPassword(); + rp.ClearQuery(); + rp.ClearRef(); + form.origin = url.ReplaceComponents(rp); + form.username_value = ac_list[i].data[0]; + form.password_value = ac_list[i].data[1]; + form.signon_realm = url.GetOrigin().spec(); + + // This is not precise, because a scheme of https does not imply a valid + // certificate was presented; however we assign it this way so that if we + // import a password from IE whose scheme is https, we give it the benefit + // of the doubt and DON'T auto-fill it unless the form appears under + // valid TLS conditions. + form.ssl_valid = url.SchemeIsCryptographic(); + + // Goes through the list to find out the username field + // of the web page. + size_t list_it, item_it; + for (list_it = 0; list_it < ac_list.size(); ++list_it) { + if (ac_list[list_it].is_url) + continue; + + for (item_it = 0; item_it < ac_list[list_it].data.size(); ++item_it) + if (ac_list[list_it].data[item_it] == form.username_value) { + form.username_element = ac_list[list_it].key; + break; + } + } + + bridge_->SetPasswordForm(form); + } +} + +void IEImporter::ImportPasswordsIE7() { + base::string16 key_path(importer::GetIE7PasswordsKey()); + base::win::RegKey key(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ); + base::win::RegistryValueIterator reg_iterator(HKEY_CURRENT_USER, + key_path.c_str()); + importer::ImporterIE7PasswordInfo password_info; + while (reg_iterator.Valid() && !cancelled()) { + // Get the size of the encrypted data. + DWORD value_len = 0; + key.ReadValue(reg_iterator.Name(), NULL, &value_len, NULL); + if (value_len) { + // Query the encrypted data. + password_info.encrypted_data.resize(value_len); + if (key.ReadValue(reg_iterator.Name(), + &password_info.encrypted_data.front(), + &value_len, NULL) == ERROR_SUCCESS) { + password_info.url_hash = reg_iterator.Name(); + password_info.date_created = base::Time::Now(); + + bridge_->AddIE7PasswordInfo(password_info); + } + } + + ++reg_iterator; + } +} + +void IEImporter::ImportSearchEngines() { + // On IE, search engines are stored in the registry, under: + // Software\Microsoft\Internet Explorer\SearchScopes + // Each key represents a search engine. The URL value contains the URL and + // the DisplayName the name. + typedef std::map SearchEnginesMap; + SearchEnginesMap search_engines_map; + for (base::win::RegistryKeyIterator key_iter(HKEY_CURRENT_USER, + kSearchScopePath); key_iter.Valid(); ++key_iter) { + base::string16 sub_key_name = kSearchScopePath; + sub_key_name.append(L"\\").append(key_iter.Name()); + base::win::RegKey sub_key(HKEY_CURRENT_USER, sub_key_name.c_str(), + KEY_READ); + base::string16 wide_url; + if ((sub_key.ReadValue(L"URL", &wide_url) != ERROR_SUCCESS) || + wide_url.empty()) { + VLOG(1) << "No URL for IE search engine at " << key_iter.Name(); + continue; + } + // For the name, we try the default value first (as Live Search uses a + // non displayable name in DisplayName, and the readable name under the + // default value). + base::string16 name; + if ((sub_key.ReadValue(NULL, &name) != ERROR_SUCCESS) || name.empty()) { + // Try the displayable name. + if ((sub_key.ReadValue(L"DisplayName", &name) != ERROR_SUCCESS) || + name.empty()) { + VLOG(1) << "No name for IE search engine at " << key_iter.Name(); + continue; + } + } + + std::string url(base::WideToUTF8(wide_url)); + SearchEnginesMap::iterator t_iter = search_engines_map.find(url); + if (t_iter == search_engines_map.end()) { + // First time we see that URL. + GURL gurl(url); + if (gurl.is_valid()) { + t_iter = search_engines_map.insert(std::make_pair(url, name)).first; + } + } + } + // ProfileWriter::AddKeywords() requires a vector and we have a map. + std::vector search_engines; + for (SearchEnginesMap::iterator i = search_engines_map.begin(); + i != search_engines_map.end(); ++i) { + importer::SearchEngineInfo search_engine_info; + search_engine_info.url = base::UTF8ToUTF16(i->first); + search_engine_info.display_name = i->second; + search_engines.push_back(search_engine_info); + } + bridge_->SetKeywords(search_engines, true); +} + +void IEImporter::ImportHomepage() { + const wchar_t* kIEHomepage = L"Start Page"; + const wchar_t* kIEDefaultHomepage = L"Default_Page_URL"; + + base::string16 key_path(importer::GetIESettingsKey()); + + base::win::RegKey key(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ); + base::string16 homepage_url; + if (key.ReadValue(kIEHomepage, &homepage_url) != ERROR_SUCCESS || + homepage_url.empty()) + return; + + GURL homepage = GURL(homepage_url); + if (!homepage.is_valid()) + return; + + // Check to see if this is the default website and skip import. + base::win::RegKey keyDefault(HKEY_LOCAL_MACHINE, key_path.c_str(), KEY_READ); + base::string16 default_homepage_url; + LONG result = keyDefault.ReadValue(kIEDefaultHomepage, &default_homepage_url); + if (result == ERROR_SUCCESS && !default_homepage_url.empty()) { + if (homepage.spec() == GURL(default_homepage_url).spec()) + return; + } + bridge_->AddHomePage(homepage); +} + +bool IEImporter::GetFavoritesInfo(IEImporter::FavoritesInfo* info) { + if (!source_path_.empty()) { + // Source path exists during testing as well as when importing from Edge. + info->path = source_path_; + info->path = info->path.AppendASCII("Favorites"); + info->links_folder = L"Links"; + return true; + } + + // IE stores the favorites in the Favorites under user profile's folder. + wchar_t buffer[MAX_PATH]; + if (FAILED(SHGetFolderPath(NULL, CSIDL_FAVORITES, NULL, + SHGFP_TYPE_CURRENT, buffer))) + return false; + info->path = base::FilePath(buffer); + + // There is a Links folder under Favorites folder in Windows Vista, but it + // is not recording in Vista's registry. So in Vista, we assume the Links + // folder is under Favorites folder since it looks like there is not name + // different in every language version of Windows Vista. + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + // The Link folder name is stored in the registry. + DWORD buffer_length = sizeof(buffer); + base::win::RegKey reg_key(HKEY_CURRENT_USER, kIEToolbarKey, KEY_READ); + if (reg_key.ReadValue(L"LinksFolderName", buffer, + &buffer_length, NULL) != ERROR_SUCCESS) + return false; + info->links_folder = buffer; + } else { + info->links_folder = L"Links"; + } + + return true; +} + +void IEImporter::ParseFavoritesFolder( + const FavoritesInfo& info, + BookmarkVector* bookmarks, + favicon_base::FaviconUsageDataList* favicons) { + base::FilePath file; + std::vector file_list; + base::FilePath favorites_path(info.path); + // Favorites path length. Make sure it doesn't include the trailing \. + size_t favorites_path_len = + favorites_path.StripTrailingSeparators().value().size(); + base::FileEnumerator file_enumerator( + favorites_path, true, base::FileEnumerator::FILES); + while (!(file = file_enumerator.Next()).value().empty() && !cancelled()) + file_list.push_back(file.value()); + + // Keep the bookmarks in alphabetical order. + std::sort(file_list.begin(), file_list.end()); + + // Map from favicon URLs to the favicon data (the binary image data and the + // set of bookmark URLs referring to the favicon). + typedef std::map FaviconMap; + FaviconMap favicon_map; + + for (std::vector::iterator it = file_list.begin(); + it != file_list.end(); ++it) { + base::FilePath shortcut(*it); + if (!base::LowerCaseEqualsASCII(shortcut.Extension(), ".url")) + continue; + + // Skip the bookmark with invalid URL. + base::win::ScopedComPtr url_locator; + if (!LoadInternetShortcut(*it, &url_locator)) + continue; + GURL url = ReadURLFromInternetShortcut(url_locator.get()); + if (!url.is_valid()) + continue; + // Skip default bookmarks. go.microsoft.com redirects to + // search.microsoft.com, and http://go.microsoft.com/fwlink/?LinkId=XXX, + // which URLs IE has as default, to some another sites. + // We expect that users will never themselves create bookmarks having this + // hostname. + if (url.host() == "go.microsoft.com") + continue; + // Read favicon. + UpdateFaviconMap(*it, url, url_locator.get(), &favicon_map); + + // Make the relative path from the Favorites folder, without the basename. + // ex. Suppose that the Favorites folder is C:\Users\Foo\Favorites. + // C:\Users\Foo\Favorites\Foo.url -> "" + // C:\Users\Foo\Favorites\Links\Bar\Baz.url -> "Links\Bar" + base::FilePath::StringType relative_string = + shortcut.DirName().value().substr(favorites_path_len); + if (!relative_string.empty() && + base::FilePath::IsSeparator(relative_string[0])) + relative_string = relative_string.substr(1); + base::FilePath relative_path(relative_string); + + ImportedBookmarkEntry entry; + // Remove the dot, the file extension, and the directory path. + entry.title = shortcut.RemoveExtension().BaseName().value(); + entry.url = url; + entry.creation_time = GetFileCreationTime(*it); + if (!relative_path.empty()) + relative_path.GetComponents(&entry.path); + + // Add the bookmark. + if (!entry.path.empty() && entry.path[0] == info.links_folder) { + // Bookmarks in the Link folder should be imported to the toolbar. + entry.in_toolbar = true; + } + bookmarks->push_back(entry); + } + + if (!edge_import_mode_) { + // Reflect the menu order in IE. + SortBookmarksInIEOrder(this, bookmarks); + } + + // Record favicon data. + for (FaviconMap::iterator iter = favicon_map.begin(); + iter != favicon_map.end(); ++iter) + favicons->push_back(iter->second); +} + +int IEImporter::CurrentIEVersion() const { + static int version = -1; + if (version < 0) { + wchar_t buffer[128]; + DWORD buffer_length = sizeof(buffer); + base::win::RegKey reg_key(HKEY_LOCAL_MACHINE, kIEVersionKey, KEY_READ); + LONG result = reg_key.ReadValue(L"Version", buffer, &buffer_length, NULL); + version = ((result == ERROR_SUCCESS)? _wtoi(buffer) : 0); + } + return version; +} diff --git a/chromium_src/chrome/utility/importer/ie_importer_win.h b/chromium_src/chrome/utility/importer/ie_importer_win.h new file mode 100644 index 0000000000..cfe8b7f26c --- /dev/null +++ b/chromium_src/chrome/utility/importer/ie_importer_win.h @@ -0,0 +1,91 @@ +// 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. + +#ifndef CHROME_UTILITY_IMPORTER_IE_IMPORTER_WIN_H_ +#define CHROME_UTILITY_IMPORTER_IE_IMPORTER_WIN_H_ + +#include + +#include + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "chrome/utility/importer/importer.h" +#include "components/favicon_base/favicon_usage_data.h" + +struct ImportedBookmarkEntry; + +class IEImporter : public Importer { + public: + IEImporter(); + + // Importer: + void StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) override; + + private: + typedef std::vector BookmarkVector; + + // A struct that hosts the information of IE Favorite folder. + struct FavoritesInfo { + base::FilePath path; + base::string16 links_folder; + }; + + // IE PStore subkey GUID: AutoComplete password & form data. + static const GUID kPStoreAutocompleteGUID; + + // A fake GUID for unit test. + static const GUID kUnittestGUID; + + FRIEND_TEST_ALL_PREFIXES(ImporterTest, IEImporter); + + ~IEImporter() override; + + void ImportFavorites(); + + // Reads history information from COM interface. + void ImportHistory(); + + // Import password for IE6 stored in protected storage. + void ImportPasswordsIE6(); + + // Import password for IE7 and IE8 stored in Storage2. + void ImportPasswordsIE7(); + + void ImportSearchEngines(); + + // Import the homepage setting of IE. Note: IE supports multiple home pages, + // whereas Chrome doesn't, so we import only the one defined under the + // 'Start Page' registry key. We don't import if the homepage is set to the + // machine default. + void ImportHomepage(); + + // Gets the information of Favorites folder. Returns true if successful. + bool GetFavoritesInfo(FavoritesInfo* info); + + // This function will read the files in the Favorites folder, and store + // the bookmark items in |bookmarks| and favicon information in |favicons|. + void ParseFavoritesFolder(const FavoritesInfo& info, + BookmarkVector* bookmarks, + favicon_base::FaviconUsageDataList* favicons); + + // Determines which version of IE is in use. + int CurrentIEVersion() const; + + // Set to true when importing favorites from old Edge on Windows 10. + bool edge_import_mode_; + + // IE does not have source path. It's used in unit tests only for providing a + // fake source and it's used if importing old Edge favorites on Windows 10. + base::FilePath source_path_; + + DISALLOW_COPY_AND_ASSIGN(IEImporter); +}; + +#endif // CHROME_UTILITY_IMPORTER_IE_IMPORTER_WIN_H_ diff --git a/chromium_src/chrome/utility/importer/importer.cc b/chromium_src/chrome/utility/importer/importer.cc new file mode 100644 index 0000000000..8da3b79c69 --- /dev/null +++ b/chromium_src/chrome/utility/importer/importer.cc @@ -0,0 +1,15 @@ +// 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. + +#include "chrome/utility/importer/importer.h" + +#include "chrome/common/importer/importer_bridge.h" + +void Importer::Cancel() { + cancelled_ = true; +} + +Importer::Importer() : cancelled_(false) {} + +Importer::~Importer() {} diff --git a/chromium_src/chrome/utility/importer/importer.h b/chromium_src/chrome/utility/importer/importer.h new file mode 100644 index 0000000000..1b541a27c6 --- /dev/null +++ b/chromium_src/chrome/utility/importer/importer.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef CHROME_UTILITY_IMPORTER_IMPORTER_H_ +#define CHROME_UTILITY_IMPORTER_IMPORTER_H_ + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +class ImporterBridge; + +namespace importer { +struct SourceProfile; +} + +// The base class of all importers. +class Importer : public base::RefCountedThreadSafe { + public: + // All importers should implement this method by adding their import logic. + // And it will be run in file thread by ImporterHost. Since we do async + // import, the importer should invoke ImporterHost::NotifyImportEnded() to + // notify its host that import stuff have been finished. + virtual void StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) = 0; + + // Cancels the import process. + virtual void Cancel(); + + bool cancelled() const { return cancelled_; } + + protected: + friend class base::RefCountedThreadSafe; + + Importer(); + virtual ~Importer(); + + scoped_refptr bridge_; + + private: + // True if the caller cancels the import process. + bool cancelled_; + + DISALLOW_COPY_AND_ASSIGN(Importer); +}; + +#endif // CHROME_UTILITY_IMPORTER_IMPORTER_H_ diff --git a/chromium_src/chrome/utility/importer/importer_creator.cc b/chromium_src/chrome/utility/importer/importer_creator.cc new file mode 100644 index 0000000000..f7cf6bacdb --- /dev/null +++ b/chromium_src/chrome/utility/importer/importer_creator.cc @@ -0,0 +1,62 @@ +// Copyright 2013 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. + +/** + * This is not a straight copy from chromium src, in particular + * we add chrome importer. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include "chrome/utility/importer/importer_creator.h" + +#include "atom/utility/importer/chrome_importer.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "chrome/utility/importer/bookmarks_file_importer.h" +#include "chrome/utility/importer/firefox_importer.h" + +#if defined(OS_WIN) +#include "chrome/common/importer/edge_importer_utils_win.h" +#include "chrome/utility/importer/edge_importer_win.h" +#include "chrome/utility/importer/ie_importer_win.h" +#endif + +#if defined(OS_MACOSX) +#include + +#include "base/mac/foundation_util.h" +#include "chrome/utility/importer/safari_importer.h" +#endif + +namespace importer { + +scoped_refptr CreateImporterByType(ImporterType type) { + switch (type) { +#if defined(OS_WIN) + case TYPE_IE: + return new IEImporter(); + case TYPE_EDGE: + // If legacy mode we pass back an IE importer. + if (IsEdgeFavoritesLegacyMode()) + return new IEImporter(); + return new EdgeImporter(); +#endif + case TYPE_BOOKMARKS_FILE: + return new BookmarksFileImporter(); + case TYPE_FIREFOX: + return new FirefoxImporter(); +#if defined(OS_MACOSX) + case TYPE_SAFARI: + return new SafariImporter(base::mac::GetUserLibraryPath()); +#endif + case TYPE_CHROME: + return new ChromeImporter(); + default: + NOTREACHED(); + return nullptr; + } +} + +} // namespace importer diff --git a/chromium_src/chrome/utility/importer/importer_creator.h b/chromium_src/chrome/utility/importer/importer_creator.h new file mode 100644 index 0000000000..dc61cd1b4f --- /dev/null +++ b/chromium_src/chrome/utility/importer/importer_creator.h @@ -0,0 +1,20 @@ +// Copyright 2013 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. + +#ifndef CHROME_UTILITY_IMPORTER_IMPORTER_CREATOR_H_ +#define CHROME_UTILITY_IMPORTER_IMPORTER_CREATOR_H_ + +#include "base/memory/ref_counted.h" +#include "chrome/common/importer/importer_type.h" + +class Importer; + +namespace importer { + +// Creates an Importer of the specified |type|. +scoped_refptr CreateImporterByType(ImporterType type); + +} // namespace importer + +#endif // CHROME_UTILITY_IMPORTER_IMPORTER_CREATOR_H_ diff --git a/chromium_src/chrome/utility/importer/nss_decryptor.cc b/chromium_src/chrome/utility/importer/nss_decryptor.cc new file mode 100644 index 0000000000..3f3d83834f --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor.cc @@ -0,0 +1,382 @@ +// 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/utility/importer/nss_decryptor.h" + +#include + +#include +#include +#include + +#include "base/base64.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "components/autofill/core/common/password_form.h" +#include "sql/connection.h" +#include "sql/statement.h" + +#if defined(USE_NSS_CERTS) +#include +#include +#endif // defined(USE_NSS_CERTS) + +// This method is based on some Firefox code in +// security/manager/ssl/src/nsSDR.cpp +// The license block is: + +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is the Netscape security libraries. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 1994-2000 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +// Use this structure to store unprocessed information extracted from +// Firefox's password file. +struct FirefoxRawPasswordInfo { + std::string host; + std::string realm; + base::string16 username_element; + base::string16 password_element; + std::string encrypted_username; + std::string encrypted_password; + std::string form_action; +}; + +namespace { + +autofill::PasswordForm CreateBlacklistPasswordForm( + const std::string& blacklist_host) { + GURL::Replacements rep; + rep.ClearQuery(); + rep.ClearRef(); + rep.ClearUsername(); + rep.ClearPassword(); + + autofill::PasswordForm form; + form.origin = GURL(blacklist_host).ReplaceComponents(rep); + form.signon_realm = form.origin.GetOrigin().spec(); + form.blacklisted_by_user = true; + return form; +} + +} // namespace + +base::string16 NSSDecryptor::Decrypt(const std::string& crypt) const { + // Do nothing if NSS is not loaded. + if (!is_nss_initialized_) + return base::string16(); + + if (crypt.empty()) + return base::string16(); + + // The old style password is encoded in base64. They are identified + // by a leading '~'. Otherwise, we should decrypt the text. + std::string plain; + if (crypt[0] != '~') { + std::string decoded_data; + if (!base::Base64Decode(crypt, &decoded_data)) + return base::string16(); + PK11SlotInfo* slot = GetKeySlotForDB(); + SECStatus result = PK11_Authenticate(slot, PR_TRUE, NULL); + if (result != SECSuccess) { + FreeSlot(slot); + return base::string16(); + } + + SECItem request; + request.data = reinterpret_cast( + const_cast(decoded_data.data())); + request.len = static_cast(decoded_data.size()); + SECItem reply; + reply.data = NULL; + reply.len = 0; +#if defined(USE_NSS_CERTS) + result = PK11SDR_DecryptWithSlot(slot, &request, &reply, NULL); +#else + result = PK11SDR_Decrypt(&request, &reply, NULL); +#endif // defined(USE_NSS_CERTS) + if (result == SECSuccess) + plain.assign(reinterpret_cast(reply.data), reply.len); + + SECITEM_FreeItem(&reply, PR_FALSE); + FreeSlot(slot); + } else { + // Deletes the leading '~' before decoding. + if (!base::Base64Decode(crypt.substr(1), &plain)) + return base::string16(); + } + + return base::UTF8ToUTF16(plain); +} + +// There are three versions of password files. They store saved user +// names and passwords. +// References: +// http://kb.mozillazine.org/Signons.txt +// http://kb.mozillazine.org/Signons2.txt +// http://kb.mozillazine.org/Signons3.txt +void NSSDecryptor::ParseSignons(const base::FilePath& signon_file, + std::vector* forms) { + forms->clear(); + + std::string content; + base::ReadFileToString(signon_file, &content); + + // Splits the file content into lines. + std::vector lines = base::SplitString( + content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + + // The first line is the file version. We skip the unknown versions. + if (lines.empty()) + return; + int version; + if (lines[0] == "#2c") + version = 1; + else if (lines[0] == "#2d") + version = 2; + else if (lines[0] == "#2e") + version = 3; + else + return; + + // Reads never-saved list. Domains are stored one per line. + size_t i; + for (i = 1; i < lines.size() && lines[i].compare(".") != 0; ++i) + forms->push_back(CreateBlacklistPasswordForm(lines[i])); + ++i; + + // Reads saved passwords. The information is stored in blocks + // seperated by lines that only contain a dot. We find a block + // by the seperator and parse them one by one. + while (i < lines.size()) { + size_t begin = i; + size_t end = i + 1; + while (end < lines.size() && lines[end].compare(".") != 0) + ++end; + i = end + 1; + + // A block has at least five lines. + if (end - begin < 5) + continue; + + FirefoxRawPasswordInfo raw_password_info; + + // The first line is the site URL. + // For HTTP authentication logins, the URL may contain http realm, + // which will be in bracket: + // sitename:8080 (realm) + const char kRealmBracketBegin[] = " ("; + const char kRealmBracketEnd[] = ")"; + if (lines[begin].find(kRealmBracketBegin) != std::string::npos) { + size_t start = lines[begin].find(kRealmBracketBegin); + raw_password_info.host = lines[begin].substr(0, start); + start += std::string(kRealmBracketBegin).size(); + size_t end = lines[begin].rfind(kRealmBracketEnd); + raw_password_info.realm = lines[begin].substr(start, end - start); + } else { + raw_password_info.host = lines[begin]; + } + + ++begin; + + // There may be multiple username/password pairs for this site. + // In this case, they are saved in one block without a seperated + // line (contains a dot). + while (begin + 4 < end) { + // The user name. + raw_password_info.username_element = base::UTF8ToUTF16(lines[begin++]); + raw_password_info.encrypted_username = lines[begin++]; + // The element name has a leading '*'. + if (lines[begin].at(0) == '*') { + raw_password_info.password_element = + base::UTF8ToUTF16(lines[begin++].substr(1)); + raw_password_info.encrypted_password = lines[begin++]; + } else { + // Maybe the file is bad, we skip to next block. + break; + } + // The action attribute from the form element. This line exists + // in versin 2 or above. + if (version >= 2) { + if (begin < end) + raw_password_info.form_action = lines[begin]; + ++begin; + } + // Version 3 has an extra line for further use. + if (version == 3) + ++begin; + + autofill::PasswordForm form; + if (CreatePasswordFormFromRawInfo(raw_password_info, &form)) + forms->push_back(form); + } + } +} + +bool NSSDecryptor::ReadAndParseSignons( + const base::FilePath& sqlite_file, + std::vector* forms) { + sql::Connection db; + if (!db.Open(sqlite_file)) + return false; + + const char query[] = "SELECT hostname FROM moz_disabledHosts"; + sql::Statement s(db.GetUniqueStatement(query)); + if (!s.is_valid()) + return false; + + // Read domains for which passwords are never saved. + while (s.Step()) + forms->push_back(CreateBlacklistPasswordForm(s.ColumnString(0))); + + const char query2[] = "SELECT hostname, httpRealm, formSubmitURL, " + "usernameField, passwordField, encryptedUsername, " + "encryptedPassword FROM moz_logins"; + + sql::Statement s2(db.GetUniqueStatement(query2)); + if (!s2.is_valid()) + return false; + + while (s2.Step()) { + FirefoxRawPasswordInfo raw_password_info; + raw_password_info.host = s2.ColumnString(0); + raw_password_info.realm = s2.ColumnString(1); + // The user name, password and action. + raw_password_info.username_element = s2.ColumnString16(3); + raw_password_info.encrypted_username = s2.ColumnString(5); + raw_password_info.password_element = s2.ColumnString16(4); + raw_password_info.encrypted_password = s2.ColumnString(6); + raw_password_info.form_action = s2.ColumnString(2); + autofill::PasswordForm form; + if (CreatePasswordFormFromRawInfo(raw_password_info, &form)) + forms->push_back(form); + } + return true; +} + +bool NSSDecryptor::ReadAndParseLogins( + const base::FilePath& json_file, + std::vector* forms) { + std::string json_content; + base::ReadFileToString(json_file, &json_content); + std::unique_ptr parsed_json( + base::JSONReader::Read(json_content)); + const base::DictionaryValue* password_dict; + const base::ListValue* password_list; + const base::ListValue* blacklist_domains; + if (!parsed_json || !parsed_json->GetAsDictionary(&password_dict)) + return false; + + if (password_dict->GetList("disabledHosts", &blacklist_domains)) { + for (const base::Value* value : *blacklist_domains) { + std::string disabled_host; + if (!value->GetAsString(&disabled_host)) + continue; + forms->push_back(CreateBlacklistPasswordForm(disabled_host)); + } + } + + if (password_dict->GetList("logins", &password_list)) { + for (const base::Value* value : *password_list) { + const base::DictionaryValue* password_detail; + if (!value->GetAsDictionary(&password_detail)) + continue; + + FirefoxRawPasswordInfo raw_password_info; + password_detail->GetString("hostname", &raw_password_info.host); + password_detail->GetString("usernameField", + &raw_password_info.username_element); + password_detail->GetString("passwordField", + &raw_password_info.password_element); + password_detail->GetString("encryptedUsername", + &raw_password_info.encrypted_username); + password_detail->GetString("encryptedPassword", + &raw_password_info.encrypted_password); + password_detail->GetString("formSubmitURL", + &raw_password_info.form_action); + password_detail->GetString("httpRealm", &raw_password_info.realm); + + autofill::PasswordForm form; + if (CreatePasswordFormFromRawInfo(raw_password_info, &form)) + forms->push_back(form); + } + } + + return true; +} + +bool NSSDecryptor::CreatePasswordFormFromRawInfo( + const FirefoxRawPasswordInfo& raw_password_info, + autofill::PasswordForm* form) { + GURL::Replacements rep; + rep.ClearQuery(); + rep.ClearRef(); + rep.ClearUsername(); + rep.ClearPassword(); + + GURL url; + if (!raw_password_info.realm.empty() && + raw_password_info.host.find("://") == std::string::npos) { + // Assume HTTP for forms with non-empty realm and no scheme in hostname. + url = GURL("http://" + raw_password_info.host); + } else { + url = GURL(raw_password_info.host); + } + // Skip this login if the URL is not valid. + if (!url.is_valid()) + return false; + + form->origin = url.ReplaceComponents(rep); + form->signon_realm = form->origin.GetOrigin().spec(); + if (!raw_password_info.realm.empty()) { + form->signon_realm += raw_password_info.realm; + // Non-empty realm indicates that it's not html form authentication entry. + // Extracted data doesn't allow us to distinguish basic_auth entry from + // digest_auth entry, so let's assume basic_auth. + form->scheme = autofill::PasswordForm::SCHEME_BASIC; + } + form->ssl_valid = form->origin.SchemeIsCryptographic(); + form->username_element = raw_password_info.username_element; + form->username_value = Decrypt(raw_password_info.encrypted_username); + form->password_element = raw_password_info.password_element; + form->password_value = Decrypt(raw_password_info.encrypted_password); + form->action = GURL(raw_password_info.form_action).ReplaceComponents(rep); + + return true; +} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor.h b/chromium_src/chrome/utility/importer/nss_decryptor.h new file mode 100644 index 0000000000..d53e640b51 --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor.h @@ -0,0 +1,20 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_H_ +#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_H_ + +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include "chrome/utility/importer/nss_decryptor_mac.h" +#elif defined(OS_WIN) +#include "chrome/utility/importer/nss_decryptor_win.h" +#elif defined(USE_NSS_CERTS) +#include "chrome/utility/importer/nss_decryptor_system_nss.h" +#else +#error NSSDecryptor not implemented. +#endif + +#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_H_ diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_mac.h b/chromium_src/chrome/utility/importer/nss_decryptor_mac.h new file mode 100644 index 0000000000..941ce5cf58 --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor_mac.h @@ -0,0 +1,178 @@ +// 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. + +#ifndef CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_MAC_H_ +#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_MAC_H_ + +#include +#include + +#include "base/macros.h" +#include "base/strings/string16.h" + +namespace base { +class FilePath; +} + +// The following declarations of functions and types are from Firefox +// NSS library. +// source code: +// security/nss/lib/util/seccomon.h +// security/nss/lib/nss/nss.h +// The license block is: + +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is the Netscape security libraries. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 1994-2000 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +enum SECItemType { + siBuffer = 0, + siClearDataBuffer = 1, + siCipherDataBuffer = 2, + siDERCertBuffer = 3, + siEncodedCertBuffer = 4, + siDERNameBuffer = 5, + siEncodedNameBuffer = 6, + siAsciiNameString = 7, + siAsciiString = 8, + siDEROID = 9, + siUnsignedInteger = 10, + siUTCTime = 11, + siGeneralizedTime = 12 +}; + +struct SECItem { + SECItemType type; + unsigned char *data; + unsigned int len; +}; + +enum SECStatus { + SECWouldBlock = -2, + SECFailure = -1, + SECSuccess = 0 +}; + +typedef int PRBool; +#define PR_TRUE 1 +#define PR_FALSE 0 + +typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; + +typedef struct PK11SlotInfoStr PK11SlotInfo; + +typedef SECStatus (*NSSInitFunc)(const char *configdir); +typedef SECStatus (*NSSShutdownFunc)(void); +typedef PK11SlotInfo* (*PK11GetInternalKeySlotFunc)(void); +typedef void (*PK11FreeSlotFunc)(PK11SlotInfo *slot); +typedef SECStatus (*PK11CheckUserPasswordFunc)(PK11SlotInfo *slot, char *pw); +typedef SECStatus + (*PK11AuthenticateFunc)(PK11SlotInfo *slot, PRBool loadCerts, void *wincx); +typedef SECStatus + (*PK11SDRDecryptFunc)(SECItem *data, SECItem *result, void *cx); +typedef void (*SECITEMFreeItemFunc)(SECItem *item, PRBool free_it); +typedef void (*PLArenaFinishFunc)(void); +typedef PRStatus (*PRCleanupFunc)(void); + +struct FirefoxRawPasswordInfo; + +namespace autofill { +struct PasswordForm; +} + +// A wrapper for Firefox NSS decrypt component. +class NSSDecryptor { + public: + NSSDecryptor() + : NSS_Init(NULL), NSS_Shutdown(NULL), PK11_GetInternalKeySlot(NULL), + PK11_CheckUserPassword(NULL), PK11_FreeSlot(NULL), + PK11_Authenticate(NULL), PK11SDR_Decrypt(NULL), SECITEM_FreeItem(NULL), + is_nss_initialized_(false) {} + ~NSSDecryptor(); + + // Initializes NSS if it hasn't already been initialized. + bool Init(const base::FilePath& dll_path, const base::FilePath& db_path); + + // Decrypts Firefox stored passwords. Before using this method, + // make sure Init() returns true. + base::string16 Decrypt(const std::string& crypt) const; + + // Parses the Firefox password file content, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + void ParseSignons(const base::FilePath& signon_file, + std::vector* forms); + + // Reads and parses the Firefox password sqlite db, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + bool ReadAndParseSignons(const base::FilePath& sqlite_file, + std::vector* forms); + + // Reads and parses the Firefox password file logins.json, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + bool ReadAndParseLogins(const base::FilePath& json_file, + std::vector* forms); + + private: + PK11SlotInfo* GetKeySlotForDB() const { return PK11_GetInternalKeySlot(); } + + void FreeSlot(PK11SlotInfo* slot) const { PK11_FreeSlot(slot); } + + // Turns unprocessed information extracted from Firefox's password file + // into PasswordForm. + bool CreatePasswordFormFromRawInfo( + const FirefoxRawPasswordInfo& raw_password_info, + autofill::PasswordForm* form); + + // Methods in Firefox security components. + NSSInitFunc NSS_Init; + NSSShutdownFunc NSS_Shutdown; + PK11GetInternalKeySlotFunc PK11_GetInternalKeySlot; + PK11CheckUserPasswordFunc PK11_CheckUserPassword; + PK11FreeSlotFunc PK11_FreeSlot; + PK11AuthenticateFunc PK11_Authenticate; + PK11SDRDecryptFunc PK11SDR_Decrypt; + SECITEMFreeItemFunc SECITEM_FreeItem; + + // True if NSS_Init() has been called + bool is_nss_initialized_; + + DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); +}; + +#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_MAC_H_ diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_mac.mm b/chromium_src/chrome/utility/importer/nss_decryptor_mac.mm new file mode 100644 index 0000000000..c01e96e11b --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor_mac.mm @@ -0,0 +1,71 @@ +// Copyright (c) 2009 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 + +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/strings/sys_string_conversions.h" + +#include "chrome/common/importer/firefox_importer_utils.h" +#include "chrome/utility/importer/nss_decryptor_mac.h" + +// Important!! : On OS X the nss3 libraries are compiled with depedencies +// on one another, referenced using dyld's @executable_path directive. +// To make a long story short in order to get the libraries to load, dyld's +// fallback path needs to be set to the directory containing the libraries. +// To do so, the process this function runs in must have the +// DYLD_FALLBACK_LIBRARY_PATH set on startup to said directory. +bool NSSDecryptor::Init(const base::FilePath& dll_path, + const base::FilePath& db_path) { + if (getenv("DYLD_FALLBACK_LIBRARY_PATH") == NULL) { + LOG(ERROR) << "DYLD_FALLBACK_LIBRARY_PATH variable not set"; + return false; + } + base::FilePath nss3_path = dll_path.Append("libnss3.dylib"); + + void* nss_3_lib = dlopen(nss3_path.value().c_str(), RTLD_LAZY); + if (!nss_3_lib) { + LOG(ERROR) << "Failed to load nss3 lib" << dlerror(); + return false; + } + + NSS_Init = (NSSInitFunc)dlsym(nss_3_lib, "NSS_Init"); + NSS_Shutdown = (NSSShutdownFunc)dlsym(nss_3_lib, "NSS_Shutdown"); + PK11_GetInternalKeySlot = + (PK11GetInternalKeySlotFunc)dlsym(nss_3_lib, "PK11_GetInternalKeySlot"); + PK11_CheckUserPassword = + (PK11CheckUserPasswordFunc)dlsym(nss_3_lib, "PK11_CheckUserPassword"); + PK11_FreeSlot = (PK11FreeSlotFunc)dlsym(nss_3_lib, "PK11_FreeSlot"); + PK11_Authenticate = + (PK11AuthenticateFunc)dlsym(nss_3_lib, "PK11_Authenticate"); + PK11SDR_Decrypt = (PK11SDRDecryptFunc)dlsym(nss_3_lib, "PK11SDR_Decrypt"); + SECITEM_FreeItem = (SECITEMFreeItemFunc)dlsym(nss_3_lib, "SECITEM_FreeItem"); + + if (!NSS_Init || !NSS_Shutdown || !PK11_GetInternalKeySlot || + !PK11_CheckUserPassword || !PK11_FreeSlot || !PK11_Authenticate || + !PK11SDR_Decrypt || !SECITEM_FreeItem) { + LOG(ERROR) << "NSS3 importer couldn't find entry points"; + return false; + } + + SECStatus result = NSS_Init(db_path.value().c_str()); + + if (result != SECSuccess) { + LOG(ERROR) << "NSS_Init Failed returned: " << result; + return false; + } + + is_nss_initialized_ = true; + return true; +} + +NSSDecryptor::~NSSDecryptor() { + if (NSS_Shutdown && is_nss_initialized_) { + NSS_Shutdown(); + is_nss_initialized_ = false; + } +} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc b/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc new file mode 100644 index 0000000000..9d5d2486fb --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc @@ -0,0 +1,273 @@ +// 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. + +#include "chrome/utility/importer/nss_decryptor_system_nss.h" + +#include +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "crypto/nss_util.h" + +NSSDecryptor::NSSDecryptor() : is_nss_initialized_(false), db_slot_(NULL) {} +NSSDecryptor::~NSSDecryptor() { + if (db_slot_) { + // Deliberately leave the user db open, just in case we need to open more + // than one, because there's an NSS bug with reopening user dbs. + // https://bugzilla.mozilla.org/show_bug.cgi?id=506140 + // SECMOD_CloseUserDB(db_slot_); + PK11_FreeSlot(db_slot_); + } +} + +bool NSSDecryptor::Init(const base::FilePath& dll_path, + const base::FilePath& db_path) { + crypto::EnsureNSSInit(); + is_nss_initialized_ = true; + const std::string modspec = + base::StringPrintf( + "configDir='%s' tokenDescription='Firefox NSS database' " + "flags=readOnly", + db_path.value().c_str()); + db_slot_ = SECMOD_OpenUserDB(modspec.c_str()); + return db_slot_ != NULL; +} + +// This method is based on some NSS code in +// security/nss/lib/pk11wrap/pk11sdr.c, CVS revision 1.22 +// This code is copied because the implementation assumes the use of the +// internal key slot for decryption, but we need to use another slot. +// The license block is: +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * thayes@netscape.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Data structure and template for encoding the result of an SDR operation + * This is temporary. It should include the algorithm ID of the encryption + * mechanism + */ +struct SDRResult +{ + SECItem keyid; + SECAlgorithmID alg; + SECItem data; +}; +typedef struct SDRResult SDRResult; + +static SEC_ASN1Template g_template[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) }, + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) }, + { 0 } +}; + +static SECStatus +unpadBlock(SECItem *data, int blockSize, SECItem *result) +{ + SECStatus rv = SECSuccess; + int padLength; + int i; + + result->data = 0; + result->len = 0; + + /* Remove the padding from the end if the input data */ + if (data->len == 0 || data->len % blockSize != 0) { + rv = SECFailure; + goto loser; + } + + padLength = data->data[data->len-1]; + if (padLength > blockSize) { rv = SECFailure; goto loser; } + + /* verify padding */ + for (i = data->len - padLength; static_cast(i) < data->len; i++) { + if (data->data[i] != padLength) { + rv = SECFailure; + goto loser; + } + } + + result->len = data->len - padLength; + result->data = (unsigned char *)PORT_Alloc(result->len); + if (!result->data) { rv = SECFailure; goto loser; } + + PORT_Memcpy(result->data, data->data, result->len); + + if (padLength < 2) { + return SECWouldBlock; + } + +loser: + return rv; +} + +/* decrypt a block */ +static SECStatus +pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena, + CK_MECHANISM_TYPE type, PK11SymKey *key, + SECItem *params, SECItem *in, SECItem *result) +{ + PK11Context *ctx = 0; + SECItem paddedResult; + SECStatus rv; + + paddedResult.len = 0; + paddedResult.data = 0; + + ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params); + if (!ctx) { rv = SECFailure; goto loser; } + + paddedResult.len = in->len; + paddedResult.data = static_cast( + PORT_ArenaAlloc(arena, paddedResult.len)); + + rv = PK11_CipherOp(ctx, paddedResult.data, + (int*)&paddedResult.len, paddedResult.len, + in->data, in->len); + if (rv != SECSuccess) goto loser; + + PK11_Finalize(ctx); + + /* Remove the padding */ + rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result); + if (rv) goto loser; + +loser: + if (ctx) PK11_DestroyContext(ctx, PR_TRUE); + return rv; +} + +SECStatus NSSDecryptor::PK11SDR_DecryptWithSlot( + PK11SlotInfo* slot, SECItem* data, SECItem* result, void* cx) const { + SECStatus rv = SECSuccess; + PK11SymKey *key = 0; + CK_MECHANISM_TYPE type; + SDRResult sdrResult; + SECItem *params = 0; + SECItem possibleResult = { siBuffer, NULL, 0 }; + PLArenaPool *arena = 0; + + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (!arena) { rv = SECFailure; goto loser; } + + /* Decode the incoming data */ + memset(&sdrResult, 0, sizeof sdrResult); + rv = SEC_QuickDERDecodeItem(arena, &sdrResult, g_template, data); + if (rv != SECSuccess) goto loser; /* Invalid format */ + + /* Get the parameter values from the data */ + params = PK11_ParamFromAlgid(&sdrResult.alg); + if (!params) { rv = SECFailure; goto loser; } + + /* Use triple-DES (Should look up the algorithm) */ + type = CKM_DES3_CBC; + key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx); + if (!key) { + rv = SECFailure; + } else { + rv = pk11Decrypt(slot, arena, type, key, params, + &sdrResult.data, result); + } + + /* + * if the pad value was too small (1 or 2), then it's statistically + * 'likely' that (1 in 256) that we may not have the correct key. + * Check the other keys for a better match. If we find none, use + * this result. + */ + if (rv == SECWouldBlock) + possibleResult = *result; + + /* + * handle the case where your key indicies may have been broken + */ + if (rv != SECSuccess) { + PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx); + PK11SymKey *testKey = NULL; + PK11SymKey *nextKey = NULL; + + for (testKey = keyList; testKey; + testKey = PK11_GetNextSymKey(testKey)) { + rv = pk11Decrypt(slot, arena, type, testKey, params, + &sdrResult.data, result); + if (rv == SECSuccess) + break; + + /* found a close match. If it's our first remember it */ + if (rv == SECWouldBlock) { + if (possibleResult.data) { + /* this is unlikely but possible. If we hit this condition, + * we have no way of knowing which possibility to prefer. + * in this case we just match the key the application + * thought was the right one */ + SECITEM_ZfreeItem(result, PR_FALSE); + } else { + possibleResult = *result; + } + } + } + + /* free the list */ + for (testKey = keyList; testKey; testKey = nextKey) { + nextKey = PK11_GetNextSymKey(testKey); + PK11_FreeSymKey(testKey); + } + } + + /* we didn't find a better key, use the one with a small pad value */ + if ((rv != SECSuccess) && (possibleResult.data)) { + *result = possibleResult; + possibleResult.data = NULL; + rv = SECSuccess; + } + + loser: + if (arena) PORT_FreeArena(arena, PR_TRUE); + if (key) PK11_FreeSymKey(key); + if (params) SECITEM_ZfreeItem(params, PR_TRUE); + if (possibleResult.data) SECITEM_ZfreeItem(&possibleResult, PR_FALSE); + + return rv; +} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h b/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h new file mode 100644 index 0000000000..8edd95f0f2 --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h @@ -0,0 +1,78 @@ +// 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. + +#ifndef CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_SYSTEM_NSS_H_ +#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_SYSTEM_NSS_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/strings/string16.h" + +struct FirefoxRawPasswordInfo; + +namespace autofill { +struct PasswordForm; +} + +namespace base { +class FilePath; +} + +// A wrapper for Firefox NSS decrypt component. +class NSSDecryptor { + public: + NSSDecryptor(); + ~NSSDecryptor(); + + // Initializes NSS if it hasn't already been initialized. + bool Init(const base::FilePath& dll_path, const base::FilePath& db_path); + + // Decrypts Firefox stored passwords. Before using this method, + // make sure Init() returns true. + base::string16 Decrypt(const std::string& crypt) const; + + // Parses the Firefox password file content, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + void ParseSignons(const base::FilePath& signon_file, + std::vector* forms); + + // Reads and parses the Firefox password sqlite db, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + bool ReadAndParseSignons(const base::FilePath& sqlite_file, + std::vector* forms); + + // Reads and parses the Firefox password file logins.json, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + bool ReadAndParseLogins(const base::FilePath& json_file, + std::vector* forms); + + private: + // Does not actually free the slot, since we'll free it when NSSDecryptor is + // destroyed. + void FreeSlot(PK11SlotInfo* slot) const {} + + // Turns unprocessed information extracted from Firefox's password file + // into PasswordForm. + bool CreatePasswordFormFromRawInfo( + const FirefoxRawPasswordInfo& raw_password_info, + autofill::PasswordForm* form); + + PK11SlotInfo* GetKeySlotForDB() const { return db_slot_; } + + SECStatus PK11SDR_DecryptWithSlot( + PK11SlotInfo* slot, SECItem* data, SECItem* result, void* cx) const; + + bool is_nss_initialized_; + PK11SlotInfo* db_slot_; + + DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); +}; + +#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_SYSTEM_NSS_H_ diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_win.cc b/chromium_src/chrome/utility/importer/nss_decryptor_win.cc new file mode 100644 index 0000000000..587d3fe929 --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor_win.cc @@ -0,0 +1,177 @@ +// 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. + +#include "chrome/utility/importer/nss_decryptor_win.h" + +#include "base/files/file_path.h" +#include "base/strings/sys_string_conversions.h" + +namespace { + +typedef BOOL (WINAPI* SetDllDirectoryFunc)(LPCTSTR lpPathName); + +// A helper class whose destructor calls SetDllDirectory(NULL) to undo the +// effects of a previous SetDllDirectory call. +class SetDllDirectoryCaller { + public: + explicit SetDllDirectoryCaller() : func_(NULL) { } + + ~SetDllDirectoryCaller() { + if (func_) + func_(NULL); + } + + // Sets the SetDllDirectory function pointer to activates this object. + void set_func(SetDllDirectoryFunc func) { func_ = func; } + + private: + SetDllDirectoryFunc func_; +}; + +} // namespace + +// static +const wchar_t NSSDecryptor::kNSS3Library[] = L"nss3.dll"; +const wchar_t NSSDecryptor::kSoftokn3Library[] = L"softokn3.dll"; +const wchar_t NSSDecryptor::kPLDS4Library[] = L"plds4.dll"; +const wchar_t NSSDecryptor::kNSPR4Library[] = L"nspr4.dll"; + +bool NSSDecryptor::Init(const base::FilePath& dll_path, + const base::FilePath& db_path) { + // We call SetDllDirectory to work around a Purify bug (GetModuleHandle + // fails inside Purify under certain conditions). SetDllDirectory only + // exists on Windows XP SP1 or later, so we look up its address at run time. + HMODULE kernel32_dll = GetModuleHandle(L"kernel32.dll"); + if (kernel32_dll == NULL) + return false; + SetDllDirectoryFunc set_dll_directory = + (SetDllDirectoryFunc)GetProcAddress(kernel32_dll, "SetDllDirectoryW"); + SetDllDirectoryCaller caller; + + if (set_dll_directory != NULL) { + if (!set_dll_directory(dll_path.value().c_str())) + return false; + caller.set_func(set_dll_directory); + nss3_dll_ = LoadLibrary(kNSS3Library); + if (nss3_dll_ == NULL) + return false; + } else { + // Fall back on LoadLibraryEx if SetDllDirectory isn't available. We + // actually prefer this method because it doesn't change the DLL search + // path, which is a process-wide property. + base::FilePath path = dll_path.Append(kNSS3Library); + nss3_dll_ = LoadLibraryEx(path.value().c_str(), NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); + if (nss3_dll_ == NULL) + return false; + + // Firefox 2 uses NSS 3.11. Firefox 3 uses NSS 3.12. NSS 3.12 has two + // changes in its DLLs: + // 1. nss3.dll is not linked with softokn3.dll at build time, but rather + // loads softokn3.dll using LoadLibrary in NSS_Init. + // 2. softokn3.dll has a new dependency sqlite3.dll. + // NSS_Init's LoadLibrary call has trouble finding sqlite3.dll. To help + // it out, we preload softokn3.dll using LoadLibraryEx with the + // LOAD_WITH_ALTERED_SEARCH_PATH flag. This helps because LoadLibrary + // doesn't load a DLL again if it's already loaded. This workaround is + // harmless for NSS 3.11. + path = base::FilePath(dll_path).Append(kSoftokn3Library); + softokn3_dll_ = LoadLibraryEx(path.value().c_str(), NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); + if (softokn3_dll_ == NULL) { + Free(); + return false; + } + } + HMODULE plds4_dll = GetModuleHandle(kPLDS4Library); + HMODULE nspr4_dll = GetModuleHandle(kNSPR4Library); + + // On Firefox 22 and higher, NSPR is part of nss3.dll rather than separate + // DLLs. + if (plds4_dll == NULL) { + plds4_dll = nss3_dll_; + nspr4_dll = nss3_dll_; + } + return InitNSS(db_path, plds4_dll, nspr4_dll); +} + +NSSDecryptor::NSSDecryptor() + : NSS_Init(NULL), NSS_Shutdown(NULL), PK11_GetInternalKeySlot(NULL), + PK11_CheckUserPassword(NULL), PK11_FreeSlot(NULL), + PK11_Authenticate(NULL), PK11SDR_Decrypt(NULL), SECITEM_FreeItem(NULL), + PL_ArenaFinish(NULL), PR_Cleanup(NULL), + nss3_dll_(NULL), softokn3_dll_(NULL), + is_nss_initialized_(false) { +} + +NSSDecryptor::~NSSDecryptor() { + Free(); +} + +bool NSSDecryptor::InitNSS(const base::FilePath& db_path, + base::NativeLibrary plds4_dll, + base::NativeLibrary nspr4_dll) { + // Gets the function address. + NSS_Init = (NSSInitFunc) + base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Init"); + NSS_Shutdown = (NSSShutdownFunc) + base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Shutdown"); + PK11_GetInternalKeySlot = (PK11GetInternalKeySlotFunc) + base::GetFunctionPointerFromNativeLibrary(nss3_dll_, + "PK11_GetInternalKeySlot"); + PK11_FreeSlot = (PK11FreeSlotFunc) + base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_FreeSlot"); + PK11_Authenticate = (PK11AuthenticateFunc) + base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_Authenticate"); + PK11SDR_Decrypt = (PK11SDRDecryptFunc) + base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11SDR_Decrypt"); + SECITEM_FreeItem = (SECITEMFreeItemFunc) + base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "SECITEM_FreeItem"); + PL_ArenaFinish = (PLArenaFinishFunc) + base::GetFunctionPointerFromNativeLibrary(plds4_dll, "PL_ArenaFinish"); + PR_Cleanup = (PRCleanupFunc) + base::GetFunctionPointerFromNativeLibrary(nspr4_dll, "PR_Cleanup"); + + if (NSS_Init == NULL || NSS_Shutdown == NULL || + PK11_GetInternalKeySlot == NULL || PK11_FreeSlot == NULL || + PK11_Authenticate == NULL || PK11SDR_Decrypt == NULL || + SECITEM_FreeItem == NULL || PL_ArenaFinish == NULL || + PR_Cleanup == NULL) { + Free(); + return false; + } + + SECStatus result = NSS_Init(base::SysWideToNativeMB(db_path.value()).c_str()); + if (result != SECSuccess) { + Free(); + return false; + } + + is_nss_initialized_ = true; + return true; +} + +void NSSDecryptor::Free() { + if (is_nss_initialized_) { + NSS_Shutdown(); + PL_ArenaFinish(); + PR_Cleanup(); + is_nss_initialized_ = false; + } + if (softokn3_dll_ != NULL) + base::UnloadNativeLibrary(softokn3_dll_); + if (nss3_dll_ != NULL) + base::UnloadNativeLibrary(nss3_dll_); + NSS_Init = NULL; + NSS_Shutdown = NULL; + PK11_GetInternalKeySlot = NULL; + PK11_FreeSlot = NULL; + PK11_Authenticate = NULL; + PK11SDR_Decrypt = NULL; + SECITEM_FreeItem = NULL; + PL_ArenaFinish = NULL; + PR_Cleanup = NULL; + nss3_dll_ = NULL; + softokn3_dll_ = NULL; +} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_win.h b/chromium_src/chrome/utility/importer/nss_decryptor_win.h new file mode 100644 index 0000000000..edcf46c2f7 --- /dev/null +++ b/chromium_src/chrome/utility/importer/nss_decryptor_win.h @@ -0,0 +1,193 @@ +// 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. + +#ifndef CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_WIN_H_ +#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_WIN_H_ + +#include +#include + +#include "base/macros.h" +#include "base/native_library.h" +#include "base/strings/string16.h" + +// The following declarations of functions and types are from Firefox +// NSS library. +// source code: +// security/nss/lib/util/seccomon.h +// security/nss/lib/nss/nss.h +// The license block is: + +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is the Netscape security libraries. +* +* The Initial Developer of the Original Code is +* Netscape Communications Corporation. +* Portions created by the Initial Developer are Copyright (C) 1994-2000 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* +* Alternatively, the contents of this file may be used under the terms of +* either the GNU General Public License Version 2 or later (the "GPL"), or +* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +* in which case the provisions of the GPL or the LGPL are applicable instead +* of those above. If you wish to allow use of your version of this file only +* under the terms of either the GPL or the LGPL, and not to allow others to +* use your version of this file under the terms of the MPL, indicate your +* decision by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL or the LGPL. If you do not delete +* the provisions above, a recipient may use your version of this file under +* the terms of any one of the MPL, the GPL or the LGPL. +* +* ***** END LICENSE BLOCK ***** */ + +enum SECItemType { + siBuffer = 0, + siClearDataBuffer = 1, + siCipherDataBuffer = 2, + siDERCertBuffer = 3, + siEncodedCertBuffer = 4, + siDERNameBuffer = 5, + siEncodedNameBuffer = 6, + siAsciiNameString = 7, + siAsciiString = 8, + siDEROID = 9, + siUnsignedInteger = 10, + siUTCTime = 11, + siGeneralizedTime = 12 +}; + +struct SECItem { + SECItemType type; + unsigned char *data; + unsigned int len; +}; + +enum SECStatus { + SECWouldBlock = -2, + SECFailure = -1, + SECSuccess = 0 +}; + +typedef int PRBool; +#define PR_TRUE 1 +#define PR_FALSE 0 + +typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; + +typedef struct PK11SlotInfoStr PK11SlotInfo; + +typedef SECStatus (*NSSInitFunc)(const char *configdir); +typedef SECStatus (*NSSShutdownFunc)(void); +typedef PK11SlotInfo* (*PK11GetInternalKeySlotFunc)(void); +typedef void (*PK11FreeSlotFunc)(PK11SlotInfo *slot); +typedef SECStatus (*PK11CheckUserPasswordFunc)(PK11SlotInfo *slot, char *pw); +typedef SECStatus + (*PK11AuthenticateFunc)(PK11SlotInfo *slot, PRBool loadCerts, void *wincx); +typedef SECStatus + (*PK11SDRDecryptFunc)(SECItem *data, SECItem *result, void *cx); +typedef void (*SECITEMFreeItemFunc)(SECItem *item, PRBool free_it); +typedef void (*PLArenaFinishFunc)(void); +typedef PRStatus (*PRCleanupFunc)(void); + +struct FirefoxRawPasswordInfo; + +namespace autofill { +struct PasswordForm; +} + +// A wrapper for Firefox NSS decrypt component. +class NSSDecryptor { + public: + NSSDecryptor(); + ~NSSDecryptor(); + + // Loads NSS3 library and returns true if successful. + // |dll_path| indicates the location of NSS3 DLL files, and |db_path| + // is the location of the database file that stores the keys. + bool Init(const base::FilePath& dll_path, const base::FilePath& db_path); + + // Frees the libraries. + void Free(); + + // Decrypts Firefox stored passwords. Before using this method, + // make sure Init() returns true. + base::string16 Decrypt(const std::string& crypt) const; + + // Parses the Firefox password file content, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + void ParseSignons(const base::FilePath& signon_file, + std::vector* forms); + + // Reads and parses the Firefox password sqlite db, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + bool ReadAndParseSignons(const base::FilePath& sqlite_file, + std::vector* forms); + + // Reads and parses the Firefox password file logins.json, decrypts the + // username/password and reads other related information. + // The result will be stored in |forms|. + bool ReadAndParseLogins(const base::FilePath& json_file, + std::vector* forms); + + private: + // Call NSS initialization funcs. + bool InitNSS(const base::FilePath& db_path, + base::NativeLibrary plds4_dll, + base::NativeLibrary nspr4_dll); + + PK11SlotInfo* GetKeySlotForDB() const { return PK11_GetInternalKeySlot(); } + + void FreeSlot(PK11SlotInfo* slot) const { PK11_FreeSlot(slot); } + + // Turns unprocessed information extracted from Firefox's password file + // into PasswordForm. + bool CreatePasswordFormFromRawInfo( + const FirefoxRawPasswordInfo& raw_password_info, + autofill::PasswordForm* form); + + // Methods in Firefox security components. + NSSInitFunc NSS_Init; + NSSShutdownFunc NSS_Shutdown; + PK11GetInternalKeySlotFunc PK11_GetInternalKeySlot; + PK11CheckUserPasswordFunc PK11_CheckUserPassword; + PK11FreeSlotFunc PK11_FreeSlot; + PK11AuthenticateFunc PK11_Authenticate; + PK11SDRDecryptFunc PK11SDR_Decrypt; + SECITEMFreeItemFunc SECITEM_FreeItem; + PLArenaFinishFunc PL_ArenaFinish; + PRCleanupFunc PR_Cleanup; + + // Libraries necessary for decrypting the passwords. + static const wchar_t kNSS3Library[]; + static const wchar_t kSoftokn3Library[]; + static const wchar_t kPLDS4Library[]; + static const wchar_t kNSPR4Library[]; + + // NSS3 module handles. + base::NativeLibrary nss3_dll_; + base::NativeLibrary softokn3_dll_; + + // True if NSS_Init() has been called + bool is_nss_initialized_; + + DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); +}; + +#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_WIN_H_ diff --git a/chromium_src/chrome/utility/importer/safari_importer.h b/chromium_src/chrome/utility/importer/safari_importer.h new file mode 100644 index 0000000000..b0c524a8be --- /dev/null +++ b/chromium_src/chrome/utility/importer/safari_importer.h @@ -0,0 +1,106 @@ +// 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. + +#ifndef CHROME_UTILITY_IMPORTER_SAFARI_IMPORTER_H_ +#define CHROME_UTILITY_IMPORTER_SAFARI_IMPORTER_H_ + +#include + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "chrome/common/importer/importer_url_row.h" +#include "chrome/utility/importer/importer.h" +#include "components/favicon_base/favicon_usage_data.h" + +#if __OBJC__ +@class NSDictionary; +@class NSString; +#else +class NSDictionary; +class NSString; +#endif + +class GURL; +struct ImportedBookmarkEntry; + +namespace sql { +class Connection; +} + +// Importer for Safari on OS X. +class SafariImporter : public Importer { + public: + // |library_dir| is the full path to the ~/Library directory, + // We pass it in as a parameter for testing purposes. + explicit SafariImporter(const base::FilePath& library_dir); + + // Importer: + void StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) override; + + private: + FRIEND_TEST_ALL_PREFIXES(SafariImporterTest, BookmarkImport); + FRIEND_TEST_ALL_PREFIXES(SafariImporterTest, + BookmarkImportWithEmptyBookmarksMenu); + FRIEND_TEST_ALL_PREFIXES(SafariImporterTest, FaviconImport); + FRIEND_TEST_ALL_PREFIXES(SafariImporterTest, HistoryImport); + + ~SafariImporter() override; + + // Multiple URLs can share the same favicon; this is a map + // of URLs -> IconIDs that we load as a temporary step before + // actually loading the icons. + typedef std::map> FaviconMap; + + void ImportBookmarks(); + void ImportPasswords(); + void ImportHistory(); + + // Parse Safari's stored bookmarks. + void ParseBookmarks(const base::string16& toolbar_name, + std::vector* bookmarks); + + // Function to recursively read Bookmarks out of Safari plist. + // |bookmark_folder| The dictionary containing a folder to parse. + // |parent_path_elements| Path elements up to this point. + // |is_in_toolbar| Is this folder in the toolbar. + // |out_bookmarks| BookMark element array to write into. + void RecursiveReadBookmarksFolder( + NSDictionary* bookmark_folder, + const std::vector& parent_path_elements, + bool is_in_toolbar, + const base::string16& toolbar_name, + std::vector* out_bookmarks); + + // Converts history time stored by Safari as a double serialized as a string, + // to seconds-since-UNIX-Ephoch-format used by Chrome. + double HistoryTimeToEpochTime(NSString* history_time); + + // Parses Safari's history and loads it into the input array. + void ParseHistoryItems(std::vector* history_items); + + // Opens the favicon database file. + bool OpenDatabase(sql::Connection* db); + + // Loads the urls associated with the favicons into favicon_map; + void ImportFaviconURLs(sql::Connection* db, FaviconMap* favicon_map); + + // Loads and reencodes the individual favicons. + void LoadFaviconData(sql::Connection* db, + const FaviconMap& favicon_map, + favicon_base::FaviconUsageDataList* favicons); + + base::FilePath library_dir_; + + DISALLOW_COPY_AND_ASSIGN(SafariImporter); +}; + +#endif // CHROME_UTILITY_IMPORTER_SAFARI_IMPORTER_H_ diff --git a/chromium_src/chrome/utility/importer/safari_importer.mm b/chromium_src/chrome/utility/importer/safari_importer.mm new file mode 100644 index 0000000000..e7e2c9263d --- /dev/null +++ b/chromium_src/chrome/utility/importer/safari_importer.mm @@ -0,0 +1,390 @@ +// 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. + +/** + * This is not a straight copy from chromium src, in particular + * some functionality is removed. + * This was originally forked with 52.0.2743.116. Diff against + * a version of that file for a full list of changes. + */ + +#include + +#include "chrome/utility/importer/safari_importer.h" + +#include +#include + +#include "base/files/file_util.h" +#include "base/mac/foundation_util.h" +#include "base/strings/string16.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "chrome/common/importer/imported_bookmark_entry.h" +#include "chrome/common/importer/importer_bridge.h" +// #include "chrome/common/url_constants.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/utility/importer/favicon_reencode.h" +#include "components/strings/grit/components_strings.h" +#include "net/base/data_url.h" +#include "sql/statement.h" +#include "url/gurl.h" + +namespace { + +// A function like this is used by other importers in order to filter out +// URLS we don't want to import. +// For now it's pretty basic, but I've split it out so it's easy to slot +// in necessary logic for filtering URLS, should we need it. +bool CanImportSafariURL(const GURL& url) { + // The URL is not valid. + if (!url.is_valid()) + return false; + + return true; +} + +} // namespace + +SafariImporter::SafariImporter(const base::FilePath& library_dir) + : library_dir_(library_dir) { +} + +SafariImporter::~SafariImporter() { +} + +void SafariImporter::StartImport(const importer::SourceProfile& source_profile, + uint16_t items, + ImporterBridge* bridge) { + bridge_ = bridge; + // The order here is important! + bridge_->NotifyStarted(); + + // In keeping with import on other platforms (and for other browsers), we + // don't import the home page (since it may lead to a useless homepage); see + // crbug.com/25603. + if ((items & importer::HISTORY) && !cancelled()) { + bridge_->NotifyItemStarted(importer::HISTORY); + ImportHistory(); + bridge_->NotifyItemEnded(importer::HISTORY); + } + if ((items & importer::FAVORITES) && !cancelled()) { + bridge_->NotifyItemStarted(importer::FAVORITES); + ImportBookmarks(); + bridge_->NotifyItemEnded(importer::FAVORITES); + } + if ((items & importer::PASSWORDS) && !cancelled()) { + bridge_->NotifyItemStarted(importer::PASSWORDS); + ImportPasswords(); + bridge_->NotifyItemEnded(importer::PASSWORDS); + } + bridge_->NotifyEnded(); +} + +void SafariImporter::ImportBookmarks() { + base::string16 toolbar_name = + // bridge_->GetLocalizedString(IDS_BOOKMARK_BAR_FOLDER_NAME); + base::UTF8ToUTF16("Bookmark Bar"); + std::vector bookmarks; + ParseBookmarks(toolbar_name, &bookmarks); + + // Write bookmarks into profile. + if (!bookmarks.empty() && !cancelled()) { + const base::string16& first_folder_name = + // bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_SAFARI); + base::UTF8ToUTF16("Imported from Safari"); + bridge_->AddBookmarks(bookmarks, first_folder_name); + } + + // Import favicons. + sql::Connection db; + if (!OpenDatabase(&db)) + return; + + FaviconMap favicon_map; + ImportFaviconURLs(&db, &favicon_map); + // Write favicons into profile. + if (!favicon_map.empty() && !cancelled()) { + favicon_base::FaviconUsageDataList favicons; + LoadFaviconData(&db, favicon_map, &favicons); + bridge_->SetFavicons(favicons); + } +} + +bool SafariImporter::OpenDatabase(sql::Connection* db) { + // Construct ~/Library/Safari/WebIcons.db path. + NSString* library_dir = [NSString + stringWithUTF8String:library_dir_.value().c_str()]; + NSString* safari_dir = [library_dir + stringByAppendingPathComponent:@"Safari"]; + NSString* favicons_db_path = [safari_dir + stringByAppendingPathComponent:@"WebpageIcons.db"]; + + const char* db_path = [favicons_db_path fileSystemRepresentation]; + return db->Open(base::FilePath(db_path)); +} + +void SafariImporter::ImportFaviconURLs(sql::Connection* db, + FaviconMap* favicon_map) { + const char query[] = "SELECT iconID, url FROM PageURL;"; + sql::Statement s(db->GetUniqueStatement(query)); + + while (s.Step() && !cancelled()) { + int64_t icon_id = s.ColumnInt64(0); + GURL url = GURL(s.ColumnString(1)); + (*favicon_map)[icon_id].insert(url); + } +} + +void SafariImporter::LoadFaviconData( + sql::Connection* db, + const FaviconMap& favicon_map, + favicon_base::FaviconUsageDataList* favicons) { + const char query[] = "SELECT i.url, d.data " + "FROM IconInfo i JOIN IconData d " + "ON i.iconID = d.iconID " + "WHERE i.iconID = ?;"; + sql::Statement s(db->GetUniqueStatement(query)); + + for (FaviconMap::const_iterator i = favicon_map.begin(); + i != favicon_map.end(); ++i) { + s.Reset(true); + s.BindInt64(0, i->first); + if (s.Step()) { + favicon_base::FaviconUsageData usage; + + usage.favicon_url = GURL(s.ColumnString(0)); + if (!usage.favicon_url.is_valid()) + continue; // Don't bother importing favicons with invalid URLs. + + std::vector data; + s.ColumnBlobAsVector(1, &data); + if (data.empty()) + continue; // Data definitely invalid. + + if (!importer::ReencodeFavicon(&data[0], data.size(), &usage.png_data)) + continue; // Unable to decode. + + usage.urls = i->second; + favicons->push_back(usage); + } + } +} + +void SafariImporter::RecursiveReadBookmarksFolder( + NSDictionary* bookmark_folder, + const std::vector& parent_path_elements, + bool is_in_toolbar, + const base::string16& toolbar_name, + std::vector* out_bookmarks) { + DCHECK(bookmark_folder); + + NSString* type = [bookmark_folder objectForKey:@"WebBookmarkType"]; + NSString* title = [bookmark_folder objectForKey:@"Title"]; + + // Are we the dictionary that contains all other bookmarks? + // We need to know this so we don't add it to the path. + bool is_top_level_bookmarks_container = [bookmark_folder + objectForKey:@"WebBookmarkFileVersion"] != nil; + + // We're expecting a list of bookmarks here, if that isn't what we got, fail. + if (!is_top_level_bookmarks_container) { + // Top level containers sometimes don't have title attributes. + if (![type isEqualToString:@"WebBookmarkTypeList"] || !title) { + NOTREACHED() << "Type=(" + << (type ? base::SysNSStringToUTF8(type) : "Null type") + << ") Title=(" + << (title ? base::SysNSStringToUTF8(title) : "Null title") + << ")"; + return; + } + } + + NSArray* elements = [bookmark_folder objectForKey:@"Children"]; + if (!elements && + (!parent_path_elements.empty() || !is_in_toolbar) && + ![title isEqualToString:@"BookmarksMenu"]) { + // This is an empty folder, so add it explicitly. Note that the condition + // above prevents either the toolbar folder or the bookmarks menu from being + // added if either is empty. Note also that all non-empty folders are added + // implicitly when their children are added. + ImportedBookmarkEntry entry; + // Safari doesn't specify a creation time for the folder. + entry.creation_time = base::Time::Now(); + entry.title = base::SysNSStringToUTF16(title); + entry.path = parent_path_elements; + entry.in_toolbar = is_in_toolbar; + entry.is_folder = true; + + out_bookmarks->push_back(entry); + return; + } + + std::vector path_elements(parent_path_elements); + // Create a folder for the toolbar, but not for the bookmarks menu. + if (path_elements.empty() && [title isEqualToString:@"BookmarksBar"]) { + is_in_toolbar = true; + path_elements.push_back(toolbar_name); + } else if (!is_top_level_bookmarks_container && + !(path_elements.empty() && + [title isEqualToString:@"BookmarksMenu"])) { + if (title) + path_elements.push_back(base::SysNSStringToUTF16(title)); + } + + // Iterate over individual bookmarks. + for (NSDictionary* bookmark in elements) { + NSString* type = [bookmark objectForKey:@"WebBookmarkType"]; + if (!type) + continue; + + // If this is a folder, recurse. + if ([type isEqualToString:@"WebBookmarkTypeList"]) { + RecursiveReadBookmarksFolder(bookmark, + path_elements, + is_in_toolbar, + toolbar_name, + out_bookmarks); + } + + // If we didn't see a bookmark folder, then we're expecting a bookmark + // item. If that's not what we got then ignore it. + if (![type isEqualToString:@"WebBookmarkTypeLeaf"]) + continue; + + NSString* url = [bookmark objectForKey:@"URLString"]; + NSString* title = [[bookmark objectForKey:@"URIDictionary"] + objectForKey:@"title"]; + + if (!url || !title) + continue; + + // Output Bookmark. + ImportedBookmarkEntry entry; + // Safari doesn't specify a creation time for the bookmark. + entry.creation_time = base::Time::Now(); + entry.title = base::SysNSStringToUTF16(title); + entry.url = GURL(base::SysNSStringToUTF8(url)); + entry.path = path_elements; + entry.in_toolbar = is_in_toolbar; + + out_bookmarks->push_back(entry); + } +} + +void SafariImporter::ParseBookmarks( + const base::string16& toolbar_name, + std::vector* bookmarks) { + DCHECK(bookmarks); + + // Construct ~/Library/Safari/Bookmarks.plist path + NSString* library_dir = [NSString + stringWithUTF8String:library_dir_.value().c_str()]; + NSString* safari_dir = [library_dir + stringByAppendingPathComponent:@"Safari"]; + NSString* bookmarks_plist = [safari_dir + stringByAppendingPathComponent:@"Bookmarks.plist"]; + + // Load the plist file. + NSDictionary* bookmarks_dict = [NSDictionary + dictionaryWithContentsOfFile:bookmarks_plist]; + if (!bookmarks_dict) + return; + + // Recursively read in bookmarks. + std::vector parent_path_elements; + RecursiveReadBookmarksFolder(bookmarks_dict, parent_path_elements, false, + toolbar_name, bookmarks); +} + +void SafariImporter::ImportPasswords() { + // Safari stores it's passwords in the Keychain, same as us so we don't need + // to import them. + // Note: that we don't automatically pick them up, there is some logic around + // the user needing to explicitly input his username in a page and blurring + // the field before we pick it up, but the details of that are beyond the + // scope of this comment. +} + +void SafariImporter::ImportHistory() { + std::vector rows; + ParseHistoryItems(&rows); + + if (!rows.empty() && !cancelled()) { + bridge_->SetHistoryItems(rows, importer::VISIT_SOURCE_SAFARI_IMPORTED); + } +} + +double SafariImporter::HistoryTimeToEpochTime(NSString* history_time) { + DCHECK(history_time); + // Add Difference between Unix epoch and CFAbsoluteTime epoch in seconds. + // Unix epoch is 1970-01-01 00:00:00.0 UTC, + // CF epoch is 2001-01-01 00:00:00.0 UTC. + return CFStringGetDoubleValue(base::mac::NSToCFCast(history_time)) + + kCFAbsoluteTimeIntervalSince1970; +} + +void SafariImporter::ParseHistoryItems( + std::vector* history_items) { + DCHECK(history_items); + + // Construct ~/Library/Safari/History.plist path + NSString* library_dir = [NSString + stringWithUTF8String:library_dir_.value().c_str()]; + NSString* safari_dir = [library_dir + stringByAppendingPathComponent:@"Safari"]; + NSString* history_plist = [safari_dir + stringByAppendingPathComponent:@"History.plist"]; + + // Load the plist file. + NSDictionary* history_dict = [NSDictionary + dictionaryWithContentsOfFile:history_plist]; + if (!history_dict) + return; + + NSArray* safari_history_items = [history_dict + objectForKey:@"WebHistoryDates"]; + + for (NSDictionary* history_item in safari_history_items) { + NSString* url_ns = [history_item objectForKey:@""]; + if (!url_ns) + continue; + + GURL url(base::SysNSStringToUTF8(url_ns)); + + if (!CanImportSafariURL(url)) + continue; + + ImporterURLRow row(url); + NSString* title_ns = [history_item objectForKey:@"title"]; + + // Sometimes items don't have a title, in which case we just substitue + // the url. + if (!title_ns) + title_ns = url_ns; + + row.title = base::SysNSStringToUTF16(title_ns); + int visit_count = [[history_item objectForKey:@"visitCount"] + intValue]; + row.visit_count = visit_count; + // Include imported URLs in autocompletion - don't hide them. + row.hidden = 0; + // Item was never typed before in the omnibox. + row.typed_count = 0; + + NSString* last_visit_str = [history_item objectForKey:@"lastVisitedDate"]; + // The last visit time should always be in the history item, but if not + /// just continue without this item. + DCHECK(last_visit_str); + if (!last_visit_str) + continue; + + // Convert Safari's last visit time to Unix Epoch time. + double seconds_since_unix_epoch = HistoryTimeToEpochTime(last_visit_str); + row.last_visit = base::Time::FromDoubleT(seconds_since_unix_epoch); + + history_items->push_back(row); + } +} diff --git a/chromium_src/chrome/utility/profile_import_handler.cc b/chromium_src/chrome/utility/profile_import_handler.cc new file mode 100644 index 0000000000..a50eaec1b2 --- /dev/null +++ b/chromium_src/chrome/utility/profile_import_handler.cc @@ -0,0 +1,95 @@ +// 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/utility/profile_import_handler.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "chrome/common/importer/profile_import_process_messages.h" +#include "chrome/utility/importer/external_process_importer_bridge.h" +#include "chrome/utility/importer/importer.h" +#include "chrome/utility/importer/importer_creator.h" +#include "content/public/utility/utility_thread.h" + +namespace { + +bool Send(IPC::Message* message) { + return content::UtilityThread::Get()->Send(message); +} + +} // namespace + +ProfileImportHandler::ProfileImportHandler() : items_to_import_(0) {} + +ProfileImportHandler::~ProfileImportHandler() {} + +bool ProfileImportHandler::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ProfileImportHandler, message) + IPC_MESSAGE_HANDLER(ProfileImportProcessMsg_StartImport, OnImportStart) + IPC_MESSAGE_HANDLER(ProfileImportProcessMsg_CancelImport, OnImportCancel) + IPC_MESSAGE_HANDLER(ProfileImportProcessMsg_ReportImportItemFinished, + OnImportItemFinished) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void ProfileImportHandler::OnImportStart( + const importer::SourceProfile& source_profile, + uint16_t items, + const base::DictionaryValue& localized_strings) { + content::UtilityThread::Get()->EnsureBlinkInitialized(); + bridge_ = new ExternalProcessImporterBridge( + localized_strings, + content::UtilityThread::Get(), + base::ThreadTaskRunnerHandle::Get().get()); + importer_ = importer::CreateImporterByType(source_profile.importer_type); + if (!importer_.get()) { + Send(new ProfileImportProcessHostMsg_Import_Finished( + false, "Importer could not be created.")); + return; + } + + items_to_import_ = items; + + // Create worker thread in which importer runs. + import_thread_.reset(new base::Thread("import_thread")); +#if defined(OS_WIN) + import_thread_->init_com_with_mta(false); +#endif + if (!import_thread_->Start()) { + NOTREACHED(); + ImporterCleanup(); + } + import_thread_->task_runner()->PostTask( + FROM_HERE, base::Bind(&Importer::StartImport, importer_.get(), + source_profile, items, base::RetainedRef(bridge_))); +} + +void ProfileImportHandler::OnImportCancel() { + ImporterCleanup(); +} + +void ProfileImportHandler::OnImportItemFinished(uint16_t item) { + items_to_import_ ^= item; // Remove finished item from mask. + // If we've finished with all items, notify the browser process. + if (items_to_import_ == 0) { + Send(new ProfileImportProcessHostMsg_Import_Finished(true, std::string())); + ImporterCleanup(); + } +} + +void ProfileImportHandler::ImporterCleanup() { + importer_->Cancel(); + importer_ = NULL; + bridge_ = NULL; + import_thread_.reset(); + content::UtilityThread::Get()->ReleaseProcessIfNeeded(); +} diff --git a/chromium_src/chrome/utility/profile_import_handler.h b/chromium_src/chrome/utility/profile_import_handler.h new file mode 100644 index 0000000000..02c43a5a8f --- /dev/null +++ b/chromium_src/chrome/utility/profile_import_handler.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef CHROME_UTILITY_PROFILE_IMPORT_HANDLER_H_ +#define CHROME_UTILITY_PROFILE_IMPORT_HANDLER_H_ + +#include + +#include + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "chrome/utility/utility_message_handler.h" + +class ExternalProcessImporterBridge; +class Importer; + +namespace base { +class DictionaryValue; +class Thread; +} + +namespace importer { +struct SourceProfile; +} + +// Dispatches IPCs for out of process profile import. +class ProfileImportHandler : public UtilityMessageHandler { + public: + ProfileImportHandler(); + ~ProfileImportHandler() override; + + // IPC::Listener: + bool OnMessageReceived(const IPC::Message& message) override; + + private: + void OnImportStart(const importer::SourceProfile& source_profile, + uint16_t items, + const base::DictionaryValue& localized_strings); + void OnImportCancel(); + void OnImportItemFinished(uint16_t item); + + // The following are used with out of process profile import: + void ImporterCleanup(); + + // Thread that importer runs on, while ProfileImportThread handles messages + // from the browser process. + std::unique_ptr import_thread_; + + // Bridge object is passed to importer, so that it can send IPC calls + // directly back to the ProfileImportProcessHost. + scoped_refptr bridge_; + + // A bitmask of importer::ImportItem. + uint16_t items_to_import_; + + // Importer of the appropriate type (Firefox, Safari, IE, etc.) + scoped_refptr importer_; +}; + +#endif // CHROME_UTILITY_PROFILE_IMPORT_HANDLER_H_ diff --git a/electron.gyp b/electron.gyp index 6e1d0f271d..f2bf03a785 100644 --- a/electron.gyp +++ b/electron.gyp @@ -11,6 +11,7 @@ 'vendor/native_mate/native_mate_files.gypi', 'extensions.gypi', 'autofill.gypi', + 'importer.gypi', ], 'target_defaults': { 'msvs_disabled_warnings': [ @@ -235,6 +236,7 @@ 'sources': [ '<@(lib_sources)', '<@(autofill_sources)', + '<@(importer_sources)', ], 'include_dirs': [ '.', @@ -243,6 +245,7 @@ '<(libchromiumcontent_dir)/gen/protoc_out', '<(libchromiumcontent_src_dir)/third_party/protobuf/src', # end autofill + '<@(importer_include_dir)', 'vendor/brightray', 'vendor/native_mate', # Include atom_natives.h. @@ -290,7 +293,9 @@ ['OS!="linux" and libchromiumcontent_component', { 'link_settings': { # Following libraries are always linked statically. - 'libraries': [ '<@(autofill_libraries)' ], + 'libraries': [ '<@(autofill_libraries)', + '<@(importer_libraries)', + ], }, }], ['OS=="linux" and libchromiumcontent_component', { @@ -299,6 +304,7 @@ # hack to handle cyclic dependencies '-Wl,--start-group', '<@(autofill_libraries)', + '<@(importer_libraries)', '-Wl,--end-group', ], } @@ -336,6 +342,8 @@ '-lcomdlg32.lib', '-lwininet.lib', '-lwinmm.lib', + # TODO(Anthony): doesn't work in importer.gypi for release build + '-lesent.lib', ], }, 'dependencies': [ diff --git a/filenames.gypi b/filenames.gypi index 56215381aa..82729b0a1e 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -18,6 +18,7 @@ 'lib/browser/api/dialog.js', 'lib/browser/api/exports/electron.js', 'lib/browser/api/global-shortcut.js', + 'lib/browser/api/importer.js', 'lib/browser/api/ipc-main.js', 'lib/browser/api/menu.js', 'lib/browser/api/menu-item.js', @@ -382,6 +383,13 @@ 'atom/common/draggable_region.cc', 'atom/common/draggable_region.h', 'atom/common/google_api_key.h', + 'atom/common/importer/chrome_importer_utils.cc', + 'atom/common/importer/chrome_importer_utils.h', + 'atom/common/importer/chrome_importer_utils_mac.mm', + 'atom/common/importer/chrome_importer_utils_linux.cc', + 'atom/common/importer/chrome_importer_utils_win.cc', + 'atom/common/importer/imported_cookie_entry.cc', + 'atom/common/importer/imported_cookie_entry.h', 'atom/common/key_weak_map.h', 'atom/common/keyboard_util.cc', 'atom/common/keyboard_util.h', @@ -444,6 +452,8 @@ 'atom/renderer/preferences_manager.h', 'atom/utility/atom_content_utility_client.cc', 'atom/utility/atom_content_utility_client.h', + 'atom/utility/importer/chrome_importer.cc', + 'atom/utility/importer/chrome_importer.h', 'chromium_src/chrome/browser/browser_process.cc', 'chromium_src/chrome/browser/browser_process.h', 'chromium_src/chrome/browser/chrome_process_finder_win.cc', @@ -459,6 +469,23 @@ 'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h', 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc', 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h', + 'chromium_src/chrome/browser/importer/external_process_importer_client.cc', + 'chromium_src/chrome/browser/importer/external_process_importer_client.h', + 'chromium_src/chrome/browser/importer/external_process_importer_host.cc', + 'chromium_src/chrome/browser/importer/external_process_importer_host.h', + 'chromium_src/chrome/browser/importer/firefox_profile_lock.cc', + 'chromium_src/chrome/browser/importer/firefox_profile_lock.h', + 'chromium_src/chrome/browser/importer/firefox_profile_lock_posix.cc', + 'chromium_src/chrome/browser/importer/firefox_profile_lock_win.cc', + 'chromium_src/chrome/browser/importer/importer_list.cc', + 'chromium_src/chrome/browser/importer/importer_list.h', + 'chromium_src/chrome/browser/importer/importer_lock_dialog.h', + 'chromium_src/chrome/browser/importer/importer_progress_observer.h', + 'chromium_src/chrome/browser/importer/importer_uma.cc', + 'chromium_src/chrome/browser/importer/importer_uma.h', + 'chromium_src/chrome/browser/importer/in_process_importer_bridge.cc', + 'chromium_src/chrome/browser/importer/in_process_importer_bridge.h', + 'chromium_src/chrome/browser/importer/profile_writer.h', 'chromium_src/chrome/browser/media/desktop_media_list.h', 'chromium_src/chrome/browser/media/desktop_media_list_observer.h', 'chromium_src/chrome/browser/media/native_desktop_media_list.cc', @@ -534,6 +561,37 @@ 'chromium_src/chrome/common/chrome_paths_mac.mm', 'chromium_src/chrome/common/chrome_paths_win.cc', 'chromium_src/chrome/common/chrome_utility_messages.h', + 'chromium_src/chrome/common/common_param_traits_macros.h', + 'chromium_src/chrome/common/importer/edge_importer_utils_win.cc', + 'chromium_src/chrome/common/importer/edge_importer_utils_win.h', + 'chromium_src/chrome/common/importer/firefox_importer_utils.cc', + 'chromium_src/chrome/common/importer/firefox_importer_utils.h', + 'chromium_src/chrome/common/importer/firefox_importer_utils_linux.cc', + 'chromium_src/chrome/common/importer/firefox_importer_utils_mac.mm', + 'chromium_src/chrome/common/importer/firefox_importer_utils_win.cc', + 'chromium_src/chrome/common/importer/ie_importer_utils_win.cc', + 'chromium_src/chrome/common/importer/ie_importer_utils_win.h', + 'chromium_src/chrome/common/importer/imported_bookmark_entry.cc', + 'chromium_src/chrome/common/importer/imported_bookmark_entry.h', + 'chromium_src/chrome/common/importer/importer_autofill_form_data_entry.cc', + 'chromium_src/chrome/common/importer/importer_autofill_form_data_entry.h', + 'chromium_src/chrome/common/importer/importer_bridge.cc', + 'chromium_src/chrome/common/importer/importer_bridge.h', + 'chromium_src/chrome/common/importer/importer_data_types.cc', + 'chromium_src/chrome/common/importer/importer_data_types.h', + 'chromium_src/chrome/common/importer/importer_test_registry_overrider_win.cc', + 'chromium_src/chrome/common/importer/importer_test_registry_overrider_win.h', + 'chromium_src/chrome/common/importer/importer_types.h', + 'chromium_src/chrome/common/importer/importer_url_row.cc', + 'chromium_src/chrome/common/importer/importer_url_row.h', + 'chromium_src/chrome/common/importer/profile_import_process_messages.cc', + 'chromium_src/chrome/common/importer/profile_import_process_messages.h', + 'chromium_src/chrome/common/importer/profile_import_process_param_traits_macros.h', + 'chromium_src/chrome/common/importer/pstore_declarations.h', + 'chromium_src/chrome/common/importer/safari_importer_utils.h', + 'chromium_src/chrome/common/importer/safari_importer_utils.mm', + 'chromium_src/chrome/common/ini_parser.cc', + 'chromium_src/chrome/common/ini_parser.h', 'chromium_src/chrome/common/pref_names.cc', 'chromium_src/chrome/common/pref_names.h', 'chromium_src/chrome/common/print_messages.cc', @@ -569,6 +627,36 @@ 'chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h', 'chromium_src/chrome/renderer/tts_dispatcher.cc', 'chromium_src/chrome/renderer/tts_dispatcher.h', + 'chromium_src/chrome/utility/importer/bookmark_html_reader.cc', + 'chromium_src/chrome/utility/importer/bookmark_html_reader.h', + 'chromium_src/chrome/utility/importer/bookmarks_file_importer.cc', + 'chromium_src/chrome/utility/importer/bookmarks_file_importer.h', + 'chromium_src/chrome/utility/importer/edge_database_reader_win.cc', + 'chromium_src/chrome/utility/importer/edge_database_reader_win.h', + 'chromium_src/chrome/utility/importer/edge_importer_win.cc', + 'chromium_src/chrome/utility/importer/edge_importer_win.h', + 'chromium_src/chrome/utility/importer/external_process_importer_bridge.cc', + 'chromium_src/chrome/utility/importer/external_process_importer_bridge.h', + 'chromium_src/chrome/utility/importer/favicon_reencode.cc', + 'chromium_src/chrome/utility/importer/favicon_reencode.h', + 'chromium_src/chrome/utility/importer/firefox_importer.cc', + 'chromium_src/chrome/utility/importer/firefox_importer.h', + 'chromium_src/chrome/utility/importer/ie_importer_win.cc', + 'chromium_src/chrome/utility/importer/ie_importer_win.h', + 'chromium_src/chrome/utility/importer/importer.cc', + 'chromium_src/chrome/utility/importer/importer.h', + 'chromium_src/chrome/utility/importer/importer_creator.cc', + 'chromium_src/chrome/utility/importer/importer_creator.h', + 'chromium_src/chrome/utility/importer/nss_decryptor.cc', + 'chromium_src/chrome/utility/importer/nss_decryptor.h', + 'chromium_src/chrome/utility/importer/nss_decryptor_mac.h', + 'chromium_src/chrome/utility/importer/nss_decryptor_mac.mm', + 'chromium_src/chrome/utility/importer/nss_decryptor_win.cc', + 'chromium_src/chrome/utility/importer/nss_decryptor_win.h', + 'chromium_src/chrome/utility/importer/safari_importer.h', + 'chromium_src/chrome/utility/importer/safari_importer.mm', + 'chromium_src/chrome/utility/profile_import_handler.cc', + 'chromium_src/chrome/utility/profile_import_handler.h', 'chromium_src/chrome/utility/utility_message_handler.h', 'chromium_src/library_loaders/libspeechd_loader.cc', 'chromium_src/library_loaders/libspeechd.h', @@ -633,6 +721,12 @@ '<(libchromiumcontent_src_dir)/ui/resources/cursors/zoom_out.cur', ], }], # OS=="win" + ['OS=="linux"', { + 'lib_sources': [ + 'chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc', + 'chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h', + ], + }], # OS=="linux" ], }, } diff --git a/importer.gypi b/importer.gypi new file mode 100644 index 0000000000..b16866fd31 --- /dev/null +++ b/importer.gypi @@ -0,0 +1,73 @@ +{ + 'variables': { + 'importer_sources': [ + 'atom/browser/api/atom_api_importer.cc', + 'atom/browser/api/atom_api_importer.h', + 'atom/browser/importer/profile_writer.cc', + ], + }, + 'conditions': [ + ['OS=="mac"', { + 'variables': { + 'importer_libraries': [ + '<(libchromiumcontent_dir)/libbookmarks_browser.a', + '<(libchromiumcontent_dir)/libcomponent_metrics_proto.a', + '<(libchromiumcontent_dir)/libfavicon_base.a', + '<(libchromiumcontent_dir)/libgoogle_apis.a', + '<(libchromiumcontent_dir)/libgoogle_core_browser.a', + '<(libchromiumcontent_dir)/libhistory_core_browser.a', + '<(libchromiumcontent_dir)/libquery_parser.a', + '<(libchromiumcontent_dir)/liburl_formatter.a', + '<(libchromiumcontent_dir)/libsearch_engines.a', + ], + 'importer_include_dir': [ + '<(libchromiumcontent_dir)/gen/components/strings', + ], + } + }], + ['OS=="linux"', { + 'variables': { + 'importer_libraries': [ + '<(libchromiumcontent_dir)/libbookmarks_browser.a', + '<(libchromiumcontent_dir)/libcomponent_metrics_proto.a', + '<(libchromiumcontent_dir)/libfavicon_base.a', + '<(libchromiumcontent_dir)/libgoogle_apis.a', + '<(libchromiumcontent_dir)/libgoogle_core_browser.a', + '<(libchromiumcontent_dir)/libhistory_core_browser.a', + '<(libchromiumcontent_dir)/libquery_parser.a', + '<(libchromiumcontent_dir)/liburl_formatter.a', + '<(libchromiumcontent_dir)/libsearch_engines.a', + ], + 'importer_include_dir': [ + '<(libchromiumcontent_dir)/gen/components/strings', + '/usr/local/include/nss', + '/usr/local/include/nspr', + ], + } + }], + ['OS=="win"', { + 'variables': { + 'importer_libraries': [ + '<(libchromiumcontent_dir)/bookmarks_browser.lib', + '<(libchromiumcontent_dir)/component_metrics_proto.lib', + '<(libchromiumcontent_dir)/favicon_base.lib', + '<(libchromiumcontent_dir)/google_apis.lib', + '<(libchromiumcontent_dir)/google_core_browser.lib', + '<(libchromiumcontent_dir)/history_core_browser.lib', + '<(libchromiumcontent_dir)/query_parser.lib', + '<(libchromiumcontent_dir)/url_formatter.lib', + '<(libchromiumcontent_dir)/search_engines.lib', + '-lesent.lib', + ], + 'importer_include_dir': [ + '<(libchromiumcontent_dir)/gen/components/strings', + ], + }, + }], + ], + 'target_defaults': { + 'defines': [ + 'GOOGLE_PROTOBUF_NO_RTTI' + ], + }, +} diff --git a/lib/browser/api/exports/electron.js b/lib/browser/api/exports/electron.js index c47c046906..44a191b4bc 100644 --- a/lib/browser/api/exports/electron.js +++ b/lib/browser/api/exports/electron.js @@ -35,6 +35,12 @@ Object.defineProperties(exports, { return require('../dialog') } }, + importer: { + enumerable: true, + get: function () { + return require('../importer') + } + }, ipcMain: { enumerable: true, get: function () { diff --git a/lib/browser/api/importer.js b/lib/browser/api/importer.js new file mode 100644 index 0000000000..1ad43f343c --- /dev/null +++ b/lib/browser/api/importer.js @@ -0,0 +1,6 @@ +const {EventEmitter} = require('events') +const {importer, Importer} = process.atomBinding('importer') + +Object.setPrototypeOf(Importer.prototype, EventEmitter.prototype) + +module.exports = importer