Bug 1581765 - Migrate existing Mork address books to JS provider; r=mkmelin
authorGeoff Lankow <geoff@darktrojan.net>
Mon, 07 Oct 2019 20:31:06 +1300
changeset 37099 faabd8eebf18b69aae2665033582c6ff07a80bf1
parent 37098 dcd12e30d6d85bc3f91e0732f01396b28cb8c882
child 37100 d8233c63b15f53d0810a4be5415b7897038fe82f
push id395
push userclokep@gmail.com
push dateMon, 02 Dec 2019 19:38:57 +0000
reviewersmkmelin
bugs1581765
Bug 1581765 - Migrate existing Mork address books to JS provider; r=mkmelin
mail/base/modules/MailMigrator.jsm
mailnews/addrbook/jsaddrbook/AddrBookDirectory.jsm
--- a/mail/base/modules/MailMigrator.jsm
+++ b/mail/base/modules/MailMigrator.jsm
@@ -6,16 +6,21 @@
 /**
  * This module handles migrating mail-specific preferences, etc. Migration has
  * traditionally been a part of msgMail3PaneWindow.js, but separating the code
  * out into a module makes unit testing much easier.
  */
 
 var EXPORTED_SYMBOLS = ["MailMigrator"];
 
+ChromeUtils.defineModuleGetter(
+  this,
+  "AddrBookDirectory",
+  "resource:///modules/AddrBookDirectory.jsm"
+);
 const { MailServices } = ChromeUtils.import(
   "resource:///modules/MailServices.jsm"
 );
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { IOUtils } = ChromeUtils.import("resource:///modules/IOUtils.js");
 
 var MailMigrator = {
   /**
@@ -670,39 +675,141 @@ var MailMigrator = {
    * Perform any migration work that needs to occur after the Account Wizard
    * has had a chance to appear.
    */
   migratePostAccountWizard() {
     this.migrateToClearTypeFonts();
   },
 
   /**
-   * Migrate address books away from Mork. In time this will do actual
-   * migration, but for now, just set the default pref back to what it was.
+   * Migrate address books from Mork to JS/SQLite. This must happen before
+   * address book start-up and without causing it.
+   *
+   * All Mork address books found in the prefs are converted to JS/SQLite
+   * address books and the prefs updated. Migrated Mork files in the profile
+   * are renamed with the extension ".mab.bak" to avoid confusion.
    */
   _migrateAddressBooks() {
-    let pab = Services.dirsvc.get("ProfD", Ci.nsIFile);
-    pab.append("abook.mab");
-    if (pab.exists()) {
-      let defaultBranch = Services.prefs.getDefaultBranch("");
-      defaultBranch.setIntPref("ldap_2.servers.pab.dirType", 2);
-      defaultBranch.setStringPref("ldap_2.servers.pab.filename", "abook.mab");
-      defaultBranch.setIntPref("ldap_2.servers.history.dirType", 2);
-      defaultBranch.setStringPref(
-        "ldap_2.servers.history.filename",
-        "history.mab"
-      );
-      defaultBranch.setStringPref(
-        "mail.collect_addressbook",
-        "moz-abmdbdirectory://history.mab"
-      );
-      defaultBranch.setStringPref(
-        "mail.server.default.whiteListAbURI",
-        "moz-abmdbdirectory://abook.mab"
-      );
+    function migrateBook(fileName, notFoundThrows = true) {
+      let oldFile = profileDir.clone();
+      oldFile.append(`${fileName}.mab`);
+      if (!oldFile.exists()) {
+        if (notFoundThrows) {
+          throw Cr.NS_ERROR_NOT_AVAILABLE;
+        }
+        return;
+      }
+
+      console.log(`Creating new ${fileName}.sqlite`);
+      let newBook = new AddrBookDirectory();
+      newBook.init(`jsaddrbook://${fileName}.sqlite`);
+
+      let database = Cc[
+        "@mozilla.org/addressbook/carddatabase;1"
+      ].createInstance(Ci.nsIAddrDatabase);
+      database.dbPath = oldFile;
+      database.openMDB(oldFile, false);
+
+      let directory = Cc[
+        "@mozilla.org/addressbook/directory;1?type=moz-abmdbdirectory"
+      ].createInstance(Ci.nsIAbMDBDirectory);
+
+      let cardMap = new Map();
+      for (let card of database.enumerateCards(directory)) {
+        if (!card.isMailList) {
+          newBook.addCard(card);
+          cardMap.set(card.localId, card);
+        }
+      }
+
+      for (let card of database.enumerateCards(directory)) {
+        if (card.isMailList) {
+          let mailList = Cc[
+            "@mozilla.org/addressbook/directoryproperty;1"
+          ].createInstance(Ci.nsIAbDirectory);
+          mailList.isMailList = true;
+          mailList.dirName = card.displayName;
+          mailList.listNickName = card.getProperty("NickName", "");
+          mailList.description = card.getProperty("Notes", "");
+          mailList = newBook.addMailList(mailList);
+
+          directory.dbRowID = card.localId;
+          for (let listCard of database.enumerateListAddresses(directory)) {
+            mailList.addCard(
+              cardMap.get(listCard.QueryInterface(Ci.nsIAbCard).localId)
+            );
+          }
+        }
+      }
+
+      database.closeMDB(false);
+      database.forceClosed();
+
+      console.log(`Renaming ${fileName}.mab to ${fileName}.mab.bak`);
+      oldFile.renameTo(profileDir, `${fileName}.mab.bak`);
+    }
+
+    let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+    for (let name of Services.prefs.getChildList("ldap_2.servers.")) {
+      try {
+        if (
+          !name.endsWith(".dirType") ||
+          Services.prefs.getIntPref(name) != 2
+        ) {
+          continue;
+        }
+
+        let prefName = name.substring(0, name.length - 8);
+        let fileName = Services.prefs.getStringPref(`${prefName}.filename`);
+        fileName = fileName.replace(/\.mab$/, "");
+
+        Services.prefs.setIntPref(`${prefName}.dirType`, 101);
+        Services.prefs.setStringPref(
+          `${prefName}.filename`,
+          `${fileName}.sqlite`
+        );
+        if (Services.prefs.prefHasUserValue(`${prefName}.uri`)) {
+          Services.prefs.setStringPref(
+            `${prefName}.uri`,
+            `jsaddrbook://${fileName}.sqlite`
+          );
+        }
+        migrateBook(fileName);
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }
+
+    try {
+      migrateBook("abook", false);
+    } catch (ex) {
+      Cu.reportError(ex);
+    }
+    try {
+      migrateBook("history", false);
+    } catch (ex) {
+      Cu.reportError(ex);
+    }
+
+    for (let prefName of [
+      "mail.collect_addressbook",
+      "mail.server.default.whiteListAbURI",
+    ]) {
+      try {
+        if (Services.prefs.prefHasUserValue(prefName)) {
+          let uri = Services.prefs.getStringPref(prefName);
+          uri = uri.replace(
+            /^moz-abmdbdirectory:\/\/(.*).mab$/,
+            "jsaddrbook://$1.sqlite"
+          );
+          Services.prefs.setStringPref(prefName, uri);
+        }
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
     }
   },
 
   /**
    * Perform any migration work that needs to occur once the user profile has
    * been loaded.
    */
   migrateAtProfileStartup() {
--- a/mailnews/addrbook/jsaddrbook/AddrBookDirectory.jsm
+++ b/mailnews/addrbook/jsaddrbook/AddrBookDirectory.jsm
@@ -662,16 +662,29 @@ var bookPrototype = {
       "INSERT INTO cards (uid, localId) VALUES (:uid, :localId)"
     );
     insertStatement.params.uid = newCard.UID;
     insertStatement.params.localId = newCard.localId;
     insertStatement.execute();
     insertStatement.finalize();
 
     for (let { name, value } of fixIterator(card.properties, Ci.nsIProperty)) {
+      if (
+        [
+          "DbRowID",
+          "LowercasePrimaryEmail",
+          "LowercaseSecondEmail",
+          "RecordKey",
+          "UID",
+        ].includes(name)
+      ) {
+        // These properties are either stored elsewhere (DbRowID, UID), or no
+        // longer needed. Don't store them.
+        continue;
+      }
       newCard.setProperty(name, value);
     }
     this._saveCardProperties(newCard);
 
     MailServices.ab.notifyDirectoryItemAdded(this, newCard);
     Services.obs.notifyObservers(newCard, "addrbook-contact-created", this.UID);
 
     return newCard;