From 6c9851a40d3f6280dc322c2392d9cfcf8fba1b2d Mon Sep 17 00:00:00 2001 From: "beng@google.com" Date: Sat, 13 Sep 2008 00:56:27 +0000 Subject: Move importer files into an importer subdirectory. Also delete title chomper no one uses it. B=2205 Review URL: http://codereview.chromium.org/3035 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2154 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/browser.vcproj | 124 ++-- chrome/browser/firefox2_importer.cc | 515 ------------- chrome/browser/firefox2_importer.h | 112 --- chrome/browser/firefox3_importer.cc | 502 ------------- chrome/browser/firefox3_importer.h | 85 --- chrome/browser/firefox_importer_unittest.cc | 216 ------ chrome/browser/firefox_importer_utils.cc | 727 ------------------ chrome/browser/firefox_importer_utils.h | 211 ------ chrome/browser/firefox_profile_lock.cc | 87 --- chrome/browser/firefox_profile_lock.h | 94 --- chrome/browser/first_run.cc | 2 +- chrome/browser/ie_importer.cc | 567 -------------- chrome/browser/ie_importer.h | 89 --- chrome/browser/importer.cc | 541 -------------- chrome/browser/importer.h | 357 --------- chrome/browser/importer/firefox2_importer.cc | 515 +++++++++++++ chrome/browser/importer/firefox2_importer.h | 112 +++ chrome/browser/importer/firefox3_importer.cc | 502 +++++++++++++ chrome/browser/importer/firefox3_importer.h | 85 +++ .../browser/importer/firefox_importer_unittest.cc | 216 ++++++ chrome/browser/importer/firefox_importer_utils.cc | 727 ++++++++++++++++++ chrome/browser/importer/firefox_importer_utils.h | 211 ++++++ chrome/browser/importer/firefox_profile_lock.cc | 87 +++ chrome/browser/importer/firefox_profile_lock.h | 94 +++ chrome/browser/importer/ie_importer.cc | 567 ++++++++++++++ chrome/browser/importer/ie_importer.h | 89 +++ chrome/browser/importer/importer.cc | 541 ++++++++++++++ chrome/browser/importer/importer.h | 357 +++++++++ chrome/browser/importer/importer_unittest.cc | 819 +++++++++++++++++++++ chrome/browser/importer/mork_reader.cc | 581 +++++++++++++++ chrome/browser/importer/mork_reader.h | 165 +++++ chrome/browser/importer_unittest.cc | 819 --------------------- chrome/browser/mork_reader.cc | 581 --------------- chrome/browser/mork_reader.h | 165 ----- chrome/browser/title_chomper.cc | 75 -- chrome/browser/title_chomper.h | 30 - chrome/browser/title_chomper_unittest.cc | 95 --- chrome/browser/views/first_run_customize_view.cc | 2 +- chrome/browser/views/first_run_view.cc | 2 +- chrome/browser/views/first_run_view_base.h | 3 +- chrome/browser/views/importer_view.h | 2 +- chrome/test/unit/unittests.vcproj | 12 +- 42 files changed, 5736 insertions(+), 5947 deletions(-) delete mode 100644 chrome/browser/firefox2_importer.cc delete mode 100644 chrome/browser/firefox2_importer.h delete mode 100644 chrome/browser/firefox3_importer.cc delete mode 100644 chrome/browser/firefox3_importer.h delete mode 100644 chrome/browser/firefox_importer_unittest.cc delete mode 100644 chrome/browser/firefox_importer_utils.cc delete mode 100644 chrome/browser/firefox_importer_utils.h delete mode 100644 chrome/browser/firefox_profile_lock.cc delete mode 100644 chrome/browser/firefox_profile_lock.h delete mode 100644 chrome/browser/ie_importer.cc delete mode 100644 chrome/browser/ie_importer.h delete mode 100644 chrome/browser/importer.cc delete mode 100644 chrome/browser/importer.h create mode 100644 chrome/browser/importer/firefox2_importer.cc create mode 100644 chrome/browser/importer/firefox2_importer.h create mode 100644 chrome/browser/importer/firefox3_importer.cc create mode 100644 chrome/browser/importer/firefox3_importer.h create mode 100644 chrome/browser/importer/firefox_importer_unittest.cc create mode 100644 chrome/browser/importer/firefox_importer_utils.cc create mode 100644 chrome/browser/importer/firefox_importer_utils.h create mode 100644 chrome/browser/importer/firefox_profile_lock.cc create mode 100644 chrome/browser/importer/firefox_profile_lock.h create mode 100644 chrome/browser/importer/ie_importer.cc create mode 100644 chrome/browser/importer/ie_importer.h create mode 100644 chrome/browser/importer/importer.cc create mode 100644 chrome/browser/importer/importer.h create mode 100644 chrome/browser/importer/importer_unittest.cc create mode 100644 chrome/browser/importer/mork_reader.cc create mode 100644 chrome/browser/importer/mork_reader.h delete mode 100644 chrome/browser/importer_unittest.cc delete mode 100644 chrome/browser/mork_reader.cc delete mode 100644 chrome/browser/mork_reader.h delete mode 100644 chrome/browser/title_chomper.cc delete mode 100644 chrome/browser/title_chomper.h delete mode 100644 chrome/browser/title_chomper_unittest.cc (limited to 'chrome') diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 078f95f..e98fea0 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -1154,14 +1154,6 @@ > - - - - @@ -1639,6 +1631,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - @@ -2135,22 +2155,6 @@ > - - - - - - - - @@ -2163,14 +2167,6 @@ > - - - - diff --git a/chrome/browser/firefox2_importer.cc b/chrome/browser/firefox2_importer.cc deleted file mode 100644 index 15e2931..0000000 --- a/chrome/browser/firefox2_importer.cc +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright (c) 2006-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/firefox2_importer.h" - -#include "base/file_util.h" -#include "base/path_service.h" -#include "base/registry.h" -#include "base/string_util.h" -#include "base/values.h" -#include "chrome/browser/firefox_importer_utils.h" -#include "chrome/browser/mork_reader.h" -#include "chrome/browser/template_url.h" -#include "chrome/browser/template_url_parser.h" -#include "chrome/common/l10n_util.h" -#include "chrome/common/time_format.h" -#include "generated_resources.h" -#include "net/base/data_url.h" - -// Firefox2Importer. - -Firefox2Importer::Firefox2Importer() { -} - -Firefox2Importer::~Firefox2Importer() { -} - -void Firefox2Importer::StartImport(ProfileInfo profile_info, - uint16 items, ProfileWriter* writer, - ImporterHost* host) { - writer_ = writer; - source_path_ = profile_info.source_path; - app_path_ = profile_info.app_path; - importer_host_ = host; - - // The order here is important! - NotifyStarted(); - if ((items & HOME_PAGE) && !cancelled()) - ImportHomepage(); // Doesn't have a UI item. - if ((items & FAVORITES) && !cancelled()) { - NotifyItemStarted(FAVORITES); - ImportBookmarks(); - NotifyItemEnded(FAVORITES); - } - if ((items & SEARCH_ENGINES) && !cancelled()) { - NotifyItemStarted(SEARCH_ENGINES); - ImportSearchEngines(); - NotifyItemEnded(SEARCH_ENGINES); - } - if ((items & PASSWORDS) && !cancelled()) { - NotifyItemStarted(PASSWORDS); - ImportPasswords(); - NotifyItemEnded(PASSWORDS); - } - if ((items & HISTORY) && !cancelled()) { - NotifyItemStarted(HISTORY); - ImportHistory(); - NotifyItemEnded(HISTORY); - } - NotifyEnded(); -} - -// static -void Firefox2Importer::LoadDefaultBookmarks(const std::wstring& app_path, - std::set *urls) { - // Firefox keeps its default bookmarks in a bookmarks.html file that - // lives at: \defaults\profile\bookmarks.html - std::wstring file = app_path; - file_util::AppendToPath(&file, L"defaults\\profile\\bookmarks.html"); - - urls->clear(); - - // Read the whole file. - std::string content; - file_util::ReadFileToString(file, &content); - std::vector lines; - SplitString(content, '\n', &lines); - - std::string charset; - for (size_t i = 0; i < lines.size(); ++i) { - std::string line; - TrimString(lines[i], " ", &line); - - // Get the encoding of the bookmark file. - if (ParseCharsetFromLine(line, &charset)) - continue; - - // Get the bookmark. - std::wstring title; - GURL url, favicon; - std::wstring shortcut; - Time add_date; - std::wstring post_data; - if (ParseBookmarkFromLine(line, charset, &title, &url, - &favicon, &shortcut, &add_date, - &post_data)) - urls->insert(url); - } -} - -// static -TemplateURL* Firefox2Importer::CreateTemplateURL(const std::wstring& title, - const std::wstring& keyword, - const GURL& url) { - // Skip if the keyword or url is invalid. - if (keyword.empty() && url.is_valid()) - return NULL; - - TemplateURL* t_url = new TemplateURL(); - // We set short name by using the title if it exists. - // Otherwise, we use the shortcut. - t_url->set_short_name(!title.empty() ? title : keyword); - t_url->set_keyword(keyword); - t_url->SetURL(TemplateURLRef::DisplayURLToURLRef(UTF8ToWide(url.spec())), - 0, 0); - return t_url; -} - -void Firefox2Importer::ImportBookmarks() { - // Read the whole file. - std::wstring file = source_path_; - file_util::AppendToPath(&file, L"bookmarks.html"); - std::string content; - file_util::ReadFileToString(file, &content); - std::vector lines; - SplitString(content, '\n', &lines); - - // Load the default bookmarks. - std::set default_urls; - LoadDefaultBookmarks(app_path_, &default_urls); - - // Parse the bookmarks.html file. - std::vector bookmarks, toolbar_bookmarks; - std::vector template_urls; - std::vector favicons; - std::wstring last_folder - = l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_FIREFOX); - bool last_folder_on_toolbar = false; - std::vector path; - size_t toolbar_folder = 0; - std::string charset; - for (size_t i = 0; i < lines.size() && !cancelled(); ++i) { - std::string line; - TrimString(lines[i], " ", &line); - - // Get the encoding of the bookmark file. - if (ParseCharsetFromLine(line, &charset)) - continue; - - // Get the folder name. - if (ParseFolderNameFromLine(line, charset, &last_folder, - &last_folder_on_toolbar)) - continue; - - // Get the bookmark entry. - std::wstring title, shortcut; - GURL url, favicon; - Time add_date; - std::wstring post_data; - // TODO(jcampan): http://b/issue?id=1196285 we do not support POST based - // keywords yet. - if (ParseBookmarkFromLine(line, charset, &title, - &url, &favicon, &shortcut, &add_date, - &post_data) && - post_data.empty() && - CanImportURL(GURL(url)) && - default_urls.find(url) == default_urls.end()) { - if (toolbar_folder > path.size() && path.size() > 0) { - NOTREACHED(); // error in parsing. - break; - } - - ProfileWriter::BookmarkEntry entry; - entry.creation_time = add_date; - entry.url = url; - entry.title = title; - - if (first_run() && toolbar_folder) { - // Flatten the items in toolbar. - entry.in_toolbar = true; - entry.path.assign(path.begin() + toolbar_folder, path.end()); - toolbar_bookmarks.push_back(entry); - } else { - // Insert the item into the "Imported from Firefox" folder after - // the first run. - entry.path.assign(path.begin(), path.end()); - if (first_run()) - entry.path.erase(entry.path.begin()); - bookmarks.push_back(entry); - } - - // Save the favicon. DataURLToFaviconUsage will handle the case where - // there is no favicon. - DataURLToFaviconUsage(url, favicon, &favicons); - - // If there is a SHORTCUT attribute for this bookmark, we - // add it as our keywords. - TemplateURL* t_url = CreateTemplateURL(title, shortcut, url); - if (t_url) - template_urls.push_back(t_url); - - continue; - } - - // Bookmarks in sub-folder are encapsulated with
tag. - if (StartsWithASCII(line, "
", true)) { - path.push_back(last_folder); - last_folder.clear(); - if (last_folder_on_toolbar && !toolbar_folder) - toolbar_folder = path.size(); - } else if (StartsWithASCII(line, "
", true)) { - if (path.empty()) - break; // Mismatch
. - path.pop_back(); - if (toolbar_folder > path.size()) - toolbar_folder = 0; - } - } - - // Write data into profile. - bookmarks.insert(bookmarks.begin(), toolbar_bookmarks.begin(), - toolbar_bookmarks.end()); - if (!bookmarks.empty() && !cancelled()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddBookmarkEntry, bookmarks)); - } - if (!template_urls.empty() && !cancelled()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddKeywords, template_urls, -1, false)); - } else { - STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); - } - if (!favicons.empty()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddFavicons, favicons)); - } -} - -void Firefox2Importer::ImportPasswords() { - // Initializes NSS3. - NSSDecryptor decryptor; - if (!decryptor.Init(source_path_, source_path_) && - !decryptor.Init(app_path_, source_path_)) - return; - - // Firefox 2 uses signons2.txt to store the pssswords. If it doesn't - // exist, we try to find its older version. - std::wstring file = source_path_; - file_util::AppendToPath(&file, L"signons2.txt"); - if (!file_util::PathExists(file)) { - file = source_path_; - file_util::AppendToPath(&file, L"signons.txt"); - } - - std::string content; - file_util::ReadFileToString(file, &content); - std::vector forms; - decryptor.ParseSignons(content, &forms); - - if (!cancelled()) { - for (size_t i = 0; i < forms.size(); ++i) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddPasswordForm, forms[i])); - } - } -} - -void Firefox2Importer::ImportHistory() { - std::wstring file = source_path_; - file_util::AppendToPath(&file, L"history.dat"); - ImportHistoryFromFirefox2(file, main_loop_, writer_); -} - -void Firefox2Importer::ImportSearchEngines() { - std::vector files; - GetSearchEnginesXMLFiles(&files); - - std::vector search_engines; - ParseSearchEnginesFromXMLFiles(files, &search_engines); - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddKeywords, search_engines, - GetFirefoxDefaultSearchEngineIndex(search_engines, source_path_), - true)); -} - -void Firefox2Importer::ImportHomepage() { - GURL homepage = GetHomepage(source_path_); - if (homepage.is_valid() && !IsDefaultHomepage(homepage, app_path_)) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddHomepage, homepage)); - } -} - -void Firefox2Importer::GetSearchEnginesXMLFiles( - std::vector* files) { - // Search engines are contained in XML files in a searchplugins directory that - // can be found in 2 locations: - // - Firefox install dir (default search engines) - // - the profile dir (user added search engines) - std::wstring dir(app_path_); - file_util::AppendToPath(&dir, L"searchplugins"); - FindXMLFilesInDir(dir, files); - - std::wstring profile_dir = source_path_; - file_util::AppendToPath(&profile_dir, L"searchplugins"); - FindXMLFilesInDir(profile_dir, files); -} - -// static -bool Firefox2Importer::ParseCharsetFromLine(const std::string& line, - std::string* charset) { - const char kCharset[] = "charset="; - if (StartsWithASCII(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; - - CodepageToWide(line.substr(tag_end, end - tag_end), charset.c_str(), - OnStringUtilConversionError::SKIP, folder_name); - HTMLUnescape(folder_name); - - std::string attribute_list = line.substr(arraysize(kFolderOpen), - tag_end - arraysize(kFolderOpen) - 1); - std::string value; - if (GetAttribute(attribute_list, kToolbarFolderAttribute, &value) && - LowerCaseEqualsASCII(value, "true")) - *is_toolbar_folder = true; - else - *is_toolbar_folder = false; - - return true; -} - -// static -bool Firefox2Importer::ParseBookmarkFromLine(const std::string& line, - const std::string& charset, - std::wstring* title, - GURL* url, - GURL* favicon, - std::wstring* shortcut, - Time* add_date, - std::wstring* post_data) { - const char kItemOpen[] = "
clear(); - *url = GURL(); - *favicon = GURL(); - shortcut->clear(); - post_data->clear(); - *add_date = Time(); - - if (!StartsWithASCII(line, kItemOpen, true)) - 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 - CodepageToWide(line.substr(tag_end, end - tag_end), charset.c_str(), - OnStringUtilConversionError::SKIP, title); - HTMLUnescape(title); - - // URL - if (GetAttribute(attribute_list, kHrefAttribute, &value)) { - ReplaceSubstringsAfterOffset(&value, 0, "%22", "\""); - *url = GURL(value); - } - - // Favicon - if (GetAttribute(attribute_list, kIconAttribute, &value)) - *favicon = GURL(value); - - // Keyword - if (GetAttribute(attribute_list, kShortcutURLAttribute, &value)) { - CodepageToWide(value, charset.c_str(), OnStringUtilConversionError::SKIP, - shortcut); - HTMLUnescape(shortcut); - } - - // Add date - if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { - int64 time = StringToInt64(value); - // Upper bound it at 32 bits. - if (0 < time && time < (1LL << 32)) - *add_date = Time::FromTimeT(time); - } - - // Post data. - if (GetAttribute(attribute_list, kPostDataAttribute, &value)) { - CodepageToWide(value, charset.c_str(), - OnStringUtilConversionError::SKIP, post_data); - HTMLUnescape(post_data); - } - - return true; -} - -// static -bool Firefox2Importer::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_list.find(kQuote, begin) + 1; - size_t end = attribute_list.find(kQuote, begin); - if (end == std::string::npos) - return false; // The value is not quoted. - - *value = attribute_list.substr(begin, end - begin); - return true; -} - -// static -void Firefox2Importer::HTMLUnescape(std::wstring *text) { - ReplaceSubstringsAfterOffset(text, 0, L"<", L"<"); - ReplaceSubstringsAfterOffset(text, 0, L">", L">"); - ReplaceSubstringsAfterOffset(text, 0, L"&", L"&"); - ReplaceSubstringsAfterOffset(text, 0, L""", L"\""); - ReplaceSubstringsAfterOffset(text, 0, L"'", L"\'"); -} - -// static -void Firefox2Importer::FindXMLFilesInDir( - const std::wstring& dir, - std::vector* xml_files) { - file_util::FileEnumerator file_enum(dir, false, - file_util::FileEnumerator::FILES, - L"*.xml"); - std::wstring file(file_enum.Next()); - while (!file.empty()) { - xml_files->push_back(file); - file = file_enum.Next(); - } -} - -// static -void Firefox2Importer::DataURLToFaviconUsage( - const GURL& link_url, - const GURL& favicon_data, - std::vector* favicons) { - if (!link_url.is_valid() || !favicon_data.is_valid() || - !favicon_data.SchemeIs("data")) - 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; - - history::ImportedFavIconUsage usage; - if (!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); -} - diff --git a/chrome/browser/firefox2_importer.h b/chrome/browser/firefox2_importer.h deleted file mode 100644 index ed03e2b..0000000 --- a/chrome/browser/firefox2_importer.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef CHROME_BROWSER_FIREFOX2_IMPORTER_H_ -#define CHROME_BROWSER_FIREFOX2_IMPORTER_H_ - -#include "chrome/browser/importer.h" - -class TemplateURL; - -// Importer for Mozilla Firefox 2. -class Firefox2Importer : public Importer { - public: - Firefox2Importer(); - virtual ~Firefox2Importer(); - - // Importer methods. - virtual void StartImport(ProfileInfo profile_info, - uint16 items, - ProfileWriter* writer, - ImporterHost* host); - - // Loads the default bookmarks in the Firefox installed at |firefox_app_path|, - // and stores their locations in |urls|. - static void LoadDefaultBookmarks(const std::wstring& firefox_app_path, - std::set *urls); - - // Creates a TemplateURL with the |keyword| and |url|. |title| may be empty. - // This function transfers ownership of the created TemplateURL to the caller. - static TemplateURL* CreateTemplateURL(const std::wstring& title, - const std::wstring& keyword, - const GURL& url); - - private: - FRIEND_TEST(FirefoxImporterTest, Firefox2BookmarkParse); - FRIEND_TEST(FirefoxImporterTest, Firefox2CookesParse); - - 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(); - - // Fills |files| with the paths to the files containing the search engine - // descriptions. - void GetSearchEnginesXMLFiles(std::vector* files); - - // Helper methods for parsing bookmark file. - // Firefox 2 saves its bookmarks in a html file. We are interested in the - // bookmarks and folders, and their hierarchy. A folder starts with a - // heading tag, which contains it title. All bookmarks and sub-folders is - // following, and bracketed by a
tag: - //

title

- //

- // ... container ... - //

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

name - // Reference: http://kb.mozillazine.org/Bookmarks.html - static bool ParseCharsetFromLine(const std::string& line, - std::string* charset); - static bool ParseFolderNameFromLine(const std::string& line, - const std::string& charset, - std::wstring* folder_name, - bool* is_toolbar_folder); - // 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). - static bool ParseBookmarkFromLine(const std::string& line, - const std::string& charset, - std::wstring* title, - GURL* url, - GURL* favicon, - std::wstring* shortcut, - Time* add_date, - std::wstring* post_data); - - // Fetches the given attribute value from the |tag|. Returns true if - // successful, and |value| will contain the value. - static bool GetAttribute(const std::string& tag, - const std::string& attribute, - std::string* value); - - // There are some characters in html file will be escaped: - // '<', '>', '"', '\', '&' - // Un-escapes them if the bookmark name has those characters. - static void HTMLUnescape(std::wstring* text); - - // Fills |xml_files| with the file with an xml extension found under |dir|. - static void FindXMLFilesInDir(const std::wstring& dir, - std::vector* xml_files); - - // Given the URL of a page and a favicon data URL, adds an appropriate record - // to the given favicon usage vector. Will do nothing if the favicon is not - // valid. - static void DataURLToFaviconUsage( - const GURL& link_url, - const GURL& favicon_data, - std::vector* favicons); - - ProfileWriter* writer_; - std::wstring source_path_; - std::wstring app_path_; - - DISALLOW_EVIL_CONSTRUCTORS(Firefox2Importer); -}; - -#endif // CHROME_BROWSER_FIREFOX2_IMPORTER_H_ - diff --git a/chrome/browser/firefox3_importer.cc b/chrome/browser/firefox3_importer.cc deleted file mode 100644 index 8c325bee..0000000 --- a/chrome/browser/firefox3_importer.cc +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright (c) 2006-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/firefox3_importer.h" - -#include - -#include "base/file_util.h" -#include "base/scoped_ptr.h" -#include "base/string_util.h" -#include "chrome/browser/firefox2_importer.h" -#include "chrome/browser/firefox_importer_utils.h" -#include "chrome/common/l10n_util.h" -#include "chrome/common/time_format.h" -#include "generated_resources.h" - -// Wraps the function sqlite3_close() in a class that is -// used in scoped_ptr_malloc. - -namespace { - -class DBClose { - public: - inline void operator()(sqlite3* x) const { - sqlite3_close(x); - } -}; - -} // namespace - -void Firefox3Importer::StartImport(ProfileInfo profile_info, - uint16 items, ProfileWriter* writer, - ImporterHost* host) { - writer_ = writer; - source_path_ = profile_info.source_path; - app_path_ = profile_info.app_path; - importer_host_ = host; - - - // The order here is important! - NotifyStarted(); - if ((items & HOME_PAGE) && !cancelled()) - ImportHomepage(); // Doesn't have a UI item. - if ((items & FAVORITES) && !cancelled()) { - NotifyItemStarted(FAVORITES); - ImportBookmarks(); - NotifyItemEnded(FAVORITES); - } - if ((items & SEARCH_ENGINES) && !cancelled()) { - NotifyItemStarted(SEARCH_ENGINES); - ImportSearchEngines(); - NotifyItemEnded(SEARCH_ENGINES); - } - if ((items & PASSWORDS) && !cancelled()) { - NotifyItemStarted(PASSWORDS); - ImportPasswords(); - NotifyItemEnded(PASSWORDS); - } - if ((items & HISTORY) && !cancelled()) { - NotifyItemStarted(HISTORY); - ImportHistory(); - NotifyItemEnded(HISTORY); - } - NotifyEnded(); -} - -void Firefox3Importer::ImportHistory() { - std::wstring file = source_path_; - file_util::AppendToPath(&file, L"places.sqlite"); - if (!file_util::PathExists(file)) - return; - - sqlite3* sqlite; - if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) - return; - scoped_ptr_malloc db(sqlite); - - SQLStatement s; - // |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* stmt = "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"; - - if (s.prepare(db.get(), stmt) != SQLITE_OK) - return; - - std::vector rows; - while (s.step() == SQLITE_ROW && !cancelled()) { - GURL url(s.column_string(0)); - - // Filter out unwanted URLs. - if (!CanImportURL(url)) - continue; - - history::URLRow row(url); - row.set_title(s.column_string16(1)); - row.set_visit_count(s.column_int(2)); - row.set_hidden(s.column_int(3) == 1); - row.set_typed_count(s.column_int(4)); - row.set_last_visit(Time::FromTimeT(s.column_int64(5)/1000000)); - - rows.push_back(row); - } - if (!rows.empty() && !cancelled()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddHistoryPage, rows)); - } -} - -void Firefox3Importer::ImportBookmarks() { - std::wstring file = source_path_; - file_util::AppendToPath(&file, L"places.sqlite"); - if (!file_util::PathExists(file)) - return; - - sqlite3* sqlite; - if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) - return; - scoped_ptr_malloc db(sqlite); - - // 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.get(), &toolbar_folder_id, &menu_folder_id, - &unsorted_folder_id); - - // Load livemark IDs. - std::set livemark_id; - LoadLivemarkIDs(db.get(), &livemark_id); - - // Load the default bookmarks. Its storage is the same as Firefox 2. - std::set default_urls; - Firefox2Importer::LoadDefaultBookmarks(app_path_, &default_urls); - - BookmarkList list; - GetTopBookmarkFolder(db.get(), toolbar_folder_id, &list); - GetTopBookmarkFolder(db.get(), menu_folder_id, &list); - GetTopBookmarkFolder(db.get(), unsorted_folder_id, &list); - size_t count = list.size(); - for (size_t i = 0; i < count; ++i) - GetWholeBookmarkFolder(db.get(), &list, i); - - std::vector bookmarks; - std::vector template_urls; - 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; - SQLStatement s; - const char* stmt = "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'"; - if (s.prepare(db.get(), stmt) == SQLITE_OK) { - while (s.step() == SQLITE_ROW && !cancelled()) - post_keyword_ids.insert(s.column_int(0)); - } else { - NOTREACHED(); - } - - std::wstring firefox_folder = - l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_FIREFOX); - for (size_t i = 0; i < list.size(); ++i) { - BookmarkItem* item = list[i]; - - // The type of bookmark items is 1. - if (item->type != 1) - continue; - - // Skip the default bookmarks and unwanted URLs. - if (!CanImportURL(item->url) || - 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 (parent->id == toolbar_folder_id) { - // This bookmark entry should be put in the bookmark bar. - // But we put it in the Firefox group after first run, so - // that do not mess up the bookmark bar. - if (first_run()) { - is_in_toolbar = true; - } else { - path.insert(path.begin(), parent->title); - path.insert(path.begin(), firefox_folder); - } - found_path = true; - break; - } else if (parent->id == menu_folder_id || - parent->id == unsorted_folder_id) { - // After the first run, the item will be placed in a folder in - // the "Other bookmarks". - if (!first_run()) - path.insert(path.begin(), firefox_folder); - found_path = true; - break; - } else if (livemark_id.find(parent->id) != livemark_id.end()) { - // If the entry is under a livemark folder, we don't import it. - break; - } - path.insert(path.begin(), parent->title); - child = parent; - } - - if (!found_path) - continue; - - ProfileWriter::BookmarkEntry entry; - entry.creation_time = item->date_added; - entry.title = item->title; - entry.url = item->url; - entry.path = path; - entry.in_toolbar = is_in_toolbar; - - bookmarks.push_back(entry); - - if (item->favicon) - favicon_map[item->favicon].insert(item->url); - - // This bookmark has a keyword, we import it to our TemplateURL model. - TemplateURL* t_url = Firefox2Importer::CreateTemplateURL( - item->title, UTF8ToWide(item->keyword), item->url); - if (t_url) - template_urls.push_back(t_url); - } - - STLDeleteContainerPointers(list.begin(), list.end()); - - // Write into profile. - if (!bookmarks.empty() && !cancelled()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddBookmarkEntry, bookmarks)); - } - if (!template_urls.empty() && !cancelled()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddKeywords, template_urls, -1, false)); - } else { - STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); - } - if (!favicon_map.empty() && !cancelled()) { - std::vector favicons; - LoadFavicons(db.get(), favicon_map, &favicons); - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddFavicons, favicons)); - } -} - -void Firefox3Importer::ImportPasswords() { - // Initializes NSS3. - NSSDecryptor decryptor; - if (!decryptor.Init(source_path_, source_path_) && - !decryptor.Init(app_path_, source_path_)) - return; - - // Firefox 3 uses signons3.txt to store the passwords. - std::wstring file = source_path_; - file_util::AppendToPath(&file, L"signons3.txt"); - if (!file_util::PathExists(file)) { - file = source_path_; - file_util::AppendToPath(&file, L"signons2.txt"); - } - - std::string content; - file_util::ReadFileToString(file, &content); - std::vector forms; - decryptor.ParseSignons(content, &forms); - - if (!cancelled()) { - for (size_t i = 0; i < forms.size(); ++i) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddPasswordForm, forms[i])); - } - } -} - -void Firefox3Importer::ImportSearchEngines() { - std::vector files; - GetSearchEnginesXMLFiles(&files); - - std::vector search_engines; - ParseSearchEnginesFromXMLFiles(files, &search_engines); - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddKeywords, search_engines, - GetFirefoxDefaultSearchEngineIndex(search_engines, source_path_), true)); -} - -void Firefox3Importer::ImportHomepage() { - GURL homepage = GetHomepage(source_path_); - if (homepage.is_valid() && !IsDefaultHomepage(homepage, app_path_)) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddHomepage, homepage)); - } -} - -void Firefox3Importer::GetSearchEnginesXMLFiles( - std::vector* files) { - std::wstring file = source_path_; - file_util::AppendToPath(&file, L"search.sqlite"); - if (!file_util::PathExists(file)) - return; - - sqlite3* sqlite; - if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) - return; - scoped_ptr_malloc db(sqlite); - - SQLStatement s; - const char* stmt = "SELECT engineid FROM engine_data ORDER BY value ASC"; - - if (s.prepare(db.get(), stmt) != SQLITE_OK) - return; - - std::wstring app_path = app_path_; - file_util::AppendToPath(&app_path, L"searchplugins"); - std::wstring profile_path = source_path_; - file_util::AppendToPath(&profile_path, L"searchplugins"); - - const std::wstring kAppPrefix = L"[app]/"; - const std::wstring kProfilePrefix = L"[profile]/"; - while (s.step() == SQLITE_ROW && !cancelled()) { - std::wstring file; - std::wstring engine = UTF8ToWide(s.column_string(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::wstring::npos) { - file = app_path; - file_util::AppendToPath( - &file, - engine.substr(index + kAppPrefix.length())); // Remove '[app]/'. - } else if ((index = engine.find(kProfilePrefix)) != std::wstring::npos) { - file = profile_path; - file_util::AppendToPath( - &file, - engine.substr(index + kProfilePrefix.length())); // Remove - // '[profile]/'. - } else { - NOTREACHED() << "Unexpected Firefox 3 search engine id"; - continue; - } - files->push_back(file); - } -} - -void Firefox3Importer::LoadRootNodeID(sqlite3* db, - int* toolbar_folder_id, - int* menu_folder_id, - int* unsorted_folder_id) { - const char kToolbarFolderName[] = "toolbar"; - const char kMenuFolderName[] = "menu"; - const char kUnsortedFolderName[] = "unfiled"; - - SQLStatement s; - const char* stmt = "SELECT root_name, folder_id FROM moz_bookmarks_roots"; - if (s.prepare(db, stmt) != SQLITE_OK) - return; - - while (s.step() == SQLITE_ROW) { - std::string folder = s.column_string(0); - int id = s.column_int(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 Firefox3Importer::LoadLivemarkIDs(sqlite3* db, - std::set* livemark) { - const char kFeedAnnotation[] = "livemark/feedURI"; - livemark->clear(); - - SQLStatement s; - const char* stmt = "SELECT b.item_id " - "FROM moz_anno_attributes a " - "JOIN moz_items_annos b ON a.id = b.anno_attribute_id " - "WHERE a.name = ? "; - if (s.prepare(db, stmt) != SQLITE_OK) - return; - - s.bind_string(0, kFeedAnnotation); - while (s.step() == SQLITE_ROW && !cancelled()) - livemark->insert(s.column_int(0)); -} - -void Firefox3Importer::GetTopBookmarkFolder(sqlite3* db, int folder_id, - BookmarkList* list) { - SQLStatement s; - const char* stmt = "SELECT b.title " - "FROM moz_bookmarks b " - "WHERE b.type = 2 AND b.id = ? " - "ORDER BY b.position"; - if (s.prepare(db, stmt) != SQLITE_OK) - return; - - s.bind_int(0, folder_id); - if (s.step() == SQLITE_ROW) { - BookmarkItem* item = new BookmarkItem; - item->parent = -1; // The top level folder has no parent. - item->id = folder_id; - item->title = s.column_string16(0); - item->type = 2; - item->favicon = 0; - list->push_back(item); - } -} - -void Firefox3Importer::GetWholeBookmarkFolder(sqlite3* db, BookmarkList* list, - size_t position) { - if (position >= list->size()) { - NOTREACHED(); - return; - } - - SQLStatement s; - const char* stmt = "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"; - if (s.prepare(db, stmt) != SQLITE_OK) - return; - - s.bind_int(0, (*list)[position]->id); - BookmarkList temp_list; - while (s.step() == SQLITE_ROW) { - BookmarkItem* item = new BookmarkItem; - item->parent = static_cast(position); - item->id = s.column_int(0); - item->url = GURL(s.column_string(1)); - item->title = s.column_string16(2); - item->type = s.column_int(3); - item->keyword = s.column_string(4); - item->date_added = Time::FromTimeT(s.column_int64(5)/1000000); - item->favicon = s.column_int64(6); - - temp_list.push_back(item); - } - - // 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 == 2) - GetWholeBookmarkFolder(db, list, list->size() - 1); - } -} - -void Firefox3Importer::LoadFavicons( - sqlite3* db, - const FaviconMap& favicon_map, - std::vector* favicons) { - SQLStatement s; - const char* stmt = "SELECT url, data FROM moz_favicons WHERE id=?"; - if (s.prepare(db, stmt) != SQLITE_OK) - return; - - for (FaviconMap::const_iterator i = favicon_map.begin(); - i != favicon_map.end(); ++i) { - s.bind_int64(0, i->first); - if (s.step() == SQLITE_ROW) { - history::ImportedFavIconUsage usage; - - usage.favicon_url = GURL(s.column_string(0)); - if (!usage.favicon_url.is_valid()) - continue; // Don't bother importing favicons with invalid URLs. - - std::vector data; - if (!s.column_blob_as_vector(1, &data) || data.empty()) - continue; // Data definitely invalid. - - if (!ReencodeFavicon(&data[0], data.size(), &usage.png_data)) - continue; // Unable to decode. - - usage.urls = i->second; - favicons->push_back(usage); - } - s.reset(); - } -} - diff --git a/chrome/browser/firefox3_importer.h b/chrome/browser/firefox3_importer.h deleted file mode 100644 index 92c8e18..0000000 --- a/chrome/browser/firefox3_importer.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef CHROME_BROWSER_FIREFOX3_IMPORTER_H_ -#define CHROME_BROWSER_FIREFOX3_IMPORTER_H_ - -#include -#include -#include - -#include "base/basictypes.h" -#include "chrome/browser/importer.h" -#include "chrome/common/sqlite_utils.h" -#include "googleurl/src/gurl.h" - -// Importer for Mozilla Firefox 3. -// Firefox 3 stores its persistent information in a new system called places. -// http://wiki.mozilla.org/Places -class Firefox3Importer : public Importer { - public: - Firefox3Importer() { } - virtual ~Firefox3Importer() { } - - // Importer methods. - virtual void StartImport(ProfileInfo profile_info, - uint16 items, - ProfileWriter* writer, - ImporterHost* host); - - private: - typedef std::map > FaviconMap; - - 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 GetSearchEnginesXMLFiles(std::vector* files); - - // The struct stores the information about a bookmark item. - struct BookmarkItem { - int parent; - int id; - GURL url; - std::wstring title; - int type; - std::string keyword; - Time date_added; - int64 favicon; - }; - typedef std::vector BookmarkList; - - // Gets the specific IDs of bookmark root node from |db|. - void LoadRootNodeID(sqlite3* db, int* toolbar_folder_id, - int* menu_folder_id, int* unsorted_folder_id); - - // Loads all livemark IDs from database |db|. - void LoadLivemarkIDs(sqlite3* db, std::set* livemark); - - // Gets the bookmark folder with given ID, and adds the entry in |list| - // if successful. - void GetTopBookmarkFolder(sqlite3* db, int folder_id, BookmarkList* list); - - // Loads all children of the given folder, and appends them to the |list|. - void GetWholeBookmarkFolder(sqlite3* db, BookmarkList* list, - size_t position); - - // Loads the favicons given in the map from the database, loads the data, - // and converts it into FaviconUsage structures. - void LoadFavicons(sqlite3* db, - const FaviconMap& favicon_map, - std::vector* favicons); - - ProfileWriter* writer_; - std::wstring source_path_; - std::wstring app_path_; - - DISALLOW_EVIL_CONSTRUCTORS(Firefox3Importer); -}; - -#endif // CHROME_BROWSER_FIREFOX3_IMPORTER_H_ - diff --git a/chrome/browser/firefox_importer_unittest.cc b/chrome/browser/firefox_importer_unittest.cc deleted file mode 100644 index 08209db..0000000 --- a/chrome/browser/firefox_importer_unittest.cc +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) 2006-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 "testing/gtest/include/gtest/gtest.h" - -#include "base/file_util.h" -#include "base/path_service.h" -#include "chrome/browser/firefox2_importer.h" -#include "chrome/browser/firefox_importer_utils.h" -#include "chrome/browser/firefox_profile_lock.h" -#include "chrome/common/chrome_paths.h" - -TEST(FirefoxImporterTest, Firefox2NSS3Decryptor) { - std::wstring nss_path; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &nss_path)); - file_util::AppendToPath(&nss_path, L"firefox2_nss"); - std::wstring db_path; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &db_path)); - file_util::AppendToPath(&db_path, L"firefox2_profile"); - NSSDecryptor decryptor; - EXPECT_TRUE(decryptor.Init(nss_path, db_path)); - EXPECT_EQ(L"hello", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" - "wFAYIKoZIhvcNAwcECBJM63MpT9rtBAjMCm7qo/EhlA==")); - // Test UTF-16 encoding. - EXPECT_EQ(L"\x4E2D", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" - "wFAYIKoZIhvcNAwcECN9OQ5ZFmhb8BAiFo1Z+fUvaIQ==")); -} - -TEST(FirefoxImporterTest, Firefox3NSS3Decryptor) { - std::wstring nss_path; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &nss_path)); - file_util::AppendToPath(&nss_path, L"firefox3_nss"); - std::wstring db_path; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &db_path)); - file_util::AppendToPath(&db_path, L"firefox3_profile"); - NSSDecryptor decryptor; - EXPECT_TRUE(decryptor.Init(nss_path, db_path)); - EXPECT_EQ(L"hello", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" - "wFAYIKoZIhvcNAwcECKajtRg4qFSHBAhv9luFkXgDJA==")); - // Test UTF-16 encoding. - EXPECT_EQ(L"\x4E2D", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" - "wFAYIKoZIhvcNAwcECLWqqiccfQHWBAie74hxnULxlw==")); -} - -TEST(FirefoxImporterTest, Firefox2BookmarkParse) { - bool result; - - // Tests charset. - std::string charset; - result = Firefox2Importer::ParseCharsetFromLine( - "", - &charset); - EXPECT_TRUE(result); - EXPECT_EQ("UTF-8", charset); - - // Escaped characters in name. - std::wstring folder_name; - bool is_toolbar_folder; - result = Firefox2Importer::ParseFolderNameFromLine( - "

