1 // Copyright (c) 2011 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/ie_importer.h"
6
7 #include <ole2.h>
8 #include <intshcut.h>
9 #include <pstore.h>
10 #include <shlobj.h>
11 #include <urlhist.h>
12
13 #include <algorithm>
14 #include <map>
15 #include <string>
16 #include <vector>
17
18 #include "app/win/scoped_co_mem.h"
19 #include "app/win/scoped_com_initializer.h"
20 #include "base/file_path.h"
21 #include "base/file_util.h"
22 #include "base/string_split.h"
23 #include "base/string_util.h"
24 #include "base/time.h"
25 #include "base/utf_string_conversions.h"
26 #include "base/win/registry.h"
27 #include "base/win/scoped_comptr.h"
28 #include "base/win/scoped_handle.h"
29 #include "base/win/windows_version.h"
30 #include "chrome/browser/importer/importer_bridge.h"
31 #include "chrome/browser/importer/importer_data_types.h"
32 #include "chrome/browser/password_manager/ie7_password.h"
33 #include "chrome/browser/search_engines/template_url.h"
34 #include "chrome/browser/search_engines/template_url_model.h"
35 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
36 #include "chrome/common/time_format.h"
37 #include "chrome/common/url_constants.h"
38 #include "googleurl/src/gurl.h"
39 #include "grit/generated_resources.h"
40 #include "ui/base/l10n/l10n_util.h"
41 #include "webkit/glue/password_form.h"
42
43 namespace {
44
45 // A struct that hosts the information of AutoComplete data in PStore.
46 struct AutoCompleteInfo {
47 std::wstring key;
48 std::vector<std::wstring> data;
49 bool is_url;
50 };
51
52 // Gets the creation time of the given file or directory.
GetFileCreationTime(const std::wstring & file)53 base::Time GetFileCreationTime(const std::wstring& file) {
54 base::Time creation_time;
55 base::win::ScopedHandle file_handle(
56 CreateFile(file.c_str(), GENERIC_READ,
57 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
58 NULL, OPEN_EXISTING,
59 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL));
60 FILETIME creation_filetime;
61 if (GetFileTime(file_handle, &creation_filetime, NULL, NULL))
62 creation_time = base::Time::FromFileTime(creation_filetime);
63 return creation_time;
64 }
65
66 } // namespace
67
68 // static
69 // {E161255A-37C3-11D2-BCAA-00C04fD929DB}
70 const GUID IEImporter::kPStoreAutocompleteGUID = {
71 0xe161255a, 0x37c3, 0x11d2,
72 { 0xbc, 0xaa, 0x00, 0xc0, 0x4f, 0xd9, 0x29, 0xdb }
73 };
74 // {A79029D6-753E-4e27-B807-3D46AB1545DF}
75 const GUID IEImporter::kUnittestGUID = {
76 0xa79029d6, 0x753e, 0x4e27,
77 { 0xb8, 0x7, 0x3d, 0x46, 0xab, 0x15, 0x45, 0xdf }
78 };
79
IEImporter()80 IEImporter::IEImporter() {
81 }
82
StartImport(const importer::SourceProfile & source_profile,uint16 items,ImporterBridge * bridge)83 void IEImporter::StartImport(const importer::SourceProfile& source_profile,
84 uint16 items,
85 ImporterBridge* bridge) {
86 bridge_ = bridge;
87 source_path_ = source_profile.source_path;
88
89 bridge_->NotifyStarted();
90
91 // Some IE settings (such as Protected Storage) are obtained via COM APIs.
92 app::win::ScopedCOMInitializer com_initializer;
93
94 if ((items & importer::HOME_PAGE) && !cancelled())
95 ImportHomepage(); // Doesn't have a UI item.
96 // The order here is important!
97 if ((items & importer::HISTORY) && !cancelled()) {
98 bridge_->NotifyItemStarted(importer::HISTORY);
99 ImportHistory();
100 bridge_->NotifyItemEnded(importer::HISTORY);
101 }
102 if ((items & importer::FAVORITES) && !cancelled()) {
103 bridge_->NotifyItemStarted(importer::FAVORITES);
104 ImportFavorites();
105 bridge_->NotifyItemEnded(importer::FAVORITES);
106 }
107 if ((items & importer::SEARCH_ENGINES) && !cancelled()) {
108 bridge_->NotifyItemStarted(importer::SEARCH_ENGINES);
109 ImportSearchEngines();
110 bridge_->NotifyItemEnded(importer::SEARCH_ENGINES);
111 }
112 if ((items & importer::PASSWORDS) && !cancelled()) {
113 bridge_->NotifyItemStarted(importer::PASSWORDS);
114 // Always import IE6 passwords.
115 ImportPasswordsIE6();
116
117 if (CurrentIEVersion() >= 7)
118 ImportPasswordsIE7();
119 bridge_->NotifyItemEnded(importer::PASSWORDS);
120 }
121 bridge_->NotifyEnded();
122 }
123
~IEImporter()124 IEImporter::~IEImporter() {
125 }
126
ImportFavorites()127 void IEImporter::ImportFavorites() {
128 std::wstring path;
129
130 FavoritesInfo info;
131 if (!GetFavoritesInfo(&info))
132 return;
133
134 BookmarkVector bookmarks;
135 ParseFavoritesFolder(info, &bookmarks);
136
137 if (!bookmarks.empty() && !cancelled()) {
138 const string16& first_folder_name =
139 l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_IE);
140 int options = 0;
141 if (import_to_bookmark_bar())
142 options = ProfileWriter::IMPORT_TO_BOOKMARK_BAR;
143 bridge_->AddBookmarkEntries(bookmarks, first_folder_name, options);
144 }
145 }
146
ImportHistory()147 void IEImporter::ImportHistory() {
148 const std::string kSchemes[] = {chrome::kHttpScheme,
149 chrome::kHttpsScheme,
150 chrome::kFtpScheme,
151 chrome::kFileScheme};
152 int total_schemes = arraysize(kSchemes);
153
154 base::win::ScopedComPtr<IUrlHistoryStg2> url_history_stg2;
155 HRESULT result;
156 result = url_history_stg2.CreateInstance(CLSID_CUrlHistory, NULL,
157 CLSCTX_INPROC_SERVER);
158 if (FAILED(result))
159 return;
160 base::win::ScopedComPtr<IEnumSTATURL> enum_url;
161 if (SUCCEEDED(result = url_history_stg2->EnumUrls(enum_url.Receive()))) {
162 std::vector<history::URLRow> rows;
163 STATURL stat_url;
164 ULONG fetched;
165 while (!cancelled() &&
166 (result = enum_url->Next(1, &stat_url, &fetched)) == S_OK) {
167 std::wstring url_string;
168 std::wstring title_string;
169 if (stat_url.pwcsUrl) {
170 url_string = stat_url.pwcsUrl;
171 CoTaskMemFree(stat_url.pwcsUrl);
172 }
173 if (stat_url.pwcsTitle) {
174 title_string = stat_url.pwcsTitle;
175 CoTaskMemFree(stat_url.pwcsTitle);
176 }
177
178 GURL url(url_string);
179 // Skips the URLs that are invalid or have other schemes.
180 if (!url.is_valid() ||
181 (std::find(kSchemes, kSchemes + total_schemes, url.scheme()) ==
182 kSchemes + total_schemes))
183 continue;
184
185 history::URLRow row(url);
186 row.set_title(title_string);
187 row.set_last_visit(base::Time::FromFileTime(stat_url.ftLastVisited));
188 if (stat_url.dwFlags == STATURL_QUERYFLAG_TOPLEVEL) {
189 row.set_visit_count(1);
190 row.set_hidden(false);
191 } else {
192 row.set_hidden(true);
193 }
194
195 rows.push_back(row);
196 }
197
198 if (!rows.empty() && !cancelled()) {
199 bridge_->SetHistoryItems(rows, history::SOURCE_IE_IMPORTED);
200 }
201 }
202 }
203
ImportPasswordsIE6()204 void IEImporter::ImportPasswordsIE6() {
205 GUID AutocompleteGUID = kPStoreAutocompleteGUID;
206 if (!source_path_.empty()) {
207 // We supply a fake GUID for testting.
208 AutocompleteGUID = kUnittestGUID;
209 }
210
211 // The PStoreCreateInstance function retrieves an interface pointer
212 // to a storage provider. But this function has no associated import
213 // library or header file, we must call it using the LoadLibrary()
214 // and GetProcAddress() functions.
215 typedef HRESULT (WINAPI *PStoreCreateFunc)(IPStore**, DWORD, DWORD, DWORD);
216 HMODULE pstorec_dll = LoadLibrary(L"pstorec.dll");
217 if (!pstorec_dll)
218 return;
219 PStoreCreateFunc PStoreCreateInstance =
220 (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance");
221 if (!PStoreCreateInstance) {
222 FreeLibrary(pstorec_dll);
223 return;
224 }
225
226 base::win::ScopedComPtr<IPStore, &IID_IPStore> pstore;
227 HRESULT result = PStoreCreateInstance(pstore.Receive(), 0, 0, 0);
228 if (result != S_OK) {
229 FreeLibrary(pstorec_dll);
230 return;
231 }
232
233 std::vector<AutoCompleteInfo> ac_list;
234
235 // Enumerates AutoComplete items in the protected database.
236 base::win::ScopedComPtr<IEnumPStoreItems, &IID_IEnumPStoreItems> item;
237 result = pstore->EnumItems(0, &AutocompleteGUID,
238 &AutocompleteGUID, 0, item.Receive());
239 if (result != PST_E_OK) {
240 pstore.Release();
241 FreeLibrary(pstorec_dll);
242 return;
243 }
244
245 wchar_t* item_name;
246 while (!cancelled() && SUCCEEDED(item->Next(1, &item_name, 0))) {
247 DWORD length = 0;
248 unsigned char* buffer = NULL;
249 result = pstore->ReadItem(0, &AutocompleteGUID, &AutocompleteGUID,
250 item_name, &length, &buffer, NULL, 0);
251 if (SUCCEEDED(result)) {
252 AutoCompleteInfo ac;
253 ac.key = item_name;
254 std::wstring data;
255 data.insert(0, reinterpret_cast<wchar_t*>(buffer),
256 length / sizeof(wchar_t));
257
258 // The key name is always ended with ":StringData".
259 const wchar_t kDataSuffix[] = L":StringData";
260 size_t i = ac.key.rfind(kDataSuffix);
261 if (i != std::wstring::npos && ac.key.substr(i) == kDataSuffix) {
262 ac.key.erase(i);
263 ac.is_url = (ac.key.find(L"://") != std::wstring::npos);
264 ac_list.push_back(ac);
265 base::SplitString(data, L'\0', &ac_list[ac_list.size() - 1].data);
266 }
267 CoTaskMemFree(buffer);
268 }
269 CoTaskMemFree(item_name);
270 }
271 // Releases them before unload the dll.
272 item.Release();
273 pstore.Release();
274 FreeLibrary(pstorec_dll);
275
276 size_t i;
277 for (i = 0; i < ac_list.size(); i++) {
278 if (!ac_list[i].is_url || ac_list[i].data.size() < 2)
279 continue;
280
281 GURL url(ac_list[i].key.c_str());
282 if (!(LowerCaseEqualsASCII(url.scheme(), chrome::kHttpScheme) ||
283 LowerCaseEqualsASCII(url.scheme(), chrome::kHttpsScheme))) {
284 continue;
285 }
286
287 webkit_glue::PasswordForm form;
288 GURL::Replacements rp;
289 rp.ClearUsername();
290 rp.ClearPassword();
291 rp.ClearQuery();
292 rp.ClearRef();
293 form.origin = url.ReplaceComponents(rp);
294 form.username_value = ac_list[i].data[0];
295 form.password_value = ac_list[i].data[1];
296 form.signon_realm = url.GetOrigin().spec();
297
298 // This is not precise, because a scheme of https does not imply a valid
299 // certificate was presented; however we assign it this way so that if we
300 // import a password from IE whose scheme is https, we give it the benefit
301 // of the doubt and DONT auto-fill it unless the form appears under
302 // valid SSL conditions.
303 form.ssl_valid = url.SchemeIsSecure();
304
305 // Goes through the list to find out the username field
306 // of the web page.
307 size_t list_it, item_it;
308 for (list_it = 0; list_it < ac_list.size(); ++list_it) {
309 if (ac_list[list_it].is_url)
310 continue;
311
312 for (item_it = 0; item_it < ac_list[list_it].data.size(); ++item_it)
313 if (ac_list[list_it].data[item_it] == form.username_value) {
314 form.username_element = ac_list[list_it].key;
315 break;
316 }
317 }
318
319 bridge_->SetPasswordForm(form);
320 }
321 }
322
ImportPasswordsIE7()323 void IEImporter::ImportPasswordsIE7() {
324 if (!source_path_.empty()) {
325 // We have been called from the unit tests. Don't import real passwords.
326 return;
327 }
328
329 const wchar_t* kStorage2Path =
330 L"Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
331
332 base::win::RegKey key(HKEY_CURRENT_USER, kStorage2Path, KEY_READ);
333 base::win::RegistryValueIterator reg_iterator(HKEY_CURRENT_USER,
334 kStorage2Path);
335 IE7PasswordInfo password_info;
336 while (reg_iterator.Valid() && !cancelled()) {
337 // Get the size of the encrypted data.
338 DWORD value_len = 0;
339 key.ReadValue(reg_iterator.Name(), NULL, &value_len, NULL);
340 if (value_len) {
341 // Query the encrypted data.
342 password_info.encrypted_data.resize(value_len);
343 if (key.ReadValue(reg_iterator.Name(),
344 &password_info.encrypted_data.front(),
345 &value_len, NULL) == ERROR_SUCCESS) {
346 password_info.url_hash = reg_iterator.Name();
347 password_info.date_created = base::Time::Now();
348
349 bridge_->AddIE7PasswordInfo(password_info);
350 }
351 }
352
353 ++reg_iterator;
354 }
355 }
356
ImportSearchEngines()357 void IEImporter::ImportSearchEngines() {
358 // On IE, search engines are stored in the registry, under:
359 // Software\Microsoft\Internet Explorer\SearchScopes
360 // Each key represents a search engine. The URL value contains the URL and
361 // the DisplayName the name.
362 // The default key's name is contained under DefaultScope.
363 const wchar_t* kSearchScopePath =
364 L"Software\\Microsoft\\Internet Explorer\\SearchScopes";
365
366 base::win::RegKey key(HKEY_CURRENT_USER, kSearchScopePath, KEY_READ);
367 std::wstring default_search_engine_name;
368 const TemplateURL* default_search_engine = NULL;
369 std::map<std::string, TemplateURL*> search_engines_map;
370 key.ReadValue(L"DefaultScope", &default_search_engine_name);
371 base::win::RegistryKeyIterator key_iterator(HKEY_CURRENT_USER,
372 kSearchScopePath);
373 while (key_iterator.Valid()) {
374 std::wstring sub_key_name = kSearchScopePath;
375 sub_key_name.append(L"\\").append(key_iterator.Name());
376 base::win::RegKey sub_key(HKEY_CURRENT_USER, sub_key_name.c_str(),
377 KEY_READ);
378 std::wstring wide_url;
379 if ((sub_key.ReadValue(L"URL", &wide_url) != ERROR_SUCCESS) ||
380 wide_url.empty()) {
381 VLOG(1) << "No URL for IE search engine at " << key_iterator.Name();
382 ++key_iterator;
383 continue;
384 }
385 // For the name, we try the default value first (as Live Search uses a
386 // non displayable name in DisplayName, and the readable name under the
387 // default value).
388 std::wstring name;
389 if ((sub_key.ReadValue(NULL, &name) != ERROR_SUCCESS) || name.empty()) {
390 // Try the displayable name.
391 if ((sub_key.ReadValue(L"DisplayName", &name) != ERROR_SUCCESS) ||
392 name.empty()) {
393 VLOG(1) << "No name for IE search engine at " << key_iterator.Name();
394 ++key_iterator;
395 continue;
396 }
397 }
398
399 std::string url(WideToUTF8(wide_url));
400 std::map<std::string, TemplateURL*>::iterator t_iter =
401 search_engines_map.find(url);
402 TemplateURL* template_url =
403 (t_iter != search_engines_map.end()) ? t_iter->second : NULL;
404 if (!template_url) {
405 // First time we see that URL.
406 template_url = new TemplateURL();
407 template_url->set_short_name(name);
408 template_url->SetURL(url, 0, 0);
409 // Give this a keyword to facilitate tab-to-search, if possible.
410 GURL gurl = GURL(url);
411 template_url->set_keyword(TemplateURLModel::GenerateKeyword(gurl,
412 false));
413 template_url->set_logo_id(
414 TemplateURLPrepopulateData::GetSearchEngineLogo(gurl));
415 template_url->set_show_in_default_list(true);
416 search_engines_map[url] = template_url;
417 }
418 if (template_url && key_iterator.Name() == default_search_engine_name) {
419 DCHECK(!default_search_engine);
420 default_search_engine = template_url;
421 }
422 ++key_iterator;
423 }
424
425 // ProfileWriter::AddKeywords() requires a vector and we have a map.
426 std::map<std::string, TemplateURL*>::iterator t_iter;
427 std::vector<TemplateURL*> search_engines;
428 int default_search_engine_index = -1;
429 for (t_iter = search_engines_map.begin(); t_iter != search_engines_map.end();
430 ++t_iter) {
431 search_engines.push_back(t_iter->second);
432 if (default_search_engine == t_iter->second) {
433 default_search_engine_index =
434 static_cast<int>(search_engines.size()) - 1;
435 }
436 }
437 bridge_->SetKeywords(search_engines, default_search_engine_index, true);
438 }
439
ImportHomepage()440 void IEImporter::ImportHomepage() {
441 const wchar_t* kIESettingsMain =
442 L"Software\\Microsoft\\Internet Explorer\\Main";
443 const wchar_t* kIEHomepage = L"Start Page";
444 const wchar_t* kIEDefaultHomepage = L"Default_Page_URL";
445
446 base::win::RegKey key(HKEY_CURRENT_USER, kIESettingsMain, KEY_READ);
447 std::wstring homepage_url;
448 if (key.ReadValue(kIEHomepage, &homepage_url) != ERROR_SUCCESS ||
449 homepage_url.empty())
450 return;
451
452 GURL homepage = GURL(homepage_url);
453 if (!homepage.is_valid())
454 return;
455
456 // Check to see if this is the default website and skip import.
457 base::win::RegKey keyDefault(HKEY_LOCAL_MACHINE, kIESettingsMain, KEY_READ);
458 std::wstring default_homepage_url;
459 LONG result = keyDefault.ReadValue(kIEDefaultHomepage, &default_homepage_url);
460 if (result == ERROR_SUCCESS && !default_homepage_url.empty()) {
461 if (homepage.spec() == GURL(default_homepage_url).spec())
462 return;
463 }
464
465 bridge_->AddHomePage(homepage);
466 }
467
ResolveInternetShortcut(const std::wstring & file)468 std::wstring IEImporter::ResolveInternetShortcut(const std::wstring& file) {
469 app::win::ScopedCoMem<wchar_t> url;
470 base::win::ScopedComPtr<IUniformResourceLocator> url_locator;
471 HRESULT result = url_locator.CreateInstance(CLSID_InternetShortcut, NULL,
472 CLSCTX_INPROC_SERVER);
473 if (FAILED(result))
474 return std::wstring();
475
476 base::win::ScopedComPtr<IPersistFile> persist_file;
477 result = persist_file.QueryFrom(url_locator);
478 if (FAILED(result))
479 return std::wstring();
480
481 // Loads the Internet Shortcut from persistent storage.
482 result = persist_file->Load(file.c_str(), STGM_READ);
483 if (FAILED(result))
484 return std::wstring();
485
486 result = url_locator->GetURL(&url);
487 // GetURL can return S_FALSE (FAILED(S_FALSE) is false) when url == NULL.
488 if (FAILED(result) || (url == NULL))
489 return std::wstring();
490
491 return std::wstring(url);
492 }
493
GetFavoritesInfo(IEImporter::FavoritesInfo * info)494 bool IEImporter::GetFavoritesInfo(IEImporter::FavoritesInfo* info) {
495 if (!source_path_.empty()) {
496 // Source path exists during testing.
497 info->path = source_path_;
498 info->path = info->path.AppendASCII("Favorites");
499 info->links_folder = L"Links";
500 return true;
501 }
502
503 // IE stores the favorites in the Favorites under user profile's folder.
504 wchar_t buffer[MAX_PATH];
505 if (FAILED(SHGetFolderPath(NULL, CSIDL_FAVORITES, NULL,
506 SHGFP_TYPE_CURRENT, buffer)))
507 return false;
508 info->path = FilePath(buffer);
509
510 // There is a Links folder under Favorites folder in Windows Vista, but it
511 // is not recording in Vista's registry. So in Vista, we assume the Links
512 // folder is under Favorites folder since it looks like there is not name
513 // different in every language version of Windows Vista.
514 if (base::win::GetVersion() < base::win::VERSION_VISTA) {
515 // The Link folder name is stored in the registry.
516 DWORD buffer_length = sizeof(buffer);
517 base::win::RegKey reg_key(HKEY_CURRENT_USER,
518 L"Software\\Microsoft\\Internet Explorer\\Toolbar", KEY_READ);
519 if (reg_key.ReadValue(L"LinksFolderName", buffer,
520 &buffer_length, NULL) != ERROR_SUCCESS)
521 return false;
522 info->links_folder = buffer;
523 } else {
524 info->links_folder = L"Links";
525 }
526
527 return true;
528 }
529
ParseFavoritesFolder(const FavoritesInfo & info,BookmarkVector * bookmarks)530 void IEImporter::ParseFavoritesFolder(const FavoritesInfo& info,
531 BookmarkVector* bookmarks) {
532 std::wstring ie_folder =
533 UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_IE));
534 BookmarkVector toolbar_bookmarks;
535 FilePath file;
536 std::vector<FilePath::StringType> file_list;
537 FilePath favorites_path(info.path);
538 // Favorites path length. Make sure it doesn't include the trailing \.
539 size_t favorites_path_len =
540 favorites_path.StripTrailingSeparators().value().size();
541 file_util::FileEnumerator file_enumerator(
542 favorites_path, true, file_util::FileEnumerator::FILES);
543 while (!(file = file_enumerator.Next()).value().empty() && !cancelled())
544 file_list.push_back(file.value());
545
546 // Keep the bookmarks in alphabetical order.
547 std::sort(file_list.begin(), file_list.end());
548
549 for (std::vector<FilePath::StringType>::iterator it = file_list.begin();
550 it != file_list.end(); ++it) {
551 FilePath shortcut(*it);
552 if (!LowerCaseEqualsASCII(shortcut.Extension(), ".url"))
553 continue;
554
555 // Skip the bookmark with invalid URL.
556 GURL url = GURL(ResolveInternetShortcut(*it));
557 if (!url.is_valid())
558 continue;
559
560 // Make the relative path from the Favorites folder, without the basename.
561 // ex. Suppose that the Favorites folder is C:\Users\Foo\Favorites.
562 // C:\Users\Foo\Favorites\Foo.url -> ""
563 // C:\Users\Foo\Favorites\Links\Bar\Baz.url -> "Links\Bar"
564 FilePath::StringType relative_string =
565 shortcut.DirName().value().substr(favorites_path_len);
566 if (!relative_string.empty() && FilePath::IsSeparator(relative_string[0]))
567 relative_string = relative_string.substr(1);
568 FilePath relative_path(relative_string);
569
570 ProfileWriter::BookmarkEntry entry;
571 // Remove the dot, the file extension, and the directory path.
572 entry.title = shortcut.RemoveExtension().BaseName().value();
573 entry.url = url;
574 entry.creation_time = GetFileCreationTime(*it);
575 if (!relative_path.empty())
576 relative_path.GetComponents(&entry.path);
577
578 // Flatten the bookmarks in Link folder onto bookmark toolbar. Otherwise,
579 // put it into "Other bookmarks".
580 if (import_to_bookmark_bar() &&
581 (!entry.path.empty() && entry.path[0] == info.links_folder)) {
582 entry.in_toolbar = true;
583 entry.path.erase(entry.path.begin());
584 toolbar_bookmarks.push_back(entry);
585 } else {
586 // We put the bookmarks in a "Imported From IE"
587 // folder, so that we don't mess up the "Other bookmarks".
588 if (!import_to_bookmark_bar())
589 entry.path.insert(entry.path.begin(), ie_folder);
590 bookmarks->push_back(entry);
591 }
592 }
593 bookmarks->insert(bookmarks->begin(), toolbar_bookmarks.begin(),
594 toolbar_bookmarks.end());
595 }
596
CurrentIEVersion() const597 int IEImporter::CurrentIEVersion() const {
598 static int version = -1;
599 if (version < 0) {
600 wchar_t buffer[128];
601 DWORD buffer_length = sizeof(buffer);
602 base::win::RegKey reg_key(HKEY_LOCAL_MACHINE,
603 L"Software\\Microsoft\\Internet Explorer", KEY_READ);
604 LONG result = reg_key.ReadValue(L"Version", buffer, &buffer_length, NULL);
605 version = ((result == ERROR_SUCCESS)? _wtoi(buffer) : 0);
606 }
607 return version;
608 }
609