[Pkg-mozext-maintainers] Bug#1082086: bookworm-pu: package eas4tbsync/4.11-1~deb12u1

Mechtilde Stehmann mechtilde at debian.org
Wed Sep 18 09:23:47 BST 2024


Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: eas4tbsync at packages.debian.org, mechtilde at debian.org
Control: affects -1 + src:eas4tbsync
User: release.debian.org at packages.debian.org
Usertags: pu

[ Reason ]
Thunderbird will come with a new version (>=128.2.x) into stable. This need an
update for the Add-Ons (here: eas4tbsync, which is a dependency of tbsync) too

[ Impact ]
If the update isn't approved the user can't anymore use the connection to an
exchange server.

[ Tests ]
The same upstream code works with thunderbird >= 128.2 in testing.

[ Risks ]
code is trivial so no risk

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]
The new version of thunderbird needs a new version of webext-tbsync and there
dependencies

[ Other info ]
The only reason is the new upcomming version of the thunderbird.
-------------- next part --------------
diffstat for eas4tbsync-4.8 eas4tbsync-4.11

 _locales/ja/messages.json                     |   22 
 background.js                                 |   24 
 content/api/BootstrapLoader/implementation.js |  742 ---------------
 content/bootstrap.js                          |    2 
 content/includes/calendarsync.js              |    1 
 content/includes/contactsync.js               |    3 
 content/includes/network.js                   | 1271 +++++++++++++-------------
 content/includes/sync.js                      |    1 
 content/includes/tasksync.js                  |    1 
 content/includes/tools.js                     |   10 
 content/includes/wbxmltools.js                |    3 
 content/includes/xmltools.js                  |    3 
 content/manager/createAccount.js              |   20 
 content/manager/createAccount.xhtml           |    2 
 content/provider.js                           |    3 
 debian/changelog                              |   21 
 debian/control                                |    5 
 manifest.json                                 |    6 
 18 files changed, 761 insertions(+), 1379 deletions(-)

diff -Nru eas4tbsync-4.8/background.js eas4tbsync-4.11/background.js
--- eas4tbsync-4.8/background.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/background.js	2024-08-19 20:22:14.000000000 +0200
@@ -1,26 +1,6 @@
-function isCompatible(version) {
-  let [ major, minor , patch ] = version.split(".").map(e => parseInt(e,10));
-  return (
-    major > 102 || 
-    (major == 102 && minor > 3) ||
-    (major == 102 && minor == 3 && patch > 2)
-  );
-}
-
 async function main() {
-  let { version } = await browser.runtime.getBrowserInfo();
-  if (isCompatible(version)) {
-    await messenger.BootstrapLoader.registerChromeUrl([ ["content", "eas4tbsync", "content/"] ]);
-    await messenger.BootstrapLoader.registerBootstrapScript("chrome://eas4tbsync/content/bootstrap.js");  
-  } else {
-    let manifest = browser.runtime.getManifest();
-    browser.notifications.create({
-      type: "basic",
-      iconUrl: browser.runtime.getURL("content/skin/eas32.png"),
-      title: `${manifest.name}`,
-      message: "Please update Thunderbird to at least 102.3.3 to be able to use this provider.",
-    });
-  }
+  await messenger.BootstrapLoader.registerChromeUrl([ ["content", "eas4tbsync", "content/"] ]);
+  await messenger.BootstrapLoader.registerBootstrapScript("chrome://eas4tbsync/content/bootstrap.js");  
 }
 
 main();