< >" - " & " ' \\ /

", - charset, &folder_name, &is_toolbar_folder); - EXPECT_TRUE(result); - EXPECT_EQ(L"< > & \" ' \\ /", folder_name); - EXPECT_EQ(false, is_toolbar_folder); - - // Empty name and toolbar folder attribute. - result = Firefox2Importer::ParseFolderNameFromLine( - "

", - charset, &folder_name, &is_toolbar_folder); - EXPECT_TRUE(result); - EXPECT_EQ(L"", folder_name); - EXPECT_EQ(true, is_toolbar_folder); - - // Unicode characters in title and shortcut. - std::wstring title; - GURL url, favicon; - std::wstring shortcut; - std::wstring post_data; - Time add_date; - result = Firefox2Importer::ParseBookmarkFromLine( - "
\xE4\xB8\xAD\xE6\x96\x87", - charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); - EXPECT_TRUE(result); - EXPECT_EQ(L"\x4E2D\x6587", title); - EXPECT_EQ("http://chinese.site.cn/path?query=1#ref", url.spec()); - EXPECT_EQ(L"\x4E2D", shortcut); - EXPECT_EQ(L"", post_data); - EXPECT_TRUE(Time() == add_date); - - // No shortcut, and url contains %22 ('"' character). - result = Firefox2Importer::ParseBookmarkFromLine( - "
%22\">name", - charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); - EXPECT_TRUE(result); - EXPECT_EQ(L"name", title); - EXPECT_EQ("http://domain.com/?q=\"%3C%3E\"", url.spec()); - EXPECT_EQ(L"", shortcut); - EXPECT_EQ(L"", post_data); - EXPECT_TRUE(Time() == add_date); - - // Creation date. - result = Firefox2Importer::ParseBookmarkFromLine( - "
name", - charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); - EXPECT_TRUE(result); - EXPECT_EQ(L"name", title); - EXPECT_EQ(GURL("http://site/"), url); - EXPECT_EQ(L"", shortcut); - EXPECT_EQ(L"", post_data); - EXPECT_TRUE(Time::FromTimeT(1121301154) == add_date); - - // Post-data - result = Firefox2Importer::ParseBookmarkFromLine( - "
Test Post keyword", - charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); - EXPECT_TRUE(result); - EXPECT_EQ(L"Test Post keyword", title); - EXPECT_EQ("http://localhost:8080/test/hello.html", url.spec()); - EXPECT_EQ(L"post", shortcut); - EXPECT_EQ(L"lname%3D%25s", post_data); - EXPECT_TRUE(Time::FromTimeT(1212447159) == add_date); - - // Invalid case. - result = Firefox2Importer::ParseBookmarkFromLine( - "
lock; - EXPECT_EQ(static_cast(NULL), lock.get()); - EXPECT_FALSE(file_util::PathExists(lock_file_path)); - lock.reset(new FirefoxProfileLock(test_path)); - EXPECT_TRUE(lock->HasAcquired()); - EXPECT_TRUE(file_util::PathExists(lock_file_path)); - lock->Unlock(); - EXPECT_FALSE(lock->HasAcquired()); - EXPECT_FALSE(file_util::PathExists(lock_file_path)); - lock->Lock(); - EXPECT_TRUE(lock->HasAcquired()); - EXPECT_TRUE(file_util::PathExists(lock_file_path)); - lock->Lock(); - EXPECT_TRUE(lock->HasAcquired()); - lock->Unlock(); - EXPECT_FALSE(lock->HasAcquired()); - EXPECT_FALSE(file_util::PathExists(lock_file_path)); -} - -// If for some reason the lock file is left behind by the previous owner, we -// should still be able to lock it, at least in the Windows implementation. -TEST(FirefoxImporterTest, ProfileLockOrphaned) { - std::wstring test_path; - file_util::CreateNewTempDirectory(L"firefox_profile", &test_path); - std::wstring lock_file_path = test_path; - file_util::AppendToPath(&lock_file_path, FirefoxProfileLock::kLockFileName); - - // Create the orphaned lock file. - HANDLE lock_file = CreateFile(lock_file_path.c_str(), - GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, - NULL); - CloseHandle(lock_file); - EXPECT_TRUE(file_util::PathExists(lock_file_path)); - - scoped_ptr lock; - EXPECT_EQ(static_cast(NULL), lock.get()); - lock.reset(new FirefoxProfileLock(test_path)); - EXPECT_TRUE(lock->HasAcquired()); - lock->Unlock(); - EXPECT_FALSE(lock->HasAcquired()); -} - -// Tests two locks contending for the same lock file. -TEST(FirefoxImporterTest, ProfileLockContention) { - std::wstring test_path; - file_util::CreateNewTempDirectory(L"firefox_profile", &test_path); - - scoped_ptr lock1; - EXPECT_EQ(static_cast(NULL), lock1.get()); - lock1.reset(new FirefoxProfileLock(test_path)); - EXPECT_TRUE(lock1->HasAcquired()); - - scoped_ptr lock2; - EXPECT_EQ(static_cast(NULL), lock2.get()); - lock2.reset(new FirefoxProfileLock(test_path)); - EXPECT_FALSE(lock2->HasAcquired()); - - lock1->Unlock(); - EXPECT_FALSE(lock1->HasAcquired()); - - lock2->Lock(); - EXPECT_TRUE(lock2->HasAcquired()); - lock2->Unlock(); - EXPECT_FALSE(lock2->HasAcquired()); -} - diff --git a/chrome/browser/firefox_importer_utils.cc b/chrome/browser/firefox_importer_utils.cc deleted file mode 100644 index c21dac6..0000000 --- a/chrome/browser/firefox_importer_utils.cc +++ /dev/null @@ -1,727 +0,0 @@ -// Copyright (c) 2006-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/firefox_importer_utils.h" - -#include - -#include "base/file_util.h" -#include "base/logging.h" -#include "base/registry.h" -#include "base/string_util.h" -#include "base/sys_string_conversions.h" -#include "base/time.h" -#include "chrome/browser/template_url.h" -#include "chrome/browser/template_url_model.h" -#include "chrome/browser/template_url_parser.h" -#include "chrome/common/win_util.h" -#include "googleurl/src/gurl.h" -#include "net/base/base64.h" - -namespace { - -// 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 -}; - -// FirefoxURLParameterFilter is used to remove parameter mentioning Firefox from -// the search URL when importing search engines. -class FirefoxURLParameterFilter : public TemplateURLParser::ParameterFilter { - public: - FirefoxURLParameterFilter() { } - ~FirefoxURLParameterFilter() { } - - // TemplateURLParser::ParameterFilter method. - virtual bool KeepParameter(const std::string& key, - const std::string& value) { - std::string low_value = StringToLowerASCII(value); - if (low_value.find("mozilla") != -1 || low_value.find("firefox") != -1 || - low_value.find("moz:") != -1 ) - return false; - return true; - } - - private: - DISALLOW_EVIL_CONSTRUCTORS(FirefoxURLParameterFilter); -}; - -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 - -int GetCurrentFirefoxMajorVersion() { - TCHAR ver_buffer[128]; - DWORD ver_buffer_length = sizeof(ver_buffer); - // 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 (int i = 0; i < arraysize(kFireFoxRegistryPaths); ++i) { - bool result = ReadFromRegistry(kFireFoxRegistryPaths[i], - L"Software\\Mozilla\\Mozilla Firefox", - L"CurrentVersion", ver_buffer, &ver_buffer_length); - if (!result) - continue; - return _wtoi(ver_buffer); - } - return 0; -} - -std::wstring GetProfilesINI() { - // The default location of the profile folder containing user data is - // under the "Application Data" folder in Windows XP. - std::wstring ini_file; - wchar_t buffer[MAX_PATH] = {0}; - if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, - SHGFP_TYPE_CURRENT, buffer))) { - ini_file = buffer; - file_util::AppendToPath(&ini_file, L"Mozilla\\Firefox\\profiles.ini"); - } - if (!file_util::PathExists(ini_file)) - ini_file.clear(); - - return ini_file; -} - -std::wstring GetFirefoxInstallPath() { - // Detects the path that Firefox is installed in. - std::wstring registry_path = L"Software\\Mozilla\\Mozilla Firefox"; - TCHAR buffer[MAX_PATH]; - DWORD buffer_length = sizeof(buffer); - bool result; - result = ReadFromRegistry(HKEY_LOCAL_MACHINE, registry_path.c_str(), - L"CurrentVersion", buffer, &buffer_length); - if (!result) - return std::wstring(); - registry_path += L"\\" + std::wstring(buffer) + L"\\Main"; - buffer_length = sizeof(buffer); - result = ReadFromRegistry(HKEY_LOCAL_MACHINE, registry_path.c_str(), - L"Install Directory", buffer, &buffer_length); - if (!result) - return std::wstring(); - return buffer; -} - -void ParseProfileINI(std::wstring file, DictionaryValue* root) { - // Reads the whole INI file. - std::string content; - file_util::ReadFileToString(file, &content); - ReplaceSubstringsAfterOffset(&content, 0, "\r\n", "\n"); - std::vector lines; - SplitString(content, '\n', &lines); - - // Parses the file. - root->Clear(); - std::wstring current_section; - for (size_t i = 0; i < lines.size(); ++i) { - std::wstring line = UTF8ToWide(lines[i]); - if (line.empty()) { - // Skips the empty line. - continue; - } - if (line[0] == L'#' || line[0] == L';') { - // This line is a comment. - continue; - } - if (line[0] == L'[') { - // It is a section header. - current_section = line.substr(1); - size_t end = current_section.rfind(L']'); - if (end != std::wstring::npos) - current_section.erase(end); - } else { - std::wstring key, value; - size_t equal = line.find(L'='); - if (equal != std::wstring::npos) { - key = line.substr(0, equal); - value = line.substr(equal + 1); - // Checks whether the section and key contain a '.' character. - // Those sections and keys break DictionaryValue's path format, - // so we discard them. - if (current_section.find(L'.') == std::wstring::npos && - key.find(L'.') == std::wstring::npos) - root->SetString(current_section + L"." + key, value); - } - } - } -} - -bool CanImportURL(const GURL& url) { - const char* kInvalidSchemes[] = {"wyciwyg", "place", "about", "chrome"}; - - // The URL is not valid. - if (!url.is_valid()) - return false; - - // Filter out the URLs with unsupported schemes. - for (int i = 0; i < arraysize(kInvalidSchemes); ++i) { - if (url.SchemeIs(kInvalidSchemes[i])) - return false; - } - - return true; -} - -void ParseSearchEnginesFromXMLFiles(const std::vector& xml_files, - std::vector* search_engines) { - DCHECK(search_engines); - - std::map search_engine_for_url; - std::string content; - // The first XML file represents the default search engine in Firefox 3, so we - // need to keep it on top of the list. - TemplateURL* default_turl = NULL; - for (std::vector::const_iterator iter = xml_files.begin(); - iter != xml_files.end(); ++iter) { - file_util::ReadFileToString(*iter, &content); - TemplateURL* template_url = new TemplateURL(); - FirefoxURLParameterFilter param_filter; - if (TemplateURLParser::Parse( - reinterpret_cast(content.data()), - content.length(), ¶m_filter, template_url) && - template_url->url()) { - std::wstring url = template_url->url()->url(); - std::map::iterator iter = - search_engine_for_url.find(url); - if (iter != search_engine_for_url.end()) { - // 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; - search_engine_for_url.erase(iter); - } - // Give this a keyword to facilitate tab-to-search, if possible. - template_url->set_keyword(TemplateURLModel::GenerateKeyword(GURL(url), - false)); - template_url->set_show_in_default_list(true); - search_engine_for_url[url] = template_url; - if (!default_turl) - default_turl = template_url; - } else { - delete template_url; - } - content.clear(); - } - - // Put the results in the |search_engines| vector. - std::map::iterator t_iter; - for (t_iter = search_engine_for_url.begin(); - t_iter != search_engine_for_url.end(); ++t_iter) { - if (t_iter->second == default_turl) - search_engines->insert(search_engines->begin(), default_turl); - else - search_engines->push_back(t_iter->second); - } -} - -bool ReadPrefFile(const std::wstring& path_name, - const std::wstring& file_name, - std::string* content) { - if (content == NULL) - return false; - - std::wstring file = path_name; - file_util::AppendToPath(&file, file_name.c_str()); - - file_util::ReadFileToString(file, content); - - if (content->empty()) { - NOTREACHED() << L"Firefox preference file " << file_name.c_str() - << L" is empty."; - return false; - } - - return true; -} - -std::string ReadBrowserConfigProp(const std::wstring& app_path, - const std::string& pref_key) { - std::string content; - if (!ReadPrefFile(app_path, L"browserconfig.properties", &content)) - return ""; - - // This file has the syntax: key=value. - size_t prop_index = content.find(pref_key + "="); - if (prop_index == -1) - return ""; - - size_t start = prop_index + pref_key.length(); - size_t stop = -1; - if (start != -1) - stop = content.find("\n", start + 1); - - if (start == -1 || stop == -1 || (start == stop)) { - NOTREACHED() << "Firefox property " << pref_key << " could not be parsed."; - return ""; - } - - return content.substr(start + 1, stop - start - 1); -} - -std::string ReadPrefsJsValue(const std::wstring& profile_path, - const std::string& pref_key) { - std::string content; - if (!ReadPrefFile(profile_path, L"prefs.js", &content)) - return ""; - - // 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 == -1) - return ""; - - size_t start = prop_index + search_for.length(); - size_t stop = -1; - if (start != -1) - stop = content.find(")", start + 1); - - if (start == -1 || stop == -1) { - NOTREACHED() << "Firefox property " << pref_key << " could not be parsed."; - return ""; - } - - // 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); -} - -int GetFirefoxDefaultSearchEngineIndex( - const std::vector& search_engines, - const std::wstring& profile_path) { - // The default search engine is contained in the file prefs.js found in the - // profile directory. - // It is the "browser.search.selectedEngine" property. - if (search_engines.empty()) - return -1; - - std::wstring default_se_name = UTF8ToWide( - ReadPrefsJsValue(profile_path, "browser.search.selectedEngine")); - - int default_se_index = -1; - for (std::vector::const_iterator iter = search_engines.begin(); - iter != search_engines.end(); ++iter) { - if (default_se_name == (*iter)->short_name()) { - default_se_index = static_cast(iter - search_engines.begin()); - break; - } - } - if (default_se_index == -1) { - NOTREACHED() << - "Firefox default search engine not found in search engine list"; - } - - return default_se_index; -} - -GURL GetHomepage(const std::wstring& 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 std::wstring& 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. - std::vector urls; - SplitString(default_homepages, '|', &urls); - - for (size_t i = 0; i < urls.size(); ++i) { - if (homepage.spec() == GURL(urls[i]).spec()) - return true; - } - - return false; -} - -// class NSSDecryptor. - -// 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"; - -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::Init(const std::wstring& dll_path, - const std::wstring& 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.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. - std::wstring path = dll_path; - file_util::AppendToPath(&path, kNSS3Library); - nss3_dll_ = LoadLibraryEx(path.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 = dll_path; - file_util::AppendToPath(&path, kSoftokn3Library); - softokn3_dll_ = LoadLibraryEx(path.c_str(), NULL, - LOAD_WITH_ALTERED_SEARCH_PATH); - if (softokn3_dll_ == NULL) { - Free(); - return false; - } - } - - // NSPR DLLs are already loaded now. - HMODULE plds4_dll = GetModuleHandle(kPLDS4Library); - HMODULE nspr4_dll = GetModuleHandle(kNSPR4Library); - if (plds4_dll == NULL || nspr4_dll == NULL) { - Free(); - return false; - } - - // Gets the function address. - NSS_Init = (NSSInitFunc)GetProcAddress(nss3_dll_, "NSS_Init"); - NSS_Shutdown = (NSSShutdownFunc)GetProcAddress(nss3_dll_, "NSS_Shutdown"); - PK11_GetInternalKeySlot = (PK11GetInternalKeySlotFunc) - GetProcAddress(nss3_dll_, "PK11_GetInternalKeySlot"); - PK11_FreeSlot = (PK11FreeSlotFunc)GetProcAddress(nss3_dll_, "PK11_FreeSlot"); - PK11_Authenticate = (PK11AuthenticateFunc) - GetProcAddress(nss3_dll_, "PK11_Authenticate"); - PK11SDR_Decrypt = (PK11SDRDecryptFunc) - GetProcAddress(nss3_dll_, "PK11SDR_Decrypt"); - SECITEM_FreeItem = (SECITEMFreeItemFunc) - GetProcAddress(nss3_dll_, "SECITEM_FreeItem"); - PL_ArenaFinish = (PLArenaFinishFunc) - GetProcAddress(plds4_dll, "PL_ArenaFinish"); - PR_Cleanup = (PRCleanupFunc)GetProcAddress(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).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) - FreeLibrary(softokn3_dll_); - softokn3_dll_ = NULL; - if (nss3_dll_ != NULL) - FreeLibrary(nss3_dll_); - nss3_dll_ = NULL; - 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; -} - -// 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 ***** */ - -std::wstring NSSDecryptor::Decrypt(const std::string& crypt) const { - // Do nothing if NSS is not loaded. - if (!nss3_dll_) - return std::wstring(); - - std::string plain; - - // The old style password is encoded in base64. They are identified - // by a leading '~'. Otherwise, we should decrypt the text. - if (crypt[0] != '~') { - std::string decoded_data; - net::Base64Decode(crypt, &decoded_data); - PK11SlotInfo* slot = NULL; - slot = PK11_GetInternalKeySlot(); - SECStatus result = PK11_Authenticate(slot, PR_TRUE, NULL); - if (result != SECSuccess) { - PK11_FreeSlot(slot); - return std::wstring(); - } - - 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; - result = PK11SDR_Decrypt(&request, &reply, NULL); - if (result == SECSuccess) - plain.assign(reinterpret_cast(reply.data), reply.len); - - SECITEM_FreeItem(&reply, PR_FALSE); - PK11_FreeSlot(slot); - } else { - // Deletes the leading '~' before decoding. - net::Base64Decode(crypt.substr(1), &plain); - } - - return UTF8ToWide(plain); -} - -// There are three versions of password filess. 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 std::string& content, - std::vector* forms) { - forms->clear(); - - // Splits the file content into lines. - std::vector lines; - SplitString(content, '\n', &lines); - - // 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; - - GURL::Replacements rep; - rep.ClearQuery(); - rep.ClearRef(); - rep.ClearUsername(); - rep.ClearPassword(); - - // Reads never-saved list. Domains are stored one per line. - size_t i; - for (i = 1; i < lines.size() && lines[i].compare(".") != 0; ++i) { - PasswordForm form; - form.origin = GURL(lines[i]).ReplaceComponents(rep); - form.signon_realm = form.origin.GetOrigin().spec(); - form.blacklisted_by_user = true; - forms->push_back(form); - } - ++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; - - PasswordForm form; - - // 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) - GURL url; - std::string realm; - const char kRealmBracketBegin[] = " ("; - const char kRealmBracketEnd[] = ")"; - if (lines[begin].find(kRealmBracketBegin) != std::string::npos) { - // In this case, the scheme may not exsit. We assume that the - // scheme is HTTP. - if (lines[begin].find("://") == std::string::npos) - lines[begin] = "http://" + lines[begin]; - - size_t start = lines[begin].find(kRealmBracketBegin); - url = GURL(lines[begin].substr(0, start)); - - start += std::string(kRealmBracketBegin).size(); - size_t end = lines[begin].rfind(kRealmBracketEnd); - realm = lines[begin].substr(start, end - start); - } else { - // Don't have http realm. It is the URL that the following passwords - // belong to. - url = GURL(lines[begin]); - } - // Skips this block if the URL is not valid. - if (!url.is_valid()) - continue; - form.origin = url.ReplaceComponents(rep); - form.signon_realm = form.origin.GetOrigin().spec(); - if (!realm.empty()) - form.signon_realm += realm; - form.ssl_valid = form.origin.SchemeIsSecure(); - ++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. - form.username_element = UTF8ToWide(lines[begin++]); - form.username_value = Decrypt(lines[begin++]); - // The element name has a leading '*'. - if (lines[begin].at(0) == '*') { - form.password_element = UTF8ToWide(lines[begin++].substr(1)); - form.password_value = Decrypt(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) - form.action = GURL(lines[begin]).ReplaceComponents(rep); - ++begin; - } - // Version 3 has an extra line for further use. - if (version == 3) { - ++begin; - } - - forms->push_back(form); - } - } -} - diff --git a/chrome/browser/firefox_importer_utils.h b/chrome/browser/firefox_importer_utils.h deleted file mode 100644 index bcadab9..0000000 --- a/chrome/browser/firefox_importer_utils.h +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef CHROME_BROWSER_FIREFOX_IMPORTER_UTILS_H_ -#define CHROME_BROWSER_FIREFOX_IMPORTER_UTILS_H_ - -#include "base/values.h" -#include "webkit/glue/password_form.h" - -class GURL; -class TemplateURL; - -// Detects which version of Firefox is installed. Returns its -// major version, and drops the minor version. Returns 0 if -// failed. -int GetCurrentFirefoxMajorVersion(); - -// Gets the full path of the profiles.ini file. This file records -// the profiles that can be used by Firefox. Returns an empty -// string if failed. -std::wstring GetProfilesINI(); - -// Detects where Firefox lives. Returns a empty string if Firefox -// is not installed. -std::wstring GetFirefoxInstallPath(); - -// 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 separeated 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(std::wstring file, DictionaryValue* root); - -// Returns true if we want to add the URL to the history. We filter -// out the URL with a unsupported scheme. -bool CanImportURL(const GURL& url); - -// Parses the OpenSearch XML files in |xml_files| and populates |search_engines| -// with the resulting TemplateURLs. -void ParseSearchEnginesFromXMLFiles(const std::vector& xml_files, - std::vector* search_engines); - -// Returns the index of the default search engine in the |search_engines| list. -// If none is found, -1 is returned. -int GetFirefoxDefaultSearchEngineIndex( - const std::vector& search_engines, - const std::wstring& profile_path); - -// Returns the home page set in Firefox in a particular profile. -GURL GetHomepage(const std::wstring& 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 std::wstring& app_path); - -// 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); - -// 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 std::wstring& dll_path, const std::wstring& db_path); - - // Frees the libraries. - void Free(); - - // Decrypts Firefox stored passwords. Before using this method, - // make sure Init() returns true. - std::wstring 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 std::string& content, - std::vector* forms); - - private: - // 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. - HMODULE nss3_dll_; - HMODULE softokn3_dll_; - - // True if NSS_Init() has been called - bool is_nss_initialized_; - - DISALLOW_EVIL_CONSTRUCTORS(NSSDecryptor); -}; - -#endif // CHROME_BROWSER_FIREFOX_IMPORTER_UTILS_H__ - diff --git a/chrome/browser/firefox_profile_lock.cc b/chrome/browser/firefox_profile_lock.cc deleted file mode 100644 index 0a57144..0000000 --- a/chrome/browser/firefox_profile_lock.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2006-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/firefox_profile_lock.h" - -#include "base/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 ***** */ - -// static -wchar_t FirefoxProfileLock::kLockFileName[] = L"parent.lock"; - -FirefoxProfileLock::FirefoxProfileLock(const std::wstring& path) - : lock_handle_(INVALID_HANDLE_VALUE) { - lock_file_ = path; - file_util::AppendToPath(&lock_file_, kLockFileName); - Lock(); -} - -FirefoxProfileLock::~FirefoxProfileLock() { - Unlock(); -} - -void FirefoxProfileLock::Lock() { - if (HasAcquired()) - return; - lock_handle_ = CreateFile(lock_file_.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/chrome/browser/firefox_profile_lock.h b/chrome/browser/firefox_profile_lock.h deleted file mode 100644 index f8b91c8..0000000 --- a/chrome/browser/firefox_profile_lock.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef CHROME_BROWSER_FIREFOX_PROFILE_LOCK_H__ -#define CHROME_BROWSER_FIREFOX_PROFILE_LOCK_H__ - -#include - -#include - -#include "base/basictypes.h" -#include "testing/gtest/include/gtest/gtest_prod.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 std::wstring& path); - ~FirefoxProfileLock(); - - // Locks and releases the profile. - void Lock(); - void Unlock(); - - // Returns true if we lock the profile successfully. - bool HasAcquired(); - - private: - FRIEND_TEST(FirefoxImporterTest, ProfileLock); - FRIEND_TEST(FirefoxImporterTest, ProfileLockOrphaned); - - // The name of the lock file. - static wchar_t kLockFileName[]; - - // Full path of the lock file in the profile folder. - std::wstring lock_file_; - - // The handle of the lock file. - HANDLE lock_handle_; - - DISALLOW_EVIL_CONSTRUCTORS(FirefoxProfileLock); -}; - -#endif // CHROME_BROWSER_FIREFOX_PROFILE_LOCK_H__ - diff --git a/chrome/browser/first_run.cc b/chrome/browser/first_run.cc index 097ec77..da1bdd3 100644 --- a/chrome/browser/first_run.cc +++ b/chrome/browser/first_run.cc @@ -19,7 +19,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/hang_monitor/hung_window_detector.h" -#include "chrome/browser/importer.h" +#include "chrome/browser/importer/importer.h" #include "chrome/browser/profile.h" #include "chrome/browser/views/first_run_view.h" #include "chrome/common/chrome_constants.h" diff --git a/chrome/browser/ie_importer.cc b/chrome/browser/ie_importer.cc deleted file mode 100644 index 374a34d..0000000 --- a/chrome/browser/ie_importer.cc +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright (c) 2006-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/ie_importer.h" - -#include -#include -#include -#include -#include - -#include "base/file_util.h" -#include "base/registry.h" -#include "base/string_util.h" -#include "base/time.h" -#include "base/win_util.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/ie7_password.h" -#include "chrome/browser/template_url_model.h" -#include "chrome/common/l10n_util.h" -#include "chrome/common/time_format.h" -#include "chrome/common/win_util.h" -#include "googleurl/src/gurl.h" - -#include "generated_resources.h" - -namespace { - -// Gets the creation time of the given file or directory. -static Time GetFileCreationTime(const std::wstring& file) { - Time creation_time; - 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 (GetFileTime(file_handle, &creation_filetime, NULL, NULL)) - creation_time = Time::FromFileTime(creation_filetime); - return creation_time; -} - -} // 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}}; - -void IEImporter::StartImport(ProfileInfo profile_info, - uint16 items, - ProfileWriter* writer, - ImporterHost* host) { - writer_ = writer; - source_path_ = profile_info.source_path; - importer_host_ = host; - - NotifyStarted(); - - // Some IE settings (such as Protected Storage) is obtained via COM APIs. - win_util::ScopedCOMInitializer com_initializer; - - if ((items & HOME_PAGE) && !cancelled()) - ImportHomepage(); // Doesn't have a UI item. - // The order here is important! - if ((items & FAVORITES) && !cancelled()) { - NotifyItemStarted(FAVORITES); - ImportFavorites(); - NotifyItemEnded(FAVORITES); - } - if ((items & SEARCH_ENGINES) && !cancelled()) { - NotifyItemStarted(SEARCH_ENGINES); - ImportSearchEngines(); - NotifyItemEnded(SEARCH_ENGINES); - } - if ((items & PASSWORDS) && !cancelled()) { - NotifyItemStarted(PASSWORDS); - // Always import IE6 passwords. - ImportPasswordsIE6(); - - if (CurrentIEVersion() >= 7) - ImportPasswordsIE7(); - NotifyItemEnded(PASSWORDS); - } - if ((items & HISTORY) && !cancelled()) { - NotifyItemStarted(HISTORY); - ImportHistory(); - NotifyItemEnded(HISTORY); - } - NotifyEnded(); -} - -void IEImporter::ImportFavorites() { - std::wstring path; - - FavoritesInfo info; - if (!GetFavoritesInfo(&info)) - return; - - BookmarkVector bookmarks; - ParseFavoritesFolder(info, &bookmarks); - - if (!bookmarks.empty() && !cancelled()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddBookmarkEntry, bookmarks)); - } -} - -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"); - PStoreCreateFunc PStoreCreateInstance = - (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance"); - - CComPtr pstore; - HRESULT result = PStoreCreateInstance(&pstore, 0, 0, 0); - if (result != S_OK) { - FreeLibrary(pstorec_dll); - return; - } - - std::vector ac_list; - - // Enumerates AutoComplete items in the protected database. - CComPtr item; - result = pstore->EnumItems(0, &AutocompleteGUID, - &AutocompleteGUID, 0, &item); - 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; - std::wstring 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 != std::wstring::npos && ac.key.substr(i) == kDataSuffix) { - ac.key.erase(i); - ac.is_url = (ac.key.find(L"://") != std::wstring::npos); - ac_list.push_back(ac); - SplitString(data, L'\0', &ac_list[ac_list.size() - 1].data); - } - 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 (!(LowerCaseEqualsASCII(url.scheme(), "http") || - LowerCaseEqualsASCII(url.scheme(), "https"))) { - continue; - } - - 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 DONT auto-fill it unless the form appears under - // valid SSL conditions. - form.ssl_valid = url.SchemeIsSecure(); - - // 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; - } - } - - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddPasswordForm, form)); - } -} - -void IEImporter::ImportPasswordsIE7() { - if (!source_path_.empty()) { - // We have been called from the unit tests. Don't import real passwords. - return; - } - - const wchar_t kStorage2Path[] = - L"Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2"; - - RegKey key(HKEY_CURRENT_USER, kStorage2Path, KEY_READ); - RegistryValueIterator reg_iterator(HKEY_CURRENT_USER, kStorage2Path); - while (reg_iterator.Valid() && !cancelled()) { - // Get the size of the encrypted data. - DWORD value_len = 0; - if (key.ReadValue(reg_iterator.Name(), NULL, &value_len) && value_len) { - // Query the encrypted data. - std::vector value; - value.resize(value_len); - if (key.ReadValue(reg_iterator.Name(), &value.front(), &value_len)) { - IE7PasswordInfo password_info; - password_info.url_hash = reg_iterator.Name(); - password_info.encrypted_data = value; - password_info.date_created = Time::Now(); - main_loop_->PostTask(FROM_HERE, - NewRunnableMethod(writer_, - &ProfileWriter::AddIE7PasswordInfo, - password_info)); - } - } - - ++reg_iterator; - } -} - -// Reads history information from COM interface. -void IEImporter::ImportHistory() { - const std::string kSchemes[] = {"http", "https", "ftp", "file"}; - int total_schemes = arraysize(kSchemes); - - CComPtr url_history_stg2; - HRESULT result; - result = url_history_stg2.CoCreateInstance(CLSID_CUrlHistory, NULL, - CLSCTX_INPROC_SERVER); - if (FAILED(result)) - return; - CComPtr enum_url; - if (SUCCEEDED(result = url_history_stg2->EnumUrls(&enum_url))) { - std::vector rows; - STATURL stat_url; - ULONG fetched; - while (!cancelled() && - (result = enum_url->Next(1, &stat_url, &fetched)) == S_OK) { - std::wstring url_string; - std::wstring title_string; - if (stat_url.pwcsUrl) { - url_string = stat_url.pwcsUrl; - CoTaskMemFree(stat_url.pwcsUrl); - } - 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; - - history::URLRow row(url); - row.set_title(title_string); - row.set_last_visit(Time::FromFileTime(stat_url.ftLastVisited)); - if (stat_url.dwFlags == STATURL_QUERYFLAG_TOPLEVEL) { - row.set_visit_count(1); - row.set_hidden(false); - } else { - row.set_hidden(true); - } - - rows.push_back(row); - } - - if (!rows.empty() && !cancelled()) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddHistoryPage, rows)); - } - } -} - -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. - // The default key's name is contained under DefaultScope. - const wchar_t kSearchScopePath[] = - L"Software\\Microsoft\\Internet Explorer\\SearchScopes"; - - RegKey key(HKEY_CURRENT_USER, kSearchScopePath, KEY_READ); - std::wstring default_search_engine_name; - const TemplateURL* default_search_engine = NULL; - std::map search_engines_map; - key.ReadValue(L"DefaultScope", &default_search_engine_name); - RegistryKeyIterator key_iterator(HKEY_CURRENT_USER, kSearchScopePath); - while (key_iterator.Valid()) { - std::wstring sub_key_name = kSearchScopePath; - sub_key_name.append(L"\\").append(key_iterator.Name()); - RegKey sub_key(HKEY_CURRENT_USER, sub_key_name.c_str(), KEY_READ); - std::wstring url; - if (!sub_key.ReadValue(L"URL", &url) || url.empty()) { - LOG(INFO) << "No URL for IE search engine at " << key_iterator.Name(); - ++key_iterator; - 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). - std::wstring name; - if (!sub_key.ReadValue(NULL, &name) || name.empty()) { - // Try the displayable name. - if (!sub_key.ReadValue(L"DisplayName", &name) || name.empty()) { - LOG(INFO) << "No name for IE search engine at " << key_iterator.Name(); - ++key_iterator; - continue; - } - } - - std::map::iterator t_iter = - search_engines_map.find(url); - TemplateURL* template_url = - (t_iter != search_engines_map.end()) ? t_iter->second : NULL; - if (!template_url) { - // First time we see that URL. - template_url = new TemplateURL(); - template_url->set_short_name(name); - template_url->SetURL(url, 0, 0); - // Give this a keyword to facilitate tab-to-search, if possible. - template_url->set_keyword(TemplateURLModel::GenerateKeyword(GURL(url), - false)); - template_url->set_show_in_default_list(true); - search_engines_map[url] = template_url; - } - if (template_url && key_iterator.Name() == default_search_engine_name) { - DCHECK(!default_search_engine); - default_search_engine = template_url; - } - ++key_iterator; - } - - // ProfileWriter::AddKeywords() requires a vector and we have a map. - std::map::iterator t_iter; - std::vector search_engines; - int default_search_engine_index = -1; - for (t_iter = search_engines_map.begin(); t_iter != search_engines_map.end(); - ++t_iter) { - search_engines.push_back(t_iter->second); - if (default_search_engine == t_iter->second) { - default_search_engine_index = - static_cast(search_engines.size()) - 1; - } - } - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddKeywords, search_engines, default_search_engine_index, - true)); -} - -void IEImporter::ImportHomepage() { - const wchar_t kIESettingsMain[] = - L"Software\\Microsoft\\Internet Explorer\\Main"; - const wchar_t kIEHomepage[] = L"Start Page"; - const wchar_t kIEDefaultHomepage[] = L"Default_Page_URL"; - - RegKey key(HKEY_CURRENT_USER, kIESettingsMain, KEY_READ); - std::wstring homepage_url; - if (!key.ReadValue(kIEHomepage, &homepage_url) || 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. - RegKey keyDefault(HKEY_LOCAL_MACHINE, kIESettingsMain, KEY_READ); - std::wstring default_homepage_url; - if (keyDefault.ReadValue(kIEDefaultHomepage, &default_homepage_url) && - !default_homepage_url.empty()) { - if (homepage.spec() == GURL(default_homepage_url).spec()) - return; - } - - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, - &ProfileWriter::AddHomepage, homepage)); -} - -bool IEImporter::GetFavoritesInfo(IEImporter::FavoritesInfo *info) { - if (!source_path_.empty()) { - // Source path exists during testing. - info->path = source_path_; - file_util::AppendToPath(&info->path, L"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 = 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 (win_util::GetWinVersion() != win_util::WINVERSION_VISTA) { - // The Link folder name is stored in the registry. - DWORD buffer_length = sizeof(buffer); - if (!ReadFromRegistry(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Internet Explorer\\Toolbar", - L"LinksFolderName", buffer, &buffer_length)) - return false; - info->links_folder = buffer; - } else { - info->links_folder = L"Links"; - } - - // Gets the creation time of the user's profile folder. - if (FAILED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, - SHGFP_TYPE_CURRENT, buffer))) - return false; - info->profile_creation_time = GetFileCreationTime(buffer); - - return true; -} - -void IEImporter::ParseFavoritesFolder(const FavoritesInfo& info, - BookmarkVector* bookmarks) { - std::wstring ie_folder = l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_IE); - BookmarkVector toolbar_bookmarks; - std::wstring file; - std::vector file_list; - file_util::FileEnumerator file_enumerator(info.path, true, - file_util::FileEnumerator::FILES); - while (!(file = file_enumerator.Next()).empty() && !cancelled()) - file_list.push_back(file); - - // Keep the bookmarks in alphabetical order. - std::sort(file_list.begin(), file_list.end()); - - for (std::vector::iterator it = file_list.begin(); - it != file_list.end(); ++it) { - std::wstring filename = file_util::GetFilenameFromPath(*it); - std::wstring extension = file_util::GetFileExtensionFromPath(filename); - if (!LowerCaseEqualsASCII(extension, "url")) - continue; - - // We don't import default bookmarks from IE, e.g. "Customize links", - // "Free Hotmail". To detect these, we compare the creation time of the - // .url file with that of the profile folder. The file's creation time - // should be 2 minute later (to allow jitter). - Time creation = GetFileCreationTime(*it); - if (info.profile_creation_time != Time() && - creation - info.profile_creation_time <= TimeDelta::FromMinutes(2)) - continue; - - // Skip the bookmark with invalid URL. - GURL url = GURL(ResolveInternetShortcut(*it)); - if (!url.is_valid()) - continue; - - // Remove the dot and the file extension, and the directory path. - std::wstring relative_path = it->substr(info.path.size(), - it->size() - filename.size() - info.path.size()); - TrimString(relative_path, L"\\", &relative_path); - - ProfileWriter::BookmarkEntry entry; - entry.title = filename.substr(0, filename.size() - (extension.size() + 1)); - entry.url = url; - entry.creation_time = creation; - if (!relative_path.empty()) - SplitString(relative_path, file_util::kPathSeparator, &entry.path); - - // Flatten the bookmarks in Link folder onto bookmark toolbar. Otherwise, - // put it into "Other bookmarks". - if (first_run() && - (entry.path.size() > 0 && entry.path[0] == info.links_folder)) { - entry.in_toolbar = true; - entry.path.erase(entry.path.begin()); - toolbar_bookmarks.push_back(entry); - } else { - // After the first run, we put the bookmarks in a "Imported From IE" - // folder, so that we don't mess up the "Other bookmarks". - if (!first_run()) - entry.path.insert(entry.path.begin(), ie_folder); - bookmarks->push_back(entry); - } - } - bookmarks->insert(bookmarks->begin(), toolbar_bookmarks.begin(), - toolbar_bookmarks.end()); -} - -std::wstring IEImporter::ResolveInternetShortcut(const std::wstring& file) { - win_util::CoMemReleaser url; - CComPtr url_locator; - HRESULT result = url_locator.CoCreateInstance(CLSID_InternetShortcut, NULL, - CLSCTX_INPROC_SERVER); - if (FAILED(result)) - return std::wstring(); - - CComPtr persist_file; - result = url_locator.QueryInterface(&persist_file); - if (FAILED(result)) - return std::wstring(); - - // Loads the Internet Shortcut from persistent storage. - result = persist_file->Load(file.c_str(), STGM_READ); - if (FAILED(result)) - return std::wstring(); - - result = url_locator->GetURL(&url); - // GetURL can return S_FALSE (FAILED(S_FALSE) is false) when url == NULL. - if (FAILED(result) || (url == NULL)) - return std::wstring(); - - return std::wstring(url); -} - -int IEImporter::CurrentIEVersion() const { - static int version = -1; - if (version < 0) { - wchar_t buffer[128]; - DWORD buffer_length = sizeof(buffer); - bool result = ReadFromRegistry(HKEY_LOCAL_MACHINE, - L"Software\\Microsoft\\Internet Explorer", - L"Version", buffer, &buffer_length); - version = (result ? _wtoi(buffer) : 0); - } - return version; -} - diff --git a/chrome/browser/ie_importer.h b/chrome/browser/ie_importer.h deleted file mode 100644 index ab6f1d5..0000000 --- a/chrome/browser/ie_importer.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef CHROME_BROWSER_IE_IMPORTER_H_ -#define CHROME_BROWSER_IE_IMPORTER_H_ - -#include "chrome/browser/importer.h" - -class IEImporter : public Importer { - public: - IEImporter() {} - virtual ~IEImporter() {} - - // Importer methods. - virtual void StartImport(ProfileInfo browser_info, - uint16 items, - ProfileWriter* writer, - ImporterHost* host); - - private: - FRIEND_TEST(ImporterTest, IEImporter); - - void ImportFavorites(); - void ImportHistory(); - - // Import password for IE6 stored in protected storage. - void ImportPasswordsIE6(); - - // Import password for IE7 and IE8 stored in Storage2. - void ImportPasswordsIE7(); - - virtual 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(); - - // Resolves what's the .url file actually targets. - // Returns empty string if failed. - std::wstring ResolveInternetShortcut(const std::wstring& file); - - // A struct hosts the information of IE Favorite folder. - struct FavoritesInfo { - std::wstring path; - std::wstring links_folder; - // The creation time of the user's profile folder. - Time profile_creation_time; - }; - typedef std::vector BookmarkVector; - - // Gets the information of Favorites folder. Returns true if successful. - bool GetFavoritesInfo(FavoritesInfo* info); - - // This function will read the files in the Favorite folder, and store - // the bookmark items in |bookmarks|. - void ParseFavoritesFolder(const FavoritesInfo& info, - BookmarkVector* bookmarks); - - // Determines which version of IE is in use. - int CurrentIEVersion() const; - - // Hosts the writer used in this importer. - ProfileWriter* writer_; - - // IE PStore subkey GUID: AutoComplete password & form data. - static const GUID kPStoreAutocompleteGUID; - - // A fake GUID for unit test. - static const GUID kUnittestGUID; - - // A struct hosts the information of AutoComplete data in PStore. - struct AutoCompleteInfo { - std::wstring key; - std::vector data; - bool is_url; - }; - - // IE does not have source path. It's used in unit tests only for - // providing a fake source. - std::wstring source_path_; - - DISALLOW_EVIL_CONSTRUCTORS(IEImporter); -}; - -#endif // CHROME_BROWSER_IE_IMPORTER_H_ - diff --git a/chrome/browser/importer.cc b/chrome/browser/importer.cc deleted file mode 100644 index 47eea2f..0000000 --- a/chrome/browser/importer.cc +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright (c) 2006-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.h" - -#include - -#include "base/file_util.h" -#include "base/gfx/image_operations.h" -#include "base/gfx/png_encoder.h" -#include "base/string_util.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/firefox2_importer.h" -#include "chrome/browser/firefox3_importer.h" -#include "chrome/browser/firefox_importer_utils.h" -#include "chrome/browser/firefox_profile_lock.h" -#include "chrome/browser/ie_importer.h" -#include "chrome/browser/template_url_model.h" -#include "chrome/browser/shell_integration.h" -#include "chrome/browser/webdata/web_data_service.h" -#include "chrome/common/gfx/favicon_size.h" -#include "chrome/common/l10n_util.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/pref_service.h" -#include "chrome/views/window.h" -#include "webkit/glue/image_decoder.h" - -#include "generated_resources.h" - -// ProfileWriter. - -bool ProfileWriter::BookmarkModelIsLoaded() const { - return profile_->GetBookmarkModel()->IsLoaded(); -} - -void ProfileWriter::AddBookmarkModelObserver(BookmarkModelObserver* observer) { - profile_->GetBookmarkModel()->AddObserver(observer); -} - -bool ProfileWriter::TemplateURLModelIsLoaded() const { - return profile_->GetTemplateURLModel()->loaded(); -} - -void ProfileWriter::AddTemplateURLModelObserver( - NotificationObserver* observer) { - TemplateURLModel* model = profile_->GetTemplateURLModel(); - NotificationService::current()->AddObserver( - observer, TEMPLATE_URL_MODEL_LOADED, - Source(model)); - model->Load(); -} - -void ProfileWriter::AddPasswordForm(const PasswordForm& form) { - profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->AddLogin(form); -} - -void ProfileWriter::AddIE7PasswordInfo(const IE7PasswordInfo& info) { - profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->AddIE7Login(info); -} - -void ProfileWriter::AddHistoryPage(const std::vector& page) { - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> - AddPagesWithDetails(page); -} - -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. - prefs->SetString(prefs::kHomePage, ASCIIToWide(home_page.spec())); - prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread()); -} - -void ProfileWriter::AddBookmarkEntry( - const std::vector& bookmark) { - BookmarkModel* model = profile_->GetBookmarkModel(); - DCHECK(model->IsLoaded()); - - bool show_bookmark_toolbar = false; - std::set groups_added_to; - for (std::vector::const_iterator it = bookmark.begin(); - it != bookmark.end(); ++it) { - // Don't insert this url if it exists in model or url is not valid. - if (model->GetNodeByURL(it->url) != NULL || !it->url.is_valid()) - continue; - - // Set up groups in BookmarkModel in such a way that path[i] is - // the subgroup of path[i-1]. Finally they construct a path in the - // model: - // path[0] \ path[1] \ ... \ path[size() - 1] - BookmarkNode* parent = - (it->in_toolbar ? model->GetBookmarkBarNode() : model->other_node()); - for (std::vector::const_iterator i = it->path.begin(); - i != it->path.end(); ++i) { - BookmarkNode* child = NULL; - for (int index = 0; index < parent->GetChildCount(); ++index) { - BookmarkNode* node = parent->GetChild(index); - if ((node->GetType() == history::StarredEntry::BOOKMARK_BAR || - node->GetType() == history::StarredEntry::USER_GROUP) && - node->GetTitle() == *i) { - child = node; - break; - } - } - if (child == NULL) - child = model->AddGroup(parent, parent->GetChildCount(), *i); - parent = child; - } - groups_added_to.insert(parent); - model->AddURLWithCreationTime(parent, parent->GetChildCount(), - it->title, it->url, it->creation_time); - - // If some items are put into toolbar, it looks like the user was using - // it in their last browser. We turn on the bookmarks toolbar. - if (it->in_toolbar) - show_bookmark_toolbar = true; - } - - // Reset the date modified time of the groups we added to. We do this to - // make sure the 'recently added to' combobox in the bubble doesn't get random - // groups. - for (std::set::const_iterator i = groups_added_to.begin(); - i != groups_added_to.end(); ++i) { - model->ResetDateGroupModified(*i); - } - - if (show_bookmark_toolbar) - ShowBookmarkBar(); -} - -void ProfileWriter::AddFavicons( - const std::vector& favicons) { - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> - SetImportedFavicons(favicons); -} - -typedef std::map HostPathMap; - -// 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, it should not be added to HostPathMap. -static std::string BuildHostPathKey(const TemplateURL* t_url) { - if (t_url->url() && t_url->url()->SupportsReplacement()) { - GURL search_url(t_url->url()->ReplaceSearchTerms( - *t_url, L"random string", TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, - std::wstring())); - if (search_url.is_valid()) - return search_url.host() + search_url.path(); - } - return std::string(); -} - -// Builds a set that contains an entry of the host+path for each TemplateURL in -// the TemplateURLModel that has a valid search url. -static void BuildHostPathMap(const TemplateURLModel& model, - HostPathMap* host_path_map) { - std::vector template_urls = model.GetTemplateURLs(); - for (size_t i = 0; i < template_urls.size(); ++i) { - const std::string host_path = BuildHostPathKey(template_urls[i]); - 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(const std::vector& template_urls, - int default_keyword_index, - bool unique_on_host_and_path) { - TemplateURLModel* model = profile_->GetTemplateURLModel(); - HostPathMap host_path_map; - if (unique_on_host_and_path) - BuildHostPathMap(*model, &host_path_map); - - for (std::vector::const_iterator i = template_urls.begin(); - i != template_urls.end(); ++i) { - TemplateURL* t_url = *i; - bool default_keyword = - default_keyword_index >= 0 && - (i - template_urls.begin() == default_keyword_index); - - // TemplateURLModel requires keywords to be unique. If there is already a - // TemplateURL with this keyword, don't import it again. - const TemplateURL* turl_with_keyword = - model->GetTemplateURLForKeyword(t_url->keyword()); - if (turl_with_keyword != NULL) { - if (default_keyword) - model->SetDefaultSearchProvider(turl_with_keyword); - delete t_url; - 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(t_url)) != host_path_map.end()) { - if (default_keyword) { - const TemplateURL* turl_with_host_path = - host_path_map[BuildHostPathKey(t_url)]; - if (turl_with_host_path) - model->SetDefaultSearchProvider(turl_with_host_path); - else - NOTREACHED(); // BuildHostPathMap should only insert non-null values. - } - delete t_url; - continue; - } - model->Add(t_url); - if (default_keyword) - model->SetDefaultSearchProvider(t_url); - } -} - -void ProfileWriter::ShowBookmarkBar() { - DCHECK(profile_); - - PrefService* prefs = profile_->GetPrefs(); - // Check whether the bookmark bar is shown in current pref. - if (!prefs->GetBoolean(prefs::kShowBookmarkBar)) { - // Set the pref and notify the notification service. - prefs->SetBoolean(prefs::kShowBookmarkBar, true); - prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread()); - Source source(profile_); - NotificationService::current()->Notify( - NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, source, - NotificationService::NoDetails()); - } -} - -// Importer. - -// static -bool Importer::ReencodeFavicon(const unsigned char* src_data, size_t src_len, - std::vector* png_data) { - // Decode the favicon using WebKit's image decoder. - webkit_glue::ImageDecoder decoder(gfx::Size(kFavIconSize, kFavIconSize)); - SkBitmap decoded = decoder.Decode(src_data, src_len); - if (decoded.empty()) - return false; // Unable to decode. - - if (decoded.width() != kFavIconSize || decoded.height() != kFavIconSize) { - // The bitmap is not the correct size, re-sample. - int new_width = decoded.width(); - int new_height = decoded.height(); - calc_favicon_target_size(&new_width, &new_height); - decoded = gfx::ImageOperations::Resize( - decoded, gfx::ImageOperations::RESIZE_LANCZOS3, - gfx::Size(new_width, new_height)); - } - - // Encode our bitmap as a PNG. - SkAutoLockPixels decoded_lock(decoded); - PNGEncoder::Encode(reinterpret_cast(decoded.getPixels()), - PNGEncoder::FORMAT_BGRA, decoded.width(), - decoded.height(), decoded.width() * 4, false, png_data); - return true; -} - -// ImporterHost. - -ImporterHost::ImporterHost() - : observer_(NULL), - task_(NULL), - importer_(NULL), - file_loop_(g_browser_process->file_thread()->message_loop()), - waiting_for_bookmarkbar_model_(false), - waiting_for_template_url_model_(false), - is_source_readable_(true) { - DetectSourceProfiles(); -} - -ImporterHost::ImporterHost(MessageLoop* file_loop) - : observer_(NULL), - task_(NULL), - importer_(NULL), - file_loop_(file_loop), - waiting_for_bookmarkbar_model_(false), - waiting_for_template_url_model_(false), - is_source_readable_(true) { - DetectSourceProfiles(); -} - -ImporterHost::~ImporterHost() { - STLDeleteContainerPointers(source_profiles_.begin(), source_profiles_.end()); -} - -void ImporterHost::Loaded(BookmarkModel* model) { - model->RemoveObserver(this); - waiting_for_bookmarkbar_model_ = false; - InvokeTaskIfDone(); -} - -void ImporterHost::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type == TEMPLATE_URL_MODEL_LOADED); - TemplateURLModel* model = Source(source).ptr(); - NotificationService::current()->RemoveObserver( - this, TEMPLATE_URL_MODEL_LOADED, - Source(model)); - waiting_for_template_url_model_ = false; - InvokeTaskIfDone(); -} - -void ImporterHost::ShowWarningDialog() { - ChromeViews::Window::CreateChromeWindow(GetActiveWindow(), gfx::Rect(), - new ImporterLockView(this))->Show(); -} - -void ImporterHost::OnLockViewEnd(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; - InvokeTaskIfDone(); - } else { - ShowWarningDialog(); - } - } else { - // User chose to skip the import process. We should delete - // the task and notify the ImporterHost to finish. - delete task_; - task_ = NULL; - importer_ = NULL; - ImportEnded(); - } -} - -void ImporterHost::StartImportSettings(const ProfileInfo& profile_info, - uint16 items, - ProfileWriter* writer, - bool first_run) { - // Preserves the observer and creates a task, since we do async import - // so that it doesn't block the UI. When the import is complete, observer - // will be notified. - writer_ = writer; - importer_ = CreateImporterByType(profile_info.browser_type); - importer_->set_first_run(first_run); - task_ = NewRunnableMethod(importer_, &Importer::StartImport, - profile_info, items, writer_.get(), this); - - // We should lock the Firefox profile directory to prevent corruption. - if (profile_info.browser_type == FIREFOX2 || - profile_info.browser_type == FIREFOX3) { - firefox_lock_.reset(new FirefoxProfileLock(profile_info.source_path)); - if (!firefox_lock_->HasAcquired()) { - // If fail to acquire the lock, we set the source unreadable and - // show a warning dialog. - is_source_readable_ = false; - ShowWarningDialog(); - } - } - - // 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 & FAVORITES) && !writer_->BookmarkModelIsLoaded()) { - writer_->AddBookmarkModelObserver(this); - waiting_for_bookmarkbar_model_ = true; - } - - // Observes the TemplateURLModel 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 & SEARCH_ENGINES) || (items & FAVORITES)) { - if (!writer_->TemplateURLModelIsLoaded()) { - writer_->AddTemplateURLModelObserver(this); - waiting_for_template_url_model_ = true; - } - } - - AddRef(); - InvokeTaskIfDone(); -} - -void ImporterHost::Cancel() { - if (importer_) - importer_->Cancel(); -} - -void ImporterHost::SetObserver(Observer* observer) { - observer_ = observer; -} - -void ImporterHost::InvokeTaskIfDone() { - if (waiting_for_bookmarkbar_model_ || waiting_for_template_url_model_ || - !is_source_readable_) - return; - file_loop_->PostTask(FROM_HERE, task_); -} - -void ImporterHost::ImportItemStarted(ImportItem item) { - if (observer_) - observer_->ImportItemStarted(item); -} - -void ImporterHost::ImportItemEnded(ImportItem item) { - if (observer_) - observer_->ImportItemEnded(item); -} - -void ImporterHost::ImportStarted() { - if (observer_) - observer_->ImportStarted(); -} - -void ImporterHost::ImportEnded() { - firefox_lock_.reset(); // Release the Firefox profile lock. - if (observer_) - observer_->ImportEnded(); - Release(); -} - -Importer* ImporterHost::CreateImporterByType(ProfileType type) { - switch (type) { - case MS_IE: - return new IEImporter(); - case FIREFOX2: - return new Firefox2Importer(); - case FIREFOX3: - return new Firefox3Importer(); - } - NOTREACHED(); - return NULL; -} - -int ImporterHost::GetAvailableProfileCount() { - return static_cast(source_profiles_.size()); -} - -std::wstring ImporterHost::GetSourceProfileNameAt(int index) const { - DCHECK(index < static_cast(source_profiles_.size())); - return source_profiles_[index]->description; -} - -const ProfileInfo& ImporterHost::GetSourceProfileInfoAt(int index) const { - DCHECK(index < static_cast(source_profiles_.size())); - return *source_profiles_[index]; -} - -void ImporterHost::DetectSourceProfiles() { - if (ShellIntegration::IsFirefoxDefaultBrowser()) { - DetectFirefoxProfiles(); - DetectIEProfiles(); - } else { - DetectIEProfiles(); - DetectFirefoxProfiles(); - } -} - -void ImporterHost::DetectIEProfiles() { - // IE always exists and don't have multiple profiles. - ProfileInfo* ie = new ProfileInfo(); - ie->description = l10n_util::GetString(IDS_IMPORT_FROM_IE); - ie->browser_type = MS_IE; - ie->source_path.clear(); - ie->app_path.clear(); - source_profiles_.push_back(ie); -} - -void ImporterHost::DetectFirefoxProfiles() { - // Detects which version of Firefox is installed. - int version = GetCurrentFirefoxMajorVersion(); - ProfileType firefox_type; - if (version == 2) { - firefox_type = FIREFOX2; - } else if (version == 3) { - firefox_type = FIREFOX3; - } else { - // Ignores other versions of firefox. - return; - } - - std::wstring ini_file = GetProfilesINI(); - DictionaryValue root; - ParseProfileINI(ini_file, &root); - - std::wstring source_path; - for (int i = 0; ; ++i) { - std::wstring current_profile = L"Profile" + IntToWString(i); - if (!root.HasKey(current_profile)) { - // Profiles are continuously numbered. So we exit when we can't - // find the i-th one. - break; - } - std::wstring is_relative, path, profile_path; - if (root.GetString(current_profile + L".IsRelative", &is_relative) && - root.GetString(current_profile + L".Path", &path)) { - ReplaceSubstringsAfterOffset(&path, 0, L"/", L"\\"); - - // 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 == L"1") { - profile_path = file_util::GetDirectoryFromPath(ini_file); - file_util::AppendToPath(&profile_path, path); - } else { - profile_path = path; - } - - // We only import the default profile when multiple profiles exist, - // since the other profiles are used mostly by developers for testing. - // Otherwise, Profile0 will be imported. - std::wstring is_default; - if ((root.GetString(current_profile + L".Default", &is_default) && - is_default == L"1") || i == 0) { - source_path = profile_path; - // We break out of the loop when we have found the default profile. - if (is_default == L"1") - break; - } - } - } - - if (!source_path.empty()) { - ProfileInfo* firefox = new ProfileInfo(); - firefox->description = l10n_util::GetString(IDS_IMPORT_FROM_FIREFOX); - firefox->browser_type = firefox_type; - firefox->source_path = source_path; - firefox->app_path = GetFirefoxInstallPath(); - source_profiles_.push_back(firefox); - } -} - diff --git a/chrome/browser/importer.h b/chrome/browser/importer.h deleted file mode 100644 index 65ba842..0000000 --- a/chrome/browser/importer.h +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef CHROME_BROWSER_IMPORTER_H_ -#define CHROME_BROWSER_IMPORTER_H_ - -#include -#include - -#include "base/basictypes.h" -#include "base/message_loop.h" -#include "base/ref_counted.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/history/history_types.h" -#include "chrome/browser/ie7_password.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/template_url.h" -#include "chrome/browser/views/importer_lock_view.h" -#include "chrome/common/notification_service.h" -#include "googleurl/src/gurl.h" -#include "webkit/glue/password_form.h" - -// An enumeration of the type of browsers that we support to import -// settings and data from them. -enum ProfileType { - MS_IE = 0, - FIREFOX2, - FIREFOX3 -}; - -// An enumeration of the type of data we want to import. -enum ImportItem { - NONE = 0x0000, - HISTORY = 0x0001, - FAVORITES = 0x0002, - COOKIES = 0x0004, // not supported yet. - PASSWORDS = 0x0008, - SEARCH_ENGINES = 0x0010, - HOME_PAGE = 0x0020, -}; - -typedef struct { - std::wstring description; - ProfileType browser_type; - std::wstring source_path; - std::wstring app_path; -} ProfileInfo; - -class FirefoxProfileLock; -class Importer; - -// ProfileWriter encapsulates profile for writing entries into it. -// This object must be invoked on UI thread. -class ProfileWriter : public base::RefCounted { - public: - explicit ProfileWriter(Profile* profile) : profile_(profile) { } - virtual ~ProfileWriter() { } - - // Methods for monitoring BookmarkModel status. - virtual bool BookmarkModelIsLoaded() const; - virtual void AddBookmarkModelObserver( - BookmarkModelObserver* observer); - - // Methods for monitoring TemplateURLModel status. - virtual bool TemplateURLModelIsLoaded() const; - virtual void AddTemplateURLModelObserver( - NotificationObserver* observer); - - // A bookmark entry. - struct BookmarkEntry { - bool in_toolbar; - GURL url; - std::vector path; - std::wstring title; - Time creation_time; - - BookmarkEntry() : in_toolbar(false) {} - }; - - // Helper methods for adding data to local stores. - virtual void AddPasswordForm(const PasswordForm& form); - virtual void AddIE7PasswordInfo(const IE7PasswordInfo& info); - virtual void AddHistoryPage(const std::vector& page); - virtual void AddHomepage(const GURL& homepage); - virtual void AddBookmarkEntry(const std::vector& bookmark); - virtual void AddFavicons( - const std::vector& favicons); - // Add the TemplateURLs in |template_urls| to the local store and make the - // TemplateURL at |default_keyword_index| the default keyword (does not set - // a default keyword if it is -1). 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 takes precedence and the - // duplicate in |template_urls| are deleted. - // If unique_on_host_and_path 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(const std::vector& template_urls, - int default_keyword_index, - bool unique_on_host_and_path); - - // Shows the bookmarks toolbar. - void ShowBookmarkBar(); - - private: - Profile* profile_; - - DISALLOW_EVIL_CONSTRUCTORS(ProfileWriter); -}; - -// This class hosts the importers. It enumerates profiles from other -// browsers dynamically, and controls the process of importing. When -// the import process is done, ImporterHost deletes itself. -class ImporterHost : public base::RefCounted, - public BookmarkModelObserver, - public NotificationObserver { - public: - ImporterHost(); - ~ImporterHost(); - - // This constructor only be used by unit-tests, where file thread does not - // exist. - explicit ImporterHost(MessageLoop* file_loop); - - // BookmarkModelObserver methods. - virtual void Loaded(BookmarkModel* model); - virtual void BookmarkNodeMoved(BookmarkModel* model, - BookmarkNode* old_parent, - int old_index, - BookmarkNode* new_parent, - int new_index) {} - virtual void BookmarkNodeAdded(BookmarkModel* model, - BookmarkNode* parent, - int index) {} - virtual void BookmarkNodeRemoved(BookmarkModel* model, - BookmarkNode* parent, - int index) {} - virtual void BookmarkNodeChanged(BookmarkModel* model, - BookmarkNode* node) {} - virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, - BookmarkNode* node) {} - - // NotificationObserver method. Called when TemplateURLModel has been loaded. - void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - // ShowWarningDialog() asks user to close the application that is owning the - // lock. They can retry or skip the importing process. - void ShowWarningDialog(); - - // OnLockViewEnd() is called when user end the dialog by clicking a push - // button. |is_continue| is true when user clicked the "Continue" button. - void OnLockViewEnd(bool is_continue); - - // Starts the process of importing the settings and data depending - // on what the user selected. - void StartImportSettings(const ProfileInfo& profile_info, - uint16 items, - ProfileWriter* writer, - bool first_run); - - // Cancel - void Cancel(); - - // An interface which an object can implement to be notified of events during - // the import process. - class Observer { - public: - virtual ~Observer() {} - // 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 import begins. - virtual void ImportStarted() = 0; - - // Invoked when the source profile has been imported. - virtual void ImportEnded() = 0; - }; - void SetObserver(Observer* observer); - - // A series of functions invoked at the start, during and end of the end - // of the import process. The middle functions are notifications that the - // harvesting of a particular source of data (specified by |item|) is under - // way. - void ImportStarted(); - void ImportItemStarted(ImportItem item); - void ImportItemEnded(ImportItem item); - void ImportEnded(); - - Importer* CreateImporterByType(ProfileType type); - - // Returns the number of different browser profiles you can import from. - int GetAvailableProfileCount(); - - // Returns the name of the profile at the 'index' slot. The profiles are - // ordered such that the profile at index 0 is the likely default browser. - std::wstring GetSourceProfileNameAt(int index) const; - - // Returns the ProfileInfo at the specified index. The ProfileInfo should be - // passed to StartImportSettings(). - const ProfileInfo& GetSourceProfileInfoAt(int index) const; - - private: - // If we're not waiting on any model to finish loading, invokes the task_. - void InvokeTaskIfDone(); - - // Detects the installed browsers and their associated profiles, then - // stores their information in a list. It returns the list of description - // of all profiles. - void DetectSourceProfiles(); - - // Helper methods for detecting available profiles. - void DetectIEProfiles(); - void DetectFirefoxProfiles(); - - // The list of profiles with the default one first. - std::vector source_profiles_; - - Observer* observer_; - scoped_refptr writer_; - - // The task is the process of importing settings from other browsers. - Task* task_; - - // The importer used in the task; - Importer* importer_; - - // The message loop for reading the source profiles. - MessageLoop* file_loop_; - - // True if we're waiting for the model to finish loading. - bool waiting_for_bookmarkbar_model_; - bool waiting_for_template_url_model_; - - // True if source profile is readable. - bool is_source_readable_; - - // Firefox profile lock. - scoped_ptr firefox_lock_; - - DISALLOW_EVIL_CONSTRUCTORS(ImporterHost); -}; - -// The base class of all importers. -class Importer : public base::RefCounted { - public: - virtual ~Importer() { } - - // 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::Finished() to notify its host that import - // stuff have been finished. - virtual void StartImport(ProfileInfo profile_info, - uint16 items, - ProfileWriter* writer, - ImporterHost* host) = 0; - - // Cancels the import process. - void Cancel() { cancelled_ = true; } - - void set_first_run(bool first_run) { first_run_ = first_run; } - - protected: - Importer() - : main_loop_(MessageLoop::current()), - importer_host_(NULL), - cancelled_(false) {} - - // Notifies the coordinator that the collection of data for the specified - // item has begun. - void NotifyItemStarted(ImportItem item) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(importer_host_, - &ImporterHost::ImportItemStarted, item)); - } - - // Notifies the coordinator that the collection of data for the specified - // item has completed. - void NotifyItemEnded(ImportItem item) { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(importer_host_, - &ImporterHost::ImportItemEnded, item)); - } - - // Notifies the coordinator that the import operation has begun. - void NotifyStarted() { - main_loop_->PostTask(FROM_HERE, NewRunnableMethod(importer_host_, - &ImporterHost::ImportStarted)); - } - - // Notifies the coordinator that the entire import operation has completed. - void NotifyEnded() { - main_loop_->PostTask(FROM_HERE, - NewRunnableMethod(importer_host_, &ImporterHost::ImportEnded)); - } - - // 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. - static bool ReencodeFavicon(const unsigned char* src_data, size_t src_len, - std::vector* png_data); - - bool cancelled() const { return cancelled_; } - - bool first_run() const { return first_run_; } - - // The importer should know the main thread so that ProfileWriter - // will be invoked in thread instead. - MessageLoop* main_loop_; - - // The coordinator host for this importer. - ImporterHost* importer_host_; - - private: - // True if the caller cancels the import process. - bool cancelled_; - - // True if the importer is created in the first run UI. - bool first_run_; - - DISALLOW_EVIL_CONSTRUCTORS(Importer); -}; - -// An interface an object that calls StartImportingWithUI can call to be -// notified about the state of the import operation. -class ImportObserver { - public: - virtual ~ImportObserver() {} - // The import operation was canceled by the user. - virtual void ImportCanceled() = 0; - - // The import operation was completed successfully. - virtual void ImportComplete() = 0; -}; - - -// Shows a UI for importing and begins importing the specified items from -// source_profile to target_profile. observer is notified when the process is -// complete, can be NULL. parent is the window to parent the UI to, can be NULL -// if there's nothing to parent to. first_run is true if it's invoked in the -// first run UI. -void StartImportingWithUI(HWND parent_window, - int16 items, - ImporterHost* coordinator, - const ProfileInfo& source_profile, - Profile* target_profile, - ImportObserver* observer, - bool first_run); - -#endif // CHROME_BROWSER_IMPORTER_H__ - diff --git a/chrome/browser/importer/firefox2_importer.cc b/chrome/browser/importer/firefox2_importer.cc new file mode 100644 index 0000000..59f1778 --- /dev/null +++ b/chrome/browser/importer/firefox2_importer.cc @@ -0,0 +1,515 @@ +// Copyright (c) 2006-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/firefox2_importer.h" + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/browser/importer/firefox_importer_utils.h" +#include "chrome/browser/importer/mork_reader.h" +#include "chrome/browser/template_url.h" +#include "chrome/browser/template_url_parser.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/time_format.h" +#include "generated_resources.h" +#include "net/base/data_url.h" + +// Firefox2Importer. + +Firefox2Importer::Firefox2Importer() { +} + +Firefox2Importer::~Firefox2Importer() { +} + +void Firefox2Importer::StartImport(ProfileInfo profile_info, + uint16 items, ProfileWriter* writer, + ImporterHost* host) { + writer_ = writer; + source_path_ = profile_info.source_path; + app_path_ = profile_info.app_path; + importer_host_ = host; + + // The order here is important! + NotifyStarted(); + if ((items & HOME_PAGE) && !cancelled()) + ImportHomepage(); // Doesn't have a UI item. + if ((items & FAVORITES) && !cancelled()) { + NotifyItemStarted(FAVORITES); + ImportBookmarks(); + NotifyItemEnded(FAVORITES); + } + if ((items & SEARCH_ENGINES) && !cancelled()) { + NotifyItemStarted(SEARCH_ENGINES); + ImportSearchEngines(); + NotifyItemEnded(SEARCH_ENGINES); + } + if ((items & PASSWORDS) && !cancelled()) { + NotifyItemStarted(PASSWORDS); + ImportPasswords(); + NotifyItemEnded(PASSWORDS); + } + if ((items & HISTORY) && !cancelled()) { + NotifyItemStarted(HISTORY); + ImportHistory(); + NotifyItemEnded(HISTORY); + } + NotifyEnded(); +} + +// static +void Firefox2Importer::LoadDefaultBookmarks(const std::wstring& app_path, + std::set *urls) { + // Firefox keeps its default bookmarks in a bookmarks.html file that + // lives at: \defaults\profile\bookmarks.html + std::wstring file = app_path; + file_util::AppendToPath(&file, L"defaults\\profile\\bookmarks.html"); + + urls->clear(); + + // Read the whole file. + std::string content; + file_util::ReadFileToString(file, &content); + std::vector lines; + SplitString(content, '\n', &lines); + + std::string charset; + for (size_t i = 0; i < lines.size(); ++i) { + std::string line; + TrimString(lines[i], " ", &line); + + // Get the encoding of the bookmark file. + if (ParseCharsetFromLine(line, &charset)) + continue; + + // Get the bookmark. + std::wstring title; + GURL url, favicon; + std::wstring shortcut; + Time add_date; + std::wstring post_data; + if (ParseBookmarkFromLine(line, charset, &title, &url, + &favicon, &shortcut, &add_date, + &post_data)) + urls->insert(url); + } +} + +// static +TemplateURL* Firefox2Importer::CreateTemplateURL(const std::wstring& title, + const std::wstring& keyword, + const GURL& url) { + // Skip if the keyword or url is invalid. + if (keyword.empty() && url.is_valid()) + return NULL; + + TemplateURL* t_url = new TemplateURL(); + // We set short name by using the title if it exists. + // Otherwise, we use the shortcut. + t_url->set_short_name(!title.empty() ? title : keyword); + t_url->set_keyword(keyword); + t_url->SetURL(TemplateURLRef::DisplayURLToURLRef(UTF8ToWide(url.spec())), + 0, 0); + return t_url; +} + +void Firefox2Importer::ImportBookmarks() { + // Read the whole file. + std::wstring file = source_path_; + file_util::AppendToPath(&file, L"bookmarks.html"); + std::string content; + file_util::ReadFileToString(file, &content); + std::vector lines; + SplitString(content, '\n', &lines); + + // Load the default bookmarks. + std::set default_urls; + LoadDefaultBookmarks(app_path_, &default_urls); + + // Parse the bookmarks.html file. + std::vector bookmarks, toolbar_bookmarks; + std::vector template_urls; + std::vector favicons; + std::wstring last_folder + = l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_FIREFOX); + bool last_folder_on_toolbar = false; + std::vector path; + size_t toolbar_folder = 0; + std::string charset; + for (size_t i = 0; i < lines.size() && !cancelled(); ++i) { + std::string line; + TrimString(lines[i], " ", &line); + + // Get the encoding of the bookmark file. + if (ParseCharsetFromLine(line, &charset)) + continue; + + // Get the folder name. + if (ParseFolderNameFromLine(line, charset, &last_folder, + &last_folder_on_toolbar)) + continue; + + // Get the bookmark entry. + std::wstring title, shortcut; + GURL url, favicon; + Time add_date; + std::wstring post_data; + // TODO(jcampan): http://b/issue?id=1196285 we do not support POST based + // keywords yet. + if (ParseBookmarkFromLine(line, charset, &title, + &url, &favicon, &shortcut, &add_date, + &post_data) && + post_data.empty() && + CanImportURL(GURL(url)) && + default_urls.find(url) == default_urls.end()) { + if (toolbar_folder > path.size() && path.size() > 0) { + NOTREACHED(); // error in parsing. + break; + } + + ProfileWriter::BookmarkEntry entry; + entry.creation_time = add_date; + entry.url = url; + entry.title = title; + + if (first_run() && toolbar_folder) { + // Flatten the items in toolbar. + entry.in_toolbar = true; + entry.path.assign(path.begin() + toolbar_folder, path.end()); + toolbar_bookmarks.push_back(entry); + } else { + // Insert the item into the "Imported from Firefox" folder after + // the first run. + entry.path.assign(path.begin(), path.end()); + if (first_run()) + entry.path.erase(entry.path.begin()); + bookmarks.push_back(entry); + } + + // Save the favicon. DataURLToFaviconUsage will handle the case where + // there is no favicon. + DataURLToFaviconUsage(url, favicon, &favicons); + + // If there is a SHORTCUT attribute for this bookmark, we + // add it as our keywords. + TemplateURL* t_url = CreateTemplateURL(title, shortcut, url); + if (t_url) + template_urls.push_back(t_url); + + continue; + } + + // Bookmarks in sub-folder are encapsulated with
tag. + if (StartsWithASCII(line, "
", true)) { + path.push_back(last_folder); + last_folder.clear(); + if (last_folder_on_toolbar && !toolbar_folder) + toolbar_folder = path.size(); + } else if (StartsWithASCII(line, "
", true)) { + if (path.empty()) + break; // Mismatch
. + path.pop_back(); + if (toolbar_folder > path.size()) + toolbar_folder = 0; + } + } + + // Write data into profile. + bookmarks.insert(bookmarks.begin(), toolbar_bookmarks.begin(), + toolbar_bookmarks.end()); + if (!bookmarks.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddBookmarkEntry, bookmarks)); + } + if (!template_urls.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddKeywords, template_urls, -1, false)); + } else { + STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); + } + if (!favicons.empty()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddFavicons, favicons)); + } +} + +void Firefox2Importer::ImportPasswords() { + // Initializes NSS3. + NSSDecryptor decryptor; + if (!decryptor.Init(source_path_, source_path_) && + !decryptor.Init(app_path_, source_path_)) + return; + + // Firefox 2 uses signons2.txt to store the pssswords. If it doesn't + // exist, we try to find its older version. + std::wstring file = source_path_; + file_util::AppendToPath(&file, L"signons2.txt"); + if (!file_util::PathExists(file)) { + file = source_path_; + file_util::AppendToPath(&file, L"signons.txt"); + } + + std::string content; + file_util::ReadFileToString(file, &content); + std::vector forms; + decryptor.ParseSignons(content, &forms); + + if (!cancelled()) { + for (size_t i = 0; i < forms.size(); ++i) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddPasswordForm, forms[i])); + } + } +} + +void Firefox2Importer::ImportHistory() { + std::wstring file = source_path_; + file_util::AppendToPath(&file, L"history.dat"); + ImportHistoryFromFirefox2(file, main_loop_, writer_); +} + +void Firefox2Importer::ImportSearchEngines() { + std::vector files; + GetSearchEnginesXMLFiles(&files); + + std::vector search_engines; + ParseSearchEnginesFromXMLFiles(files, &search_engines); + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddKeywords, search_engines, + GetFirefoxDefaultSearchEngineIndex(search_engines, source_path_), + true)); +} + +void Firefox2Importer::ImportHomepage() { + GURL homepage = GetHomepage(source_path_); + if (homepage.is_valid() && !IsDefaultHomepage(homepage, app_path_)) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddHomepage, homepage)); + } +} + +void Firefox2Importer::GetSearchEnginesXMLFiles( + std::vector* files) { + // Search engines are contained in XML files in a searchplugins directory that + // can be found in 2 locations: + // - Firefox install dir (default search engines) + // - the profile dir (user added search engines) + std::wstring dir(app_path_); + file_util::AppendToPath(&dir, L"searchplugins"); + FindXMLFilesInDir(dir, files); + + std::wstring profile_dir = source_path_; + file_util::AppendToPath(&profile_dir, L"searchplugins"); + FindXMLFilesInDir(profile_dir, files); +} + +// static +bool Firefox2Importer::ParseCharsetFromLine(const std::string& line, + std::string* charset) { + const char kCharset[] = "charset="; + if (StartsWithASCII(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; + + CodepageToWide(line.substr(tag_end, end - tag_end), charset.c_str(), + OnStringUtilConversionError::SKIP, folder_name); + HTMLUnescape(folder_name); + + std::string attribute_list = line.substr(arraysize(kFolderOpen), + tag_end - arraysize(kFolderOpen) - 1); + std::string value; + if (GetAttribute(attribute_list, kToolbarFolderAttribute, &value) && + LowerCaseEqualsASCII(value, "true")) + *is_toolbar_folder = true; + else + *is_toolbar_folder = false; + + return true; +} + +// static +bool Firefox2Importer::ParseBookmarkFromLine(const std::string& line, + const std::string& charset, + std::wstring* title, + GURL* url, + GURL* favicon, + std::wstring* shortcut, + Time* add_date, + std::wstring* post_data) { + const char kItemOpen[] = "
clear(); + *url = GURL(); + *favicon = GURL(); + shortcut->clear(); + post_data->clear(); + *add_date = Time(); + + if (!StartsWithASCII(line, kItemOpen, true)) + 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 + CodepageToWide(line.substr(tag_end, end - tag_end), charset.c_str(), + OnStringUtilConversionError::SKIP, title); + HTMLUnescape(title); + + // URL + if (GetAttribute(attribute_list, kHrefAttribute, &value)) { + ReplaceSubstringsAfterOffset(&value, 0, "%22", "\""); + *url = GURL(value); + } + + // Favicon + if (GetAttribute(attribute_list, kIconAttribute, &value)) + *favicon = GURL(value); + + // Keyword + if (GetAttribute(attribute_list, kShortcutURLAttribute, &value)) { + CodepageToWide(value, charset.c_str(), OnStringUtilConversionError::SKIP, + shortcut); + HTMLUnescape(shortcut); + } + + // Add date + if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { + int64 time = StringToInt64(value); + // Upper bound it at 32 bits. + if (0 < time && time < (1LL << 32)) + *add_date = Time::FromTimeT(time); + } + + // Post data. + if (GetAttribute(attribute_list, kPostDataAttribute, &value)) { + CodepageToWide(value, charset.c_str(), + OnStringUtilConversionError::SKIP, post_data); + HTMLUnescape(post_data); + } + + return true; +} + +// static +bool Firefox2Importer::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_list.find(kQuote, begin) + 1; + size_t end = attribute_list.find(kQuote, begin); + if (end == std::string::npos) + return false; // The value is not quoted. + + *value = attribute_list.substr(begin, end - begin); + return true; +} + +// static +void Firefox2Importer::HTMLUnescape(std::wstring *text) { + ReplaceSubstringsAfterOffset(text, 0, L"<", L"<"); + ReplaceSubstringsAfterOffset(text, 0, L">", L">"); + ReplaceSubstringsAfterOffset(text, 0, L"&", L"&"); + ReplaceSubstringsAfterOffset(text, 0, L""", L"\""); + ReplaceSubstringsAfterOffset(text, 0, L"'", L"\'"); +} + +// static +void Firefox2Importer::FindXMLFilesInDir( + const std::wstring& dir, + std::vector* xml_files) { + file_util::FileEnumerator file_enum(dir, false, + file_util::FileEnumerator::FILES, + L"*.xml"); + std::wstring file(file_enum.Next()); + while (!file.empty()) { + xml_files->push_back(file); + file = file_enum.Next(); + } +} + +// static +void Firefox2Importer::DataURLToFaviconUsage( + const GURL& link_url, + const GURL& favicon_data, + std::vector* favicons) { + if (!link_url.is_valid() || !favicon_data.is_valid() || + !favicon_data.SchemeIs("data")) + 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; + + history::ImportedFavIconUsage usage; + if (!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); +} + diff --git a/chrome/browser/importer/firefox2_importer.h b/chrome/browser/importer/firefox2_importer.h new file mode 100644 index 0000000..2c44993 --- /dev/null +++ b/chrome/browser/importer/firefox2_importer.h @@ -0,0 +1,112 @@ +// Copyright (c) 2006-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. + +#ifndef CHROME_BROWSER_IMPORTER_FIREFOX2_IMPORTER_H_ +#define CHROME_BROWSER_IMPORTER_FIREFOX2_IMPORTER_H_ + +#include "chrome/browser/importer/importer.h" + +class TemplateURL; + +// Importer for Mozilla Firefox 2. +class Firefox2Importer : public Importer { + public: + Firefox2Importer(); + virtual ~Firefox2Importer(); + + // Importer methods. + virtual void StartImport(ProfileInfo profile_info, + uint16 items, + ProfileWriter* writer, + ImporterHost* host); + + // Loads the default bookmarks in the Firefox installed at |firefox_app_path|, + // and stores their locations in |urls|. + static void LoadDefaultBookmarks(const std::wstring& firefox_app_path, + std::set *urls); + + // Creates a TemplateURL with the |keyword| and |url|. |title| may be empty. + // This function transfers ownership of the created TemplateURL to the caller. + static TemplateURL* CreateTemplateURL(const std::wstring& title, + const std::wstring& keyword, + const GURL& url); + + private: + FRIEND_TEST(FirefoxImporterTest, Firefox2BookmarkParse); + FRIEND_TEST(FirefoxImporterTest, Firefox2CookesParse); + + 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(); + + // Fills |files| with the paths to the files containing the search engine + // descriptions. + void GetSearchEnginesXMLFiles(std::vector* files); + + // Helper methods for parsing bookmark file. + // Firefox 2 saves its bookmarks in a html file. We are interested in the + // bookmarks and folders, and their hierarchy. A folder starts with a + // heading tag, which contains it title. All bookmarks and sub-folders is + // following, and bracketed by a
tag: + //

