/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is The Mail Profile Migrator. * * The Initial Developer of the Original Code is Ben Goodger. * Portions created by the Initial Developer are Copyright (C) 2004 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Ben Goodger * Scott MacGregor * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsMailProfileMigratorUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsIObserverService.h" #include "nsIPasswordManagerInternal.h" #include "nsIPrefLocalizedString.h" #include "nsIPrefService.h" #include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsISupportsPrimitives.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsSeamonkeyProfileMigrator.h" #include "nsIRelativeFilePref.h" #include "nsAppDirectoryServiceDefs.h" #include "prprf.h" #include "nsVoidArray.h" #include "msgCore.h" static PRUint32 StringHash(const char *ubuf); nsresult NS_MsgHashIfNecessary(nsCString &name); // Mail specific folder paths #define MAIL_DIR_50_NAME NS_LITERAL_STRING("Mail") #define IMAP_MAIL_DIR_50_NAME NS_LITERAL_STRING("ImapMail") #define NEWS_DIR_50_NAME NS_LITERAL_STRING("News") /////////////////////////////////////////////////////////////////////////////// // nsSeamonkeyProfileMigrator #define FILE_NAME_JUNKTRAINING NS_LITERAL_STRING("training.dat") #define FILE_NAME_PERSONALDICTIONARY NS_LITERAL_STRING("persdict.dat") #define FILE_NAME_PERSONAL_ADDRESSBOOK NS_LITERAL_STRING("abook.mab") #define FILE_NAME_MAILVIEWS NS_LITERAL_STRING("mailviews.dat") #define FILE_NAME_CERT8DB NS_LITERAL_STRING("cert8.db") #define FILE_NAME_KEY3DB NS_LITERAL_STRING("key3.db") #define FILE_NAME_SECMODDB NS_LITERAL_STRING("secmod.db") #define FILE_NAME_MIMETYPES NS_LITERAL_STRING("mimeTypes.rdf") #define FILE_NAME_PREFS NS_LITERAL_STRING("prefs.js") #define FILE_NAME_USER_PREFS NS_LITERAL_STRING("user.js") struct PrefBranchStruct { char* prefName; PRInt32 type; union { char* stringValue; PRInt32 intValue; PRBool boolValue; PRUnichar* wstringValue; }; }; NS_IMPL_ISUPPORTS2(nsSeamonkeyProfileMigrator, nsIMailProfileMigrator, nsITimerCallback) nsSeamonkeyProfileMigrator::nsSeamonkeyProfileMigrator() { mObserverService = do_GetService("@mozilla.org/observer-service;1"); mMaxProgress = LL_ZERO; mCurrentProgress = LL_ZERO; } nsSeamonkeyProfileMigrator::~nsSeamonkeyProfileMigrator() { } /////////////////////////////////////////////////////////////////////////////// // nsITimerCallback NS_IMETHODIMP nsSeamonkeyProfileMigrator::Notify(nsITimer *timer) { CopyNextFolder(); return NS_OK; } void nsSeamonkeyProfileMigrator::CopyNextFolder() { if (mFileCopyTransactionIndex < mFileCopyTransactions->Count()) { PRUint32 percentage = 0; fileTransactionEntry* fileTransaction = (fileTransactionEntry*) mFileCopyTransactions->SafeElementAt(mFileCopyTransactionIndex++); if (fileTransaction) // copy the file { fileTransaction->srcFile->CopyTo(fileTransaction->destFile, EmptyString()); // add to our current progress PRInt64 fileSize; fileTransaction->srcFile->GetFileSize(&fileSize); LL_ADD(mCurrentProgress, mCurrentProgress, fileSize); PRInt64 percentDone; LL_MUL(percentDone, mCurrentProgress, 100); LL_DIV(percentDone, percentDone, mMaxProgress); LL_L2UI(percentage, percentDone); nsAutoString index; index.AppendInt( percentage ); NOTIFY_OBSERVERS(MIGRATION_PROGRESS, index.get()); } // fire a timer to handle the next one. mFileIOTimer = do_CreateInstance("@mozilla.org/timer;1"); if (mFileIOTimer) mFileIOTimer->InitWithCallback(NS_STATIC_CAST(nsITimerCallback *, this), percentage == 100 ? 500 : 0, nsITimer::TYPE_ONE_SHOT); } else EndCopyFolders(); return; } void nsSeamonkeyProfileMigrator::EndCopyFolders() { // clear out the file transaction array if (mFileCopyTransactions) { PRUint32 count = mFileCopyTransactions->Count(); for (PRUint32 i = 0; i < count; ++i) { fileTransactionEntry* fileTransaction = (fileTransactionEntry*) mFileCopyTransactions->ElementAt(i); if (fileTransaction) { fileTransaction->srcFile = nsnull; fileTransaction->destFile = nsnull; delete fileTransaction; } } mFileCopyTransactions->Clear(); delete mFileCopyTransactions; } // notify the UI that we are done with the migration process nsAutoString index; index.AppendInt(nsIMailProfileMigrator::MAILDATA); NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); NOTIFY_OBSERVERS(MIGRATION_ENDED, nsnull); } /////////////////////////////////////////////////////////////////////////////// // nsIMailProfileMigrator NS_IMETHODIMP nsSeamonkeyProfileMigrator::Migrate(PRUint16 aItems, nsIProfileStartup* aStartup, const PRUnichar* aProfile) { nsresult rv = NS_OK; PRBool aReplace = aStartup ? PR_TRUE : PR_FALSE; if (!mTargetProfile) { GetProfilePath(aStartup, mTargetProfile); if (!mTargetProfile) return NS_ERROR_FAILURE; } if (!mSourceProfile) GetSourceProfile(aProfile); NOTIFY_OBSERVERS(MIGRATION_STARTED, nsnull); COPY_DATA(CopyPreferences, aReplace, nsIMailProfileMigrator::SETTINGS); // 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 PRUint32 count = mFileCopyTransactions->Count(); for (PRUint32 i = 0; i < count; ++i) { fileTransactionEntry* fileTransaction = (fileTransactionEntry*) mFileCopyTransactions->ElementAt(i); if (fileTransaction) { PRInt64 fileSize; fileTransaction->srcFile->GetFileSize(&fileSize); LL_ADD(mMaxProgress, mMaxProgress, fileSize); } } CopyNextFolder(); return rv; } NS_IMETHODIMP nsSeamonkeyProfileMigrator::GetMigrateData(const PRUnichar* aProfile, PRBool aReplace, PRUint16* aResult) { *aResult = 0; if (!mSourceProfile) { GetSourceProfile(aProfile); if (!mSourceProfile) return NS_ERROR_FILE_NOT_FOUND; } MigrationData data[] = { { ToNewUnicode(FILE_NAME_PREFS), nsIMailProfileMigrator::SETTINGS, PR_TRUE }, { ToNewUnicode(FILE_NAME_JUNKTRAINING), nsIMailProfileMigrator::JUNKTRAINING, PR_TRUE }, }; // Frees file name strings allocated above. GetMigrateDataFromArray(data, sizeof(data)/sizeof(MigrationData), aReplace, mSourceProfile, aResult); // Now locate passwords nsCString signonsFileName; GetSignonFileName(aReplace, getter_Copies(signonsFileName)); if (!signonsFileName.IsEmpty()) { nsAutoString fileName; CopyASCIItoUTF16(signonsFileName, fileName); nsCOMPtr sourcePasswordsFile; mSourceProfile->Clone(getter_AddRefs(sourcePasswordsFile)); sourcePasswordsFile->Append(fileName); PRBool 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::GetSourceExists(PRBool* aResult) { nsCOMPtr profiles; GetSourceProfiles(getter_AddRefs(profiles)); if (profiles) { PRUint32 count; profiles->Count(&count); *aResult = count > 0; } else *aResult = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsSeamonkeyProfileMigrator::GetSourceHasMultipleProfiles(PRBool* aResult) { nsCOMPtr profiles; GetSourceProfiles(getter_AddRefs(profiles)); if (profiles) { PRUint32 count; profiles->Count(&count); *aResult = count > 1; } else *aResult = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsSeamonkeyProfileMigrator::GetSourceProfiles(nsISupportsArray** aResult) { if (!mProfileNames && !mProfileLocations) { nsresult rv = NS_NewISupportsArray(getter_AddRefs(mProfileNames)); if (NS_FAILED(rv)) return rv; rv = NS_NewISupportsArray(getter_AddRefs(mProfileLocations)); if (NS_FAILED(rv)) return rv; // Fills mProfileNames and mProfileLocations FillProfileDataFromSeamonkeyRegistry(); } NS_IF_ADDREF(*aResult = mProfileNames); return NS_OK; } /////////////////////////////////////////////////////////////////////////////// // nsSeamonkeyProfileMigrator nsresult nsSeamonkeyProfileMigrator::GetSourceProfile(const PRUnichar* aProfile) { PRUint32 count; mProfileNames->Count(&count); for (PRUint32 i = 0; i < count; ++i) { nsCOMPtr str(do_QueryElementAt(mProfileNames, i)); nsString profileName; str->GetData(profileName); if (profileName.Equals(aProfile)) { mSourceProfile = do_QueryElementAt(mProfileLocations, i); break; } } return NS_OK; } nsresult nsSeamonkeyProfileMigrator::FillProfileDataFromSeamonkeyRegistry() { // Find the Seamonkey Registry nsCOMPtr fileLocator(do_GetService("@mozilla.org/file/directory_service;1")); nsCOMPtr seamonkeyRegistry; #ifdef XP_WIN fileLocator->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry)); NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE); seamonkeyRegistry->Append(NS_LITERAL_STRING("Mozilla")); seamonkeyRegistry->Append(NS_LITERAL_STRING("registry.dat")); #elif defined(XP_MACOSX) fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry)); NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE); seamonkeyRegistry->Append(NS_LITERAL_STRING("Mozilla")); seamonkeyRegistry->Append(NS_LITERAL_STRING("Application Registry")); #elif defined(XP_UNIX) fileLocator->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry)); NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE); seamonkeyRegistry->Append(NS_LITERAL_STRING(".mozilla")); seamonkeyRegistry->Append(NS_LITERAL_STRING("appreg")); #elif defined(XP_OS2) fileLocator->Get(NS_OS2_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry)); NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE); seamonkeyRegistry->Append(NS_LITERAL_STRING("Mozilla")); seamonkeyRegistry->Append(NS_LITERAL_STRING("registry.dat")); #endif return GetProfileDataFromRegistry(seamonkeyRegistry, mProfileNames, mProfileLocations); } #define F(a) nsSeamonkeyProfileMigrator::a #define MAKEPREFTRANSFORM(pref, newpref, getmethod, setmethod) \ { pref, newpref, F(Get##getmethod), F(Set##setmethod), PR_FALSE, -1 } #define MAKESAMETYPEPREFTRANSFORM(pref, method) \ { pref, 0, F(Get##method), F(Set##method), PR_FALSE, -1 } 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("news.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.html_domains", String), MAKESAMETYPEPREFTRANSFORM("mailnews.plaintext_domains", String), 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.gopher", String), MAKESAMETYPEPREFTRANSFORM("network.proxy.gopher_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) }; 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->ReadUserPrefs(sourcePrefsFile); nsCOMPtr branch(do_QueryInterface(psvc)); for (transform = gTransforms; transform < end; ++transform) transform->prefGetterFunc(transform, branch); // read in the various pref branch trees for accounts, identities, servers, etc. nsVoidArray* accounts = new nsVoidArray(); nsVoidArray* identities = new nsVoidArray(); nsVoidArray* servers = new nsVoidArray(); nsVoidArray* smtpservers = new nsVoidArray(); nsVoidArray* ldapservers = new nsVoidArray(); nsVoidArray* labelPrefs = new nsVoidArray(); if (!accounts || !identities || !servers || !smtpservers || !ldapservers) return NS_ERROR_OUT_OF_MEMORY; ReadBranch("mail.account.", psvc, accounts); ReadBranch("mail.identity.", psvc, identities); ReadBranch("mail.server.", psvc, servers); ReadBranch("mail.smtpserver.", psvc, smtpservers); ReadBranch("ldap_2.servers.", psvc, ldapservers); ReadBranch("mailnews.labels.", psvc, labelPrefs); // 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(identities, 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(servers, psvc); CopyAddressBookDirectories(ldapservers, psvc); // Now that we have all the pref data in memory, load the target pref file, // and write it back out psvc->ResetPrefs(); for (transform = gTransforms; transform < end; ++transform) transform->prefSetterFunc(transform, branch); WriteBranch("mail.account.", psvc, accounts); WriteBranch("mail.identity.", psvc, identities); WriteBranch("mail.server.", psvc, servers); WriteBranch("mail.smtpserver.", psvc, smtpservers); WriteBranch("ldap_2.servers.", psvc, ldapservers); WriteBranch("mailnews.labels.", psvc, labelPrefs); delete accounts; delete identities; delete servers; delete smtpservers; delete ldapservers; delete labelPrefs; nsCOMPtr targetPrefsFile; mTargetProfile->Clone(getter_AddRefs(targetPrefsFile)); targetPrefsFile->Append(aTargetPrefFileName); psvc->SavePrefFile(targetPrefsFile); return NS_OK; } nsresult nsSeamonkeyProfileMigrator::CopyAddressBookDirectories(nsVoidArray* aLdapServers, nsIPrefService* aPrefService) { // each server has a pref ending with .filename. The value of that pref points to a profile which we // need to migrate. nsAutoString index; index.AppendInt(nsIMailProfileMigrator::ADDRESSBOOK_DATA); NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); PRUint32 count = aLdapServers->Count(); for (PRUint32 i = 0; i < count; ++i) { PrefBranchStruct* pref = (PrefBranchStruct*) aLdapServers->ElementAt(i); nsDependentCString prefName (pref->prefName); if (StringEndsWith(prefName, nsDependentCString(".filename"))) { // should we be assuming utf-8 or ascii here? CopyFile(NS_ConvertUTF8toUTF16(pref->stringValue), NS_ConvertUTF8toUTF16(pref->stringValue)); } // we don't need to do anything to the fileName pref itself } NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); return NS_OK; } nsresult nsSeamonkeyProfileMigrator::CopySignatureFiles(nsVoidArray* aIdentities, nsIPrefService* aPrefService) { nsresult rv = NS_OK; PRUint32 count = aIdentities->Count(); for (PRUint32 i = 0; i < count; ++i) { PrefBranchStruct* pref = (PrefBranchStruct*)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 thenn 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, nsDependentCString(".sig_file"))) { // turn the pref into a nsILocalFile nsCOMPtr srcSigFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); srcSigFile->SetPersistentDescriptor(nsDependentCString(pref->stringValue)); nsCOMPtr targetSigFile; rv = mTargetProfile->Clone(getter_AddRefs(targetSigFile)); NS_ENSURE_SUCCESS(rv, rv); // now make the copy PRBool 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 nsCAutoString descriptorString; nsCOMPtr localFile = do_QueryInterface(targetSigFile); localFile->GetPersistentDescriptor(descriptorString); NS_Free(pref->stringValue); pref->stringValue = ToNewCString(descriptorString); } } } return NS_OK; } nsresult nsSeamonkeyProfileMigrator::CopyMailFolders(nsVoidArray* 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 = NS_OK; PRUint32 count = aMailServers->Count(); for (PRUint32 i = 0; i < count; ++i) { PrefBranchStruct* pref = (PrefBranchStruct*)aMailServers->ElementAt(i); nsDependentCString prefName (pref->prefName); if (StringEndsWith(prefName, NS_LITERAL_CSTRING(".directory-rel"))) { // 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) NS_Free(pref->stringValue); aMailServers->RemoveElementAt(i); // Now decrease i and count to match the removed element --i; --count; } else if (StringEndsWith(prefName, nsDependentCString(".directory"))) { // 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", getter_Copies(serverType)); nsCOMPtr sourceMailFolder; serverBranch->GetComplexValue("directory", NS_GET_IID(nsILocalFile), 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")) { // 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", getter_Copies(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. targetMailFolder->CreateUnique(nsIFile::DIRECTORY_TYPE, 0777); rv = 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. nsCAutoString descriptorString; nsCOMPtr localFile = do_QueryInterface(targetMailFolder); localFile->GetPersistentDescriptor(descriptorString); NS_Free(pref->stringValue); pref->stringValue = ToNewCString(descriptorString); } } else if (StringEndsWith(prefName, nsDependentCString(".newsrc.file"))) { // 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 nsILocalFile nsCOMPtr srcNewsRCFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); srcNewsRCFile->SetPersistentDescriptor(nsDependentCString(pref->stringValue)); // now make the copy PRBool 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 nsCAutoString descriptorString; nsCOMPtr localFile = do_QueryInterface(targetNewsRCFile); localFile->GetPersistentDescriptor(descriptorString); NS_Free(pref->stringValue); pref->stringValue = ToNewCString(descriptorString); } } } return NS_OK; } nsresult nsSeamonkeyProfileMigrator::CopyPreferences(PRBool aReplace) { nsresult rv = NS_OK; if (!aReplace) return rv; rv |= TransformPreferences(FILE_NAME_PREFS, FILE_NAME_PREFS); rv |= CopyFile(FILE_NAME_USER_PREFS, FILE_NAME_USER_PREFS); // Security Stuff rv |= CopyFile(FILE_NAME_CERT8DB, FILE_NAME_CERT8DB); rv |= CopyFile(FILE_NAME_KEY3DB, FILE_NAME_KEY3DB); rv |= CopyFile(FILE_NAME_SECMODDB, FILE_NAME_SECMODDB); // User MIME Type overrides rv |= CopyFile(FILE_NAME_MIMETYPES, FILE_NAME_MIMETYPES); rv |= CopyFile(FILE_NAME_PERSONALDICTIONARY, FILE_NAME_PERSONALDICTIONARY); rv |= CopyFile(FILE_NAME_MAILVIEWS, FILE_NAME_MAILVIEWS); return rv; } void nsSeamonkeyProfileMigrator::ReadBranch(const char * branchName, nsIPrefService* aPrefService, nsVoidArray* aPrefs) { // Enumerate the branch nsCOMPtr branch; aPrefService->GetBranch(branchName, getter_AddRefs(branch)); PRUint32 count; char** prefs = nsnull; nsresult rv = branch->GetChildList("", &count, &prefs); if (NS_FAILED(rv)) return; for (PRUint32 i = 0; i < count; ++i) { // Save each pref's value into an array char* currPref = prefs[i]; PRInt32 type; branch->GetPrefType(currPref, &type); PrefBranchStruct* pref = new PrefBranchStruct; pref->prefName = currPref; pref->type = type; switch (type) { case nsIPrefBranch::PREF_STRING: rv = branch->GetCharPref(currPref, &pref->stringValue); break; case nsIPrefBranch::PREF_BOOL: rv = branch->GetBoolPref(currPref, &pref->boolValue); break; case nsIPrefBranch::PREF_INT: rv = branch->GetIntPref(currPref, &pref->intValue); break; case nsIPrefBranch::PREF_INVALID: { nsCOMPtr str; rv = branch->GetComplexValue(currPref, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(str)); if (NS_SUCCEEDED(rv) && str) str->ToString(&pref->wstringValue); } break; } if (NS_SUCCEEDED(rv)) aPrefs->AppendElement((void*)pref); } } void nsSeamonkeyProfileMigrator::WriteBranch(const char * branchName, nsIPrefService* aPrefService, nsVoidArray* aPrefs) { nsresult rv; // Enumerate the branch nsCOMPtr branch; aPrefService->GetBranch(branchName, getter_AddRefs(branch)); PRUint32 count = aPrefs->Count(); for (PRUint32 i = 0; i < count; ++i) { PrefBranchStruct* pref = (PrefBranchStruct*)aPrefs->ElementAt(i); switch (pref->type) { case nsIPrefBranch::PREF_STRING: rv = branch->SetCharPref(pref->prefName, pref->stringValue); NS_Free(pref->stringValue); pref->stringValue = nsnull; break; case nsIPrefBranch::PREF_BOOL: rv = branch->SetBoolPref(pref->prefName, pref->boolValue); break; case nsIPrefBranch::PREF_INT: rv = branch->SetIntPref(pref->prefName, pref->intValue); break; case nsIPrefBranch::PREF_INVALID: nsCOMPtr pls(do_CreateInstance("@mozilla.org/pref-localizedstring;1")); pls->SetData(pref->wstringValue); rv = branch->SetComplexValue(pref->prefName, NS_GET_IID(nsIPrefLocalizedString), pls); NS_Free(pref->wstringValue); pref->wstringValue = nsnull; break; } NS_Free(pref->prefName); pref->prefName = nsnull; delete pref; pref = nsnull; } aPrefs->Clear(); } nsresult nsSeamonkeyProfileMigrator::DummyCopyRoutine(PRBool aReplace) { // place holder function only to fake the UI out into showing some migration process. return NS_OK; } nsresult nsSeamonkeyProfileMigrator::CopyJunkTraining(PRBool aReplace) { return aReplace ? CopyFile(FILE_NAME_JUNKTRAINING, FILE_NAME_JUNKTRAINING) : NS_OK; } nsresult nsSeamonkeyProfileMigrator::CopyPasswords(PRBool aReplace) { nsresult rv; nsCString signonsFileName; GetSignonFileName(aReplace, getter_Copies(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; } // helper functions for news migration static PRUint32 StringHash(const char *ubuf) { unsigned char * buf = (unsigned char*) ubuf; PRUint32 h=1; while(*buf) { h = 0x63c63cd9*h + 0x9c39c33d + (int32)*buf; buf++; } return h; } nsresult NS_MsgHashIfNecessary(nsCString &name) { #if defined(XP_UNIX) || defined(XP_BEOS) const PRUint32 MAX_LEN = 55; #elif defined(XP_WIN32) const PRUint32 MAX_LEN = 55; #elif defined(XP_OS2) const PRUint32 MAX_LEN = 55; #else #error need_to_define_your_max_filename_length #endif nsCAutoString illegalChars(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS); nsCAutoString str(name); // Given a filename, make it safe for filesystem // certain filenames require hashing because they // are too long or contain illegal characters PRInt32 illegalCharacterIndex = str.FindCharInSet(illegalChars); char hashedname[MAX_LEN + 1]; if (illegalCharacterIndex == kNotFound) { // no illegal chars, it's just too long // keep the initial part of the string, but hash to make it fit if (str.Length() > MAX_LEN) { PL_strncpy(hashedname, str.get(), MAX_LEN + 1); PR_snprintf(hashedname + MAX_LEN - 8, 9, "%08lx", (unsigned long) StringHash(str.get())); name = hashedname; } } else { // found illegal chars, hash the whole thing // if we do substitution, then hash, two strings // could hash to the same value. // for example, on mac: "foo__bar", "foo:_bar", "foo::bar" // would map to "foo_bar". this way, all three will map to // different values PR_snprintf(hashedname, 9, "%08lx", (unsigned long) StringHash(str.get())); name = hashedname; } return NS_OK; }