diff options
Diffstat (limited to 'comm/mail/components/migration/src/nsSeamonkeyProfileMigrator.cpp')
-rw-r--r-- | comm/mail/components/migration/src/nsSeamonkeyProfileMigrator.cpp | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/comm/mail/components/migration/src/nsSeamonkeyProfileMigrator.cpp b/comm/mail/components/migration/src/nsSeamonkeyProfileMigrator.cpp new file mode 100644 index 0000000000..27251462c9 --- /dev/null +++ b/comm/mail/components/migration/src/nsSeamonkeyProfileMigrator.cpp @@ -0,0 +1,1175 @@ +/* -*- 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, + "chrome://messenger/locale/addressbook/" + "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, nsCString("mab"))) { + 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, nsCString("sqlite"))) { + 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; +} |