title

+ //

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

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

name + // Reference: http://kb.mozillazine.org/Bookmarks.html + static bool ParseCharsetFromLine(const std::string& line, + std::string* charset); + static bool ParseFolderNameFromLine(const std::string& line, + const std::string& charset, + std::wstring* folder_name, + bool* is_toolbar_folder); + // 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). + static bool ParseBookmarkFromLine(const std::string& line, + const std::string& charset, + std::wstring* title, + GURL* url, + GURL* favicon, + std::wstring* shortcut, + Time* add_date, + std::wstring* post_data); + + // Fetches the given attribute value from the |tag|. Returns true if + // successful, and |value| will contain the value. + static bool GetAttribute(const std::string& tag, + const std::string& attribute, + std::string* value); + + // There are some characters in html file will be escaped: + // '<', '>', '"', '\', '&' + // Un-escapes them if the bookmark name has those characters. + static void HTMLUnescape(std::wstring* text); + + // Fills |xml_files| with the file with an xml extension found under |dir|. + static void FindXMLFilesInDir(const std::wstring& dir, + std::vector* xml_files); + + // Given the URL of a page and a favicon data URL, adds an appropriate record + // to the given favicon usage vector. Will do nothing if the favicon is not + // valid. + static void DataURLToFaviconUsage( + const GURL& link_url, + const GURL& favicon_data, + std::vector* favicons); + + ProfileWriter* writer_; + std::wstring source_path_; + std::wstring app_path_; + + DISALLOW_EVIL_CONSTRUCTORS(Firefox2Importer); +}; + +#endif // CHROME_BROWSER_IMPORTER_FIREFOX2_IMPORTER_H_ + diff --git a/chrome/browser/importer/firefox3_importer.cc b/chrome/browser/importer/firefox3_importer.cc new file mode 100644 index 0000000..3279130 --- /dev/null +++ b/chrome/browser/importer/firefox3_importer.cc @@ -0,0 +1,502 @@ +// Copyright (c) 2006-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/firefox3_importer.h" + +#include + +#include "base/file_util.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/browser/importer/firefox2_importer.h" +#include "chrome/browser/importer/firefox_importer_utils.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/time_format.h" +#include "generated_resources.h" + +// Wraps the function sqlite3_close() in a class that is +// used in scoped_ptr_malloc. + +namespace { + +class DBClose { + public: + inline void operator()(sqlite3* x) const { + sqlite3_close(x); + } +}; + +} // namespace + +void Firefox3Importer::StartImport(ProfileInfo profile_info, + uint16 items, ProfileWriter* writer, + ImporterHost* host) { + writer_ = writer; + source_path_ = profile_info.source_path; + app_path_ = profile_info.app_path; + importer_host_ = host; + + + // The order here is important! + NotifyStarted(); + if ((items & HOME_PAGE) && !cancelled()) + ImportHomepage(); // Doesn't have a UI item. + if ((items & FAVORITES) && !cancelled()) { + NotifyItemStarted(FAVORITES); + ImportBookmarks(); + NotifyItemEnded(FAVORITES); + } + if ((items & SEARCH_ENGINES) && !cancelled()) { + NotifyItemStarted(SEARCH_ENGINES); + ImportSearchEngines(); + NotifyItemEnded(SEARCH_ENGINES); + } + if ((items & PASSWORDS) && !cancelled()) { + NotifyItemStarted(PASSWORDS); + ImportPasswords(); + NotifyItemEnded(PASSWORDS); + } + if ((items & HISTORY) && !cancelled()) { + NotifyItemStarted(HISTORY); + ImportHistory(); + NotifyItemEnded(HISTORY); + } + NotifyEnded(); +} + +void Firefox3Importer::ImportHistory() { + std::wstring file = source_path_; + file_util::AppendToPath(&file, L"places.sqlite"); + if (!file_util::PathExists(file)) + return; + + sqlite3* sqlite; + if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) + return; + scoped_ptr_malloc db(sqlite); + + SQLStatement s; + // |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* stmt = "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"; + + if (s.prepare(db.get(), stmt) != SQLITE_OK) + return; + + std::vector rows; + while (s.step() == SQLITE_ROW && !cancelled()) { + GURL url(s.column_string(0)); + + // Filter out unwanted URLs. + if (!CanImportURL(url)) + continue; + + history::URLRow row(url); + row.set_title(s.column_string16(1)); + row.set_visit_count(s.column_int(2)); + row.set_hidden(s.column_int(3) == 1); + row.set_typed_count(s.column_int(4)); + row.set_last_visit(Time::FromTimeT(s.column_int64(5)/1000000)); + + rows.push_back(row); + } + if (!rows.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddHistoryPage, rows)); + } +} + +void Firefox3Importer::ImportBookmarks() { + std::wstring file = source_path_; + file_util::AppendToPath(&file, L"places.sqlite"); + if (!file_util::PathExists(file)) + return; + + sqlite3* sqlite; + if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) + return; + scoped_ptr_malloc db(sqlite); + + // 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.get(), &toolbar_folder_id, &menu_folder_id, + &unsorted_folder_id); + + // Load livemark IDs. + std::set livemark_id; + LoadLivemarkIDs(db.get(), &livemark_id); + + // Load the default bookmarks. Its storage is the same as Firefox 2. + std::set default_urls; + Firefox2Importer::LoadDefaultBookmarks(app_path_, &default_urls); + + BookmarkList list; + GetTopBookmarkFolder(db.get(), toolbar_folder_id, &list); + GetTopBookmarkFolder(db.get(), menu_folder_id, &list); + GetTopBookmarkFolder(db.get(), unsorted_folder_id, &list); + size_t count = list.size(); + for (size_t i = 0; i < count; ++i) + GetWholeBookmarkFolder(db.get(), &list, i); + + std::vector bookmarks; + std::vector template_urls; + 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; + SQLStatement s; + const char* stmt = "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'"; + if (s.prepare(db.get(), stmt) == SQLITE_OK) { + while (s.step() == SQLITE_ROW && !cancelled()) + post_keyword_ids.insert(s.column_int(0)); + } else { + NOTREACHED(); + } + + std::wstring firefox_folder = + l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_FIREFOX); + for (size_t i = 0; i < list.size(); ++i) { + BookmarkItem* item = list[i]; + + // The type of bookmark items is 1. + if (item->type != 1) + continue; + + // Skip the default bookmarks and unwanted URLs. + if (!CanImportURL(item->url) || + 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 (parent->id == toolbar_folder_id) { + // This bookmark entry should be put in the bookmark bar. + // But we put it in the Firefox group after first run, so + // that do not mess up the bookmark bar. + if (first_run()) { + is_in_toolbar = true; + } else { + path.insert(path.begin(), parent->title); + path.insert(path.begin(), firefox_folder); + } + found_path = true; + break; + } else if (parent->id == menu_folder_id || + parent->id == unsorted_folder_id) { + // After the first run, the item will be placed in a folder in + // the "Other bookmarks". + if (!first_run()) + path.insert(path.begin(), firefox_folder); + found_path = true; + break; + } else if (livemark_id.find(parent->id) != livemark_id.end()) { + // If the entry is under a livemark folder, we don't import it. + break; + } + path.insert(path.begin(), parent->title); + child = parent; + } + + if (!found_path) + continue; + + ProfileWriter::BookmarkEntry entry; + entry.creation_time = item->date_added; + entry.title = item->title; + entry.url = item->url; + entry.path = path; + entry.in_toolbar = is_in_toolbar; + + bookmarks.push_back(entry); + + if (item->favicon) + favicon_map[item->favicon].insert(item->url); + + // This bookmark has a keyword, we import it to our TemplateURL model. + TemplateURL* t_url = Firefox2Importer::CreateTemplateURL( + item->title, UTF8ToWide(item->keyword), item->url); + if (t_url) + template_urls.push_back(t_url); + } + + STLDeleteContainerPointers(list.begin(), list.end()); + + // Write into profile. + if (!bookmarks.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddBookmarkEntry, bookmarks)); + } + if (!template_urls.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddKeywords, template_urls, -1, false)); + } else { + STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); + } + if (!favicon_map.empty() && !cancelled()) { + std::vector favicons; + LoadFavicons(db.get(), favicon_map, &favicons); + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddFavicons, favicons)); + } +} + +void Firefox3Importer::ImportPasswords() { + // Initializes NSS3. + NSSDecryptor decryptor; + if (!decryptor.Init(source_path_, source_path_) && + !decryptor.Init(app_path_, source_path_)) + return; + + // Firefox 3 uses signons3.txt to store the passwords. + std::wstring file = source_path_; + file_util::AppendToPath(&file, L"signons3.txt"); + if (!file_util::PathExists(file)) { + file = source_path_; + file_util::AppendToPath(&file, L"signons2.txt"); + } + + std::string content; + file_util::ReadFileToString(file, &content); + std::vector forms; + decryptor.ParseSignons(content, &forms); + + if (!cancelled()) { + for (size_t i = 0; i < forms.size(); ++i) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddPasswordForm, forms[i])); + } + } +} + +void Firefox3Importer::ImportSearchEngines() { + std::vector files; + GetSearchEnginesXMLFiles(&files); + + std::vector search_engines; + ParseSearchEnginesFromXMLFiles(files, &search_engines); + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddKeywords, search_engines, + GetFirefoxDefaultSearchEngineIndex(search_engines, source_path_), true)); +} + +void Firefox3Importer::ImportHomepage() { + GURL homepage = GetHomepage(source_path_); + if (homepage.is_valid() && !IsDefaultHomepage(homepage, app_path_)) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddHomepage, homepage)); + } +} + +void Firefox3Importer::GetSearchEnginesXMLFiles( + std::vector* files) { + std::wstring file = source_path_; + file_util::AppendToPath(&file, L"search.sqlite"); + if (!file_util::PathExists(file)) + return; + + sqlite3* sqlite; + if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) + return; + scoped_ptr_malloc db(sqlite); + + SQLStatement s; + const char* stmt = "SELECT engineid FROM engine_data ORDER BY value ASC"; + + if (s.prepare(db.get(), stmt) != SQLITE_OK) + return; + + std::wstring app_path = app_path_; + file_util::AppendToPath(&app_path, L"searchplugins"); + std::wstring profile_path = source_path_; + file_util::AppendToPath(&profile_path, L"searchplugins"); + + const std::wstring kAppPrefix = L"[app]/"; + const std::wstring kProfilePrefix = L"[profile]/"; + while (s.step() == SQLITE_ROW && !cancelled()) { + std::wstring file; + std::wstring engine = UTF8ToWide(s.column_string(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::wstring::npos) { + file = app_path; + file_util::AppendToPath( + &file, + engine.substr(index + kAppPrefix.length())); // Remove '[app]/'. + } else if ((index = engine.find(kProfilePrefix)) != std::wstring::npos) { + file = profile_path; + file_util::AppendToPath( + &file, + engine.substr(index + kProfilePrefix.length())); // Remove + // '[profile]/'. + } else { + NOTREACHED() << "Unexpected Firefox 3 search engine id"; + continue; + } + files->push_back(file); + } +} + +void Firefox3Importer::LoadRootNodeID(sqlite3* db, + int* toolbar_folder_id, + int* menu_folder_id, + int* unsorted_folder_id) { + const char kToolbarFolderName[] = "toolbar"; + const char kMenuFolderName[] = "menu"; + const char kUnsortedFolderName[] = "unfiled"; + + SQLStatement s; + const char* stmt = "SELECT root_name, folder_id FROM moz_bookmarks_roots"; + if (s.prepare(db, stmt) != SQLITE_OK) + return; + + while (s.step() == SQLITE_ROW) { + std::string folder = s.column_string(0); + int id = s.column_int(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 Firefox3Importer::LoadLivemarkIDs(sqlite3* db, + std::set* livemark) { + const char kFeedAnnotation[] = "livemark/feedURI"; + livemark->clear(); + + SQLStatement s; + const char* stmt = "SELECT b.item_id " + "FROM moz_anno_attributes a " + "JOIN moz_items_annos b ON a.id = b.anno_attribute_id " + "WHERE a.name = ? "; + if (s.prepare(db, stmt) != SQLITE_OK) + return; + + s.bind_string(0, kFeedAnnotation); + while (s.step() == SQLITE_ROW && !cancelled()) + livemark->insert(s.column_int(0)); +} + +void Firefox3Importer::GetTopBookmarkFolder(sqlite3* db, int folder_id, + BookmarkList* list) { + SQLStatement s; + const char* stmt = "SELECT b.title " + "FROM moz_bookmarks b " + "WHERE b.type = 2 AND b.id = ? " + "ORDER BY b.position"; + if (s.prepare(db, stmt) != SQLITE_OK) + return; + + s.bind_int(0, folder_id); + if (s.step() == SQLITE_ROW) { + BookmarkItem* item = new BookmarkItem; + item->parent = -1; // The top level folder has no parent. + item->id = folder_id; + item->title = s.column_string16(0); + item->type = 2; + item->favicon = 0; + list->push_back(item); + } +} + +void Firefox3Importer::GetWholeBookmarkFolder(sqlite3* db, BookmarkList* list, + size_t position) { + if (position >= list->size()) { + NOTREACHED(); + return; + } + + SQLStatement s; + const char* stmt = "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"; + if (s.prepare(db, stmt) != SQLITE_OK) + return; + + s.bind_int(0, (*list)[position]->id); + BookmarkList temp_list; + while (s.step() == SQLITE_ROW) { + BookmarkItem* item = new BookmarkItem; + item->parent = static_cast(position); + item->id = s.column_int(0); + item->url = GURL(s.column_string(1)); + item->title = s.column_string16(2); + item->type = s.column_int(3); + item->keyword = s.column_string(4); + item->date_added = Time::FromTimeT(s.column_int64(5)/1000000); + item->favicon = s.column_int64(6); + + temp_list.push_back(item); + } + + // 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 == 2) + GetWholeBookmarkFolder(db, list, list->size() - 1); + } +} + +void Firefox3Importer::LoadFavicons( + sqlite3* db, + const FaviconMap& favicon_map, + std::vector* favicons) { + SQLStatement s; + const char* stmt = "SELECT url, data FROM moz_favicons WHERE id=?"; + if (s.prepare(db, stmt) != SQLITE_OK) + return; + + for (FaviconMap::const_iterator i = favicon_map.begin(); + i != favicon_map.end(); ++i) { + s.bind_int64(0, i->first); + if (s.step() == SQLITE_ROW) { + history::ImportedFavIconUsage usage; + + usage.favicon_url = GURL(s.column_string(0)); + if (!usage.favicon_url.is_valid()) + continue; // Don't bother importing favicons with invalid URLs. + + std::vector data; + if (!s.column_blob_as_vector(1, &data) || data.empty()) + continue; // Data definitely invalid. + + if (!ReencodeFavicon(&data[0], data.size(), &usage.png_data)) + continue; // Unable to decode. + + usage.urls = i->second; + favicons->push_back(usage); + } + s.reset(); + } +} + diff --git a/chrome/browser/importer/firefox3_importer.h b/chrome/browser/importer/firefox3_importer.h new file mode 100644 index 0000000..d4cc0bc --- /dev/null +++ b/chrome/browser/importer/firefox3_importer.h @@ -0,0 +1,85 @@ +// Copyright (c) 2006-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. + +#ifndef CHROME_BROWSER_IMPORTER_FIREFOX3_IMPORTER_H_ +#define CHROME_BROWSER_IMPORTER_FIREFOX3_IMPORTER_H_ + +#include +#include +#include + +#include "base/basictypes.h" +#include "chrome/browser/importer/importer.h" +#include "chrome/common/sqlite_utils.h" +#include "googleurl/src/gurl.h" + +// Importer for Mozilla Firefox 3. +// Firefox 3 stores its persistent information in a new system called places. +// http://wiki.mozilla.org/Places +class Firefox3Importer : public Importer { + public: + Firefox3Importer() { } + virtual ~Firefox3Importer() { } + + // Importer methods. + virtual void StartImport(ProfileInfo profile_info, + uint16 items, + ProfileWriter* writer, + ImporterHost* host); + + private: + typedef std::map > FaviconMap; + + 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 GetSearchEnginesXMLFiles(std::vector* files); + + // The struct stores the information about a bookmark item. + struct BookmarkItem { + int parent; + int id; + GURL url; + std::wstring title; + int type; + std::string keyword; + Time date_added; + int64 favicon; + }; + typedef std::vector BookmarkList; + + // Gets the specific IDs of bookmark root node from |db|. + void LoadRootNodeID(sqlite3* db, int* toolbar_folder_id, + int* menu_folder_id, int* unsorted_folder_id); + + // Loads all livemark IDs from database |db|. + void LoadLivemarkIDs(sqlite3* db, std::set* livemark); + + // Gets the bookmark folder with given ID, and adds the entry in |list| + // if successful. + void GetTopBookmarkFolder(sqlite3* db, int folder_id, BookmarkList* list); + + // Loads all children of the given folder, and appends them to the |list|. + void GetWholeBookmarkFolder(sqlite3* db, BookmarkList* list, + size_t position); + + // Loads the favicons given in the map from the database, loads the data, + // and converts it into FaviconUsage structures. + void LoadFavicons(sqlite3* db, + const FaviconMap& favicon_map, + std::vector* favicons); + + ProfileWriter* writer_; + std::wstring source_path_; + std::wstring app_path_; + + DISALLOW_EVIL_CONSTRUCTORS(Firefox3Importer); +}; + +#endif // CHROME_BROWSER_IMPORTER_FIREFOX3_IMPORTER_H_ + diff --git a/chrome/browser/importer/firefox_importer_unittest.cc b/chrome/browser/importer/firefox_importer_unittest.cc new file mode 100644 index 0000000..d47b133 --- /dev/null +++ b/chrome/browser/importer/firefox_importer_unittest.cc @@ -0,0 +1,216 @@ +// Copyright (c) 2006-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 "testing/gtest/include/gtest/gtest.h" + +#include "base/file_util.h" +#include "base/path_service.h" +#include "chrome/browser/importer/firefox2_importer.h" +#include "chrome/browser/importer/firefox_importer_utils.h" +#include "chrome/browser/importer/firefox_profile_lock.h" +#include "chrome/common/chrome_paths.h" + +TEST(FirefoxImporterTest, Firefox2NSS3Decryptor) { + std::wstring nss_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &nss_path)); + file_util::AppendToPath(&nss_path, L"firefox2_nss"); + std::wstring db_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &db_path)); + file_util::AppendToPath(&db_path, L"firefox2_profile"); + NSSDecryptor decryptor; + EXPECT_TRUE(decryptor.Init(nss_path, db_path)); + EXPECT_EQ(L"hello", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" + "wFAYIKoZIhvcNAwcECBJM63MpT9rtBAjMCm7qo/EhlA==")); + // Test UTF-16 encoding. + EXPECT_EQ(L"\x4E2D", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" + "wFAYIKoZIhvcNAwcECN9OQ5ZFmhb8BAiFo1Z+fUvaIQ==")); +} + +TEST(FirefoxImporterTest, Firefox3NSS3Decryptor) { + std::wstring nss_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &nss_path)); + file_util::AppendToPath(&nss_path, L"firefox3_nss"); + std::wstring db_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &db_path)); + file_util::AppendToPath(&db_path, L"firefox3_profile"); + NSSDecryptor decryptor; + EXPECT_TRUE(decryptor.Init(nss_path, db_path)); + EXPECT_EQ(L"hello", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" + "wFAYIKoZIhvcNAwcECKajtRg4qFSHBAhv9luFkXgDJA==")); + // Test UTF-16 encoding. + EXPECT_EQ(L"\x4E2D", decryptor.Decrypt("MDIEEPgAAAAAAAAAAAAAAAAAAAE" + "wFAYIKoZIhvcNAwcECLWqqiccfQHWBAie74hxnULxlw==")); +} + +TEST(FirefoxImporterTest, Firefox2BookmarkParse) { + bool result; + + // Tests charset. + std::string charset; + result = Firefox2Importer::ParseCharsetFromLine( + "", + &charset); + EXPECT_TRUE(result); + EXPECT_EQ("UTF-8", charset); + + // Escaped characters in name. + std::wstring folder_name; + bool is_toolbar_folder; + result = Firefox2Importer::ParseFolderNameFromLine( + "

