Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsMailProfileMigratorUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIMsgAccountManager.h"
#include "nsISmtpServer.h"
#include "nsISmtpService.h"
#include "nsIPrefLocalizedString.h"
#include "nsIPrefService.h"
#include "nsISupportsPrimitives.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsSeamonkeyProfileMigrator.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsComponentManagerUtils.h" // for do_CreateInstance
#include "mozilla/ArrayUtils.h"
#include "nsIFile.h"
#include "nsIAbManager.h"
#include "nsIAbDirectory.h"
#include "../../../../mailnews/import/src/MorkImport.h"
// Mail specific folder paths
#define MAIL_DIR_50_NAME u"Mail"_ns
#define IMAP_MAIL_DIR_50_NAME u"ImapMail"_ns
#define NEWS_DIR_50_NAME u"News"_ns
///////////////////////////////////////////////////////////////////////////////
// nsSeamonkeyProfileMigrator
#define FILE_NAME_JUNKTRAINING u"training.dat"_ns
#define FILE_NAME_PERSONALDICTIONARY u"persdict.dat"_ns
#define FILE_NAME_PERSONAL_ADDRESSBOOK u"abook.mab"_ns
#define FILE_NAME_MAILVIEWS u"mailviews.dat"_ns
#define FILE_NAME_CERT9DB u"cert9.db"_ns
#define FILE_NAME_KEY4DB u"key4.db"_ns
#define FILE_NAME_SECMODDB u"secmod.db"_ns
#define FILE_NAME_PREFS u"prefs.js"_ns
#define FILE_NAME_USER_PREFS u"user.js"_ns
struct PrefBranchStruct {
char* prefName;
int32_t type;
union {
char* stringValue;
int32_t intValue;
bool boolValue;
char16_t* wstringValue;
};
};
NS_IMPL_ISUPPORTS(nsSeamonkeyProfileMigrator, nsIMailProfileMigrator,
nsITimerCallback)
nsSeamonkeyProfileMigrator::nsSeamonkeyProfileMigrator() {}
nsSeamonkeyProfileMigrator::~nsSeamonkeyProfileMigrator() {}
///////////////////////////////////////////////////////////////////////////////
// nsIMailProfileMigrator
NS_IMETHODIMP
nsSeamonkeyProfileMigrator::Migrate(uint16_t aItems,
nsIProfileStartup* aStartup,
const char16_t* aProfile) {
nsresult rv = NS_OK;
bool aReplace = aStartup ? true : false;
if (!mTargetProfile) {
GetProfilePath(aStartup, mTargetProfile);
if (!mTargetProfile) return NS_ERROR_FAILURE;
}
if (!mSourceProfile) {
GetSourceProfile(aProfile);
if (!mSourceProfile) return NS_ERROR_FAILURE;
}
NOTIFY_OBSERVERS(MIGRATION_STARTED, nullptr);
if (aReplace) {
CopyPreferences(aReplace);
} else {
ImportPreferences(aItems);
}
// fake notifications for things we've already imported as part of
// CopyPreferences
COPY_DATA(DummyCopyRoutine, aReplace,
nsIMailProfileMigrator::ACCOUNT_SETTINGS);
COPY_DATA(DummyCopyRoutine, aReplace, nsIMailProfileMigrator::NEWSDATA);
// copy junk mail training file
COPY_DATA(CopyJunkTraining, aReplace, nsIMailProfileMigrator::JUNKTRAINING);
COPY_DATA(CopyPasswords, aReplace, nsIMailProfileMigrator::PASSWORDS);
// the last thing to do is to actually copy over any mail folders we have
// marked for copying we want to do this last and it will be asynchronous so
// the UI doesn't freeze up while we perform this potentially very long
// operation.
nsAutoString index;
index.AppendInt(nsIMailProfileMigrator::MAILDATA);
NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get());
// Generate the max progress value now that we know all of the files we need
// to copy
uint32_t count = mFileCopyTransactions.Length();
for (uint32_t i = 0; i < count; ++i) {
fileTransactionEntry fileTransaction = mFileCopyTransactions.ElementAt(i);
int64_t fileSize;
fileTransaction.srcFile->GetFileSize(&fileSize);
mMaxProgress += fileSize;
}
CopyNextFolder();
return rv;
}
NS_IMETHODIMP
nsSeamonkeyProfileMigrator::GetMigrateData(const char16_t* aProfile,
bool aReplace, uint16_t* aResult) {
*aResult = 0;
if (!mSourceProfile) {
GetSourceProfile(aProfile);
if (!mSourceProfile) return NS_ERROR_FILE_NOT_FOUND;
}
MigrationData data[] = {
{ToNewUnicode(FILE_NAME_PREFS), nsIMailProfileMigrator::SETTINGS, false},
{ToNewUnicode(FILE_NAME_JUNKTRAINING),
nsIMailProfileMigrator::JUNKTRAINING, true},
};
// Frees file name strings allocated above.
GetMigrateDataFromArray(data, sizeof(data) / sizeof(MigrationData), aReplace,
mSourceProfile, aResult);
// Now locate passwords
nsCString signonsFileName;
GetSignonFileName(aReplace, signonsFileName);
if (!signonsFileName.IsEmpty()) {
nsAutoString fileName;
CopyASCIItoUTF16(signonsFileName, fileName);
nsCOMPtr<nsIFile> sourcePasswordsFile;
mSourceProfile->Clone(getter_AddRefs(sourcePasswordsFile));
sourcePasswordsFile->Append(fileName);
bool exists;
sourcePasswordsFile->Exists(&exists);
if (exists) *aResult |= nsIMailProfileMigrator::PASSWORDS;
}
// add some extra migration fields for things we also migrate
*aResult |= nsIMailProfileMigrator::ACCOUNT_SETTINGS |
nsIMailProfileMigrator::MAILDATA |
nsIMailProfileMigrator::NEWSDATA |
nsIMailProfileMigrator::ADDRESSBOOK_DATA;
return NS_OK;
}
NS_IMETHODIMP
nsSeamonkeyProfileMigrator::GetSourceProfiles(nsTArray<nsString>& aResult) {
if (mProfileNames.IsEmpty() && mProfileLocations.IsEmpty()) {
// Fills mProfileNames and mProfileLocations
FillProfileDataFromSeamonkeyRegistry();
}
aResult = mProfileNames.Clone();
return NS_OK;
}
NS_IMETHODIMP
nsSeamonkeyProfileMigrator::GetSourceProfileLocations(
nsTArray<RefPtr<nsIFile>>& aResult) {
if (mProfileNames.IsEmpty() && mProfileLocations.IsEmpty()) {
// Fills mProfileNames and mProfileLocations
FillProfileDataFromSeamonkeyRegistry();
}
aResult = mProfileLocations.Clone();
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// nsSeamonkeyProfileMigrator
nsresult nsSeamonkeyProfileMigrator::GetSourceProfile(
const char16_t* aProfile) {
uint32_t count = mProfileNames.Length();
for (uint32_t i = 0; i < count; ++i) {
nsString profileName = mProfileNames[i];
if (profileName.Equals(aProfile)) {
mSourceProfile = mProfileLocations[i];
break;
}
}
return NS_OK;
}
nsresult nsSeamonkeyProfileMigrator::FillProfileDataFromSeamonkeyRegistry() {
// Find the Seamonkey Registry
nsCOMPtr<nsIProperties> fileLocator(
do_GetService("@mozilla.org/file/directory_service;1"));
nsCOMPtr<nsIFile> seamonkeyData;
#undef EXTRA_PREPEND
#ifdef XP_WIN
# define NEW_FOLDER "SeaMonkey"
# define EXTRA_PREPEND "Mozilla"
fileLocator->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(seamonkeyData));
NS_ENSURE_TRUE(seamonkeyData, NS_ERROR_FAILURE);
#elif defined(XP_MACOSX)
# define NEW_FOLDER "SeaMonkey"
# define EXTRA_PREPEND "Application Support"
fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(seamonkeyData));
NS_ENSURE_TRUE(seamonkeyData, NS_ERROR_FAILURE);
#elif defined(XP_UNIX)
# define NEW_FOLDER "seamonkey"
# define EXTRA_PREPEND ".mozilla"
fileLocator->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(seamonkeyData));
NS_ENSURE_TRUE(seamonkeyData, NS_ERROR_FAILURE);
#else
// On other OS just abort.
return NS_ERROR_FAILURE;
#endif
nsCOMPtr<nsIFile> newSeamonkeyData;
seamonkeyData->Clone(getter_AddRefs(newSeamonkeyData));
NS_ENSURE_TRUE(newSeamonkeyData, NS_ERROR_FAILURE);
#ifdef EXTRA_PREPEND
newSeamonkeyData->Append(NS_LITERAL_STRING_FROM_CSTRING(EXTRA_PREPEND));
#endif
newSeamonkeyData->Append(NS_LITERAL_STRING_FROM_CSTRING(NEW_FOLDER));
nsresult rv = GetProfileDataFromProfilesIni(newSeamonkeyData, mProfileNames,
mProfileLocations);
return rv;
}
static nsSeamonkeyProfileMigrator::PrefTransform gTransforms[] = {
MAKESAMETYPEPREFTRANSFORM("signon.SignonFileName", String),
MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showUserAgent", Bool),
MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showOrganization", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.collect_addressbook", String),
MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_outgoing", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.wrap_long_lines", Bool),
MAKESAMETYPEPREFTRANSFORM("mailnews.customHeaders", String),
MAKESAMETYPEPREFTRANSFORM("mail.default_html_action", Int),
MAKESAMETYPEPREFTRANSFORM("mail.forward_message_mode", Int),
MAKESAMETYPEPREFTRANSFORM("mail.SpellCheckBeforeSend", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.warn_on_send_accel_key", Bool),
MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showUserAgent", Bool),
MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showOrganization", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound.type", Int),
MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound.url", String),
MAKESAMETYPEPREFTRANSFORM("mail.biff.show_alert", Bool),
MAKESAMETYPEPREFTRANSFORM("network.proxy.type", Int),
MAKESAMETYPEPREFTRANSFORM("network.proxy.http", String),
MAKESAMETYPEPREFTRANSFORM("network.proxy.http_port", Int),
MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp", String),
MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp_port", Int),
MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl", String),
MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl_port", Int),
MAKESAMETYPEPREFTRANSFORM("network.proxy.socks", String),
MAKESAMETYPEPREFTRANSFORM("network.proxy.socks_port", Int),
MAKESAMETYPEPREFTRANSFORM("network.proxy.no_proxies_on", String),
MAKESAMETYPEPREFTRANSFORM("network.proxy.autoconfig_url", String),
MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.accounts", String),
MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.defaultaccount", String),
MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.localfoldersserver", String),
MAKESAMETYPEPREFTRANSFORM("mail.smtp.defaultserver", String),
MAKESAMETYPEPREFTRANSFORM("mail.smtpservers", String),
MAKESAMETYPEPREFTRANSFORM("msgcompose.font_face", String),
MAKESAMETYPEPREFTRANSFORM("msgcompose.font_size", String),
MAKESAMETYPEPREFTRANSFORM("msgcompose.text_color", String),
MAKESAMETYPEPREFTRANSFORM("msgcompose.background_color", String),
MAKEPREFTRANSFORM("mail.pane_config", "mail.pane_config.dynamic", Int,
Int)};
/**
* Use the current Seamonkey's prefs.js as base, and transform some branches.
* Thunderbird's prefs.js is thrown away.
*/
nsresult nsSeamonkeyProfileMigrator::TransformPreferences(
const nsAString& aSourcePrefFileName,
const nsAString& aTargetPrefFileName) {
PrefTransform* transform;
PrefTransform* end =
gTransforms + sizeof(gTransforms) / sizeof(PrefTransform);
// Load the source pref file
nsCOMPtr<nsIPrefService> psvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
psvc->ResetPrefs();
nsCOMPtr<nsIFile> sourcePrefsFile;
mSourceProfile->Clone(getter_AddRefs(sourcePrefsFile));
sourcePrefsFile->Append(aSourcePrefFileName);
psvc->ReadUserPrefsFromFile(sourcePrefsFile);
nsCOMPtr<nsIPrefBranch> branch(do_QueryInterface(psvc));
for (transform = gTransforms; transform < end; ++transform)
transform->prefGetterFunc(transform, branch);
static const char* branchNames[] = {
// Keep the three below first, or change the indexes below
"mail.identity.", "mail.server.", "ldap_2.servers.",
"mail.account.", "mail.smtpserver.", "mailnews.labels.",
"mailnews.tags."};
// read in the various pref branch trees for accounts, identities, servers,
// etc.
PBStructArray branches[MOZ_ARRAY_LENGTH(branchNames)];
uint32_t i;
for (i = 0; i < MOZ_ARRAY_LENGTH(branchNames); ++i)
ReadBranch(branchNames[i], psvc, branches[i]);
// The signature file prefs may be paths to files in the seamonkey profile
// path so we need to copy them over and fix these paths up before we write
// them out to the new prefs.js.
CopySignatureFiles(branches[0], psvc);
// Certain mail prefs may actually be absolute paths instead of profile
// relative paths we need to fix these paths up before we write them out to
// the new prefs.js
CopyMailFolders(branches[1], psvc);
TransformAddressbooksForImport(psvc, branches[2], true);
// Now that we have all the pref data in memory, load the target pref file,
// and write it back out.
psvc->ResetPrefs();
// XXX Re-order this?
for (transform = gTransforms; transform < end; ++transform)
transform->prefSetterFunc(transform, branch);
for (i = 0; i < MOZ_ARRAY_LENGTH(branchNames); i++)
WriteBranch(branchNames[i], psvc, branches[i]);
nsCOMPtr<nsIFile> targetPrefsFile;
mTargetProfile->Clone(getter_AddRefs(targetPrefsFile));
targetPrefsFile->Append(aTargetPrefFileName);
psvc->SavePrefFile(targetPrefsFile);
return NS_OK;
}
nsresult nsSeamonkeyProfileMigrator::CopySignatureFiles(
PBStructArray& aIdentities, nsIPrefService* aPrefService) {
nsresult rv = NS_OK;
uint32_t count = aIdentities.Length();
for (uint32_t i = 0; i < count; ++i) {
PrefBranchStruct* pref = aIdentities.ElementAt(i);
nsDependentCString prefName(pref->prefName);
// a partial fix for bug #255043
// if the user's signature file from seamonkey lives in the
// seamonkey profile root, we'll copy it over to the new
// thunderbird profile root and then set the pref to the new value
// note, this doesn't work for multiple signatures that live
// below the seamonkey profile root
if (StringEndsWith(prefName, ".sig_file"_ns)) {
// turn the pref into a nsIFile
nsCOMPtr<nsIFile> srcSigFile =
do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
rv = srcSigFile->SetPersistentDescriptor(
nsDependentCString(pref->stringValue));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> targetSigFile;
rv = mTargetProfile->Clone(getter_AddRefs(targetSigFile));
NS_ENSURE_SUCCESS(rv, rv);
// now make the copy
bool exists;
srcSigFile->Exists(&exists);
if (exists) {
nsAutoString leafName;
srcSigFile->GetLeafName(leafName);
srcSigFile->CopyTo(
targetSigFile,
leafName); // will fail if we've already copied a sig file here
targetSigFile->Append(leafName);
// now write out the new descriptor
nsAutoCString descriptorString;
rv = targetSigFile->GetPersistentDescriptor(descriptorString);
NS_ENSURE_SUCCESS(rv, rv);
free(pref->stringValue);
pref->stringValue = ToNewCString(descriptorString);
}
}
}
return NS_OK;
}
nsresult nsSeamonkeyProfileMigrator::CopyMailFolders(
PBStructArray& aMailServers, nsIPrefService* aPrefService) {
// Each server has a .directory pref which points to the location of the mail
// data for that server. We need to do two things for that case...
// (1) Fix up the directory path for the new profile
// (2) copy the mail folder data from the source directory pref to the
// destination directory pref
nsresult rv;
uint32_t count = aMailServers.Length();
for (uint32_t i = 0; i < count; i++) {
PrefBranchStruct* pref = aMailServers.ElementAt(i);
nsDependentCString prefName(pref->prefName);
if (StringEndsWith(prefName, ".directory-rel"_ns)) {
// When the directories are modified below, we may change the .directory
// pref. As we don't have a pref branch to modify at this stage and set
// up the relative folders properly, we'll just remove all the
// *.directory-rel prefs. Mailnews will cope with this, creating them
// when it first needs them.
if (pref->type == nsIPrefBranch::PREF_STRING) free(pref->stringValue);
aMailServers.RemoveElementAt(i);
// Now decrease i and count to match the removed element
--i;
--count;
} else if (StringEndsWith(prefName, ".directory"_ns)) {
// let's try to get a branch for this particular server to simplify things
prefName.Cut(prefName.Length() - strlen("directory"),
strlen("directory"));
prefName.Insert("mail.server.", 0);
nsCOMPtr<nsIPrefBranch> serverBranch;
aPrefService->GetBranch(prefName.get(), getter_AddRefs(serverBranch));
if (!serverBranch)
break; // should we clear out this server pref from aMailServers?
nsCString serverType;
serverBranch->GetCharPref("type", serverType);
nsCOMPtr<nsIFile> sourceMailFolder;
serverBranch->GetComplexValue("directory", NS_GET_IID(nsIFile),
getter_AddRefs(sourceMailFolder));
// now based on type, we need to build a new destination path for the mail
// folders for this server
nsCOMPtr<nsIFile> targetMailFolder;
if (serverType.Equals("imap")) {
mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
targetMailFolder->Append(IMAP_MAIL_DIR_50_NAME);
} else if (serverType.Equals("none") || serverType.Equals("pop3") ||
serverType.Equals("rss")) {
// local folders and POP3 servers go under <profile>\Mail
mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
targetMailFolder->Append(MAIL_DIR_50_NAME);
} else if (serverType.Equals("nntp")) {
mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
targetMailFolder->Append(NEWS_DIR_50_NAME);
}
if (targetMailFolder) {
// for all of our server types, append the host name to the directory as
// part of the new location
nsCString hostName;
serverBranch->GetCharPref("hostname", hostName);
targetMailFolder->Append(NS_ConvertASCIItoUTF16(hostName));
// we should make sure the host name based directory we are going to
// migrate the accounts into is unique. This protects against the case
// where the user has multiple servers with the same host name.
rv = targetMailFolder->CreateUnique(nsIFile::DIRECTORY_TYPE, 0777);
NS_ENSURE_SUCCESS(rv, rv);
(void)RecursiveCopy(sourceMailFolder, targetMailFolder);
// now we want to make sure the actual directory pref that gets
// transformed into the new profile's pref.js has the right file
// location.
nsAutoCString descriptorString;
rv = targetMailFolder->GetPersistentDescriptor(descriptorString);
NS_ENSURE_SUCCESS(rv, rv);
free(pref->stringValue);
pref->stringValue = ToNewCString(descriptorString);
}
} else if (StringEndsWith(prefName, ".newsrc.file"_ns)) {
// copy the news RC file into \News. this won't work if the user has
// different newsrc files for each account I don't know what to do in that
// situation.
nsCOMPtr<nsIFile> targetNewsRCFile;
mTargetProfile->Clone(getter_AddRefs(targetNewsRCFile));
targetNewsRCFile->Append(NEWS_DIR_50_NAME);
// turn the pref into a nsIFile
nsCOMPtr<nsIFile> srcNewsRCFile =
do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
rv = srcNewsRCFile->SetPersistentDescriptor(
nsDependentCString(pref->stringValue));
NS_ENSURE_SUCCESS(rv, rv);
// now make the copy
bool exists;
srcNewsRCFile->Exists(&exists);
if (exists) {
nsAutoString leafName;
srcNewsRCFile->GetLeafName(leafName);
srcNewsRCFile->CopyTo(
targetNewsRCFile,
leafName); // will fail if we've already copied a newsrc file here
targetNewsRCFile->Append(leafName);
// now write out the new descriptor
nsAutoCString descriptorString;
rv = targetNewsRCFile->GetPersistentDescriptor(descriptorString);
NS_ENSURE_SUCCESS(rv, rv);
free(pref->stringValue);
pref->stringValue = ToNewCString(descriptorString);
}
}
}
return NS_OK;
}
nsresult nsSeamonkeyProfileMigrator::CopyPreferences(bool aReplace) {
nsresult rv = NS_OK;
nsresult tmp;
tmp = TransformPreferences(FILE_NAME_PREFS, FILE_NAME_PREFS);
if (NS_FAILED(tmp)) {
rv = tmp;
}
tmp = CopyFile(FILE_NAME_USER_PREFS, FILE_NAME_USER_PREFS);
if (NS_FAILED(tmp)) {
rv = tmp;
}
// Security Stuff
tmp = CopyFile(FILE_NAME_CERT9DB, FILE_NAME_CERT9DB);
if (NS_FAILED(tmp)) {
rv = tmp;
}
tmp = CopyFile(FILE_NAME_KEY4DB, FILE_NAME_KEY4DB);
if (NS_FAILED(tmp)) {
rv = tmp;
}
tmp = CopyFile(FILE_NAME_SECMODDB, FILE_NAME_SECMODDB);
if (NS_FAILED(tmp)) {
rv = tmp;
}
tmp = CopyFile(FILE_NAME_PERSONALDICTIONARY, FILE_NAME_PERSONALDICTIONARY);
if (NS_FAILED(tmp)) {
rv = tmp;
}
tmp = CopyFile(FILE_NAME_MAILVIEWS, FILE_NAME_MAILVIEWS);
if (NS_FAILED(tmp)) {
rv = tmp;
}
return rv;
}
/**
* Use the current Thunderbird's prefs.js as base, transform branches of
* Seamonkey's prefs.js so that those branches can be imported without conflicts
* or overwriting.
*/
nsresult nsSeamonkeyProfileMigrator::ImportPreferences(uint16_t aItems) {
nsresult rv;
nsCOMPtr<nsIPrefService> psvc(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
// Because all operations on nsIPrefService or nsIPrefBranch will update
// prefs.js directly, we need to backup the current pref file to be used as a
// base later.
nsCOMPtr<nsIFile> targetPrefsFile;
mTargetProfile->Clone(getter_AddRefs(targetPrefsFile));
targetPrefsFile->Append(FILE_NAME_PREFS + u".orig"_ns);
rv = psvc->SavePrefFile(targetPrefsFile);
NS_ENSURE_SUCCESS(rv, rv);
// Load the source pref file.
rv = psvc->ResetPrefs();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> sourcePrefsFile;
mSourceProfile->Clone(getter_AddRefs(sourcePrefsFile));
sourcePrefsFile->Append(FILE_NAME_PREFS);
rv = psvc->ReadUserPrefsFromFile(sourcePrefsFile);
NS_ENSURE_SUCCESS(rv, rv);
// Read in the various pref branch trees for accounts, identities, servers,
// etc.
static const char* branchNames[] = {"mail.identity.", "mail.server.",
"mail.account.", "mail.smtpserver.",
"mailnews.labels.", "mailnews.tags.",
"ldap_2.servers."};
PBStructArray sourceBranches[MOZ_ARRAY_LENGTH(branchNames)];
for (uint32_t i = 0; i < MOZ_ARRAY_LENGTH(branchNames); i++) {
if ((!(aItems & nsIMailProfileMigrator::SETTINGS) && i <= 5) ||
(!(aItems & nsIMailProfileMigrator::ADDRESSBOOK_DATA) && i == 6)) {
continue;
}
ReadBranch(branchNames[i], psvc, sourceBranches[i]);
}
// Read back the original prefs.
rv = psvc->ResetPrefs();
NS_ENSURE_SUCCESS(rv, rv);
rv = psvc->ReadUserPrefsFromFile(targetPrefsFile);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgAccountManager> accountManager(
do_GetService("@mozilla.org/messenger/account-manager;1", &rv));
NS_ENSURE_SUCCESS(rv, rv);
PrefKeyHashTable smtpServerKeyHashTable;
PrefKeyHashTable identityKeyHashTable;
PrefKeyHashTable serverKeyHashTable;
// Transforming order is important here.
TransformSmtpServersForImport(sourceBranches[3], smtpServerKeyHashTable);
// mail.identity.idN.smtpServer depends on previous step.
TransformIdentitiesForImport(sourceBranches[0], accountManager,
smtpServerKeyHashTable, identityKeyHashTable);
TransformMailServersForImport(branchNames[1], psvc, sourceBranches[1],
accountManager, serverKeyHashTable);
// mail.accountN.{identities,server} depends on previous steps.
TransformMailAccountsForImport(psvc, sourceBranches[2], accountManager,
identityKeyHashTable, serverKeyHashTable);
// CopyMailFolders requires mail.server.serverN branch exists.
WriteBranch(branchNames[1], psvc, sourceBranches[1], false);
CopyMailFolders(sourceBranches[1], psvc);
// TransformAddressbooksForImport writes the branch and migrates the files.
TransformAddressbooksForImport(psvc, sourceBranches[6], false);
for (uint32_t i = 0; i < MOZ_ARRAY_LENGTH(branchNames); i++)
WriteBranch(branchNames[i], psvc, sourceBranches[i]);
targetPrefsFile->Remove(false);
return rv;
}
/**
* Transform mail.identity branch.
*/
nsresult nsSeamonkeyProfileMigrator::TransformIdentitiesForImport(
PBStructArray& aIdentities, nsIMsgAccountManager* accountManager,
PrefKeyHashTable& smtpServerKeyHashTable, PrefKeyHashTable& keyHashTable) {
nsresult rv;
nsTArray<nsCString> newKeys;
for (auto pref : aIdentities) {
nsDependentCString prefName(pref->prefName);
nsTArray<nsCString> keys;
ParseString(prefName, '.', keys);
auto key = keys[0];
if (key == "default") {
continue;
} else if (StringEndsWith(prefName, ".smtpServer"_ns)) {
nsDependentCString serverKey(pref->stringValue);
nsCString newServerKey;
if (smtpServerKeyHashTable.Get(serverKey, &newServerKey)) {
pref->stringValue = moz_xstrdup(newServerKey.get());
}
}
// For every seamonkey identity, create a new one to avoid conflicts.
nsCString newKey;
if (!keyHashTable.Get(key, &newKey)) {
nsCOMPtr<nsIMsgIdentity> identity;
rv = accountManager->CreateIdentity(getter_AddRefs(identity));
NS_ENSURE_SUCCESS(rv, rv);
identity->GetKey(newKey);
keyHashTable.InsertOrUpdate(key, newKey);
}
// Replace the prefName with the new key.
prefName.Assign(moz_xstrdup(newKey.get()));
for (uint32_t j = 1; j < keys.Length(); j++) {
prefName.Append('.');
prefName.Append(keys[j]);
}
pref->prefName = moz_xstrdup(prefName.get());
}
return NS_OK;
}
/**
* Transform mail.account branch. Also update mail.accountmanager.accounts at
* the end.
*/
nsresult nsSeamonkeyProfileMigrator::TransformMailAccountsForImport(
nsIPrefService* aPrefService, PBStructArray& aAccounts,
nsIMsgAccountManager* accountManager,
PrefKeyHashTable& identityKeyHashTable,
PrefKeyHashTable& serverKeyHashTable) {
nsTHashMap<nsCStringHashKey, nsCString> keyHashTable;
nsTArray<nsCString> newKeys;
for (auto pref : aAccounts) {
nsDependentCString prefName(pref->prefName);
nsTArray<nsCString> keys;
ParseString(prefName, '.', keys);
auto key = keys[0];
if (key == "default") {
continue;
} else if (StringEndsWith(prefName, ".identities"_ns)) {
nsDependentCString identityKey(pref->stringValue);
nsCString newIdentityKey;
if (identityKeyHashTable.Get(identityKey, &newIdentityKey)) {
pref->stringValue = moz_xstrdup(newIdentityKey.get());
}
} else if (StringEndsWith(prefName, ".server"_ns)) {
nsDependentCString serverKey(pref->stringValue);
nsCString newServerKey;
if (serverKeyHashTable.Get(serverKey, &newServerKey)) {
pref->stringValue = moz_xstrdup(newServerKey.get());
}
}
// For every seamonkey account, create a new one to avoid conflicts.
nsCString newKey;
if (!keyHashTable.Get(key, &newKey)) {
accountManager->GetUniqueAccountKey(newKey);
newKeys.AppendElement(newKey);
keyHashTable.InsertOrUpdate(key, newKey);
}
// Replace the prefName with the new key.
prefName.Assign(moz_xstrdup(newKey.get()));
for (uint32_t j = 1; j < keys.Length(); j++) {
prefName.Append('.');
prefName.Append(keys[j]);
}
pref->prefName = moz_xstrdup(prefName.get());
}
// Append newly create accounts to mail.accountmanager.accounts.
nsCOMPtr<nsIPrefBranch> branch;
nsCString newAccounts;
uint32_t count = newKeys.Length();
if (count) {
nsresult rv =
aPrefService->GetBranch("mail.accountmanager.", getter_AddRefs(branch));
NS_ENSURE_SUCCESS(rv, rv);
rv = branch->GetCharPref("accounts", newAccounts);
NS_ENSURE_SUCCESS(rv, rv);
}
for (uint32_t i = 0; i < count; i++) {
newAccounts.Append(',');
newAccounts.Append(newKeys[i]);
}
if (count) {
(void)branch->SetCharPref("accounts", newAccounts);
}
return NS_OK;
}
/**
* Transform mail.server branch.
*/
nsresult nsSeamonkeyProfileMigrator::TransformMailServersForImport(
const char* branchName, nsIPrefService* aPrefService,
PBStructArray& aMailServers, nsIMsgAccountManager* accountManager,
PrefKeyHashTable& keyHashTable) {
nsTArray<nsCString> newKeys;
for (auto pref : aMailServers) {
nsDependentCString prefName(pref->prefName);
nsTArray<nsCString> keys;
ParseString(prefName, '.', keys);
auto key = keys[0];
if (key == "default") {
continue;
}
nsCString newKey;
bool exists = keyHashTable.Get(key, &newKey);
if (!exists) {
do {
// Since updating prefs.js is batched, GetUniqueServerKey may return the
// previous key. Sleep 500ms and check if the returned key already
// exists to workaround it.
PR_Sleep(PR_MillisecondsToInterval(500));
accountManager->GetUniqueServerKey(newKey);
} while (newKeys.Contains(newKey));
newKeys.AppendElement(newKey);
keyHashTable.InsertOrUpdate(key, newKey);
}
prefName.Assign(moz_xstrdup(newKey.get()));
for (uint32_t j = 1; j < keys.Length(); j++) {
prefName.Append('.');
prefName.Append(keys[j]);
}
pref->prefName = moz_xstrdup(prefName.get());
// Set `mail.server.serverN.type` so that GetUniqueServerKey next time will
// get a new key.
if (!exists) {
nsCOMPtr<nsIPrefBranch> branch;
nsAutoCString serverTypeKey;
serverTypeKey.Assign(newKey.get());
serverTypeKey.AppendLiteral(".type");
nsresult rv = aPrefService->GetBranch(branchName, getter_AddRefs(branch));
NS_ENSURE_SUCCESS(rv, rv);
(void)branch->SetCharPref(serverTypeKey.get(), "placeholder"_ns);
}
}
return NS_OK;
}
/**
* Transform mail.smtpserver branch.
* CreateServer will update mail.smtpservers for us.
*/
nsresult nsSeamonkeyProfileMigrator::TransformSmtpServersForImport(
PBStructArray& aServers, PrefKeyHashTable& keyHashTable) {
nsresult rv;
nsCOMPtr<nsISmtpService> smtpService(
do_GetService("@mozilla.org/messengercompose/smtp;1", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<nsCString> newKeys;
for (auto pref : aServers) {
nsDependentCString prefName(pref->prefName);
nsTArray<nsCString> keys;
ParseString(prefName, '.', keys);
auto key = keys[0];
if (key == "default") {
continue;
}
// For every seamonkey smtp server, create a new one to avoid conflicts.
nsCString newKey;
if (!keyHashTable.Get(key, &newKey)) {
nsCOMPtr<nsISmtpServer> server;
rv = smtpService->CreateServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
char* str;
server->GetKey(&str);
newKey.Assign(str);
newKeys.AppendElement(newKey);
keyHashTable.InsertOrUpdate(key, newKey);
}
// Replace the prefName with the new key.
prefName.Assign(moz_xstrdup(newKey.get()));
for (uint32_t j = 1; j < keys.Length(); j++) {
prefName.Append('.');
prefName.Append(keys[j]);
}
pref->prefName = moz_xstrdup(prefName.get());
}
return NS_OK;
}
/**
* Transform ldap_2.servers branch.
*/
nsresult nsSeamonkeyProfileMigrator::TransformAddressbooksForImport(
nsIPrefService* aPrefService, PBStructArray& aAddressbooks, bool aReplace) {
nsTHashMap<nsCStringHashKey, nsCString> keyHashTable;
nsTHashMap<nsCStringHashKey, nsCString> pendingMigrations;
nsTArray<nsCString> newKeys;
nsresult rv;
nsCOMPtr<nsIPrefBranch> branch;
rv = aPrefService->GetBranch("ldap_2.servers.", getter_AddRefs(branch));
NS_ENSURE_SUCCESS(rv, rv);
for (auto pref : aAddressbooks) {
nsDependentCString prefName(pref->prefName);
nsTArray<nsCString> keys;
ParseString(prefName, '.', keys);
auto key = keys[0];
if (key == "default") {
continue;
}
nsCString newKey;
if (aReplace) {
newKey.Assign(key);
} else {
// For every addressbook, create a new one to avoid conflicts.
if (!keyHashTable.Get(key, &newKey)) {
uint32_t uniqueCount = 0;
while (true) {
nsAutoCString filenameKey;
nsAutoCString filename;
filenameKey.Assign(key);
filenameKey.AppendInt(++uniqueCount);
filenameKey.AppendLiteral(".filename");
nsresult rv = branch->GetCharPref(filenameKey.get(), filename);
if (NS_FAILED(rv)) {
newKey.Assign(key);
newKey.AppendInt(uniqueCount);
(void)branch->SetCharPref(filenameKey.get(), "placeholder"_ns);
break;
}
}
keyHashTable.InsertOrUpdate(key, newKey);
}
}
// Replace the prefName with the new key.
prefName.Assign(moz_xstrdup(newKey.get()));
for (uint32_t j = 1; j < keys.Length(); j++) {
prefName.Append('.');
prefName.Append(keys[j]);
if (j == 1) {
if (keys[j].Equals("dirType")) {
// Make sure we have the right type of directory.
pref->intValue = 101;
} else if (!aReplace && keys[j].Equals("description") &&
!strcmp(pref->stringValue,
"addressBook.properties")) {
// We're importing the default directories, which have localized
// names. The names are tied to the pref's name, which we are
// changing, so the localization will fail. Instead, do the
// localization here and assign it to the directory being copied.
nsCOMPtr<nsIPrefLocalizedString> localizedString;
rv = branch->GetComplexValue(pref->prefName,
NS_GET_IID(nsIPrefLocalizedString),
getter_AddRefs(localizedString));
if (NS_SUCCEEDED(rv)) {
nsString localizedValue;
localizedString->GetData(localizedValue);
pref->stringValue =
moz_xstrdup(NS_ConvertUTF16toUTF8(localizedValue).get());
}
} else if (keys[j].Equals("filename")) {
// Update the prefs for the new filename of the directory.
nsCString oldFileName(pref->stringValue);
nsCString newFileName(pref->stringValue);
if (StringEndsWith(newFileName, "mab"_ns)) {
newFileName.Cut(newFileName.Length() - strlen("mab"),
strlen("mab"));
newFileName.Append("sqlite");
pref->stringValue = moz_xstrdup(newFileName.get());
}
if (!aReplace) {
// Find an unused filename in the destination directory.
nsCOMPtr<nsIFile> targetAddrbook;
mTargetProfile->Clone(getter_AddRefs(targetAddrbook));
targetAddrbook->Append(NS_ConvertUTF8toUTF16(newFileName));
nsresult rv =
targetAddrbook->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
NS_ENSURE_SUCCESS(rv, rv);
nsString leafName;
targetAddrbook->GetLeafName(leafName);
pref->stringValue =
moz_xstrdup(NS_ConvertUTF16toUTF8(leafName).get());
}
if (StringEndsWith(oldFileName, "sqlite"_ns)) {
nsCOMPtr<nsIFile> oldFile;
mSourceProfile->Clone(getter_AddRefs(oldFile));
oldFile->Append(NS_ConvertUTF8toUTF16(oldFileName));
bool exists = false;
oldFile->Exists(&exists);
if (exists) {
// The source directory already has SQLite directories.
// Just copy them.
CopyFile(NS_ConvertUTF8toUTF16(oldFileName),
NS_ConvertUTF8toUTF16(newFileName));
continue;
}
oldFileName.Cut(oldFileName.Length() - strlen("sqlite"),
strlen("sqlite"));
oldFileName.Append("mab");
}
// Store the directories to be migrated for later.
pendingMigrations.InsertOrUpdate(newKey, oldFileName);
}
}
}
pref->prefName = moz_xstrdup(prefName.get());
}
// Write out the preferences and ask the address book manager to reload.
// This initializes the directories using the new prefs we've just set up.
WriteBranch("ldap_2.servers.", aPrefService, aAddressbooks, false);
NOTIFY_OBSERVERS("addrbook-reload", nullptr);
// Do the migration.
for (auto iter = pendingMigrations.Iter(); !iter.Done(); iter.Next()) {
nsCString dirPrefId = "ldap_2.servers."_ns;
dirPrefId.Append(iter.Key());
MigrateMABFile(dirPrefId, iter.UserData());
}
return NS_OK;
}
nsresult nsSeamonkeyProfileMigrator::MigrateMABFile(
const nsCString& aDirPrefId, const nsCString& aSourceFileName) {
nsCOMPtr<nsIFile> sourceFile;
mSourceProfile->Clone(getter_AddRefs(sourceFile));
sourceFile->Append(NS_ConvertUTF8toUTF16(aSourceFileName));
bool exists = false;
sourceFile->Exists(&exists);
if (!exists) return NS_OK;
nsresult rv;
nsCOMPtr<nsIAbManager> abManager(
do_GetService("@mozilla.org/abmanager;1", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAbDirectory> directory;
rv = abManager->GetDirectoryFromId(aDirPrefId, getter_AddRefs(directory));
NS_ENSURE_SUCCESS(rv, NS_OK);
rv = ReadMABToDirectory(sourceFile, directory);
return NS_OK;
}
void nsSeamonkeyProfileMigrator::ReadBranch(const char* branchName,
nsIPrefService* aPrefService,
PBStructArray& aPrefs) {
// Enumerate the branch
nsCOMPtr<nsIPrefBranch> branch;
aPrefService->GetBranch(branchName, getter_AddRefs(branch));
nsTArray<nsCString> prefs;
nsresult rv = branch->GetChildList("", prefs);
if (NS_FAILED(rv)) return;
for (auto& pref : prefs) {
// Save each pref's value into an array
char* currPref = moz_xstrdup(pref.get());
int32_t type;
branch->GetPrefType(currPref, &type);
PrefBranchStruct* prefBranch = new PrefBranchStruct;
prefBranch->prefName = currPref;
prefBranch->type = type;
switch (type) {
case nsIPrefBranch::PREF_STRING: {
nsCString str;
rv = branch->GetCharPref(currPref, str);
prefBranch->stringValue = moz_xstrdup(str.get());
break;
}
case nsIPrefBranch::PREF_BOOL:
rv = branch->GetBoolPref(currPref, &prefBranch->boolValue);
break;
case nsIPrefBranch::PREF_INT:
rv = branch->GetIntPref(currPref, &prefBranch->intValue);
break;
default:
NS_WARNING(
"Invalid Pref Type in "
"nsNetscapeProfileMigratorBase::ReadBranch");
break;
}
if (NS_SUCCEEDED(rv))
aPrefs.AppendElement(prefBranch);
else
delete prefBranch;
}
}
void nsSeamonkeyProfileMigrator::WriteBranch(const char* branchName,
nsIPrefService* aPrefService,
PBStructArray& aPrefs,
bool deallocate) {
// Enumerate the branch
nsCOMPtr<nsIPrefBranch> branch;
aPrefService->GetBranch(branchName, getter_AddRefs(branch));
uint32_t count = aPrefs.Length();
for (uint32_t i = 0; i < count; i++) {
PrefBranchStruct* pref = aPrefs.ElementAt(i);
switch (pref->type) {
case nsIPrefBranch::PREF_STRING:
(void)branch->SetCharPref(pref->prefName,
nsDependentCString(pref->stringValue));
if (deallocate) {
free(pref->stringValue);
pref->stringValue = nullptr;
}
break;
case nsIPrefBranch::PREF_BOOL:
(void)branch->SetBoolPref(pref->prefName, pref->boolValue);
break;
case nsIPrefBranch::PREF_INT:
(void)branch->SetIntPref(pref->prefName, pref->intValue);
break;
default:
NS_WARNING(
"Invalid Pref Type in "
"nsNetscapeProfileMigratorBase::WriteBranch");
break;
}
if (deallocate) {
free(pref->prefName);
pref->prefName = nullptr;
delete pref;
}
pref = nullptr;
}
if (deallocate) {
aPrefs.Clear();
}
}
nsresult nsSeamonkeyProfileMigrator::DummyCopyRoutine(bool aReplace) {
// place holder function only to fake the UI out into showing some migration
// process.
return NS_OK;
}
nsresult nsSeamonkeyProfileMigrator::CopyJunkTraining(bool aReplace) {
return aReplace ? CopyFile(FILE_NAME_JUNKTRAINING, FILE_NAME_JUNKTRAINING)
: NS_OK;
}
nsresult nsSeamonkeyProfileMigrator::CopyPasswords(bool aReplace) {
nsresult rv = NS_OK;
nsCString signonsFileName;
GetSignonFileName(aReplace, signonsFileName);
if (signonsFileName.IsEmpty()) return NS_ERROR_FILE_NOT_FOUND;
nsAutoString fileName;
CopyASCIItoUTF16(signonsFileName, fileName);
if (aReplace)
rv = CopyFile(fileName, fileName);
else {
// don't do anything right now
}
return rv;
}