diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/suite/components/migration | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/suite/components/migration')
16 files changed, 2529 insertions, 0 deletions
diff --git a/comm/suite/components/migration/SuiteProfileMigrator.js b/comm/suite/components/migration/SuiteProfileMigrator.js new file mode 100644 index 0000000000..0ad4dfcca3 --- /dev/null +++ b/comm/suite/components/migration/SuiteProfileMigrator.js @@ -0,0 +1,149 @@ +/* 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/. */ + +"use strict"; + +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +var { XPCOMUtils } = + ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const { FileUtils } = + ChromeUtils.import("resource://gre/modules/FileUtils.jsm"); +const { AppConstants } = + ChromeUtils.import('resource://gre/modules/AppConstants.jsm'); + +ChromeUtils.defineModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); + +function ProfileMigrator() { +} + +ProfileMigrator.prototype = { + migrate: function PM_migrate(aStartup) { + // By opening the wizard with a supplied migrator, it will automatically + // migrate from it. + let [key, migrator] = this._getDefaultMigrator(); + if (!key) + return; + + let params = Cc["@mozilla.org/array;1"] + .createInstance(Ci.nsIMutableArray); + params.appendElement(this._toString(key)); + params.appendElement(migrator); + params.appendElement(aStartup); + + Services.ww.openWindow(null, + "chrome://communicator/content/migration/migration.xul", + "_blank", + "chrome,dialog,modal,centerscreen,titlebar", + params); + }, + + _toString: function PM__toString(aStr) { + let str = Cc["@mozilla.org/supports-string;1"] + .createInstance(Ci.nsISupportsString); + str.data = aStr; + return str; + }, + + _getMigratorIfSourceExists: function PM__getMigratorIfSourceExists(aKey) { + let cid = "@mozilla.org/profile/migrator;1?app=suite&type=" + aKey; + let migrator = Cc[cid].createInstance(Ci.nsISuiteProfileMigrator); + if (migrator.sourceExists) + return migrator; + return null; + }, + + // We don't yet support checking for the default browser on all platforms, + // needless to say we don't have migrators for all browsers. Thus, for each + // platform, there's a fallback list of migrators used in these cases. + _PLATFORM_FALLBACK_LIST: + ["thunderbird"], + + _getDefaultMigrator: function PM__getDefaultMigrator() { + + let migratorsOrdered = Array.from(this._PLATFORM_FALLBACK_LIST); + + // FIXME This is all so not working currently. + // There are currently no migrators for browsers available. + // See Bug 739056. + if (false) { + let defaultBrowser = ""; + + if (AppConstants.platform == "win") { + try { + const REG_KEY = "SOFTWARE\\Classes\\HTTP\\shell\\open\\command"; + let regKey = Cc["@mozilla.org/windows-registry-key;1"] + .createInstance(Ci.nsIWindowsRegKey); + regKey.open(regKey.ROOT_KEY_LOCAL_MACHINE, REG_KEY, + regKey.ACCESS_READ); + let value = regKey.readStringValue("").toLowerCase(); + let pathMatches = value.match(/^"?(.+?\.exe)"?/); + if (!pathMatches) { + throw new Error("Could not extract path from " + + REG_KEY + "(" + value + ")"); + } + + // We want to find out what the default browser is but the path in and of + // itself isn't enough. Why? Because sometimes on Windows paths get + // truncated like so: C:\PROGRA~1\MOZILL~2\MOZILL~1.EXE. How do we know + // what product that is? Mozilla's file objects do nothing to 'normalize' + // the path so we need to attain an actual product descriptor from the + // file somehow, and in this case it means getting the "InternalName" + // field of the file's VERSIONINFO resource. + // + // In the file's resource segment there is a VERSIONINFO section that is + // laid out like this: + // + // VERSIONINFO + // StringFileInfo + // <TranslationID> + // InternalName "iexplore" + // VarFileInfo + // Translation <TranslationID> + // + // By Querying the VERSIONINFO section for its Tranlations, we can find + // out where the InternalName lives (A file can have more than one + // translation of its VERSIONINFO segment, but we just assume the first + // one). + let file = FileUtils.File(pathMatches[1]) + .QueryInterface(Ci.nsILocalFileWin); + switch (file.getVersionInfoField("InternalName").toLowerCase()) { + case "iexplore": + defaultBrowser = "ie"; + break; + case "chrome": + defaultBrowser = "chrome"; + break; + } + } + catch (ex) { + Cu.reportError("Could not retrieve default browser: " + ex); + } + } + + // If we found the default browser and we have support for that browser, + // make sure to check it before any other browser, by moving it to the head + // of the array. + if (defaultBrowser) { + migratorsOrdered.sort((a, b) => b == defaultBrowser ? 1 : 0); + } + } + + for (let key of migratorsOrdered) { + let migrator = this._getMigratorIfSourceExists(key); + if (migrator) { + return [key, migrator]; + } + } + + return ["", null]; + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIProfileMigrator]), + classDescription: "Profile Migrator", + contractID: "@mozilla.org/toolkit/profile-migrator;1", + classID: Components.ID("{d5148b7c-ba4e-4f7a-a80b-1ae48b90b910}"), +}; + +var NSGetFactory = XPCOMUtils.generateNSGetFactory([ProfileMigrator]); diff --git a/comm/suite/components/migration/SuiteProfileMigrator.manifest b/comm/suite/components/migration/SuiteProfileMigrator.manifest new file mode 100644 index 0000000000..a57376b855 --- /dev/null +++ b/comm/suite/components/migration/SuiteProfileMigrator.manifest @@ -0,0 +1,2 @@ +component {d5148b7c-ba4e-4f7a-a80b-1ae48b90b910} SuiteProfileMigrator.js +contract @mozilla.org/toolkit/profile-migrator;1 {d5148b7c-ba4e-4f7a-a80b-1ae48b90b910} diff --git a/comm/suite/components/migration/content/migration.js b/comm/suite/components/migration/content/migration.js new file mode 100644 index 0000000000..b106e51e22 --- /dev/null +++ b/comm/suite/components/migration/content/migration.js @@ -0,0 +1,413 @@ +/* -*- Mode: Java; 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/. */ + +var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const NS_PROFILE_MIGRATOR_CONTRACTID = + "@mozilla.org/profile/migrator;1?app=suite&type="; + +var MigrationWizard = { + _source: "", // Source Profile Migrator ContractID suffix + _itemsFlags: Ci.nsISuiteProfileMigrator.ALL, // Selected Import Data Sources + _selectedProfile: null, // Selected Profile name to import from + _wiz: null, // Shortcut to the wizard + _migrator: null, // The actual profile migrator. + _autoMigrate: null, // Whether or not we are actually migrating. + _singleItem: false, // Are we choosing just to import a single + // item into the current profile? + _newHomePage: null, // Are we setting a new home page - what to? + + init: function() { + Services.obs.addObserver(this, "Migration:Started"); + Services.obs.addObserver(this, "Migration:ItemBeforeMigrate"); + Services.obs.addObserver(this, "Migration:ItemAfterMigrate"); + Services.obs.addObserver(this, "Migration:Ended"); + Services.obs.addObserver(this, "Migration:Progress"); + + this._wiz = document.documentElement; + this._wiz.canRewind = false; + + if ("arguments" in window) { + if ("arguments" in window && window.arguments[0] == "bookmarks") { + this._singleItem = true; + this._itemsFlags = Ci.nsISuiteProfileMigrator.BOOKMARKS; + document.getElementById("fromFile").hidden = false; + document.getElementById("importBookmarks").hidden = false; + document.getElementById("importAll").hidden = true; + } + else if (window.arguments.length > 1) { + this._source = window.arguments[0]; + this._migrator = + window.arguments[1] instanceof Ci.nsISuiteProfileMigrator ? + window.arguments[1] : null; + this._autoMigrate = window.arguments[2] + .QueryInterface(Ci.nsIProfileStartup); + // Show the "nothing" option in the automigrate case to provide an + // easily identifiable way to avoid migration and create a new profile. + document.getElementById("nothing").hidden = false; + } + } + + this.onImportSourcePageShow(); + }, + + uninit: function() { + Services.obs.removeObserver(this, "Migration:Started"); + Services.obs.removeObserver(this, "Migration:ItemBeforeMigrate"); + Services.obs.removeObserver(this, "Migration:ItemAfterMigrate"); + Services.obs.removeObserver(this, "Migration:Ended"); + Services.obs.removeObserver(this, "Migration:Progress"); + }, + + // 1 - Import Source + onImportSourcePageShow: function() { + // Figure out what source apps are are available to import from: + var group = document.getElementById("importSourceGroup"); + var firstSelectable = null; + for (var i = 0; i < group.childNodes.length; ++i) { + var suffix = group.childNodes[i].id; + if (suffix != "nothing" && suffix != "fromFile") { + var contractID = NS_PROFILE_MIGRATOR_CONTRACTID + suffix; + var migrator = null; + if (contractID in Cc) { + migrator = Cc[contractID] + .createInstance(Ci.nsISuiteProfileMigrator); + } else { + dump("*** invalid contractID =" + contractID + "\n"); + // This is an invalid contract id, therefore hide this element + // and allow things to continue - that way we should be able to + // copy with anything happening. + group.childNodes[i].hidden = true; + break; + } + + // Ensure that we only allow import selections for profile + // migrators that support the requested action. + if (!migrator.sourceExists || + !(migrator.supportedItems & this._itemsFlags)) { + group.childNodes[i].hidden = true; + break; + } + + if (!firstSelectable && !group.childNodes[i].disabled && + !group.childNodes[i].hidden) { + firstSelectable = group.childNodes[i]; + } + } + } + + if (this._source) { + // Somehow the Profile Migrator got confused, and gave us a migrate source + // that doesn't actually exist. This could be because of a bogus registry + // state. Set the _source property to null so the first visible item in + // the list is selected instead. + if (document.getElementById(this._source).hidden) + this._source = null; + } + group.selectedItem = this._source ? + document.getElementById(this._source) : firstSelectable; + }, + + onImportSourcePageAdvanced: function() { + var newSource = document.getElementById("importSourceGroup").value; + + if (newSource == "nothing" || newSource == "fromFile") { + if (newSource == "fromFile") { + window.opener.fromFile = true; + } + document.documentElement.cancel(); + // Don't let the wizard migrate to the next page event though we've + // called cancel - cancel may not get processed first. + return false; + } + + if (!this._migrator || newSource != this._source) { + // Create the migrator for the selected source. + var contractID = NS_PROFILE_MIGRATOR_CONTRACTID + newSource; + this._migrator = Cc[contractID] + .createInstance(Ci.nsISuiteProfileMigrator); + + this._selectedProfile = null; + } + this._source = newSource; + + // check for more than one source profile + if (this._migrator.sourceHasMultipleProfiles) + this._wiz.currentPage.next = "selectProfile"; + else { + if (this._autoMigrate) + this._wiz.currentPage.next = "homePageImport"; + else if (this._singleItem) + this._wiz.currentPage.next = "migrating"; + else + this._wiz.currentPage.next = "importItems"; + + var sourceProfiles = this._migrator.sourceProfiles; + if (sourceProfiles && sourceProfiles.length == 1) { + this._selectedProfile = sourceProfiles + .queryElementAt(0, Ci.nsISupportsString).data; + } + else + this._selectedProfile = ""; + } + return true; + }, + + // 2 - [Profile Selection] + onSelectProfilePageShow: function() { + var profiles = document.getElementById("profiles"); + while (profiles.hasChildNodes()) + profiles.lastChild.remove(); + + var sourceProfiles = this._migrator.sourceProfiles; + var count = sourceProfiles.length; + for (var i = 0; i < count; ++i) { + var item = document.createElement("radio"); + item.id = sourceProfiles.queryElementAt(i, Ci.nsISupportsString).data; + item.setAttribute("label", item.id); + profiles.appendChild(item); + } + + profiles.selectedItem = this._selectedProfile ? + document.getElementById(this._selectedProfile) : profiles.firstChild; + }, + + onSelectProfilePageAdvanced: function() { + this._selectedProfile = document.getElementById("profiles").selectedItem.id; + + // If we're automigrating or just doing bookmarks don't show the item + // selection page + if (this._autoMigrate) + this._wiz.currentPage.next = "homePageImport"; + else if (this._singleItem) + this._wiz.currentPage.next = "migrating" + }, + + // 3 - ImportItems + onImportItemsPageShow: function() { + var dataSources = document.getElementById("dataSources"); + while (dataSources.hasChildNodes()) + dataSources.lastChild.remove(); + + var bundle = document.getElementById("bundle"); + + var items = this._migrator.getMigrateData(this._selectedProfile, + this._autoMigrate); + + for (var i = 0; i < 16; ++i) { + var itemID = items & (1 << i); + if (itemID) { + var checkbox = document.createElement("checkbox"); + checkbox.id = itemID; + try { + checkbox.setAttribute("label", + bundle.getString(itemID + "_" + this._source)); + } + catch (ex) { + checkbox.setAttribute("label", + bundle.getString(itemID + "_generic")); + } + dataSources.appendChild(checkbox); + if (this._itemsFlags & itemID) + checkbox.setAttribute("checked", true); + } + } + }, + + onImportItemsPageRewound: function() { + this._wiz.canAdvance = true; + this.onImportItemsPageAdvanced(); + }, + + onImportItemsPageAdvanced: function() { + var dataSources = document.getElementById("dataSources"); + this._itemsFlags = 0; + for (var i = 0; i < dataSources.childNodes.length; ++i) { + var checkbox = dataSources.childNodes[i]; + if (checkbox.checked) + this._itemsFlags |= checkbox.id; + } + }, + + onImportItemCommand: function(aEvent) { + this._wiz.canAdvance = document + .getElementById("dataSources") + .getElementsByAttribute("checked", "true").item(0) != null; + }, + + // 4 - Home Page Selection + onHomePageMigrationPageShow: function() { + // only want this on the first run + if (!this._autoMigrate) { + this._wiz.advance(); + return; + } + + var bundle = document.getElementById("bundle"); + var pageTitle = bundle.getString("homePageMigrationPageTitle"); + var pageDesc = bundle.getString("homePageMigrationDescription"); + + document.getElementById("homePageImport").setAttribute("label", pageTitle); + document.getElementById("homePageImportDesc") + .setAttribute("value", pageDesc); + + this._wiz._adjustWizardHeader(); + + var homePageStart = document.getElementById("homePageStart"); + + // Find out if the target profile already has a homepage or not + var mainStr = this.targetHasHomePageURL() ? + bundle.getString("homePageStartCurrent") : + bundle.getString("homePageStartDefault"); + + homePageStart.setAttribute("label", mainStr); + + var source = null; + if (this._source != "") { + source = "sourceName" + this._source; + } + + var availableItems = this._migrator.getMigrateData(this._selectedProfile, + this._autoMigrate); + + if (source && (availableItems & Ci.nsISuiteProfileMigrator.HOMEPAGEDATA)) { + var appName = document.getElementById("bundle").getString(source); + var oldHomePageLabel = bundle.getFormattedString("homePageImport", + [appName]); + var oldHomePage = document.getElementById("oldHomePage"); + oldHomePage.setAttribute("label", oldHomePageLabel); + oldHomePage.setAttribute("value", "source"); + oldHomePage.removeAttribute("hidden"); + oldHomePage.focus(); + + document.getElementById("homePageRadioGroup").selectedItem = oldHomePage; + } + else { + // if we don't have at least two options, just advance + this._wiz.advance(); + } + }, + + onHomePageMigrationPageAdvanced: function() { + // we might not have a selectedItem if we're in fallback mode + try { + this._newHomePage = document.getElementById("homePageRadioGroup") + .value; + } catch(ex) {} + }, + + // 5 - Migrating + onMigratingPageShow: function() { + this._wiz.getButton("cancel").disabled = true; + this._wiz.canRewind = false; + this._wiz.canAdvance = false; + + // When migrating a profile on startup, show all of the data that can be + // received from this source, but exclude home pages if the user didn't + // want to migrate it. + if (this._autoMigrate) { + this._itemsFlags = this._migrator.getMigrateData(this._selectedProfile, + this._autoMigrate); + if (!this._newHomePage) + this._itemsFlags &= ~Ci.nsISuiteProfileMigrator.HOMEPAGEDATA; + } + + this._listItems("migratingItems"); + setTimeout(this.onMigratingMigrate, 0, this); + }, + + onMigratingMigrate: function(aOuter) { + aOuter._migrator.migrate(aOuter._itemsFlags, + aOuter._autoMigrate, + aOuter._selectedProfile); + }, + + _listItems: function(aID) { + var items = document.getElementById(aID); + while (items.hasChildNodes()) + items.lastChild.remove(); + + var bundle = document.getElementById("bundle"); + var itemID; + for (var x = 1; x < Ci.nsISuiteProfileMigrator.ALL; + x = x << 1) { + if (x & this._itemsFlags) { + var label = document.createElement("label"); + label.id = x + "_migrated"; + try { + label.setAttribute("value", + bundle.stringBundle + .GetStringFromName(x + "_"+ this._source)); + label.setAttribute("class", "migration-pending"); + items.appendChild(label); + } + catch (ex) { + try { + label.setAttribute("value", bundle.getString(x + "_generic")); + label.setAttribute("class", "migration-pending"); + items.appendChild(label); + } + catch (e) { + // if the block above throws, we've enumerated all the import + // data types we currently support and are now just wasting time. + break; + } + } + } + } + }, + + observe: function(aSubject, aTopic, aData) { + switch (aTopic) { + case "Migration:Progress": + document.getElementById("progressBar").value = aData; + break; + case "Migration:Started": + break; + case "Migration:ItemBeforeMigrate": + var label = document.getElementById(aData + "_migrated"); + if (label) + label.setAttribute("class", "migration-in-progress"); + break; + case "Migration:ItemAfterMigrate": + var label = document.getElementById(aData + "_migrated"); + if (label) + label.setAttribute("class", "migration-finished"); + break; + case "Migration:Ended": + // We're done now. + this._wiz.canAdvance = true; + this._wiz.advance(); + + if (this._autoMigrate) + setTimeout(close, 5000); + + break; + } + }, + + onDonePageShow: function() { + this._wiz.getButton("cancel").disabled = true; + this._wiz.canRewind = false; + this._listItems("doneItems"); + }, + + targetHasHomePageURL: function() { + var targetPrefsFile = this._autoMigrate.directory; + + targetPrefsFile.append("prefs.js"); + + // If the target prefs file doesn't exist, then we can't have a + // homepage set in the target profile. + if (!targetPrefsFile.exists()) + return false; + + Services.prefs.resetPrefs(); + + Services.prefs.readUserPrefsFromFile(targetPrefsFile); + + return Services.prefs.prefHasUserValue("browser.startup.homepage"); + } +}; diff --git a/comm/suite/components/migration/content/migration.xul b/comm/suite/components/migration/content/migration.xul new file mode 100644 index 0000000000..ad8b279361 --- /dev/null +++ b/comm/suite/components/migration/content/migration.xul @@ -0,0 +1,95 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/migration/migration.dtd" > + +<wizard id="migrationWizard" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="&migrationWizard.title;" + onload="MigrationWizard.init()" + onunload="MigrationWizard.uninit()" + style="width: 40em; height: 32em;" + branded="true" + buttons="accept,cancel" + xmlns:xhtml="http://www.w3.org/1999/xhtml"> + + <script src="chrome://communicator/content/migration/migration.js"/> + + <stringbundle id="bundle" + src="chrome://communicator/locale/migration/migration.properties"/> + + <wizardpage id="importSource" pageid="importSource" next="selectProfile" + label="&importSource.title;" + onpageadvanced="return MigrationWizard.onImportSourcePageAdvanced();"> + <label id="importAll" + control="importSourceGroup">&importAllFrom.label;</label> + <label id="importBookmarks" control="importSourceGroup" + hidden="true">&importBookmarksFrom.label;</label> + + <radiogroup id="importSourceGroup" align="start"> + <radio id="thunderbird" label="&importFromThunderbird.label;" + accesskey="&importFromThunderbird.accesskey;" + value="thunderbird"/> + <!-- fromfile is used for bookmark importing --> + <radio id="fromFile" label="&importFromFile.label;" value="fromFile" + accesskey="&importFromFile.accesskey;" hidden="true"/> + <radio id="nothing" label="&importFromNothing.label;" value="nothing" + accesskey="&importFromNothing.accesskey;" hidden="true"/> + </radiogroup> + </wizardpage> + + <wizardpage id="selectProfile" pageid="selectProfile" + label="&selectProfile.title;" next="importItems" + onpageshow="return MigrationWizard.onSelectProfilePageShow();" + onpageadvanced="return MigrationWizard.onSelectProfilePageAdvanced();"> + <label control="profiles">&selectProfile.label;</label> + + <radiogroup id="profiles" align="left"/> + </wizardpage> + + <wizardpage id="importItems" pageid="importItems" label="&importItems.title;" + next="homePageImport" + onpageshow="return MigrationWizard.onImportItemsPageShow();" + onpageadvanced="return MigrationWizard.onImportItemsPageAdvanced();" + oncommand="MigrationWizard.onImportItemCommand();"> + <label control="dataSources">&importItems.label;</label> + + <vbox id="dataSources" style="overflow-y: auto;" + align="left" flex="1" role="group"/> + </wizardpage> + + <wizardpage id="homePageImport" pageid="homePageImport" + next="migrating" + onpageshow="return MigrationWizard.onHomePageMigrationPageShow();" + onpageadvanced="return MigrationWizard.onHomePageMigrationPageAdvanced();"> + + <label id="homePageImportDesc" control="homePageRadioGroup"/> + <radiogroup id="homePageRadioGroup" align="start"> + <radio id="oldHomePage" hidden="true"/> + <radio id="homePageStart" selected="true"/> + </radiogroup> + </wizardpage> + + <wizardpage id="migrating" pageid="migrating" label="&migrating.title;" + next="done" onpageshow="MigrationWizard.onMigratingPageShow();"> + <label control="migratingItems">&migrating.label;</label> + + <vbox id="migratingItems" style="overflow-y: auto;" align="left" role="group"/> + + <hbox> + <progressmeter class="progressmeter-statusbar" id="progressBar" + flex="1" mode="normal" value="0"/> + </hbox> + </wizardpage> + + <wizardpage id="done" pageid="done" label="&done.title;" + onpageshow="MigrationWizard.onDonePageShow();"> + <label control="doneItems">&done.label;</label> + + <vbox id="doneItems" style="overflow-y: auto;" align="left" role="group"/> + </wizardpage> +</wizard> diff --git a/comm/suite/components/migration/jar.mn b/comm/suite/components/migration/jar.mn new file mode 100644 index 0000000000..a202ce87a1 --- /dev/null +++ b/comm/suite/components/migration/jar.mn @@ -0,0 +1,7 @@ +# 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/. + +comm.jar: + content/communicator/migration/migration.xul (content/migration.xul) + content/communicator/migration/migration.js (content/migration.js) diff --git a/comm/suite/components/migration/moz.build b/comm/suite/components/migration/moz.build new file mode 100644 index 0000000000..ef93d7617a --- /dev/null +++ b/comm/suite/components/migration/moz.build @@ -0,0 +1,19 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DIRS += [ + "public", + "src", +] + +EXTRA_COMPONENTS += [ + "SuiteProfileMigrator.js", + "SuiteProfileMigrator.manifest", +] + +FINAL_LIBRARY = "suite" + +JAR_MANIFESTS += ["jar.mn"] diff --git a/comm/suite/components/migration/public/moz.build b/comm/suite/components/migration/public/moz.build new file mode 100644 index 0000000000..a25b10157b --- /dev/null +++ b/comm/suite/components/migration/public/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + "nsISuiteProfileMigrator.idl", +] + +XPIDL_MODULE = "suitemigration" + +EXPORTS += [ + "nsSuiteMigrationCID.h", +] diff --git a/comm/suite/components/migration/public/nsISuiteProfileMigrator.idl b/comm/suite/components/migration/public/nsISuiteProfileMigrator.idl new file mode 100644 index 0000000000..ca0bd98413 --- /dev/null +++ b/comm/suite/components/migration/public/nsISuiteProfileMigrator.idl @@ -0,0 +1,76 @@ +/* -*- 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 "nsISupports.idl" + +interface nsIArray; +interface nsIProfileStartup; + +[scriptable, uuid(b6adb2b8-5e3b-4fdd-b085-d58998b5c21a)] +interface nsISuiteProfileMigrator : nsISupports +{ + /** + * profile items to migrate. use with migrate(). + */ + const unsigned short SETTINGS = 0x0001; + const unsigned short COOKIES = 0x0002; + const unsigned short HISTORY = 0x0004; + const unsigned short HOMEPAGEDATA = 0x0008; + const unsigned short PASSWORDS = 0x0010; + const unsigned short BOOKMARKS = 0x0020; + const unsigned short OTHERDATA = 0x0040; + const unsigned short ACCOUNT_SETTINGS = 0x0080; + const unsigned short ADDRESSBOOK_DATA = 0x0100; + const unsigned short JUNKTRAINING = 0x0200; + const unsigned short NEWSDATA = 0x0400; + const unsigned short MAILDATA = 0x0800; + const unsigned short ALL = 0x0FFF; + + /** + * Copy user profile information to the current active profile. + * + * @param aItems list of data items to migrate. see above for values. + * @param aReplace replace or append current data where applicable. + * @param aProfile profile to migrate from, if there is more than one. + */ + void migrate(in unsigned short aItems, in nsIProfileStartup aStartup, + in wstring aProfile); + + /** + * A bit field containing profile items that this migrator is able + * to import for a specified source profile. + * + * @param aProfile the profile that we are looking for available data + * to import + * @param aStarting "true" if the profile is not currently being used. + * @returns bit field containing profile items (see above) + */ + unsigned short getMigrateData(in wstring aProfile, in boolean aDoingStartup); + + /** + * A bit field containing profile items that this migrator may be able + * to import for any source profile of its type. + */ + readonly attribute unsigned short supportedItems; + + /** + * Whether or not there is any data that can be imported from this + * browser (i.e. whether or not it is installed, and there exists + * a user profile) + */ + readonly attribute boolean sourceExists; + + /** + * Whether or not the import source implementing this interface + * has multiple user profiles configured. + */ + readonly attribute boolean sourceHasMultipleProfiles; + + /** + * An enumeration of available profiles. If the import source does + * not support profiles, this attribute is null. + */ + readonly attribute nsIArray sourceProfiles; +}; diff --git a/comm/suite/components/migration/public/nsSuiteMigrationCID.h b/comm/suite/components/migration/public/nsSuiteMigrationCID.h new file mode 100644 index 0000000000..252f5ed9df --- /dev/null +++ b/comm/suite/components/migration/public/nsSuiteMigrationCID.h @@ -0,0 +1,8 @@ +/* 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/. */ + +#define NS_SUITEPROFILEMIGRATOR_CONTRACTID_PREFIX "@mozilla.org/profile/migrator;1?app=suite&type=" + +#define NS_THUNDERBIRDPROFILEMIGRATOR_CID \ +{ 0x6ba91adb, 0xa4ed, 0x405f, { 0xbd, 0x6c, 0xe9, 0x04, 0xa9, 0x9d, 0x9a, 0xd8 } } diff --git a/comm/suite/components/migration/src/moz.build b/comm/suite/components/migration/src/moz.build new file mode 100644 index 0000000000..367fecf45e --- /dev/null +++ b/comm/suite/components/migration/src/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +SOURCES += [ + "nsSuiteProfileMigratorBase.cpp", + "nsSuiteProfileMigratorUtils.cpp", + "nsThunderbirdProfileMigrator.cpp", +] + +FINAL_LIBRARY = "suite" diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.cpp b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.cpp new file mode 100644 index 0000000000..521fa32215 --- /dev/null +++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.cpp @@ -0,0 +1,844 @@ +/* -*- 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 "nsAppDirectoryServiceDefs.h" +#include "nsSuiteProfileMigratorUtils.h" +#include "nsCRT.h" +#include "nsIFile.h" +#include "nsILineInputStream.h" +#include "nsIOutputStream.h" +#include "nsIPrefBranch.h" +#include "nsIPrefLocalizedString.h" +#include "nsIPrefService.h" +#include "nsIServiceManager.h" +#include "nsISupportsPrimitives.h" +#include "nsIURL.h" +#include "nsSuiteProfileMigratorBase.h" +#include "nsNetUtil.h" +#include "nsIDirectoryEnumerator.h" +#include "nsIFileProtocolHandler.h" +#include "nsServiceManagerUtils.h" +#include "prtime.h" +#include "nsINIParser.h" +#include "nsArrayUtils.h" + +#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 +#define DIR_NAME_CHROME u"chrome"_ns + +NS_IMPL_ISUPPORTS(nsSuiteProfileMigratorBase, nsISuiteProfileMigrator, + nsITimerCallback) + +using namespace mozilla; + +/////////////////////////////////////////////////////////////////////////////// +// nsITimerCallback + +NS_IMETHODIMP +nsSuiteProfileMigratorBase::Notify(nsITimer *timer) { + CopyNextFolder(); + return NS_OK; +} + +NS_IMETHODIMP +nsSuiteProfileMigratorBase::GetName(nsACString& aName) { + aName.AssignLiteral("nsSuiteProfileMigratorBase"); + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// nsSuiteProfileMigratorBase + +nsSuiteProfileMigratorBase::nsSuiteProfileMigratorBase() { + mFileCopyTransactionIndex = 0; + mObserverService = do_GetService("@mozilla.org/observer-service;1"); +} + +/////////////////////////////////////////////////////////////////////////////// +// nsISuiteProfileMigrator methods + +NS_IMETHODIMP +nsSuiteProfileMigratorBase::GetSourceExists(bool* aResult) { + nsCOMPtr<nsIArray> profiles; + GetSourceProfiles(getter_AddRefs(profiles)); + + if (profiles) { + uint32_t count; + profiles->GetLength(&count); + *aResult = count > 0; + } + else + *aResult = false; + + return NS_OK; +} + +NS_IMETHODIMP +nsSuiteProfileMigratorBase::GetSourceHasMultipleProfiles(bool* aResult) { + nsCOMPtr<nsIArray> profiles; + GetSourceProfiles(getter_AddRefs(profiles)); + + if (profiles) { + uint32_t count; + profiles->GetLength(&count); + *aResult = count > 1; + } + else + *aResult = false; + + return NS_OK; +} + +NS_IMETHODIMP +nsSuiteProfileMigratorBase::GetSourceProfiles(nsIArray** aResult) { + if (!mProfileNames && !mProfileLocations) { + nsresult rv; + mProfileNames = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + mProfileLocations = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + // Fills mProfileNames and mProfileLocations + FillProfileDataFromRegistry(); + } + + NS_IF_ADDREF(*aResult = mProfileNames); + return NS_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Pref Transform methods + +#define GETPREF(xform, method, value) \ + xform->prefHasValue = NS_SUCCEEDED(aBranch->method(xform->sourcePrefName, value)); \ + return NS_OK; + +#define SETPREF(xform, method, value) \ + if (xform->prefHasValue) { \ + return aBranch->method(xform->targetPrefName ? \ + xform->targetPrefName : \ + xform->sourcePrefName, value); \ + } \ + return NS_OK; + +nsresult +nsSuiteProfileMigratorBase::GetString(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + PrefTransform* xform = (PrefTransform*)aTransform; + nsCString str; + nsresult rv = aBranch->GetCharPref(xform->sourcePrefName, str); + if (NS_SUCCEEDED(rv)) { + xform->prefHasValue = true; + xform->stringValue = moz_xstrdup(str.get()); + } + return rv; +} + +nsresult +nsSuiteProfileMigratorBase::SetString(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + PrefTransform* xform = (PrefTransform*) aTransform; + SETPREF(aTransform, SetCharPref, + nsDependentCString(xform->stringValue)); +} + +nsresult +nsSuiteProfileMigratorBase::GetBool(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + GETPREF(aTransform, GetBoolPref, &aTransform->boolValue) +} + +nsresult +nsSuiteProfileMigratorBase::SetBool(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + SETPREF(aTransform, SetBoolPref, aTransform->boolValue) +} + +nsresult +nsSuiteProfileMigratorBase::GetInt(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + GETPREF(aTransform, GetIntPref, &aTransform->intValue) +} + +nsresult +nsSuiteProfileMigratorBase::SetInt(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + SETPREF(aTransform, SetIntPref, aTransform->intValue) +} + +nsresult +nsSuiteProfileMigratorBase::SetFile(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + // In this case targetPrefName is just an additional preference + // that needs to be modified and not what the sourcePrefName is + // going to be saved to once it is modified. + nsresult rv = NS_OK; + if (aTransform->prefHasValue) { + nsCOMPtr<nsIProtocolHandler> handler; + nsCOMPtr<nsIIOService> ioService(do_GetIOService()); + if (!ioService) + return NS_OK; + rv = ioService->GetProtocolHandler("file", getter_AddRefs(handler)); + if (NS_FAILED(rv)) + return NS_OK; + nsCOMPtr<nsIFileProtocolHandler> fileHandler(do_QueryInterface(handler)); + if (!fileHandler) + return NS_OK; + + nsCString fileURL(aTransform->stringValue); + nsCOMPtr<nsIFile> file; + // Start off by assuming fileURL is a URL spec and + // try and get a File from it. + rv = fileHandler->GetFileFromURLSpec(fileURL, getter_AddRefs(file)); + if (NS_FAILED(rv)) { + // Okay it wasn't a URL spec so assume it is a localfile, + // if this fails then just don't set anything. + rv = NS_NewNativeLocalFile(fileURL, false, getter_AddRefs(file)); + if (NS_FAILED(rv)) + return NS_OK; + } + // Now test to see if File exists and is an actual file. + bool exists = false; + rv = file->Exists(&exists); + if (NS_SUCCEEDED(rv) && exists) + rv = file->IsFile(&exists); + + if (NS_SUCCEEDED(rv) && exists) { + // After all that let's just get the URL spec and set the pref to it. + rv = fileHandler->GetURLSpecFromFile(file, fileURL); + if (NS_FAILED(rv)) + return NS_OK; + rv = aBranch->SetCharPref(aTransform->sourcePrefName, fileURL); + if (NS_SUCCEEDED(rv) && aTransform->targetPrefName) + rv = aBranch->SetIntPref(aTransform->targetPrefName, 1); + } + } + return rv; +} + +nsresult +nsSuiteProfileMigratorBase::SetImage(PrefTransform* aTransform, + nsIPrefBranch* aBranch) { + if (aTransform->prefHasValue) + // This transforms network.image.imageBehavior into + // permissions.default.image + return aBranch->SetIntPref("permissions.default.image", + aTransform->intValue == 1 ? 3 : + aTransform->intValue == 2 ? 2 : 1); + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// General Utility Methods + +nsresult +nsSuiteProfileMigratorBase::GetSourceProfile(const char16_t* aProfile) { + uint32_t count; + mProfileNames->GetLength(&count); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsISupportsString> str(do_QueryElementAt(mProfileNames, i)); + nsString profileName; + str->GetData(profileName); + if (profileName.Equals(aProfile)) + { + mSourceProfile = do_QueryElementAt(mProfileLocations, i); + break; + } + } + + return NS_OK; +} + +nsresult +nsSuiteProfileMigratorBase::GetProfileDataFromProfilesIni(nsIFile* aDataDir, + nsIMutableArray* aProfileNames, + nsIMutableArray* aProfileLocations) { + nsresult rv; + nsCOMPtr<nsIFile> profileIni; + rv = aDataDir->Clone(getter_AddRefs(profileIni)); + NS_ENSURE_SUCCESS(rv, rv); + + profileIni->Append(u"profiles.ini"_ns); + + // Does it exist? + bool profileFileExists = false; + rv = profileIni->Exists(&profileFileExists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!profileFileExists) + return NS_ERROR_FILE_NOT_FOUND; + + nsINIParser parser; + rv = parser.Init(profileIni); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString buffer, filePath; + bool isRelative; + + unsigned int c = 0; + for (c = 0; true; ++c) { + nsAutoCString profileID("Profile"); + profileID.AppendInt(c); + + rv = parser.GetString(profileID.get(), "IsRelative", buffer); + if (NS_FAILED(rv)) + break; + + isRelative = buffer.EqualsLiteral("1"); + + rv = parser.GetString(profileID.get(), "Path", filePath); + if (NS_FAILED(rv)) { + NS_ERROR("Malformed profiles.ini: Path= not found"); + continue; + } + + rv = parser.GetString(profileID.get(), "Name", buffer); + if (NS_FAILED(rv)) { + NS_ERROR("Malformed profiles.ini: Name= not found"); + continue; + } + + nsCOMPtr<nsIFile> rootDir; + rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(rootDir)); + NS_ENSURE_SUCCESS(rv, rv); + + if (isRelative) + rv = rootDir->SetRelativeDescriptor(aDataDir, filePath); + else + rv = rootDir->SetPersistentDescriptor(filePath); + + if (NS_FAILED(rv)) continue; + + bool exists; + rootDir->Exists(&exists); + + if (exists) { + aProfileLocations->AppendElement(rootDir); + + nsCOMPtr<nsISupportsString> profileNameString( + do_CreateInstance("@mozilla.org/supports-string;1")); + + profileNameString->SetData(NS_ConvertUTF8toUTF16(buffer)); + aProfileNames->AppendElement(profileNameString); + } + } + return NS_OK; +} + +nsresult +nsSuiteProfileMigratorBase::CopyFile(const char* aSourceFileName, + const char* aTargetFileName) { + nsCOMPtr<nsIFile> sourceFile; + mSourceProfile->Clone(getter_AddRefs(sourceFile)); + + sourceFile->AppendNative(nsDependentCString(aSourceFileName)); + bool exists = false; + sourceFile->Exists(&exists); + if (!exists) + return NS_OK; + + nsCOMPtr<nsIFile> targetFile; + mTargetProfile->Clone(getter_AddRefs(targetFile)); + + targetFile->AppendNative(nsDependentCString(aTargetFileName)); + targetFile->Exists(&exists); + if (exists) + targetFile->Remove(false); + + return sourceFile->CopyToNative(mTargetProfile, + nsDependentCString(aTargetFileName)); +} + +// helper function, copies the contents of srcDir into destDir. +// destDir will be created if it doesn't exist. +nsresult +nsSuiteProfileMigratorBase::RecursiveCopy(nsIFile* srcDir, + nsIFile* destDir) { + bool exists; + nsresult rv = srcDir->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!exists) + // We do not want to fail if the source folder does not exist because then + // parts of the migration process following this would not get executed + return NS_OK; + + bool isDir; + + rv = srcDir->IsDirectory(&isDir); + NS_ENSURE_SUCCESS(rv, rv); + + if (!isDir) + return NS_ERROR_INVALID_ARG; + + rv = destDir->Exists(&exists); + if (NS_SUCCEEDED(rv) && !exists) + rv = destDir->Create(nsIFile::DIRECTORY_TYPE, 0775); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDirectoryEnumerator> dirIterator; + rv = srcDir->GetDirectoryEntries(getter_AddRefs(dirIterator)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore = false; + while (NS_SUCCEEDED(dirIterator->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsIFile> dirEntry; + rv = dirIterator->GetNextFile(getter_AddRefs(dirEntry)); + if (NS_SUCCEEDED(rv) && dirEntry) { + rv = dirEntry->IsDirectory(&isDir); + if (NS_SUCCEEDED(rv)) { + if (isDir) { + nsCOMPtr<nsIFile> newChild; + rv = destDir->Clone(getter_AddRefs(newChild)); + if (NS_SUCCEEDED(rv)) { + nsAutoString leafName; + dirEntry->GetLeafName(leafName); + + newChild->AppendRelativePath(leafName); + rv = newChild->Exists(&exists); + if (NS_SUCCEEDED(rv) && !exists) + rv = newChild->Create(nsIFile::DIRECTORY_TYPE, 0775); + + rv = RecursiveCopy(dirEntry, newChild); + } + } + else { + // we aren't going to do any actual file copying here. Instead, + // add this to our file transaction list so we can copy files + // asynchronously... + fileTransactionEntry fileEntry; + + fileEntry.srcFile = dirEntry; + fileEntry.destFile = destDir; + + mFileCopyTransactions.AppendElement(fileEntry); + } + } + } + } + + return rv; +} + +void +nsSuiteProfileMigratorBase::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; + if (!prefBranch) { + NS_WARNING("Could not create new PrefBranchStruct"); + free(currPref); + return; + } + 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 prefBranch Type in " + "nsSuiteProfileMigratorBase::ReadBranch\n"); + break; + } + + if (NS_SUCCEEDED(rv)) + aPrefs.AppendElement(prefBranch); + } +} + +void +nsSuiteProfileMigratorBase::WriteBranch(const char * branchName, + nsIPrefService* aPrefService, + PBStructArray &aPrefs) { + // 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: { + branch->SetCharPref(pref->prefName, + nsDependentCString(pref->stringValue)); + free(pref->stringValue); + pref->stringValue = nullptr; + break; + } + case nsIPrefBranch::PREF_BOOL: + branch->SetBoolPref(pref->prefName, pref->boolValue); + break; + case nsIPrefBranch::PREF_INT: + branch->SetIntPref(pref->prefName, pref->intValue); + break; + default: + NS_WARNING("Invalid Pref Type in " + "nsSuiteProfileMigratorBase::WriteBranch\n"); + break; + } + free(pref->prefName); + pref->prefName = nullptr; + delete pref; + pref = nullptr; + } + aPrefs.Clear(); +} + +nsresult +nsSuiteProfileMigratorBase::GetFileValue(nsIPrefBranch* aPrefBranch, + const char* aRelPrefName, + const char* aPrefName, + nsIFile** aReturnFile) { + nsCString prefValue; + nsCOMPtr<nsIFile> theFile; + nsresult rv = aPrefBranch->GetCharPref(aRelPrefName, prefValue); + if (NS_SUCCEEDED(rv)) { + // The pref has the format: [ProfD]a/b/c + if (!StringBeginsWith(prefValue, "[ProfD]"_ns)) + return NS_ERROR_FILE_NOT_FOUND; + + rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(theFile)); + if (NS_FAILED(rv)) + return rv; + + rv = theFile->SetRelativeDescriptor(mSourceProfile, Substring(prefValue, 7)); + if (NS_FAILED(rv)) + return rv; + } else { + rv = aPrefBranch->GetComplexValue(aPrefName, + NS_GET_IID(nsIFile), + getter_AddRefs(theFile)); + } + + theFile.forget(aReturnFile); + return rv; +} + +/////////////////////////////////////////////////////////////////////////////// +// Mail Import Functions + +nsresult +nsSuiteProfileMigratorBase::CopyAddressBookDirectories(PBStructArray &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(nsISuiteProfileMigrator::ADDRESSBOOK_DATA); + NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); + + uint32_t count = aLdapServers.Length(); + for (uint32_t i = 0; i < count; ++i) { + PrefBranchStruct* pref = aLdapServers.ElementAt(i); + nsDependentCString prefName(pref->prefName); + + if (StringEndsWith(prefName, ".filename"_ns)) { + CopyFile(pref->stringValue, pref->stringValue); + } + + // we don't need to do anything to the fileName pref itself + } + + NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); + + return NS_OK; +} + +nsresult +nsSuiteProfileMigratorBase::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 + // old profile root, we'll copy it over to the new 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); + // will fail if we've already copied a sig file here + srcSigFile->CopyTo(targetSigFile, leafName); + 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 +nsSuiteProfileMigratorBase::CopyJunkTraining(bool aReplace) +{ + return aReplace ? CopyFile(FILE_NAME_JUNKTRAINING, + FILE_NAME_JUNKTRAINING) : NS_OK; +} + +nsresult +nsSuiteProfileMigratorBase::CopyMailFolderPrefs(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 + CopyFile(FILE_NAME_VIRTUALFOLDERS, FILE_NAME_VIRTUALFOLDERS); + + int32_t count = aMailServers.Length(); + for (int32_t i = 0; i < count; ++i) { + PrefBranchStruct* pref = aMailServers.ElementAt(i); + nsDependentCString prefName(pref->prefName); + + 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; + nsresult rv = GetFileValue(serverBranch, "directory-rel", "directory", + getter_AddRefs(sourceMailFolder)); + NS_ENSURE_SUCCESS(rv, rv); + + // 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")) { + // 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); + + 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); + nsresult 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); + // will fail if we've already copied a newsrc file here + srcNewsRCFile->CopyTo(targetNewsRCFile,leafName); + 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); + } + } + } + + // Remove all .directory-rel prefs as those might have changed; MailNews + // will create those prefs again on first use + for (int32_t i = count; i-- > 0; ) { + PrefBranchStruct* pref = aMailServers.ElementAt(i); + nsDependentCString prefName(pref->prefName); + + if (StringEndsWith(prefName, ".directory-rel"_ns)) { + if (pref->type == nsIPrefBranch::PREF_STRING) + free(pref->stringValue); + + aMailServers.RemoveElementAt(i); + } + } + + return NS_OK; +} + +void +nsSuiteProfileMigratorBase::CopyMailFolders() { + nsAutoString index; + index.AppendInt(nsISuiteProfileMigrator::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(); + mMaxProgress = 0; + mCurrentProgress = 0; + + for (uint32_t i = 0; i < count; ++i) { + fileTransactionEntry fileTransaction = mFileCopyTransactions[i]; + int64_t fileSize; + fileTransaction.srcFile->GetFileSize(&fileSize); + mMaxProgress += fileSize; + } + + CopyNextFolder(); +} + +void +nsSuiteProfileMigratorBase::CopyNextFolder() { + if (mFileCopyTransactionIndex < mFileCopyTransactions.Length()) { + fileTransactionEntry fileTransaction = + mFileCopyTransactions.ElementAt(mFileCopyTransactionIndex++); + + // copy the file + fileTransaction.srcFile->CopyTo(fileTransaction.destFile, + EmptyString()); + + // add to our current progress + int64_t fileSize; + fileTransaction.srcFile->GetFileSize(&fileSize); + mCurrentProgress += fileSize; + + uint32_t percentage = (uint32_t)(mCurrentProgress * 100 / mMaxProgress); + + nsAutoString index; + index.AppendInt(percentage); + + NOTIFY_OBSERVERS(MIGRATION_PROGRESS, index.get()); + + if (mFileCopyTransactionIndex == mFileCopyTransactions.Length()) + { + EndCopyFolders(); + return; + } + + // fire a timer to handle the next one. + mFileIOTimer = do_CreateInstance("@mozilla.org/timer;1"); + + if (mFileIOTimer) + mFileIOTimer->InitWithCallback(static_cast<nsITimerCallback *>(this), + 1, nsITimer::TYPE_ONE_SHOT); + } + else + EndCopyFolders(); + + return; +} + +void +nsSuiteProfileMigratorBase::EndCopyFolders() { + mFileCopyTransactions.Clear(); + mFileCopyTransactionIndex = 0; + + // notify the UI that we are done with the migration process + nsAutoString index; + index.AppendInt(nsISuiteProfileMigrator::MAILDATA); + NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); + + NOTIFY_OBSERVERS(MIGRATION_ENDED, nullptr); +} diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.h b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.h new file mode 100644 index 0000000000..6a4d40e3df --- /dev/null +++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.h @@ -0,0 +1,147 @@ +/* -*- 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/. */ + +#ifndef nsSuiteProfileMigratorBase___h___ +#define nsSuiteProfileMigratorBase___h___ + +#include "nsAttrValue.h" +#include "nsIFile.h" +#include "nsIMutableArray.h" +#include "nsTArray.h" +#include "nsITimer.h" +#include "nsIObserverService.h" +#include "nsISuiteProfileMigrator.h" +#include "nsSuiteMigrationCID.h" + +class nsIPrefBranch; +class nsIPrefService; + +struct fileTransactionEntry { + nsCOMPtr<nsIFile> srcFile; // the src path including leaf name + nsCOMPtr<nsIFile> destFile; // the destination path + nsString newName; // only valid if the file should be renamed after + // getting copied +}; + +#define FILE_NAME_PREFS "prefs.js" +#define FILE_NAME_JUNKTRAINING "training.dat" +#define FILE_NAME_VIRTUALFOLDERS "virtualFolders.dat" + +#define F(a) nsSuiteProfileMigratorBase::a + +#define MAKEPREFTRANSFORM(pref, newpref, getmethod, setmethod) \ + { pref, newpref, F(Get##getmethod), F(Set##setmethod), false, { -1 } } + +#define MAKESAMETYPEPREFTRANSFORM(pref, method) \ + { pref, 0, F(Get##method), F(Set##method), false, { -1 } } + +class nsSuiteProfileMigratorBase : public nsISuiteProfileMigrator, + public nsITimerCallback, + public nsINamed +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSITIMERCALLBACK + NS_DECL_NSINAMED + + nsSuiteProfileMigratorBase(); + + struct PrefTransform; + typedef nsresult(*prefConverter)(PrefTransform*, nsIPrefBranch*); + + struct PrefTransform { + const char* sourcePrefName; + const char* targetPrefName; + prefConverter prefGetterFunc; + prefConverter prefSetterFunc; + bool prefHasValue; + union { + int32_t intValue; + bool boolValue; + char* stringValue; + }; + }; + + struct PrefBranchStruct { + char* prefName; + int32_t type; + union { + char* stringValue; + int32_t intValue; + bool boolValue; + }; + }; + + typedef nsTArray<PrefBranchStruct*> PBStructArray; + + // nsISuiteProfileMigrator methods + NS_IMETHOD GetSourceExists(bool* aSourceExists) override; + NS_IMETHOD GetSourceHasMultipleProfiles(bool* aSourceHasMultipleProfiles) override; + NS_IMETHOD GetSourceProfiles(nsIArray** aResult) override; + + // Pref Transform Methods + static nsresult GetString(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult SetString(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult GetBool(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult SetBool(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult GetInt(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult SetInt(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult SetFile(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult SetImage(PrefTransform* aTransform, nsIPrefBranch* aBranch); + static nsresult SetCookie(PrefTransform* aTransform, nsIPrefBranch* aBranch); + +protected: + virtual ~nsSuiteProfileMigratorBase() {} + // This function is designed to be overriden by derived classes so that + // the required profile data for the specific application can be obtained. + virtual nsresult FillProfileDataFromRegistry() = 0; + + // General Utility Methods + nsresult GetSourceProfile(const char16_t* aProfile); + nsresult GetProfileDataFromProfilesIni(nsIFile* aDataDir, + nsIMutableArray* aProfileNames, + nsIMutableArray* aProfileLocations); + nsresult GetFileValue(nsIPrefBranch* aPrefBranch, const char* aRelPrefName, + const char* aPrefName, nsIFile** aReturnFile); + nsresult CopyFile(const char* aSourceFileName, + const char* aTargetFileName); + nsresult RecursiveCopy(nsIFile* srcDir, nsIFile* destDir); + void ReadBranch(const char * branchName, nsIPrefService* aPrefService, + PBStructArray &aPrefs); + void WriteBranch(const char * branchName, nsIPrefService* aPrefService, + PBStructArray &aPrefs); + + // Mail Import Functions + nsresult CopyAddressBookDirectories(PBStructArray &aLdapServers, + nsIPrefService* aPrefService); + nsresult CopySignatureFiles(PBStructArray &aIdentities, + nsIPrefService* aPrefService); + nsresult CopyJunkTraining(bool aReplace); + nsresult CopyMailFolderPrefs(PBStructArray &aMailServers, + nsIPrefService* aPrefService); + void CopyMailFolders(); + void CopyNextFolder(); + void EndCopyFolders(); + + // Source & Target profiles + nsCOMPtr<nsIFile> mSourceProfile; + nsCOMPtr<nsIFile> mTargetProfile; + + // list of src/destination files we still have to copy into the new profile + // directory + nsTArray<fileTransactionEntry> mFileCopyTransactions; + uint32_t mFileCopyTransactionIndex; + + nsCOMPtr<nsIObserverService> mObserverService; + int64_t mMaxProgress; + int64_t mCurrentProgress; + + nsCOMPtr<nsIMutableArray> mProfileNames; + nsCOMPtr<nsIMutableArray> mProfileLocations; + + nsCOMPtr<nsITimer> mFileIOTimer; +}; + +#endif diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.cpp b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.cpp new file mode 100644 index 0000000000..091a96540d --- /dev/null +++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.cpp @@ -0,0 +1,66 @@ +/* -*- 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 "nsSuiteProfileMigratorUtils.h" +#include "nsIPrefBranch.h" +#include "nsIFile.h" +#include "nsIInputStream.h" +#include "nsILineInputStream.h" +#include "nsIProfileMigrator.h" + +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsIProperties.h" +#include "nsServiceManagerUtils.h" +#include "nsISupportsPrimitives.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsIStringBundle.h" +#include "nsCRT.h" + +#define MIGRATION_BUNDLE "chrome://communicator/migration/locale/migration.properties" + +using namespace mozilla; + +void GetMigrateDataFromArray(MigrationData* aDataArray, + int32_t aDataArrayLength, + bool aReplace, nsIFile* aSourceProfile, + uint16_t* aResult) +{ + nsCOMPtr<nsIFile> sourceFile; + bool exists; + MigrationData* cursor; + MigrationData* end = aDataArray + aDataArrayLength; + for (cursor = aDataArray; cursor < end; ++cursor) { + // When in replace mode, all items can be imported. + // When in non-replace mode, only items that do not require file + // replacement can be imported. + if (aReplace || !cursor->replaceOnly) { + aSourceProfile->Clone(getter_AddRefs(sourceFile)); + sourceFile->AppendNative(nsDependentCString(cursor->fileName)); + sourceFile->Exists(&exists); + if (exists) + *aResult |= cursor->sourceFlag; + } + } +} + +void +GetProfilePath(nsIProfileStartup* aStartup, nsIFile** aProfileDir) +{ + *aProfileDir = nullptr; + + if (aStartup) { + aStartup->GetDirectory(aProfileDir); + } + else { + nsCOMPtr<nsIProperties> dirSvc + (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); + if (dirSvc) { + dirSvc->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), + (void**)aProfileDir); + } + } +} diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.h b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.h new file mode 100644 index 0000000000..e469f5723d --- /dev/null +++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.h @@ -0,0 +1,60 @@ +/* -*- 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/. */ + +#ifndef SuiteProfileMigratorUtils_h__ +#define SuiteProfileMigratorUtils_h__ + +#define MIGRATION_ITEMBEFOREMIGRATE "Migration:ItemBeforeMigrate" +#define MIGRATION_ITEMAFTERMIGRATE "Migration:ItemAfterMigrate" +#define MIGRATION_STARTED "Migration:Started" +#define MIGRATION_ENDED "Migration:Ended" +#define MIGRATION_PROGRESS "Migration:Progress" + +#define NOTIFY_OBSERVERS(message, item) \ + mObserverService->NotifyObservers(nullptr, message, item) + +#define COPY_DATA(func, replace, itemIndex) \ + if (NS_SUCCEEDED(rv) && (aItems & itemIndex || !aItems)) { \ + nsAutoString index; \ + index.AppendInt(itemIndex); \ + NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); \ + rv = func(replace); \ + NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); \ + } + +#define MAKEPREFTRANSFORM(pref, newpref, getmethod, setmethod) \ + { pref, newpref, F(Get##getmethod), F(Set##setmethod), false, { -1 } } + +#define MAKESAMETYPEPREFTRANSFORM(pref, method) \ + { pref, 0, F(Get##method), F(Set##method), false, { -1 } } + +#include "nsString.h" +#include "nscore.h" +#include "nsCOMPtr.h" + +class nsIPrefBranch; +class nsIProfileStartup; +class nsIFile; + +struct MigrationData { + const char* fileName; + uint32_t sourceFlag; + bool replaceOnly; +}; + +class nsIFile; +void GetMigrateDataFromArray(MigrationData* aDataArray, + int32_t aDataArrayLength, + bool aReplace, + nsIFile* aSourceProfile, + uint16_t* aResult); + + +// get the base directory of the *target* profile +// this is already cloned, modify it to your heart's content +void GetProfilePath(nsIProfileStartup* aStartup, + nsIFile** aProfileDir); + +#endif diff --git a/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.cpp b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.cpp new file mode 100644 index 0000000000..3f5365f12a --- /dev/null +++ b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.cpp @@ -0,0 +1,571 @@ +/* -*- 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 "nsSuiteProfileMigratorUtils.h" +#include "mozilla/ArrayUtils.h" +#include "nsCRT.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIObserverService.h" +#include "nsIPrefLocalizedString.h" +#include "nsIPrefService.h" +#include "nsIServiceManager.h" +#include "nsISupportsPrimitives.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsIProperties.h" +#include "nsServiceManagerUtils.h" +#include "nsThunderbirdProfileMigrator.h" +#include "prprf.h" + +/////////////////////////////////////////////////////////////////////////////// +// nsThunderbirdProfileMigrator + +#define FILE_NAME_SITEPERM_OLD "cookperm.txt" +#define FILE_NAME_SITEPERM_NEW "hostperm.1" +#define FILE_NAME_CERT8DB "cert8.db" +#define FILE_NAME_KEY3DB "key3.db" +#define FILE_NAME_SECMODDB "secmod.db" +#define FILE_NAME_HISTORY "history.dat" +#define FILE_NAME_SIGNONS "signons.sqlite" +#define FILE_NAME_MIMETYPES "mimeTypes.rdf" +#define FILE_NAME_USER_PREFS "user.js" +#define FILE_NAME_PERSONALDICTIONARY "persdict.dat" +#define FILE_NAME_MAILVIEWS "mailViews.dat" + +NS_IMPL_ISUPPORTS(nsThunderbirdProfileMigrator, nsISuiteProfileMigrator, + nsITimerCallback) + +nsThunderbirdProfileMigrator::nsThunderbirdProfileMigrator() +{ +} + +nsThunderbirdProfileMigrator::~nsThunderbirdProfileMigrator() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +// nsISuiteProfileMigrator + +NS_IMETHODIMP +nsThunderbirdProfileMigrator::Migrate(uint16_t aItems, + nsIProfileStartup* aStartup, + const char16_t* aProfile) +{ + nsresult rv = NS_OK; + bool aReplace = aStartup ? true : false; + + if (!mTargetProfile) { + GetProfilePath(aStartup, getter_AddRefs(mTargetProfile)); + if (!mTargetProfile) + return NS_ERROR_FILE_NOT_FOUND; + } + if (!mSourceProfile) { + GetSourceProfile(aProfile); + if (!mSourceProfile) + return NS_ERROR_FILE_NOT_FOUND; + } + + NOTIFY_OBSERVERS(MIGRATION_STARTED, nullptr); + + COPY_DATA(CopyPreferences, aReplace, nsISuiteProfileMigrator::SETTINGS); + COPY_DATA(CopyHistory, aReplace, nsISuiteProfileMigrator::HISTORY); + COPY_DATA(CopyPasswords, aReplace, nsISuiteProfileMigrator::PASSWORDS); + + // fake notifications for things we've already imported as part of + // CopyPreferences + nsAutoString index; + index.AppendInt(nsISuiteProfileMigrator::ACCOUNT_SETTINGS); + NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); + NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); + + index.Truncate(); + index.AppendInt(nsISuiteProfileMigrator::NEWSDATA); + NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); + NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); + + // copy junk mail training file + COPY_DATA(CopyJunkTraining, aReplace, nsISuiteProfileMigrator::JUNKTRAINING); + + if (aReplace && + (aItems & nsISuiteProfileMigrator::SETTINGS || + aItems & nsISuiteProfileMigrator::PASSWORDS || + !aItems)) { + // Permissions (Images) + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_SITEPERM_NEW, FILE_NAME_SITEPERM_NEW); + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_SITEPERM_OLD, FILE_NAME_SITEPERM_OLD); + } + + // 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. + CopyMailFolders(); + + return rv; +} + +NS_IMETHODIMP +nsThunderbirdProfileMigrator::GetMigrateData(const char16_t* aProfile, + bool aReplace, + uint16_t* aResult) +{ + *aResult = 0; + + if (!mSourceProfile) { + GetSourceProfile(aProfile); + if (!mSourceProfile) + return NS_ERROR_FILE_NOT_FOUND; + } + + // migration fields for things we always migrate + *aResult = + nsISuiteProfileMigrator::ACCOUNT_SETTINGS | + nsISuiteProfileMigrator::MAILDATA | + nsISuiteProfileMigrator::NEWSDATA | + nsISuiteProfileMigrator::ADDRESSBOOK_DATA; + + MigrationData data[] = { { FILE_NAME_PREFS, + nsISuiteProfileMigrator::SETTINGS, + true }, + { FILE_NAME_USER_PREFS, + nsISuiteProfileMigrator::SETTINGS, + true }, + { FILE_NAME_HISTORY, + nsISuiteProfileMigrator::HISTORY, + true }, + { FILE_NAME_SIGNONS, + nsISuiteProfileMigrator::PASSWORDS, + true }, + { FILE_NAME_JUNKTRAINING, + nsISuiteProfileMigrator::JUNKTRAINING, + true } }; + + GetMigrateDataFromArray(data, sizeof(data)/sizeof(MigrationData), + aReplace, mSourceProfile, aResult); + + return NS_OK; +} + +NS_IMETHODIMP +nsThunderbirdProfileMigrator::GetSupportedItems(uint16_t *aSupportedItems) +{ + NS_ENSURE_ARG_POINTER(aSupportedItems); + + *aSupportedItems = nsISuiteProfileMigrator::SETTINGS | + nsISuiteProfileMigrator::HISTORY | + nsISuiteProfileMigrator::JUNKTRAINING | + nsISuiteProfileMigrator::PASSWORDS | + nsISuiteProfileMigrator::ACCOUNT_SETTINGS | + nsISuiteProfileMigrator::MAILDATA | + nsISuiteProfileMigrator::NEWSDATA | + nsISuiteProfileMigrator::ADDRESSBOOK_DATA; + + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// nsThunderbirdProfileMigrator + +nsresult +nsThunderbirdProfileMigrator::FillProfileDataFromRegistry() +{ + // Find the Thunderbird Registry + nsCOMPtr<nsIProperties> fileLocator( + do_GetService("@mozilla.org/file/directory_service;1")); + nsCOMPtr<nsIFile> thunderbirdData; +#ifdef XP_WIN + fileLocator->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsIFile), + getter_AddRefs(thunderbirdData)); + + thunderbirdData->Append(u"Thunderbird"_ns); + +#elif defined(XP_MACOSX) + fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsIFile), + getter_AddRefs(thunderbirdData)); + + thunderbirdData->Append(u"Thunderbird"_ns); + +#elif defined(XP_UNIX) + fileLocator->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsIFile), + getter_AddRefs(thunderbirdData)); + + thunderbirdData->Append(u".thunderbird"_ns); + +#else + // On other OS just abort + return NS_ERROR_FILE_NOT_FOUND; +#endif + + // Try profiles.ini first + return GetProfileDataFromProfilesIni(thunderbirdData, + mProfileNames, + mProfileLocations); +} + +static +nsThunderbirdProfileMigrator::PrefTransform gTransforms[] = { + MAKESAMETYPEPREFTRANSFORM("accessibility.typeaheadfind.autostart", Bool), + MAKESAMETYPEPREFTRANSFORM("accessibility.typeaheadfind.linksonly", Bool), + + MAKESAMETYPEPREFTRANSFORM("browser.anchor_color", String), + MAKESAMETYPEPREFTRANSFORM("browser.active_color", String), + MAKESAMETYPEPREFTRANSFORM("browser.display.background_color", String), + MAKESAMETYPEPREFTRANSFORM("browser.display.foreground_color", String), + MAKESAMETYPEPREFTRANSFORM("browser.display.use_system_colors", Bool), + MAKESAMETYPEPREFTRANSFORM("browser.display.document_color_use", Int), + MAKESAMETYPEPREFTRANSFORM("browser.display.use_document_fonts", Bool), + MAKESAMETYPEPREFTRANSFORM("browser.enable_automatic_image_resizing", Bool), + MAKESAMETYPEPREFTRANSFORM("browser.tabs.autoHide", Bool), + MAKESAMETYPEPREFTRANSFORM("browser.tabs.loadInBackground", Bool), + MAKESAMETYPEPREFTRANSFORM("browser.underline_anchors", Bool), + MAKESAMETYPEPREFTRANSFORM("browser.visited_color", String), + + MAKESAMETYPEPREFTRANSFORM("dom.disable_open_during_load", Bool), + MAKESAMETYPEPREFTRANSFORM("dom.disable_window_move_resize", Bool), + MAKESAMETYPEPREFTRANSFORM("dom.disable_window_flip", Bool), + MAKESAMETYPEPREFTRANSFORM("dom.disable_window_open_feature.status", Bool), + MAKESAMETYPEPREFTRANSFORM("dom.disable_window_status_change", Bool), + + MAKESAMETYPEPREFTRANSFORM("extensions.spellcheck.inline.max-misspellings",Int), + + MAKESAMETYPEPREFTRANSFORM("general.warnOnAboutConfig", Bool), + + MAKESAMETYPEPREFTRANSFORM("intl.accept_charsets", String), + MAKESAMETYPEPREFTRANSFORM("intl.accept_languages", String), + MAKESAMETYPEPREFTRANSFORM("intl.charset.fallback.override", String), + + MAKESAMETYPEPREFTRANSFORM("javascript.enabled", Bool), + MAKESAMETYPEPREFTRANSFORM("javascript.options.relimit", Bool), + MAKESAMETYPEPREFTRANSFORM("javascript.options.showInConsole", Bool), + MAKESAMETYPEPREFTRANSFORM("javascript.options.strict", Bool), + + MAKESAMETYPEPREFTRANSFORM("layout.spellcheckDefault", Int), + + MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.accounts", String), + MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.defaultaccount", String), + MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.localfoldersserver", String), + MAKESAMETYPEPREFTRANSFORM("mail.accountwizard.deferstorage", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.adaptivefilters.junk_threshold", Int), + MAKESAMETYPEPREFTRANSFORM("mail.autoComplete.highlightNonMatches", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.biff.animate_doc_icon", 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("mail.biff.show_tray_icon", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.check_all_imap_folders_for_new", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.citation_color", String), + MAKESAMETYPEPREFTRANSFORM("mail.collect_addressbook", String), + MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_incoming", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_newsgroup", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_outgoing", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.compose.add_undisclosed_recipients", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.compose.autosave", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.compose.autosaveinterval", Int), + MAKESAMETYPEPREFTRANSFORM("mail.compose.dontWarnMail2Newsgroup", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.compose.other.header", String), + MAKESAMETYPEPREFTRANSFORM("mail.content_disposition_type", Int), + + MAKESAMETYPEPREFTRANSFORM("mail.default_html_action", Int), + MAKESAMETYPEPREFTRANSFORM("mail.default_sendlater_uri", String), + MAKESAMETYPEPREFTRANSFORM("mail.delete_matches_sort_order", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.display_glyph", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.display_struct", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.enable_autocomplete", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.fcc_folder", String), + MAKESAMETYPEPREFTRANSFORM("mail.file_attach_binary", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.fixed_width_messages", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.forward_message_mode", Int), + + MAKESAMETYPEPREFTRANSFORM("mail.incorporate.return_receipt", Int), + MAKESAMETYPEPREFTRANSFORM("mail.inline_attachments", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.label_ascii_only_mail_as_us_ascii", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.notification.sound", String), + + MAKESAMETYPEPREFTRANSFORM("mail.pane_config.dynamic", Int), + MAKESAMETYPEPREFTRANSFORM("mail.password_protect_local_cache", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.phishing.detection.enabled", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.pop3.deleteFromServerOnMove", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.prompt_purge_threshhold", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.purge_threshhold", Int), + MAKESAMETYPEPREFTRANSFORM("mail.purge.ask", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.purge.min_delay", Int), + MAKESAMETYPEPREFTRANSFORM("mail.purge.timer_interval", Int), + + MAKESAMETYPEPREFTRANSFORM("mail.quoteasblock", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.quoted_graphical", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.quoted_size", Int), + MAKESAMETYPEPREFTRANSFORM("mail.quoted_style", Int), + + MAKESAMETYPEPREFTRANSFORM("mail.receipt.request_header_type", Int), + MAKESAMETYPEPREFTRANSFORM("mail.receipt.request_return_receipt_on", Bool), + + MAKESAMETYPEPREFTRANSFORM("mail.send_struct", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.show_headers", Int), + MAKESAMETYPEPREFTRANSFORM("mail.smtp.useMatchingDomainServer", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.smtp.useMatchingHostNameServer", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.smtp.defaultserver", String), + MAKESAMETYPEPREFTRANSFORM("mail.smtpservers", String), + MAKESAMETYPEPREFTRANSFORM("mail.spellcheck.inline", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.SpellCheckBeforeSend", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.startup.enabledMailCheckOnce", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.strict_threading", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.strictly_mime", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.strictly_mime_headers", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.strictly_mime.parm_folding", Bool), + + MAKESAMETYPEPREFTRANSFORM("mail.thread_without_re", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.trusteddomains", String), + MAKESAMETYPEPREFTRANSFORM("mail.warn_on_send_accel_key", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.warn_filter_changed", Bool), + MAKESAMETYPEPREFTRANSFORM("mail.wrap_long_lines", Bool), + + MAKESAMETYPEPREFTRANSFORM("mailnews.account_central_page.url", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.confirm.moveFoldersToTrash", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.customDBHeaders", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.customHeaders", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.default_sort_order", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.default_sort_type", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.default_news_sort_order", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.default_news_sort_type", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.display.disable_format_flowed_support", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.display.disallow_mime_handlers", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.display.html_as", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.display_html_sanitzer.allowed_tags", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.display.original_date", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.display.prefer_plaintext", Bool), + + MAKESAMETYPEPREFTRANSFORM("mailnews.force_ascii_search", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.force_charset_override", Bool), + + MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showOrganization", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showUserAgent", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.headers.extraExpandedHeaders", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.html_domains", String), + + MAKESAMETYPEPREFTRANSFORM("mailnews.mark_message_read.delay", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.mark_message_read.delay.interval", Int), + + MAKESAMETYPEPREFTRANSFORM("mailnews.message_display.disable_remote_image", Bool), + + MAKESAMETYPEPREFTRANSFORM("mailnews.nav_crosses_folders", Int), + + MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sync_mail", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sych_news", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sync_send_unsent", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sync_work_offline", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.open_window_warning", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.plaintext_domains", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.remember_selected_message", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.reply_in_default_charset", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.reuse_message_window", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.scroll_to_new_message", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.search_date_format", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.search_date_leading_zeros", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.search_date_separator", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.send_default_charset", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.send_plaintext_flowed", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.show_send_progress", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.start_page.enabled", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.start_page.url", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.tcptimeout", Int), + MAKESAMETYPEPREFTRANSFORM("mailnews.thread_pane_column_unthreads", Bool), + MAKESAMETYPEPREFTRANSFORM("mailnews.view_default_charset", String), + MAKESAMETYPEPREFTRANSFORM("mailnews.wraplength", Int), + + MAKESAMETYPEPREFTRANSFORM("messenger.save.dir", String), + + MAKESAMETYPEPREFTRANSFORM("msgcompose.background_color", String), + MAKESAMETYPEPREFTRANSFORM("msgcompose.font_face", String), + MAKESAMETYPEPREFTRANSFORM("msgcompose.font_size", String), + MAKESAMETYPEPREFTRANSFORM("msgcompose.text_color", String), + + MAKESAMETYPEPREFTRANSFORM("news.get_messages_on_select", Bool), + MAKESAMETYPEPREFTRANSFORM("news.show_size_in_lines", Bool), + MAKESAMETYPEPREFTRANSFORM("news.update_unread_on_expand", Bool), + + // pdi is the new preference, but nii is the old one - so do nii first, and + // then do pdi to account for both situations + MAKEPREFTRANSFORM("network.image.imageBehavior", 0, Int, Image), + MAKESAMETYPEPREFTRANSFORM("permissions.default.image", Int), + + MAKESAMETYPEPREFTRANSFORM("network.proxy.autoconfig_url", String), + MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp", String), + MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp_port", Int), + MAKESAMETYPEPREFTRANSFORM("network.proxy.http", String), + MAKESAMETYPEPREFTRANSFORM("network.proxy.http_port", Int), + MAKESAMETYPEPREFTRANSFORM("network.proxy.no_proxies_on", String), + MAKESAMETYPEPREFTRANSFORM("network.proxy.socks", String), + MAKESAMETYPEPREFTRANSFORM("network.proxy.socks_port", Int), + MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl", String), + MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl_port", Int), + MAKESAMETYPEPREFTRANSFORM("network.proxy.type", Int), + + MAKESAMETYPEPREFTRANSFORM("offline.autodetect", Bool), + MAKESAMETYPEPREFTRANSFORM("offline.download.download_messages", Int), + MAKESAMETYPEPREFTRANSFORM("offline.send.unsent_messages", Int), + MAKESAMETYPEPREFTRANSFORM("offline.startup_state", Int), + MAKESAMETYPEPREFTRANSFORM("security.default_personal_cert", String), + MAKESAMETYPEPREFTRANSFORM("security.password_lifetime", Int), + MAKESAMETYPEPREFTRANSFORM("security.tls.version.min", Int), + MAKESAMETYPEPREFTRANSFORM("security.tls.version.max", Int), + MAKESAMETYPEPREFTRANSFORM("security.warn_entering_secure", Bool), + MAKESAMETYPEPREFTRANSFORM("security.warn_leaving_secure", Bool), + MAKESAMETYPEPREFTRANSFORM("security.warn_submit_insecure", Bool), + MAKESAMETYPEPREFTRANSFORM("security.warn_viewing_mixed", Bool), + + MAKESAMETYPEPREFTRANSFORM("signon.rememberSignons", Bool), + + MAKESAMETYPEPREFTRANSFORM("slider.snapMultiplier", Int), + MAKESAMETYPEPREFTRANSFORM("startup.homepage_override_url", String), + +#ifdef XP_UNIX + MAKESAMETYPEPREFTRANSFORM("ui.allow_platform_file_picker", Bool), +#endif + MAKESAMETYPEPREFTRANSFORM("ui.click_hold_context_menus", Bool) +}; + +nsresult +nsThunderbirdProfileMigrator::TransformPreferences( + const char* aSourcePrefFileName, + const char* 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->AppendNative(nsDependentCString(aSourcePrefFileName)); + psvc->ReadUserPrefsFromFile(sourcePrefsFile); + + nsCOMPtr<nsIPrefBranch> 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. + static const char* branchNames[] = + { + // Keep the three below first, or change the indexes below + "mail.identity.", + "mail.server.", + "ldap_2.", + "accessibility.", + "applications.", + "bidi.", + "dom.", + "editor.", + "font.", + "helpers.", + "mail.account.", + "mail.addr_book.", + "mail.imap.", + "mail.mdn.", + "mail.smtpserver.", + "mail.spam.", + "mail.toolbars.", + "mailnews.labels.", + "mailnews.reply_", + "mailnews.tags.", + "middlemouse.", + "mousewheel.", + "network.http.", + "print.", + "privacy.", + "security.OSCP.", + "security.crl.", + "ui.key.", + "wallet." + }; + + 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 thunderbird 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 + CopyMailFolderPrefs(branches[1], psvc); + + CopyAddressBookDirectories(branches[2], psvc); + + // Now that we have all the pref data in memory, load the target pref file, + // and write it back out + psvc->ResetPrefs(); + + nsCOMPtr<nsIFile> targetPrefsFile; + mTargetProfile->Clone(getter_AddRefs(targetPrefsFile)); + targetPrefsFile->AppendNative(nsDependentCString(aTargetPrefFileName)); + + // Don't use nullptr here as we're too early in the cycle for the prefs + // service to get its default file (because the NS_GetDirectoryService items + // aren't fully set up yet). + psvc->ReadUserPrefsFromFile(targetPrefsFile); + + 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]); + + psvc->SavePrefFile(targetPrefsFile); + + return NS_OK; +} + +nsresult +nsThunderbirdProfileMigrator::CopyPreferences(bool aReplace) +{ + nsresult rv = NS_OK; + if (!aReplace) + return rv; + + if (NS_SUCCEEDED(rv)) + rv = TransformPreferences(FILE_NAME_PREFS, FILE_NAME_PREFS); + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_USER_PREFS, FILE_NAME_USER_PREFS); + + // Security Stuff + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_CERT8DB, FILE_NAME_CERT8DB); + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_KEY3DB, FILE_NAME_KEY3DB); + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_SECMODDB, FILE_NAME_SECMODDB); + + // User MIME Type overrides + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_MIMETYPES, FILE_NAME_MIMETYPES); + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_PERSONALDICTIONARY, FILE_NAME_PERSONALDICTIONARY); + if (NS_SUCCEEDED(rv)) + rv = CopyFile(FILE_NAME_MAILVIEWS, FILE_NAME_MAILVIEWS); + + return rv; +} + +nsresult +nsThunderbirdProfileMigrator::CopyHistory(bool aReplace) +{ + return aReplace ? CopyFile(FILE_NAME_HISTORY, FILE_NAME_HISTORY) : NS_OK; +} + +nsresult +nsThunderbirdProfileMigrator::CopyPasswords(bool aReplace) +{ + return aReplace ? CopyFile(FILE_NAME_SIGNONS, FILE_NAME_SIGNONS) : NS_OK; +} diff --git a/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.h b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.h new file mode 100644 index 0000000000..2b3bbf87ec --- /dev/null +++ b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.h @@ -0,0 +1,44 @@ +/* -*- 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/. */ + +#ifndef ThunderbirdProfileMigrator_h__ +#define ThunderbirdProfileMigrator_h__ + +#include "nsISuiteProfileMigrator.h" +#include "nsIFile.h" +#include "nsIObserverService.h" +#include "nsSuiteProfileMigratorBase.h" +#include "nsITimer.h" +#include "mozilla/Attributes.h" + +class nsIFile; +class nsIPrefBranch; +class nsIPrefService; + +class nsThunderbirdProfileMigrator final : public nsSuiteProfileMigratorBase +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + nsThunderbirdProfileMigrator(); + + // nsISuiteProfileMigrator methods + NS_IMETHOD Migrate(uint16_t aItems, nsIProfileStartup *aStartup, + const char16_t *aProfile) override; + NS_IMETHOD GetMigrateData(const char16_t *aProfile, bool aDoingStartup, + uint16_t *_retval) override; + NS_IMETHOD GetSupportedItems(uint16_t *aSupportedItems) override; + +protected: + virtual ~nsThunderbirdProfileMigrator(); + nsresult FillProfileDataFromRegistry() override; + nsresult CopyPreferences(bool aReplace); + nsresult TransformPreferences(const char* aSourcePrefFileName, + const char* aTargetPrefFileName); + nsresult CopyHistory(bool aReplace); + nsresult CopyPasswords(bool aReplace); +}; + +#endif |