summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/content/calendar-migration.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/calendar/base/content/calendar-migration.js')
-rw-r--r--comm/calendar/base/content/calendar-migration.js323
1 files changed, 323 insertions, 0 deletions
diff --git a/comm/calendar/base/content/calendar-migration.js b/comm/calendar/base/content/calendar-migration.js
new file mode 100644
index 0000000000..e5a5a1add2
--- /dev/null
+++ b/comm/calendar/base/content/calendar-migration.js
@@ -0,0 +1,323 @@
+/* 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/. */
+
+/* globals putItemsIntoCal*/
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+var { AppConstants } = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs");
+
+/**
+ * A data migrator prototype, holding the information for migration
+ *
+ * @class
+ * @param aTitle The title of the migrator
+ * @param aMigrateFunction The function to call when migrating
+ * @param aArguments The arguments to pass in.
+ */
+function dataMigrator(aTitle, aMigrateFunction, aArguments) {
+ this.title = aTitle;
+ this.migrate = aMigrateFunction;
+ this.args = aArguments || [];
+}
+
+var gDataMigrator = {
+ /**
+ * Call to do a general data migration (for a clean profile) Will run
+ * through all of the known migrator-checkers. These checkers will return
+ * an array of valid dataMigrator objects, for each kind of data they find.
+ * If there is at least one valid migrator, we'll pop open the migration
+ * wizard, otherwise, we'll return silently.
+ */
+ checkAndMigrate() {
+ let DMs = [];
+ let migrators = [this.checkEvolution, this.checkWindowsMail, this.checkIcal];
+ for (let migrator of migrators) {
+ let migs = migrator.call(this);
+ for (let mig of migs) {
+ DMs.push(mig);
+ }
+ }
+
+ if (DMs.length == 0) {
+ // No migration available
+ return;
+ }
+
+ let url = "chrome://calendar/content/calendar-migration-dialog.xhtml";
+ if (AppConstants.platform == "macosx") {
+ let win = Services.wm.getMostRecentWindow("Calendar:MigrationWizard");
+ if (win) {
+ win.focus();
+ } else {
+ openDialog(url, "migration", "centerscreen,chrome,resizable=no,width=500,height=400", DMs);
+ }
+ } else {
+ openDialog(
+ url,
+ "migration",
+ "modal,centerscreen,chrome,resizable=no,width=500,height=400",
+ DMs
+ );
+ }
+ },
+
+ /**
+ * Checks to see if Apple's iCal is installed and offers to migrate any data
+ * the user has created in it.
+ */
+ checkIcal() {
+ function icalMigrate(aDataDir, aCallback) {
+ aDataDir.append("Sources");
+
+ let i = 1;
+ for (let dataDir of aDataDir.directoryEntries) {
+ let dataStore = dataDir.clone();
+ dataStore.append("corestorage.ics");
+ if (!dataStore.exists()) {
+ continue;
+ }
+
+ let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+
+ fileStream.init(dataStore, 0x01, parseInt("0444", 8), {});
+ let convIStream = Cc["@mozilla.org/intl/converter-input-stream;1"].getService(
+ Ci.nsIConverterInputStream
+ );
+ convIStream.init(fileStream, "UTF-8", 0, 0x0000);
+ let tmpStr = {};
+ let str = "";
+ while (convIStream.readString(-1, tmpStr)) {
+ str += tmpStr.value;
+ }
+
+ // Strip out the timezone definitions, since it makes the file
+ // invalid otherwise
+ let index = str.indexOf(";TZID=");
+ while (index != -1) {
+ let endIndex = str.indexOf(":", index);
+ let otherEnd = str.indexOf(";", index + 2);
+ if (otherEnd < endIndex) {
+ endIndex = otherEnd;
+ }
+ let sub = str.substring(index, endIndex);
+ str = str.split(sub).join("");
+ index = str.indexOf(";TZID=");
+ }
+ let tempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ tempFile.append("icalTemp.ics");
+ tempFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0600", 8));
+
+ let stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ stream.init(tempFile, 0x2a, parseInt("0600", 8), 0);
+ let convOStream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(
+ Ci.nsIConverterOutputStream
+ );
+ convOStream.init(stream, "UTF-8");
+ convOStream.writeString(str);
+
+ let calendar = gDataMigrator.importICSToStorage(tempFile);
+ calendar.name = "iCalendar" + i;
+ i++;
+ cal.manager.registerCalendar(calendar);
+ cal.view.getCompositeCalendar(window).addCalendar(calendar);
+ }
+ console.debug("icalMig making callback");
+ aCallback();
+ }
+
+ console.debug("Checking for ical data");
+ let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ let icalSpec = profileDir.path;
+ let diverge = icalSpec.indexOf("Thunderbird");
+ if (diverge == -1) {
+ return [];
+ }
+ icalSpec = icalSpec.substr(0, diverge);
+ let icalFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ icalFile.initWithPath(icalSpec);
+ icalFile.append("Application Support");
+
+ icalFile.append("iCal");
+ if (icalFile.exists()) {
+ return [new dataMigrator("Apple iCal", icalMigrate, [icalFile])];
+ }
+
+ return [];
+ },
+
+ /**
+ * Checks to see if Evolution is installed and offers to migrate any data
+ * stored there.
+ */
+ checkEvolution() {
+ function evoMigrate(aDataDir, aCallback) {
+ let i = 1;
+ let evoDataMigrate = function (dataStore) {
+ console.debug("Migrating evolution data file in " + dataStore.path);
+ if (dataStore.exists()) {
+ let calendar = gDataMigrator.importICSToStorage(dataStore);
+ calendar.name = "Evolution " + i++;
+ cal.manager.registerCalendar(calendar);
+ cal.view.getCompositeCalendar(window).addCalendar(calendar);
+ }
+ return dataStore.exists();
+ };
+
+ for (let dataDir of aDataDir.directoryEntries) {
+ let dataStore = dataDir.clone();
+ dataStore.append("calendar.ics");
+ evoDataMigrate(dataStore);
+ }
+
+ aCallback();
+ }
+
+ let evoDir = Services.dirsvc.get("Home", Ci.nsIFile);
+ evoDir.append(".evolution");
+ evoDir.append("calendar");
+ evoDir.append("local");
+ return evoDir.exists() ? [new dataMigrator("Evolution", evoMigrate, [evoDir])] : [];
+ },
+
+ checkWindowsMail() {
+ function doMigrate(aCalendarNodes, aMailDir, aCallback) {
+ for (let node of aCalendarNodes) {
+ let name = node.getElementsByTagName("Name")[0].textContent;
+ let color = node.getElementsByTagName("Color")[0].textContent;
+ let enabled = node.getElementsByTagName("Enabled")[0].textContent == "True";
+
+ // The name is quoted, and the color also contains an alpha
+ // value. Lets just ignore the alpha value and take the
+ // color part.
+ name = name.replace(/(^'|'$)/g, "");
+ color = color.replace(/0x[0-9a-fA-F]{2}([0-9a-fA-F]{4})/, "#$1");
+
+ let calfile = aMailDir.clone();
+ calfile.append(name + ".ics");
+
+ if (calfile.exists()) {
+ let storage = gDataMigrator.importICSToStorage(calfile);
+ storage.name = name;
+
+ if (color) {
+ storage.setProperty("color", color);
+ }
+ cal.manager.registerCalendar(storage);
+
+ if (enabled) {
+ cal.view.getCompositeCalendar(window).addCalendar(storage);
+ }
+ }
+ }
+ aCallback();
+ }
+
+ if (!Services.dirsvc.has("LocalAppData")) {
+ // We are probably not on windows
+ return [];
+ }
+
+ let maildir = Services.dirsvc.get("LocalAppData", Ci.nsIFile);
+
+ maildir.append("Microsoft");
+ maildir.append("Windows Calendar");
+ maildir.append("Calendars");
+
+ let settingsxml = maildir.clone();
+ settingsxml.append("Settings.xml");
+
+ let migrators = [];
+ if (settingsxml.exists()) {
+ let settingsXmlUri = Services.io.newFileURI(settingsxml);
+
+ let req = new XMLHttpRequest();
+ req.open("GET", settingsXmlUri.spec, false);
+ req.send(null);
+ if (req.status == 0) {
+ // The file was found, it seems we are on windows vista.
+ let doc = req.responseXML;
+
+ // Get all calendar property tags and return the migrator.
+ let calendars = doc.getElementsByTagName("VCalendar");
+ if (calendars.length > 0) {
+ migrators = [
+ new dataMigrator("Windows Calendar", doMigrate.bind(null, calendars, maildir)),
+ ];
+ }
+ }
+ }
+ return migrators;
+ },
+
+ /**
+ * Creates and registers a storage calendar and imports the given ics file into it.
+ *
+ * @param icsFile The nsI(Local)File to import.
+ */
+ importICSToStorage(icsFile) {
+ const uri = "moz-storage-calendar://";
+ let calendar = cal.manager.createCalendar("storage", Services.io.newURI(uri));
+ let icsImporter = Cc["@mozilla.org/calendar/import;1?type=ics"].getService(Ci.calIImporter);
+
+ let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ let items = [];
+
+ calendar.id = cal.getUUID();
+
+ try {
+ const MODE_RDONLY = 0x01;
+ inputStream.init(icsFile, MODE_RDONLY, parseInt("0444", 8), {});
+ items = icsImporter.importFromStream(inputStream);
+ } catch (ex) {
+ switch (ex.result) {
+ case Ci.calIErrors.INVALID_TIMEZONE:
+ cal.showError(cal.l10n.getCalString("timezoneError", [icsFile.path]), window);
+ break;
+ default:
+ cal.showError(cal.l10n.getCalString("unableToRead") + icsFile.path + "\n" + ex, window);
+ }
+ } finally {
+ inputStream.close();
+ }
+
+ // Defined in import-export.js
+ putItemsIntoCal(calendar, items, {
+ duplicateCount: 0,
+ failedCount: 0,
+ lastError: null,
+
+ onDuplicate(item, error) {
+ this.duplicateCount++;
+ },
+ onError(item, error) {
+ this.failedCount++;
+ this.lastError = error;
+ },
+ onEnd() {
+ if (this.failedCount) {
+ cal.showError(
+ cal.l10n.getCalString("importItemsFailed", [
+ this.failedCount,
+ this.lastError.toString(),
+ ]),
+ window
+ );
+ } else if (this.duplicateCount) {
+ cal.showError(
+ cal.l10n.getCalString("duplicateError", [this.duplicateCount, icsFile.path]),
+ window
+ );
+ }
+ },
+ });
+
+ return calendar;
+ },
+};