diff -Nru eas4tbsync-4.8/content/api/BootstrapLoader/implementation.js eas4tbsync-4.11/content/api/BootstrapLoader/implementation.js
--- eas4tbsync-4.8/content/api/BootstrapLoader/implementation.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/api/BootstrapLoader/implementation.js	2024-08-19 20:22:14.000000000 +0200
@@ -15,16 +15,6 @@
 var { ExtensionCommon } = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
 var { ExtensionSupport } = ChromeUtils.import("resource:///modules/ExtensionSupport.jsm");
 var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-function getThunderbirdVersion() {
-  let parts = Services.appinfo.version.split(".");
-  return {
-    major: parseInt(parts[0]),
-    minor: parseInt(parts[1]),
-    revision: parts.length > 2 ? parseInt(parts[2]) : 0,
-  }
-}
 
 function getMessenger(context) {
   let apis = ["storage", "runtime", "extension", "i18n"];
@@ -51,13 +41,13 @@
   for (let api of apis) {
     switch (api) {
       case "storage":
-        XPCOMUtils.defineLazyGetter(messenger, "storage", () =>
+        ChromeUtils.defineLazyGetter(messenger, "storage", () =>
           getStorage()
         );
         break;
 
       default:
-        XPCOMUtils.defineLazyGetter(messenger, api, () =>
+        ChromeUtils.defineLazyGetter(messenger, api, () =>
           context.apiCan.findAPIPath(api)
         );
     }
@@ -65,607 +55,16 @@
   return messenger;
 }
 
-var BootstrapLoader_102 = class extends ExtensionCommon.ExtensionAPI {
-  getCards(e) {
-    // This gets triggered by real events but also manually by providing the outer window.
-    // The event is attached to the outer browser, get the inner one.
-    let doc;
-
-    // 78,86, and 87+ need special handholding. *Yeah*.
-    if (getThunderbirdVersion().major < 86) {
-      let ownerDoc = e.document || e.target.ownerDocument;
-      doc = ownerDoc.getElementById("html-view-browser").contentDocument;
-    } else if (getThunderbirdVersion().major < 87) {
-      let ownerDoc = e.document || e.target;
-      doc = ownerDoc.getElementById("html-view-browser").contentDocument;
-    } else {
-      doc = e.document || e.target;
-    }
-    return doc.querySelectorAll("addon-card");
-  }
-
-  // Add pref entry to 68
-  add68PrefsEntry(event) {
-    let id = this.menu_addonPrefs_id + "_" + this.uniqueRandomID;
-
-    // Get the best size of the icon (16px or bigger)
-    let iconSizes = this.extension.manifest.icons
-      ? Object.keys(this.extension.manifest.icons)
-      : [];
-    iconSizes.sort((a, b) => a - b);
-    let bestSize = iconSizes.filter(e => parseInt(e) >= 16).shift();
-    let icon = bestSize ? this.extension.manifest.icons[bestSize] : "";
-
-    let name = this.extension.manifest.name;
-    let entry = icon
-      ? event.target.ownerGlobal.MozXULElement.parseXULToFragment(
-        `<menuitem class="menuitem-iconic" id="${id}" image="${icon}" label="${name}" />`)
-      : event.target.ownerGlobal.MozXULElement.parseXULToFragment(
-        `<menuitem id="${id}" label="${name}" />`);
-
-    event.target.appendChild(entry);
-    let noPrefsElem = event.target.querySelector('[disabled="true"]');
-    // using collapse could be undone by core, so we use display none
-    // noPrefsElem.setAttribute("collapsed", "true");
-    noPrefsElem.style.display = "none";
-    event.target.ownerGlobal.document.getElementById(id).addEventListener("command", this);
-  }
-
-  // Event handler for the addon manager, to update the state of the options button.
-  handleEvent(e) {
-    switch (e.type) {
-      // 68 add-on options menu showing
-      case "popupshowing": {
-        this.add68PrefsEntry(e);
-      }
-        break;
-
-      // 78/88 add-on options menu/button click
-      case "click": {
-        e.preventDefault();
-        e.stopPropagation();
-        let BL = {}
-        BL.extension = this.extension;
-        BL.messenger = getMessenger(this.context);
-        let w = Services.wm.getMostRecentWindow("mail:3pane");
-        w.openDialog(this.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);
-      }
-        break;
-
-      // 68 add-on options menu command
-      case "command": {
-        let BL = {}
-        BL.extension = this.extension;
-        BL.messenger = getMessenger(this.context);
-        e.target.ownerGlobal.openDialog(this.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);
-      }
-        break;
-
-      // update, ViewChanged and manual call for add-on manager options overlay
-      default: {
-        let cards = this.getCards(e);
-        for (let card of cards) {
-          // Setup either the options entry in the menu or the button
-          if (card.addon.id == this.extension.id) {
-            let optionsMenu =
-              (getThunderbirdVersion().major > 78 && getThunderbirdVersion().major < 88) ||
-              (getThunderbirdVersion().major == 78 && getThunderbirdVersion().minor < 10) ||
-              (getThunderbirdVersion().major == 78 && getThunderbirdVersion().minor == 10 && getThunderbirdVersion().revision < 2);
-            if (optionsMenu) {
-              // Options menu in 78.0-78.10 and 79-87
-              let addonOptionsLegacyEntry = card.querySelector(".extension-options-legacy");
-              if (card.addon.isActive && !addonOptionsLegacyEntry) {
-                let addonOptionsEntry = card.querySelector("addon-options panel-list panel-item[action='preferences']");
-                addonOptionsLegacyEntry = card.ownerDocument.createElement("panel-item");
-                addonOptionsLegacyEntry.setAttribute("data-l10n-id", "preferences-addon-button");
-                addonOptionsLegacyEntry.classList.add("extension-options-legacy");
-                addonOptionsEntry.parentNode.insertBefore(
-                  addonOptionsLegacyEntry,
-                  addonOptionsEntry
-                );
-                card.querySelector(".extension-options-legacy").addEventListener("click", this);
-              } else if (!card.addon.isActive && addonOptionsLegacyEntry) {
-                addonOptionsLegacyEntry.remove();
-              }
-            } else {
-              // Add-on button in 88
-              let addonOptionsButton = card.querySelector(".extension-options-button2");
-              if (card.addon.isActive && !addonOptionsButton) {
-                addonOptionsButton = card.ownerDocument.createElement("button");
-                addonOptionsButton.classList.add("extension-options-button2");
-                addonOptionsButton.style["min-width"] = "auto";
-                addonOptionsButton.style["min-height"] = "auto";
-                addonOptionsButton.style["width"] = "24px";
-                addonOptionsButton.style["height"] = "24px";
-                addonOptionsButton.style["margin"] = "0";
-                addonOptionsButton.style["margin-inline-start"] = "8px";
-                addonOptionsButton.style["-moz-context-properties"] = "fill";
-                addonOptionsButton.style["fill"] = "currentColor";
-                addonOptionsButton.style["background-image"] = "url('chrome://messenger/skin/icons/developer.svg')";
-                addonOptionsButton.style["background-repeat"] = "no-repeat";
-                addonOptionsButton.style["background-position"] = "center center";
-                addonOptionsButton.style["padding"] = "1px";
-                addonOptionsButton.style["display"] = "flex";
-                addonOptionsButton.style["justify-content"] = "flex-end";
-                card.optionsButton.parentNode.insertBefore(
-                  addonOptionsButton,
-                  card.optionsButton
-                );
-                card.querySelector(".extension-options-button2").addEventListener("click", this);
-              } else if (!card.addon.isActive && addonOptionsButton) {
-                addonOptionsButton.remove();
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // Some tab/add-on-manager related functions
-  getTabMail(window) {
-    return window.document.getElementById("tabmail");
-  }
-
-  // returns the outer browser, not the nested browser of the add-on manager
-  // events must be attached to the outer browser
-  getAddonManagerFromTab(tab) {
-    if (tab.browser && tab.mode.name == "contentTab") {
-      let win = tab.browser.contentWindow;
-      if (win && win.location.href == "about:addons") {
-        return win;
-      }
-    }
-  }
-
-  getAddonManagerFromWindow(window) {
-    let tabMail = this.getTabMail(window);
-    for (let tab of tabMail.tabInfo) {
-      let managerWindow = this.getAddonManagerFromTab(tab);
-      if (managerWindow) {
-        return managerWindow;
-      }
-    }
-  }
-
-  async getAddonManagerFromWindowWaitForLoad(window) {
-    let { setTimeout } = Services.wm.getMostRecentWindow("mail:3pane");
-
-    let tabMail = this.getTabMail(window);
-    for (let tab of tabMail.tabInfo) {
-      if (tab.browser && tab.mode.name == "contentTab") {
-        // Instead of registering a load observer, wait until its loaded. Not nice,
-        // but gets aroud a lot of edge cases.
-        while (!tab.pageLoaded) {
-          await new Promise(r => setTimeout(r, 150));
-        }
-        let managerWindow = this.getAddonManagerFromTab(tab);
-        if (managerWindow) {
-          return managerWindow;
-        }
-      }
-    }
-  }
-
-  setupAddonManager(managerWindow, forceLoad = false) {
-    if (!managerWindow) {
-      return;
-    }
-    if (
-      managerWindow &&
-      managerWindow[this.uniqueRandomID] &&
-      managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners
-    ) {
-      return;
-    }
-    managerWindow.document.addEventListener("ViewChanged", this);
-    managerWindow.document.addEventListener("update", this);
-    managerWindow.document.addEventListener("view-loaded", this);
-    managerWindow[this.uniqueRandomID] = {};
-    managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = true;
-    if (forceLoad) {
-      this.handleEvent(managerWindow);
-    }
-  }
-
-  getAPI(context) {
-    this.uniqueRandomID = "AddOnNS" + context.extension.instanceId;
-    this.menu_addonPrefs_id = "addonPrefs";
-
-
-    this.pathToBootstrapScript = null;
-    this.pathToOptionsPage = null;
-    this.chromeHandle = null;
-    this.chromeData = null;
-    this.resourceData = null;
-    this.bootstrappedObj = {};
-
-    // make the extension object and the messenger object available inside
-    // the bootstrapped scope
-    this.bootstrappedObj.extension = context.extension;
-    this.bootstrappedObj.messenger = getMessenger(this.context);
-
-    this.BOOTSTRAP_REASONS = {
-      APP_STARTUP: 1,
-      APP_SHUTDOWN: 2,
-      ADDON_ENABLE: 3,
-      ADDON_DISABLE: 4,
-      ADDON_INSTALL: 5,
-      ADDON_UNINSTALL: 6, // not supported
-      ADDON_UPGRADE: 7,
-      ADDON_DOWNGRADE: 8,
-    };
-
-    const aomStartup = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup);
-    const resProto = Cc["@mozilla.org/network/protocol;1?name=resource"].getService(Ci.nsISubstitutingProtocolHandler);
-
-    let self = this;
-
-    // TabMonitor to detect opening of tabs, to setup the options button in the add-on manager.
-    this.tabMonitor = {
-      onTabTitleChanged(tab) { },
-      onTabClosing(tab) { },
-      onTabPersist(tab) { },
-      onTabRestored(tab) { },
-      onTabSwitched(aNewTab, aOldTab) { },
-      async onTabOpened(tab) {
-        if (tab.browser && tab.mode.name == "contentTab") {
-          let { setTimeout } = Services.wm.getMostRecentWindow("mail:3pane");
-          // Instead of registering a load observer, wait until its loaded. Not nice,
-          // but gets aroud a lot of edge cases.
-          while (!tab.pageLoaded) {
-            await new Promise(r => setTimeout(r, 150));
-          }
-          self.setupAddonManager(self.getAddonManagerFromTab(tab));
-        }
-      },
-    };
-
-    return {
-      BootstrapLoader: {
-
-        registerOptionsPage(optionsUrl) {
-          self.pathToOptionsPage = optionsUrl.startsWith("chrome://")
-            ? optionsUrl
-            : context.extension.rootURI.resolve(optionsUrl);
-        },
-
-        openOptionsDialog(windowId) {
-          let window = context.extension.windowManager.get(windowId, context).window
-          let BL = {}
-          BL.extension = self.extension;
-          BL.messenger = getMessenger(self.context);
-          window.openDialog(self.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);
-        },
-
-        registerChromeUrl(data) {
-          let chromeData = [];
-          let resourceData = [];
-          for (let entry of data) {
-            if (entry[0] == "resource") resourceData.push(entry);
-            else chromeData.push(entry)
-          }
-
-          if (chromeData.length > 0) {
-            const manifestURI = Services.io.newURI(
-              "manifest.json",
-              null,
-              context.extension.rootURI
-            );
-            self.chromeHandle = aomStartup.registerChrome(manifestURI, chromeData);
-          }
-
-          for (let res of resourceData) {
-            // [ "resource", "shortname" , "path" ]
-            let uri = Services.io.newURI(
-              res[2],
-              null,
-              context.extension.rootURI
-            );
-            resProto.setSubstitutionWithFlags(
-              res[1],
-              uri,
-              resProto.ALLOW_CONTENT_ACCESS
-            );
-          }
-
-          self.chromeData = chromeData;
-          self.resourceData = resourceData;
-        },
-
-        registerBootstrapScript: async function (aPath) {
-          self.pathToBootstrapScript = aPath.startsWith("chrome://")
-            ? aPath
-            : context.extension.rootURI.resolve(aPath);
-
-          // Get the addon object belonging to this extension.
-          let addon = await AddonManager.getAddonByID(context.extension.id);
-          //make the addon globally available in the bootstrapped scope
-          self.bootstrappedObj.addon = addon;
-
-          // add BOOTSTRAP_REASONS to scope
-          for (let reason of Object.keys(self.BOOTSTRAP_REASONS)) {
-            self.bootstrappedObj[reason] = self.BOOTSTRAP_REASONS[reason];
-          }
-
-          // Load registered bootstrap scripts and execute its startup() function.
-          try {
-            if (self.pathToBootstrapScript) Services.scriptloader.loadSubScript(self.pathToBootstrapScript, self.bootstrappedObj, "UTF-8");
-            if (self.bootstrappedObj.startup) self.bootstrappedObj.startup.call(self.bootstrappedObj, self.extension.addonData, self.BOOTSTRAP_REASONS[self.extension.startupReason]);
-          } catch (e) {
-            Components.utils.reportError(e)
-          }
-
-          // Register window listener for main TB window
-          if (self.pathToOptionsPage) {
-            ExtensionSupport.registerWindowListener("injectListener_" + self.uniqueRandomID, {
-              chromeURLs: [
-                "chrome://messenger/content/messenger.xul",
-                "chrome://messenger/content/messenger.xhtml",
-              ],
-              async onLoadWindow(window) {
-                if (getThunderbirdVersion().major < 78) {
-                  let element_addonPrefs = window.document.getElementById(self.menu_addonPrefs_id);
-                  element_addonPrefs.addEventListener("popupshowing", self);
-                } else {
-                  // Add a tabmonitor, to be able to setup the options button/menu in the add-on manager.
-                  self.getTabMail(window).registerTabMonitor(self.tabMonitor);
-                  window[self.uniqueRandomID] = {};
-                  window[self.uniqueRandomID].hasTabMonitor = true;
-                  // Setup the options button/menu in the add-on manager, if it is already open.
-                  let managerWindow = await self.getAddonManagerFromWindowWaitForLoad(window);
-                  self.setupAddonManager(managerWindow, true);
-                }
-              },
-
-              onUnloadWindow(window) {
-              }
-            });
-          }
-        }
-      }
-    };
-  }
-
-  onShutdown(isAppShutdown) {
-    if (isAppShutdown) {
-      return; // the application gets unloaded anyway
-    }
-
-    //remove our entry in the add-on options menu
-    if (this.pathToOptionsPage) {
-      for (let window of Services.wm.getEnumerator("mail:3pane")) {
-        if (getThunderbirdVersion().major < 78) {
-          let element_addonPrefs = window.document.getElementById(this.menu_addonPrefs_id);
-          element_addonPrefs.removeEventListener("popupshowing", this);
-          // Remove our entry.
-          let entry = window.document.getElementById(this.menu_addonPrefs_id + "_" + this.uniqueRandomID);
-          if (entry) entry.remove();
-          // Do we have to unhide the noPrefsElement?
-          if (element_addonPrefs.children.length == 1) {
-            let noPrefsElem = element_addonPrefs.querySelector('[disabled="true"]');
-            noPrefsElem.style.display = "inline";
-          }
-        } else {
-          // Remove event listener for addon manager view changes
-          let managerWindow = this.getAddonManagerFromWindow(window);
-          if (
-            managerWindow && 
-            managerWindow[this.uniqueRandomID] && 
-            managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners
-          ) {
-            managerWindow.document.removeEventListener("ViewChanged", this);
-            managerWindow.document.removeEventListener("update", this);
-            managerWindow.document.removeEventListener("view-loaded", this);
-            managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = false;
-
-            let cards = this.getCards(managerWindow);
-            if (getThunderbirdVersion().major < 88) {
-              // Remove options menu in 78-87
-              for (let card of cards) {
-                let addonOptionsLegacyEntry = card.querySelector(".extension-options-legacy");
-                if (addonOptionsLegacyEntry) addonOptionsLegacyEntry.remove();
-              }
-            } else {
-              // Remove options button in 88
-              for (let card of cards) {
-                if (card.addon.id == this.extension.id) {
-                  let addonOptionsButton = card.querySelector(".extension-options-button2");
-                  if (addonOptionsButton) addonOptionsButton.remove();
-                  break;
-                }
-              }
-            }
-          }
-
-          // Remove tabmonitor
-          if (window[this.uniqueRandomID].hasTabMonitor) {
-            this.getTabMail(window).unregisterTabMonitor(this.tabMonitor);
-            window[this.uniqueRandomID].hasTabMonitor = false;
-          }
-
-        }
-      }
-      // Stop listening for new windows.
-      ExtensionSupport.unregisterWindowListener("injectListener_" + this.uniqueRandomID);
-    }
-
-    // Execute registered shutdown()
-    try {
-      if (this.bootstrappedObj.shutdown) {
-        this.bootstrappedObj.shutdown(
-          this.extension.addonData,
-          isAppShutdown
-            ? this.BOOTSTRAP_REASONS.APP_SHUTDOWN
-            : this.BOOTSTRAP_REASONS.ADDON_DISABLE);
-      }
-    } catch (e) {
-      Components.utils.reportError(e)
-    }
-
-    if (this.resourceData) {
-      const resProto = Cc["@mozilla.org/network/protocol;1?name=resource"].getService(Ci.nsISubstitutingProtocolHandler);
-      for (let res of this.resourceData) {
-        // [ "resource", "shortname" , "path" ]
-        resProto.setSubstitution(
-          res[1],
-          null,
-        );
-      }
-    }
-
-    if (this.chromeHandle) {
-      this.chromeHandle.destruct();
-      this.chromeHandle = null;
-    }
-    // Flush all caches
-    Services.obs.notifyObservers(null, "startupcache-invalidate");
-    console.log("BootstrapLoader for " + this.extension.id + " unloaded!");
-  }
-};
-
 // Removed all extra code for backward compatibility for better maintainability.
-var BootstrapLoader_115 = class extends ExtensionCommon.ExtensionAPI {
-  getCards(e) {
-    // This gets triggered by real events but also manually by providing the outer window.
-    // The event is attached to the outer browser, get the inner one.
-    let doc = e.document || e.target;
-    return doc.querySelectorAll("addon-card");
-  }
-
-  // Event handler for the addon manager, to update the state of the options button.
-  handleEvent(e) {
-    switch (e.type) {
-      case "click": {
-        e.preventDefault();
-        e.stopPropagation();
-        let BL = {}
-        BL.extension = this.extension;
-        BL.messenger = getMessenger(this.context);
-        let w = Services.wm.getMostRecentWindow("mail:3pane");
-        w.openDialog(
-          this.pathToOptionsPage,
-          "AddonOptions",
-          "chrome,resizable,centerscreen",
-          BL
-        );
-      }
-        break;
-
-
-      // update, ViewChanged and manual call for add-on manager options overlay
-      default: {
-        let cards = this.getCards(e);
-        for (let card of cards) {
-          // Setup either the options entry in the menu or the button
-          if (card.addon.id == this.extension.id) {
-            // Add-on button
-            let addonOptionsButton = card.querySelector(
-              ".windowlistener-options-button"
-            );
-            if (card.addon.isActive && !addonOptionsButton) {
-              let origAddonOptionsButton = card.querySelector(".extension-options-button")
-              origAddonOptionsButton.setAttribute("hidden", "true");
-
-              addonOptionsButton = card.ownerDocument.createElement("button");
-              addonOptionsButton.classList.add("windowlistener-options-button");
-              addonOptionsButton.classList.add("extension-options-button");
-              card.optionsButton.parentNode.insertBefore(
-                addonOptionsButton,
-                card.optionsButton
-              );
-              card
-                .querySelector(".windowlistener-options-button")
-                .addEventListener("click", this);
-            } else if (!card.addon.isActive && addonOptionsButton) {
-              addonOptionsButton.remove();
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // Some tab/add-on-manager related functions
-  getTabMail(window) {
-    return window.document.getElementById("tabmail");
-  }
-
-  // returns the outer browser, not the nested browser of the add-on manager
-  // events must be attached to the outer browser
-  getAddonManagerFromTab(tab) {
-    if (tab.browser && tab.mode.name == "contentTab") {
-      let win = tab.browser.contentWindow;
-      if (win && win.location.href == "about:addons") {
-        return win;
-      }
-    }
-  }
-
-  getAddonManagerFromWindow(window) {
-    let tabMail = this.getTabMail(window);
-    for (let tab of tabMail.tabInfo) {
-      let managerWindow = this.getAddonManagerFromTab(tab);
-      if (managerWindow) {
-        return managerWindow;
-      }
-    }
-  }
-
-  async getAddonManagerFromWindowWaitForLoad(window) {
-    let { setTimeout } = Services.wm.getMostRecentWindow("mail:3pane");
-
-    let tabMail = this.getTabMail(window);
-    for (let tab of tabMail.tabInfo) {
-      if (tab.browser && tab.mode.name == "contentTab") {
-        // Instead of registering a load observer, wait until its loaded. Not nice,
-        // but gets aroud a lot of edge cases.
-        while (!tab.pageLoaded) {
-          await new Promise(r => setTimeout(r, 150));
-        }
-        let managerWindow = this.getAddonManagerFromTab(tab);
-        if (managerWindow) {
-          return managerWindow;
-        }
-      }
-    }
-  }
-
-  setupAddonManager(managerWindow, forceLoad = false) {
-    if (!managerWindow) {
-      return;
-    }
-    if (!this.pathToOptionsPage) {
-      return;
-    }
-    if (
-      managerWindow &&
-      managerWindow[this.uniqueRandomID] &&
-      managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners
-    ) {
-      return;
-    }
-    
-    managerWindow.document.addEventListener("ViewChanged", this);
-    managerWindow.document.addEventListener("update", this);
-    managerWindow.document.addEventListener("view-loaded", this);
-    managerWindow[this.uniqueRandomID] = {};
-    managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = true;
-    if (forceLoad) {
-      this.handleEvent(managerWindow);
-    }
-  }
-
+var BootstrapLoader = class extends ExtensionCommon.ExtensionAPI {
   getAPI(context) {
     this.uniqueRandomID = "AddOnNS" + context.extension.instanceId;
     this.menu_addonPrefs_id = "addonPrefs";
 
 
     this.pathToBootstrapScript = null;
-    this.pathToOptionsPage = null;
     this.chromeHandle = null;
     this.chromeData = null;
-    this.resourceData = null;
     this.bootstrappedObj = {};
 
     // make the extension object and the messenger object available inside
@@ -711,27 +110,10 @@
 
     return {
       BootstrapLoader: {
-
-        registerOptionsPage(optionsUrl) {
-          self.pathToOptionsPage = optionsUrl.startsWith("chrome://")
-            ? optionsUrl
-            : context.extension.rootURI.resolve(optionsUrl);
-        },
-
-        openOptionsDialog(windowId) {
-          let window = context.extension.windowManager.get(windowId, context).window
-          let BL = {}
-          BL.extension = self.extension;
-          BL.messenger = getMessenger(self.context);
-          window.openDialog(self.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);
-        },
-
         registerChromeUrl(data) {
           let chromeData = [];
-          let resourceData = [];
           for (let entry of data) {
-            if (entry[0] == "resource") resourceData.push(entry);
-            else chromeData.push(entry)
+            chromeData.push(entry)
           }
 
           if (chromeData.length > 0) {
@@ -743,22 +125,7 @@
             self.chromeHandle = aomStartup.registerChrome(manifestURI, chromeData);
           }
 
-          for (let res of resourceData) {
-            // [ "resource", "shortname" , "path" ]
-            let uri = Services.io.newURI(
-              res[2],
-              null,
-              context.extension.rootURI
-            );
-            resProto.setSubstitutionWithFlags(
-              res[1],
-              uri,
-              resProto.ALLOW_CONTENT_ACCESS
-            );
-          }
-
           self.chromeData = chromeData;
-          self.resourceData = resourceData;
         },
 
         registerBootstrapScript: async function (aPath) {
@@ -768,6 +135,7 @@
 
           // Get the addon object belonging to this extension.
           let addon = await AddonManager.getAddonByID(context.extension.id);
+          console.log(addon.id);
           //make the addon globally available in the bootstrapped scope
           self.bootstrappedObj.addon = addon;
 
@@ -784,32 +152,6 @@
             Components.utils.reportError(e)
           }
 
-          // Register window listener for main TB window
-          if (self.pathToOptionsPage) {
-            ExtensionSupport.registerWindowListener("injectListener_" + self.uniqueRandomID, {
-              chromeURLs: [
-                "chrome://messenger/content/messenger.xul",
-                "chrome://messenger/content/messenger.xhtml",
-              ],
-              async onLoadWindow(window) {
-                if (getThunderbirdVersion().major < 78) {
-                  let element_addonPrefs = window.document.getElementById(self.menu_addonPrefs_id);
-                  element_addonPrefs.addEventListener("popupshowing", self);
-                } else {
-                  // Add a tabmonitor, to be able to setup the options button/menu in the add-on manager.
-                  self.getTabMail(window).registerTabMonitor(self.tabMonitor);
-                  window[self.uniqueRandomID] = {};
-                  window[self.uniqueRandomID].hasTabMonitor = true;
-                  // Setup the options button/menu in the add-on manager, if it is already open.
-                  let managerWindow = await self.getAddonManagerFromWindowWaitForLoad(window);
-                  self.setupAddonManager(managerWindow, true);
-                }
-              },
-
-              onUnloadWindow(window) {
-              }
-            });
-          }
         }
       }
     };
@@ -820,63 +162,6 @@
       return; // the application gets unloaded anyway
     }
 
-    //remove our entry in the add-on options menu
-    if (this.pathToOptionsPage) {
-      for (let window of Services.wm.getEnumerator("mail:3pane")) {
-        if (getThunderbirdVersion().major < 78) {
-          let element_addonPrefs = window.document.getElementById(this.menu_addonPrefs_id);
-          element_addonPrefs.removeEventListener("popupshowing", this);
-          // Remove our entry.
-          let entry = window.document.getElementById(this.menu_addonPrefs_id + "_" + this.uniqueRandomID);
-          if (entry) entry.remove();
-          // Do we have to unhide the noPrefsElement?
-          if (element_addonPrefs.children.length == 1) {
-            let noPrefsElem = element_addonPrefs.querySelector('[disabled="true"]');
-            noPrefsElem.style.display = "inline";
-          }
-        } else {
-          // Remove event listener for addon manager view changes
-          let managerWindow = this.getAddonManagerFromWindow(window);
-          if (
-            managerWindow && 
-            managerWindow[this.uniqueRandomID] && 
-            managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners
-          ) {
-            managerWindow.document.removeEventListener("ViewChanged", this);
-            managerWindow.document.removeEventListener("update", this);
-            managerWindow.document.removeEventListener("view-loaded", this);
-            managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = false;
-
-            let cards = this.getCards(managerWindow);
-            if (getThunderbirdVersion().major < 88) {
-              // Remove options menu in 78-87
-              for (let card of cards) {
-                let addonOptionsLegacyEntry = card.querySelector(".extension-options-legacy");
-                if (addonOptionsLegacyEntry) addonOptionsLegacyEntry.remove();
-              }
-            } else {
-              // Remove options button in 88
-              for (let card of cards) {
-                if (card.addon.id == this.extension.id) {
-                  let addonOptionsButton = card.querySelector(".extension-options-button2");
-                  if (addonOptionsButton) addonOptionsButton.remove();
-                  break;
-                }
-              }
-            }
-          }
-
-          // Remove tabmonitor
-          if (window[this.uniqueRandomID].hasTabMonitor) {
-            this.getTabMail(window).unregisterTabMonitor(this.tabMonitor);
-            window[this.uniqueRandomID].hasTabMonitor = false;
-          }
-
-        }
-      }
-      // Stop listening for new windows.
-      ExtensionSupport.unregisterWindowListener("injectListener_" + this.uniqueRandomID);
-    }
 
     // Execute registered shutdown()
     try {
@@ -891,17 +176,6 @@
       Components.utils.reportError(e)
     }
 
-    if (this.resourceData) {
-      const resProto = Cc["@mozilla.org/network/protocol;1?name=resource"].getService(Ci.nsISubstitutingProtocolHandler);
-      for (let res of this.resourceData) {
-        // [ "resource", "shortname" , "path" ]
-        resProto.setSubstitution(
-          res[1],
-          null,
-        );
-      }
-    }
-
     if (this.chromeHandle) {
       this.chromeHandle.destruct();
       this.chromeHandle = null;
@@ -910,8 +184,4 @@
     Services.obs.notifyObservers(null, "startupcache-invalidate");
     console.log("BootstrapLoader for " + this.extension.id + " unloaded!");
   }
-};
-
-var BootstrapLoader = getThunderbirdVersion().major < 111
-  ? BootstrapLoader_102
-  : BootstrapLoader_115;
+};
\ Kein Zeilenumbruch am Dateiende.
diff -Nru eas4tbsync-4.8/content/bootstrap.js eas4tbsync-4.11/content/bootstrap.js
--- eas4tbsync-4.8/content/bootstrap.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/bootstrap.js	2024-08-19 20:22:14.000000000 +0200
@@ -8,8 +8,6 @@
 
 // no need to create namespace, we are in a sandbox
 
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
 let onInitDoneObserver = {
     observe: async function (aSubject, aTopic, aData) {        
         let valid = false;
diff -Nru eas4tbsync-4.8/content/includes/calendarsync.js eas4tbsync-4.11/content/includes/calendarsync.js
--- eas4tbsync-4.8/content/includes/calendarsync.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/calendarsync.js	2024-08-19 20:22:14.000000000 +0200
@@ -18,6 +18,7 @@
  CalTodo: "resource:///modules/CalTodo.jsm",
 }); 
 
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
 const cal = TbSync.lightning.cal;
 const ICAL = TbSync.lightning.ICAL;
 
diff -Nru eas4tbsync-4.8/content/includes/contactsync.js eas4tbsync-4.11/content/includes/contactsync.js
--- eas4tbsync-4.8/content/includes/contactsync.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/contactsync.js	2024-08-19 20:22:14.000000000 +0200
@@ -21,6 +21,9 @@
     VCardUtils: "resource:///modules/VCardUtils.jsm",
 });
 
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
+var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
+
 const eas = TbSync.providers.eas;
 
 var Contacts = {
diff -Nru eas4tbsync-4.8/content/includes/network.js eas4tbsync-4.11/content/includes/network.js
--- eas4tbsync-4.8/content/includes/network.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/network.js	2024-08-19 20:22:14.000000000 +0200
@@ -5,29 +5,81 @@
  * 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";
+
+"use strict";
 
 var { OAuth2 } = ChromeUtils.import("resource:///modules/OAuth2.jsm");
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
+
+var containers = [];
+var sandboxes = {};
+
+function resetContainerWithId(id) {
+    Services.clearData.deleteDataFromOriginAttributesPattern({ userContextId: id });
+}
+
+function getContainerIdForContainerName(containerName) {
+    // Define the allowed range of container ids to be used
+    // TbSync is using 10000 - 19999
+    // Lightning is using 20000 - 29999
+    // Cardbook is using 30000 - 39999
+    let min = 10000;
+    let max = 19999;
+
+    //reset if adding an entry will exceed allowed range
+    if (containers.length > (max - min) && containers.indexOf(containerName) == -1) {
+        for (let i = 0; i < containers.length; i++) {
+            resetContainerWithId(i + min);
+        }
+        containers = [];
+    }
+
+    let idx = containers.indexOf(containerName);
+    return (idx == -1) ? containers.push(containerName) - 1 + min : (idx + min);
+}
+
+function getSandBoxedXHR({ user, accountname }, uri, containerReset = false) {
+    // The content principal used for the sandbox honours CORS. A server redirect
+    // to a different server may cause CORS violations. We implemented code to
+    // catch such redirects and re-run the request with the correct sandbox. If
+    // that becomes an issue, we need to make sandboxing optional.
+    // return new XMLHttpRequest({ mozAnon: false });
+
+    let containerName = `${accountname}::${user}@${uri.scheme}://${uri.hostPort}`;
+
+    let userContextId = getContainerIdForContainerName(containerName);
+    if (containerReset) {
+        resetContainerWithId(userContextId);
+    }
+    if (!sandboxes.hasOwnProperty(containerName)) {
+        console.log("Creating sandbox for <" + containerName + ">");
+        let principal = Services.scriptSecurityManager.createContentPrincipal(uri, { userContextId });
+        sandboxes[containerName] = Components.utils.Sandbox(principal, {
+            wantXrays: true,
+            wantGlobalProperties: ["XMLHttpRequest"],
+        });
+    }
+    return new sandboxes[containerName].XMLHttpRequest({ mozAnon: false });
+}
+
+var network = {
 
-var network = {  
-    
-    getEasURL: function(accountData) {
+    getEasURL: function (accountData) {
         let protocol = (accountData.getAccountProperty("https")) ? "https://" : "http://";
-        let h = protocol + accountData.getAccountProperty("host"); 
-        while (h.endsWith("/")) { h = h.slice(0,-1); }
+        let h = protocol + accountData.getAccountProperty("host");
+        while (h.endsWith("/")) { h = h.slice(0, -1); }
 
         if (h.endsWith("Microsoft-Server-ActiveSync")) return h;
-        return h + "/Microsoft-Server-ActiveSync"; 
+        return h + "/Microsoft-Server-ActiveSync";
     },
-    
-    getAuthData: function(accountData) {
+
+    getAuthData: function (accountData) {
         let authData = {
             // This is the host for the password manager, which could be different from
             // the actual host property of the account. For EAS we want to couple the password
             // with the ACCOUNT and not any sort of url, which could change via autodiscover
             // at any time.
-            get host() { 
+            get host() {
                 return "TbSync#" + accountData.accountID;
             },
 
@@ -39,213 +91,244 @@
                 return TbSync.passwordManager.getLoginInfo(this.host, "TbSync/EAS", this.user);
             },
 
-            updateLoginData: function(newUsername, newPassword) {
+            get accountname() {
+                return accountData.getAccountProperty("accountname");
+            },
+
+            updateLoginData: async function (newUsername, newPassword) {
                 let oldUsername = this.user;
-                TbSync.passwordManager.updateLoginInfo(this.host, "TbSync/EAS", oldUsername, newUsername, newPassword);
+                await TbSync.passwordManager.updateLoginInfo(this.host, "TbSync/EAS", oldUsername, newUsername, newPassword);
                 // Also update the username of this account. Add dedicated username setter?
                 accountData.setAccountProperty("user", newUsername);
-            },          
+            },
 
-            removeLoginData: function() {
-              TbSync.passwordManager.removeLoginInfos(this.host, "TbSync/EAS");
+            removeLoginData: function () {
+                TbSync.passwordManager.removeLoginInfos(this.host, "TbSync/EAS");
             }
         };
         return authData;
-    },  
-    
-  // prepare and patch OAuth2 object
-  getOAuthObj: function(configObject = null) {
-    let accountname, user, host, accountID, servertype;
-      
-    let accountData = (configObject && configObject.hasOwnProperty("accountData")) ? configObject.accountData : null;
-    if (accountData) {
-      accountname = accountData.getAccountProperty("accountname");
-      user = accountData.getAccountProperty("user");
-      host = accountData.getAccountProperty("host");
-      servertype = accountData.getAccountProperty("servertype");
-      accountID = accountData.accountID;
-    } else {
-      accountname = (configObject && configObject.hasOwnProperty("accountname")) ? configObject.accountname : "";
-      user = (configObject && configObject.hasOwnProperty("user")) ? configObject.user : "";
-      host = (configObject && configObject.hasOwnProperty("host")) ? configObject.host : "";
-      servertype = (configObject && configObject.hasOwnProperty("servertype")) ? configObject.servertype : "";
-      accountID = "";
-    }
+    },
+
+    getContextData: function (configObject = null) {
+        let contextData = {}
+        contextData.accountData = (configObject && configObject.hasOwnProperty("accountData")) ? configObject.accountData : null;
+
+        if (contextData.accountData) {
+            contextData.accountname = contextData.accountData.getAccountProperty("accountname");
+            contextData.user = contextData.accountData.getAccountProperty("user");
+            contextData.host = contextData.accountData.getAccountProperty("host");
+            contextData.servertype = contextData.accountData.getAccountProperty("servertype");
+            contextData.accountID = contextData.accountData.accountID;
+        } else {
+            contextData.accountname = (configObject && configObject.hasOwnProperty("accountname")) ? configObject.accountname : "";
+            contextData.user = (configObject && configObject.hasOwnProperty("user")) ? configObject.user : "";
+            contextData.host = (configObject && configObject.hasOwnProperty("host")) ? configObject.host : "";
+            contextData.servertype = (configObject && configObject.hasOwnProperty("servertype")) ? configObject.servertype : "";
+            contextData.accountID = "";
+        }
 
-    if (!["office365"].includes(servertype))
-      return null;
+        return contextData;
+    },
 
-    let config = {};
-    let customID = eas.Base.getCustomeOauthClientID();
-    switch (host) {
-      case "outlook.office365.com":
-      case "eas.outlook.com":
-        config = {
-          auth_uri : "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
-          token_uri : "https://login.microsoftonline.com/common/oauth2/v2.0/token",
-          redirect_uri : "https://login.microsoftonline.com/common/oauth2/nativeclient",
-          client_id : customID != "" ? customID : "2980deeb-7460-4723-864a-f9b0f10cd992",
-        }
-        break;
-      
-      default:
-        return null;
-    }
+    // prepare and patch OAuth2 object
+    getOAuthObj: function (configObject = null) {
+        let {
+            accountData,
+            accountname,
+            user,
+            host,
+            accountID,
+            servertype
+        } = this.getContextData(configObject);
+
+        if (!["office365"].includes(servertype))
+            return null;
+
+        let config = {};
+        let customID = eas.Base.getCustomeOauthClientID();
+        switch (host) {
+            case "outlook.office365.com":
+            case "eas.outlook.com":
+                config = {
+                    auth_uri: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
+                    token_uri: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
+                    redirect_uri: "https://login.microsoftonline.com/common/oauth2/nativeclient",
+                    client_id: customID != "" ? customID : "2980deeb-7460-4723-864a-f9b0f10cd992",
+                }
+                break;
 
-    switch (host) {
-      case "outlook.office365.com":
-        config.scope = "offline_access https://outlook.office.com/.default";
-        break;
-      case "eas.outlook.com":
-        config.scope = "offline_access https://outlook.office.com/EAS.AccessAsUser.All";
-        break;
-    }
+            default:
+                return null;
+        }
+
+        switch (host) {
+            case "outlook.office365.com":
+                config.scope = "offline_access https://outlook.office.com/.default";
+                break;
+            case "eas.outlook.com":
+                config.scope = "offline_access https://outlook.office.com/EAS.AccessAsUser.All";
+                break;
+        }
 
-    let oauth = new OAuth2(config.scope, {
-        authorizationEndpoint: config.auth_uri,
-        tokenEndpoint: config.token_uri,
-        clientId: config.client_id,
-        clientSecret: config.client_secret
-    });
-    oauth.requestWindowFeatures = "chrome,private,centerscreen,width=500,height=750";
-
-    // The v2 redirection endpoint differs from the default and needs manual override
-    oauth.redirectionEndpoint = config.redirect_uri;
-    
-    oauth.extraAuthParams = [
-      // removed in beta 1.14.1, according to
-      // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#default-and-consent
-      // prompt = consent will always ask for admin consent, even if it was granted
-      //["prompt", "consent"],
-      ["login_hint", user],
-    ];
-        
-    if (accountname) {
-      oauth.requestWindowTitle = "TbSync account <" + accountname + "> requests authorization.";
-    } else {
-      oauth.requestWindowTitle = "A TbSync account requests authorization.";
-    }      
-
-    
-    
-    
-    /* Adding custom methods to the oauth object */ 
-
-    oauth.asyncConnect = async function(rv) {
-      let self = this;
-      rv.error = "";
-      rv.tokens = "";
-
-      // If multiple resources need to authenticate they will all end here, even though they
-      // might share the same token. Due to the async nature, each process will refresh
-      // "its own" token again, which is not needed. We force clear the token here and each
-      // final connect process will actually check the acccessToken and abort the refresh, 
-      // if it is already there, generated by some other process. 
-      if (self.accessToken) self.accessToken = "";
-      
-      try {
-        await new Promise(function(resolve, reject) {
-          // refresh = false will do nothing and resolve immediately, if an accessToken
-          // exists already, which must have been generated by another process, as
-          // we cleared it beforehand.
-          self.connect(resolve, reject, /* with UI */ true, /* refresh */ false);
+        let oauth = new OAuth2(config.scope, {
+            authorizationEndpoint: config.auth_uri,
+            tokenEndpoint: config.token_uri,
+            clientId: config.client_id,
+            clientSecret: config.client_secret
         });
-        rv.tokens = self.tokens;
-        return true;
-      } catch (e) {
-        rv.error = eas.tools.isString(e) ? e : JSON.stringify(e);
-      }
-      
-      try {
-        switch (JSON.parse(rv.error).error) {
-          case "invalid_grant":
-            self.accessToken = "";
-            self.refreshToken = "";
-            return true;
-
-          case "cancelled": 
-            rv.error = "OAuthAbortError"; 
-            break;
-          
-          default:
-            rv.error = "OAuthServerError::"+rv.error; 
-            break;
-        }
-      } catch (e) {
-        rv.error = "OAuthServerError::"+rv.error; 
-        Components.utils.reportError(e);
-      }
-      return false;
-    };
-
-    oauth.isExpired = function() {
-      const OAUTH_GRACE_TIME = 30 * 1000;
-      return (this.tokenExpires - OAUTH_GRACE_TIME < new Date().getTime());
-    };
-    
-    const OAUTHVALUES = [
-      ["access", "", "accessToken"], 
-      ["refresh", "", "refreshToken"], 
-      ["expires", Number.MAX_VALUE, "tokenExpires"],
-    ];
-        
-    // returns a JSON string containing all the oauth values
-    Object.defineProperty(oauth, "tokens", {
-      get: function() {
-        let tokensObj = {};
-        for (let oauthValue of OAUTHVALUES) {
-          // use the system value or if not defined the default
-          tokensObj[oauthValue[0]] = this[oauthValue[2]] || oauthValue[1];
-        }
-        return JSON.stringify(tokensObj);
-      },
-      enumerable: true,
-    });
-    
-    if (accountData) {      
-      // authData allows us to access the password manager values belonging to this account/calendar
-      // simply by authdata.username and authdata.password
-      oauth.authData = TbSync.providers.eas.network.getAuthData(accountData);        
+        oauth.requestWindowFeatures = "chrome,private,centerscreen,width=500,height=750";
 
-      oauth.parseAndSanitizeTokenString = function(tokenString) {
-        let _tokensObj = {};
-        try {
-          _tokensObj = JSON.parse(tokenString);
-        } catch (e) {}
+        // The v2 redirection endpoint differs from the default and needs manual override
+        oauth.redirectionEndpoint = config.redirect_uri;
+
+        oauth.extraAuthParams = [
+            // removed in beta 1.14.1, according to
+            // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#default-and-consent
+            // prompt = consent will always ask for admin consent, even if it was granted
+            //["prompt", "consent"],
+            ["login_hint", user],
+        ];
+
+        if (accountname) {
+            oauth.requestWindowTitle = "TbSync account <" + accountname + "> requests authorization.";
+        } else {
+            oauth.requestWindowTitle = "A TbSync account requests authorization.";
+        }
+
+
+
+
+        /* Adding custom methods to the oauth object */
 
-        let tokensObj = {};
-        for (let oauthValue of OAUTHVALUES) {
-          // use the provided value or if not defined the default
-          tokensObj[oauthValue[0]] = (_tokensObj && _tokensObj.hasOwnProperty(oauthValue[0]))
-            ? _tokensObj[oauthValue[0]]
-            : oauthValue[1];
-        }
-        return tokensObj;
-      };
-      
-      // Define getter/setter to act on the password manager password value belonging to this account/calendar
-      for (let oauthValue of OAUTHVALUES) {
-        Object.defineProperty(oauth, oauthValue[2], {
-          get: function() {
-            return this.parseAndSanitizeTokenString(this.authData.password)[oauthValue[0]];
-          },
-          set: function(val) {
-            let tokens = this.parseAndSanitizeTokenString(this.authData.password);
-            let valueChanged = (val != tokens[oauthValue[0]])
-            if (valueChanged) {
-              tokens[oauthValue[0]] = val;
-              this.authData.updateLoginData(this.authData.user, JSON.stringify(tokens));
+        oauth.asyncConnect = async function (rv) {
+            rv.error = "";
+
+            // If multiple resources need to authenticate they will all end here, even though they
+            // might share the same token. Due to the async nature, each process will refresh
+            // "its own" token again, which is not needed. We force clear the token here and each
+            // final connect process will actually check the acccessToken and abort the refresh, 
+            // if it is already there, generated by some other process. 
+            if (oauth.getToken("accessToken")) {
+                await oauth.setToken("accessToken", "");
+            }
+
+
+            try {
+                // refresh = false will do nothing and resolve immediately, if an accessToken
+                // exists already, which must have been generated by another process, as
+                // we cleared it beforehand.
+                await oauth.connect(/* with UI */ true, /* refresh */ false);
+                await oauth.setToken("accessToken", oauth.accessToken);
+                await oauth.setToken("refreshToken", oauth.refreshToken);
+                await oauth.setToken("tokenExpires", oauth.tokenExpires);
+                rv.tokens = oauth.tokens;
+                return true;
+            } catch (e) {
+                await oauth.setToken("accessToken", oauth.accessToken);
+                await oauth.setToken("refreshToken", oauth.refreshToken);
+                await oauth.setToken("tokenExpires", oauth.tokenExpires);
+                rv.error = eas.tools.isString(e) ? e : JSON.stringify(e);
+            }
+
+            try {
+                switch (JSON.parse(rv.error).error) {
+                    case "invalid_grant":
+                        await oauth.setToken("accessToken", "");
+                        await oauth.setToken("refreshToken", "");
+                        rv.tokens = oauth.tokens;
+                        return true;
+
+                    case "cancelled":
+                        rv.error = "OAuthAbortError";
+                        break;
+
+                    default:
+                        rv.error = "OAuthServerError::" + rv.error;
+                        break;
+                }
+            } catch (e) {
+                rv.error = "OAuthServerError::" + rv.error;
+                Components.utils.reportError(e);
             }
-          },
-          enumerable: true,
+            rv.tokens = oauth.tokens;
+            return false;
+        };
+
+        oauth.isExpired = function () {
+            const OAUTH_GRACE_TIME = 30 * 1000;
+            return (oauth.getToken("tokenExpires") - OAUTH_GRACE_TIME < new Date().getTime());
+        };
+
+        const OAUTHVALUES = [
+            ["access", "", "accessToken"],
+            ["refresh", "", "refreshToken"],
+            ["expires", Number.MAX_VALUE, "tokenExpires"],
+        ];
+
+        // returns a JSON string containing all the oauth values
+        Object.defineProperty(oauth, "tokens", {
+            get: function () {
+                let tokensObj = {};
+                for (let oauthValue of OAUTHVALUES) {
+                    // use the system value or if not defined the default
+                    tokensObj[oauthValue[0]] = this[oauthValue[2]] || oauthValue[1];
+                }
+                return JSON.stringify(tokensObj);
+            },
+            enumerable: true,
         });
-      }
-    }
-    
-    return oauth;
-  },    
 
-    getOAuthValue: function(currentTokenString, type = "access") {
+        if (accountData) {
+            // authData allows us to access the password manager values belonging to this account/calendar
+            // simply by authdata.username and authdata.password
+            oauth.authData = TbSync.providers.eas.network.getAuthData(accountData);
+
+            oauth.parseAndSanitizeTokenString = function (tokenString) {
+                let _tokensObj = {};
+                try {
+                    _tokensObj = JSON.parse(tokenString);
+                } catch (e) { }
+
+                let tokensObj = {};
+                for (let oauthValue of OAUTHVALUES) {
+                    // use the provided value or if not defined the default
+                    tokensObj[oauthValue[0]] = (_tokensObj && _tokensObj.hasOwnProperty(oauthValue[0]))
+                        ? _tokensObj[oauthValue[0]]
+                        : oauthValue[1];
+                }
+                return tokensObj;
+            };
+
+            oauth.getToken = function (tokenName) {
+                let oauthValue = OAUTHVALUES.find(e => e[2] == tokenName);
+                return oauth.parseAndSanitizeTokenString(oauth.authData.password)[oauthValue[0]];
+            };
+
+            oauth.setToken = async function (tokenName, val) {
+                oauth[tokenName] = val;
+
+                let oauthValue = OAUTHVALUES.find(e => e[2] == tokenName);
+                let tokens = oauth.parseAndSanitizeTokenString(oauth.authData.password);
+                let valueChanged = (val != tokens[oauthValue[0]])
+                if (valueChanged) {
+                    tokens[oauthValue[0]] = val;
+                    await oauth.authData.updateLoginData(oauth.authData.user, JSON.stringify(tokens));
+                }
+            };
+        } else {
+            oauth.getToken = function (tokenName) {
+                return oauth[tokenName];
+            };
+
+            oauth.setToken = async function (tokenName, val) {
+                oauth[tokenName] = val;
+            };
+        }
+
+        return oauth;
+    },
+
+    getOAuthValue: function (currentTokenString, type = "access") {
         try {
             let tokens = JSON.parse(currentTokenString);
             if (tokens.hasOwnProperty(type))
@@ -258,91 +341,91 @@
 
     sendRequest: async function (wbxml, command, syncData, allowSoftFail = false) {
         let ALLOWED_RETRIES = {
-            PasswordPrompt : 3,
-            NetworkError : 1,
+            PasswordPrompt: 3,
+            NetworkError: 1,
         }
-        
+
         let rv = {};
         let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData });
-        let syncState = syncData.getSyncState().state; 
-        
-        for (;;) {
+        let syncState = syncData.getSyncState().state;
+
+        for (; ;) {
 
-            if (rv.errorType) {                
+            if (rv.errorType) {
                 let retry = false;
-                
+
                 if (ALLOWED_RETRIES[rv.errorType] > 0) {
                     ALLOWED_RETRIES[rv.errorType]--;
-                    
+
 
                     switch (rv.errorType) {
-                        
-                        case "PasswordPrompt": 
-                        {
-                            
-                            if (oauthData) {
-                                oauthData.accessToken = "";
-                                retry = true;                                
-                            } else {
-                                let authData = eas.network.getAuthData(syncData.accountData);
-                                syncData.setSyncState("passwordprompt");
-                                let promptData = {
-                                    windowID: "auth:" + syncData.accountData.accountID,
-                                    accountname: syncData.accountData.getAccountProperty("accountname"),
-                                    usernameLocked: syncData.accountData.isConnected(),
-                                    username: authData.user
-                                }
-                                let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
-                                if (credentials) {
-                                  retry = true;
-                                  authData.updateLoginData(credentials.username, credentials.password);
+
+                        case "PasswordPrompt":
+                            {
+
+                                if (oauthData) {
+                                    await oauthData.setToken("accessToken", "");
+                                    retry = true;
+                                } else {
+                                    let authData = eas.network.getAuthData(syncData.accountData);
+                                    syncData.setSyncState("passwordprompt");
+                                    let promptData = {
+                                        windowID: "auth:" + syncData.accountData.accountID,
+                                        accountname: syncData.accountData.getAccountProperty("accountname"),
+                                        usernameLocked: syncData.accountData.isConnected(),
+                                        username: authData.user
+                                    }
+                                    let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
+                                    if (credentials) {
+                                        retry = true;
+                                        await authData.updateLoginData(credentials.username, credentials.password);
+                                    }
                                 }
                             }
-                        }
-                        break;
-                        
+                            break;
+
                         case "NetworkError":
-                        {
-                            // Could not connect to server. Can we rerun autodiscover?
-                            // Note: Autodiscover is currently not supported by OAuth
-                            if (syncData.accountData.getAccountProperty( "servertype") == "auto" && !oauthData) {
-                                let errorcode = await eas.network.updateServerConnectionViaAutodiscover(syncData);
-                                console.log("ERR: " + errorcode);
-                                if (errorcode == 200) {                       
-                                    // autodiscover succeeded, retry with new data
-                                    retry = true;                            
-                                } else if (errorcode == 401) {
-                                    // manipulate rv to run password prompt
-                                    ALLOWED_RETRIES[rv.errorType]++;
-                                    rv.errorType = "PasswordPrompt";
-                                    rv.errorObj = eas.sync.finish("error", "401");
-                                    continue; // with the next loop, skip connection to the server
+                            {
+                                // Could not connect to server. Can we rerun autodiscover?
+                                // Note: Autodiscover is currently not supported by OAuth
+                                if (syncData.accountData.getAccountProperty("servertype") == "auto" && !oauthData) {
+                                    let errorcode = await eas.network.updateServerConnectionViaAutodiscover(syncData);
+                                    console.log("ERR: " + errorcode);
+                                    if (errorcode == 200) {
+                                        // autodiscover succeeded, retry with new data
+                                        retry = true;
+                                    } else if (errorcode == 401) {
+                                        // manipulate rv to run password prompt
+                                        ALLOWED_RETRIES[rv.errorType]++;
+                                        rv.errorType = "PasswordPrompt";
+                                        rv.errorObj = eas.sync.finish("error", "401");
+                                        continue; // with the next loop, skip connection to the server
+                                    }
                                 }
                             }
-                        }
-                        break;
-                        
+                            break;
+
                     }
-                } 
-                
+                }
+
                 if (!retry) throw rv.errorObj;
             }
-            
+
             // check OAuth situation before connecting
-            if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
-                syncData.setSyncState("oauthprompt");                                
+            if (oauthData && (!oauthData.getToken("accessToken") || oauthData.isExpired())) {
+                syncData.setSyncState("oauthprompt");
                 let _rv = {}
                 if (!(await oauthData.asyncConnect(_rv))) {
-                    throw eas.sync.finish("error", _rv.error); 
+                    throw eas.sync.finish("error", _rv.error);
                 }
-            }            
-            
+            }
+
             // Return to original syncstate
             if (syncState != syncData.getSyncState().state) {
                 syncData.setSyncState(syncState);
             }
             rv = await this.sendRequestPromise(wbxml, command, syncData, allowSoftFail);
-            
+
             if (rv.errorType) {
                 // make sure, there is a valid ALLOWED_RETRIES setting for the returned error
                 if (rv.errorType && !ALLOWED_RETRIES.hasOwnProperty(rv.errorType)) {
@@ -351,7 +434,7 @@
             } else {
                 return rv;
             }
-        }        
+        }
     },
 
     sendRequestPromise: function (wbxml, command, syncData, allowSoftFail = false) {
@@ -365,19 +448,20 @@
         let deviceType = syncData.accountData.getAccountProperty("devicetype");
         let deviceId = syncData.accountData.getAccountProperty("deviceId");
 
-        TbSync.dump("Sending (EAS v"+syncData.accountData.getAccountProperty("asversion") +")", "POST " + eas.network.getEasURL(syncData.accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(connection.user) + '&DeviceType=' +deviceType + '&DeviceId=' + deviceId, true);
+        TbSync.dump("Sending (EAS v" + syncData.accountData.getAccountProperty("asversion") + ")", "POST " + eas.network.getEasURL(syncData.accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(connection.user) + '&DeviceType=' + deviceType + '&DeviceId=' + deviceId, true);
 
         const textEncoder = new TextEncoder();
-        let encoded = textEncoder.encode(wbxml);            
+        let encoded = textEncoder.encode(wbxml);
         // console.log("wbxml: " + wbxml);
         // console.log("byte array: " + encoded);
         // console.log("length :" + wbxml.length + " vs " + encoded.byteLength + " vs " + encoded.length);
-        
-        return new Promise(function(resolve,reject) {
-            // Create request handler - API changed with TB60 to new XMKHttpRequest()
-            syncData.req = new XMLHttpRequest();
+
+        let contextData = eas.network.getContextData({ accountData: syncData.accountData });
+        let uri = Services.io.newURI(eas.network.getEasURL(syncData.accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(connection.user) + '&DeviceType=' + encodeURIComponent(deviceType) + '&DeviceId=' + deviceId);
+        return new Promise(function (resolve, reject) {
+            syncData.req = getSandBoxedXHR(contextData, uri);
             syncData.req.mozBackgroundRequest = true;
-            syncData.req.open("POST", eas.network.getEasURL(syncData.accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(connection.user) + '&DeviceType=' +encodeURIComponent(deviceType) + '&DeviceId=' + deviceId, true);
+            syncData.req.open("POST", uri.spec, true);
             syncData.req.overrideMimeType("text/plain");
             syncData.req.setRequestHeader("User-Agent", userAgent);
             syncData.req.setRequestHeader("Content-Type", "application/vnd.ms-sync.wbxml");
@@ -388,7 +472,7 @@
                     syncData.req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(connection.user + ':' + connection.password));
                 }
             }
-            
+
             if (syncData.accountData.getAccountProperty("asversion") == "2.5") {
                 syncData.req.setRequestHeader("MS-ASProtocolVersion", "2.5");
             } else {
@@ -422,9 +506,9 @@
                 }
             };
 
-            syncData.req.onload = function() {
+            syncData.req.onload = function () {
                 let response = syncData.req.responseText;
-                switch(syncData.req.status) {
+                switch (syncData.req.status) {
 
                     case 200: //OK
                         let msg = "Receiving data <" + syncData.getSyncState().state + "> for " + syncData.accountData.getAccountProperty("accountname");
@@ -459,12 +543,12 @@
                         let header = syncData.req.getResponseHeader("X-MS-Location");
                         let newHost = header.slice(header.indexOf("://") + 3, header.indexOf("/M"));
 
-                        TbSync.dump("redirect (451)", "header: " + header + ", oldHost: " +syncData.accountData.getAccountProperty("host") + ", newHost: " + newHost);
+                        TbSync.dump("redirect (451)", "header: " + header + ", oldHost: " + syncData.accountData.getAccountProperty("host") + ", newHost: " + newHost);
 
                         syncData.accountData.setAccountProperty("host", newHost);
                         reject(eas.sync.finish("resyncAccount", syncData.req.status));
                         break;
-                        
+
                     default:
                         if (allowSoftFail) {
                             resolve("");
@@ -475,7 +559,7 @@
             };
 
             syncData.req.send(encoded);
-            
+
         });
     },
 
@@ -489,21 +573,21 @@
 
 
     // RESPONSE EVALUATION
-    
-    logXML : function (wbxml, what) {
+
+    logXML: function (wbxml, what) {
         let rawxml = eas.wbxmltools.convert2xml(wbxml);
         let xml = null;
-        if (rawxml)  {
+        if (rawxml) {
             xml = rawxml.split('><').join('>\n<');
         }
-                
+
         //include xml in log, if userdatalevel 2 or greater
         if (TbSync.prefs.getIntPref("log.userdatalevel") > 1) {
 
             //log raw wbxml if userdatalevel is 3 or greater
             if (TbSync.prefs.getIntPref("log.userdatalevel") > 2) {
                 let charcodes = [];
-                for (let i=0; i< wbxml.length; i++) charcodes.push(wbxml.charCodeAt(i).toString(16));
+                for (let i = 0; i < wbxml.length; i++) charcodes.push(wbxml.charCodeAt(i).toString(16));
                 let bytestring = charcodes.join(" ");
                 TbSync.dump("WBXML: " + what, "\n" + bytestring);
             }
@@ -516,12 +600,12 @@
                 TbSync.dump("XML: " + what, "\nFailed to convert WBXML to XML!\n");
             }
         }
-    
+
         return xml;
     },
-    
+
     //returns false on parse error and null on empty response (if allowed)
-    getDataFromResponse: function (wbxml, allowEmptyResponse = !eas.flags.allowEmptyResponse) {        
+    getDataFromResponse: function (wbxml, allowEmptyResponse = !eas.flags.allowEmptyResponse) {
         //check for empty wbxml
         if (wbxml.length === 0) {
             if (allowEmptyResponse) return null;
@@ -533,21 +617,21 @@
         if (xml === false) {
             throw eas.sync.finish("warning", "wbxml-parse-error");
         }
-        
+
         //retrieve data and check for empty data (all returned data fields are already decoded by decodeURIComponent)
         let wbxmlData = eas.xmltools.getDataFromXMLString(xml);
         if (wbxmlData === null) {
             if (allowEmptyResponse) return null;
             else throw eas.sync.finish("warning", "response-contains-no-data");
         }
-        
+
         //debug
         eas.xmltools.printXmlData(wbxmlData, false); //do not include ApplicationData in log
         return wbxmlData;
-    },  
-  
+    },
+
     updateSynckey: function (syncData, wbxmlData) {
-        let synckey = eas.xmltools.getWbxmlDataField(wbxmlData,"Sync.Collections.Collection.SyncKey");
+        let synckey = eas.xmltools.getWbxmlDataField(wbxmlData, "Sync.Collections.Collection.SyncKey");
 
         if (synckey) {
             // This COULD be a cause of problems... 
@@ -558,24 +642,24 @@
         }
     },
 
-    checkStatus : function (syncData, wbxmlData, path, rootpath="", allowSoftFail = false) {
+    checkStatus: function (syncData, wbxmlData, path, rootpath = "", allowSoftFail = false) {
         //path is relative to wbxmlData
         //rootpath is the absolute path and must be specified, if wbxml is not the root node and thus path is not the rootpath	    
-        let status = eas.xmltools.getWbxmlDataField(wbxmlData,path);
-        let fullpath = (rootpath=="") ? path : rootpath;
+        let status = eas.xmltools.getWbxmlDataField(wbxmlData, path);
+        let fullpath = (rootpath == "") ? path : rootpath;
         let elements = fullpath.split(".");
         let type = elements[0];
 
         //check if fallback to main class status: the answer could just be a "Sync.Status" instead of a "Sync.Collections.Collections.Status"
         if (status === false) {
-            let mainStatus = eas.xmltools.getWbxmlDataField(wbxmlData, type + "." + elements[elements.length-1]);
+            let mainStatus = eas.xmltools.getWbxmlDataField(wbxmlData, type + "." + elements[elements.length - 1]);
             if (mainStatus === false) {
                 //both possible status fields are missing, abort
                 throw eas.sync.finish("warning", "wbxmlmissingfield::" + fullpath, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
             } else {
                 //the alternative status could be extracted
                 status = mainStatus;
-                fullpath = type + "." + elements[elements.length-1];
+                fullpath = type + "." + elements[elements.length - 1];
             }
         }
 
@@ -587,7 +671,7 @@
         TbSync.dump("wbxml status check", type + ": " + fullpath + " = " + status);
 
         //handle errrors based on type
-        let statusType = type+"."+status;
+        let statusType = type + "." + status;
         switch (statusType) {
             case "Sync.3": /*
                         MUST return to SyncKey element value of 0 for the collection. The client SHOULD either delete any items that were added 
@@ -596,7 +680,7 @@
                 TbSync.eventlog.add("warning", syncData.eventLogInfo, "Forced Folder Resync", "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
                 syncData.currentFolderData.remove();
                 throw eas.sync.finish("resyncFolder", statusType);
-            
+
             case "Sync.4": //Malformed request
             case "Sync.5": //Temporary server issues or invalid item
             case "Sync.6": //Invalid item
@@ -620,49 +704,49 @@
                     let folders = syncData.accountData.getAllFoldersIncludingCache();
                     for (let folder of folders) {
                         folder.remove();
-                    }		    
+                    }
                     // reset account
                     eas.Base.onEnableAccount(syncData.accountData);
                     throw eas.sync.finish("resyncAccount", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
                 }
         }
-        
+
         //handle global error (https://msdn.microsoft.com/en-us/library/ee218647(v=exchg.80).aspx)
         let descriptions = {};
-        switch(status) {
+        switch (status) {
             case "101": //invalid content
             case "102": //invalid wbxml
             case "103": //invalid xml
                 throw eas.sync.finish("error", "global." + status, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
-            
-            case "109": descriptions["109"]="DeviceTypeMissingOrInvalid";
-            case "112": descriptions["112"]="ActiveDirectoryAccessDenied";
-            case "126": descriptions["126"]="UserDisabledForSync";
-            case "127": descriptions["127"]="UserOnNewMailboxCannotSync";
-            case "128": descriptions["128"]="UserOnLegacyMailboxCannotSync";
-            case "129": descriptions["129"]="DeviceIsBlockedForThisUser";
-            case "130": descriptions["120"]="AccessDenied";
-            case "131": descriptions["131"]="AccountDisabled";
-                throw eas.sync.finish("error", "global.clientdenied"+ "::" + status + "::" + descriptions[status]);
+
+            case "109": descriptions["109"] = "DeviceTypeMissingOrInvalid";
+            case "112": descriptions["112"] = "ActiveDirectoryAccessDenied";
+            case "126": descriptions["126"] = "UserDisabledForSync";
+            case "127": descriptions["127"] = "UserOnNewMailboxCannotSync";
+            case "128": descriptions["128"] = "UserOnLegacyMailboxCannotSync";
+            case "129": descriptions["129"] = "DeviceIsBlockedForThisUser";
+            case "130": descriptions["120"] = "AccessDenied";
+            case "131": descriptions["131"] = "AccountDisabled";
+                throw eas.sync.finish("error", "global.clientdenied" + "::" + status + "::" + descriptions[status]);
 
             case "110": //server error - abort and disable autoSync for 30 minutes
-            {
-                let noAutosyncUntil = 30 * 60000 + Date.now();
-                let humanDate = new Date(noAutosyncUntil).toUTCString();
-                syncData.accountData.setAccountProperty("noAutosyncUntil", noAutosyncUntil);
-                throw eas.sync.finish("error", "global." + status, "AutoSync disabled until: " + humanDate + " \n\nRequest:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
-
-/*                     // reset account
- *                     let folders = syncData.accountData.getAllFoldersIncludingCache();
- *                     for (let folder of folders) {
- *                         folder.remove();
- *                     }		    
- *                     // reset account
- *                     eas.Base.onEnableAccount(syncData.accountData);
- *                     throw eas.sync.finish("resyncAccount", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
- */
-            }
-                
+                {
+                    let noAutosyncUntil = 30 * 60000 + Date.now();
+                    let humanDate = new Date(noAutosyncUntil).toUTCString();
+                    syncData.accountData.setAccountProperty("noAutosyncUntil", noAutosyncUntil);
+                    throw eas.sync.finish("error", "global." + status, "AutoSync disabled until: " + humanDate + " \n\nRequest:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
+
+                    /*                     // reset account
+                     *                     let folders = syncData.accountData.getAllFoldersIncludingCache();
+                     *                     for (let folder of folders) {
+                     *                         folder.remove();
+                     *                     }		    
+                     *                     // reset account
+                     *                     eas.Base.onEnableAccount(syncData.accountData);
+                     *                     throw eas.sync.finish("resyncAccount", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
+                     */
+                }
+
             case "141": // The device is not provisionable
             case "142": // DeviceNotProvisioned
             case "143": // PolicyRefresh
@@ -671,14 +755,14 @@
                 syncData.accountData.setAccountProperty("provision", true);
                 syncData.accountData.resetAccountProperty("policykey");
                 throw eas.sync.finish("resyncAccount", statusType);
-            
+
             default:
                 if (allowSoftFail) return statusType;
                 throw eas.sync.finish("error", statusType, "Request:\n" + syncData.request + "\n\nResponse:\n" + syncData.response);
 
-        }		
+        }
     },
-    
+
 
 
 
@@ -689,25 +773,25 @@
 
 
     // WBXML COMM STUFF
-    
-    setDeviceInformation: async function (syncData)  {
+
+    setDeviceInformation: async function (syncData) {
         if (syncData.accountData.getAccountProperty("asversion") == "2.5" || !syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("Settings")) {
             return;
         }
-            
+
         syncData.setSyncState("prepare.request.setdeviceinfo");
 
         let wbxml = wbxmltools.createWBXML();
         wbxml.switchpage("Settings");
         wbxml.otag("Settings");
-            wbxml.otag("DeviceInformation");
-                wbxml.otag("Set");
-                    wbxml.atag("Model", "Computer");
-                    wbxml.atag("FriendlyName", "TbSync on Device " + syncData.accountData.getAccountProperty("deviceId").substring(4));
-                    wbxml.atag("OS", Services.appinfo.OS);
-                    wbxml.atag("UserAgent", syncData.accountData.getAccountProperty("useragent"));
-                wbxml.ctag();
-            wbxml.ctag();
+        wbxml.otag("DeviceInformation");
+        wbxml.otag("Set");
+        wbxml.atag("Model", "Computer");
+        wbxml.atag("FriendlyName", "TbSync on Device " + syncData.accountData.getAccountProperty("deviceId").substring(4));
+        wbxml.atag("OS", Services.appinfo.OS);
+        wbxml.atag("UserAgent", syncData.accountData.getAccountProperty("useragent"));
+        wbxml.ctag();
+        wbxml.ctag();
         wbxml.ctag();
 
         syncData.setSyncState("send.request.setdeviceinfo");
@@ -716,24 +800,24 @@
         syncData.setSyncState("eval.response.setdeviceinfo");
         let wbxmlData = eas.network.getDataFromResponse(response);
 
-        eas.network.checkStatus(syncData, wbxmlData,"Settings.Status");
+        eas.network.checkStatus(syncData, wbxmlData, "Settings.Status");
     },
 
-    getPolicykey: async function (syncData)  {
+    getPolicykey: async function (syncData) {
         //build WBXML to request provision
-       syncData.setSyncState("prepare.request.provision");
+        syncData.setSyncState("prepare.request.provision");
         let wbxml = eas.wbxmltools.createWBXML();
         wbxml.switchpage("Provision");
         wbxml.otag("Provision");
-            wbxml.otag("Policies");
-                wbxml.otag("Policy");
-                    wbxml.atag("PolicyType", (syncData.accountData.getAccountProperty("asversion") == "2.5") ? "MS-WAP-Provisioning-XML" : "MS-EAS-Provisioning-WBXML" );
-                wbxml.ctag();
-            wbxml.ctag();
+        wbxml.otag("Policies");
+        wbxml.otag("Policy");
+        wbxml.atag("PolicyType", (syncData.accountData.getAccountProperty("asversion") == "2.5") ? "MS-WAP-Provisioning-XML" : "MS-EAS-Provisioning-WBXML");
+        wbxml.ctag();
+        wbxml.ctag();
         wbxml.ctag();
 
-        for (let loop=0; loop < 2; loop++) {
-           syncData.setSyncState("send.request.provision");
+        for (let loop = 0; loop < 2; loop++) {
+            syncData.setSyncState("send.request.provision");
             let response = await eas.network.sendRequest(wbxml.getBytes(), "Provision", syncData);
 
             syncData.setSyncState("eval.response.provision");
@@ -744,12 +828,12 @@
                 throw eas.sync.finish("error", "wbxmlmissingfield::Provision.Status");
             } else if (provisionStatus != "1") {
                 //dump policy status as well
-                if (policyStatus) TbSync.dump("PolicyKey","Received policy status: " + policyStatus);
+                if (policyStatus) TbSync.dump("PolicyKey", "Received policy status: " + policyStatus);
                 throw eas.sync.finish("error", "provision::" + provisionStatus);
             }
 
             //reaching this point: provision status was ok
-            let policykey = eas.xmltools.getWbxmlDataField(wbxmlData,"Provision.Policies.Policy.PolicyKey");
+            let policykey = eas.xmltools.getWbxmlDataField(wbxmlData, "Provision.Policies.Policy.PolicyKey");
             switch (policyStatus) {
                 case false:
                     throw eas.sync.finish("error", "wbxmlmissingfield::Provision.Policies.Policy.Status");
@@ -763,8 +847,8 @@
                 case "1":
                     if (policykey === false) {
                         throw eas.sync.finish("error", "wbxmlmissingfield::Provision.Policies.Policy.PolicyKey");
-                    } 
-                    TbSync.dump("PolicyKey","Received policykey (" + loop + "): " + policykey);
+                    }
+                    TbSync.dump("PolicyKey", "Received policykey (" + loop + "): " + policykey);
                     syncData.accountData.setAccountProperty("policykey", policykey);
                     break;
 
@@ -773,19 +857,19 @@
             }
 
             //build WBXML to acknowledge provision
-           syncData.setSyncState("prepare.request.provision");
+            syncData.setSyncState("prepare.request.provision");
             wbxml = eas.wbxmltools.createWBXML();
             wbxml.switchpage("Provision");
             wbxml.otag("Provision");
-                wbxml.otag("Policies");
-                    wbxml.otag("Policy");
-                        wbxml.atag("PolicyType",(syncData.accountData.getAccountProperty("asversion") == "2.5") ? "MS-WAP-Provisioning-XML" : "MS-EAS-Provisioning-WBXML" );
-                        wbxml.atag("PolicyKey", policykey);
-                        wbxml.atag("Status", "1");
-                    wbxml.ctag();
-                wbxml.ctag();
+            wbxml.otag("Policies");
+            wbxml.otag("Policy");
+            wbxml.atag("PolicyType", (syncData.accountData.getAccountProperty("asversion") == "2.5") ? "MS-WAP-Provisioning-XML" : "MS-EAS-Provisioning-WBXML");
+            wbxml.atag("PolicyKey", policykey);
+            wbxml.atag("Status", "1");
+            wbxml.ctag();
             wbxml.ctag();
-            
+            wbxml.ctag();
+
             //this wbxml will be used by Send at the top of this loop
         }
     },
@@ -795,15 +879,15 @@
         //build WBXML to request a new syncKey
         let wbxml = eas.wbxmltools.createWBXML();
         wbxml.otag("Sync");
-            wbxml.otag("Collections");
-                wbxml.otag("Collection");
-                    if (syncData.accountData.getAccountProperty("asversion") == "2.5") wbxml.atag("Class", syncData.type);
-                    wbxml.atag("SyncKey","0");
-                    wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
-                wbxml.ctag();
-            wbxml.ctag();
+        wbxml.otag("Collections");
+        wbxml.otag("Collection");
+        if (syncData.accountData.getAccountProperty("asversion") == "2.5") wbxml.atag("Class", syncData.type);
+        wbxml.atag("SyncKey", "0");
+        wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
+        wbxml.ctag();
         wbxml.ctag();
-        
+        wbxml.ctag();
+
         syncData.setSyncState("send.request.synckey");
         let response = await eas.network.sendRequest(wbxml.getBytes(), "Sync", syncData);
 
@@ -811,52 +895,52 @@
         // get data from wbxml response
         let wbxmlData = eas.network.getDataFromResponse(response);
         //check status
-        eas.network.checkStatus(syncData, wbxmlData,"Sync.Collections.Collection.Status");
+        eas.network.checkStatus(syncData, wbxmlData, "Sync.Collections.Collection.Status");
         //update synckey
         eas.network.updateSynckey(syncData, wbxmlData);
     },
 
-    getItemEstimate: async function (syncData)  {
+    getItemEstimate: async function (syncData) {
         syncData.progressData.reset();
-        
+
         if (!syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("GetItemEstimate")) {
             return; //do not throw, this is optional
         }
-        
+
         syncData.setSyncState("prepare.request.estimate");
-        
+
         // BUILD WBXML
         let wbxml = eas.wbxmltools.createWBXML();
         wbxml.switchpage("GetItemEstimate");
         wbxml.otag("GetItemEstimate");
-            wbxml.otag("Collections");
-                wbxml.otag("Collection");
-                    if (syncData.accountData.getAccountProperty("asversion") == "2.5") { //got this order for 2.5 directly from Microsoft support
-                        wbxml.atag("Class", syncData.type); //only 2.5
-                        wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
-                        wbxml.switchpage("AirSync");
-                        // required !
-                        // https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ascmd/ffbefa62-e315-40b9-9cc6-f8d74b5f65d4
-                        if (syncData.type == "Calendar") wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
-                        else wbxml.atag("FilterType", "0"); // we may filter incomplete tasks
-                        
-                        wbxml.atag("SyncKey", syncData.synckey);
-                        wbxml.switchpage("GetItemEstimate");
-                    } else { //14.0
-                        wbxml.switchpage("AirSync");
-                        wbxml.atag("SyncKey", syncData.synckey);
-                        wbxml.switchpage("GetItemEstimate");
-                        wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
-                        wbxml.switchpage("AirSync");
-                        wbxml.otag("Options");
-                            // optional
-                            if (syncData.type == "Calendar") wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
-                            wbxml.atag("Class", syncData.type);
-                        wbxml.ctag();
-                        wbxml.switchpage("GetItemEstimate");
-                    }
-                wbxml.ctag();
+        wbxml.otag("Collections");
+        wbxml.otag("Collection");
+        if (syncData.accountData.getAccountProperty("asversion") == "2.5") { //got this order for 2.5 directly from Microsoft support
+            wbxml.atag("Class", syncData.type); //only 2.5
+            wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
+            wbxml.switchpage("AirSync");
+            // required !
+            // https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ascmd/ffbefa62-e315-40b9-9cc6-f8d74b5f65d4
+            if (syncData.type == "Calendar") wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
+            else wbxml.atag("FilterType", "0"); // we may filter incomplete tasks
+
+            wbxml.atag("SyncKey", syncData.synckey);
+            wbxml.switchpage("GetItemEstimate");
+        } else { //14.0
+            wbxml.switchpage("AirSync");
+            wbxml.atag("SyncKey", syncData.synckey);
+            wbxml.switchpage("GetItemEstimate");
+            wbxml.atag("CollectionId", syncData.currentFolderData.getFolderProperty("serverID"));
+            wbxml.switchpage("AirSync");
+            wbxml.otag("Options");
+            // optional
+            if (syncData.type == "Calendar") wbxml.atag("FilterType", syncData.currentFolderData.accountData.getAccountProperty("synclimit"));
+            wbxml.atag("Class", syncData.type);
             wbxml.ctag();
+            wbxml.switchpage("GetItemEstimate");
+        }
+        wbxml.ctag();
+        wbxml.ctag();
         wbxml.ctag();
 
         //SEND REQUEST
@@ -878,7 +962,7 @@
         }
     },
 
-    getUserInfo: async function (syncData)  {
+    getUserInfo: async function (syncData) {
         if (!syncData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("Settings")) {
             return;
         }
@@ -888,9 +972,9 @@
         let wbxml = eas.wbxmltools.createWBXML();
         wbxml.switchpage("Settings");
         wbxml.otag("Settings");
-            wbxml.otag("UserInformation");
-                wbxml.atag("Get");
-            wbxml.ctag();
+        wbxml.otag("UserInformation");
+        wbxml.atag("Get");
+        wbxml.ctag();
         wbxml.ctag();
 
         syncData.setSyncState("send.request.getuserinfo");
@@ -900,12 +984,12 @@
         syncData.setSyncState("eval.response.getuserinfo");
         let wbxmlData = eas.network.getDataFromResponse(response);
 
-        eas.network.checkStatus(syncData, wbxmlData,"Settings.Status");
+        eas.network.checkStatus(syncData, wbxmlData, "Settings.Status");
     },
 
 
 
-    
+
 
 
 
@@ -919,50 +1003,51 @@
         let _wbxml = eas.wbxmltools.createWBXML();
         _wbxml.switchpage("Search");
         _wbxml.otag("Search");
-            _wbxml.otag("Store");
-                _wbxml.atag("Name", "GAL");
-                _wbxml.atag("Query", currentQuery);
-                _wbxml.otag("Options");
-                    _wbxml.atag("Range", "0-99"); //Z-Push needs a Range
-                    //Not valid for GAL: https://msdn.microsoft.com/en-us/library/gg675461(v=exchg.80).aspx
-                    //_wbxml.atag("DeepTraversal");
-                    //_wbxml.atag("RebuildResults");
-                _wbxml.ctag();
-            _wbxml.ctag();
+        _wbxml.otag("Store");
+        _wbxml.atag("Name", "GAL");
+        _wbxml.atag("Query", currentQuery);
+        _wbxml.otag("Options");
+        _wbxml.atag("Range", "0-99"); //Z-Push needs a Range
+        //Not valid for GAL: https://msdn.microsoft.com/en-us/library/gg675461(v=exchg.80).aspx
+        //_wbxml.atag("DeepTraversal");
+        //_wbxml.atag("RebuildResults");
+        _wbxml.ctag();
+        _wbxml.ctag();
         _wbxml.ctag();
 
         let wbxml = _wbxml.getBytes();
-        
+
         eas.network.logXML(wbxml, "Send (GAL Search)");
         let command = "Search";
-        
+
         let authData = eas.network.getAuthData(accountData);
         let oauthData = eas.network.getOAuthObj({ accountData });
         let userAgent = accountData.getAccountProperty("useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
         let deviceType = accountData.getAccountProperty("devicetype");
         let deviceId = accountData.getAccountProperty("deviceId");
 
-        TbSync.dump("Sending (EAS v" + accountData.getAccountProperty("asversion") +")", "POST " + eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +deviceType + '&DeviceId=' + deviceId, true);
-        
-        for (let i=0; i < 2; i++) {
+        TbSync.dump("Sending (EAS v" + accountData.getAccountProperty("asversion") + ")", "POST " + eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' + deviceType + '&DeviceId=' + deviceId, true);
+
+        for (let i = 0; i < 2; i++) {
             // check OAuth situation before connecting
-            if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
+            if (oauthData && (!oauthData.getToken("accessToken") || oauthData.isExpired())) {
                 let _rv = {}
                 if (!(await oauthData.asyncConnect(_rv))) {
-                    throw eas.sync.finish("error", _rv.error); 
+                    throw eas.sync.finish("error", _rv.error);
                 }
-            } 
-            
+            }
+
             try {
-                let response = await new Promise(function(resolve, reject) {
-                    // Create request handler - API changed with TB60 to new XMKHttpRequest()
-                    let req = new XMLHttpRequest();
+                let contextData = eas.network.getContextData({ accountData });
+                let uri = Services.io.newURI(eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' + encodeURIComponent(deviceType) + '&DeviceId=' + deviceId);
+                let response = await new Promise(function (resolve, reject) {
+                    let req = getSandBoxedXHR(contextData, uri);
                     req.mozBackgroundRequest = true;
-                    req.open("POST", eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +encodeURIComponent(deviceType) + '&DeviceId=' + deviceId, true);
+                    req.open("POST", uri.spec, true);
                     req.overrideMimeType("text/plain");
                     req.setRequestHeader("User-Agent", userAgent);
                     req.setRequestHeader("Content-Type", "application/vnd.ms-sync.wbxml");
-                    
+
                     if (authData.password) {
                         if (eas.network.getOAuthObj({ accountData })) {
                             req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(authData.password, "access"));
@@ -970,7 +1055,7 @@
                             req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(authData.user + ':' + authData.password));
                         }
                     }
-                    
+
                     if (accountData.getAccountProperty("asversion") == "2.5") {
                         req.setRequestHeader("MS-ASProtocolVersion", "2.5");
                     } else {
@@ -992,10 +1077,10 @@
                         reject("GAL Search Error");
                     };
 
-                    req.onload = function() {
+                    req.onload = function () {
                         let response = req.responseText;
-                        
-                        switch(req.status) {
+
+                        switch (req.status) {
 
                             case 200: //OK
                                 eas.network.logXML(response, "Received (GAL Search");
@@ -1008,27 +1093,27 @@
                                     resolve(response);
                                 }
                                 break;
-                              
+
                             case 401: // bad auth
-                                    resolve("401");
+                                resolve("401");
                                 break;
-                                
+
                             default:
                                 reject("GAL Search Failed: " + req.status);
                         }
                     };
 
                     req.send(wbxml);
-                    
+
                 });
 
                 if (response === "401") {
                     // try to recover from bad auth via token refresh
                     if (oauthData) {
-                        oauthData.accessToken = "";
+                        await oauthData.setToken("accessToken", "");
                         continue;
                     }
-                }                    
+                }
 
                 return response;
             } catch (e) {
@@ -1047,37 +1132,39 @@
 
 
 
-       // OPTIONS
+    // OPTIONS
 
-    getServerOptions: async function (syncData) {        
+    getServerOptions: async function (syncData) {
         syncData.setSyncState("prepare.request.options");
         let authData = eas.network.getAuthData(syncData.accountData);
 
         let userAgent = syncData.accountData.getAccountProperty("useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
         TbSync.dump("Sending", "OPTIONS " + eas.network.getEasURL(syncData.accountData));
-        
+
         let allowedRetries = 5;
         let retry;
         let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData });
-        
+
         do {
             retry = false;
-            
+
             // Check OAuth situation before connecting
-            if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
+            if (oauthData && (!oauthData.getToken("accessToken") || oauthData.isExpired())) {
                 let _rv = {};
-                syncData.setSyncState("oauthprompt");                                
+                syncData.setSyncState("oauthprompt");
                 if (!(await oauthData.asyncConnect(_rv))) {
                     throw eas.sync.finish("error", _rv.error);
                 }
             }
-            
-            let result = await new Promise(function(resolve,reject) {
-                syncData.req = new XMLHttpRequest();
+
+            let contextData = eas.network.getContextData({ accountData: syncData.accountData });
+            let uri = Services.io.newURI(eas.network.getEasURL(syncData.accountData));
+            let result = await new Promise(function (resolve, reject) {
+                syncData.req = getSandBoxedXHR(contextData, uri);
                 syncData.req.mozBackgroundRequest = true;
-                syncData.req.open("OPTIONS", eas.network.getEasURL(syncData.accountData), true);
+                syncData.req.open("OPTIONS", uri.spec, true);
                 syncData.req.overrideMimeType("text/plain");
-                syncData.req.setRequestHeader("User-Agent", userAgent);            
+                syncData.req.setRequestHeader("User-Agent", userAgent);
                 if (authData.password) {
                     if (eas.network.getOAuthObj({ accountData: syncData.accountData })) {
                         syncData.req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(authData.password, "access"));
@@ -1093,21 +1180,21 @@
 
                 syncData.req.onerror = function () {
                     let responseData = {};
-                    responseData["MS-ASProtocolVersions"] =  syncData.req.getResponseHeader("MS-ASProtocolVersions");
-                    responseData["MS-ASProtocolCommands"] =  syncData.req.getResponseHeader("MS-ASProtocolCommands");                        
+                    responseData["MS-ASProtocolVersions"] = syncData.req.getResponseHeader("MS-ASProtocolVersions");
+                    responseData["MS-ASProtocolCommands"] = syncData.req.getResponseHeader("MS-ASProtocolCommands");
 
-                    TbSync.dump("EAS OPTIONS with response (status: "+syncData.req.status+")", "\n" +
-                    "responseText: " + syncData.req.responseText + "\n" +
-                    "responseHeader(MS-ASProtocolVersions): " + responseData["MS-ASProtocolVersions"]+"\n" +
-                    "responseHeader(MS-ASProtocolCommands): " + responseData["MS-ASProtocolCommands"]);
+                    TbSync.dump("EAS OPTIONS with response (status: " + syncData.req.status + ")", "\n" +
+                        "responseText: " + syncData.req.responseText + "\n" +
+                        "responseHeader(MS-ASProtocolVersions): " + responseData["MS-ASProtocolVersions"] + "\n" +
+                        "responseHeader(MS-ASProtocolCommands): " + responseData["MS-ASProtocolCommands"]);
                     resolve();
                 };
 
-                syncData.req.onload = function() {
+                syncData.req.onload = function () {
                     syncData.setSyncState("eval.request.options");
                     let responseData = {};
 
-                    switch(syncData.req.status) {
+                    switch (syncData.req.status) {
                         case 401: // AuthError
                             let rv = {};
                             rv.errorObj = eas.sync.finish("error", "401");
@@ -1116,39 +1203,39 @@
                             break;
 
                         case 200:
-                                responseData["MS-ASProtocolVersions"] =  syncData.req.getResponseHeader("MS-ASProtocolVersions");
-                                responseData["MS-ASProtocolCommands"] =  syncData.req.getResponseHeader("MS-ASProtocolCommands");                        
+                            responseData["MS-ASProtocolVersions"] = syncData.req.getResponseHeader("MS-ASProtocolVersions");
+                            responseData["MS-ASProtocolCommands"] = syncData.req.getResponseHeader("MS-ASProtocolCommands");
 
-                                TbSync.dump("EAS OPTIONS with response (status: 200)", "\n" +
+                            TbSync.dump("EAS OPTIONS with response (status: 200)", "\n" +
                                 "responseText: " + syncData.req.responseText + "\n" +
-                                "responseHeader(MS-ASProtocolVersions): " + responseData["MS-ASProtocolVersions"]+"\n" +
+                                "responseHeader(MS-ASProtocolVersions): " + responseData["MS-ASProtocolVersions"] + "\n" +
                                 "responseHeader(MS-ASProtocolCommands): " + responseData["MS-ASProtocolCommands"]);
 
-                                if (responseData && responseData["MS-ASProtocolCommands"] && responseData["MS-ASProtocolVersions"]) {
-                                    syncData.accountData.setAccountProperty("allowedEasCommands", responseData["MS-ASProtocolCommands"]);
-                                    syncData.accountData.setAccountProperty("allowedEasVersions", responseData["MS-ASProtocolVersions"]);
-                                    syncData.accountData.setAccountProperty("lastEasOptionsUpdate", Date.now());
-                                }
-                                resolve();
+                            if (responseData && responseData["MS-ASProtocolCommands"] && responseData["MS-ASProtocolVersions"]) {
+                                syncData.accountData.setAccountProperty("allowedEasCommands", responseData["MS-ASProtocolCommands"]);
+                                syncData.accountData.setAccountProperty("allowedEasVersions", responseData["MS-ASProtocolVersions"]);
+                                syncData.accountData.setAccountProperty("lastEasOptionsUpdate", Date.now());
+                            }
+                            resolve();
                             break;
 
                         default:
-                                resolve();
+                            resolve();
                             break;
 
                     }
                 };
-                
+
                 syncData.setSyncState("send.request.options");
                 syncData.req.send();
-                
+
             });
-            
+
             if (result && result.hasOwnProperty("errorType") && result.errorType == "PasswordPrompt") {
                 if (allowedRetries > 0) {
                     if (oauthData) {
-                        oauthData.accessToken = "";
-                        retry = true;                            
+                        await oauthData.setToken("accessToken", "");
+                        retry = true;
                     } else {
                         syncData.setSyncState("passwordprompt");
                         let authData = eas.network.getAuthData(syncData.accountData);
@@ -1160,8 +1247,8 @@
                         }
                         let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
                         if (credentials) {
-                          authData.updateLoginData(credentials.username, credentials.password);
-                          retry = true;
+                            await authData.updateLoginData(credentials.username, credentials.password);
+                            retry = true;
                         }
                     }
                 }
@@ -1170,7 +1257,7 @@
                     throw result.errorObj;
                 }
             }
-            
+
             allowedRetries--;
         } while (retry);
     },
@@ -1185,111 +1272,112 @@
 
 
     // AUTODISCOVER
-    
+
     updateServerConnectionViaAutodiscover: async function (syncData) {
         syncData.setSyncState("prepare.request.autodiscover");
         let authData = eas.network.getAuthData(syncData.accountData);
 
         syncData.setSyncState("send.request.autodiscover");
-        let result = await eas.network.getServerConnectionViaAutodiscover(authData.user, authData.password, 30*1000);
+        let result = await eas.network.getServerConnectionViaAutodiscover(authData.accountname, authData.user, authData.password, 30 * 1000);
 
         syncData.setSyncState("eval.response.autodiscover");
         if (result.errorcode == 200) {
             //update account
-            syncData.accountData.setAccountProperty("host", eas.network.stripAutodiscoverUrl(result.server)); 
+            syncData.accountData.setAccountProperty("host", eas.network.stripAutodiscoverUrl(result.server));
             syncData.accountData.setAccountProperty("user", result.user);
-            syncData.accountData.setAccountProperty("https", (result.server.substring(0,5) == "https"));
+            syncData.accountData.setAccountProperty("https", (result.server.substring(0, 5) == "https"));
         }
 
         return result.errorcode;
     },
-    
-    stripAutodiscoverUrl: function(url) {
+
+    stripAutodiscoverUrl: function (url) {
         let u = url;
-        while (u.endsWith("/")) { u = u.slice(0,-1); }
-        if (u.endsWith("/Microsoft-Server-ActiveSync")) u=u.slice(0, -28);
+        while (u.endsWith("/")) { u = u.slice(0, -1); }
+        if (u.endsWith("/Microsoft-Server-ActiveSync")) u = u.slice(0, -28);
         else TbSync.dump("Received non-standard EAS url via autodiscover:", url);
 
         return u.split("//")[1]; //cut off protocol
     },
 
-    getServerConnectionViaAutodiscover : async function (user, password, maxtimeout) {
+    getServerConnectionViaAutodiscover: async function (accountname, user, password, maxtimeout) {
         let urls = [];
         let parts = user.split("@");
-        
-        urls.push({"url":"http://autodiscover."+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
-        urls.push({"url":"http://"+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
-        urls.push({"url":"http://autodiscover."+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
-        urls.push({"url":"http://"+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
-
-        urls.push({"url":"https://autodiscover."+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
-        urls.push({"url":"https://"+parts[1]+"/autodiscover/autodiscover.xml", "user":user});
-        urls.push({"url":"https://autodiscover."+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
-        urls.push({"url":"https://"+parts[1]+"/Autodiscover/Autodiscover.xml", "user":user});
-        
+
+        urls.push({ "url": "https://autodiscover." + parts[1] + "/autodiscover/autodiscover.xml", "user": user });
+        urls.push({ "url": "https://" + parts[1] + "/autodiscover/autodiscover.xml", "user": user });
+        urls.push({ "url": "https://autodiscover." + parts[1] + "/Autodiscover/Autodiscover.xml", "user": user });
+        urls.push({ "url": "https://" + parts[1] + "/Autodiscover/Autodiscover.xml", "user": user });
+
+        urls.push({ "url": "http://autodiscover." + parts[1] + "/autodiscover/autodiscover.xml", "user": user });
+        urls.push({ "url": "http://" + parts[1] + "/autodiscover/autodiscover.xml", "user": user });
+        urls.push({ "url": "http://autodiscover." + parts[1] + "/Autodiscover/Autodiscover.xml", "user": user });
+        urls.push({ "url": "http://" + parts[1] + "/Autodiscover/Autodiscover.xml", "user": user });
+
         let requests = [];
         let responses = []; //array of objects {url, error, server}
-        
-        for (let i=0; i< urls.length; i++) {
+
+        for (let i = 0; i < urls.length; i++) {
             await TbSync.tools.sleep(200);
-            requests.push( eas.network.getServerConnectionViaAutodiscoverRedirectWrapper(urls[i].url, urls[i].user, password, maxtimeout) );
+            requests.push(eas.network.getServerConnectionViaAutodiscoverRedirectWrapper(
+                accountname,
+                urls[i].url,
+                urls[i].user,
+                password,
+                maxtimeout
+            ));
         }
 
         try {
-            responses = await Promise.all(requests); 
+            responses = await Promise.all(requests);
         } catch (e) {
             responses.push(e.result); //this is actually a success, see return value of getServerConnectionViaAutodiscoverRedirectWrapper()
         }
-        
+
         let result;
-        let log = [];        
-        for (let r=0; r < responses.length; r++) {
-            log.push("*  "+responses[r].url+" @ " + responses[r].user +" : " + (responses[r].server ? responses[r].server : responses[r].error));
+        let log = [];
+        for (let r = 0; r < responses.length; r++) {
+            log.push("*  " + responses[r].url + " @ " + responses[r].user + " : " + (responses[r].server ? responses[r].server : responses[r].error));
 
             if (responses[r].server) {
-                result = {"server": responses[r].server, "user": responses[r].user, "error": "", "errorcode": 200};
+                result = { "server": responses[r].server, "user": responses[r].user, "error": "", "errorcode": 200 };
                 break;
             }
-            
+
             if (responses[r].error == 403 || responses[r].error == 401) {
                 //we could still find a valid server, so just store this state
-                result = {"server": "", "user": responses[r].user, "errorcode": responses[r].error, "error": TbSync.getString("status." + responses[r].error, "eas")};
+                result = { "server": "", "user": responses[r].user, "errorcode": responses[r].error, "error": TbSync.getString("status." + responses[r].error, "eas") };
             }
-        } 
-        
+        }
+
         //this is only reached on fail, if no result defined yet, use general error
-        if (!result) { 
-            result = {"server": "", "user": user, "error": TbSync.getString("autodiscover.Failed","eas").replace("##user##", user), "errorcode": 503};
+        if (!result) {
+            result = { "server": "", "user": user, "error": TbSync.getString("autodiscover.Failed", "eas").replace("##user##", user), "errorcode": 503 };
         }
 
         TbSync.eventlog.add("error", new TbSync.EventLogInfo("eas"), result.error, log.join("\n"));
-        return result;        
+        return result;
     },
-       
-    getServerConnectionViaAutodiscoverRedirectWrapper : async function (url, user, password, maxtimeout) {        
-        //using HEAD to find URL redirects until response URL no longer changes 
-        // * XHR should follow redirects transparently, but that does not always work, POST data could get lost, so we
-        // * need to find the actual POST candidates (example: outlook.de accounts)
+
+    getServerConnectionViaAutodiscoverRedirectWrapper: async function (accountname, url, user, password, maxtimeout) {
         let result = {};
-        let method = "HEAD";
-        let connection = { url, user };
-        
-        do {            
+        let method = "POST";
+        let connection = { accountname, url, user };
+
+        do {
             await TbSync.tools.sleep(200);
             result = await eas.network.getServerConnectionViaAutodiscoverRequest(method, connection, password, maxtimeout);
             method = "";
-            
+
             if (result.error == "redirect found") {
-                TbSync.dump("EAS autodiscover URL redirect",  "\n" + connection.url + " @ " + connection.user + " => \n" + result.url + " @ " + result.user);
+                TbSync.dump("EAS autodiscover URL redirect", "\n" + connection.url + " @ " + connection.user + " => \n" + result.url + " @ " + result.user);
                 connection.url = result.url;
                 connection.user = result.user;
-                method = "HEAD";
-            } else if (result.error == "POST candidate found") {
                 method = "POST";
             }
 
         } while (method);
-        
+
         //invert reject and resolve, so we exit the promise group on success right away
         if (result.server) {
             let e = new Error("Not an error (early exit from promise group)");
@@ -1298,13 +1386,13 @@
         } else {
             return result;
         }
-    },    
-    
+    },
+
     getServerConnectionViaAutodiscoverRequest: function (method, connection, password, maxtimeout) {
         TbSync.dump("Querry EAS autodiscover URL", connection.url + " @ " + connection.user);
-        
-        return new Promise(function(resolve,reject) {
-            
+
+        return new Promise(function (resolve, reject) {
+
             let xml = '<?xml version="1.0" encoding="utf-8"?>\r\n';
             xml += '<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/requestschema/2006">\r\n';
             xml += '<Request>\r\n';
@@ -1312,18 +1400,17 @@
             xml += '<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006</AcceptableResponseSchema>\r\n';
             xml += '</Request>\r\n';
             xml += '</Autodiscover>\r\n';
-            
-            let userAgent = eas.prefs.getCharPref("clientID.useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
 
-            // Create request handler - API changed with TB60 to new XMKHttpRequest()
-            let req = new XMLHttpRequest();
+            let userAgent = eas.prefs.getCharPref("clientID.useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
+            let uri = Services.io.newURI(connection.url);
+            let req = getSandBoxedXHR(connection, uri);
             req.mozBackgroundRequest = true;
-            req.open(method, connection.url, true);
+            req.open(method, uri.spec, true);
             req.timeout = maxtimeout;
             req.setRequestHeader("User-Agent", userAgent);
-            
-            let secure = (connection.url.substring(0,8).toLowerCase() == "https://");
-            
+
+            let secure = (connection.url.substring(0, 8).toLowerCase() == "https://");
+
             if (method == "POST") {
                 req.setRequestHeader("Content-Length", xml.length);
                 req.setRequestHeader("Content-Type", "text/xml");
@@ -1331,43 +1418,44 @@
                     // OAUTH accounts cannot authenticate against the standard discovery services
                     // updateServerConnectionViaAutodiscover() is not passing them on
                     req.setRequestHeader("Authorization", "Basic " + TbSync.tools.b64encode(connection.user + ":" + password));
-                }                    
+                }
             }
 
             req.ontimeout = function () {
                 TbSync.dump("EAS autodiscover with timeout", "\n" + connection.url + " => \n" + req.responseURL);
-                resolve({"url":req.responseURL, "error":"timeout", "server":"", "user":connection.user});
+                resolve({ "url": req.responseURL, "error": "timeout", "server": "", "user": connection.user });
             };
-           
+
             req.onerror = function () {
                 let error = TbSync.network.createTCPErrorFromFailedXHR(req);
                 if (!error) error = req.responseText;
-                TbSync.dump("EAS autodiscover with error ("+error+")",  "\n" + connection.url + " => \n" + req.responseURL);
-                resolve({"url":req.responseURL, "error":error, "server":"", "user":connection.user});
-            };
 
-            req.onload = function() { 
-                //initiate rerun on redirects
-                if (req.responseURL != connection.url) {
-                    resolve({"url":req.responseURL, "error":"redirect found", "server":"", "user":connection.user});
+                // CORS violations can happen on redirects. They come back as NS_ERROR_DOM_BAD_URI.
+                if (error == "network::NS_ERROR_DOM_BAD_URI" && req.channel.URI.spec != uri.spec) {
+                    resolve({ "url": req.channel.URI.spec, "error": "redirect found", "server": "", "user": connection.user });
                     return;
                 }
 
-                //initiate rerun on HEAD request without redirect (rerun and do a POST on this)
-                if (method == "HEAD") {
-                    resolve({"url":req.responseURL, "error":"POST candidate found", "server":"", "user":connection.user});
+                TbSync.dump("EAS autodiscover with error (" + error + ")", "\n" + connection.url + " => \n" + req.responseURL);
+                resolve({ "url": req.responseURL, "error": error, "server": "", "user": connection.user });
+            };
+
+            req.onload = function () {
+                //initiate rerun on redirects
+                if (req.responseURL != connection.url) {
+                    resolve({ "url": req.responseURL, "error": "redirect found", "server": "", "user": connection.user });
                     return;
                 }
 
                 //ignore POST without autherization (we just do them to get redirect information)
                 if (!secure) {
-                    resolve({"url":req.responseURL, "error":"unsecure POST", "server":"", "user":connection.user});
+                    resolve({ "url": req.responseURL, "error": "unsecure POST", "server": "", "user": connection.user });
                     return;
                 }
-                
+
                 //evaluate secure POST requests which have not been redirected
-                TbSync.dump("EAS autodiscover POST with status (" + req.status + ")",   "\n" + connection.url + " => \n" + req.responseURL  + "\n[" + req.responseText + "]");
-                
+                TbSync.dump("EAS autodiscover POST with status (" + req.status + ")", "\n" + connection.url + " => \n" + req.responseURL + "\n[" + req.responseText + "]");
+
                 if (req.status === 200) {
                     let data = null;
                     // getDataFromXMLString may throw an error which cannot be catched outside onload,
@@ -1376,16 +1464,16 @@
                     try {
                         data = eas.xmltools.getDataFromXMLString(req.responseText);
                     } catch (e) {
-                        resolve({"url":req.responseURL, "error":"bad response", "server":"", "user":connection.user});
+                        resolve({ "url": req.responseURL, "error": "bad response", "server": "", "user": connection.user });
                         return;
                     }
-            
+
                     if (!(data === null) && data.Autodiscover && data.Autodiscover.Response && data.Autodiscover.Response.Action) {
                         // "Redirect" or "Settings" are possible
                         if (data.Autodiscover.Response.Action.Redirect) {
                             // redirect, start again with new user
                             let newuser = action.Redirect;
-                            resolve({"url":req.responseURL, "error":"redirect found", "server":"", "user":newuser});
+                            resolve({ "url": req.responseURL, "error": "redirect found", "server": "", "user": newuser });
 
                         } else if (data.Autodiscover.Response.Action.Settings) {
                             // get server settings
@@ -1393,67 +1481,64 @@
 
                             for (let count = 0; count < server.length; count++) {
                                 if (server[count].Type == "MobileSync" && server[count].Url) {
-                                    resolve({"url":req.responseURL, "error":"", "server":server[count].Url, "user":connection.user});
+                                    resolve({ "url": req.responseURL, "error": "", "server": server[count].Url, "user": connection.user });
                                     return;
                                 }
                             }
                         }
                     } else {
-                        resolve({"url":req.responseURL, "error":"invalid", "server":"", "user":connection.user});
+                        resolve({ "url": req.responseURL, "error": "invalid", "server": "", "user": connection.user });
                     }
                 } else {
-                    resolve({"url":req.responseURL, "error":req.status, "server":"", "user":connection.user});                     
+                    resolve({ "url": req.responseURL, "error": req.status, "server": "", "user": connection.user });
                 }
             };
-            
-            if (method == "HEAD") req.send();
-            else  req.send(xml);
-            
+
+            req.send(xml);
         });
     },
-    
-    getServerConnectionViaAutodiscoverV2JsonRequest: function (url, maxtimeout) {
+
+    getServerConnectionViaAutodiscoverV2JsonRequest: function (accountname, user, url, maxtimeout) {
         TbSync.dump("Querry EAS autodiscover V2 URL", url);
-        
-        return new Promise(function(resolve,reject) {
-                        
-            let userAgent = eas.prefs.getCharPref("clientID.useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
 
-            // Create request handler - API changed with TB60 to new XMKHttpRequest()
-            let req = new XMLHttpRequest();
+        return new Promise(function (resolve, reject) {
+
+            let userAgent = eas.prefs.getCharPref("clientID.useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
+            let uri = Services.io.newURI(url);
+            let req = getSandBoxedXHR({ accountname, user }, uri);
             req.mozBackgroundRequest = true;
-            req.open("GET", url, true);
+            req.open("GET", uri.spec, true);
             req.timeout = maxtimeout;
             req.setRequestHeader("User-Agent", userAgent);
-            
+
             req.ontimeout = function () {
                 TbSync.dump("EAS autodiscover V2 with timeout", "\n" + url + " => \n" + req.responseURL);
-                resolve({"url":req.responseURL, "error":"timeout", "server":""});
+                resolve({ "url": req.responseURL, "error": "timeout", "server": "" });
             };
-           
+
             req.onerror = function () {
                 let error = TbSync.network.createTCPErrorFromFailedXHR(req);
                 if (!error) error = req.responseText;
-                TbSync.dump("EAS autodiscover V2 with error ("+error+")",  "\n" + url + " => \n" + req.responseURL);
-                resolve({"url":req.responseURL, "error":error, "server":""});
+                TbSync.dump("EAS autodiscover V2 with error (" + error + ")", "\n" + url + " => \n" + req.responseURL);
+                resolve({ "url": req.responseURL, "error": error, "server": "" });
             };
 
-            req.onload = function() { 
+            req.onload = function () {
                 if (req.status === 200) {
                     let data = JSON.parse(req.responseText);
-            
+
                     if (data && data.Url) {
-                        resolve({"url":req.responseURL, "error":"", "server": eas.network.stripAutodiscoverUrl(data.Url)});
+                        resolve({ "url": req.responseURL, "error": "", "server": eas.network.stripAutodiscoverUrl(data.Url) });
                     } else {
-                        resolve({"url":req.responseURL, "error":"invalid", "server":""});
+                        resolve({ "url": req.responseURL, "error": "invalid", "server": "" });
                     }
                     return;
                 }
-                
-                resolve({"url":req.responseURL, "error":req.status, "server":""});                     
+
+                resolve({ "url": req.responseURL, "error": req.status, "server": "" });
             };
-            
-            req.send();            
+
+            req.send();
         });
-    }    
+    }
 }
diff -Nru eas4tbsync-4.8/content/includes/sync.js eas4tbsync-4.11/content/includes/sync.js
--- eas4tbsync-4.8/content/includes/sync.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/sync.js	2024-08-19 20:22:14.000000000 +0200
@@ -9,6 +9,7 @@
 "use strict";
 
 var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
     CalRecurrenceInfo: "resource:///modules/CalRecurrenceInfo.jsm",
diff -Nru eas4tbsync-4.8/content/includes/tasksync.js eas4tbsync-4.11/content/includes/tasksync.js
--- eas4tbsync-4.8/content/includes/tasksync.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/tasksync.js	2024-08-19 20:22:14.000000000 +0200
@@ -9,6 +9,7 @@
 "use strict";
 
 var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
  CalAlarm: "resource:///modules/CalAlarm.jsm",
diff -Nru eas4tbsync-4.8/content/includes/tools.js eas4tbsync-4.11/content/includes/tools.js
--- eas4tbsync-4.8/content/includes/tools.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/tools.js	2024-08-19 20:22:14.000000000 +0200
@@ -6,7 +6,13 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
  */
  
- "use strict";
+"use strict";
+
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
+var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
+var { NetUtil } = ChromeUtils.importESModule(
+    "resource://gre/modules/NetUtil.sys.mjs"
+);
 
 var tools = {
 
@@ -435,7 +441,7 @@
             obj.displayname = "Coordinated Universal Time (UTC)";
             return obj;
         }
-                
+
         //we could parse the icalstring by ourself, but I wanted to use ICAL.parse - TODO try catch
         let info = TbSync.lightning.ICAL.parse("BEGIN:VCALENDAR\r\n" + timezone.icalComponent.toString() + "\r\nEND:VCALENDAR");
         let comp = new TbSync.lightning.ICAL.Component(info);
diff -Nru eas4tbsync-4.8/content/includes/wbxmltools.js eas4tbsync-4.11/content/includes/wbxmltools.js
--- eas4tbsync-4.8/content/includes/wbxmltools.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/wbxmltools.js	2024-08-19 20:22:14.000000000 +0200
@@ -6,8 +6,9 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
  */
  
- "use strict";
+"use strict";
 
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
 var wbxmltools = {
 
     // Convert a WBXML (WAP Binary XML) to plain XML - returns save xml with all special chars in the user data encoded by encodeURIComponent
diff -Nru eas4tbsync-4.8/content/includes/xmltools.js eas4tbsync-4.11/content/includes/xmltools.js
--- eas4tbsync-4.8/content/includes/xmltools.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/includes/xmltools.js	2024-08-19 20:22:14.000000000 +0200
@@ -6,8 +6,9 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
  */
  
- "use strict";
+"use strict";
 
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
 var xmltools = {
 
     isString : function (obj) {
diff -Nru eas4tbsync-4.8/content/manager/createAccount.js eas4tbsync-4.11/content/manager/createAccount.js
--- eas4tbsync-4.8/content/manager/createAccount.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/manager/createAccount.js	2024-08-19 20:22:14.000000000 +0200
@@ -156,7 +156,11 @@
             updateTimer.initWithCallback({notify : function () {tbSyncEasNewAccount.updateAutodiscoverStatus()}}, 1000, 3);
 
             if (servertype == "office365") {
-                let v2 = await eas.network.getServerConnectionViaAutodiscoverV2JsonRequest("https://autodiscover-s.outlook.com/autodiscover/autodiscover.json?Email="+encodeURIComponent(user)+"&Protocol=ActiveSync");
+                let v2 = await eas.network.getServerConnectionViaAutodiscoverV2JsonRequest(
+                    accountname,
+                    user,
+                    "https://autodiscover-s.outlook.com/autodiscover/autodiscover.json?Email="+encodeURIComponent(user)+"&Protocol=ActiveSync",
+                );
                 let oauthData = eas.network.getOAuthObj({ host: v2.server, user, accountname, servertype });
                 if (oauthData) {
                     // ask for token
@@ -173,7 +177,12 @@
                     error = TbSync.getString("status.404", "eas");
                 }
             } else {
-                let result = await eas.network.getServerConnectionViaAutodiscover(user, password, tbSyncEasNewAccount.maxTimeout*1000);
+                let result = await eas.network.getServerConnectionViaAutodiscover(
+                    accountname,
+                    user,
+                    password,
+                    tbSyncEasNewAccount.maxTimeout*1000
+                );
                 if (result.server) {
                     user = result.user;
                     url = result.server;
@@ -192,7 +201,7 @@
 
         //add if valid
         if (!error) {
-            tbSyncEasNewAccount.addAccount(user, password, servertype, accountname, url);
+            await tbSyncEasNewAccount.addAccount(user, password, servertype, accountname, url);
         }
         
         //end validation
@@ -214,6 +223,7 @@
             document.getElementById("tbsync.error.message").textContent = error;
             document.getElementById("tbsync.error").hidden = false;
         }            
+        window.sizeToContent();
     },
 
     updateAutodiscoverStatus: function () {
@@ -223,7 +233,7 @@
         document.getElementById('tbsync.newaccount.autodiscoverstatus').value  = TbSync.getString("autodiscover.Querying","eas") + timeout;
     },
 
-    addAccount (user, password, servertype, accountname, url) {
+    async addAccount (user, password, servertype, accountname, url) {
         let newAccountEntry = this.providerData.getDefaultAccountEntries();
         newAccountEntry.user = user;
         newAccountEntry.servertype = servertype;
@@ -237,7 +247,7 @@
 
         // Add the new account.
         let newAccountData = this.providerData.addAccount(accountname, newAccountEntry);
-        eas.network.getAuthData(newAccountData).updateLoginData(user, password);
+        await eas.network.getAuthData(newAccountData).updateLoginData(user, password);
 
         window.close();
     }
diff -Nru eas4tbsync-4.8/content/manager/createAccount.xhtml eas4tbsync-4.11/content/manager/createAccount.xhtml
--- eas4tbsync-4.8/content/manager/createAccount.xhtml	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/manager/createAccount.xhtml	2024-08-19 20:22:14.000000000 +0200
@@ -79,7 +79,7 @@
                 <label id='tbsync.newaccount.autodiscoverstatus' value="" />
             </hbox>
            
-            <vbox id="tbsync.error" style="width: 450px;">
+            <vbox id="tbsync.error">
                 <description id="tbsync.error.message" flex="1" style="font-weight: bold;"></description>
                 <vbox>
                   <button 
diff -Nru eas4tbsync-4.8/content/provider.js eas4tbsync-4.11/content/provider.js
--- eas4tbsync-4.8/content/provider.js	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/content/provider.js	2024-08-19 20:22:14.000000000 +0200
@@ -8,6 +8,9 @@
 
 "use strict";
 
+var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm");
+var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
+
 // Every object in here will be loaded into TbSync.providers.<providername>.
 const eas = TbSync.providers.eas;
 
diff -Nru eas4tbsync-4.8/debian/changelog eas4tbsync-4.11/debian/changelog
--- eas4tbsync-4.8/debian/changelog	2024-05-15 12:46:39.000000000 +0200
+++ eas4tbsync-4.11/debian/changelog	2024-09-18 09:43:23.000000000 +0200
@@ -1,3 +1,17 @@
+eas4tbsync (4.11-1~deb12u1) bookworm; urgency=medium
+
+  * [6c3dc26] Merge branch 'debian/sid' into debian/bookworm
+  * Prepared for release in bookworm (proposed-updates)
+
+ -- Mechtilde Stehmann <mechtilde at debian.org>  Wed, 18 Sep 2024 09:43:23 +0200
+
+eas4tbsync (4.11-1) unstable; urgency=medium
+
+  * [6db438f] New upstream version 4.11
+  * [d895bd7] Bumped version of dependencies in d/control
+
+ -- Mechtilde Stehmann <mechtilde at debian.org>  Sun, 01 Sep 2024 12:49:21 +0200
+
 eas4tbsync (4.8-1) unstable; urgency=medium
 
   * [cf34002] New upstream version 4.8
@@ -7,6 +21,13 @@
 
  -- Mechtilde Stehmann <mechtilde at debian.org>  Wed, 15 May 2024 12:46:39 +0200
 
+eas4tbsync (4.7-1~deb12u1) bookworm; urgency=medium
+
+  * [4af7393] Adjust version of dependencies
+  *     Prepared for release in bookworm (proposed-updates)
+
+ -- Mechtilde Stehmann <mechtilde at debian.org>  Sun, 22 Oct 2023 12:57:50 +0200
+
 eas4tbsync (4.7-1) unstable; urgency=medium
 
   * [a18dc06] Changed compression for tar.gz
diff -Nru eas4tbsync-4.8/debian/control eas4tbsync-4.11/debian/control
--- eas4tbsync-4.8/debian/control	2024-05-15 12:41:12.000000000 +0200
+++ eas4tbsync-4.11/debian/control	2024-09-14 19:13:29.000000000 +0200
@@ -15,8 +15,9 @@
 Package: webext-eas4tbsync
 Architecture: all
 Depends: ${misc:Depends}
- , thunderbird (>= 1:115.10)
- , webext-tbsync (>= 4.8)
+ , thunderbird (>= 1:128.0)
+ , thunderbird (<= 1:128.x)
+ , webext-tbsync (>= 4.12)
 Description: Provide Exchange ActiveSync (EAS v2.5 & v14.0) synchronization capabilities
  The Exchange ActiveSync provider for TbSync to sync contacts, tasks and
  calendars to Thunderbird.
diff -Nru eas4tbsync-4.8/_locales/ja/messages.json eas4tbsync-4.11/_locales/ja/messages.json
--- eas4tbsync-4.8/_locales/ja/messages.json	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/_locales/ja/messages.json	2024-08-19 20:22:14.000000000 +0200
@@ -60,13 +60,13 @@
         "message": "?????? 3:"
     },
     "abCard.header.messaging": {
-        "message": "Messaging:"
+        "message": "???????:"
     },
     "abCard.header.otheraddress": {
         "message": "???????? (EAS)"
     },
     "abCard.header.othernumbers": {
-        "message": "Additional numbers:"
+        "message": "???????:"
     },
     "abCard.header.people": {
         "message": "People:"
@@ -279,7 +279,7 @@
         "message": "???????????? (HTTP ??? 404)?"
     },
     "status.449": {
-        "message": "Server requests provisioning (HTTP Error 449)."
+        "message": "????????????????????? (HTTP ??? 449)?"
     },
     "status.500": {
         "message": "??????? ??? (HTTP ??? 500)?"
@@ -441,7 +441,7 @@
         "message": "Sync failed. Server responded with status <##replace.1##>."
     },
     "status.wbxmlmissingfield": {
-        "message": "ActiveSync protocol violation: Mandatory field <##replace.1##> is missing from server response."
+        "message": "ActiveSync ???????: ??????? <##replace.1##> ?????????????????"
     },
     "syncstate.accountdone": {
         "message": "Finished account"
@@ -513,7 +513,7 @@
         "message": "Requesting remote changes"
     },
     "syncstate.prepare.request.revertlocalchanges": {
-        "message": "Collecting local changes"
+        "message": "???????????"
     },
     "syncstate.prepare.request.setdeviceinfo": {
         "message": "??????????"
@@ -534,13 +534,13 @@
         "message": "Waiting for change estimate"
     },
     "syncstate.send.request.folders": {
-        "message": "Waiting for folder list update"
+        "message": "??????????????"
     },
     "syncstate.send.request.localchanges": {
-        "message": "Waiting for acknowledgment of local changes"
+        "message": "??????????????"
     },
     "syncstate.send.request.localdeletes": {
-        "message": "Waiting for acknowledgment of local deletes"
+        "message": "??????????????"
     },
     "syncstate.send.request.options": {
         "message": "Waiting for server options"
@@ -549,7 +549,7 @@
         "message": "Waiting for provision"
     },
     "syncstate.send.request.remotechanges": {
-        "message": "Waiting for remote changes"
+        "message": "???????????"
     },
     "syncstate.send.request.revertlocalchanges": {
         "message": "Waiting for most recent versions"
@@ -558,9 +558,9 @@
         "message": "??????????"
     },
     "syncstate.send.request.synckey": {
-        "message": "Waiting for SyncKey"
+        "message": "????????"
     },
     "syncstate.syncing": {
-        "message": "Initialize synchronization"
+        "message": "???????"
     }
 }
diff -Nru eas4tbsync-4.8/manifest.json eas4tbsync-4.11/manifest.json
--- eas4tbsync-4.8/manifest.json	2023-12-03 00:43:29.000000000 +0100
+++ eas4tbsync-4.11/manifest.json	2024-08-19 20:22:14.000000000 +0200
@@ -2,13 +2,13 @@
   "applications": {
     "gecko": {
       "id": "eas4tbsync at jobisoft.de",
-      "strict_min_version": "102.7.0",
-      "strict_max_version": "115.*"
+      "strict_min_version": "128.0",
+      "strict_max_version": "128.*"
     }
   },
   "manifest_version": 2,
   "name": "__MSG_extensionName__",
-  "version": "4.8",
+  "version": "4.11",
   "author": "John Bieling",
   "homepage_url": "https://github.com/jobisoft/EAS-4-TbSync/",
   "default_locale": "en-US",


More information about the Pkg-mozext-maintainers mailing list