/* -*- 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 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& aResult) { if (mProfileNames.IsEmpty() && mProfileLocations.IsEmpty()) { // Fills mProfileNames and mProfileLocations FillProfileDataFromSeamonkeyRegistry(); } aResult = mProfileNames.Clone(); return NS_OK; } NS_IMETHODIMP nsSeamonkeyProfileMigrator::GetSourceProfileLocations( nsTArray>& 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 fileLocator( do_GetService("@mozilla.org/file/directory_service;1")); nsCOMPtr 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 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 psvc(do_GetService(NS_PREFSERVICE_CONTRACTID)); psvc->ResetPrefs(); nsCOMPtr sourcePrefsFile; mSourceProfile->Clone(getter_AddRefs(sourcePrefsFile)); sourcePrefsFile->Append(aSourcePrefFileName); psvc->ReadUserPrefsFromFile(sourcePrefsFile); nsCOMPtr 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 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 srcSigFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); rv = srcSigFile->SetPersistentDescriptor( nsDependentCString(pref->stringValue)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 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 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 \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 targetNewsRCFile; mTargetProfile->Clone(getter_AddRefs(targetNewsRCFile)); targetNewsRCFile->Append(NEWS_DIR_50_NAME); // turn the pref into a nsIFile nsCOMPtr 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 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 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 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 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 newKeys; for (auto pref : aIdentities) { nsDependentCString prefName(pref->prefName); nsTArray 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 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 keyHashTable; nsTArray newKeys; for (auto pref : aAccounts) { nsDependentCString prefName(pref->prefName); nsTArray 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 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 newKeys; for (auto pref : aMailServers) { nsDependentCString prefName(pref->prefName); nsTArray 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 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 smtpService( do_GetService("@mozilla.org/messengercompose/smtp;1", &rv)); NS_ENSURE_SUCCESS(rv, rv); nsTArray newKeys; for (auto pref : aServers) { nsDependentCString prefName(pref->prefName); nsTArray 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 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 keyHashTable; nsTHashMap pendingMigrations; nsTArray newKeys; nsresult rv; nsCOMPtr branch; rv = aPrefService->GetBranch("ldap_2.servers.", getter_AddRefs(branch)); NS_ENSURE_SUCCESS(rv, rv); for (auto pref : aAddressbooks) { nsDependentCString prefName(pref->prefName); nsTArray 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 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 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 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 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 abManager( do_GetService("@mozilla.org/abmanager;1", &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 branch; aPrefService->GetBranch(branchName, getter_AddRefs(branch)); nsTArray 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 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; }