1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/importer/in_process_importer_bridge.h"
6
7#include "base/bind.h"
8#include "base/debug/dump_without_crashing.h"
9#include "base/files/file_util.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/importer/external_process_importer_host.h"
13#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
14#include "chrome/common/importer/imported_bookmark_entry.h"
15#include "chrome/common/importer/imported_favicon_usage.h"
16#include "chrome/common/importer/importer_autofill_form_data_entry.h"
17#include "components/autofill/core/browser/webdata/autofill_entry.h"
18#include "components/autofill/core/common/password_form.h"
19#include "components/search_engines/template_url.h"
20#include "components/search_engines/template_url_parser.h"
21#include "components/search_engines/template_url_prepopulate_data.h"
22#include "content/public/browser/browser_thread.h"
23#include "ui/base/l10n/l10n_util.h"
24
25#if defined(OS_WIN)
26#include "components/os_crypt/ie7_password_win.h"
27#endif
28
29#include <iterator>
30
31namespace {
32
33history::URLRows ConvertImporterURLRowsToHistoryURLRows(
34    const std::vector<ImporterURLRow>& rows) {
35  history::URLRows converted;
36  converted.reserve(rows.size());
37  for (std::vector<ImporterURLRow>::const_iterator it = rows.begin();
38       it != rows.end(); ++it) {
39    history::URLRow row(it->url);
40    row.set_title(it->title);
41    row.set_visit_count(it->visit_count);
42    row.set_typed_count(it->typed_count);
43    row.set_last_visit(it->last_visit);
44    row.set_hidden(it->hidden);
45    converted.push_back(row);
46  }
47  return converted;
48}
49
50history::VisitSource ConvertImporterVisitSourceToHistoryVisitSource(
51    importer::VisitSource visit_source) {
52  switch (visit_source) {
53    case importer::VISIT_SOURCE_BROWSED:
54      return history::SOURCE_BROWSED;
55    case importer::VISIT_SOURCE_FIREFOX_IMPORTED:
56      return history::SOURCE_FIREFOX_IMPORTED;
57    case importer::VISIT_SOURCE_IE_IMPORTED:
58      return history::SOURCE_IE_IMPORTED;
59    case importer::VISIT_SOURCE_SAFARI_IMPORTED:
60      return history::SOURCE_SAFARI_IMPORTED;
61  }
62  NOTREACHED();
63  return history::SOURCE_SYNCED;
64}
65
66// http://crbug.com/404012. Let's see where the empty fields come from.
67void CheckForEmptyUsernameAndPassword(const autofill::PasswordForm& form) {
68  if (form.username_value.empty() &&
69      form.password_value.empty() &&
70      !form.blacklisted_by_user) {
71    base::debug::DumpWithoutCrashing();
72  }
73}
74
75}  // namespace
76
77using content::BrowserThread;
78
79namespace {
80
81// FirefoxURLParameterFilter is used to remove parameter mentioning Firefox from
82// the search URL when importing search engines.
83class FirefoxURLParameterFilter : public TemplateURLParser::ParameterFilter {
84 public:
85  FirefoxURLParameterFilter() {}
86  virtual ~FirefoxURLParameterFilter() {}
87
88  // TemplateURLParser::ParameterFilter method.
89  virtual bool KeepParameter(const std::string& key,
90                             const std::string& value) OVERRIDE {
91    std::string low_value = base::StringToLowerASCII(value);
92    if (low_value.find("mozilla") != std::string::npos ||
93        low_value.find("firefox") != std::string::npos ||
94        low_value.find("moz:") != std::string::npos) {
95      return false;
96    }
97    return true;
98  }
99
100 private:
101  DISALLOW_COPY_AND_ASSIGN(FirefoxURLParameterFilter);
102};
103
104// Creates a TemplateURL with the |keyword| and |url|. |title| may be empty.
105// This function transfers ownership of the created TemplateURL to the caller.
106TemplateURL* CreateTemplateURL(const base::string16& title,
107                               const base::string16& keyword,
108                               const GURL& url) {
109  // Skip if the url is invalid.
110  if (!url.is_valid())
111    return NULL;
112
113  TemplateURLData data;
114  if (keyword.empty())
115    data.SetKeyword(TemplateURL::GenerateKeyword(url));
116  else
117    data.SetKeyword(keyword);
118  // We set short name by using the title if it exists.
119  // Otherwise, we use the shortcut.
120  data.short_name = title.empty() ? keyword : title;
121  data.SetURL(
122      TemplateURLRef::DisplayURLToURLRef(base::UTF8ToUTF16(url.spec())));
123  return new TemplateURL(data);
124}
125
126// Parses the OpenSearch XML files in |xml_files| and populates |search_engines|
127// with the resulting TemplateURLs.
128void ParseSearchEnginesFromFirefoxXMLData(
129    const std::vector<std::string>& xml_data,
130    std::vector<TemplateURL*>* search_engines) {
131  DCHECK(search_engines);
132
133  typedef std::map<std::string, TemplateURL*> SearchEnginesMap;
134  SearchEnginesMap search_engine_for_url;
135  FirefoxURLParameterFilter param_filter;
136  // The first XML file represents the default search engine in Firefox 3, so we
137  // need to keep it on top of the list.
138  SearchEnginesMap::const_iterator default_turl = search_engine_for_url.end();
139  for (std::vector<std::string>::const_iterator xml_iter =
140           xml_data.begin(); xml_iter != xml_data.end(); ++xml_iter) {
141    TemplateURL* template_url = TemplateURLParser::Parse(
142        UIThreadSearchTermsData(NULL), true,
143        xml_iter->data(), xml_iter->length(), &param_filter);
144    if (template_url) {
145      SearchEnginesMap::iterator iter =
146          search_engine_for_url.find(template_url->url());
147      if (iter == search_engine_for_url.end()) {
148        iter = search_engine_for_url.insert(
149            std::make_pair(template_url->url(), template_url)).first;
150      } else {
151        // We have already found a search engine with the same URL.  We give
152        // priority to the latest one found, as GetSearchEnginesXMLFiles()
153        // returns a vector with first Firefox default search engines and then
154        // the user's ones.  We want to give priority to the user ones.
155        delete iter->second;
156        iter->second = template_url;
157      }
158      if (default_turl == search_engine_for_url.end())
159        default_turl = iter;
160    }
161  }
162
163  // Put the results in the |search_engines| vector.
164  for (SearchEnginesMap::iterator t_iter = search_engine_for_url.begin();
165       t_iter != search_engine_for_url.end(); ++t_iter) {
166    if (t_iter == default_turl)
167      search_engines->insert(search_engines->begin(), default_turl->second);
168    else
169      search_engines->push_back(t_iter->second);
170  }
171}
172
173}  // namespace
174
175InProcessImporterBridge::InProcessImporterBridge(
176    ProfileWriter* writer,
177    base::WeakPtr<ExternalProcessImporterHost> host) : writer_(writer),
178                                                       host_(host) {
179}
180
181void InProcessImporterBridge::AddBookmarks(
182    const std::vector<ImportedBookmarkEntry>& bookmarks,
183    const base::string16& first_folder_name) {
184  BrowserThread::PostTask(
185      BrowserThread::UI, FROM_HERE,
186      base::Bind(&ProfileWriter::AddBookmarks, writer_, bookmarks,
187                 first_folder_name));
188}
189
190void InProcessImporterBridge::AddHomePage(const GURL& home_page) {
191  BrowserThread::PostTask(
192      BrowserThread::UI, FROM_HERE,
193      base::Bind(&ProfileWriter::AddHomepage, writer_, home_page));
194}
195
196#if defined(OS_WIN)
197void InProcessImporterBridge::AddIE7PasswordInfo(
198    const importer::ImporterIE7PasswordInfo& password_info) {
199  IE7PasswordInfo ie7_password_info;
200  ie7_password_info.url_hash = password_info.url_hash;
201  ie7_password_info.encrypted_data = password_info.encrypted_data;
202  ie7_password_info.date_created = password_info.date_created;
203
204  BrowserThread::PostTask(
205      BrowserThread::UI, FROM_HERE,
206      base::Bind(&ProfileWriter::AddIE7PasswordInfo, writer_,
207                 ie7_password_info));
208}
209#endif  // OS_WIN
210
211void InProcessImporterBridge::SetFavicons(
212    const std::vector<ImportedFaviconUsage>& favicons) {
213  BrowserThread::PostTask(
214      BrowserThread::UI, FROM_HERE,
215      base::Bind(&ProfileWriter::AddFavicons, writer_, favicons));
216}
217
218void InProcessImporterBridge::SetHistoryItems(
219    const std::vector<ImporterURLRow>& rows,
220    importer::VisitSource visit_source) {
221  history::URLRows converted_rows =
222      ConvertImporterURLRowsToHistoryURLRows(rows);
223  history::VisitSource converted_visit_source =
224      ConvertImporterVisitSourceToHistoryVisitSource(visit_source);
225  BrowserThread::PostTask(BrowserThread::UI,
226                          FROM_HERE,
227                          base::Bind(&ProfileWriter::AddHistoryPage,
228                                     writer_,
229                                     converted_rows,
230                                     converted_visit_source));
231}
232
233void InProcessImporterBridge::SetKeywords(
234    const std::vector<importer::URLKeywordInfo>& url_keywords,
235    bool unique_on_host_and_path) {
236  ScopedVector<TemplateURL> owned_template_urls;
237  for (size_t i = 0; i < url_keywords.size(); ++i) {
238    owned_template_urls.push_back(
239        CreateTemplateURL(url_keywords[i].display_name,
240                          url_keywords[i].keyword,
241                          url_keywords[i].url));
242  }
243  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
244      base::Bind(&ProfileWriter::AddKeywords, writer_,
245                 base::Passed(&owned_template_urls), unique_on_host_and_path));
246}
247
248void InProcessImporterBridge::SetFirefoxSearchEnginesXMLData(
249    const std::vector<std::string>& search_engine_data) {
250  std::vector<TemplateURL*> search_engines;
251  ParseSearchEnginesFromFirefoxXMLData(search_engine_data, &search_engines);
252
253  ScopedVector<TemplateURL> owned_template_urls;
254  std::copy(search_engines.begin(), search_engines.end(),
255            std::back_inserter(owned_template_urls));
256
257  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
258      base::Bind(&ProfileWriter::AddKeywords, writer_,
259                 base::Passed(&owned_template_urls), true));
260}
261
262void InProcessImporterBridge::SetPasswordForm(
263    const autofill::PasswordForm& form) {
264  CheckForEmptyUsernameAndPassword(form);
265  BrowserThread::PostTask(
266      BrowserThread::UI, FROM_HERE,
267      base::Bind(&ProfileWriter::AddPasswordForm, writer_, form));
268}
269
270void InProcessImporterBridge::SetAutofillFormData(
271    const std::vector<ImporterAutofillFormDataEntry>& entries) {
272  std::vector<autofill::AutofillEntry> autofill_entries;
273  for (size_t i = 0; i < entries.size(); ++i) {
274    autofill_entries.push_back(autofill::AutofillEntry(
275        autofill::AutofillKey(entries[i].name, entries[i].value),
276        entries[i].first_used,
277        entries[i].last_used));
278  }
279
280  BrowserThread::PostTask(BrowserThread::UI,
281                          FROM_HERE,
282                          base::Bind(&ProfileWriter::AddAutofillFormDataEntries,
283                                     writer_,
284                                     autofill_entries));
285}
286
287void InProcessImporterBridge::NotifyStarted() {
288  BrowserThread::PostTask(
289      BrowserThread::UI, FROM_HERE,
290      base::Bind(&ExternalProcessImporterHost::NotifyImportStarted, host_));
291}
292
293void InProcessImporterBridge::NotifyItemStarted(importer::ImportItem item) {
294  BrowserThread::PostTask(
295      BrowserThread::UI, FROM_HERE,
296      base::Bind(&ExternalProcessImporterHost::NotifyImportItemStarted,
297                 host_, item));
298}
299
300void InProcessImporterBridge::NotifyItemEnded(importer::ImportItem item) {
301  BrowserThread::PostTask(
302      BrowserThread::UI, FROM_HERE,
303      base::Bind(&ExternalProcessImporterHost::NotifyImportItemEnded,
304                 host_, item));
305}
306
307void InProcessImporterBridge::NotifyEnded() {
308  BrowserThread::PostTask(
309      BrowserThread::UI, FROM_HERE,
310      base::Bind(&ExternalProcessImporterHost::NotifyImportEnded, host_));
311}
312
313base::string16 InProcessImporterBridge::GetLocalizedString(int message_id) {
314  return l10n_util::GetStringUTF16(message_id);
315}
316
317InProcessImporterBridge::~InProcessImporterBridge() {}
318