gecko/browser/components/migration/src/nsIEProfileMigrator.cpp
2012-03-14 18:57:00 +01:00

2221 lines
75 KiB
C++

/* -*- Mode: C++; tab-width: 2; 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 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):
* Daniel Matejka
* (Original Author)
* Ben Goodger <ben@bengoodger.com>
* (History, Favorites, Passwords, Form Data, some settings)
*
* 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 ***** */
#include "mozilla/Util.h"
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "nsAppDirectoryServiceDefs.h"
#include "nsBrowserProfileMigratorUtils.h"
#include "nsCOMPtr.h"
#include "nsCRTGlue.h"
#include "nsNetCID.h"
#include "nsDocShellCID.h"
#include "nsDebug.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsStringAPI.h"
#include "plstr.h"
#include "prio.h"
#include "prmem.h"
#include "prlong.h"
#include "nsICookieManager2.h"
#include "nsIEProfileMigrator.h"
#include "nsIFile.h"
#include "nsILocalFile.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsISimpleEnumerator.h"
#include "nsIProfileMigrator.h"
#include "nsIBrowserProfileMigrator.h"
#include "nsIObserverService.h"
#include "nsILocalFileWin.h"
#include "nsAutoPtr.h"
#include "prnetdb.h"
#include <objbase.h>
#include <shlguid.h>
#include <urlhist.h>
#include <comdef.h>
#include <shlobj.h>
#include <intshcut.h>
#include "nsIBrowserHistory.h"
#include "nsIGlobalHistory.h"
#include "nsIRDFRemoteDataSource.h"
#include "nsIURI.h"
#include "nsILoginManagerIEMigrationHelper.h"
#include "nsILoginInfo.h"
#include "nsIFormHistory.h"
#include "nsIRDFService.h"
#include "nsIRDFContainer.h"
#include "nsIURL.h"
#include "nsINavBookmarksService.h"
#include "nsBrowserCompsCID.h"
#include "nsIStringBundle.h"
#include "nsNetUtil.h"
#include "nsToolkitCompsCID.h"
#include "nsUnicharUtils.h"
#include "nsIWindowsRegKey.h"
#include "nsISupportsPrimitives.h"
#define kNotFound -1
#define TRIDENTPROFILE_BUNDLE "chrome://browser/locale/migration/migration.properties"
#define REGISTRY_IE_MAIN_KEY \
NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\Main")
#define REGISTRY_IE_TYPEDURL_KEY \
NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\TypedURLs")
#define REGISTRY_IE_TOOLBAR_KEY \
NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\Toolbar")
#define REGISTRY_IE_SEARCHURL_KEY \
NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\SearchUrl")
using namespace mozilla;
const int sInitialCookieBufferSize = 1024; // but it can grow
const int sUsernameLengthLimit = 80;
const int sHostnameLengthLimit = 255;
//***********************************************************************
//*** Replacements for comsupp.lib calls used by pstorec.dll
//***********************************************************************
void __stdcall _com_issue_error(HRESULT hr)
{
// XXX - Do nothing for now
}
//***********************************************************************
//*** windows registry to mozilla prefs data type translation functions
//***********************************************************************
typedef void (*regEntryHandler)(nsIWindowsRegKey *, const nsString&,
nsIPrefBranch *, char *);
// yes/no string to T/F boolean
void
TranslateYNtoTF(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
// input type is a string, lowercase "yes" or "no"
nsAutoString regValue;
if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)))
aPrefs->SetBoolPref(aPrefKeyName, regValue.EqualsLiteral("yes"));
}
// yes/no string to F/T boolean
void
TranslateYNtoFT(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
// input type is a string, lowercase "yes" or "no"
nsAutoString regValue;
if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)))
aPrefs->SetBoolPref(aPrefKeyName, !regValue.EqualsLiteral("yes"));
}
void
TranslateYNtoImageBehavior(nsIWindowsRegKey *aRegKey,
const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
// input type is a string, lowercase "yes" or "no"
nsAutoString regValue;
if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)) &&
!regValue.IsEmpty()) {
if (regValue.EqualsLiteral("yes"))
aPrefs->SetIntPref(aPrefKeyName, 1);
else
aPrefs->SetIntPref(aPrefKeyName, 2);
}
}
void
TranslateDWORDtoHTTPVersion(nsIWindowsRegKey *aRegKey,
const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
PRUint32 val;
if (NS_SUCCEEDED(aRegKey->ReadIntValue(aRegValueName, &val))) {
if (val & 0x1)
aPrefs->SetCharPref(aPrefKeyName, "1.1");
else
aPrefs->SetCharPref(aPrefKeyName, "1.0");
}
}
// decimal RGB (1,2,3) to hex RGB (#010203)
void
TranslateDRGBtoHRGB(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
// clear previous settings with defaults
char prefStringValue[10];
nsAutoString regValue;
if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)) &&
!regValue.IsEmpty()) {
int red, green, blue;
::swscanf(regValue.get(), L"%d,%d,%d", &red, &green, &blue);
::sprintf(prefStringValue, "#%02X%02X%02X", red, green, blue);
aPrefs->SetCharPref(aPrefKeyName, prefStringValue);
}
}
// translate a windows registry DWORD int to a mozilla prefs PRInt32
void
TranslateDWORDtoPRInt32(nsIWindowsRegKey *aRegKey,
const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
// clear previous settings with defaults
PRInt32 prefIntValue = 0;
if (NS_SUCCEEDED(aRegKey->ReadIntValue(aRegValueName,
reinterpret_cast<PRUint32 *>(&prefIntValue))))
aPrefs->SetIntPref(aPrefKeyName, prefIntValue);
}
// string copy
void
TranslateString(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
nsAutoString regValue;
if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)) &&
!regValue.IsEmpty()) {
aPrefs->SetCharPref(aPrefKeyName, NS_ConvertUTF16toUTF8(regValue).get());
}
}
// translate accepted language character set formats
// (modified string copy)
void
TranslateLanglist(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
nsAutoString lang;
if (NS_FAILED(aRegKey->ReadStringValue(aRegValueName, lang)))
return;
// copy source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into
// destination format like "en-us, ar-kw, ar-om"
char prefStringValue[MAX_PATH]; // a convenient size, one hopes
NS_LossyConvertUTF16toASCII langCstr(lang);
const char *source = langCstr.get(),
*sourceEnd = source + langCstr.Length();
char *dest = prefStringValue,
*destEnd = dest + (MAX_PATH-2); // room for " \0"
bool skip = false,
comma = false;
while (source < sourceEnd && *source && dest < destEnd) {
if (*source == ',')
skip = false;
else if (*source == ';')
skip = true;
if (!skip) {
if (comma && *source != ' ')
*dest++ = ' ';
*dest++ = *source;
}
comma = *source == ',';
++source;
}
*dest = 0;
aPrefs->SetCharPref(aPrefKeyName, prefStringValue);
}
static int CALLBACK
fontEnumProc(const LOGFONTW *aLogFont, const TEXTMETRICW *aMetric,
DWORD aFontType, LPARAM aClosure) {
*((int *) aClosure) = aLogFont->lfPitchAndFamily & FF_ROMAN;
return 0;
}
void
TranslatePropFont(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
nsIPrefBranch *aPrefs, char *aPrefKeyName) {
HDC dc = ::GetDC(0);
LOGFONTW lf;
int isSerif = 1;
// serif or sans-serif font?
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfPitchAndFamily = 0;
nsAutoString font;
if (NS_FAILED(aRegKey->ReadStringValue(aRegValueName, font)))
return;
::wcsncpy(lf.lfFaceName, font.get(), LF_FACESIZE);
lf.lfFaceName[LF_FACESIZE - 1] = L'\0';
::EnumFontFamiliesExW(dc, &lf, fontEnumProc, (LPARAM) &isSerif, 0);
::ReleaseDC(0, dc);
// XXX : For now, only x-western font is translated.
// All or Locale-dependent subset of fonts need to be translated.
nsDependentCString generic(isSerif ? "serif" : "sans-serif");
nsCAutoString prefName("font.name.");
prefName.Append(generic);
prefName.Append(".x-western");
aPrefs->SetCharPref(prefName.get(), NS_ConvertUTF16toUTF8(font).get());
aPrefs->SetCharPref("font.default.x-western", generic.get());
}
//***********************************************************************
//*** master table of registry-to-gecko-pref translations
//***********************************************************************
struct regEntry {
char *regKeyName, // registry key (HKCU\Software ...)
*regValueName; // registry key leaf
char *prefKeyName; // pref name ("javascript.enabled" ...)
regEntryHandler entryHandler; // processing func
};
const regEntry gRegEntries[] = {
{ "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoComplete",
"AutoSuggest",
"browser.urlbar.autocomplete.enabled",
TranslateYNtoTF },
{ "Software\\Microsoft\\Internet Explorer\\International",
"AcceptLanguage",
"intl.accept_languages",
TranslateLanglist },
// XXX : For now, only x-western font is translated.
// All or Locale-dependent subset of fonts need to be translated.
{ "Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3",
"IEFixedFontName",
"font.name.monospace.x-western",
TranslateString },
{ 0, // an optimization: 0 means use the previous key
"IEPropFontName",
"", // special-cased in the translation function
TranslatePropFont },
{ "Software\\Microsoft\\Internet Explorer\\Main",
"Use_DlgBox_Colors",
"browser.display.use_system_colors",
TranslateYNtoTF },
{ 0,
"Use FormSuggest",
"browser.formfill.enable",
TranslateYNtoTF },
{ 0,
"FormSuggest Passwords",
"signon.rememberSignons",
TranslateYNtoTF },
#if 0
// Firefox supplies its own home page.
{ 0,
"Start Page",
REG_SZ,
"browser.startup.homepage",
TranslateString },
#endif
{ 0,
"Anchor Underline",
"browser.underline_anchors",
TranslateYNtoTF },
{ 0,
"Display Inline Images",
"permissions.default.image",
TranslateYNtoImageBehavior },
{ 0,
"Enable AutoImageResize",
"browser.enable_automatic_image_resizing",
TranslateYNtoTF },
{ 0,
"Move System Caret",
"accessibility.browsewithcaret",
TranslateYNtoTF },
{ 0,
"NotifyDownloadComplete",
"browser.download.manager.showAlertOnComplete",
TranslateYNtoTF },
{ 0,
"SmoothScroll", // XXX DWORD
"general.smoothScroll",
TranslateYNtoTF },
{ "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
"EnableHttp1_1",
"network.http.version",
TranslateDWORDtoHTTPVersion },
{ 0,
"ProxyHttp1.1",
"network.http.proxy.version",
TranslateDWORDtoHTTPVersion },
// SecureProtocols
{ "Software\\Microsoft\\Internet Explorer\\Settings",
"Always Use My Colors", // XXX DWORD
"browser.display.use_document_colors",
TranslateYNtoFT },
{ 0,
"Text Color",
"browser.display.foreground_color",
TranslateDRGBtoHRGB },
{ 0,
"Background Color",
"browser.display.background_color",
TranslateDRGBtoHRGB },
{ 0,
"Anchor Color",
"browser.anchor_color",
TranslateDRGBtoHRGB },
{ 0,
"Anchor Color Visited",
"browser.visited_color",
TranslateDRGBtoHRGB },
{ 0,
"Always Use My Font Face", // XXX DWORD
"browser.display.use_document_fonts",
TranslateYNtoFT }
};
#if 0
user_pref("font.size.fixed.x-western", 14);
user_pref("font.size.variable.x-western", 15);
#endif
///////////////////////////////////////////////////////////////////////////////
// nsIBrowserProfileMigrator
NS_IMETHODIMP
nsIEProfileMigrator::Migrate(PRUint16 aItems, nsIProfileStartup* aStartup, const PRUnichar* aProfile)
{
nsresult rv = NS_OK;
bool aReplace = false;
if (aStartup) {
aReplace = true;
rv = aStartup->DoStartup();
NS_ENSURE_SUCCESS(rv, rv);
}
NOTIFY_OBSERVERS(MIGRATION_STARTED, nsnull);
COPY_DATA(CopyPreferences, aReplace, nsIBrowserProfileMigrator::SETTINGS);
COPY_DATA(CopyCookies, aReplace, nsIBrowserProfileMigrator::COOKIES);
COPY_DATA(CopyHistory, aReplace, nsIBrowserProfileMigrator::HISTORY);
COPY_DATA(CopyFormData, aReplace, nsIBrowserProfileMigrator::FORMDATA);
COPY_DATA(CopyPasswords, aReplace, nsIBrowserProfileMigrator::PASSWORDS);
COPY_DATA(CopyFavorites, aReplace, nsIBrowserProfileMigrator::BOOKMARKS);
NOTIFY_OBSERVERS(MIGRATION_ENDED, nsnull);
return rv;
}
NS_IMETHODIMP
nsIEProfileMigrator::GetMigrateData(const PRUnichar* aProfile,
bool aReplace,
PRUint16* aResult)
{
if (TestForIE7()) {
// IE7 and up store form data and passwords in an unrecoverable
// way, preventing us from importing this data.
*aResult = nsIBrowserProfileMigrator::SETTINGS |
nsIBrowserProfileMigrator::COOKIES |
nsIBrowserProfileMigrator::HISTORY |
nsIBrowserProfileMigrator::BOOKMARKS;
}
else {
*aResult = nsIBrowserProfileMigrator::SETTINGS |
nsIBrowserProfileMigrator::COOKIES |
nsIBrowserProfileMigrator::HISTORY |
nsIBrowserProfileMigrator::FORMDATA |
nsIBrowserProfileMigrator::PASSWORDS |
nsIBrowserProfileMigrator::BOOKMARKS;
}
return NS_OK;
}
NS_IMETHODIMP
nsIEProfileMigrator::GetSourceExists(bool* aResult)
{
// IE always exists.
*aResult = true;
return NS_OK;
}
NS_IMETHODIMP
nsIEProfileMigrator::GetSourceHasMultipleProfiles(bool* aResult)
{
*aResult = false;
return NS_OK;
}
NS_IMETHODIMP
nsIEProfileMigrator::GetSourceProfiles(nsIArray** aResult)
{
*aResult = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsIEProfileMigrator::GetSourceHomePageURL(nsACString& aResult)
{
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey ||
NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
REGISTRY_IE_MAIN_KEY,
nsIWindowsRegKey::ACCESS_READ)))
return NS_OK;
// Read in the main home page
NS_NAMED_LITERAL_STRING(homeURLValName, "Start Page");
nsAutoString homeURLVal;
if (NS_SUCCEEDED(regKey->ReadStringValue(homeURLValName, homeURLVal))) {
// Do we need this round-about way to get |homePageURL|?
// Perhaps, we do to have the form of URL under our control
// (cf. network.standard-url.escape-utf8)
// Note that Windows stores URLs in IRI in the registry
nsCAutoString homePageURL;
nsCOMPtr<nsIURI> homePageURI;
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(homePageURI), homeURLVal))) {
if (NS_SUCCEEDED(homePageURI->GetSpec(homePageURL)) && !homePageURL.IsEmpty()) {
aResult.Assign(homePageURL);
}
}
}
// With IE7, The "Start Page" key still exists. Secondary home pages
// are located in a string stored in "Secondary Start Pages" which
// contains multiple Unicode URI seperated by nulls. (REG_MULTI_SZ)
NS_NAMED_LITERAL_STRING(ssRegKeyName, "Secondary Start Pages");
nsAutoString secondaryList;
if (NS_SUCCEEDED(regKey->ReadStringValue(ssRegKeyName, secondaryList)) &&
!secondaryList.IsEmpty()) {
nsTArray<nsCString> parsedList;
if (!ParseString(NS_ConvertUTF16toUTF8(secondaryList), '\0', parsedList))
return NS_OK;
// Split the result up into individual uri
for (PRUint32 index = 0; index < parsedList.Length(); ++index) {
nsCOMPtr<nsIURI> uri;
nsCAutoString homePage;
// Append "|uri" to result. This is how we currently handle
// storing multiple home pages.
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), parsedList[index]))) {
if (NS_SUCCEEDED(uri->GetSpec(homePage)) && !homePage.IsEmpty()) {
aResult.AppendLiteral("|");
aResult.Append(homePage);
}
}
}
}
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// nsIEProfileMigrator
NS_IMPL_ISUPPORTS2(nsIEProfileMigrator, nsIBrowserProfileMigrator, nsINavHistoryBatchCallback);
nsIEProfileMigrator::nsIEProfileMigrator()
{
mObserverService = do_GetService("@mozilla.org/observer-service;1");
}
nsIEProfileMigrator::~nsIEProfileMigrator()
{
}
// Test used in detecting Internet Explorer 7 prior to presenting
// import options.
bool
nsIEProfileMigrator::TestForIE7()
{
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey)
return false;
NS_NAMED_LITERAL_STRING(key,
"Applications\\iexplore.exe\\shell\\open\\command");
if (NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
key, nsIWindowsRegKey::ACCESS_QUERY_VALUE)))
return false;
nsAutoString iePath;
if (NS_FAILED(regKey->ReadStringValue(EmptyString(), iePath)))
return false;
// Replace embedded environment variables.
PRUint32 bufLength =
::ExpandEnvironmentStringsW(iePath.get(),
L"", 0);
if (bufLength == 0) // Error
return false;
nsAutoArrayPtr<PRUnichar> destination(new PRUnichar[bufLength]);
if (!destination)
return false;
if (!::ExpandEnvironmentStringsW(iePath.get(),
destination,
bufLength))
return false;
iePath = destination;
// Check if the path is enclosed in quotation marks.
if (StringBeginsWith(iePath, NS_LITERAL_STRING("\""))) {
iePath.Cut(0,1);
PRInt32 index = iePath.FindChar('\"', 0);
// After removing the opening quoation mark,
// remove the closing one and everything after it.
if (index > 0)
iePath.Cut(index,iePath.Length());
}
nsCOMPtr<nsILocalFile> lf;
NS_NewLocalFile(iePath, true, getter_AddRefs(lf));
nsCOMPtr<nsILocalFileWin> lfw = do_QueryInterface(lf);
if (!lfw)
return false;
nsAutoString ieVersion;
if (NS_FAILED(lfw->GetVersionInfoField("FileVersion", ieVersion)))
return false;
if (ieVersion.Length() > 2) {
PRInt32 index = ieVersion.FindChar('.', 0);
if (index == kNotFound)
return false;
ieVersion.Cut(index, ieVersion.Length());
PRInt32 ver = wcstol(ieVersion.get(), nsnull, 0);
if (ver >= 7) // Found 7 or greater major version
return true;
}
return false;
}
NS_IMETHODIMP
nsIEProfileMigrator::RunBatched(nsISupports* aUserData)
{
PRUint8 batchAction;
nsCOMPtr<nsISupportsPRUint8> strWrapper(do_QueryInterface(aUserData));
NS_ASSERTION(strWrapper, "Unable to create nsISupportsPRUint8 wrapper!");
nsresult rv = strWrapper->GetData(&batchAction);
NS_ENSURE_SUCCESS(rv, rv);
switch (batchAction) {
case BATCH_ACTION_HISTORY:
rv = CopyHistoryBatched(false);
break;
case BATCH_ACTION_HISTORY_REPLACE:
rv = CopyHistoryBatched(true);
break;
case BATCH_ACTION_BOOKMARKS:
rv = CopyFavoritesBatched(false);
break;
case BATCH_ACTION_BOOKMARKS_REPLACE:
rv = CopyFavoritesBatched(true);
break;
}
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsIEProfileMigrator::CopyHistory(bool aReplace)
{
nsresult rv;
nsCOMPtr<nsINavHistoryService> history =
do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRUint8 batchAction = aReplace ? BATCH_ACTION_HISTORY_REPLACE
: BATCH_ACTION_HISTORY;
nsCOMPtr<nsISupportsPRUint8> supports =
do_CreateInstance(NS_SUPPORTS_PRUINT8_CONTRACTID);
NS_ENSURE_TRUE(supports, NS_ERROR_OUT_OF_MEMORY);
rv = supports->SetData(batchAction);
NS_ENSURE_SUCCESS(rv, rv);
rv = history->RunInBatchMode(this, supports);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsIEProfileMigrator::CopyHistoryBatched(bool aReplace)
{
nsCOMPtr<nsIBrowserHistory> hist(do_GetService(NS_GLOBALHISTORY2_CONTRACTID));
nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
// First, Migrate standard IE History entries...
::CoInitialize(NULL);
IUrlHistoryStg2* ieHistory;
HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUrlHistoryStg2,
reinterpret_cast<void**>(&ieHistory));
if (SUCCEEDED(hr)) {
IEnumSTATURL* enumURLs;
hr = ieHistory->EnumUrls(&enumURLs);
if (SUCCEEDED(hr)) {
STATURL statURL;
ULONG fetched;
_bstr_t url;
nsCAutoString scheme;
SYSTEMTIME st;
bool validScheme = false;
PRUnichar* tempTitle = nsnull;
for (int count = 0; (hr = enumURLs->Next(1, &statURL, &fetched)) == S_OK; ++count) {
if (statURL.pwcsUrl) {
// 1 - Page Title
tempTitle = statURL.pwcsTitle ? (PRUnichar*)((wchar_t*)(statURL.pwcsTitle)) : nsnull;
// 2 - Last Visit Date
::FileTimeToSystemTime(&(statURL.ftLastVisited), &st);
PRExplodedTime prt;
prt.tm_year = st.wYear;
prt.tm_month = st.wMonth - 1; // SYSTEMTIME's day-of-month parameter is 1-based, PRExplodedTime's is 0-based.
prt.tm_mday = st.wDay;
prt.tm_hour = st.wHour;
prt.tm_min = st.wMinute;
prt.tm_sec = st.wSecond;
prt.tm_usec = st.wMilliseconds * 1000;
prt.tm_wday = 0;
prt.tm_yday = 0;
prt.tm_params.tp_gmt_offset = 0;
prt.tm_params.tp_dst_offset = 0;
PRTime lastVisited = PR_ImplodeTime(&prt);
// 3 - URL
url = statURL.pwcsUrl;
NS_ConvertUTF16toUTF8 urlStr(url);
if (NS_FAILED(ios->ExtractScheme(urlStr, scheme))) {
::CoTaskMemFree(statURL.pwcsUrl);
if (statURL.pwcsTitle)
::CoTaskMemFree(statURL.pwcsTitle);
continue;
}
ToLowerCase(scheme);
// XXXben -
// MSIE stores some types of URLs in its history that we can't handle, like HTMLHelp
// and others. At present Necko isn't clever enough to delegate handling of these types
// to the system, so we should just avoid importing them.
const char* schemes[] = { "http", "https", "ftp", "file" };
for (int i = 0; i < 4; ++i) {
if (validScheme = scheme.Equals(schemes[i]))
break;
}
// 4 - Now add the page
if (validScheme) {
nsCOMPtr<nsIURI> uri;
ios->NewURI(urlStr, nsnull, nsnull, getter_AddRefs(uri));
if (uri) {
if (tempTitle)
hist->AddPageWithDetails(uri, tempTitle, lastVisited);
else
hist->AddPageWithDetails(uri, url, lastVisited);
}
}
::CoTaskMemFree(statURL.pwcsUrl);
}
if (statURL.pwcsTitle)
::CoTaskMemFree(statURL.pwcsTitle);
}
nsCOMPtr<nsIRDFRemoteDataSource> ds(do_QueryInterface(hist));
if (ds)
ds->Flush();
enumURLs->Release();
}
ieHistory->Release();
}
::CoUninitialize();
// Now, find out what URLs were typed in by the user
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (regKey &&
NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
REGISTRY_IE_TYPEDURL_KEY,
nsIWindowsRegKey::ACCESS_READ))) {
int offset = 0;
while (1) {
nsAutoString valueName;
if (NS_FAILED(regKey->GetValueName(offset, valueName)))
break;
nsAutoString url;
if (Substring(valueName, 0, 3).EqualsLiteral("url") &&
NS_SUCCEEDED(regKey->ReadStringValue(valueName, url))) {
nsCOMPtr<nsIURI> uri;
ios->NewURI(NS_ConvertUTF16toUTF8(url), nsnull, nsnull,
getter_AddRefs(uri));
if (uri)
hist->MarkPageAsTyped(uri);
}
++offset;
}
}
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// IE PASSWORDS AND FORM DATA - A PROTECTED STORAGE SYSTEM PRIMER
//
// Internet Explorer 4.0 and up store sensitive form data (saved through form
// autocomplete) and saved passwords in a special area of the Registry called
// the Protected Storage System. The data IE stores in the Protected Storage
// System is located under:
//
// HKEY_CURRENT_USER\Software\Microsoft\Protected Storage System Provider\
// <USER_ID>\Data\<IE_PSS_GUID>\<IE_PSS_GUID>\
//
// <USER_ID> is a long string that uniquely identifies the current user
// <IE_PSS_GUID> is a GUID that identifies a subsection of the Protected Storage
// System specific to MSIE. This GUID is defined below ("IEPStoreAutocompGUID").
//
// Data is stored in the Protected Strage System ("PStore") in the following
// format:
//
// <IE_PStore_Key> \
// fieldName1:StringData \ ItemData = <REG_BINARY>
// fieldName2:StringData \ ItemData = <REG_BINARY>
// http://foo.com/login.php:StringData \ ItemData = <REG_BINARY>
// ... etc ...
//
// Each key represents either the identifier of a web page text field that
// data was saved from (e.g. <input name="fieldName1">), or a URL that a login
// (username + password) was saved at (e.g. "http://foo.com/login.php")
//
// Data is stored for each of these cases in the following format:
//
// for both types of data, the Value ItemData is REG_BINARY data format encrypted with
// a 3DES cipher.
//
// for FormData: the decrypted data is in the form:
// value1\0value2\0value3\0value4 ...
// for Signons: the decrypted data is in the form:
// username\0password
//
// We cannot read the PStore directly by using Registry functions because the
// keys have limited read access such that only System process can read from them.
// In order to read from the PStore we need to use Microsoft's undocumented PStore
// API(*).
//
// (* Sparse documentation became available as of the January 2004 MSDN Library
// release)
//
// The PStore API lets us read decrypted data from the PStore. Encryption does not
// appear to be strong.
//
// Details on how each type of data is read from the PStore and migrated appropriately
// is discussed in more detail below.
//
// For more information about the PStore, read:
//
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devnotes/winprog/pstore.asp
//
typedef HRESULT (WINAPI *PStoreCreateInstancePtr)(IPStore**, DWORD, DWORD, DWORD);
// IE PStore Type GUIDs
// {e161255a-37c3-11d2-bcaa-00c04fd929db} Autocomplete Password & Form Data
// Subtype has same GUID
// {5e7e8100-9138-11d1-945a-00c04fc308ff} HTTP/FTP Auth Login Data
// Subtype has a zero GUID
static GUID IEPStoreAutocompGUID = { 0xe161255a, 0x37c3, 0x11d2, { 0xbc, 0xaa, 0x00, 0xc0, 0x4f, 0xd9, 0x29, 0xdb } };
static GUID IEPStoreSiteAuthGUID = { 0x5e7e8100, 0x9138, 0x11d1, { 0x94, 0x5a, 0x00, 0xc0, 0x4f, 0xc3, 0x08, 0xff } };
///////////////////////////////////////////////////////////////////////////////
// IMPORTING AUTOCOMPLETE PASSWORDS
//
// This is tricky, and requires 2 passes through the subkeys in IE's PStore
// section.
//
// First, we walk IE's PStore section, looking for subkeys that are prefixed
// with a URI. As mentioned above, we assume all such subkeys are stored
// passwords.
//
// http://foo.com/login.php:StringData username\0password
//
// We can't add this item to the Password Manager just yet though.
//
// The password manager requires the uniquifier of the username field (that is,
// the value of the "name" or "id" attribute) from the webpage so that it can
// prefill the value automatically. IE doesn't store this information with
// the password entry, but it DOES store this information independently as a
// separate Form Data entry.
//
// In other words, if you go to foo.com above and log in with "username" and
// "password" as your details in a form where the username field is uniquified
// as "un" (<input type="text" name="un">), when you login and elect to have IE
// save the password for the site, the following TWO entries are created in IE's
// PStore section:
//
// http://foo.com/login.php:StringData username\0password
// un:StringData username
//
// Thus to discover the field name for each login we need to first gather up
// all the signons (collecting usernames in the process), then walk the list
// again, looking ONLY at non-URI prefixed subkeys, and searching for each
// username as a value in each such subkey's value list. If we have a match,
// we assume that the subkey (with its uniquifier prefix) is a login field.
//
// With this information, we call Password Manager's "AddLogin" method
// providing this detail. We don't need to provide the password field name,
// we have no means of retrieving this info from IE, and the Password Manager
// knows to hunt for a password field near the login field if none is specified.
//
// IMPLICATIONS:
// 1) redundant signon entries for non-login forms might be created, but these
// should be benign.
// 2) if the IE user ever clears his Form AutoComplete cache but doesn't clear
// his passwords, we will be hosed, as we have no means of locating the
// username field. Maybe someday the Password Manager will become
// artificially intelligent and be able to guess where the login fields are,
// but I'm not holding my breath.
//
nsresult
nsIEProfileMigrator::CopyPasswords(bool aReplace)
{
HRESULT hr;
nsresult rv;
nsTArray<SignonData> signonsFound;
HMODULE pstoreDLL = ::LoadLibraryW(L"pstorec.dll");
if (!pstoreDLL) {
// XXXben TODO
// Need to figure out what to do here on Windows 98 etc... it may be that the key is universal read
// and we can just blunder into the registry and use CryptUnprotect to get the data out.
return NS_ERROR_FAILURE;
}
PStoreCreateInstancePtr PStoreCreateInstance = (PStoreCreateInstancePtr)::GetProcAddress(pstoreDLL, "PStoreCreateInstance");
IPStore* PStore;
hr = PStoreCreateInstance(&PStore, 0, 0, 0);
rv = GetSignonsListFromPStore(PStore, &signonsFound);
if (NS_SUCCEEDED(rv))
ResolveAndMigrateSignons(PStore, &signonsFound);
MigrateSiteAuthSignons(PStore);
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// IMPORTING SITE AUTHENTICATION PASSWORDS
//
// This is simple and straightforward. We iterate through the part of the
// PStore that matches the type GUID defined in IEPStoreSiteAuthGUID and
// a zero subtype GUID. For each item, we check the data for a ':' that
// separates the username and password parts. If there is no ':', we give up.
// After that, we check to see if the name of the item starts with "DPAPI:".
// We bail out if that's the case, because we can't handle those yet.
// However, if everything is all and well, we convert the itemName to a realm
// string that the password manager can work with and save this login
// via AddLogin.
nsresult
nsIEProfileMigrator::MigrateSiteAuthSignons(IPStore* aPStore)
{
HRESULT hr;
NS_ENSURE_ARG_POINTER(aPStore);
nsCOMPtr<nsILoginManagerIEMigrationHelper> pwmgr(
do_GetService("@mozilla.org/login-manager/storage/legacy;1"));
if (!pwmgr)
return NS_OK;
GUID mtGuid = {0};
IEnumPStoreItems* enumItems = NULL;
hr = aPStore->EnumItems(0, &IEPStoreSiteAuthGUID, &mtGuid, 0, &enumItems);
if (SUCCEEDED(hr) && enumItems != NULL) {
LPWSTR itemName = NULL;
while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
unsigned long count = 0;
unsigned char* data = NULL;
hr = aPStore->ReadItem(0, &IEPStoreSiteAuthGUID, &mtGuid, itemName,
&count, &data, NULL, 0);
if (SUCCEEDED(hr) && data) {
unsigned long i;
unsigned char* password = NULL;
for (i = 0; i < count; i++)
if (data[i] == ':') {
data[i] = '\0';
if (i + 1 < count)
password = &data[i + 1];
break;
}
nsAutoString host(itemName), realm;
if (Substring(host, 0, 6).EqualsLiteral("DPAPI:")) // often FTP logins
password = NULL; // We can't handle these yet
if (password) {
int idx;
idx = host.FindChar('/');
if (idx) {
realm.Assign(Substring(host, idx + 1));
host.Assign(Substring(host, 0, idx));
}
// XXX: username and password are always ASCII in IPStore?
// If not, are they in UTF-8 or the default codepage? (ref. bug 41489)
nsresult rv;
nsCOMPtr<nsILoginInfo> aLogin (do_CreateInstance(
NS_LOGININFO_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
// nsStringAPI doesn't let us create void strings, so we won't
// use Init() here.
aLogin->SetHostname(host);
aLogin->SetHttpRealm(realm);
aLogin->SetUsername(NS_ConvertUTF8toUTF16((char *)data));
aLogin->SetPassword(NS_ConvertUTF8toUTF16((char *)password));
aLogin->SetUsernameField(EmptyString());
aLogin->SetPasswordField(EmptyString());
pwmgr->MigrateAndAddLogin(aLogin);
}
::CoTaskMemFree(data);
}
}
}
return NS_OK;
}
nsresult
nsIEProfileMigrator::GetSignonsListFromPStore(IPStore* aPStore, nsTArray<SignonData>* aSignonsFound)
{
HRESULT hr;
NS_ENSURE_ARG_POINTER(aPStore);
IEnumPStoreItems* enumItems = NULL;
hr = aPStore->EnumItems(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, 0, &enumItems);
if (SUCCEEDED(hr) && enumItems != NULL) {
LPWSTR itemName = NULL;
while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
unsigned long count = 0;
unsigned char* data = NULL;
// We are responsible for freeing |data| using |CoTaskMemFree|!!
// But we don't do it here...
hr = aPStore->ReadItem(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, itemName, &count, &data, NULL, 0);
if (SUCCEEDED(hr) && data) {
nsAutoString itemNameString(itemName);
if (StringTail(itemNameString, 11).
LowerCaseEqualsLiteral(":stringdata")) {
// :StringData contains the saved data
const nsAString& key = Substring(itemNameString, 0, itemNameString.Length() - 11);
char* realm = nsnull;
if (KeyIsURI(key, &realm)) {
// This looks like a URL and could be a password. If it has username and password data, then we'll treat
// it as one and add it to the password manager
unsigned char* username = NULL;
unsigned char* pass = NULL;
GetUserNameAndPass(data, count, &username, &pass);
if (username && pass) {
// username and pass are pointers into the data buffer allocated by IPStore's ReadItem
// method, and we own that buffer. We don't free it here, since we're going to be using
// it after the password harvesting stage to locate the username field. Only after the second
// phase is complete do we free the buffer.
SignonData* d = aSignonsFound->AppendElement();
if (!d)
return NS_ERROR_OUT_OF_MEMORY;
d->user = (PRUnichar*)username;
d->pass = (PRUnichar*)pass;
d->realm = realm; // freed in ResolveAndMigrateSignons
}
}
}
}
}
}
return NS_OK;
}
bool
nsIEProfileMigrator::KeyIsURI(const nsAString& aKey, char** aRealm)
{
*aRealm = nsnull;
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), aKey)))
return false;
bool validScheme = false;
const char* schemes[] = { "http", "https" };
for (int i = 0; i < 2; ++i) {
uri->SchemeIs(schemes[i], &validScheme);
if (validScheme) {
nsCAutoString realm;
uri->GetScheme(realm);
realm.AppendLiteral("://");
nsCAutoString host;
uri->GetHost(host);
realm.Append(host);
*aRealm = ToNewCString(realm);
return validScheme;
}
}
return false;
}
nsresult
nsIEProfileMigrator::ResolveAndMigrateSignons(IPStore* aPStore, nsTArray<SignonData>* aSignonsFound)
{
HRESULT hr;
IEnumPStoreItems* enumItems = NULL;
hr = aPStore->EnumItems(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, 0, &enumItems);
if (SUCCEEDED(hr) && enumItems != NULL) {
LPWSTR itemName = NULL;
while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
unsigned long count = 0;
unsigned char* data = NULL;
hr = aPStore->ReadItem(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, itemName, &count, &data, NULL, 0);
if (SUCCEEDED(hr) && data) {
nsAutoString itemNameString(itemName);
if (StringTail(itemNameString, 11).
LowerCaseEqualsLiteral(":stringdata")) {
// :StringData contains the saved data
const nsAString& key = Substring(itemNameString, 0, itemNameString.Length() - 11);
// Assume all keys that are valid URIs are signons, not saved form data, and that
// all keys that aren't valid URIs are form field names (containing form data).
nsCString realm;
if (!KeyIsURI(key, getter_Copies(realm))) {
// Search the data for a username that matches one of the found signons.
EnumerateUsernames(key, (PRUnichar*)data, (count/sizeof(PRUnichar)), aSignonsFound);
}
}
::CoTaskMemFree(data);
}
}
// Now that we've done resolving signons, we need to walk the signons list, freeing the data buffers
// for each SignonData entry, since these buffers were allocated by the system back in |GetSignonListFromPStore|
// but never freed.
PRUint32 signonCount = aSignonsFound->Length();
for (PRUint32 i = 0; i < signonCount; ++i) {
SignonData &sd = aSignonsFound->ElementAt(i);
::CoTaskMemFree(sd.user); // |sd->user| is a pointer to the start of a buffer that also contains sd->pass
NS_Free(sd.realm);
}
aSignonsFound->Clear();
}
return NS_OK;
}
void
nsIEProfileMigrator::EnumerateUsernames(const nsAString& aKey, PRUnichar* aData, unsigned long aCount, nsTArray<SignonData>* aSignonsFound)
{
nsCOMPtr<nsILoginManagerIEMigrationHelper> pwmgr(
do_GetService("@mozilla.org/login-manager/storage/legacy;1"));
if (!pwmgr)
return;
PRUnichar* cursor = aData;
PRUint32 offset = 0;
PRUint32 signonCount = aSignonsFound->Length();
while (offset < aCount) {
nsAutoString curr; curr = cursor;
// Compare the value at the current cursor position with the collected list of signons
for (PRUint32 i = 0; i < signonCount; ++i) {
SignonData &sd = aSignonsFound->ElementAt(i);
if (curr.Equals(sd.user)) {
// Bingo! Found a username in the saved data for this item. Now, add a Signon.
nsDependentString usernameStr(sd.user), passStr(sd.pass);
nsAutoString realm(NS_ConvertUTF8toUTF16(sd.realm));
nsresult rv;
nsCOMPtr<nsILoginInfo> aLogin (do_CreateInstance(NS_LOGININFO_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, /* */);
// nsStringAPI doesn't let us create void strings, so we won't
// use Init() here.
// IE doesn't have the form submit URL, so set to empty-string,
// which the login manager uses as a wildcard value.
// IE doesn't store the password field name either, so just set it
// to an empty string.
aLogin->SetHostname(realm);
aLogin->SetFormSubmitURL(EmptyString());
aLogin->SetUsername(usernameStr);
aLogin->SetPassword(passStr);
aLogin->SetUsernameField(aKey);
aLogin->SetPasswordField(EmptyString());
pwmgr->MigrateAndAddLogin(aLogin);
}
}
// Advance the cursor
PRInt32 advance = curr.Length() + 1;
cursor += advance; // Advance to next string (length of curr string + 1 PRUnichar for null separator)
offset += advance;
}
}
void
nsIEProfileMigrator::GetUserNameAndPass(unsigned char* data, unsigned long len, unsigned char** username, unsigned char** pass)
{
*username = data;
*pass = NULL;
unsigned char* temp = data;
for (unsigned int i = 0; i < len; i += 2, temp += 2*sizeof(unsigned char)) {
if (*temp == '\0') {
*pass = temp + 2*sizeof(unsigned char);
break;
}
}
}
///////////////////////////////////////////////////////////////////////////////
// IMPORTING FORM DATA
//
// This is a much simpler task as all we need is the field name and that's part
// of the key used to identify each data set in the PStore. The algorithm here
// is as follows:
//
// fieldName1:StringData value1\0value2\0value3\0
// fieldName2:StringData value1\0value2\0value3\0
// fieldName3:StringData value1\0value2\0value3\0
//
// Walk each non-URI prefixed key in IE's PStore section, split the value provided
// into chunks (\0 delimited) and use nsIFormHistory2's |addEntry| method to add
// an entry for the fieldName prefix and each value.
//
// "Quite Easily Done". ;-)
//
nsresult
nsIEProfileMigrator::CopyFormData(bool aReplace)
{
HRESULT hr;
HMODULE pstoreDLL = ::LoadLibraryW(L"pstorec.dll");
if (!pstoreDLL) {
// XXXben TODO
// Need to figure out what to do here on Windows 98 etc... it may be that the key is universal read
// and we can just blunder into the registry and use CryptUnprotect to get the data out.
return NS_ERROR_FAILURE;
}
PStoreCreateInstancePtr PStoreCreateInstance = (PStoreCreateInstancePtr)::GetProcAddress(pstoreDLL, "PStoreCreateInstance");
IPStore* PStore = NULL;
hr = PStoreCreateInstance(&PStore, 0, 0, 0);
if (FAILED(hr) || PStore == NULL)
return NS_OK;
IEnumPStoreItems* enumItems = NULL;
hr = PStore->EnumItems(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, 0, &enumItems);
if (SUCCEEDED(hr) && enumItems != NULL) {
LPWSTR itemName = NULL;
while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
unsigned long count = 0;
unsigned char* data = NULL;
// We are responsible for freeing |data| using |CoTaskMemFree|!!
hr = PStore->ReadItem(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, itemName, &count, &data, NULL, 0);
if (SUCCEEDED(hr) && data) {
nsAutoString itemNameString(itemName);
if (StringTail(itemNameString, 11).
LowerCaseEqualsLiteral(":stringdata")) {
// :StringData contains the saved data
const nsAString& key = Substring(itemNameString, 0, itemNameString.Length() - 11);
nsCString realm;
if (!KeyIsURI(key, getter_Copies(realm))) {
nsresult rv = AddDataToFormHistory(key, (PRUnichar*)data, (count/sizeof(PRUnichar)));
if (NS_FAILED(rv)) return rv;
}
}
}
}
}
return NS_OK;
}
nsresult
nsIEProfileMigrator::AddDataToFormHistory(const nsAString& aKey, PRUnichar* aData, unsigned long aCount)
{
nsCOMPtr<nsIFormHistory2> formHistory(do_GetService("@mozilla.org/satchel/form-history;1"));
if (!formHistory)
return NS_ERROR_OUT_OF_MEMORY;
PRUnichar* cursor = aData;
PRUint32 offset = 0;
while (offset < aCount) {
nsAutoString curr; curr = cursor;
formHistory->AddEntry(aKey, curr);
// Advance the cursor
PRInt32 advance = curr.Length() + 1;
cursor += advance; // Advance to next string (length of curr string + 1 PRUnichar for null separator)
offset += advance;
}
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
//
// favorites
// search keywords
//
nsresult
nsIEProfileMigrator::CopyFavorites(bool aReplace)
{
nsresult rv;
nsCOMPtr<nsINavBookmarksService> bookmarks =
do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRUint8 batchAction = aReplace ? BATCH_ACTION_BOOKMARKS_REPLACE
: BATCH_ACTION_BOOKMARKS;
nsCOMPtr<nsISupportsPRUint8> supports =
do_CreateInstance(NS_SUPPORTS_PRUINT8_CONTRACTID);
NS_ENSURE_TRUE(supports, NS_ERROR_OUT_OF_MEMORY);
rv = supports->SetData(batchAction);
NS_ENSURE_SUCCESS(rv, rv);
rv = bookmarks->RunInBatchMode(this, supports);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsIEProfileMigrator::CopyFavoritesBatched(bool aReplace)
{
// If "aReplace" is true, merge into the root level of bookmarks. Otherwise,
// create a folder called "Imported IE Favorites" and place all the Bookmarks
// there.
nsresult rv;
nsCOMPtr<nsINavBookmarksService> bms =
do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 bookmarksMenuFolderId;
rv = bms->GetBookmarksMenuFolder(&bookmarksMenuFolderId);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString personalToolbarFolderName;
PRInt64 folder;
if (!aReplace) {
nsCOMPtr<nsIStringBundleService> bundleService =
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStringBundle> bundle;
rv = bundleService->CreateBundle(TRIDENTPROFILE_BUNDLE,
getter_AddRefs(bundle));
NS_ENSURE_SUCCESS(rv, rv);
nsString sourceNameIE;
rv = bundle->GetStringFromName(NS_LITERAL_STRING("sourceNameIE").get(),
getter_Copies(sourceNameIE));
NS_ENSURE_SUCCESS(rv, rv);
const PRUnichar* sourceNameStrings[] = { sourceNameIE.get() };
nsString importedIEFavsTitle;
rv = bundle->FormatStringFromName(NS_LITERAL_STRING("importedBookmarksFolder").get(),
sourceNameStrings, 1,
getter_Copies(importedIEFavsTitle));
NS_ENSURE_SUCCESS(rv, rv);
rv = bms->CreateFolder(bookmarksMenuFolderId,
NS_ConvertUTF16toUTF8(importedIEFavsTitle),
nsINavBookmarksService::DEFAULT_INDEX,
&folder);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
// If importing defaults fails for whatever reason, let the import process
// continue.
DebugOnly<nsresult> rv = ImportDefaultBookmarks();
MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to import default bookmarks");
// Locate the Links toolbar folder, we want to replace the Personal Toolbar
// content with Favorites in this folder.
// On versions minor or equal to IE6 the folder name is stored in the
// LinksFolderName registry key, but in newer versions it may be just a
// Links subfolder inside the default Favorites folder.
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (regKey &&
NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
REGISTRY_IE_TOOLBAR_KEY,
nsIWindowsRegKey::ACCESS_READ))) {
nsAutoString linksFolderName;
if (NS_SUCCEEDED(regKey->ReadStringValue(
NS_LITERAL_STRING("LinksFolderName"),
linksFolderName))) {
personalToolbarFolderName = linksFolderName;
}
else {
personalToolbarFolderName.AssignLiteral("Links");
}
}
folder = bookmarksMenuFolderId;
}
nsCOMPtr<nsIProperties> fileLocator =
do_GetService("@mozilla.org/file/directory_service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> favoritesDirectory;
(void)fileLocator->Get(NS_WIN_FAVORITES_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(favoritesDirectory));
// If |favoritesDirectory| is null, it means that we're on a Windows
// platform that does not have a Favorites folder, e.g. Windows 95
// (early SRs, before IE integrated with the shell).
// Only try to read Favorites folder if it exists on the machine.
if (favoritesDirectory) {
rv = ParseFavoritesFolder(favoritesDirectory, folder, bms,
personalToolbarFolderName, true);
NS_ENSURE_SUCCESS(rv, rv);
}
return CopySmartKeywords(bms, bookmarksMenuFolderId);
}
nsresult
nsIEProfileMigrator::CopySmartKeywords(nsINavBookmarksService* aBMS,
PRInt64 aParentFolder)
{
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (regKey &&
NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
REGISTRY_IE_SEARCHURL_KEY,
nsIWindowsRegKey::ACCESS_READ))) {
nsCOMPtr<nsIStringBundleService> bundleService =
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStringBundle> bundle;
rv = bundleService->CreateBundle(TRIDENTPROFILE_BUNDLE,
getter_AddRefs(bundle));
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 keywordsFolder = 0;
int offset = 0;
while (1) {
nsAutoString keyName;
if (NS_FAILED(regKey->GetChildName(offset, keyName)))
break;
if (!keywordsFolder) {
nsString sourceNameIE;
rv = bundle->GetStringFromName(NS_LITERAL_STRING("sourceNameIE").get(),
getter_Copies(sourceNameIE));
NS_ENSURE_SUCCESS(rv, rv);
const PRUnichar* sourceNameStrings[] = { sourceNameIE.get() };
nsString importedIESearchUrlsTitle;
rv = bundle->FormatStringFromName(NS_LITERAL_STRING("importedSearchURLsFolder").get(),
sourceNameStrings, 1,
getter_Copies(importedIESearchUrlsTitle));
NS_ENSURE_SUCCESS(rv, rv);
rv = aBMS->CreateFolder(aParentFolder,
NS_ConvertUTF16toUTF8(importedIESearchUrlsTitle),
nsINavBookmarksService::DEFAULT_INDEX,
&keywordsFolder);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIWindowsRegKey> childKey;
if (NS_SUCCEEDED(regKey->OpenChild(keyName,
nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(childKey)))) {
nsAutoString url;
if (NS_SUCCEEDED(childKey->ReadStringValue(EmptyString(), url))) {
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), url))) {
NS_WARNING("Invalid url while importing smart keywords of MS IE");
++offset;
childKey->Close();
continue;
}
PRInt64 id;
rv = aBMS->InsertBookmark(keywordsFolder, uri,
nsINavBookmarksService::DEFAULT_INDEX,
NS_ConvertUTF16toUTF8(keyName),
&id);
NS_ENSURE_SUCCESS(rv, rv);
}
childKey->Close();
}
++offset;
}
}
return NS_OK;
}
void
nsIEProfileMigrator::ResolveShortcut(const nsString &aFileName, char** aOutURL)
{
HRESULT result;
IUniformResourceLocatorW* urlLink = nsnull;
result = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
IID_IUniformResourceLocatorW, (void**)&urlLink);
if (SUCCEEDED(result) && urlLink) {
IPersistFile* urlFile = nsnull;
result = urlLink->QueryInterface(IID_IPersistFile, (void**)&urlFile);
if (SUCCEEDED(result) && urlFile) {
result = urlFile->Load(aFileName.get(), STGM_READ);
if (SUCCEEDED(result) ) {
LPWSTR lpTemp = nsnull;
result = urlLink->GetURL(&lpTemp);
if (SUCCEEDED(result) && lpTemp) {
*aOutURL = (char*)ToNewUTF8String(nsDependentString(lpTemp));
// free the string that GetURL alloc'd
::CoTaskMemFree(lpTemp);
}
}
urlFile->Release();
}
urlLink->Release();
}
}
nsresult
nsIEProfileMigrator::ParseFavoritesFolder(nsIFile* aDirectory,
PRInt64 aParentFolder,
nsINavBookmarksService* aBMS,
const nsAString& aPersonalToolbarFolderName,
bool aIsAtRootLevel)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> entries;
rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
NS_ENSURE_SUCCESS(rv, rv);
do {
bool hasMore = false;
rv = entries->HasMoreElements(&hasMore);
if (NS_FAILED(rv) || !hasMore) break;
nsCOMPtr<nsISupports> supp;
rv = entries->GetNext(getter_AddRefs(supp));
if (NS_FAILED(rv)) break;
nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp));
nsCOMPtr<nsIURI> uri;
rv = NS_NewFileURI(getter_AddRefs(uri), currFile);
if (NS_FAILED(rv)) break;
nsAutoString bookmarkName;
currFile->GetLeafName(bookmarkName);
bool isSymlink = false;
bool isDir = false;
currFile->IsSymlink(&isSymlink);
currFile->IsDirectory(&isDir);
if (isSymlink) {
// It's a .lnk file. Get the path and check to see if it's
// a dir. If so, create a bookmark for the dir. If not, then
// simply do nothing and continue.
// Get the path that the .lnk file is pointing to.
nsAutoString path;
rv = currFile->GetTarget(path);
if (NS_FAILED(rv)) continue;
nsCOMPtr<nsILocalFile> localFile;
rv = NS_NewLocalFile(path, true, getter_AddRefs(localFile));
if (NS_FAILED(rv)) continue;
// Check for dir here. If path is not a dir, just continue with
// next import.
rv = localFile->IsDirectory(&isDir);
NS_ENSURE_SUCCESS(rv, rv);
if (!isDir) continue;
// Look for and strip out the .lnk extension.
NS_NAMED_LITERAL_STRING(lnkExt, ".lnk");
PRInt32 lnkExtStart = bookmarkName.Length() - lnkExt.Length();
if (StringEndsWith(bookmarkName, lnkExt,
CaseInsensitiveCompare))
bookmarkName.SetLength(lnkExtStart);
nsCOMPtr<nsIURI> bookmarkURI;
rv = NS_NewFileURI(getter_AddRefs(bookmarkURI), localFile);
if (NS_FAILED(rv)) continue;
PRInt64 id;
rv = aBMS->InsertBookmark(aParentFolder, bookmarkURI,
nsINavBookmarksService::DEFAULT_INDEX,
NS_ConvertUTF16toUTF8(bookmarkName),
&id);
if (NS_FAILED(rv)) continue;
}
else if (isDir) {
PRInt64 folderId;
if (bookmarkName.Equals(aPersonalToolbarFolderName)) {
rv = aBMS->GetToolbarFolder(&folderId);
if (NS_FAILED(rv)) break;
}
else {
rv = aBMS->CreateFolder(aParentFolder,
NS_ConvertUTF16toUTF8(bookmarkName),
nsINavBookmarksService::DEFAULT_INDEX,
&folderId);
if (NS_FAILED(rv)) continue;
}
rv = ParseFavoritesFolder(currFile, folderId,
aBMS, aPersonalToolbarFolderName,
false);
if (NS_FAILED(rv)) continue;
}
else {
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
nsCAutoString extension;
url->GetFileExtension(extension);
if (!extension.Equals("url", CaseInsensitiveCompare))
continue;
nsAutoString name(Substring(bookmarkName, 0,
bookmarkName.Length() - extension.Length() - 1));
nsAutoString path;
currFile->GetPath(path);
nsCString resolvedURL;
ResolveShortcut(path, getter_Copies(resolvedURL));
nsCOMPtr<nsIURI> resolvedURI;
rv = NS_NewURI(getter_AddRefs(resolvedURI), resolvedURL);
if (NS_FAILED(rv)) continue;
PRInt64 id;
rv = aBMS->InsertBookmark(aParentFolder, resolvedURI,
nsINavBookmarksService::DEFAULT_INDEX,
NS_ConvertUTF16toUTF8(name), &id);
if (NS_FAILED(rv)) continue;
}
}
while (1);
return rv;
}
nsresult
nsIEProfileMigrator::CopyPreferences(bool aReplace)
{
bool regKeyOpen = false;
const regEntry *entry,
*endEntry = ArrayEnd(gRegEntries);
nsCOMPtr<nsIPrefBranch> prefs;
{ // scope pserve why not
nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (pserve)
pserve->GetBranch("", getter_AddRefs(prefs));
}
if (!prefs)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey)
return NS_ERROR_UNEXPECTED;
// step through gRegEntries table
for (entry = gRegEntries; entry < endEntry; ++entry) {
// a new keyname? close any previous one and open the new one
if (entry->regKeyName) {
if (regKeyOpen) {
regKey->Close();
regKeyOpen = false;
}
regKeyOpen = NS_SUCCEEDED(regKey->
Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_ConvertASCIItoUTF16(
nsDependentCString(entry->regKeyName)),
nsIWindowsRegKey::ACCESS_READ));
}
if (regKeyOpen)
// read registry data
entry->entryHandler(regKey,
NS_ConvertASCIItoUTF16(
nsDependentCString(entry->regValueName)),
prefs, entry->prefKeyName);
}
nsresult rv = CopySecurityPrefs(prefs);
if (NS_FAILED(rv)) return rv;
rv = CopyProxyPreferences(prefs);
if (NS_FAILED(rv)) return rv;
return CopyStyleSheet(aReplace);
}
/* Fetch and translate the current user's cookies.
Return true if successful. */
nsresult
nsIEProfileMigrator::CopyCookies(bool aReplace)
{
// IE cookies are stored in files named <username>@domain[n].txt
// (in <username>'s Cookies folder. isn't the naming redundant?)
nsresult rv = NS_OK;
nsCOMPtr<nsIFile> cookiesDir;
nsCOMPtr<nsISimpleEnumerator> cookieFiles;
nsCOMPtr<nsICookieManager2> cookieManager(do_GetService(NS_COOKIEMANAGER_CONTRACTID));
if (!cookieManager)
return NS_ERROR_FAILURE;
// find the cookies directory
NS_GetSpecialDirectory(NS_WIN_COOKIES_DIR, getter_AddRefs(cookiesDir));
if (!cookiesDir)
return NS_ERROR_FAILURE;
// Check for Vista's UAC, if so, tack on a "Low" sub dir
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (regKey) {
NS_NAMED_LITERAL_STRING(regPath,"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System");
if (NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
regPath,
nsIWindowsRegKey::ACCESS_QUERY_VALUE))) {
PRUint32 value;
if (NS_SUCCEEDED(regKey->ReadIntValue(NS_LITERAL_STRING("EnableLUA"),
&value)) &&
value == 1) {
nsAutoString dir;
// For cases where we are running under protected mode, check
// cookiesDir for the Low sub directory. (Simpler than using
// process token calls to check our Vista integrity level.)
cookiesDir->GetLeafName(dir);
if (!dir.EqualsLiteral("Low"))
cookiesDir->Append(NS_LITERAL_STRING("Low"));
}
}
}
cookiesDir->GetDirectoryEntries(getter_AddRefs(cookieFiles));
if (!cookieFiles)
return NS_ERROR_FAILURE;
// fetch the current user's name from the environment
PRUnichar username[sUsernameLengthLimit+2];
::GetEnvironmentVariableW(L"USERNAME", username,
sizeof(username)/sizeof(PRUnichar));
username[sUsernameLengthLimit] = L'\0';
wcscat(username, L"@");
int usernameLength = wcslen(username);
// allocate a buffer into which to read each cookie file
char *fileContents = (char *) PR_Malloc(sInitialCookieBufferSize);
if (!fileContents)
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 fileContentsSize = sInitialCookieBufferSize;
do { // for each file in the cookies directory
// get the next file
bool moreFiles = false;
if (NS_FAILED(cookieFiles->HasMoreElements(&moreFiles)) || !moreFiles)
break;
nsCOMPtr<nsISupports> supFile;
cookieFiles->GetNext(getter_AddRefs(supFile));
nsCOMPtr<nsIFile> cookieFile(do_QueryInterface(supFile));
if (!cookieFile) {
rv = NS_ERROR_FAILURE;
break; // unexpected! punt!
}
// is it a cookie file for the current user?
nsAutoString fileName;
cookieFile->GetLeafName(fileName);
const nsAString &fileOwner = Substring(fileName, 0, usernameLength);
if (!fileOwner.Equals(username, CaseInsensitiveCompare))
continue;
// ensure the contents buffer is large enough to hold the entire file
// plus one byte (see DelimitField())
PRInt64 llFileSize;
if (NS_FAILED(cookieFile->GetFileSize(&llFileSize)))
continue;
PRUint32 fileSize, readSize;
LL_L2UI(fileSize, llFileSize);
if (fileSize >= fileContentsSize) {
PR_Free(fileContents);
fileContents = (char *) PR_Malloc(fileSize+1);
if (!fileContents) {
rv = NS_ERROR_FAILURE;
break; // fatal error
}
fileContentsSize = fileSize;
}
// read the entire cookie file
PRFileDesc *fd;
nsCOMPtr<nsILocalFile> localCookieFile(do_QueryInterface(cookieFile));
if (localCookieFile &&
NS_SUCCEEDED(localCookieFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd))) {
readSize = PR_Read(fd, fileContents, fileSize);
PR_Close(fd);
if (fileSize == readSize) { // translate this file's cookies
nsresult onerv;
onerv = CopyCookiesFromBuffer(fileContents, readSize, cookieManager);
if (NS_FAILED(onerv))
rv = onerv;
}
}
} while(1);
if (fileContents)
PR_Free(fileContents);
return rv;
}
/* Fetch cookies from a single IE cookie file.
Return true if successful. */
nsresult
nsIEProfileMigrator::CopyCookiesFromBuffer(char *aBuffer,
PRUint32 aBufferLength,
nsICookieManager2 *aCookieManager)
{
nsresult rv = NS_OK;
const char *bufferEnd = aBuffer + aBufferLength;
// cookie fields:
char *name,
*value,
*host,
*path,
*flags,
*expirationDate1, *expirationDate2,
*creationDate1, *creationDate2,
*terminator;
int flagsValue;
time_t expirationDate,
creationDate;
char hostCopy[sHostnameLengthLimit+1],
*hostCopyConstructor,
*hostCopyEnd = hostCopy + sHostnameLengthLimit;
do { // for each cookie in the buffer
DelimitField(&aBuffer, bufferEnd, &name);
DelimitField(&aBuffer, bufferEnd, &value);
DelimitField(&aBuffer, bufferEnd, &host);
DelimitField(&aBuffer, bufferEnd, &flags);
DelimitField(&aBuffer, bufferEnd, &expirationDate1);
DelimitField(&aBuffer, bufferEnd, &expirationDate2);
DelimitField(&aBuffer, bufferEnd, &creationDate1);
DelimitField(&aBuffer, bufferEnd, &creationDate2);
DelimitField(&aBuffer, bufferEnd, &terminator);
// it's a cookie if we got one of each
if (terminator >= bufferEnd)
break;
// IE stores deleted cookies with a zero-length value
if (*value == '\0')
continue;
// convert flags to an int, date numbers to useable dates
::sscanf(flags, "%d", &flagsValue);
expirationDate = FileTimeToTimeT(expirationDate1, expirationDate2);
creationDate = FileTimeToTimeT(creationDate1, creationDate2);
// munge host, and separate host from path
hostCopyConstructor = hostCopy;
// first, with a non-null domain, assume it's what Mozilla considers
// a domain cookie. see bug 222343.
if (*host && *host != '.' && *host != '/')
*hostCopyConstructor++ = '.';
// copy the host part and leave path pointing to the path part
for (path = host; *path && *path != '/'; ++path)
;
int hostLength = path - host;
if (hostLength > hostCopyEnd - hostCopyConstructor)
hostLength = hostCopyEnd - hostCopyConstructor;
PL_strncpy(hostCopyConstructor, host, hostLength);
hostCopyConstructor += hostLength;
*hostCopyConstructor = '\0';
nsDependentCString stringName(name),
stringPath(path);
// delete any possible extant matching host cookie and
// check if we're dealing with an IPv4/IPv6 hostname.
bool isIPAddress = false;
if (hostCopy[0] == '.') {
aCookieManager->Remove(nsDependentCString(hostCopy+1),
stringName, stringPath, false);
PRNetAddr addr;
if (PR_StringToNetAddr(hostCopy+1, &addr) == PR_SUCCESS)
isIPAddress = true;
}
nsresult onerv;
// Add() makes a new domain cookie
onerv = aCookieManager->Add(nsDependentCString(hostCopy + (isIPAddress ? 1 : 0)),
stringPath,
stringName,
nsDependentCString(value),
flagsValue & 0x1, // isSecure
false, // isHttpOnly
false, // isSession
PRInt64(expirationDate));
if (NS_FAILED(onerv)) {
rv = onerv;
break;
}
} while(aBuffer < bufferEnd);
return rv;
}
/* Delimit the next field in the IE cookie buffer.
when called:
aBuffer: at the beginning of the next field or preceding whitespace
aBufferEnd: one past the last valid character in the buffer
on return:
aField: at the beginning of the next field, which is null delimited
aBuffer: after the null at the end of the field
the character at which aBufferEnd points must be part of the buffer
so we can set it to \0.
*/
void
nsIEProfileMigrator::DelimitField(char **aBuffer,
const char *aBufferEnd,
char **aField)
{
char *scan = *aBuffer;
*aField = scan;
while (scan < aBufferEnd && (*scan != '\r' && *scan != '\n'))
++scan;
if (scan+1 < aBufferEnd && (*(scan+1) == '\r' || *(scan+1) == '\n') &&
*scan != *(scan+1)) {
*scan = '\0';
scan += 2;
} else {
if (scan <= aBufferEnd) // (1 byte past bufferEnd is guaranteed allocated)
*scan = '\0';
++scan;
}
*aBuffer = scan;
}
// conversion routine. returns 0 (epoch date) if the input is out of range
time_t
nsIEProfileMigrator::FileTimeToTimeT(const char *aLowDateIntString,
const char *aHighDateIntString)
{
FILETIME fileTime;
SYSTEMTIME systemTime;
tm tTime;
time_t rv;
::sscanf(aLowDateIntString, "%ld", &fileTime.dwLowDateTime);
::sscanf(aHighDateIntString, "%ld", &fileTime.dwHighDateTime);
::FileTimeToSystemTime(&fileTime, &systemTime);
tTime.tm_year = systemTime.wYear - 1900;
tTime.tm_mon = systemTime.wMonth-1;
tTime.tm_mday = systemTime.wDay;
tTime.tm_hour = systemTime.wHour;
tTime.tm_min = systemTime.wMinute;
tTime.tm_sec = systemTime.wSecond;
tTime.tm_isdst = -1;
rv = ::mktime(&tTime);
return rv < 0 ? 0 : rv;
}
/* Find the accessibility stylesheet if it exists and replace Mozilla's
with it. Return true if we found and copied a stylesheet. */
nsresult
nsIEProfileMigrator::CopyStyleSheet(bool aReplace)
{
nsresult rv = NS_OK; // return failure only if filecopy fails
// is there a trident user stylesheet?
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey)
return NS_ERROR_UNEXPECTED;
NS_NAMED_LITERAL_STRING(styleKey,
"Software\\Microsoft\\Internet Explorer\\Styles");
if (NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
styleKey, nsIWindowsRegKey::ACCESS_READ)))
return NS_OK;
NS_NAMED_LITERAL_STRING(myStyleValName, "Use My StyleSheet");
PRUint32 type, useMyStyle;
if (NS_SUCCEEDED(regKey->GetValueType(myStyleValName, &type)) &&
type == nsIWindowsRegKey::TYPE_INT &&
NS_SUCCEEDED(regKey->ReadIntValue(myStyleValName, &useMyStyle)) &&
useMyStyle == 1) {
nsAutoString tridentFilename;
if (NS_SUCCEEDED(regKey->ReadStringValue(
NS_LITERAL_STRING("User Stylesheet"), tridentFilename))) {
// tridentFilename is a native path to the specified stylesheet file
// point an nsIFile at it
nsCOMPtr<nsILocalFile> tridentFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
if (tridentFile) {
bool exists;
tridentFile->InitWithPath(tridentFilename);
tridentFile->Exists(&exists);
if (exists) {
// now establish our file (userContent.css in the profile/chrome dir)
nsCOMPtr<nsIFile> chromeDir;
NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR,
getter_AddRefs(chromeDir));
if (chromeDir)
rv = tridentFile->CopyTo(chromeDir,
NS_LITERAL_STRING("userContent.css"));
}
}
}
}
return rv;
}
void
nsIEProfileMigrator::GetUserStyleSheetFile(nsIFile **aUserFile)
{
nsCOMPtr<nsIFile> userChrome;
*aUserFile = 0;
// establish the chrome directory
NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, getter_AddRefs(userChrome));
if (userChrome) {
bool exists;
userChrome->Exists(&exists);
if (!exists &&
NS_FAILED(userChrome->Create(nsIFile::DIRECTORY_TYPE, 0755)))
return;
// establish the user content stylesheet file
userChrome->Append(NS_LITERAL_STRING("userContent.css"));
*aUserFile = userChrome;
NS_ADDREF(*aUserFile);
}
}
nsresult
nsIEProfileMigrator::CopySecurityPrefs(nsIPrefBranch* aPrefs)
{
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
NS_NAMED_LITERAL_STRING(key,
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
if (regKey &&
NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
key, nsIWindowsRegKey::ACCESS_READ))) {
PRUint32 value;
if (NS_SUCCEEDED(regKey->ReadIntValue(NS_LITERAL_STRING("SecureProtocols"),
&value))) {
aPrefs->SetBoolPref("security.enable_ssl3", (value >> 5) & true);
aPrefs->SetBoolPref("security.enable_tls", (value >> 7) & true);
}
}
return NS_OK;
}
struct ProxyData {
char* prefix;
PRInt32 prefixLength;
bool proxyConfigured;
char* hostPref;
char* portPref;
};
nsresult
nsIEProfileMigrator::CopyProxyPreferences(nsIPrefBranch* aPrefs)
{
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
NS_NAMED_LITERAL_STRING(key,
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
if (regKey &&
NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
key, nsIWindowsRegKey::ACCESS_READ))) {
nsAutoString buf;
PRUint32 proxyType = 0;
// If there's an autoconfig URL specified in the registry at all,
// it is being used.
if (NS_SUCCEEDED(regKey->
ReadStringValue(NS_LITERAL_STRING("AutoConfigURL"), buf))) {
// make this future-proof (MS IE will support IDN eventually and
// 'URL' will contain more than ASCII characters)
SetUnicharPref("network.proxy.autoconfig_url", buf, aPrefs);
proxyType = 2;
}
// ProxyEnable
PRUint32 enabled;
if (NS_SUCCEEDED(regKey->
ReadIntValue(NS_LITERAL_STRING("ProxyEnable"), &enabled))) {
if (enabled & 0x1)
proxyType = 1;
}
aPrefs->SetIntPref("network.proxy.type", proxyType);
if (NS_SUCCEEDED(regKey->
ReadStringValue(NS_LITERAL_STRING("ProxyOverride"), buf)))
ParseOverrideServers(buf, aPrefs);
if (NS_SUCCEEDED(regKey->
ReadStringValue(NS_LITERAL_STRING("ProxyServer"), buf))) {
ProxyData data[] = {
{ "ftp=", 4, false, "network.proxy.ftp",
"network.proxy.ftp_port" },
{ "http=", 5, false, "network.proxy.http",
"network.proxy.http_port" },
{ "https=", 6, false, "network.proxy.ssl",
"network.proxy.ssl_port" },
{ "socks=", 6, false, "network.proxy.socks",
"network.proxy.socks_port" },
};
PRInt32 startIndex = 0, count = 0;
bool foundSpecificProxy = false;
for (PRUint32 i = 0; i < ArrayLength(data); ++i) {
PRInt32 offset = buf.Find(NS_ConvertASCIItoUTF16(data[i].prefix));
if (offset >= 0) {
foundSpecificProxy = true;
data[i].proxyConfigured = true;
startIndex = offset + data[i].prefixLength;
PRInt32 terminal = buf.FindChar(';', offset);
count = terminal > startIndex ? terminal - startIndex :
buf.Length() - startIndex;
// hostPort now contains host:port
SetProxyPref(Substring(buf, startIndex, count), data[i].hostPref,
data[i].portPref, aPrefs);
}
}
if (!foundSpecificProxy) {
// No proxy config for any specific type was found, assume
// the ProxyServer value is of the form host:port and that
// it applies to all protocols.
for (PRUint32 i = 0; i < ArrayLength(data); ++i)
SetProxyPref(buf, data[i].hostPref, data[i].portPref, aPrefs);
aPrefs->SetBoolPref("network.proxy.share_proxy_settings", true);
}
}
}
return NS_OK;
}