< >" + " & " ' \\ /

", + charset, &folder_name, &is_toolbar_folder); + EXPECT_TRUE(result); + EXPECT_EQ(L"< > & \" ' \\ /", folder_name); + EXPECT_EQ(false, is_toolbar_folder); + + // Empty name and toolbar folder attribute. + result = Firefox2Importer::ParseFolderNameFromLine( + "

", + charset, &folder_name, &is_toolbar_folder); + EXPECT_TRUE(result); + EXPECT_EQ(L"", folder_name); + EXPECT_EQ(true, is_toolbar_folder); + + // Unicode characters in title and shortcut. + std::wstring title; + GURL url, favicon; + std::wstring shortcut; + std::wstring post_data; + Time add_date; + result = Firefox2Importer::ParseBookmarkFromLine( + "
\xE4\xB8\xAD\xE6\x96\x87", + charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); + EXPECT_TRUE(result); + EXPECT_EQ(L"\x4E2D\x6587", title); + EXPECT_EQ("http://chinese.site.cn/path?query=1#ref", url.spec()); + EXPECT_EQ(L"\x4E2D", shortcut); + EXPECT_EQ(L"", post_data); + EXPECT_TRUE(Time() == add_date); + + // No shortcut, and url contains %22 ('"' character). + result = Firefox2Importer::ParseBookmarkFromLine( + "
%22\">name", + charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); + EXPECT_TRUE(result); + EXPECT_EQ(L"name", title); + EXPECT_EQ("http://domain.com/?q=\"%3C%3E\"", url.spec()); + EXPECT_EQ(L"", shortcut); + EXPECT_EQ(L"", post_data); + EXPECT_TRUE(Time() == add_date); + + // Creation date. + result = Firefox2Importer::ParseBookmarkFromLine( + "
name", + charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); + EXPECT_TRUE(result); + EXPECT_EQ(L"name", title); + EXPECT_EQ(GURL("http://site/"), url); + EXPECT_EQ(L"", shortcut); + EXPECT_EQ(L"", post_data); + EXPECT_TRUE(Time::FromTimeT(1121301154) == add_date); + + // Post-data + result = Firefox2Importer::ParseBookmarkFromLine( + "
Test Post keyword", + charset, &title, &url, &favicon, &shortcut, &add_date, &post_data); + EXPECT_TRUE(result); + EXPECT_EQ(L"Test Post keyword", title); + EXPECT_EQ("http://localhost:8080/test/hello.html", url.spec()); + EXPECT_EQ(L"post", shortcut); + EXPECT_EQ(L"lname%3D%25s", post_data); + EXPECT_TRUE(Time::FromTimeT(1212447159) == add_date); + + // Invalid case. + result = Firefox2Importer::ParseBookmarkFromLine( + "
lock; + EXPECT_EQ(static_cast(NULL), lock.get()); + EXPECT_FALSE(file_util::PathExists(lock_file_path)); + lock.reset(new FirefoxProfileLock(test_path)); + EXPECT_TRUE(lock->HasAcquired()); + EXPECT_TRUE(file_util::PathExists(lock_file_path)); + lock->Unlock(); + EXPECT_FALSE(lock->HasAcquired()); + EXPECT_FALSE(file_util::PathExists(lock_file_path)); + lock->Lock(); + EXPECT_TRUE(lock->HasAcquired()); + EXPECT_TRUE(file_util::PathExists(lock_file_path)); + lock->Lock(); + EXPECT_TRUE(lock->HasAcquired()); + lock->Unlock(); + EXPECT_FALSE(lock->HasAcquired()); + EXPECT_FALSE(file_util::PathExists(lock_file_path)); +} + +// If for some reason the lock file is left behind by the previous owner, we +// should still be able to lock it, at least in the Windows implementation. +TEST(FirefoxImporterTest, ProfileLockOrphaned) { + std::wstring test_path; + file_util::CreateNewTempDirectory(L"firefox_profile", &test_path); + std::wstring lock_file_path = test_path; + file_util::AppendToPath(&lock_file_path, FirefoxProfileLock::kLockFileName); + + // Create the orphaned lock file. + HANDLE lock_file = CreateFile(lock_file_path.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL); + CloseHandle(lock_file); + EXPECT_TRUE(file_util::PathExists(lock_file_path)); + + scoped_ptr lock; + EXPECT_EQ(static_cast(NULL), lock.get()); + lock.reset(new FirefoxProfileLock(test_path)); + EXPECT_TRUE(lock->HasAcquired()); + lock->Unlock(); + EXPECT_FALSE(lock->HasAcquired()); +} + +// Tests two locks contending for the same lock file. +TEST(FirefoxImporterTest, ProfileLockContention) { + std::wstring test_path; + file_util::CreateNewTempDirectory(L"firefox_profile", &test_path); + + scoped_ptr lock1; + EXPECT_EQ(static_cast(NULL), lock1.get()); + lock1.reset(new FirefoxProfileLock(test_path)); + EXPECT_TRUE(lock1->HasAcquired()); + + scoped_ptr lock2; + EXPECT_EQ(static_cast(NULL), lock2.get()); + lock2.reset(new FirefoxProfileLock(test_path)); + EXPECT_FALSE(lock2->HasAcquired()); + + lock1->Unlock(); + EXPECT_FALSE(lock1->HasAcquired()); + + lock2->Lock(); + EXPECT_TRUE(lock2->HasAcquired()); + lock2->Unlock(); + EXPECT_FALSE(lock2->HasAcquired()); +} + diff --git a/chrome/browser/importer/firefox_importer_utils.cc b/chrome/browser/importer/firefox_importer_utils.cc new file mode 100644 index 0000000..c9d6caa --- /dev/null +++ b/chrome/browser/importer/firefox_importer_utils.cc @@ -0,0 +1,727 @@ +// Copyright (c) 2006-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_importer_utils.h" + +#include + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/time.h" +#include "chrome/browser/template_url.h" +#include "chrome/browser/template_url_model.h" +#include "chrome/browser/template_url_parser.h" +#include "chrome/common/win_util.h" +#include "googleurl/src/gurl.h" +#include "net/base/base64.h" + +namespace { + +// 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 +}; + +// FirefoxURLParameterFilter is used to remove parameter mentioning Firefox from +// the search URL when importing search engines. +class FirefoxURLParameterFilter : public TemplateURLParser::ParameterFilter { + public: + FirefoxURLParameterFilter() { } + ~FirefoxURLParameterFilter() { } + + // TemplateURLParser::ParameterFilter method. + virtual bool KeepParameter(const std::string& key, + const std::string& value) { + std::string low_value = StringToLowerASCII(value); + if (low_value.find("mozilla") != -1 || low_value.find("firefox") != -1 || + low_value.find("moz:") != -1 ) + return false; + return true; + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(FirefoxURLParameterFilter); +}; + +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 + +int GetCurrentFirefoxMajorVersion() { + TCHAR ver_buffer[128]; + DWORD ver_buffer_length = sizeof(ver_buffer); + // 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 (int i = 0; i < arraysize(kFireFoxRegistryPaths); ++i) { + bool result = ReadFromRegistry(kFireFoxRegistryPaths[i], + L"Software\\Mozilla\\Mozilla Firefox", + L"CurrentVersion", ver_buffer, &ver_buffer_length); + if (!result) + continue; + return _wtoi(ver_buffer); + } + return 0; +} + +std::wstring GetProfilesINI() { + // The default location of the profile folder containing user data is + // under the "Application Data" folder in Windows XP. + std::wstring ini_file; + wchar_t buffer[MAX_PATH] = {0}; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, + SHGFP_TYPE_CURRENT, buffer))) { + ini_file = buffer; + file_util::AppendToPath(&ini_file, L"Mozilla\\Firefox\\profiles.ini"); + } + if (!file_util::PathExists(ini_file)) + ini_file.clear(); + + return ini_file; +} + +std::wstring GetFirefoxInstallPath() { + // Detects the path that Firefox is installed in. + std::wstring registry_path = L"Software\\Mozilla\\Mozilla Firefox"; + TCHAR buffer[MAX_PATH]; + DWORD buffer_length = sizeof(buffer); + bool result; + result = ReadFromRegistry(HKEY_LOCAL_MACHINE, registry_path.c_str(), + L"CurrentVersion", buffer, &buffer_length); + if (!result) + return std::wstring(); + registry_path += L"\\" + std::wstring(buffer) + L"\\Main"; + buffer_length = sizeof(buffer); + result = ReadFromRegistry(HKEY_LOCAL_MACHINE, registry_path.c_str(), + L"Install Directory", buffer, &buffer_length); + if (!result) + return std::wstring(); + return buffer; +} + +void ParseProfileINI(std::wstring file, DictionaryValue* root) { + // Reads the whole INI file. + std::string content; + file_util::ReadFileToString(file, &content); + ReplaceSubstringsAfterOffset(&content, 0, "\r\n", "\n"); + std::vector lines; + SplitString(content, '\n', &lines); + + // Parses the file. + root->Clear(); + std::wstring current_section; + for (size_t i = 0; i < lines.size(); ++i) { + std::wstring line = UTF8ToWide(lines[i]); + if (line.empty()) { + // Skips the empty line. + continue; + } + if (line[0] == L'#' || line[0] == L';') { + // This line is a comment. + continue; + } + if (line[0] == L'[') { + // It is a section header. + current_section = line.substr(1); + size_t end = current_section.rfind(L']'); + if (end != std::wstring::npos) + current_section.erase(end); + } else { + std::wstring key, value; + size_t equal = line.find(L'='); + if (equal != std::wstring::npos) { + key = line.substr(0, equal); + value = line.substr(equal + 1); + // Checks whether the section and key contain a '.' character. + // Those sections and keys break DictionaryValue's path format, + // so we discard them. + if (current_section.find(L'.') == std::wstring::npos && + key.find(L'.') == std::wstring::npos) + root->SetString(current_section + L"." + key, value); + } + } + } +} + +bool CanImportURL(const GURL& url) { + const char* kInvalidSchemes[] = {"wyciwyg", "place", "about", "chrome"}; + + // The URL is not valid. + if (!url.is_valid()) + return false; + + // Filter out the URLs with unsupported schemes. + for (int i = 0; i < arraysize(kInvalidSchemes); ++i) { + if (url.SchemeIs(kInvalidSchemes[i])) + return false; + } + + return true; +} + +void ParseSearchEnginesFromXMLFiles(const std::vector& xml_files, + std::vector* search_engines) { + DCHECK(search_engines); + + std::map search_engine_for_url; + std::string content; + // The first XML file represents the default search engine in Firefox 3, so we + // need to keep it on top of the list. + TemplateURL* default_turl = NULL; + for (std::vector::const_iterator iter = xml_files.begin(); + iter != xml_files.end(); ++iter) { + file_util::ReadFileToString(*iter, &content); + TemplateURL* template_url = new TemplateURL(); + FirefoxURLParameterFilter param_filter; + if (TemplateURLParser::Parse( + reinterpret_cast(content.data()), + content.length(), ¶m_filter, template_url) && + template_url->url()) { + std::wstring url = template_url->url()->url(); + std::map::iterator iter = + search_engine_for_url.find(url); + if (iter != search_engine_for_url.end()) { + // 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; + search_engine_for_url.erase(iter); + } + // Give this a keyword to facilitate tab-to-search, if possible. + template_url->set_keyword(TemplateURLModel::GenerateKeyword(GURL(url), + false)); + template_url->set_show_in_default_list(true); + search_engine_for_url[url] = template_url; + if (!default_turl) + default_turl = template_url; + } else { + delete template_url; + } + content.clear(); + } + + // Put the results in the |search_engines| vector. + std::map::iterator t_iter; + for (t_iter = search_engine_for_url.begin(); + t_iter != search_engine_for_url.end(); ++t_iter) { + if (t_iter->second == default_turl) + search_engines->insert(search_engines->begin(), default_turl); + else + search_engines->push_back(t_iter->second); + } +} + +bool ReadPrefFile(const std::wstring& path_name, + const std::wstring& file_name, + std::string* content) { + if (content == NULL) + return false; + + std::wstring file = path_name; + file_util::AppendToPath(&file, file_name.c_str()); + + file_util::ReadFileToString(file, content); + + if (content->empty()) { + NOTREACHED() << L"Firefox preference file " << file_name.c_str() + << L" is empty."; + return false; + } + + return true; +} + +std::string ReadBrowserConfigProp(const std::wstring& app_path, + const std::string& pref_key) { + std::string content; + if (!ReadPrefFile(app_path, L"browserconfig.properties", &content)) + return ""; + + // This file has the syntax: key=value. + size_t prop_index = content.find(pref_key + "="); + if (prop_index == -1) + return ""; + + size_t start = prop_index + pref_key.length(); + size_t stop = -1; + if (start != -1) + stop = content.find("\n", start + 1); + + if (start == -1 || stop == -1 || (start == stop)) { + NOTREACHED() << "Firefox property " << pref_key << " could not be parsed."; + return ""; + } + + return content.substr(start + 1, stop - start - 1); +} + +std::string ReadPrefsJsValue(const std::wstring& profile_path, + const std::string& pref_key) { + std::string content; + if (!ReadPrefFile(profile_path, L"prefs.js", &content)) + return ""; + + // 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 == -1) + return ""; + + size_t start = prop_index + search_for.length(); + size_t stop = -1; + if (start != -1) + stop = content.find(")", start + 1); + + if (start == -1 || stop == -1) { + NOTREACHED() << "Firefox property " << pref_key << " could not be parsed."; + return ""; + } + + // 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); +} + +int GetFirefoxDefaultSearchEngineIndex( + const std::vector& search_engines, + const std::wstring& profile_path) { + // The default search engine is contained in the file prefs.js found in the + // profile directory. + // It is the "browser.search.selectedEngine" property. + if (search_engines.empty()) + return -1; + + std::wstring default_se_name = UTF8ToWide( + ReadPrefsJsValue(profile_path, "browser.search.selectedEngine")); + + int default_se_index = -1; + for (std::vector::const_iterator iter = search_engines.begin(); + iter != search_engines.end(); ++iter) { + if (default_se_name == (*iter)->short_name()) { + default_se_index = static_cast(iter - search_engines.begin()); + break; + } + } + if (default_se_index == -1) { + NOTREACHED() << + "Firefox default search engine not found in search engine list"; + } + + return default_se_index; +} + +GURL GetHomepage(const std::wstring& 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 std::wstring& 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. + std::vector urls; + SplitString(default_homepages, '|', &urls); + + for (size_t i = 0; i < urls.size(); ++i) { + if (homepage.spec() == GURL(urls[i]).spec()) + return true; + } + + return false; +} + +// class NSSDecryptor. + +// 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"; + +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::Init(const std::wstring& dll_path, + const std::wstring& 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.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. + std::wstring path = dll_path; + file_util::AppendToPath(&path, kNSS3Library); + nss3_dll_ = LoadLibraryEx(path.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 = dll_path; + file_util::AppendToPath(&path, kSoftokn3Library); + softokn3_dll_ = LoadLibraryEx(path.c_str(), NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); + if (softokn3_dll_ == NULL) { + Free(); + return false; + } + } + + // NSPR DLLs are already loaded now. + HMODULE plds4_dll = GetModuleHandle(kPLDS4Library); + HMODULE nspr4_dll = GetModuleHandle(kNSPR4Library); + if (plds4_dll == NULL || nspr4_dll == NULL) { + Free(); + return false; + } + + // Gets the function address. + NSS_Init = (NSSInitFunc)GetProcAddress(nss3_dll_, "NSS_Init"); + NSS_Shutdown = (NSSShutdownFunc)GetProcAddress(nss3_dll_, "NSS_Shutdown"); + PK11_GetInternalKeySlot = (PK11GetInternalKeySlotFunc) + GetProcAddress(nss3_dll_, "PK11_GetInternalKeySlot"); + PK11_FreeSlot = (PK11FreeSlotFunc)GetProcAddress(nss3_dll_, "PK11_FreeSlot"); + PK11_Authenticate = (PK11AuthenticateFunc) + GetProcAddress(nss3_dll_, "PK11_Authenticate"); + PK11SDR_Decrypt = (PK11SDRDecryptFunc) + GetProcAddress(nss3_dll_, "PK11SDR_Decrypt"); + SECITEM_FreeItem = (SECITEMFreeItemFunc) + GetProcAddress(nss3_dll_, "SECITEM_FreeItem"); + PL_ArenaFinish = (PLArenaFinishFunc) + GetProcAddress(plds4_dll, "PL_ArenaFinish"); + PR_Cleanup = (PRCleanupFunc)GetProcAddress(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).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) + FreeLibrary(softokn3_dll_); + softokn3_dll_ = NULL; + if (nss3_dll_ != NULL) + FreeLibrary(nss3_dll_); + nss3_dll_ = NULL; + 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; +} + +// 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 ***** */ + +std::wstring NSSDecryptor::Decrypt(const std::string& crypt) const { + // Do nothing if NSS is not loaded. + if (!nss3_dll_) + return std::wstring(); + + std::string plain; + + // The old style password is encoded in base64. They are identified + // by a leading '~'. Otherwise, we should decrypt the text. + if (crypt[0] != '~') { + std::string decoded_data; + net::Base64Decode(crypt, &decoded_data); + PK11SlotInfo* slot = NULL; + slot = PK11_GetInternalKeySlot(); + SECStatus result = PK11_Authenticate(slot, PR_TRUE, NULL); + if (result != SECSuccess) { + PK11_FreeSlot(slot); + return std::wstring(); + } + + 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; + result = PK11SDR_Decrypt(&request, &reply, NULL); + if (result == SECSuccess) + plain.assign(reinterpret_cast(reply.data), reply.len); + + SECITEM_FreeItem(&reply, PR_FALSE); + PK11_FreeSlot(slot); + } else { + // Deletes the leading '~' before decoding. + net::Base64Decode(crypt.substr(1), &plain); + } + + return UTF8ToWide(plain); +} + +// There are three versions of password filess. 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 std::string& content, + std::vector* forms) { + forms->clear(); + + // Splits the file content into lines. + std::vector lines; + SplitString(content, '\n', &lines); + + // 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; + + GURL::Replacements rep; + rep.ClearQuery(); + rep.ClearRef(); + rep.ClearUsername(); + rep.ClearPassword(); + + // Reads never-saved list. Domains are stored one per line. + size_t i; + for (i = 1; i < lines.size() && lines[i].compare(".") != 0; ++i) { + PasswordForm form; + form.origin = GURL(lines[i]).ReplaceComponents(rep); + form.signon_realm = form.origin.GetOrigin().spec(); + form.blacklisted_by_user = true; + forms->push_back(form); + } + ++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; + + PasswordForm form; + + // 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) + GURL url; + std::string realm; + const char kRealmBracketBegin[] = " ("; + const char kRealmBracketEnd[] = ")"; + if (lines[begin].find(kRealmBracketBegin) != std::string::npos) { + // In this case, the scheme may not exsit. We assume that the + // scheme is HTTP. + if (lines[begin].find("://") == std::string::npos) + lines[begin] = "http://" + lines[begin]; + + size_t start = lines[begin].find(kRealmBracketBegin); + url = GURL(lines[begin].substr(0, start)); + + start += std::string(kRealmBracketBegin).size(); + size_t end = lines[begin].rfind(kRealmBracketEnd); + realm = lines[begin].substr(start, end - start); + } else { + // Don't have http realm. It is the URL that the following passwords + // belong to. + url = GURL(lines[begin]); + } + // Skips this block if the URL is not valid. + if (!url.is_valid()) + continue; + form.origin = url.ReplaceComponents(rep); + form.signon_realm = form.origin.GetOrigin().spec(); + if (!realm.empty()) + form.signon_realm += realm; + form.ssl_valid = form.origin.SchemeIsSecure(); + ++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. + form.username_element = UTF8ToWide(lines[begin++]); + form.username_value = Decrypt(lines[begin++]); + // The element name has a leading '*'. + if (lines[begin].at(0) == '*') { + form.password_element = UTF8ToWide(lines[begin++].substr(1)); + form.password_value = Decrypt(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) + form.action = GURL(lines[begin]).ReplaceComponents(rep); + ++begin; + } + // Version 3 has an extra line for further use. + if (version == 3) { + ++begin; + } + + forms->push_back(form); + } + } +} + diff --git a/chrome/browser/importer/firefox_importer_utils.h b/chrome/browser/importer/firefox_importer_utils.h new file mode 100644 index 0000000..f69a27d --- /dev/null +++ b/chrome/browser/importer/firefox_importer_utils.h @@ -0,0 +1,211 @@ +// Copyright (c) 2006-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. + +#ifndef CHROME_BROWSER_IMPORTER_FIREFOX_IMPORTER_UTILS_H_ +#define CHROME_BROWSER_IMPORTER_FIREFOX_IMPORTER_UTILS_H_ + +#include "base/values.h" +#include "webkit/glue/password_form.h" + +class GURL; +class TemplateURL; + +// Detects which version of Firefox is installed. Returns its +// major version, and drops the minor version. Returns 0 if +// failed. +int GetCurrentFirefoxMajorVersion(); + +// Gets the full path of the profiles.ini file. This file records +// the profiles that can be used by Firefox. Returns an empty +// string if failed. +std::wstring GetProfilesINI(); + +// Detects where Firefox lives. Returns a empty string if Firefox +// is not installed. +std::wstring GetFirefoxInstallPath(); + +// 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 separeated 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(std::wstring file, DictionaryValue* root); + +// Returns true if we want to add the URL to the history. We filter +// out the URL with a unsupported scheme. +bool CanImportURL(const GURL& url); + +// Parses the OpenSearch XML files in |xml_files| and populates |search_engines| +// with the resulting TemplateURLs. +void ParseSearchEnginesFromXMLFiles(const std::vector& xml_files, + std::vector* search_engines); + +// Returns the index of the default search engine in the |search_engines| list. +// If none is found, -1 is returned. +int GetFirefoxDefaultSearchEngineIndex( + const std::vector& search_engines, + const std::wstring& profile_path); + +// Returns the home page set in Firefox in a particular profile. +GURL GetHomepage(const std::wstring& 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 std::wstring& app_path); + +// 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); + +// 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 std::wstring& dll_path, const std::wstring& db_path); + + // Frees the libraries. + void Free(); + + // Decrypts Firefox stored passwords. Before using this method, + // make sure Init() returns true. + std::wstring 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 std::string& content, + std::vector* forms); + + private: + // 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. + HMODULE nss3_dll_; + HMODULE softokn3_dll_; + + // True if NSS_Init() has been called + bool is_nss_initialized_; + + DISALLOW_EVIL_CONSTRUCTORS(NSSDecryptor); +}; + +#endif // CHROME_BROWSER_IMPORTER_FIREFOX_IMPORTER_UTILS_H_ + diff --git a/chrome/browser/importer/firefox_profile_lock.cc b/chrome/browser/importer/firefox_profile_lock.cc new file mode 100644 index 0000000..aaab891 --- /dev/null +++ b/chrome/browser/importer/firefox_profile_lock.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2006-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 "base/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 ***** */ + +// static +wchar_t FirefoxProfileLock::kLockFileName[] = L"parent.lock"; + +FirefoxProfileLock::FirefoxProfileLock(const std::wstring& path) + : lock_handle_(INVALID_HANDLE_VALUE) { + lock_file_ = path; + file_util::AppendToPath(&lock_file_, kLockFileName); + Lock(); +} + +FirefoxProfileLock::~FirefoxProfileLock() { + Unlock(); +} + +void FirefoxProfileLock::Lock() { + if (HasAcquired()) + return; + lock_handle_ = CreateFile(lock_file_.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/chrome/browser/importer/firefox_profile_lock.h b/chrome/browser/importer/firefox_profile_lock.h new file mode 100644 index 0000000..c32d592 --- /dev/null +++ b/chrome/browser/importer/firefox_profile_lock.h @@ -0,0 +1,94 @@ +// Copyright (c) 2006-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. + +#ifndef CHROME_BROWSER_IMPORTER_FIREFOX_PROFILE_LOCK_H__ +#define CHROME_BROWSER_IMPORTER_FIREFOX_PROFILE_LOCK_H__ + +#include + +#include + +#include "base/basictypes.h" +#include "testing/gtest/include/gtest/gtest_prod.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 std::wstring& path); + ~FirefoxProfileLock(); + + // Locks and releases the profile. + void Lock(); + void Unlock(); + + // Returns true if we lock the profile successfully. + bool HasAcquired(); + + private: + FRIEND_TEST(FirefoxImporterTest, ProfileLock); + FRIEND_TEST(FirefoxImporterTest, ProfileLockOrphaned); + + // The name of the lock file. + static wchar_t kLockFileName[]; + + // Full path of the lock file in the profile folder. + std::wstring lock_file_; + + // The handle of the lock file. + HANDLE lock_handle_; + + DISALLOW_EVIL_CONSTRUCTORS(FirefoxProfileLock); +}; + +#endif // CHROME_BROWSER_IMPORTER_FIREFOX_PROFILE_LOCK_H__ + diff --git a/chrome/browser/importer/ie_importer.cc b/chrome/browser/importer/ie_importer.cc new file mode 100644 index 0000000..8e53807 --- /dev/null +++ b/chrome/browser/importer/ie_importer.cc @@ -0,0 +1,567 @@ +// Copyright (c) 2006-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/ie_importer.h" + +#include +#include +#include +#include +#include + +#include "base/file_util.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/win_util.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/ie7_password.h" +#include "chrome/browser/template_url_model.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/time_format.h" +#include "chrome/common/win_util.h" +#include "googleurl/src/gurl.h" + +#include "generated_resources.h" + +namespace { + +// Gets the creation time of the given file or directory. +static Time GetFileCreationTime(const std::wstring& file) { + Time creation_time; + 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 (GetFileTime(file_handle, &creation_filetime, NULL, NULL)) + creation_time = Time::FromFileTime(creation_filetime); + return creation_time; +} + +} // 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}}; + +void IEImporter::StartImport(ProfileInfo profile_info, + uint16 items, + ProfileWriter* writer, + ImporterHost* host) { + writer_ = writer; + source_path_ = profile_info.source_path; + importer_host_ = host; + + NotifyStarted(); + + // Some IE settings (such as Protected Storage) is obtained via COM APIs. + win_util::ScopedCOMInitializer com_initializer; + + if ((items & HOME_PAGE) && !cancelled()) + ImportHomepage(); // Doesn't have a UI item. + // The order here is important! + if ((items & FAVORITES) && !cancelled()) { + NotifyItemStarted(FAVORITES); + ImportFavorites(); + NotifyItemEnded(FAVORITES); + } + if ((items & SEARCH_ENGINES) && !cancelled()) { + NotifyItemStarted(SEARCH_ENGINES); + ImportSearchEngines(); + NotifyItemEnded(SEARCH_ENGINES); + } + if ((items & PASSWORDS) && !cancelled()) { + NotifyItemStarted(PASSWORDS); + // Always import IE6 passwords. + ImportPasswordsIE6(); + + if (CurrentIEVersion() >= 7) + ImportPasswordsIE7(); + NotifyItemEnded(PASSWORDS); + } + if ((items & HISTORY) && !cancelled()) { + NotifyItemStarted(HISTORY); + ImportHistory(); + NotifyItemEnded(HISTORY); + } + NotifyEnded(); +} + +void IEImporter::ImportFavorites() { + std::wstring path; + + FavoritesInfo info; + if (!GetFavoritesInfo(&info)) + return; + + BookmarkVector bookmarks; + ParseFavoritesFolder(info, &bookmarks); + + if (!bookmarks.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddBookmarkEntry, bookmarks)); + } +} + +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"); + PStoreCreateFunc PStoreCreateInstance = + (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance"); + + CComPtr pstore; + HRESULT result = PStoreCreateInstance(&pstore, 0, 0, 0); + if (result != S_OK) { + FreeLibrary(pstorec_dll); + return; + } + + std::vector ac_list; + + // Enumerates AutoComplete items in the protected database. + CComPtr item; + result = pstore->EnumItems(0, &AutocompleteGUID, + &AutocompleteGUID, 0, &item); + 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; + std::wstring 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 != std::wstring::npos && ac.key.substr(i) == kDataSuffix) { + ac.key.erase(i); + ac.is_url = (ac.key.find(L"://") != std::wstring::npos); + ac_list.push_back(ac); + SplitString(data, L'\0', &ac_list[ac_list.size() - 1].data); + } + 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 (!(LowerCaseEqualsASCII(url.scheme(), "http") || + LowerCaseEqualsASCII(url.scheme(), "https"))) { + continue; + } + + 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 DONT auto-fill it unless the form appears under + // valid SSL conditions. + form.ssl_valid = url.SchemeIsSecure(); + + // 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; + } + } + + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddPasswordForm, form)); + } +} + +void IEImporter::ImportPasswordsIE7() { + if (!source_path_.empty()) { + // We have been called from the unit tests. Don't import real passwords. + return; + } + + const wchar_t kStorage2Path[] = + L"Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2"; + + RegKey key(HKEY_CURRENT_USER, kStorage2Path, KEY_READ); + RegistryValueIterator reg_iterator(HKEY_CURRENT_USER, kStorage2Path); + while (reg_iterator.Valid() && !cancelled()) { + // Get the size of the encrypted data. + DWORD value_len = 0; + if (key.ReadValue(reg_iterator.Name(), NULL, &value_len) && value_len) { + // Query the encrypted data. + std::vector value; + value.resize(value_len); + if (key.ReadValue(reg_iterator.Name(), &value.front(), &value_len)) { + IE7PasswordInfo password_info; + password_info.url_hash = reg_iterator.Name(); + password_info.encrypted_data = value; + password_info.date_created = Time::Now(); + main_loop_->PostTask(FROM_HERE, + NewRunnableMethod(writer_, + &ProfileWriter::AddIE7PasswordInfo, + password_info)); + } + } + + ++reg_iterator; + } +} + +// Reads history information from COM interface. +void IEImporter::ImportHistory() { + const std::string kSchemes[] = {"http", "https", "ftp", "file"}; + int total_schemes = arraysize(kSchemes); + + CComPtr url_history_stg2; + HRESULT result; + result = url_history_stg2.CoCreateInstance(CLSID_CUrlHistory, NULL, + CLSCTX_INPROC_SERVER); + if (FAILED(result)) + return; + CComPtr enum_url; + if (SUCCEEDED(result = url_history_stg2->EnumUrls(&enum_url))) { + std::vector rows; + STATURL stat_url; + ULONG fetched; + while (!cancelled() && + (result = enum_url->Next(1, &stat_url, &fetched)) == S_OK) { + std::wstring url_string; + std::wstring title_string; + if (stat_url.pwcsUrl) { + url_string = stat_url.pwcsUrl; + CoTaskMemFree(stat_url.pwcsUrl); + } + 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; + + history::URLRow row(url); + row.set_title(title_string); + row.set_last_visit(Time::FromFileTime(stat_url.ftLastVisited)); + if (stat_url.dwFlags == STATURL_QUERYFLAG_TOPLEVEL) { + row.set_visit_count(1); + row.set_hidden(false); + } else { + row.set_hidden(true); + } + + rows.push_back(row); + } + + if (!rows.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddHistoryPage, rows)); + } + } +} + +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. + // The default key's name is contained under DefaultScope. + const wchar_t kSearchScopePath[] = + L"Software\\Microsoft\\Internet Explorer\\SearchScopes"; + + RegKey key(HKEY_CURRENT_USER, kSearchScopePath, KEY_READ); + std::wstring default_search_engine_name; + const TemplateURL* default_search_engine = NULL; + std::map search_engines_map; + key.ReadValue(L"DefaultScope", &default_search_engine_name); + RegistryKeyIterator key_iterator(HKEY_CURRENT_USER, kSearchScopePath); + while (key_iterator.Valid()) { + std::wstring sub_key_name = kSearchScopePath; + sub_key_name.append(L"\\").append(key_iterator.Name()); + RegKey sub_key(HKEY_CURRENT_USER, sub_key_name.c_str(), KEY_READ); + std::wstring url; + if (!sub_key.ReadValue(L"URL", &url) || url.empty()) { + LOG(INFO) << "No URL for IE search engine at " << key_iterator.Name(); + ++key_iterator; + 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). + std::wstring name; + if (!sub_key.ReadValue(NULL, &name) || name.empty()) { + // Try the displayable name. + if (!sub_key.ReadValue(L"DisplayName", &name) || name.empty()) { + LOG(INFO) << "No name for IE search engine at " << key_iterator.Name(); + ++key_iterator; + continue; + } + } + + std::map::iterator t_iter = + search_engines_map.find(url); + TemplateURL* template_url = + (t_iter != search_engines_map.end()) ? t_iter->second : NULL; + if (!template_url) { + // First time we see that URL. + template_url = new TemplateURL(); + template_url->set_short_name(name); + template_url->SetURL(url, 0, 0); + // Give this a keyword to facilitate tab-to-search, if possible. + template_url->set_keyword(TemplateURLModel::GenerateKeyword(GURL(url), + false)); + template_url->set_show_in_default_list(true); + search_engines_map[url] = template_url; + } + if (template_url && key_iterator.Name() == default_search_engine_name) { + DCHECK(!default_search_engine); + default_search_engine = template_url; + } + ++key_iterator; + } + + // ProfileWriter::AddKeywords() requires a vector and we have a map. + std::map::iterator t_iter; + std::vector search_engines; + int default_search_engine_index = -1; + for (t_iter = search_engines_map.begin(); t_iter != search_engines_map.end(); + ++t_iter) { + search_engines.push_back(t_iter->second); + if (default_search_engine == t_iter->second) { + default_search_engine_index = + static_cast(search_engines.size()) - 1; + } + } + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddKeywords, search_engines, default_search_engine_index, + true)); +} + +void IEImporter::ImportHomepage() { + const wchar_t kIESettingsMain[] = + L"Software\\Microsoft\\Internet Explorer\\Main"; + const wchar_t kIEHomepage[] = L"Start Page"; + const wchar_t kIEDefaultHomepage[] = L"Default_Page_URL"; + + RegKey key(HKEY_CURRENT_USER, kIESettingsMain, KEY_READ); + std::wstring homepage_url; + if (!key.ReadValue(kIEHomepage, &homepage_url) || 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. + RegKey keyDefault(HKEY_LOCAL_MACHINE, kIESettingsMain, KEY_READ); + std::wstring default_homepage_url; + if (keyDefault.ReadValue(kIEDefaultHomepage, &default_homepage_url) && + !default_homepage_url.empty()) { + if (homepage.spec() == GURL(default_homepage_url).spec()) + return; + } + + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddHomepage, homepage)); +} + +bool IEImporter::GetFavoritesInfo(IEImporter::FavoritesInfo *info) { + if (!source_path_.empty()) { + // Source path exists during testing. + info->path = source_path_; + file_util::AppendToPath(&info->path, L"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 = 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 (win_util::GetWinVersion() != win_util::WINVERSION_VISTA) { + // The Link folder name is stored in the registry. + DWORD buffer_length = sizeof(buffer); + if (!ReadFromRegistry(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Internet Explorer\\Toolbar", + L"LinksFolderName", buffer, &buffer_length)) + return false; + info->links_folder = buffer; + } else { + info->links_folder = L"Links"; + } + + // Gets the creation time of the user's profile folder. + if (FAILED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, + SHGFP_TYPE_CURRENT, buffer))) + return false; + info->profile_creation_time = GetFileCreationTime(buffer); + + return true; +} + +void IEImporter::ParseFavoritesFolder(const FavoritesInfo& info, + BookmarkVector* bookmarks) { + std::wstring ie_folder = l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_IE); + BookmarkVector toolbar_bookmarks; + std::wstring file; + std::vector file_list; + file_util::FileEnumerator file_enumerator(info.path, true, + file_util::FileEnumerator::FILES); + while (!(file = file_enumerator.Next()).empty() && !cancelled()) + file_list.push_back(file); + + // Keep the bookmarks in alphabetical order. + std::sort(file_list.begin(), file_list.end()); + + for (std::vector::iterator it = file_list.begin(); + it != file_list.end(); ++it) { + std::wstring filename = file_util::GetFilenameFromPath(*it); + std::wstring extension = file_util::GetFileExtensionFromPath(filename); + if (!LowerCaseEqualsASCII(extension, "url")) + continue; + + // We don't import default bookmarks from IE, e.g. "Customize links", + // "Free Hotmail". To detect these, we compare the creation time of the + // .url file with that of the profile folder. The file's creation time + // should be 2 minute later (to allow jitter). + Time creation = GetFileCreationTime(*it); + if (info.profile_creation_time != Time() && + creation - info.profile_creation_time <= TimeDelta::FromMinutes(2)) + continue; + + // Skip the bookmark with invalid URL. + GURL url = GURL(ResolveInternetShortcut(*it)); + if (!url.is_valid()) + continue; + + // Remove the dot and the file extension, and the directory path. + std::wstring relative_path = it->substr(info.path.size(), + it->size() - filename.size() - info.path.size()); + TrimString(relative_path, L"\\", &relative_path); + + ProfileWriter::BookmarkEntry entry; + entry.title = filename.substr(0, filename.size() - (extension.size() + 1)); + entry.url = url; + entry.creation_time = creation; + if (!relative_path.empty()) + SplitString(relative_path, file_util::kPathSeparator, &entry.path); + + // Flatten the bookmarks in Link folder onto bookmark toolbar. Otherwise, + // put it into "Other bookmarks". + if (first_run() && + (entry.path.size() > 0 && entry.path[0] == info.links_folder)) { + entry.in_toolbar = true; + entry.path.erase(entry.path.begin()); + toolbar_bookmarks.push_back(entry); + } else { + // After the first run, we put the bookmarks in a "Imported From IE" + // folder, so that we don't mess up the "Other bookmarks". + if (!first_run()) + entry.path.insert(entry.path.begin(), ie_folder); + bookmarks->push_back(entry); + } + } + bookmarks->insert(bookmarks->begin(), toolbar_bookmarks.begin(), + toolbar_bookmarks.end()); +} + +std::wstring IEImporter::ResolveInternetShortcut(const std::wstring& file) { + win_util::CoMemReleaser url; + CComPtr url_locator; + HRESULT result = url_locator.CoCreateInstance(CLSID_InternetShortcut, NULL, + CLSCTX_INPROC_SERVER); + if (FAILED(result)) + return std::wstring(); + + CComPtr persist_file; + result = url_locator.QueryInterface(&persist_file); + if (FAILED(result)) + return std::wstring(); + + // Loads the Internet Shortcut from persistent storage. + result = persist_file->Load(file.c_str(), STGM_READ); + if (FAILED(result)) + return std::wstring(); + + result = url_locator->GetURL(&url); + // GetURL can return S_FALSE (FAILED(S_FALSE) is false) when url == NULL. + if (FAILED(result) || (url == NULL)) + return std::wstring(); + + return std::wstring(url); +} + +int IEImporter::CurrentIEVersion() const { + static int version = -1; + if (version < 0) { + wchar_t buffer[128]; + DWORD buffer_length = sizeof(buffer); + bool result = ReadFromRegistry(HKEY_LOCAL_MACHINE, + L"Software\\Microsoft\\Internet Explorer", + L"Version", buffer, &buffer_length); + version = (result ? _wtoi(buffer) : 0); + } + return version; +} + diff --git a/chrome/browser/importer/ie_importer.h b/chrome/browser/importer/ie_importer.h new file mode 100644 index 0000000..fce1f8e --- /dev/null +++ b/chrome/browser/importer/ie_importer.h @@ -0,0 +1,89 @@ +// Copyright (c) 2006-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. + +#ifndef CHROME_BROWSER_IMPORTER_IE_IMPORTER_H_ +#define CHROME_BROWSER_IMPORTER_IE_IMPORTER_H_ + +#include "chrome/browser/importer/importer.h" + +class IEImporter : public Importer { + public: + IEImporter() {} + virtual ~IEImporter() {} + + // Importer methods. + virtual void StartImport(ProfileInfo browser_info, + uint16 items, + ProfileWriter* writer, + ImporterHost* host); + + private: + FRIEND_TEST(ImporterTest, IEImporter); + + void ImportFavorites(); + void ImportHistory(); + + // Import password for IE6 stored in protected storage. + void ImportPasswordsIE6(); + + // Import password for IE7 and IE8 stored in Storage2. + void ImportPasswordsIE7(); + + virtual 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(); + + // Resolves what's the .url file actually targets. + // Returns empty string if failed. + std::wstring ResolveInternetShortcut(const std::wstring& file); + + // A struct hosts the information of IE Favorite folder. + struct FavoritesInfo { + std::wstring path; + std::wstring links_folder; + // The creation time of the user's profile folder. + Time profile_creation_time; + }; + typedef std::vector BookmarkVector; + + // Gets the information of Favorites folder. Returns true if successful. + bool GetFavoritesInfo(FavoritesInfo* info); + + // This function will read the files in the Favorite folder, and store + // the bookmark items in |bookmarks|. + void ParseFavoritesFolder(const FavoritesInfo& info, + BookmarkVector* bookmarks); + + // Determines which version of IE is in use. + int CurrentIEVersion() const; + + // Hosts the writer used in this importer. + ProfileWriter* writer_; + + // IE PStore subkey GUID: AutoComplete password & form data. + static const GUID kPStoreAutocompleteGUID; + + // A fake GUID for unit test. + static const GUID kUnittestGUID; + + // A struct hosts the information of AutoComplete data in PStore. + struct AutoCompleteInfo { + std::wstring key; + std::vector data; + bool is_url; + }; + + // IE does not have source path. It's used in unit tests only for + // providing a fake source. + std::wstring source_path_; + + DISALLOW_EVIL_CONSTRUCTORS(IEImporter); +}; + +#endif // CHROME_BROWSER_IMPORTER_IE_IMPORTER_H_ + diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc new file mode 100644 index 0000000..45d317f --- /dev/null +++ b/chrome/browser/importer/importer.cc @@ -0,0 +1,541 @@ +// Copyright (c) 2006-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/importer.h" + +#include + +#include "base/file_util.h" +#include "base/gfx/image_operations.h" +#include "base/gfx/png_encoder.h" +#include "base/string_util.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/importer/firefox2_importer.h" +#include "chrome/browser/importer/firefox3_importer.h" +#include "chrome/browser/importer/firefox_importer_utils.h" +#include "chrome/browser/importer/firefox_profile_lock.h" +#include "chrome/browser/importer/ie_importer.h" +#include "chrome/browser/template_url_model.h" +#include "chrome/browser/shell_integration.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/common/gfx/favicon_size.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/views/window.h" +#include "webkit/glue/image_decoder.h" + +#include "generated_resources.h" + +// ProfileWriter. + +bool ProfileWriter::BookmarkModelIsLoaded() const { + return profile_->GetBookmarkModel()->IsLoaded(); +} + +void ProfileWriter::AddBookmarkModelObserver(BookmarkModelObserver* observer) { + profile_->GetBookmarkModel()->AddObserver(observer); +} + +bool ProfileWriter::TemplateURLModelIsLoaded() const { + return profile_->GetTemplateURLModel()->loaded(); +} + +void ProfileWriter::AddTemplateURLModelObserver( + NotificationObserver* observer) { + TemplateURLModel* model = profile_->GetTemplateURLModel(); + NotificationService::current()->AddObserver( + observer, TEMPLATE_URL_MODEL_LOADED, + Source(model)); + model->Load(); +} + +void ProfileWriter::AddPasswordForm(const PasswordForm& form) { + profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->AddLogin(form); +} + +void ProfileWriter::AddIE7PasswordInfo(const IE7PasswordInfo& info) { + profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->AddIE7Login(info); +} + +void ProfileWriter::AddHistoryPage(const std::vector& page) { + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> + AddPagesWithDetails(page); +} + +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. + prefs->SetString(prefs::kHomePage, ASCIIToWide(home_page.spec())); + prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread()); +} + +void ProfileWriter::AddBookmarkEntry( + const std::vector& bookmark) { + BookmarkModel* model = profile_->GetBookmarkModel(); + DCHECK(model->IsLoaded()); + + bool show_bookmark_toolbar = false; + std::set groups_added_to; + for (std::vector::const_iterator it = bookmark.begin(); + it != bookmark.end(); ++it) { + // Don't insert this url if it exists in model or url is not valid. + if (model->GetNodeByURL(it->url) != NULL || !it->url.is_valid()) + continue; + + // Set up groups in BookmarkModel in such a way that path[i] is + // the subgroup of path[i-1]. Finally they construct a path in the + // model: + // path[0] \ path[1] \ ... \ path[size() - 1] + BookmarkNode* parent = + (it->in_toolbar ? model->GetBookmarkBarNode() : model->other_node()); + for (std::vector::const_iterator i = it->path.begin(); + i != it->path.end(); ++i) { + BookmarkNode* child = NULL; + for (int index = 0; index < parent->GetChildCount(); ++index) { + BookmarkNode* node = parent->GetChild(index); + if ((node->GetType() == history::StarredEntry::BOOKMARK_BAR || + node->GetType() == history::StarredEntry::USER_GROUP) && + node->GetTitle() == *i) { + child = node; + break; + } + } + if (child == NULL) + child = model->AddGroup(parent, parent->GetChildCount(), *i); + parent = child; + } + groups_added_to.insert(parent); + model->AddURLWithCreationTime(parent, parent->GetChildCount(), + it->title, it->url, it->creation_time); + + // If some items are put into toolbar, it looks like the user was using + // it in their last browser. We turn on the bookmarks toolbar. + if (it->in_toolbar) + show_bookmark_toolbar = true; + } + + // Reset the date modified time of the groups we added to. We do this to + // make sure the 'recently added to' combobox in the bubble doesn't get random + // groups. + for (std::set::const_iterator i = groups_added_to.begin(); + i != groups_added_to.end(); ++i) { + model->ResetDateGroupModified(*i); + } + + if (show_bookmark_toolbar) + ShowBookmarkBar(); +} + +void ProfileWriter::AddFavicons( + const std::vector& favicons) { + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> + SetImportedFavicons(favicons); +} + +typedef std::map HostPathMap; + +// 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, it should not be added to HostPathMap. +static std::string BuildHostPathKey(const TemplateURL* t_url) { + if (t_url->url() && t_url->url()->SupportsReplacement()) { + GURL search_url(t_url->url()->ReplaceSearchTerms( + *t_url, L"random string", TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, + std::wstring())); + if (search_url.is_valid()) + return search_url.host() + search_url.path(); + } + return std::string(); +} + +// Builds a set that contains an entry of the host+path for each TemplateURL in +// the TemplateURLModel that has a valid search url. +static void BuildHostPathMap(const TemplateURLModel& model, + HostPathMap* host_path_map) { + std::vector template_urls = model.GetTemplateURLs(); + for (size_t i = 0; i < template_urls.size(); ++i) { + const std::string host_path = BuildHostPathKey(template_urls[i]); + 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(const std::vector& template_urls, + int default_keyword_index, + bool unique_on_host_and_path) { + TemplateURLModel* model = profile_->GetTemplateURLModel(); + HostPathMap host_path_map; + if (unique_on_host_and_path) + BuildHostPathMap(*model, &host_path_map); + + for (std::vector::const_iterator i = template_urls.begin(); + i != template_urls.end(); ++i) { + TemplateURL* t_url = *i; + bool default_keyword = + default_keyword_index >= 0 && + (i - template_urls.begin() == default_keyword_index); + + // TemplateURLModel requires keywords to be unique. If there is already a + // TemplateURL with this keyword, don't import it again. + const TemplateURL* turl_with_keyword = + model->GetTemplateURLForKeyword(t_url->keyword()); + if (turl_with_keyword != NULL) { + if (default_keyword) + model->SetDefaultSearchProvider(turl_with_keyword); + delete t_url; + 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(t_url)) != host_path_map.end()) { + if (default_keyword) { + const TemplateURL* turl_with_host_path = + host_path_map[BuildHostPathKey(t_url)]; + if (turl_with_host_path) + model->SetDefaultSearchProvider(turl_with_host_path); + else + NOTREACHED(); // BuildHostPathMap should only insert non-null values. + } + delete t_url; + continue; + } + model->Add(t_url); + if (default_keyword) + model->SetDefaultSearchProvider(t_url); + } +} + +void ProfileWriter::ShowBookmarkBar() { + DCHECK(profile_); + + PrefService* prefs = profile_->GetPrefs(); + // Check whether the bookmark bar is shown in current pref. + if (!prefs->GetBoolean(prefs::kShowBookmarkBar)) { + // Set the pref and notify the notification service. + prefs->SetBoolean(prefs::kShowBookmarkBar, true); + prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread()); + Source source(profile_); + NotificationService::current()->Notify( + NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, source, + NotificationService::NoDetails()); + } +} + +// Importer. + +// static +bool Importer::ReencodeFavicon(const unsigned char* src_data, size_t src_len, + std::vector* png_data) { + // Decode the favicon using WebKit's image decoder. + webkit_glue::ImageDecoder decoder(gfx::Size(kFavIconSize, kFavIconSize)); + SkBitmap decoded = decoder.Decode(src_data, src_len); + if (decoded.empty()) + return false; // Unable to decode. + + if (decoded.width() != kFavIconSize || decoded.height() != kFavIconSize) { + // The bitmap is not the correct size, re-sample. + int new_width = decoded.width(); + int new_height = decoded.height(); + calc_favicon_target_size(&new_width, &new_height); + decoded = gfx::ImageOperations::Resize( + decoded, gfx::ImageOperations::RESIZE_LANCZOS3, + gfx::Size(new_width, new_height)); + } + + // Encode our bitmap as a PNG. + SkAutoLockPixels decoded_lock(decoded); + PNGEncoder::Encode(reinterpret_cast(decoded.getPixels()), + PNGEncoder::FORMAT_BGRA, decoded.width(), + decoded.height(), decoded.width() * 4, false, png_data); + return true; +} + +// ImporterHost. + +ImporterHost::ImporterHost() + : observer_(NULL), + task_(NULL), + importer_(NULL), + file_loop_(g_browser_process->file_thread()->message_loop()), + waiting_for_bookmarkbar_model_(false), + waiting_for_template_url_model_(false), + is_source_readable_(true) { + DetectSourceProfiles(); +} + +ImporterHost::ImporterHost(MessageLoop* file_loop) + : observer_(NULL), + task_(NULL), + importer_(NULL), + file_loop_(file_loop), + waiting_for_bookmarkbar_model_(false), + waiting_for_template_url_model_(false), + is_source_readable_(true) { + DetectSourceProfiles(); +} + +ImporterHost::~ImporterHost() { + STLDeleteContainerPointers(source_profiles_.begin(), source_profiles_.end()); +} + +void ImporterHost::Loaded(BookmarkModel* model) { + model->RemoveObserver(this); + waiting_for_bookmarkbar_model_ = false; + InvokeTaskIfDone(); +} + +void ImporterHost::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == TEMPLATE_URL_MODEL_LOADED); + TemplateURLModel* model = Source(source).ptr(); + NotificationService::current()->RemoveObserver( + this, TEMPLATE_URL_MODEL_LOADED, + Source(model)); + waiting_for_template_url_model_ = false; + InvokeTaskIfDone(); +} + +void ImporterHost::ShowWarningDialog() { + ChromeViews::Window::CreateChromeWindow(GetActiveWindow(), gfx::Rect(), + new ImporterLockView(this))->Show(); +} + +void ImporterHost::OnLockViewEnd(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; + InvokeTaskIfDone(); + } else { + ShowWarningDialog(); + } + } else { + // User chose to skip the import process. We should delete + // the task and notify the ImporterHost to finish. + delete task_; + task_ = NULL; + importer_ = NULL; + ImportEnded(); + } +} + +void ImporterHost::StartImportSettings(const ProfileInfo& profile_info, + uint16 items, + ProfileWriter* writer, + bool first_run) { + // Preserves the observer and creates a task, since we do async import + // so that it doesn't block the UI. When the import is complete, observer + // will be notified. + writer_ = writer; + importer_ = CreateImporterByType(profile_info.browser_type); + importer_->set_first_run(first_run); + task_ = NewRunnableMethod(importer_, &Importer::StartImport, + profile_info, items, writer_.get(), this); + + // We should lock the Firefox profile directory to prevent corruption. + if (profile_info.browser_type == FIREFOX2 || + profile_info.browser_type == FIREFOX3) { + firefox_lock_.reset(new FirefoxProfileLock(profile_info.source_path)); + if (!firefox_lock_->HasAcquired()) { + // If fail to acquire the lock, we set the source unreadable and + // show a warning dialog. + is_source_readable_ = false; + ShowWarningDialog(); + } + } + + // 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 & FAVORITES) && !writer_->BookmarkModelIsLoaded()) { + writer_->AddBookmarkModelObserver(this); + waiting_for_bookmarkbar_model_ = true; + } + + // Observes the TemplateURLModel 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 & SEARCH_ENGINES) || (items & FAVORITES)) { + if (!writer_->TemplateURLModelIsLoaded()) { + writer_->AddTemplateURLModelObserver(this); + waiting_for_template_url_model_ = true; + } + } + + AddRef(); + InvokeTaskIfDone(); +} + +void ImporterHost::Cancel() { + if (importer_) + importer_->Cancel(); +} + +void ImporterHost::SetObserver(Observer* observer) { + observer_ = observer; +} + +void ImporterHost::InvokeTaskIfDone() { + if (waiting_for_bookmarkbar_model_ || waiting_for_template_url_model_ || + !is_source_readable_) + return; + file_loop_->PostTask(FROM_HERE, task_); +} + +void ImporterHost::ImportItemStarted(ImportItem item) { + if (observer_) + observer_->ImportItemStarted(item); +} + +void ImporterHost::ImportItemEnded(ImportItem item) { + if (observer_) + observer_->ImportItemEnded(item); +} + +void ImporterHost::ImportStarted() { + if (observer_) + observer_->ImportStarted(); +} + +void ImporterHost::ImportEnded() { + firefox_lock_.reset(); // Release the Firefox profile lock. + if (observer_) + observer_->ImportEnded(); + Release(); +} + +Importer* ImporterHost::CreateImporterByType(ProfileType type) { + switch (type) { + case MS_IE: + return new IEImporter(); + case FIREFOX2: + return new Firefox2Importer(); + case FIREFOX3: + return new Firefox3Importer(); + } + NOTREACHED(); + return NULL; +} + +int ImporterHost::GetAvailableProfileCount() { + return static_cast(source_profiles_.size()); +} + +std::wstring ImporterHost::GetSourceProfileNameAt(int index) const { + DCHECK(index < static_cast(source_profiles_.size())); + return source_profiles_[index]->description; +} + +const ProfileInfo& ImporterHost::GetSourceProfileInfoAt(int index) const { + DCHECK(index < static_cast(source_profiles_.size())); + return *source_profiles_[index]; +} + +void ImporterHost::DetectSourceProfiles() { + if (ShellIntegration::IsFirefoxDefaultBrowser()) { + DetectFirefoxProfiles(); + DetectIEProfiles(); + } else { + DetectIEProfiles(); + DetectFirefoxProfiles(); + } +} + +void ImporterHost::DetectIEProfiles() { + // IE always exists and don't have multiple profiles. + ProfileInfo* ie = new ProfileInfo(); + ie->description = l10n_util::GetString(IDS_IMPORT_FROM_IE); + ie->browser_type = MS_IE; + ie->source_path.clear(); + ie->app_path.clear(); + source_profiles_.push_back(ie); +} + +void ImporterHost::DetectFirefoxProfiles() { + // Detects which version of Firefox is installed. + int version = GetCurrentFirefoxMajorVersion(); + ProfileType firefox_type; + if (version == 2) { + firefox_type = FIREFOX2; + } else if (version == 3) { + firefox_type = FIREFOX3; + } else { + // Ignores other versions of firefox. + return; + } + + std::wstring ini_file = GetProfilesINI(); + DictionaryValue root; + ParseProfileINI(ini_file, &root); + + std::wstring source_path; + for (int i = 0; ; ++i) { + std::wstring current_profile = L"Profile" + IntToWString(i); + if (!root.HasKey(current_profile)) { + // Profiles are continuously numbered. So we exit when we can't + // find the i-th one. + break; + } + std::wstring is_relative, path, profile_path; + if (root.GetString(current_profile + L".IsRelative", &is_relative) && + root.GetString(current_profile + L".Path", &path)) { + ReplaceSubstringsAfterOffset(&path, 0, L"/", L"\\"); + + // 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 == L"1") { + profile_path = file_util::GetDirectoryFromPath(ini_file); + file_util::AppendToPath(&profile_path, path); + } else { + profile_path = path; + } + + // We only import the default profile when multiple profiles exist, + // since the other profiles are used mostly by developers for testing. + // Otherwise, Profile0 will be imported. + std::wstring is_default; + if ((root.GetString(current_profile + L".Default", &is_default) && + is_default == L"1") || i == 0) { + source_path = profile_path; + // We break out of the loop when we have found the default profile. + if (is_default == L"1") + break; + } + } + } + + if (!source_path.empty()) { + ProfileInfo* firefox = new ProfileInfo(); + firefox->description = l10n_util::GetString(IDS_IMPORT_FROM_FIREFOX); + firefox->browser_type = firefox_type; + firefox->source_path = source_path; + firefox->app_path = GetFirefoxInstallPath(); + source_profiles_.push_back(firefox); + } +} + diff --git a/chrome/browser/importer/importer.h b/chrome/browser/importer/importer.h new file mode 100644 index 0000000..37be870 --- /dev/null +++ b/chrome/browser/importer/importer.h @@ -0,0 +1,357 @@ +// Copyright (c) 2006-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. + +#ifndef CHROME_BROWSER_IMPORTER_IMPORTER_H_ +#define CHROME_BROWSER_IMPORTER_IMPORTER_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/ie7_password.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/template_url.h" +#include "chrome/browser/views/importer_lock_view.h" +#include "chrome/common/notification_service.h" +#include "googleurl/src/gurl.h" +#include "webkit/glue/password_form.h" + +// An enumeration of the type of browsers that we support to import +// settings and data from them. +enum ProfileType { + MS_IE = 0, + FIREFOX2, + FIREFOX3 +}; + +// An enumeration of the type of data we want to import. +enum ImportItem { + NONE = 0x0000, + HISTORY = 0x0001, + FAVORITES = 0x0002, + COOKIES = 0x0004, // not supported yet. + PASSWORDS = 0x0008, + SEARCH_ENGINES = 0x0010, + HOME_PAGE = 0x0020, +}; + +typedef struct { + std::wstring description; + ProfileType browser_type; + std::wstring source_path; + std::wstring app_path; +} ProfileInfo; + +class FirefoxProfileLock; +class Importer; + +// ProfileWriter encapsulates profile for writing entries into it. +// This object must be invoked on UI thread. +class ProfileWriter : public base::RefCounted { + public: + explicit ProfileWriter(Profile* profile) : profile_(profile) { } + virtual ~ProfileWriter() { } + + // Methods for monitoring BookmarkModel status. + virtual bool BookmarkModelIsLoaded() const; + virtual void AddBookmarkModelObserver( + BookmarkModelObserver* observer); + + // Methods for monitoring TemplateURLModel status. + virtual bool TemplateURLModelIsLoaded() const; + virtual void AddTemplateURLModelObserver( + NotificationObserver* observer); + + // A bookmark entry. + struct BookmarkEntry { + bool in_toolbar; + GURL url; + std::vector path; + std::wstring title; + Time creation_time; + + BookmarkEntry() : in_toolbar(false) {} + }; + + // Helper methods for adding data to local stores. + virtual void AddPasswordForm(const PasswordForm& form); + virtual void AddIE7PasswordInfo(const IE7PasswordInfo& info); + virtual void AddHistoryPage(const std::vector& page); + virtual void AddHomepage(const GURL& homepage); + virtual void AddBookmarkEntry(const std::vector& bookmark); + virtual void AddFavicons( + const std::vector& favicons); + // Add the TemplateURLs in |template_urls| to the local store and make the + // TemplateURL at |default_keyword_index| the default keyword (does not set + // a default keyword if it is -1). 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 takes precedence and the + // duplicate in |template_urls| are deleted. + // If unique_on_host_and_path 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(const std::vector& template_urls, + int default_keyword_index, + bool unique_on_host_and_path); + + // Shows the bookmarks toolbar. + void ShowBookmarkBar(); + + private: + Profile* profile_; + + DISALLOW_EVIL_CONSTRUCTORS(ProfileWriter); +}; + +// This class hosts the importers. It enumerates profiles from other +// browsers dynamically, and controls the process of importing. When +// the import process is done, ImporterHost deletes itself. +class ImporterHost : public base::RefCounted, + public BookmarkModelObserver, + public NotificationObserver { + public: + ImporterHost(); + ~ImporterHost(); + + // This constructor only be used by unit-tests, where file thread does not + // exist. + explicit ImporterHost(MessageLoop* file_loop); + + // BookmarkModelObserver methods. + virtual void Loaded(BookmarkModel* model); + virtual void BookmarkNodeMoved(BookmarkModel* model, + BookmarkNode* old_parent, + int old_index, + BookmarkNode* new_parent, + int new_index) {} + virtual void BookmarkNodeAdded(BookmarkModel* model, + BookmarkNode* parent, + int index) {} + virtual void BookmarkNodeRemoved(BookmarkModel* model, + BookmarkNode* parent, + int index) {} + virtual void BookmarkNodeChanged(BookmarkModel* model, + BookmarkNode* node) {} + virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, + BookmarkNode* node) {} + + // NotificationObserver method. Called when TemplateURLModel has been loaded. + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // ShowWarningDialog() asks user to close the application that is owning the + // lock. They can retry or skip the importing process. + void ShowWarningDialog(); + + // OnLockViewEnd() is called when user end the dialog by clicking a push + // button. |is_continue| is true when user clicked the "Continue" button. + void OnLockViewEnd(bool is_continue); + + // Starts the process of importing the settings and data depending + // on what the user selected. + void StartImportSettings(const ProfileInfo& profile_info, + uint16 items, + ProfileWriter* writer, + bool first_run); + + // Cancel + void Cancel(); + + // An interface which an object can implement to be notified of events during + // the import process. + class Observer { + public: + virtual ~Observer() {} + // 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 import begins. + virtual void ImportStarted() = 0; + + // Invoked when the source profile has been imported. + virtual void ImportEnded() = 0; + }; + void SetObserver(Observer* observer); + + // A series of functions invoked at the start, during and end of the end + // of the import process. The middle functions are notifications that the + // harvesting of a particular source of data (specified by |item|) is under + // way. + void ImportStarted(); + void ImportItemStarted(ImportItem item); + void ImportItemEnded(ImportItem item); + void ImportEnded(); + + Importer* CreateImporterByType(ProfileType type); + + // Returns the number of different browser profiles you can import from. + int GetAvailableProfileCount(); + + // Returns the name of the profile at the 'index' slot. The profiles are + // ordered such that the profile at index 0 is the likely default browser. + std::wstring GetSourceProfileNameAt(int index) const; + + // Returns the ProfileInfo at the specified index. The ProfileInfo should be + // passed to StartImportSettings(). + const ProfileInfo& GetSourceProfileInfoAt(int index) const; + + private: + // If we're not waiting on any model to finish loading, invokes the task_. + void InvokeTaskIfDone(); + + // Detects the installed browsers and their associated profiles, then + // stores their information in a list. It returns the list of description + // of all profiles. + void DetectSourceProfiles(); + + // Helper methods for detecting available profiles. + void DetectIEProfiles(); + void DetectFirefoxProfiles(); + + // The list of profiles with the default one first. + std::vector source_profiles_; + + Observer* observer_; + scoped_refptr writer_; + + // The task is the process of importing settings from other browsers. + Task* task_; + + // The importer used in the task; + Importer* importer_; + + // The message loop for reading the source profiles. + MessageLoop* file_loop_; + + // True if we're waiting for the model to finish loading. + bool waiting_for_bookmarkbar_model_; + bool waiting_for_template_url_model_; + + // True if source profile is readable. + bool is_source_readable_; + + // Firefox profile lock. + scoped_ptr firefox_lock_; + + DISALLOW_EVIL_CONSTRUCTORS(ImporterHost); +}; + +// The base class of all importers. +class Importer : public base::RefCounted { + public: + virtual ~Importer() { } + + // 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::Finished() to notify its host that import + // stuff have been finished. + virtual void StartImport(ProfileInfo profile_info, + uint16 items, + ProfileWriter* writer, + ImporterHost* host) = 0; + + // Cancels the import process. + void Cancel() { cancelled_ = true; } + + void set_first_run(bool first_run) { first_run_ = first_run; } + + protected: + Importer() + : main_loop_(MessageLoop::current()), + importer_host_(NULL), + cancelled_(false) {} + + // Notifies the coordinator that the collection of data for the specified + // item has begun. + void NotifyItemStarted(ImportItem item) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(importer_host_, + &ImporterHost::ImportItemStarted, item)); + } + + // Notifies the coordinator that the collection of data for the specified + // item has completed. + void NotifyItemEnded(ImportItem item) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(importer_host_, + &ImporterHost::ImportItemEnded, item)); + } + + // Notifies the coordinator that the import operation has begun. + void NotifyStarted() { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(importer_host_, + &ImporterHost::ImportStarted)); + } + + // Notifies the coordinator that the entire import operation has completed. + void NotifyEnded() { + main_loop_->PostTask(FROM_HERE, + NewRunnableMethod(importer_host_, &ImporterHost::ImportEnded)); + } + + // 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. + static bool ReencodeFavicon(const unsigned char* src_data, size_t src_len, + std::vector* png_data); + + bool cancelled() const { return cancelled_; } + + bool first_run() const { return first_run_; } + + // The importer should know the main thread so that ProfileWriter + // will be invoked in thread instead. + MessageLoop* main_loop_; + + // The coordinator host for this importer. + ImporterHost* importer_host_; + + private: + // True if the caller cancels the import process. + bool cancelled_; + + // True if the importer is created in the first run UI. + bool first_run_; + + DISALLOW_EVIL_CONSTRUCTORS(Importer); +}; + +// An interface an object that calls StartImportingWithUI can call to be +// notified about the state of the import operation. +class ImportObserver { + public: + virtual ~ImportObserver() {} + // The import operation was canceled by the user. + virtual void ImportCanceled() = 0; + + // The import operation was completed successfully. + virtual void ImportComplete() = 0; +}; + + +// Shows a UI for importing and begins importing the specified items from +// source_profile to target_profile. observer is notified when the process is +// complete, can be NULL. parent is the window to parent the UI to, can be NULL +// if there's nothing to parent to. first_run is true if it's invoked in the +// first run UI. +void StartImportingWithUI(HWND parent_window, + int16 items, + ImporterHost* coordinator, + const ProfileInfo& source_profile, + Profile* target_profile, + ImportObserver* observer, + bool first_run); + +#endif // CHROME_BROWSER_IMPORTER_IMPORTER_H_ + diff --git a/chrome/browser/importer/importer_unittest.cc b/chrome/browser/importer/importer_unittest.cc new file mode 100644 index 0000000..e7f097e --- /dev/null +++ b/chrome/browser/importer/importer_unittest.cc @@ -0,0 +1,819 @@ +// Copyright (c) 2006-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 "testing/gtest/include/gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "base/file_util.h" +#include "base/path_service.h" +#include "chrome/browser/ie7_password.h" +#include "chrome/browser/importer/ie_importer.h" +#include "chrome/browser/importer/importer.h" +#include "chrome/browser/profile.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/env_util.h" +#include "chrome/common/win_util.h" + +class ImporterTest : public testing::Test { + public: + protected: + virtual void SetUp() { + // Creates a new profile in a new subdirectory in the temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_path_)); + file_util::AppendToPath(&test_path_, L"ImporterTest"); + file_util::Delete(test_path_, true); + CreateDirectory(test_path_.c_str(), NULL); + profile_path_ = test_path_; + file_util::AppendToPath(&profile_path_, L"profile"); + CreateDirectory(profile_path_.c_str(), NULL); + app_path_ = test_path_; + file_util::AppendToPath(&app_path_, L"app"); + CreateDirectory(app_path_.c_str(), NULL); + } + + virtual void TearDown() { + // Deletes the profile and cleans up the profile directory. + ASSERT_TRUE(file_util::Delete(test_path_, true)); + ASSERT_FALSE(file_util::PathExists(test_path_)); + } + + MessageLoopForUI message_loop_; + std::wstring test_path_; + std::wstring profile_path_; + std::wstring app_path_; +}; + +const int kMaxPathSize = 5; + +typedef struct { + const bool in_toolbar; + const int path_size; + const wchar_t* path[kMaxPathSize]; + const wchar_t* title; + const char* url; +} BookmarkList; + +typedef struct { + const char* origin; + const char* action; + const char* realm; + const wchar_t* username_element; + const wchar_t* username; + const wchar_t* password_element; + const wchar_t* password; + bool blacklisted; +} PasswordList; + +static const BookmarkList kIEBookmarks[] = { + {true, 0, {}, + L"TheLink", + "http://www.links-thelink.com/"}, + {true, 1, {L"SubFolderOfLinks"}, + L"SubLink", + "http://www.links-sublink.com/"}, + {false, 0, {}, + L"Google Home Page", + "http://www.google.com/"}, + {false, 0, {}, + L"TheLink", + "http://www.links-thelink.com/"}, + {false, 1, {L"SubFolder"}, + L"Title", + "http://www.link.com/"}, + {false, 0, {}, + L"WithPortAndQuery", + "http://host:8080/cgi?q=query"}, + {false, 1, {L"a"}, + L"\x4E2D\x6587", + "http://chinese-title-favorite/"}, +}; + +static const wchar_t* kIEIdentifyUrl = + L"http://A79029D6-753E-4e27-B807-3D46AB1545DF.com:8080/path?key=value"; +static const wchar_t* kIEIdentifyTitle = + L"Unittest GUID"; + +bool IsWindowsVista() { + OSVERSIONINFO info = {0}; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&info); + return (info.dwMajorVersion >=6); +} + +// Returns true if the |entry| is in the |list|. +bool FindBookmarkEntry(const ProfileWriter::BookmarkEntry& entry, + const BookmarkList* list, int list_size) { + for (int i = 0; i < list_size; ++i) + if (list[i].in_toolbar == entry.in_toolbar && + list[i].path_size == entry.path.size() && + list[i].url == entry.url.spec() && + list[i].title == entry.title) { + bool equal = true; + for (int k = 0; k < list[i].path_size; ++k) + if (list[i].path[k] != entry.path[k]) { + equal = false; + break; + } + + if (equal) + return true; + } + return false; +} + +class TestObserver : public ProfileWriter, + public ImporterHost::Observer { + public: + TestObserver() : ProfileWriter(NULL) { + bookmark_count_ = 0; + history_count_ = 0; + password_count_ = 0; + } + + virtual void ImportItemStarted(ImportItem item) {} + virtual void ImportItemEnded(ImportItem item) {} + virtual void ImportStarted() {} + virtual void ImportEnded() { + MessageLoop::current()->Quit(); + EXPECT_EQ(arraysize(kIEBookmarks), bookmark_count_); + EXPECT_EQ(1, history_count_); + if (IsWindowsVista()) + EXPECT_EQ(0, password_count_); + else + EXPECT_EQ(1, password_count_); + } + + virtual bool BookmarkModelIsLoaded() const { + // Profile is ready for writing. + return true; + } + + virtual void AddBookmarkModelObserver(BookmarkModelObserver* observer) { + NOTREACHED(); + } + + virtual bool TemplateURLModelIsLoaded() const { + return true; + } + + virtual void AddTemplateURLModelObserver(NotificationObserver* observer) { + NOTREACHED(); + } + + virtual void AddPasswordForm(const PasswordForm& form) { + // Importer should obtain this password form only. + EXPECT_EQ(GURL("http://localhost:8080/security/index.htm"), form.origin); + EXPECT_EQ("http://localhost:8080/", form.signon_realm); + EXPECT_EQ(L"user", form.username_element); + EXPECT_EQ(L"1", form.username_value); + EXPECT_EQ(L"", form.password_element); + EXPECT_EQ(L"2", form.password_value); + EXPECT_EQ("", form.action.spec()); + ++password_count_; + } + + virtual void AddHistoryPage(const std::vector& page) { + // Importer should read the specified URL. + for (size_t i = 0; i < page.size(); ++i) + if (page[i].title() == kIEIdentifyTitle && + page[i].url() == GURL(kIEIdentifyUrl)) + ++history_count_; + } + + virtual void AddBookmarkEntry(const std::vector& bookmark) { + // Importer should import the IE Favorites folder the same as the list. + for (size_t i = 0; i < bookmark.size(); ++i) { + if (FindBookmarkEntry(bookmark[i], kIEBookmarks, + arraysize(kIEBookmarks))) + ++bookmark_count_; + } + } + + virtual void AddKeyword(std::vector template_url, + int default_keyword_index) { + // TODO(jcampan): bug 1169230: we should test keyword importing for IE. + // In order to do that we'll probably need to mock the Windows registry. + NOTREACHED(); + STLDeleteContainerPointers(template_url.begin(), template_url.end()); + } + + private: + int bookmark_count_; + int history_count_; + int password_count_; +}; + +bool CreateUrlFile(std::wstring file, std::wstring url) { + CComPtr locator; + HRESULT result = locator.CoCreateInstance(CLSID_InternetShortcut, NULL, + CLSCTX_INPROC_SERVER); + if (FAILED(result)) + return false; + CComPtr persist_file; + result = locator->QueryInterface(IID_IPersistFile, + reinterpret_cast(&persist_file)); + if (FAILED(result)) + return false; + result = locator->SetURL(url.c_str(), 0); + if (FAILED(result)) + return false; + result = persist_file->Save(file.c_str(), TRUE); + if (FAILED(result)) + return false; + return true; +} + +void ClearPStoreType(IPStore* pstore, const GUID* type, const GUID* subtype) { + CComPtr item; + HRESULT result = pstore->EnumItems(0, type, subtype, 0, &item); + if (result == PST_E_OK) { + wchar_t* item_name; + while (SUCCEEDED(item->Next(1, &item_name, 0))) { + pstore->DeleteItem(0, type, subtype, item_name, NULL, 0); + CoTaskMemFree(item_name); + } + } + pstore->DeleteSubtype(0, type, subtype, 0); + pstore->DeleteType(0, type, 0); +} + +void WritePStore(IPStore* pstore, const GUID* type, const GUID* subtype) { + struct PStoreItem { + wchar_t* name; + int data_size; + char* data; + } items[] = { + {L"http://localhost:8080/security/index.htm#ref:StringData", 8, + "\x31\x00\x00\x00\x32\x00\x00\x00"}, + {L"http://localhost:8080/security/index.htm#ref:StringIndex", 20, + "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00" + "\x00\x00\x2f\x00\x74\x00\x01\x00\x00\x00"}, + {L"user:StringData", 4, + "\x31\x00\x00\x00"}, + {L"user:StringIndex", 20, + "\x57\x49\x43\x4b\x18\x00\x00\x00\x01\x00" + "\x00\x00\x2f\x00\x74\x00\x00\x00\x00\x00"}, + }; + + for (int i = 0; i < arraysize(items); ++i) { + HRESULT res = pstore->WriteItem(0, type, subtype, items[i].name, + items[i].data_size, reinterpret_cast(items[i].data), + NULL, 0, 0); + ASSERT_TRUE(res == PST_E_OK); + } +} + +TEST_F(ImporterTest, IEImporter) { + // Skips in Win2000 for the running environment can not be set up. + if (env_util::GetOperatingSystemVersion() == "5.0") + return; + + // Sets up a favorites folder. + win_util::ScopedCOMInitializer com_init; + std::wstring path = test_path_; + file_util::AppendToPath(&path, L"Favorites"); + CreateDirectory(path.c_str(), NULL); + CreateDirectory((path + L"\\SubFolder").c_str(), NULL); + CreateDirectory((path + L"\\Links").c_str(), NULL); + CreateDirectory((path + L"\\Links\\SubFolderOfLinks").c_str(), NULL); + CreateDirectory((path + L"\\\x0061").c_str(), NULL); + ASSERT_TRUE(CreateUrlFile(path + L"\\Google Home Page.url", + L"http://www.google.com/")); + ASSERT_TRUE(CreateUrlFile(path + L"\\SubFolder\\Title.url", + L"http://www.link.com/")); + ASSERT_TRUE(CreateUrlFile(path + L"\\TheLink.url", + L"http://www.links-thelink.com/")); + ASSERT_TRUE(CreateUrlFile(path + L"\\WithPortAndQuery.url", + L"http://host:8080/cgi?q=query")); + ASSERT_TRUE(CreateUrlFile(path + L"\\\x0061\\\x4E2D\x6587.url", + L"http://chinese-title-favorite/")); + ASSERT_TRUE(CreateUrlFile(path + L"\\Links\\TheLink.url", + L"http://www.links-thelink.com/")); + ASSERT_TRUE(CreateUrlFile(path + L"\\Links\\SubFolderOfLinks\\SubLink.url", + L"http://www.links-sublink.com/")); + file_util::WriteFile(path + L"\\InvalidUrlFile.url", "x", 1); + file_util::WriteFile(path + L"\\PlainTextFile.txt", "x", 1); + + // Sets up dummy password data. + HRESULT res; + CComPtr pstore; + HMODULE pstorec_dll; + GUID type = IEImporter::kUnittestGUID; + GUID subtype = IEImporter::kUnittestGUID; + // PStore is read-only in Windows Vista. + if (!IsWindowsVista()) { + typedef HRESULT (WINAPI *PStoreCreateFunc)(IPStore**, DWORD, DWORD, DWORD); + pstorec_dll = LoadLibrary(L"pstorec.dll"); + PStoreCreateFunc PStoreCreateInstance = + (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance"); + res = PStoreCreateInstance(&pstore, 0, 0, 0); + ASSERT_TRUE(res == S_OK); + ClearPStoreType(pstore, &type, &subtype); + PST_TYPEINFO type_info; + type_info.szDisplayName = L"TestType"; + type_info.cbSize = 8; + pstore->CreateType(0, &type, &type_info, 0); + pstore->CreateSubtype(0, &type, &subtype, &type_info, NULL, 0); + WritePStore(pstore, &type, &subtype); + } + + // Sets up a special history link. + CComPtr url_history_stg2; + res = url_history_stg2.CoCreateInstance(CLSID_CUrlHistory, NULL, + CLSCTX_INPROC_SERVER); + ASSERT_TRUE(res == S_OK); + res = url_history_stg2->AddUrl(kIEIdentifyUrl, kIEIdentifyTitle, 0); + ASSERT_TRUE(res == S_OK); + + // Starts to import the above settings. + MessageLoop* loop = MessageLoop::current(); + scoped_refptr host = new ImporterHost(loop); + + TestObserver* observer = new TestObserver(); + host->SetObserver(observer); + ProfileInfo profile_info; + profile_info.browser_type = MS_IE; + profile_info.source_path = test_path_; + + loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), + &ImporterHost::StartImportSettings, profile_info, + HISTORY | PASSWORDS | FAVORITES, observer, true)); + loop->Run(); + + // Cleans up. + url_history_stg2->DeleteUrl(kIEIdentifyUrl, 0); + url_history_stg2.Release(); + if (!IsWindowsVista()) { + ClearPStoreType(pstore, &type, &subtype); + // Releases it befor unload the dll. + pstore.Release(); + FreeLibrary(pstorec_dll); + } +} + +TEST_F(ImporterTest, IE7Importer) { + // This is the unencrypted values of my keys under Storage2. + // The passwords have been manually changed to abcdef... but the size remains + // the same. + unsigned char data1[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00" + "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00" + "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01" + "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76" + "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00" + "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00" + "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00" + "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00" + "\x6c\x00\x00\x00"; + + unsigned char data2[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x24\x00\x00\x00" + "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00" + "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\xa8\xea\xf4\xe5\x9f\x9a\xc8\x01" + "\x09\x00\x00\x00\x14\x00\x00\x00\xa8\xea\xf4\xe5" + "\x9f\x9a\xc8\x01\x07\x00\x00\x00\x61\x00\x62\x00" + "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00" + "\x69\x00\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00" + "\x65\x00\x66\x00\x67\x00\x00\x00"; + + + + std::vector decrypted_data1; + decrypted_data1.resize(arraysize(data1)); + memcpy(&decrypted_data1.front(), data1, sizeof(data1)); + + std::vector decrypted_data2; + decrypted_data2.resize(arraysize(data2)); + memcpy(&decrypted_data2.front(), data2, sizeof(data2)); + + std::wstring password; + std::wstring username; + ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data1, &username, + &password)); + EXPECT_EQ(L"abcdefgh", username); + EXPECT_EQ(L"abcdefghijkl", password); + + ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data2, &username, + &password)); + EXPECT_EQ(L"abcdefghi", username); + EXPECT_EQ(L"abcdefg", password); +} + +static const BookmarkList kFirefox2Bookmarks[] = { + {true, 1, {L"Folder"}, + L"On Toolbar's Subfolder", + "http://on.toolbar/bookmark/folder"}, + {true, 0, {}, + L"On Bookmark Toolbar", + "http://on.toolbar/bookmark"}, + {false, 1, {L"Folder"}, + L"New Bookmark", + "http://domain/"}, + {false, 0, {}, + L"", + "http://domain.com/q?a=\"er\"&b=%3C%20%20%3E"}, + {false, 0, {}, + L"Google Home Page", + "http://www.google.com/"}, + {false, 0, {}, + L"\x4E2D\x6587", + "http://chinese.site.cn/path?query=1#ref"}, + {false, 0, {}, + L"mail", + "mailto:username@host"}, +}; + +static const PasswordList kFirefox2Passwords[] = { + {"https://www.google.com/", "", "https://www.google.com/", + L"", L"", L"", L"", true}, + {"http://localhost:8080/", "", "http://localhost:8080/corp.google.com", + L"", L"http", L"", L"Http1+1abcdefg", false}, + {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", + L"loginuser", L"usr", L"loginpass", L"pwd", false}, + {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", + L"loginuser", L"firefox", L"loginpass", L"firefox", false}, + {"http://localhost/", "", "http://localhost/", + L"loginuser", L"hello", L"", L"world", false}, +}; + +typedef struct { + const wchar_t* keyword; + const wchar_t* url; +} KeywordList; + +static const KeywordList kFirefox2Keywords[] = { + // Searh plugins + { L"amazon.com", + L"http://www.amazon.com/exec/obidos/external-search/?field-keywords=" + L"{searchTerms}&mode=blended" }, + { L"answers.com", + L"http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, + { L"search.creativecommons.org", + L"http://search.creativecommons.org/?q={searchTerms}" }, + { L"search.ebay.com", + L"http://search.ebay.com/search/search.dll?query={searchTerms}&" + L"MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" + L"maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, + { L"google.com", + L"http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, + { L"search.yahoo.com", + L"http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, + { L"flickr.com", + L"http://www.flickr.com/photos/tags/?q={searchTerms}" }, + { L"imdb.com", + L"http://www.imdb.com/find?q={searchTerms}" }, + { L"webster.com", + L"http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, + // Search keywords. + { L"google", L"http://www.google.com/" }, + { L"< > & \" ' \\ /", L"http://g.cn/"}, +}; + +static const int kDefaultFirefox2KeywordIndex = 8; + +class FirefoxObserver : public ProfileWriter, + public ImporterHost::Observer { + public: + FirefoxObserver() : ProfileWriter(NULL) { + bookmark_count_ = 0; + history_count_ = 0; + password_count_ = 0; + keyword_count_ = 0; + } + + virtual void ImportItemStarted(ImportItem item) {} + virtual void ImportItemEnded(ImportItem item) {} + virtual void ImportStarted() {} + virtual void ImportEnded() { + MessageLoop::current()->Quit(); + EXPECT_EQ(arraysize(kFirefox2Bookmarks), bookmark_count_); + EXPECT_EQ(1, history_count_); + EXPECT_EQ(arraysize(kFirefox2Passwords), password_count_); + EXPECT_EQ(arraysize(kFirefox2Keywords), keyword_count_); + EXPECT_EQ(kFirefox2Keywords[kDefaultFirefox2KeywordIndex].keyword, + default_keyword_); + EXPECT_EQ(kFirefox2Keywords[kDefaultFirefox2KeywordIndex].url, + default_keyword_url_); + } + + virtual bool BookmarkModelIsLoaded() const { + // Profile is ready for writing. + return true; + } + + virtual void AddBookmarkModelObserver(BookmarkModelObserver* observer) { + NOTREACHED(); + } + + virtual bool TemplateURLModelIsLoaded() const { + return true; + } + + virtual void AddTemplateURLModelObserver(NotificationObserver* observer) { + NOTREACHED(); + } + + virtual void AddPasswordForm(const PasswordForm& form) { + PasswordList p = kFirefox2Passwords[password_count_]; + EXPECT_EQ(p.origin, form.origin.spec()); + EXPECT_EQ(p.realm, form.signon_realm); + EXPECT_EQ(p.action, form.action.spec()); + EXPECT_EQ(p.username_element, form.username_element); + EXPECT_EQ(p.username, form.username_value); + EXPECT_EQ(p.password_element, form.password_element); + EXPECT_EQ(p.password, form.password_value); + EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); + ++password_count_; + } + + virtual void AddHistoryPage(const std::vector& page) { + EXPECT_EQ(1, page.size()); + EXPECT_EQ("http://en-us.www.mozilla.com/", page[0].url().spec()); + EXPECT_EQ(L"Firefox Updated", page[0].title()); + ++history_count_; + } + + virtual void AddBookmarkEntry(const std::vector& bookmark) { + for (size_t i = 0; i < bookmark.size(); ++i) { + if (FindBookmarkEntry(bookmark[i], kFirefox2Bookmarks, + arraysize(kFirefox2Bookmarks))) + ++bookmark_count_; + } + } + + virtual void AddKeywords(const std::vector& template_urls, + int default_keyword_index, + bool unique_on_host_and_path) { + for (size_t i = 0; i < template_urls.size(); ++i) { + // The order might not be deterministic, look in the expected list for + // that template URL. + bool found = false; + std::wstring keyword = template_urls[i]->keyword(); + for (int j = 0; j < arraysize(kFirefox2Keywords); ++j) { + const wchar_t* comp_keyword = kFirefox2Keywords[j].keyword; + bool equal = (keyword == comp_keyword); + if (template_urls[i]->keyword() == kFirefox2Keywords[j].keyword) { + EXPECT_EQ(kFirefox2Keywords[j].url, template_urls[i]->url()->url()); + found = true; + break; + } + } + EXPECT_TRUE(found); + ++keyword_count_; + } + + if (default_keyword_index != -1) { + EXPECT_LT(default_keyword_index, static_cast(template_urls.size())); + TemplateURL* default_turl = template_urls[default_keyword_index]; + default_keyword_ = default_turl->keyword(); + default_keyword_url_ = default_turl->url()->url(); + } + + STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); + } + + void AddFavicons(const std::vector& favicons) { + } + + private: + int bookmark_count_; + int history_count_; + int password_count_; + int keyword_count_; + std::wstring default_keyword_; + std::wstring default_keyword_url_; +}; + +TEST_F(ImporterTest, Firefox2Importer) { + std::wstring data_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + file_util::AppendToPath(&data_path, L"firefox2_profile\\*"); + file_util::CopyDirectory(data_path, profile_path_, true); + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + file_util::AppendToPath(&data_path, L"firefox2_nss"); + file_util::CopyDirectory(data_path, profile_path_, false); + + std::wstring search_engine_path = app_path_; + file_util::AppendToPath(&search_engine_path, L"searchplugins"); + CreateDirectory(search_engine_path.c_str(), NULL); + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + file_util::AppendToPath(&data_path, L"firefox2_searchplugins"); + file_util::CopyDirectory(data_path, search_engine_path, false); + + MessageLoop* loop = MessageLoop::current(); + scoped_refptr host = new ImporterHost(loop); + FirefoxObserver* observer = new FirefoxObserver(); + host->SetObserver(observer); + ProfileInfo profile_info; + profile_info.browser_type = FIREFOX2; + profile_info.app_path = app_path_; + profile_info.source_path = profile_path_; + + loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), + &ImporterHost::StartImportSettings, profile_info, + HISTORY | PASSWORDS | FAVORITES | SEARCH_ENGINES, observer, true)); + loop->Run(); +} + +static const BookmarkList kFirefox3Bookmarks[] = { + {true, 0, {}, + L"Toolbar", + "http://site/"}, + {false, 0, {}, + L"Title", + "http://www.google.com/"}, +}; + +static const PasswordList kFirefox3Passwords[] = { + {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", + L"loginuser", L"abc", L"loginpass", L"123", false}, + {"http://localhost:8080/", "", "http://localhost:8080/localhost", + L"", L"http", L"", L"Http1+1abcdefg", false}, +}; + +static const KeywordList kFirefox3Keywords[] = { + { L"amazon.com", + L"http://www.amazon.com/exec/obidos/external-search/?field-keywords=" + L"{searchTerms}&mode=blended" }, + { L"answers.com", + L"http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, + { L"search.creativecommons.org", + L"http://search.creativecommons.org/?q={searchTerms}" }, + { L"search.ebay.com", + L"http://search.ebay.com/search/search.dll?query={searchTerms}&" + L"MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" + L"maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, + { L"google.com", + L"http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, + { L"en.wikipedia.org", + L"http://en.wikipedia.org/wiki/Special:Search?search={searchTerms}" }, + { L"search.yahoo.com", + L"http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, + { L"flickr.com", + L"http://www.flickr.com/photos/tags/?q={searchTerms}" }, + { L"imdb.com", + L"http://www.imdb.com/find?q={searchTerms}" }, + { L"webster.com", + L"http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, + // Search keywords. + { L"\x4E2D\x6587", L"http://www.google.com/" }, +}; + +static const int kDefaultFirefox3KeywordIndex = 8; + +class Firefox3Observer : public ProfileWriter, + public ImporterHost::Observer { + public: + Firefox3Observer() : ProfileWriter(NULL) { + bookmark_count_ = 0; + history_count_ = 0; + password_count_ = 0; + keyword_count_ = 0; + } + + virtual void ImportItemStarted(ImportItem item) {} + virtual void ImportItemEnded(ImportItem item) {} + virtual void ImportStarted() {} + virtual void ImportEnded() { + MessageLoop::current()->Quit(); + EXPECT_EQ(arraysize(kFirefox3Bookmarks), bookmark_count_); + EXPECT_EQ(1, history_count_); + EXPECT_EQ(arraysize(kFirefox3Passwords), password_count_); + EXPECT_EQ(arraysize(kFirefox3Keywords), keyword_count_); + EXPECT_EQ(kFirefox3Keywords[kDefaultFirefox3KeywordIndex].keyword, + default_keyword_); + EXPECT_EQ(kFirefox3Keywords[kDefaultFirefox3KeywordIndex].url, + default_keyword_url_); + } + + virtual bool BookmarkModelIsLoaded() const { + // Profile is ready for writing. + return true; + } + + virtual void AddBookmarkModelObserver(BookmarkModelObserver* observer) { + NOTREACHED(); + } + + virtual bool TemplateURLModelIsLoaded() const { + return true; + } + + virtual void AddTemplateURLModelObserver(NotificationObserver* observer) { + NOTREACHED(); + } + + virtual void AddPasswordForm(const PasswordForm& form) { + PasswordList p = kFirefox3Passwords[password_count_]; + EXPECT_EQ(p.origin, form.origin.spec()); + EXPECT_EQ(p.realm, form.signon_realm); + EXPECT_EQ(p.action, form.action.spec()); + EXPECT_EQ(p.username_element, form.username_element); + EXPECT_EQ(p.username, form.username_value); + EXPECT_EQ(p.password_element, form.password_element); + EXPECT_EQ(p.password, form.password_value); + EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); + ++password_count_; + } + + virtual void AddHistoryPage(const std::vector& page) { + ASSERT_EQ(3, page.size()); + EXPECT_EQ("http://www.google.com/", page[0].url().spec()); + EXPECT_EQ(L"Google", page[0].title()); + EXPECT_EQ("http://www.google.com/", page[1].url().spec()); + EXPECT_EQ(L"Google", page[1].title()); + EXPECT_EQ("http://www.cs.unc.edu/~jbs/resources/perl/perl-cgi/programs/form1-POST.html", + page[2].url().spec()); + EXPECT_EQ(L"example form (POST)", page[2].title()); + ++history_count_; + } + + virtual void AddBookmarkEntry(const std::vector& bookmark) { + for (size_t i = 0; i < bookmark.size(); ++i) { + if (FindBookmarkEntry(bookmark[i], kFirefox3Bookmarks, + arraysize(kFirefox3Bookmarks))) + ++bookmark_count_; + } + } + + void AddKeywords(const std::vector& template_urls, + int default_keyword_index, + bool unique_on_host_and_path) { + for (size_t i = 0; i < template_urls.size(); ++i) { + // The order might not be deterministic, look in the expected list for + // that template URL. + bool found = false; + std::wstring keyword = template_urls[i]->keyword(); + for (int j = 0; j < arraysize(kFirefox3Keywords); ++j) { + const wchar_t* comp_keyword = kFirefox3Keywords[j].keyword; + bool equal = (keyword == comp_keyword); + if (template_urls[i]->keyword() == kFirefox3Keywords[j].keyword) { + EXPECT_EQ(kFirefox3Keywords[j].url, template_urls[i]->url()->url()); + found = true; + break; + } + } + EXPECT_TRUE(found); + ++keyword_count_; + } + + if (default_keyword_index != -1) { + EXPECT_LT(default_keyword_index, static_cast(template_urls.size())); + TemplateURL* default_turl = template_urls[default_keyword_index]; + default_keyword_ = default_turl->keyword(); + default_keyword_url_ = default_turl->url()->url(); + } + + STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); + } + + void AddFavicons(const std::vector& favicons) { + } + + private: + int bookmark_count_; + int history_count_; + int password_count_; + int keyword_count_; + std::wstring default_keyword_; + std::wstring default_keyword_url_; +}; + +TEST_F(ImporterTest, Firefox3Importer) { + std::wstring data_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + file_util::AppendToPath(&data_path, L"firefox3_profile\\*"); + file_util::CopyDirectory(data_path, profile_path_, true); + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + file_util::AppendToPath(&data_path, L"firefox3_nss"); + file_util::CopyDirectory(data_path, profile_path_, false); + + std::wstring search_engine_path = app_path_; + file_util::AppendToPath(&search_engine_path, L"searchplugins"); + CreateDirectory(search_engine_path.c_str(), NULL); + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + file_util::AppendToPath(&data_path, L"firefox3_searchplugins"); + file_util::CopyDirectory(data_path, search_engine_path, false); + + MessageLoop* loop = MessageLoop::current(); + ProfileInfo profile_info; + profile_info.browser_type = FIREFOX3; + profile_info.app_path = app_path_; + profile_info.source_path = profile_path_; + scoped_refptr host = new ImporterHost(loop); + Firefox3Observer* observer = new Firefox3Observer(); + host->SetObserver(observer); + loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), + &ImporterHost::StartImportSettings, profile_info, + HISTORY | PASSWORDS | FAVORITES | SEARCH_ENGINES, observer, true)); + loop->Run(); +} + diff --git a/chrome/browser/importer/mork_reader.cc b/chrome/browser/importer/mork_reader.cc new file mode 100644 index 0000000..3ae4b6c --- /dev/null +++ b/chrome/browser/importer/mork_reader.cc @@ -0,0 +1,581 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** 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 Mork Reader. + * + * The Initial Developer of the Original Code is + * Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner (original author) + * + * 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 ***** */ + +// Source: +// http://mxr.mozilla.org/firefox/source/db/morkreader/nsMorkReader.cpp +// This file has been converted to google style. + +#include "chrome/browser/importer/mork_reader.h" + +#include + +#include "base/logging.h" +#include "base/string_util.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/importer/firefox_importer_utils.h" + +namespace { + +// Convert a hex character (0-9, A-F) to its corresponding byte value. +// Returns -1 if the character is invalid. +inline int HexCharToInt(char c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + return -1; +} + +// Unescape a Mork value. Mork uses $xx escaping to encode non-ASCII +// characters. Additionally, '$' and '\' are backslash-escaped. +// The result of the unescape is in returned. +std::string MorkUnescape(const std::string& input) { + // We optimize for speed over space here -- size the result buffer to + // the size of the source, which is an upper bound on the size of the + // unescaped string. + std::string result; + size_t input_length = input.size(); + result.reserve(input_length); + + for (size_t i = 0; i < input_length; i++) { + char c = input[i]; + if (c == '\\') { + // Escaped literal, slip the backslash, append the next character. + i++; + if (i < input_length) + result.push_back(input[i]); + } else if (c == '$') { + // Dollar sign denotes a hex character. + if (i < input_length - 2) { + // Would be nice to use ToInteger() here, but it currently + // requires a null-terminated string. + int first = HexCharToInt(input[++i]); + int second = HexCharToInt(input[++i]); + if (first >= 0 && second >= 0) + result.push_back((first << 4) | second); + } + } else { + // Regular character, just append. + result.push_back(input[i]); + } + } + return result; +} + +} // namespace + +MorkReader::MorkReader() { +} + +MorkReader::~MorkReader() { + // Need to delete all the pointers to vectors we have in the table. + for (RowMap::iterator i = table_.begin(); i != table_.end(); ++i) + delete i->second; +} + +bool MorkReader::Read(const std::wstring& filename) { + stream_.open(filename.c_str()); + if (!stream_.is_open()) + return false; + + std::string line; + if (!ReadLine(&line) || + line.compare("// ") != 0) + return false; // Unexpected file format. + + IndexMap column_map; + while (ReadLine(&line)) { + // Trim off leading spaces + size_t idx = 0; + size_t len = line.size(); + while (idx < len && line[idx] == ' ') + ++idx; + if (idx >= len) + continue; + + // Look at the line to figure out what section type this is + if (StartsWithASCII(&line[idx], "< <(a=c)>", true)) { + // Column map. We begin by creating a hash of column id to column name. + StringMap column_name_map; + ParseMap(line, idx, &column_name_map); + + // Now that we have the list of columns, we put them into a flat array. + // Rows will have value arrays of the same size, with indexes that + // correspond to the columns array. As we insert each column into the + // array, we also make an entry in columnMap so that we can look up the + // index given the column id. + columns_.reserve(column_name_map.size()); + + for (StringMap::const_iterator i = column_name_map.begin(); + i != column_name_map.end(); ++i) { + column_map[i->first] = static_cast(columns_.size()); + MorkColumn col(i->first, i->second); + columns_.push_back(col); + } + } else if (StartsWithASCII(&line[idx], "<(", true)) { + // Value map. + ParseMap(line, idx, &value_map_); + } else if (line[idx] == '{' || line[idx] == '[') { + // Table / table row. + ParseTable(line, idx, &column_map); + } else { + // Don't know, hopefully don't care. + } + } + return true; +} + +// Parses a key/value map of the form +// <(k1=v1)(k2=v2)...> +bool MorkReader::ParseMap(const std::string& first_line, + size_t start_index, + StringMap* map) { + // If the first line is the a=c line (column map), just skip over it. + std::string line(first_line); + if (StartsWithASCII(line, "< <(a=c)>", true)) + ReadLine(&line); + + std::string key; + do { + size_t idx = start_index; + size_t len = line.size(); + size_t token_start; + + while (idx < len) { + switch (line[idx++]) { + case '(': + // Beginning of a key/value pair. + if (!key.empty()) { + DLOG(WARNING) << "unterminated key/value pair?"; + key.clear(); + } + + token_start = idx; + while (idx < len && line[idx] != '=') + ++idx; + key.assign(&line[token_start], idx - token_start); + break; + + case '=': { + // Beginning of the value. + if (key.empty()) { + DLOG(WARNING) << "stray value"; + break; + } + + token_start = idx; + while (idx < len && line[idx] != ')') { + if (line[idx] == '\\') + ++idx; // Skip escaped ')' characters. + ++idx; + } + size_t token_end = std::min(idx, len); + ++idx; + + std::string value = MorkUnescape( + std::string(&line[token_start], token_end - token_start)); + (*map)[key] = value; + key.clear(); + break; + } + case '>': + // End of the map. + DLOG_IF(WARNING, key.empty()) << + "map terminates inside of key/value pair"; + return true; + } + } + + // We should start reading the next line at the beginning. + start_index = 0; + } while (ReadLine(&line)); + + // We ran out of lines and the map never terminated. This probably indicates + // a parsing error. + DLOG(WARNING) << "didn't find end of key/value map"; + return false; +} + +// Parses a table row of the form [123(^45^67)..] +// (row id 123 has the value with id 67 for the column with id 45). +// A '^' prefix for a column or value references an entry in the column or +// value map. '=' is used as the separator when the value is a literal. +void MorkReader::ParseTable(const std::string& first_line, + size_t start_index, + const IndexMap* column_map) { + std::string line(first_line); + + // Column index of the cell we're parsing, minus one if invalid. + int column_index = -1; + + // Points to the current row we're parsing inside of the |table_|, will be + // NULL if we're not inside a row. + ColumnDataList* current_row = NULL; + + bool in_meta_row = false; + + do { + size_t idx = start_index; + size_t len = line.size(); + + while (idx < len) { + switch (line[idx++]) { + case '{': + // This marks the beginning of a table section. There's a lot of + // junk before the first row that looks like cell values but isn't. + // Skip to the first '['. + while (idx < len && line[idx] != '[') { + if (line[idx] == '{') { + in_meta_row = true; // The meta row is enclosed in { } + } else if (line[idx] == '}') { + in_meta_row = false; + } + ++idx; + } + break; + + case '[': { + // Start of a new row. Consume the row id, up to the first '('. + // Row edits also have a table namespace, separated from the row id + // by a colon. We don't make use of the namespace, but we need to + // make sure not to consider it part of the row id. + if (current_row) { + DLOG(WARNING) << "unterminated row?"; + current_row = NULL; + } + + // Check for a '-' at the start of the id. This signifies that + // if the row already exists, we should delete all columns from it + // before adding the new values. + bool cut_columns; + if (idx < len && line[idx] == '-') { + cut_columns = true; + ++idx; + } else { + cut_columns = false; + } + + // Locate the range of the ID. + size_t token_start = idx; // Index of the first char of the token. + while (idx < len && + line[idx] != '(' && + line[idx] != ']' && + line[idx] != ':') { + ++idx; + } + size_t token_end = idx; // Index of the char following the token. + while (idx < len && line[idx] != '(' && line[idx] != ']') { + ++idx; + } + + if (in_meta_row) { + // Need to create the meta row. + meta_row_.resize(columns_.size()); + current_row = &meta_row_; + } else { + // Find or create the regular row for this. + IDString row_id(&line[token_start], token_end - token_start); + RowMap::iterator found_row = table_.find(row_id); + if (found_row == table_.end()) { + // We don't already have this row, create a new one for it. + current_row = new ColumnDataList(columns_.size()); + table_[row_id] = current_row; + } else { + // The row already exists and we're adding/replacing things. + current_row = found_row->second; + } + } + if (cut_columns) { + for (size_t i = 0; i < current_row->size(); ++i) + (*current_row)[i].clear(); + } + break; + } + + case ']': + // We're done with the row. + current_row = NULL; + in_meta_row = false; + break; + + case '(': { + if (!current_row) { + DLOG(WARNING) << "cell value outside of row"; + break; + } + + bool column_is_atom; + if (line[idx] == '^') { + column_is_atom = true; + ++idx; // This is not part of the column id, advance past it. + } else { + column_is_atom = false; + } + size_t token_start = idx; + while (idx < len && line[idx] != '^' && line[idx] != '=') { + if (line[idx] == '\\') + ++idx; // Skip escaped characters. + ++idx; + } + + size_t token_end = std::min(idx, len); + + IDString column; + if (column_is_atom) + column.assign(&line[token_start], token_end - token_start); + else + column = MorkUnescape(line.substr(token_start, + token_end - token_start)); + + IndexMap::const_iterator found_column = column_map->find(column); + if (found_column == column_map->end()) { + DLOG(WARNING) << "Column not in column map, discarding it"; + column_index = -1; + } else { + column_index = found_column->second; + } + break; + } + + case '=': + case '^': { + if (column_index == -1) { + DLOG(WARNING) << "stray ^ or = marker"; + break; + } + + bool value_is_atom = (line[idx - 1] == '^'); + size_t token_start = idx - 1; // Include the '=' or '^' marker. + while (idx < len && line[idx] != ')') { + if (line[idx] == '\\') + ++idx; // Skip escaped characters. + ++idx; + } + size_t token_end = std::min(idx, len); + ++idx; + + if (value_is_atom) { + (*current_row)[column_index].assign(&line[token_start], + token_end - token_start); + } else { + (*current_row)[column_index] = + MorkUnescape(line.substr(token_start, token_end - token_start)); + } + column_index = -1; + } + break; + } + } + + // Start parsing the next line at the beginning. + start_index = 0; + } while (current_row && ReadLine(&line)); +} + +bool MorkReader::ReadLine(std::string* line) { + line->resize(256); + std::getline(stream_, *line); + if (stream_.eof() || stream_.bad()) + return false; + + while (!line->empty() && (*line)[line->size() - 1] == '\\') { + // There is a continuation for this line. Read it and append. + std::string new_line; + std::getline(stream_, new_line); + if (stream_.eof()) + return false; + line->erase(line->size() - 1); + line->append(new_line); + } + + return true; +} + +void MorkReader::NormalizeValue(std::string* value) const { + if (value->empty()) + return; + MorkReader::StringMap::const_iterator i; + switch (value->at(0)) { + case '^': + // Hex ID, lookup the name for it in the |value_map_|. + i = value_map_.find(value->substr(1)); + if (i == value_map_.end()) + value->clear(); + else + *value = i->second; + break; + case '=': + // Just use the literal after the equals sign. + value->erase(value->begin()); + break; + default: + // Anything else is invalid. + value->clear(); + break; + } +} + +// Source: +// http://mxr.mozilla.org/firefox/source/toolkit/components/places/src/nsMorkHistoryImporter.cpp + +// Columns for entry (non-meta) history rows +enum { + kURLColumn, + kNameColumn, + kVisitCountColumn, + kHiddenColumn, + kTypedColumn, + kLastVisitColumn, + kColumnCount // Keep me last. +}; + +static const char * const gColumnNames[] = { + "URL", "Name", "VisitCount", "Hidden", "Typed", "LastVisitDate" +}; + +struct TableReadClosure { + explicit TableReadClosure(const MorkReader& r) + : reader(r), + swap_bytes(false), + byte_order_column(-1) { + for (int i = 0; i < kColumnCount; ++i) + column_indexes[i] = -1; + } + + // Backpointers to the reader and history we're operating on. + const MorkReader& reader; + + // Whether we need to swap bytes (file format is other-endian). + bool swap_bytes; + + // Indexes of the columns that we care about. + int column_indexes[kColumnCount]; + int byte_order_column; +}; + +void AddToHistory(MorkReader::ColumnDataList* column_values, + const TableReadClosure& data, + std::vector* rows) { + std::string values[kColumnCount]; + + for (size_t i = 0; i < kColumnCount; ++i) { + if (data.column_indexes[i] != -1) { + values[i] = column_values->at(data.column_indexes[i]); + data.reader.NormalizeValue(&values[i]); + // Do not import hidden records. + if (i == kHiddenColumn && values[i] == "1") + return; + } + } + + GURL url(values[kURLColumn]); + + if (CanImportURL(url)) { + history::URLRow row(url); + + // title is really a UTF-16 string at this point + std::wstring title; + if (data.swap_bytes) { + CodepageToWide(values[kNameColumn], "UTF-16BE", + OnStringUtilConversionError::SKIP, &title); + } else { + CodepageToWide(values[kNameColumn], "UTF-16LE", + OnStringUtilConversionError::SKIP, &title); + } + row.set_title(title); + + int count = atoi(values[kVisitCountColumn].c_str()); + if (count == 0) + count = 1; + row.set_visit_count(count); + + time_t date = StringToInt64(values[kLastVisitColumn]); + if (date != 0) + row.set_last_visit(Time::FromTimeT(date/1000000)); + + bool is_typed = (values[kTypedColumn] == "1"); + if (is_typed) + row.set_typed_count(1); + + rows->push_back(row); + } +} + +// It sets up the file stream and loops over the lines in the file to +// parse them, then adds the resulting row set to history. +void ImportHistoryFromFirefox2(std::wstring file, MessageLoop* loop, + ProfileWriter* writer) { + MorkReader reader; + reader.Read(file); + + // Gather up the column ids so we don't need to find them on each row + TableReadClosure data(reader); + const MorkReader::MorkColumnList& columns = reader.columns(); + for (size_t i = 0; i < columns.size(); ++i) { + for (int j = 0; j < kColumnCount; ++j) + if (columns[i].name == gColumnNames[j]) { + data.column_indexes[j] = static_cast(i); + break; + } + if (columns[i].name == "ByteOrder") + data.byte_order_column = static_cast(i); + } + + // Determine the byte order from the table's meta-row. + const MorkReader::ColumnDataList& meta_row = reader.meta_row(); + if (!meta_row.empty() && data.byte_order_column != -1) { + std::string byte_order = meta_row[data.byte_order_column]; + if (!byte_order.empty()) { + // Note whether the file uses a non-native byte ordering. + // If it does, we'll have to swap bytes for PRUnichar values. + // "BE" and "LE" are the only recognized values, anything + // else is garbage and the file will be treated as native-endian + // (no swapping). + std::string byte_order_value(byte_order); + reader.NormalizeValue(&byte_order_value); + data.swap_bytes = (byte_order_value == "BE"); + } + } + + std::vector rows; + for (MorkReader::iterator i = reader.begin(); i != reader.end(); ++i) + AddToHistory(i->second, data, &rows); + if (!rows.empty()) + loop->PostTask(FROM_HERE, NewRunnableMethod(writer, + &ProfileWriter::AddHistoryPage, rows)); +} diff --git a/chrome/browser/importer/mork_reader.h b/chrome/browser/importer/mork_reader.h new file mode 100644 index 0000000..1e54b93 --- /dev/null +++ b/chrome/browser/importer/mork_reader.h @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** 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 Mork Reader. + * + * The Initial Developer of the Original Code is + * Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner (original author) + * + * 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 ***** */ + +// Source: +// http://mxr.mozilla.org/firefox/source/db/morkreader/nsMorkReader.h + +#ifndef CHROME_BROWSER_IMPORTER_MORK_READER_H__ +#define CHROME_BROWSER_IMPORTER_MORK_READER_H__ + +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/stack_container.h" +#include "chrome/browser/importer/importer.h" + +// The nsMorkReader object allows a consumer to read in a mork-format +// file and enumerate the rows that it contains. It does not provide +// any functionality for modifying mork tables. + +// References: +// http://www.mozilla.org/mailnews/arch/mork/primer.txt +// http://www.mozilla.org/mailnews/arch/mork/grammar.txt +// http://www.jwz.org/hacks/mork.pl + +class MorkReader { + public: + // The IDString type has built-in storage for the hex string representation + // of a 32-bit row id or atom map key, plus the terminating null. + // We use STL string here so that is can be operated with STL containers. + typedef std::string IDString; + + // Lists the contents of a series of columns. + typedef std::vector ColumnDataList; + + // A MorkColumn describes a column of the table. + struct MorkColumn { + MorkColumn(IDString i, const std::string& n) : id(i), name(n) { } + + IDString id; + std::string name; + }; + typedef std::vector MorkColumnList; + + // The key for each row is the identifier for it, and the data is a pointer + // to an array for each column. + typedef std::map RowMap; + + typedef RowMap::const_iterator iterator; + + MorkReader(); + ~MorkReader(); + + // Read in the given mork file. Returns true on success. + // Note: currently, only single-table mork files are supported + bool Read(const std::wstring& filename); + + // Returns the list of columns in the current table. + const MorkColumnList& columns() const { return columns_; } + + // Get the "meta row" for the table. Each table has at most one meta row, + // which records information about the table. Like normal rows, the + // meta row contains columns in the same order as returned by columns(). + // Returns null if there is no meta row for this table. + const ColumnDataList& meta_row() const { return meta_row_; } + + // Normalizes the cell value (resolves references to the value map). + // |value| is modified in-place. + void NormalizeValue(std::string* value) const; + + // Allow iteration over the table cells using STL iterators. The iterator's + // |first| will be the row ID, and the iterator's |second| will be a + // pointer to a ColumnDataList containing the cell data. + iterator begin() const { return table_.begin(); } + iterator end() const { return table_.end(); } + + private: + // A convenience typedef for an ID-to-string mapping. + typedef std::map StringMap; + + // A convenience typdef for an ID-to-index mapping, used for the column index + // hashtable. + typedef std::map IndexMap; + + // Parses a line of the file which contains key/value pairs (either + // the column map or the value map). The starting line is parsed starting at + // the given index. Additional lines are read from stream_ if the line ends + // mid-entry. The pairs are added to the map. + bool ParseMap(const std::string& first_line, + size_t start_index, + StringMap* map); + + // Parses a line of the file which contains a table or row definition, + // starting at the given offset within the line. Additional lines are read + // from |stream_| of the line ends mid-row. An entry is added to |table_| + // using the row ID as the key, which contains a column array for the row. + // The supplied column hash table maps from column id to an index in + // |columns_|. + void ParseTable(const std::string& first_line, + size_t start_index, + const IndexMap* column_map); + + // Reads a single logical line from mStream into aLine. + // Any continuation lines are consumed and appended to the line. + bool ReadLine(std::string* line); + + std::ifstream stream_; + + // Lists the names of the columns for the table. + MorkColumnList columns_; + + // Maps hex string IDs to the corrsponding names. + StringMap value_map_; + + // The data of the columns in the meta row. + ColumnDataList meta_row_; + + // The contents of the mork database. This array pointer is owned by this + // class and must be deleted. + RowMap table_; +}; + +// ImportHistoryFromFirefox2 is the main entry point to the importer. +void ImportHistoryFromFirefox2(std::wstring file, MessageLoop* loop, + ProfileWriter* writer); + +#endif // CHROME_BROWSER_IMPORTER_MORK_READER_H__ diff --git a/chrome/browser/importer_unittest.cc b/chrome/browser/importer_unittest.cc deleted file mode 100644 index 694632f..0000000 --- a/chrome/browser/importer_unittest.cc +++ /dev/null @@ -1,819 +0,0 @@ -// Copyright (c) 2006-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 "testing/gtest/include/gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "base/file_util.h" -#include "base/path_service.h" -#include "chrome/browser/ie_importer.h" -#include "chrome/browser/ie7_password.h" -#include "chrome/browser/importer.h" -#include "chrome/browser/profile.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/env_util.h" -#include "chrome/common/win_util.h" - -class ImporterTest : public testing::Test { - public: - protected: - virtual void SetUp() { - // Creates a new profile in a new subdirectory in the temp directory. - ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_path_)); - file_util::AppendToPath(&test_path_, L"ImporterTest"); - file_util::Delete(test_path_, true); - CreateDirectory(test_path_.c_str(), NULL); - profile_path_ = test_path_; - file_util::AppendToPath(&profile_path_, L"profile"); - CreateDirectory(profile_path_.c_str(), NULL); - app_path_ = test_path_; - file_util::AppendToPath(&app_path_, L"app"); - CreateDirectory(app_path_.c_str(), NULL); - } - - virtual void TearDown() { - // Deletes the profile and cleans up the profile directory. - ASSERT_TRUE(file_util::Delete(test_path_, true)); - ASSERT_FALSE(file_util::PathExists(test_path_)); - } - - MessageLoopForUI message_loop_; - std::wstring test_path_; - std::wstring profile_path_; - std::wstring app_path_; -}; - -const int kMaxPathSize = 5; - -typedef struct { - const bool in_toolbar; - const int path_size; - const wchar_t* path[kMaxPathSize]; - const wchar_t* title; - const char* url; -} BookmarkList; - -typedef struct { - const char* origin; - const char* action; - const char* realm; - const wchar_t* username_element; - const wchar_t* username; - const wchar_t* password_element; - const wchar_t* password; - bool blacklisted; -} PasswordList; - -static const BookmarkList kIEBookmarks[] = { - {true, 0, {}, - L"TheLink", - "http://www.links-thelink.com/"}, - {true, 1, {L"SubFolderOfLinks"}, - L"SubLink", - "http://www.links-sublink.com/"}, - {false, 0, {}, - L"Google Home Page", - "http://www.google.com/"}, - {false, 0, {}, - L"TheLink", - "http://www.links-thelink.com/"}, - {false, 1, {L"SubFolder"}, - L"Title", - "http://www.link.com/"}, - {false, 0, {}, - L"WithPortAndQuery", - "http://host:8080/cgi?q=query"}, - {false, 1, {L"a"}, - L"\x4E2D\x6587", - "http://chinese-title-favorite/"}, -}; - -static const wchar_t* kIEIdentifyUrl = - L"http://A79029D6-753E-4e27-B807-3D46AB1545DF.com:8080/path?key=value"; -static const wchar_t* kIEIdentifyTitle = - L"Unittest GUID"; - -bool IsWindowsVista() { - OSVERSIONINFO info = {0}; - info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&info); - return (info.dwMajorVersion >=6); -} - -// Returns true if the |entry| is in the |list|. -bool FindBookmarkEntry(const ProfileWriter::BookmarkEntry& entry, - const BookmarkList* list, int list_size) { - for (int i = 0; i < list_size; ++i) - if (list[i].in_toolbar == entry.in_toolbar && - list[i].path_size == entry.path.size() && - list[i].url == entry.url.spec() && - list[i].title == entry.title) { - bool equal = true; - for (int k = 0; k < list[i].path_size; ++k) - if (list[i].path[k] != entry.path[k]) { - equal = false; - break; - } - - if (equal) - return true; - } - return false; -} - -class TestObserver : public ProfileWriter, - public ImporterHost::Observer { - public: - TestObserver() : ProfileWriter(NULL) { - bookmark_count_ = 0; - history_count_ = 0; - password_count_ = 0; - } - - virtual void ImportItemStarted(ImportItem item) {} - virtual void ImportItemEnded(ImportItem item) {} - virtual void ImportStarted() {} - virtual void ImportEnded() { - MessageLoop::current()->Quit(); - EXPECT_EQ(arraysize(kIEBookmarks), bookmark_count_); - EXPECT_EQ(1, history_count_); - if (IsWindowsVista()) - EXPECT_EQ(0, password_count_); - else - EXPECT_EQ(1, password_count_); - } - - virtual bool BookmarkModelIsLoaded() const { - // Profile is ready for writing. - return true; - } - - virtual void AddBookmarkModelObserver(BookmarkModelObserver* observer) { - NOTREACHED(); - } - - virtual bool TemplateURLModelIsLoaded() const { - return true; - } - - virtual void AddTemplateURLModelObserver(NotificationObserver* observer) { - NOTREACHED(); - } - - virtual void AddPasswordForm(const PasswordForm& form) { - // Importer should obtain this password form only. - EXPECT_EQ(GURL("http://localhost:8080/security/index.htm"), form.origin); - EXPECT_EQ("http://localhost:8080/", form.signon_realm); - EXPECT_EQ(L"user", form.username_element); - EXPECT_EQ(L"1", form.username_value); - EXPECT_EQ(L"", form.password_element); - EXPECT_EQ(L"2", form.password_value); - EXPECT_EQ("", form.action.spec()); - ++password_count_; - } - - virtual void AddHistoryPage(const std::vector& page) { - // Importer should read the specified URL. - for (size_t i = 0; i < page.size(); ++i) - if (page[i].title() == kIEIdentifyTitle && - page[i].url() == GURL(kIEIdentifyUrl)) - ++history_count_; - } - - virtual void AddBookmarkEntry(const std::vector& bookmark) { - // Importer should import the IE Favorites folder the same as the list. - for (size_t i = 0; i < bookmark.size(); ++i) { - if (FindBookmarkEntry(bookmark[i], kIEBookmarks, - arraysize(kIEBookmarks))) - ++bookmark_count_; - } - } - - virtual void AddKeyword(std::vector template_url, - int default_keyword_index) { - // TODO(jcampan): bug 1169230: we should test keyword importing for IE. - // In order to do that we'll probably need to mock the Windows registry. - NOTREACHED(); - STLDeleteContainerPointers(template_url.begin(), template_url.end()); - } - - private: - int bookmark_count_; - int history_count_; - int password_count_; -}; - -bool CreateUrlFile(std::wstring file, std::wstring url) { - CComPtr locator; - HRESULT result = locator.CoCreateInstance(CLSID_InternetShortcut, NULL, - CLSCTX_INPROC_SERVER); - if (FAILED(result)) - return false; - CComPtr persist_file; - result = locator->QueryInterface(IID_IPersistFile, - reinterpret_cast(&persist_file)); - if (FAILED(result)) - return false; - result = locator->SetURL(url.c_str(), 0); - if (FAILED(result)) - return false; - result = persist_file->Save(file.c_str(), TRUE); - if (FAILED(result)) - return false; - return true; -} - -void ClearPStoreType(IPStore* pstore, const GUID* type, const GUID* subtype) { - CComPtr item; - HRESULT result = pstore->EnumItems(0, type, subtype, 0, &item); - if (result == PST_E_OK) { - wchar_t* item_name; - while (SUCCEEDED(item->Next(1, &item_name, 0))) { - pstore->DeleteItem(0, type, subtype, item_name, NULL, 0); - CoTaskMemFree(item_name); - } - } - pstore->DeleteSubtype(0, type, subtype, 0); - pstore->DeleteType(0, type, 0); -} - -void WritePStore(IPStore* pstore, const GUID* type, const GUID* subtype) { - struct PStoreItem { - wchar_t* name; - int data_size; - char* data; - } items[] = { - {L"http://localhost:8080/security/index.htm#ref:StringData", 8, - "\x31\x00\x00\x00\x32\x00\x00\x00"}, - {L"http://localhost:8080/security/index.htm#ref:StringIndex", 20, - "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00" - "\x00\x00\x2f\x00\x74\x00\x01\x00\x00\x00"}, - {L"user:StringData", 4, - "\x31\x00\x00\x00"}, - {L"user:StringIndex", 20, - "\x57\x49\x43\x4b\x18\x00\x00\x00\x01\x00" - "\x00\x00\x2f\x00\x74\x00\x00\x00\x00\x00"}, - }; - - for (int i = 0; i < arraysize(items); ++i) { - HRESULT res = pstore->WriteItem(0, type, subtype, items[i].name, - items[i].data_size, reinterpret_cast(items[i].data), - NULL, 0, 0); - ASSERT_TRUE(res == PST_E_OK); - } -} - -TEST_F(ImporterTest, IEImporter) { - // Skips in Win2000 for the running environment can not be set up. - if (env_util::GetOperatingSystemVersion() == "5.0") - return; - - // Sets up a favorites folder. - win_util::ScopedCOMInitializer com_init; - std::wstring path = test_path_; - file_util::AppendToPath(&path, L"Favorites"); - CreateDirectory(path.c_str(), NULL); - CreateDirectory((path + L"\\SubFolder").c_str(), NULL); - CreateDirectory((path + L"\\Links").c_str(), NULL); - CreateDirectory((path + L"\\Links\\SubFolderOfLinks").c_str(), NULL); - CreateDirectory((path + L"\\\x0061").c_str(), NULL); - ASSERT_TRUE(CreateUrlFile(path + L"\\Google Home Page.url", - L"http://www.google.com/")); - ASSERT_TRUE(CreateUrlFile(path + L"\\SubFolder\\Title.url", - L"http://www.link.com/")); - ASSERT_TRUE(CreateUrlFile(path + L"\\TheLink.url", - L"http://www.links-thelink.com/")); - ASSERT_TRUE(CreateUrlFile(path + L"\\WithPortAndQuery.url", - L"http://host:8080/cgi?q=query")); - ASSERT_TRUE(CreateUrlFile(path + L"\\\x0061\\\x4E2D\x6587.url", - L"http://chinese-title-favorite/")); - ASSERT_TRUE(CreateUrlFile(path + L"\\Links\\TheLink.url", - L"http://www.links-thelink.com/")); - ASSERT_TRUE(CreateUrlFile(path + L"\\Links\\SubFolderOfLinks\\SubLink.url", - L"http://www.links-sublink.com/")); - file_util::WriteFile(path + L"\\InvalidUrlFile.url", "x", 1); - file_util::WriteFile(path + L"\\PlainTextFile.txt", "x", 1); - - // Sets up dummy password data. - HRESULT res; - CComPtr pstore; - HMODULE pstorec_dll; - GUID type = IEImporter::kUnittestGUID; - GUID subtype = IEImporter::kUnittestGUID; - // PStore is read-only in Windows Vista. - if (!IsWindowsVista()) { - typedef HRESULT (WINAPI *PStoreCreateFunc)(IPStore**, DWORD, DWORD, DWORD); - pstorec_dll = LoadLibrary(L"pstorec.dll"); - PStoreCreateFunc PStoreCreateInstance = - (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance"); - res = PStoreCreateInstance(&pstore, 0, 0, 0); - ASSERT_TRUE(res == S_OK); - ClearPStoreType(pstore, &type, &subtype); - PST_TYPEINFO type_info; - type_info.szDisplayName = L"TestType"; - type_info.cbSize = 8; - pstore->CreateType(0, &type, &type_info, 0); - pstore->CreateSubtype(0, &type, &subtype, &type_info, NULL, 0); - WritePStore(pstore, &type, &subtype); - } - - // Sets up a special history link. - CComPtr url_history_stg2; - res = url_history_stg2.CoCreateInstance(CLSID_CUrlHistory, NULL, - CLSCTX_INPROC_SERVER); - ASSERT_TRUE(res == S_OK); - res = url_history_stg2->AddUrl(kIEIdentifyUrl, kIEIdentifyTitle, 0); - ASSERT_TRUE(res == S_OK); - - // Starts to import the above settings. - MessageLoop* loop = MessageLoop::current(); - scoped_refptr host = new ImporterHost(loop); - - TestObserver* observer = new TestObserver(); - host->SetObserver(observer); - ProfileInfo profile_info; - profile_info.browser_type = MS_IE; - profile_info.source_path = test_path_; - - loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), - &ImporterHost::StartImportSettings, profile_info, - HISTORY | PASSWORDS | FAVORITES, observer, true)); - loop->Run(); - - // Cleans up. - url_history_stg2->DeleteUrl(kIEIdentifyUrl, 0); - url_history_stg2.Release(); - if (!IsWindowsVista()) { - ClearPStoreType(pstore, &type, &subtype); - // Releases it befor unload the dll. - pstore.Release(); - FreeLibrary(pstorec_dll); - } -} - -TEST_F(ImporterTest, IE7Importer) { - // This is the unencrypted values of my keys under Storage2. - // The passwords have been manually changed to abcdef... but the size remains - // the same. - unsigned char data1[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00" - "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00" - "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01" - "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76" - "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00" - "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00" - "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00" - "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00" - "\x6c\x00\x00\x00"; - - unsigned char data2[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x24\x00\x00\x00" - "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00" - "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\xa8\xea\xf4\xe5\x9f\x9a\xc8\x01" - "\x09\x00\x00\x00\x14\x00\x00\x00\xa8\xea\xf4\xe5" - "\x9f\x9a\xc8\x01\x07\x00\x00\x00\x61\x00\x62\x00" - "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00" - "\x69\x00\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00" - "\x65\x00\x66\x00\x67\x00\x00\x00"; - - - - std::vector decrypted_data1; - decrypted_data1.resize(arraysize(data1)); - memcpy(&decrypted_data1.front(), data1, sizeof(data1)); - - std::vector decrypted_data2; - decrypted_data2.resize(arraysize(data2)); - memcpy(&decrypted_data2.front(), data2, sizeof(data2)); - - std::wstring password; - std::wstring username; - ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data1, &username, - &password)); - EXPECT_EQ(L"abcdefgh", username); - EXPECT_EQ(L"abcdefghijkl", password); - - ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data2, &username, - &password)); - EXPECT_EQ(L"abcdefghi", username); - EXPECT_EQ(L"abcdefg", password); -} - -static const BookmarkList kFirefox2Bookmarks[] = { - {true, 1, {L"Folder"}, - L"On Toolbar's Subfolder", - "http://on.toolbar/bookmark/folder"}, - {true, 0, {}, - L"On Bookmark Toolbar", - "http://on.toolbar/bookmark"}, - {false, 1, {L"Folder"}, - L"New Bookmark", - "http://domain/"}, - {false, 0, {}, - L"", - "http://domain.com/q?a=\"er\"&b=%3C%20%20%3E"}, - {false, 0, {}, - L"Google Home Page", - "http://www.google.com/"}, - {false, 0, {}, - L"\x4E2D\x6587", - "http://chinese.site.cn/path?query=1#ref"}, - {false, 0, {}, - L"mail", - "mailto:username@host"}, -}; - -static const PasswordList kFirefox2Passwords[] = { - {"https://www.google.com/", "", "https://www.google.com/", - L"", L"", L"", L"", true}, - {"http://localhost:8080/", "", "http://localhost:8080/corp.google.com", - L"", L"http", L"", L"Http1+1abcdefg", false}, - {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", - L"loginuser", L"usr", L"loginpass", L"pwd", false}, - {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", - L"loginuser", L"firefox", L"loginpass", L"firefox", false}, - {"http://localhost/", "", "http://localhost/", - L"loginuser", L"hello", L"", L"world", false}, -}; - -typedef struct { - const wchar_t* keyword; - const wchar_t* url; -} KeywordList; - -static const KeywordList kFirefox2Keywords[] = { - // Searh plugins - { L"amazon.com", - L"http://www.amazon.com/exec/obidos/external-search/?field-keywords=" - L"{searchTerms}&mode=blended" }, - { L"answers.com", - L"http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, - { L"search.creativecommons.org", - L"http://search.creativecommons.org/?q={searchTerms}" }, - { L"search.ebay.com", - L"http://search.ebay.com/search/search.dll?query={searchTerms}&" - L"MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" - L"maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, - { L"google.com", - L"http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, - { L"search.yahoo.com", - L"http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, - { L"flickr.com", - L"http://www.flickr.com/photos/tags/?q={searchTerms}" }, - { L"imdb.com", - L"http://www.imdb.com/find?q={searchTerms}" }, - { L"webster.com", - L"http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, - // Search keywords. - { L"google", L"http://www.google.com/" }, - { L"< > & \" ' \\ /", L"http://g.cn/"}, -}; - -static const int kDefaultFirefox2KeywordIndex = 8; - -class FirefoxObserver : public ProfileWriter, - public ImporterHost::Observer { - public: - FirefoxObserver() : ProfileWriter(NULL) { - bookmark_count_ = 0; - history_count_ = 0; - password_count_ = 0; - keyword_count_ = 0; - } - - virtual void ImportItemStarted(ImportItem item) {} - virtual void ImportItemEnded(ImportItem item) {} - virtual void ImportStarted() {} - virtual void ImportEnded() { - MessageLoop::current()->Quit(); - EXPECT_EQ(arraysize(kFirefox2Bookmarks), bookmark_count_); - EXPECT_EQ(1, history_count_); - EXPECT_EQ(arraysize(kFirefox2Passwords), password_count_); - EXPECT_EQ(arraysize(kFirefox2Keywords), keyword_count_); - EXPECT_EQ(kFirefox2Keywords[kDefaultFirefox2KeywordIndex].keyword, - default_keyword_); - EXPECT_EQ(kFirefox2Keywords[kDefaultFirefox2KeywordIndex].url, - default_keyword_url_); - } - - virtual bool BookmarkModelIsLoaded() const { - // Profile is ready for writing. - return true; - } - - virtual void AddBookmarkModelObserver(BookmarkModelObserver* observer) { - NOTREACHED(); - } - - virtual bool TemplateURLModelIsLoaded() const { - return true; - } - - virtual void AddTemplateURLModelObserver(NotificationObserver* observer) { - NOTREACHED(); - } - - virtual void AddPasswordForm(const PasswordForm& form) { - PasswordList p = kFirefox2Passwords[password_count_]; - EXPECT_EQ(p.origin, form.origin.spec()); - EXPECT_EQ(p.realm, form.signon_realm); - EXPECT_EQ(p.action, form.action.spec()); - EXPECT_EQ(p.username_element, form.username_element); - EXPECT_EQ(p.username, form.username_value); - EXPECT_EQ(p.password_element, form.password_element); - EXPECT_EQ(p.password, form.password_value); - EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); - ++password_count_; - } - - virtual void AddHistoryPage(const std::vector& page) { - EXPECT_EQ(1, page.size()); - EXPECT_EQ("http://en-us.www.mozilla.com/", page[0].url().spec()); - EXPECT_EQ(L"Firefox Updated", page[0].title()); - ++history_count_; - } - - virtual void AddBookmarkEntry(const std::vector& bookmark) { - for (size_t i = 0; i < bookmark.size(); ++i) { - if (FindBookmarkEntry(bookmark[i], kFirefox2Bookmarks, - arraysize(kFirefox2Bookmarks))) - ++bookmark_count_; - } - } - - virtual void AddKeywords(const std::vector& template_urls, - int default_keyword_index, - bool unique_on_host_and_path) { - for (size_t i = 0; i < template_urls.size(); ++i) { - // The order might not be deterministic, look in the expected list for - // that template URL. - bool found = false; - std::wstring keyword = template_urls[i]->keyword(); - for (int j = 0; j < arraysize(kFirefox2Keywords); ++j) { - const wchar_t* comp_keyword = kFirefox2Keywords[j].keyword; - bool equal = (keyword == comp_keyword); - if (template_urls[i]->keyword() == kFirefox2Keywords[j].keyword) { - EXPECT_EQ(kFirefox2Keywords[j].url, template_urls[i]->url()->url()); - found = true; - break; - } - } - EXPECT_TRUE(found); - ++keyword_count_; - } - - if (default_keyword_index != -1) { - EXPECT_LT(default_keyword_index, static_cast(template_urls.size())); - TemplateURL* default_turl = template_urls[default_keyword_index]; - default_keyword_ = default_turl->keyword(); - default_keyword_url_ = default_turl->url()->url(); - } - - STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); - } - - void AddFavicons(const std::vector& favicons) { - } - - private: - int bookmark_count_; - int history_count_; - int password_count_; - int keyword_count_; - std::wstring default_keyword_; - std::wstring default_keyword_url_; -}; - -TEST_F(ImporterTest, Firefox2Importer) { - std::wstring data_path; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); - file_util::AppendToPath(&data_path, L"firefox2_profile\\*"); - file_util::CopyDirectory(data_path, profile_path_, true); - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); - file_util::AppendToPath(&data_path, L"firefox2_nss"); - file_util::CopyDirectory(data_path, profile_path_, false); - - std::wstring search_engine_path = app_path_; - file_util::AppendToPath(&search_engine_path, L"searchplugins"); - CreateDirectory(search_engine_path.c_str(), NULL); - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); - file_util::AppendToPath(&data_path, L"firefox2_searchplugins"); - file_util::CopyDirectory(data_path, search_engine_path, false); - - MessageLoop* loop = MessageLoop::current(); - scoped_refptr host = new ImporterHost(loop); - FirefoxObserver* observer = new FirefoxObserver(); - host->SetObserver(observer); - ProfileInfo profile_info; - profile_info.browser_type = FIREFOX2; - profile_info.app_path = app_path_; - profile_info.source_path = profile_path_; - - loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), - &ImporterHost::StartImportSettings, profile_info, - HISTORY | PASSWORDS | FAVORITES | SEARCH_ENGINES, observer, true)); - loop->Run(); -} - -static const BookmarkList kFirefox3Bookmarks[] = { - {true, 0, {}, - L"Toolbar", - "http://site/"}, - {false, 0, {}, - L"Title", - "http://www.google.com/"}, -}; - -static const PasswordList kFirefox3Passwords[] = { - {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", - L"loginuser", L"abc", L"loginpass", L"123", false}, - {"http://localhost:8080/", "", "http://localhost:8080/localhost", - L"", L"http", L"", L"Http1+1abcdefg", false}, -}; - -static const KeywordList kFirefox3Keywords[] = { - { L"amazon.com", - L"http://www.amazon.com/exec/obidos/external-search/?field-keywords=" - L"{searchTerms}&mode=blended" }, - { L"answers.com", - L"http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, - { L"search.creativecommons.org", - L"http://search.creativecommons.org/?q={searchTerms}" }, - { L"search.ebay.com", - L"http://search.ebay.com/search/search.dll?query={searchTerms}&" - L"MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" - L"maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, - { L"google.com", - L"http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, - { L"en.wikipedia.org", - L"http://en.wikipedia.org/wiki/Special:Search?search={searchTerms}" }, - { L"search.yahoo.com", - L"http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, - { L"flickr.com", - L"http://www.flickr.com/photos/tags/?q={searchTerms}" }, - { L"imdb.com", - L"http://www.imdb.com/find?q={searchTerms}" }, - { L"webster.com", - L"http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, - // Search keywords. - { L"\x4E2D\x6587", L"http://www.google.com/" }, -}; - -static const int kDefaultFirefox3KeywordIndex = 8; - -class Firefox3Observer : public ProfileWriter, - public ImporterHost::Observer { - public: - Firefox3Observer() : ProfileWriter(NULL) { - bookmark_count_ = 0; - history_count_ = 0; - password_count_ = 0; - keyword_count_ = 0; - } - - virtual void ImportItemStarted(ImportItem item) {} - virtual void ImportItemEnded(ImportItem item) {} - virtual void ImportStarted() {} - virtual void ImportEnded() { - MessageLoop::current()->Quit(); - EXPECT_EQ(arraysize(kFirefox3Bookmarks), bookmark_count_); - EXPECT_EQ(1, history_count_); - EXPECT_EQ(arraysize(kFirefox3Passwords), password_count_); - EXPECT_EQ(arraysize(kFirefox3Keywords), keyword_count_); - EXPECT_EQ(kFirefox3Keywords[kDefaultFirefox3KeywordIndex].keyword, - default_keyword_); - EXPECT_EQ(kFirefox3Keywords[kDefaultFirefox3KeywordIndex].url, - default_keyword_url_); - } - - virtual bool BookmarkModelIsLoaded() const { - // Profile is ready for writing. - return true; - } - - virtual void AddBookmarkModelObserver(BookmarkModelObserver* observer) { - NOTREACHED(); - } - - virtual bool TemplateURLModelIsLoaded() const { - return true; - } - - virtual void AddTemplateURLModelObserver(NotificationObserver* observer) { - NOTREACHED(); - } - - virtual void AddPasswordForm(const PasswordForm& form) { - PasswordList p = kFirefox3Passwords[password_count_]; - EXPECT_EQ(p.origin, form.origin.spec()); - EXPECT_EQ(p.realm, form.signon_realm); - EXPECT_EQ(p.action, form.action.spec()); - EXPECT_EQ(p.username_element, form.username_element); - EXPECT_EQ(p.username, form.username_value); - EXPECT_EQ(p.password_element, form.password_element); - EXPECT_EQ(p.password, form.password_value); - EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); - ++password_count_; - } - - virtual void AddHistoryPage(const std::vector& page) { - ASSERT_EQ(3, page.size()); - EXPECT_EQ("http://www.google.com/", page[0].url().spec()); - EXPECT_EQ(L"Google", page[0].title()); - EXPECT_EQ("http://www.google.com/", page[1].url().spec()); - EXPECT_EQ(L"Google", page[1].title()); - EXPECT_EQ("http://www.cs.unc.edu/~jbs/resources/perl/perl-cgi/programs/form1-POST.html", - page[2].url().spec()); - EXPECT_EQ(L"example form (POST)", page[2].title()); - ++history_count_; - } - - virtual void AddBookmarkEntry(const std::vector& bookmark) { - for (size_t i = 0; i < bookmark.size(); ++i) { - if (FindBookmarkEntry(bookmark[i], kFirefox3Bookmarks, - arraysize(kFirefox3Bookmarks))) - ++bookmark_count_; - } - } - - void AddKeywords(const std::vector& template_urls, - int default_keyword_index, - bool unique_on_host_and_path) { - for (size_t i = 0; i < template_urls.size(); ++i) { - // The order might not be deterministic, look in the expected list for - // that template URL. - bool found = false; - std::wstring keyword = template_urls[i]->keyword(); - for (int j = 0; j < arraysize(kFirefox3Keywords); ++j) { - const wchar_t* comp_keyword = kFirefox3Keywords[j].keyword; - bool equal = (keyword == comp_keyword); - if (template_urls[i]->keyword() == kFirefox3Keywords[j].keyword) { - EXPECT_EQ(kFirefox3Keywords[j].url, template_urls[i]->url()->url()); - found = true; - break; - } - } - EXPECT_TRUE(found); - ++keyword_count_; - } - - if (default_keyword_index != -1) { - EXPECT_LT(default_keyword_index, static_cast(template_urls.size())); - TemplateURL* default_turl = template_urls[default_keyword_index]; - default_keyword_ = default_turl->keyword(); - default_keyword_url_ = default_turl->url()->url(); - } - - STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); - } - - void AddFavicons(const std::vector& favicons) { - } - - private: - int bookmark_count_; - int history_count_; - int password_count_; - int keyword_count_; - std::wstring default_keyword_; - std::wstring default_keyword_url_; -}; - -TEST_F(ImporterTest, Firefox3Importer) { - std::wstring data_path; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); - file_util::AppendToPath(&data_path, L"firefox3_profile\\*"); - file_util::CopyDirectory(data_path, profile_path_, true); - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); - file_util::AppendToPath(&data_path, L"firefox3_nss"); - file_util::CopyDirectory(data_path, profile_path_, false); - - std::wstring search_engine_path = app_path_; - file_util::AppendToPath(&search_engine_path, L"searchplugins"); - CreateDirectory(search_engine_path.c_str(), NULL); - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); - file_util::AppendToPath(&data_path, L"firefox3_searchplugins"); - file_util::CopyDirectory(data_path, search_engine_path, false); - - MessageLoop* loop = MessageLoop::current(); - ProfileInfo profile_info; - profile_info.browser_type = FIREFOX3; - profile_info.app_path = app_path_; - profile_info.source_path = profile_path_; - scoped_refptr host = new ImporterHost(loop); - Firefox3Observer* observer = new Firefox3Observer(); - host->SetObserver(observer); - loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), - &ImporterHost::StartImportSettings, profile_info, - HISTORY | PASSWORDS | FAVORITES | SEARCH_ENGINES, observer, true)); - loop->Run(); -} - diff --git a/chrome/browser/mork_reader.cc b/chrome/browser/mork_reader.cc deleted file mode 100644 index 9a59cf0..0000000 --- a/chrome/browser/mork_reader.cc +++ /dev/null @@ -1,581 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Mork Reader. - * - * The Initial Developer of the Original Code is - * Google Inc. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brian Ryner (original author) - * - * 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 ***** */ - -// Source: -// http://mxr.mozilla.org/firefox/source/db/morkreader/nsMorkReader.cpp -// This file has been converted to google style. - -#include "chrome/browser/mork_reader.h" - -#include - -#include "base/logging.h" -#include "base/string_util.h" -#include "chrome/browser/firefox_importer_utils.h" -#include "chrome/browser/history/history_types.h" - -namespace { - -// Convert a hex character (0-9, A-F) to its corresponding byte value. -// Returns -1 if the character is invalid. -inline int HexCharToInt(char c) { - if ('0' <= c && c <= '9') - return c - '0'; - if ('A' <= c && c <= 'F') - return c - 'A' + 10; - return -1; -} - -// Unescape a Mork value. Mork uses $xx escaping to encode non-ASCII -// characters. Additionally, '$' and '\' are backslash-escaped. -// The result of the unescape is in returned. -std::string MorkUnescape(const std::string& input) { - // We optimize for speed over space here -- size the result buffer to - // the size of the source, which is an upper bound on the size of the - // unescaped string. - std::string result; - size_t input_length = input.size(); - result.reserve(input_length); - - for (size_t i = 0; i < input_length; i++) { - char c = input[i]; - if (c == '\\') { - // Escaped literal, slip the backslash, append the next character. - i++; - if (i < input_length) - result.push_back(input[i]); - } else if (c == '$') { - // Dollar sign denotes a hex character. - if (i < input_length - 2) { - // Would be nice to use ToInteger() here, but it currently - // requires a null-terminated string. - int first = HexCharToInt(input[++i]); - int second = HexCharToInt(input[++i]); - if (first >= 0 && second >= 0) - result.push_back((first << 4) | second); - } - } else { - // Regular character, just append. - result.push_back(input[i]); - } - } - return result; -} - -} // namespace - -MorkReader::MorkReader() { -} - -MorkReader::~MorkReader() { - // Need to delete all the pointers to vectors we have in the table. - for (RowMap::iterator i = table_.begin(); i != table_.end(); ++i) - delete i->second; -} - -bool MorkReader::Read(const std::wstring& filename) { - stream_.open(filename.c_str()); - if (!stream_.is_open()) - return false; - - std::string line; - if (!ReadLine(&line) || - line.compare("// ") != 0) - return false; // Unexpected file format. - - IndexMap column_map; - while (ReadLine(&line)) { - // Trim off leading spaces - size_t idx = 0; - size_t len = line.size(); - while (idx < len && line[idx] == ' ') - ++idx; - if (idx >= len) - continue; - - // Look at the line to figure out what section type this is - if (StartsWithASCII(&line[idx], "< <(a=c)>", true)) { - // Column map. We begin by creating a hash of column id to column name. - StringMap column_name_map; - ParseMap(line, idx, &column_name_map); - - // Now that we have the list of columns, we put them into a flat array. - // Rows will have value arrays of the same size, with indexes that - // correspond to the columns array. As we insert each column into the - // array, we also make an entry in columnMap so that we can look up the - // index given the column id. - columns_.reserve(column_name_map.size()); - - for (StringMap::const_iterator i = column_name_map.begin(); - i != column_name_map.end(); ++i) { - column_map[i->first] = static_cast(columns_.size()); - MorkColumn col(i->first, i->second); - columns_.push_back(col); - } - } else if (StartsWithASCII(&line[idx], "<(", true)) { - // Value map. - ParseMap(line, idx, &value_map_); - } else if (line[idx] == '{' || line[idx] == '[') { - // Table / table row. - ParseTable(line, idx, &column_map); - } else { - // Don't know, hopefully don't care. - } - } - return true; -} - -// Parses a key/value map of the form -// <(k1=v1)(k2=v2)...> -bool MorkReader::ParseMap(const std::string& first_line, - size_t start_index, - StringMap* map) { - // If the first line is the a=c line (column map), just skip over it. - std::string line(first_line); - if (StartsWithASCII(line, "< <(a=c)>", true)) - ReadLine(&line); - - std::string key; - do { - size_t idx = start_index; - size_t len = line.size(); - size_t token_start; - - while (idx < len) { - switch (line[idx++]) { - case '(': - // Beginning of a key/value pair. - if (!key.empty()) { - DLOG(WARNING) << "unterminated key/value pair?"; - key.clear(); - } - - token_start = idx; - while (idx < len && line[idx] != '=') - ++idx; - key.assign(&line[token_start], idx - token_start); - break; - - case '=': { - // Beginning of the value. - if (key.empty()) { - DLOG(WARNING) << "stray value"; - break; - } - - token_start = idx; - while (idx < len && line[idx] != ')') { - if (line[idx] == '\\') - ++idx; // Skip escaped ')' characters. - ++idx; - } - size_t token_end = std::min(idx, len); - ++idx; - - std::string value = MorkUnescape( - std::string(&line[token_start], token_end - token_start)); - (*map)[key] = value; - key.clear(); - break; - } - case '>': - // End of the map. - DLOG_IF(WARNING, key.empty()) << - "map terminates inside of key/value pair"; - return true; - } - } - - // We should start reading the next line at the beginning. - start_index = 0; - } while (ReadLine(&line)); - - // We ran out of lines and the map never terminated. This probably indicates - // a parsing error. - DLOG(WARNING) << "didn't find end of key/value map"; - return false; -} - -// Parses a table row of the form [123(^45^67)..] -// (row id 123 has the value with id 67 for the column with id 45). -// A '^' prefix for a column or value references an entry in the column or -// value map. '=' is used as the separator when the value is a literal. -void MorkReader::ParseTable(const std::string& first_line, - size_t start_index, - const IndexMap* column_map) { - std::string line(first_line); - - // Column index of the cell we're parsing, minus one if invalid. - int column_index = -1; - - // Points to the current row we're parsing inside of the |table_|, will be - // NULL if we're not inside a row. - ColumnDataList* current_row = NULL; - - bool in_meta_row = false; - - do { - size_t idx = start_index; - size_t len = line.size(); - - while (idx < len) { - switch (line[idx++]) { - case '{': - // This marks the beginning of a table section. There's a lot of - // junk before the first row that looks like cell values but isn't. - // Skip to the first '['. - while (idx < len && line[idx] != '[') { - if (line[idx] == '{') { - in_meta_row = true; // The meta row is enclosed in { } - } else if (line[idx] == '}') { - in_meta_row = false; - } - ++idx; - } - break; - - case '[': { - // Start of a new row. Consume the row id, up to the first '('. - // Row edits also have a table namespace, separated from the row id - // by a colon. We don't make use of the namespace, but we need to - // make sure not to consider it part of the row id. - if (current_row) { - DLOG(WARNING) << "unterminated row?"; - current_row = NULL; - } - - // Check for a '-' at the start of the id. This signifies that - // if the row already exists, we should delete all columns from it - // before adding the new values. - bool cut_columns; - if (idx < len && line[idx] == '-') { - cut_columns = true; - ++idx; - } else { - cut_columns = false; - } - - // Locate the range of the ID. - size_t token_start = idx; // Index of the first char of the token. - while (idx < len && - line[idx] != '(' && - line[idx] != ']' && - line[idx] != ':') { - ++idx; - } - size_t token_end = idx; // Index of the char following the token. - while (idx < len && line[idx] != '(' && line[idx] != ']') { - ++idx; - } - - if (in_meta_row) { - // Need to create the meta row. - meta_row_.resize(columns_.size()); - current_row = &meta_row_; - } else { - // Find or create the regular row for this. - IDString row_id(&line[token_start], token_end - token_start); - RowMap::iterator found_row = table_.find(row_id); - if (found_row == table_.end()) { - // We don't already have this row, create a new one for it. - current_row = new ColumnDataList(columns_.size()); - table_[row_id] = current_row; - } else { - // The row already exists and we're adding/replacing things. - current_row = found_row->second; - } - } - if (cut_columns) { - for (size_t i = 0; i < current_row->size(); ++i) - (*current_row)[i].clear(); - } - break; - } - - case ']': - // We're done with the row. - current_row = NULL; - in_meta_row = false; - break; - - case '(': { - if (!current_row) { - DLOG(WARNING) << "cell value outside of row"; - break; - } - - bool column_is_atom; - if (line[idx] == '^') { - column_is_atom = true; - ++idx; // This is not part of the column id, advance past it. - } else { - column_is_atom = false; - } - size_t token_start = idx; - while (idx < len && line[idx] != '^' && line[idx] != '=') { - if (line[idx] == '\\') - ++idx; // Skip escaped characters. - ++idx; - } - - size_t token_end = std::min(idx, len); - - IDString column; - if (column_is_atom) - column.assign(&line[token_start], token_end - token_start); - else - column = MorkUnescape(line.substr(token_start, - token_end - token_start)); - - IndexMap::const_iterator found_column = column_map->find(column); - if (found_column == column_map->end()) { - DLOG(WARNING) << "Column not in column map, discarding it"; - column_index = -1; - } else { - column_index = found_column->second; - } - break; - } - - case '=': - case '^': { - if (column_index == -1) { - DLOG(WARNING) << "stray ^ or = marker"; - break; - } - - bool value_is_atom = (line[idx - 1] == '^'); - size_t token_start = idx - 1; // Include the '=' or '^' marker. - while (idx < len && line[idx] != ')') { - if (line[idx] == '\\') - ++idx; // Skip escaped characters. - ++idx; - } - size_t token_end = std::min(idx, len); - ++idx; - - if (value_is_atom) { - (*current_row)[column_index].assign(&line[token_start], - token_end - token_start); - } else { - (*current_row)[column_index] = - MorkUnescape(line.substr(token_start, token_end - token_start)); - } - column_index = -1; - } - break; - } - } - - // Start parsing the next line at the beginning. - start_index = 0; - } while (current_row && ReadLine(&line)); -} - -bool MorkReader::ReadLine(std::string* line) { - line->resize(256); - std::getline(stream_, *line); - if (stream_.eof() || stream_.bad()) - return false; - - while (!line->empty() && (*line)[line->size() - 1] == '\\') { - // There is a continuation for this line. Read it and append. - std::string new_line; - std::getline(stream_, new_line); - if (stream_.eof()) - return false; - line->erase(line->size() - 1); - line->append(new_line); - } - - return true; -} - -void MorkReader::NormalizeValue(std::string* value) const { - if (value->empty()) - return; - MorkReader::StringMap::const_iterator i; - switch (value->at(0)) { - case '^': - // Hex ID, lookup the name for it in the |value_map_|. - i = value_map_.find(value->substr(1)); - if (i == value_map_.end()) - value->clear(); - else - *value = i->second; - break; - case '=': - // Just use the literal after the equals sign. - value->erase(value->begin()); - break; - default: - // Anything else is invalid. - value->clear(); - break; - } -} - -// Source: -// http://mxr.mozilla.org/firefox/source/toolkit/components/places/src/nsMorkHistoryImporter.cpp - -// Columns for entry (non-meta) history rows -enum { - kURLColumn, - kNameColumn, - kVisitCountColumn, - kHiddenColumn, - kTypedColumn, - kLastVisitColumn, - kColumnCount // Keep me last. -}; - -static const char * const gColumnNames[] = { - "URL", "Name", "VisitCount", "Hidden", "Typed", "LastVisitDate" -}; - -struct TableReadClosure { - explicit TableReadClosure(const MorkReader& r) - : reader(r), - swap_bytes(false), - byte_order_column(-1) { - for (int i = 0; i < kColumnCount; ++i) - column_indexes[i] = -1; - } - - // Backpointers to the reader and history we're operating on. - const MorkReader& reader; - - // Whether we need to swap bytes (file format is other-endian). - bool swap_bytes; - - // Indexes of the columns that we care about. - int column_indexes[kColumnCount]; - int byte_order_column; -}; - -void AddToHistory(MorkReader::ColumnDataList* column_values, - const TableReadClosure& data, - std::vector* rows) { - std::string values[kColumnCount]; - - for (size_t i = 0; i < kColumnCount; ++i) { - if (data.column_indexes[i] != -1) { - values[i] = column_values->at(data.column_indexes[i]); - data.reader.NormalizeValue(&values[i]); - // Do not import hidden records. - if (i == kHiddenColumn && values[i] == "1") - return; - } - } - - GURL url(values[kURLColumn]); - - if (CanImportURL(url)) { - history::URLRow row(url); - - // title is really a UTF-16 string at this point - std::wstring title; - if (data.swap_bytes) { - CodepageToWide(values[kNameColumn], "UTF-16BE", - OnStringUtilConversionError::SKIP, &title); - } else { - CodepageToWide(values[kNameColumn], "UTF-16LE", - OnStringUtilConversionError::SKIP, &title); - } - row.set_title(title); - - int count = atoi(values[kVisitCountColumn].c_str()); - if (count == 0) - count = 1; - row.set_visit_count(count); - - time_t date = StringToInt64(values[kLastVisitColumn]); - if (date != 0) - row.set_last_visit(Time::FromTimeT(date/1000000)); - - bool is_typed = (values[kTypedColumn] == "1"); - if (is_typed) - row.set_typed_count(1); - - rows->push_back(row); - } -} - -// It sets up the file stream and loops over the lines in the file to -// parse them, then adds the resulting row set to history. -void ImportHistoryFromFirefox2(std::wstring file, MessageLoop* loop, - ProfileWriter* writer) { - MorkReader reader; - reader.Read(file); - - // Gather up the column ids so we don't need to find them on each row - TableReadClosure data(reader); - const MorkReader::MorkColumnList& columns = reader.columns(); - for (size_t i = 0; i < columns.size(); ++i) { - for (int j = 0; j < kColumnCount; ++j) - if (columns[i].name == gColumnNames[j]) { - data.column_indexes[j] = static_cast(i); - break; - } - if (columns[i].name == "ByteOrder") - data.byte_order_column = static_cast(i); - } - - // Determine the byte order from the table's meta-row. - const MorkReader::ColumnDataList& meta_row = reader.meta_row(); - if (!meta_row.empty() && data.byte_order_column != -1) { - std::string byte_order = meta_row[data.byte_order_column]; - if (!byte_order.empty()) { - // Note whether the file uses a non-native byte ordering. - // If it does, we'll have to swap bytes for PRUnichar values. - // "BE" and "LE" are the only recognized values, anything - // else is garbage and the file will be treated as native-endian - // (no swapping). - std::string byte_order_value(byte_order); - reader.NormalizeValue(&byte_order_value); - data.swap_bytes = (byte_order_value == "BE"); - } - } - - std::vector rows; - for (MorkReader::iterator i = reader.begin(); i != reader.end(); ++i) - AddToHistory(i->second, data, &rows); - if (!rows.empty()) - loop->PostTask(FROM_HERE, NewRunnableMethod(writer, - &ProfileWriter::AddHistoryPage, rows)); -} diff --git a/chrome/browser/mork_reader.h b/chrome/browser/mork_reader.h deleted file mode 100644 index 6b4b267..0000000 --- a/chrome/browser/mork_reader.h +++ /dev/null @@ -1,165 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Mork Reader. - * - * The Initial Developer of the Original Code is - * Google Inc. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brian Ryner (original author) - * - * 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 ***** */ - -// Source: -// http://mxr.mozilla.org/firefox/source/db/morkreader/nsMorkReader.h - -#ifndef MORK_READER_H__ -#define MORK_READER_H__ - -#include -#include -#include -#include -#include - -#include "base/basictypes.h" -#include "base/stack_container.h" -#include "chrome/browser/importer.h" - -// The nsMorkReader object allows a consumer to read in a mork-format -// file and enumerate the rows that it contains. It does not provide -// any functionality for modifying mork tables. - -// References: -// http://www.mozilla.org/mailnews/arch/mork/primer.txt -// http://www.mozilla.org/mailnews/arch/mork/grammar.txt -// http://www.jwz.org/hacks/mork.pl - -class MorkReader { - public: - // The IDString type has built-in storage for the hex string representation - // of a 32-bit row id or atom map key, plus the terminating null. - // We use STL string here so that is can be operated with STL containers. - typedef std::string IDString; - - // Lists the contents of a series of columns. - typedef std::vector ColumnDataList; - - // A MorkColumn describes a column of the table. - struct MorkColumn { - MorkColumn(IDString i, const std::string& n) : id(i), name(n) { } - - IDString id; - std::string name; - }; - typedef std::vector MorkColumnList; - - // The key for each row is the identifier for it, and the data is a pointer - // to an array for each column. - typedef std::map RowMap; - - typedef RowMap::const_iterator iterator; - - MorkReader(); - ~MorkReader(); - - // Read in the given mork file. Returns true on success. - // Note: currently, only single-table mork files are supported - bool Read(const std::wstring& filename); - - // Returns the list of columns in the current table. - const MorkColumnList& columns() const { return columns_; } - - // Get the "meta row" for the table. Each table has at most one meta row, - // which records information about the table. Like normal rows, the - // meta row contains columns in the same order as returned by columns(). - // Returns null if there is no meta row for this table. - const ColumnDataList& meta_row() const { return meta_row_; } - - // Normalizes the cell value (resolves references to the value map). - // |value| is modified in-place. - void NormalizeValue(std::string* value) const; - - // Allow iteration over the table cells using STL iterators. The iterator's - // |first| will be the row ID, and the iterator's |second| will be a - // pointer to a ColumnDataList containing the cell data. - iterator begin() const { return table_.begin(); } - iterator end() const { return table_.end(); } - - private: - // A convenience typedef for an ID-to-string mapping. - typedef std::map StringMap; - - // A convenience typdef for an ID-to-index mapping, used for the column index - // hashtable. - typedef std::map IndexMap; - - // Parses a line of the file which contains key/value pairs (either - // the column map or the value map). The starting line is parsed starting at - // the given index. Additional lines are read from stream_ if the line ends - // mid-entry. The pairs are added to the map. - bool ParseMap(const std::string& first_line, - size_t start_index, - StringMap* map); - - // Parses a line of the file which contains a table or row definition, - // starting at the given offset within the line. Additional lines are read - // from |stream_| of the line ends mid-row. An entry is added to |table_| - // using the row ID as the key, which contains a column array for the row. - // The supplied column hash table maps from column id to an index in - // |columns_|. - void ParseTable(const std::string& first_line, - size_t start_index, - const IndexMap* column_map); - - // Reads a single logical line from mStream into aLine. - // Any continuation lines are consumed and appended to the line. - bool ReadLine(std::string* line); - - std::ifstream stream_; - - // Lists the names of the columns for the table. - MorkColumnList columns_; - - // Maps hex string IDs to the corrsponding names. - StringMap value_map_; - - // The data of the columns in the meta row. - ColumnDataList meta_row_; - - // The contents of the mork database. This array pointer is owned by this - // class and must be deleted. - RowMap table_; -}; - -// ImportHistoryFromFirefox2 is the main entry point to the importer. -void ImportHistoryFromFirefox2(std::wstring file, MessageLoop* loop, - ProfileWriter* writer); - -#endif // MORK_READER_H__ diff --git a/chrome/browser/title_chomper.cc b/chrome/browser/title_chomper.cc deleted file mode 100644 index 715aeb58..0000000 --- a/chrome/browser/title_chomper.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2006-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 - -#include "chrome/browser/title_chomper.h" - -#include "base/logging.h" -#include "base/word_iterator.h" - -TitleChomper::TitleChomper() { -} - -void TitleChomper::AddTitle(const std::wstring& title) { - titles_.push_back(title); -} - -void TitleChomper::ChompTitles(std::vector* chomped_titles) { - std::vector::iterator title; - for (title = titles_.begin(); title != titles_.end(); ++title) { - std::wstring chomped; - GenerateChompedTitle(*title, &chomped); - chomped_titles->push_back(chomped); - } -} - -void TitleChomper::GenerateChompedTitle(const std::wstring& title, - std::wstring* chomped_title) { - // We don't chomp identical titles, since they would chomp to nothing! - if (title == last_title_) { - *chomped_title = title; - last_words_.clear(); - return; - } - last_title_ = title; - - // TODO(beng): fix locale - WordIterator iter(title, WordIterator::BREAK_WORD); - if (!iter.Init()) - return; - - int chomp_point = 0; - size_t count = 0; - - std::vector words; - - bool record_next_point = false; - bool found_chomp_point = false; - - while (iter.Advance()) { - if (iter.IsWord()) { - const std::wstring fragment = iter.GetWord(); - words.push_back(fragment); - - size_t last_words_size = last_words_.size(); - bool word_mismatch = - (count < last_words_size && last_words_.at(count) != fragment) || - (last_words_size > 0 && count >= last_words_size); - if (!found_chomp_point && word_mismatch) { - // Need to wait until the next word point so that we skip any spaces or - // punctuation at the start of the string. - record_next_point = true; - } - ++count; - } - if (!found_chomp_point && record_next_point) { - chomp_point = iter.prev(); - found_chomp_point = true; - } - } - last_words_ = words; - chomped_title->assign(title.substr(chomp_point)); -} - diff --git a/chrome/browser/title_chomper.h b/chrome/browser/title_chomper.h deleted file mode 100644 index 621da97..0000000 --- a/chrome/browser/title_chomper.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef CHROME_BROWSER_TITLE_CHOMPER_H__ -#define CHROME_BROWSER_TITLE_CHOMPER_H__ - -#include -#include - -class TitleChomper { - public: - TitleChomper(); - - void AddTitle(const std::wstring& title); - - void ChompTitles(std::vector* chomped_titles); - - private: - void GenerateChompedTitle(const std::wstring& title, - std::wstring* chomped_title); - - private: - std::vector titles_; - std::vector last_words_; - std::wstring last_title_; -}; - -#endif // #ifndef CHROME_BROWSER_TITLE_CHOMPER_H__ - diff --git a/chrome/browser/title_chomper_unittest.cc b/chrome/browser/title_chomper_unittest.cc deleted file mode 100644 index 430995a..0000000 --- a/chrome/browser/title_chomper_unittest.cc +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2006-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/title_chomper.h" -#include "googleurl/src/gurl.h" -#include "testing/gtest/include/gtest/gtest.h" - -class TitleChomperTest : public testing::Test { -}; - -TEST_F(TitleChomperTest, BasicCase) { - TitleChomper chomper; - chomper.AddTitle(L"A"); - chomper.AddTitle(L"A B"); - chomper.AddTitle(L"A B C"); - chomper.AddTitle(L"A B C D"); - - std::vector chomped_titles; - chomper.ChompTitles(&chomped_titles); - - EXPECT_EQ(L"A", chomped_titles.at(0)); - EXPECT_EQ(L"B", chomped_titles.at(1)); - EXPECT_EQ(L"C", chomped_titles.at(2)); - EXPECT_EQ(L"D", chomped_titles.at(3)); -} - -TEST_F(TitleChomperTest, LongerTitleBasicCase) { - TitleChomper chomper; - chomper.AddTitle(L"A Q"); - chomper.AddTitle(L"A B Q"); - chomper.AddTitle(L"A B C Q"); - chomper.AddTitle(L"A B C D Q"); - - std::vector chomped_titles; - chomper.ChompTitles(&chomped_titles); - - EXPECT_EQ(L"A Q", chomped_titles.at(0)); - EXPECT_EQ(L"B Q", chomped_titles.at(1)); - EXPECT_EQ(L"C Q", chomped_titles.at(2)); - EXPECT_EQ(L"D Q", chomped_titles.at(3)); -} - -TEST_F(TitleChomperTest, PunctuationCase) { - TitleChomper chomper; - // There should be able to be wacky punctuation and it should still work - chomper.AddTitle(L"A, Q."); - chomper.AddTitle(L"A, B:- Q."); - chomper.AddTitle(L"A, B:- C; Q"); - chomper.AddTitle(L"A B. C D; Q"); - - std::vector chomped_titles; - chomper.ChompTitles(&chomped_titles); - - EXPECT_EQ(L"A, Q.", chomped_titles.at(0)); - EXPECT_EQ(L"B:- Q.", chomped_titles.at(1)); - EXPECT_EQ(L"C; Q", chomped_titles.at(2)); - EXPECT_EQ(L"D; Q", chomped_titles.at(3)); -} - -TEST_F(TitleChomperTest, IdentiticalTitleCase) { - TitleChomper chomper; - chomper.AddTitle(L"A Q"); - chomper.AddTitle(L"A Q"); - chomper.AddTitle(L"A B C Q"); - chomper.AddTitle(L"A B C Q"); - chomper.AddTitle(L"A B C Q"); - - std::vector chomped_titles; - chomper.ChompTitles(&chomped_titles); - - EXPECT_EQ(L"A Q", chomped_titles.at(0)); - EXPECT_EQ(L"A Q", chomped_titles.at(1)); - EXPECT_EQ(L"A B C Q", chomped_titles.at(2)); - EXPECT_EQ(L"A B C Q", chomped_titles.at(3)); - EXPECT_EQ(L"A B C Q", chomped_titles.at(4)); -} - -TEST_F(TitleChomperTest, CraigslistCase) { - TitleChomper chomper; - chomper.AddTitle(L"craigslist: san francisco bay area classifieds for jobs, apartments, personals, for sale, services, community, and events"); - chomper.AddTitle(L"craigslist | cars & trucks"); - chomper.AddTitle(L"s.f. bayarea craigslist > > cars & trucks: search"); - chomper.AddTitle(L"s.f. bayarea craigslist > peninsula > cars & trucks: search"); - chomper.AddTitle(L"s.f. bayarea craigslist > peninsula > cars & trucks: search for \"tacoma\""); - - std::vector chomped_titles; - chomper.ChompTitles(&chomped_titles); - - EXPECT_EQ(L"craigslist: san francisco bay area classifieds for jobs, apartments, personals, for sale, services, community, and events", chomped_titles.at(0)); - EXPECT_EQ(L"cars & trucks", chomped_titles.at(1)); - EXPECT_EQ(L"s.f. bayarea craigslist > > cars & trucks: search", chomped_titles.at(2)); - EXPECT_EQ(L"peninsula > cars & trucks: search", chomped_titles.at(3)); - EXPECT_EQ(L"for \"tacoma\"", chomped_titles.at(4)); -} diff --git a/chrome/browser/views/first_run_customize_view.cc b/chrome/browser/views/first_run_customize_view.cc index a23524e..d5c91351 100644 --- a/chrome/browser/views/first_run_customize_view.cc +++ b/chrome/browser/views/first_run_customize_view.cc @@ -6,7 +6,7 @@ #include "chrome/app/locales/locale_settings.h" #include "chrome/app/theme/theme_resources.h" -#include "chrome/browser/importer.h" +#include "chrome/browser/importer/importer.h" #include "chrome/browser/first_run.h" #include "chrome/browser/shell_integration.h" #include "chrome/browser/standard_layout.h" diff --git a/chrome/browser/views/first_run_view.cc b/chrome/browser/views/first_run_view.cc index 690cd72..e4fc454 100644 --- a/chrome/browser/views/first_run_view.cc +++ b/chrome/browser/views/first_run_view.cc @@ -6,7 +6,7 @@ #include "chrome/app/locales/locale_settings.h" #include "chrome/app/theme/theme_resources.h" -#include "chrome/browser/importer.h" +#include "chrome/browser/importer/importer.h" #include "chrome/browser/first_run.h" #include "chrome/browser/standard_layout.h" #include "chrome/browser/views/first_run_customize_view.h" diff --git a/chrome/browser/views/first_run_view_base.h b/chrome/browser/views/first_run_view_base.h index d0fb0fe..da0fe65 100644 --- a/chrome/browser/views/first_run_view_base.h +++ b/chrome/browser/views/first_run_view_base.h @@ -4,7 +4,8 @@ #ifndef CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_BASE_H__ #define CHROME_BROWSER_VIEWS_FIRST_RUN_VIEW_BASE_H__ -#include "chrome/browser/importer.h" + +#include "chrome/browser/importer/importer.h" #include "chrome/views/dialog_delegate.h" #include "chrome/views/view.h" diff --git a/chrome/browser/views/importer_view.h b/chrome/browser/views/importer_view.h index 8f40a4b..18219f9 100644 --- a/chrome/browser/views/importer_view.h +++ b/chrome/browser/views/importer_view.h @@ -5,7 +5,7 @@ #ifndef CHROME_BROWSER_VIEWS_IMPORTER_VIEW_H__ #define CHROME_BROWSER_VIEWS_IMPORTER_VIEW_H__ -#include "chrome/browser/importer.h" +#include "chrome/browser/importer/importer.h" #include "chrome/views/combo_box.h" #include "chrome/views/dialog_delegate.h" #include "chrome/views/native_button.h" diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj index 39bb9b2..aa849f3 100644 --- a/chrome/test/unit/unittests.vcproj +++ b/chrome/test/unit/unittests.vcproj @@ -523,14 +523,6 @@ - - - - -- cgit v1.1