[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1054363: marked as done (bookworm-pu: package eas4tbsync)



Your message dated Sat, 09 Dec 2023 10:20:37 +0000
with message-id <83d3a3621a56b9af1e20d36ee9d390a46ab64a8a.camel@adam-barratt.org.uk>
and subject line Closing requests for updates included in 12.3 point release
has caused the Debian Bug report #1054363,
regarding bookworm-pu: package eas4tbsync
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1054363: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1054363
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: eas4tbsync@packages.debian.org, mechtilde@debian.org
Control: affects -1 + src:eas4tbsync

[ Reason ]

This package is a dependency of the extension tbsync to thunderbird. After thunderbird is updated to version 115.* in bookwork it is necessary to update this extension too.

[ Impact ]

Otherwise this extension doesn't work anymore

[ Risks ]

Only tbsync, dav4tbsync and eas4tbsync are affected and will be updated too

[ 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 ]

There is a new version of dav4tbsync to work with thunderbird 115.x

[ Other info ]

The output of debdiff (stable vs stable-pu is attached)

Tbsync and dav4tbsync are already updated

Kind regards to the release team

Mechtilde

--
Mechtilde Stehmann
## Debian Developer
## PGP encryption welcome
## F0E3 7F3D C87A 4998 2899  39E7 F287 7BBA 141A AD7F
diffstat for eas4tbsync-4.1.5 eas4tbsync-4.7

 Makefile                                      |   25 
 background.js                                 |    2 
 content/api/BootstrapLoader/CHANGELOG.md      |   15 
 content/api/BootstrapLoader/implementation.js |  676 ++++++++++++++++++++------
 content/api/BootstrapLoader/schema.json       |    4 
 content/bootstrap.js                          |   10 
 content/includes/calendarsync.js              |   13 
 content/includes/network.js                   |   25 
 content/includes/tasksync.js                  |   32 +
 content/includes/tools.js                     |   16 
 content/manager/createAccount.xhtml           |    2 
 content/provider.js                           |   21 
 debian/changelog                              |   22 
 debian/control                                |    4 
 debian/gbp.conf                               |    2 
 manifest.json                                 |    6 
 16 files changed, 677 insertions(+), 198 deletions(-)

diff -Nru eas4tbsync-4.1.5/background.js eas4tbsync-4.7/background.js
--- eas4tbsync-4.1.5/background.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/background.js	2023-08-18 16:51:43.000000000 +0200
@@ -16,7 +16,7 @@
     let manifest = browser.runtime.getManifest();
     browser.notifications.create({
       type: "basic",
-      iconUrl: browser.runtime.getURL("content/skin/sabredav32.png"),
+      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.",
     });
diff -Nru eas4tbsync-4.1.5/content/api/BootstrapLoader/CHANGELOG.md eas4tbsync-4.7/content/api/BootstrapLoader/CHANGELOG.md
--- eas4tbsync-4.1.5/content/api/BootstrapLoader/CHANGELOG.md	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/api/BootstrapLoader/CHANGELOG.md	2023-08-18 16:51:43.000000000 +0200
@@ -1,3 +1,18 @@
+Version: 1.21
+-------------
+- Explicitly set hasAddonManagerEventListeners flag to false on uninstall
+
+Version: 1.20
+-------------
+- hard fork BootstrapLoader v1.19 implementation and continue to serve it for
+  Thunderbird 111 and older
+- BootstrapLoader v1.20 has removed a lot of unnecessary code used for backward
+  compatibility
+
+Version: 1.19
+-------------
+- fix race condition which could prevent the AOM tab to be monkey patched correctly
+
 Version: 1.18
 -------------
 - be precise on which revision the wrench symbol should be displayed, instead of
diff -Nru eas4tbsync-4.1.5/content/api/BootstrapLoader/implementation.js eas4tbsync-4.7/content/api/BootstrapLoader/implementation.js
--- eas4tbsync-4.1.5/content/api/BootstrapLoader/implementation.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/api/BootstrapLoader/implementation.js	2023-08-18 16:51:43.000000000 +0200
@@ -2,7 +2,7 @@
  * This file is provided by the addon-developer-support repository at
  * https://github.com/thundernest/addon-developer-support
  *
- * Version: 1.18
+ * Version: 1.21
  *
  * Author: John Bieling (john@thunderbird.net)
  *
@@ -17,70 +17,65 @@
 var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-var BootstrapLoader = class extends ExtensionCommon.ExtensionAPI {
-  getMessenger(context) {   
-    let apis = [
-      "storage",
-      "runtime",
-      "extension",
-      "i18n",
-    ];
-
-    function getStorage() {
-      let localstorage = null;
-      try {
-        localstorage = context.apiCan.findAPIPath("storage");
-        localstorage.local.get = (...args) =>
-          localstorage.local.callMethodInParentProcess("get", args);
-        localstorage.local.set = (...args) =>
-          localstorage.local.callMethodInParentProcess("set", args);
-        localstorage.local.remove = (...args) =>
-          localstorage.local.callMethodInParentProcess("remove", args);
-        localstorage.local.clear = (...args) =>
-          localstorage.local.callMethodInParentProcess("clear", args);
-      } catch (e) {
-        console.info("Storage permission is missing");
-      }
-      return localstorage;
-    }
-    
-    let messenger = {};    
-    for (let api of apis) {
-      switch (api) {
-        case "storage":
-          XPCOMUtils.defineLazyGetter(messenger, "storage", () =>
-            getStorage()
-          );
-        break;
+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,
+  }
+}
 
-        default:
-          XPCOMUtils.defineLazyGetter(messenger, api, () =>
-            context.apiCan.findAPIPath(api)
-          );
-      }
+function getMessenger(context) {
+  let apis = ["storage", "runtime", "extension", "i18n"];
+
+  function getStorage() {
+    let localstorage = null;
+    try {
+      localstorage = context.apiCan.findAPIPath("storage");
+      localstorage.local.get = (...args) =>
+        localstorage.local.callMethodInParentProcess("get", args);
+      localstorage.local.set = (...args) =>
+        localstorage.local.callMethodInParentProcess("set", args);
+      localstorage.local.remove = (...args) =>
+        localstorage.local.callMethodInParentProcess("remove", args);
+      localstorage.local.clear = (...args) =>
+        localstorage.local.callMethodInParentProcess("clear", args);
+    } catch (e) {
+      console.info("Storage permission is missing");
     }
-    return messenger;
+    return localstorage;
   }
 
-  getThunderbirdVersion() {
-    let parts = Services.appinfo.version.split(".");
-    return {
-      major: parseInt(parts[0]),
-      minor: parseInt(parts[1]),
-      revision: parts.length > 2 ? parseInt(parts[2]) : 0,
+  let messenger = {};
+  for (let api of apis) {
+    switch (api) {
+      case "storage":
+        XPCOMUtils.defineLazyGetter(messenger, "storage", () =>
+          getStorage()
+        );
+        break;
+
+      default:
+        XPCOMUtils.defineLazyGetter(messenger, api, () =>
+          context.apiCan.findAPIPath(api)
+        );
     }
   }
-  
+  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 (this.getThunderbirdVersion().major < 86) {
+    if (getThunderbirdVersion().major < 86) {
       let ownerDoc = e.document || e.target.ownerDocument;
       doc = ownerDoc.getElementById("html-view-browser").contentDocument;
-    } else if (this.getThunderbirdVersion().major < 87) {
+    } else if (getThunderbirdVersion().major < 87) {
       let ownerDoc = e.document || e.target;
       doc = ownerDoc.getElementById("html-view-browser").contentDocument;
     } else {
@@ -88,42 +83,42 @@
     }
     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 
+    let iconSizes = this.extension.manifest.icons
       ? Object.keys(this.extension.manifest.icons)
       : [];
-    iconSizes.sort((a,b)=>a-b);
+    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}" />`);
-    
+        `<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) {   
+  handleEvent(e) {
     switch (e.type) {
       // 68 add-on options menu showing
       case "popupshowing": {
         this.add68PrefsEntry(e);
       }
-      break;
+        break;
 
       // 78/88 add-on options menu/button click
       case "click": {
@@ -131,31 +126,31 @@
         e.stopPropagation();
         let BL = {}
         BL.extension = this.extension;
-        BL.messenger = this.getMessenger(this.context);
+        BL.messenger = getMessenger(this.context);
         let w = Services.wm.getMostRecentWindow("mail:3pane");
-        w.openDialog(this.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);        
+        w.openDialog(this.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);
       }
-      break;
-      
+        break;
+
       // 68 add-on options menu command
       case "command": {
         let BL = {}
         BL.extension = this.extension;
-        BL.messenger = this.getMessenger(this.context);
+        BL.messenger = getMessenger(this.context);
         e.target.ownerGlobal.openDialog(this.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);
       }
-      break;
-      
+        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 = 
-              (this.getThunderbirdVersion().major > 78 && this.getThunderbirdVersion().major < 88) ||
-              (this.getThunderbirdVersion().major == 78 && this.getThunderbirdVersion().minor < 10) ||
-              (this.getThunderbirdVersion().major == 78 && this.getThunderbirdVersion().minor == 10 && this.getThunderbirdVersion().revision < 2);
+            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");
@@ -204,10 +199,10 @@
           }
         }
       }
-    }      
-  }  
-  
-// Some tab/add-on-manager related functions
+    }
+  }
+
+  // Some tab/add-on-manager related functions
   getTabMail(window) {
     return window.document.getElementById("tabmail");
   }
@@ -215,7 +210,7 @@
   // 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) {
+    if (tab.browser && tab.mode.name == "contentTab") {
       let win = tab.browser.contentWindow;
       if (win && win.location.href == "about:addons") {
         return win;
@@ -226,9 +221,28 @@
   getAddonManagerFromWindow(window) {
     let tabMail = this.getTabMail(window);
     for (let tab of tabMail.tabInfo) {
-      let win = this.getAddonManagerFromTab(tab)
-      if (win) {
-        return win;
+      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;
+        }
       }
     }
   }
@@ -237,15 +251,16 @@
     if (!managerWindow) {
       return;
     }
-    if (managerWindow 
-          && managerWindow[this.uniqueRandomID]
-          && managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners
+    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.document.addEventListener("view-loaded", this);
     managerWindow[this.uniqueRandomID] = {};
     managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = true;
     if (forceLoad) {
@@ -262,13 +277,13 @@
     this.pathToOptionsPage = null;
     this.chromeHandle = null;
     this.chromeData = null;
-    this.resourceData = 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 = this.getMessenger(this.context);
+    this.bootstrappedObj.messenger = getMessenger(this.context);
 
     this.BOOTSTRAP_REASONS = {
       APP_STARTUP: 1,
@@ -283,49 +298,413 @@
 
     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(aTab) {},
-      onTabClosing(aTab) {},
-      onTabPersist(aTab) {},
-      onTabRestored(aTab) {},
-      onTabSwitched(aNewTab, aOldTab) {
-        //self.setupAddonManager(self.getAddonManagerFromTab(aNewTab));
+      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));
+        }
       },
-      async onTabOpened(aTab) {
-        if (aTab.browser) {
-          if (!aTab.pageLoaded) {
-            // await a location change if browser is not loaded yet
-            await new Promise(resolve => {
-              let reporterListener = {
-                QueryInterface: ChromeUtils.generateQI([
-                  "nsIWebProgressListener",
-                  "nsISupportsWeakReference",
-                ]),
-                onStateChange() {},
-                onProgressChange() {},
-                onLocationChange(
-                    /* in nsIWebProgress*/ aWebProgress,
-                    /* in nsIRequest*/ aRequest,
-                    /* in nsIURI*/ aLocation
-                ) {
-                  aTab.browser.removeProgressListener(reporterListener);  
-                  resolve();
-                },
-                onStatusChange() {},
-                onSecurityChange() {},
-                onContentBlockingEvent() {}
-              }          
-              aTab.browser.addProgressListener(reporterListener);  
+    };
+
+    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) {
+              }
             });
           }
-          // Setup the ViewChange event listener in the outer browser of the add-on,
-          // but do not actually add the button/menu, as the inner browser is not yet ready,
-          // let the ViewChange event do it
-          self.setupAddonManager(self.getAddonManagerFromTab(aTab));
+        }
+      }
+    };
+  }
+
+  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);
+    }
+  }
+
+  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));
         }
       },
     };
@@ -343,8 +722,8 @@
           let window = context.extension.windowManager.get(windowId, context).window
           let BL = {}
           BL.extension = self.extension;
-          BL.messenger = self.getMessenger(self.context);
-          window.openDialog(self.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);        
+          BL.messenger = getMessenger(self.context);
+          window.openDialog(self.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", BL);
         },
 
         registerChromeUrl(data) {
@@ -382,7 +761,7 @@
           self.resourceData = resourceData;
         },
 
-        registerBootstrapScript: async function(aPath) {
+        registerBootstrapScript: async function (aPath) {
           self.pathToBootstrapScript = aPath.startsWith("chrome://")
             ? aPath
             : context.extension.rootURI.resolve(aPath);
@@ -404,32 +783,30 @@
           } 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",              
+                "chrome://messenger/content/messenger.xhtml",
               ],
               async onLoadWindow(window) {
-                if (self.getThunderbirdVersion().major < 78) {
+                if (getThunderbirdVersion().major < 78) {
                   let element_addonPrefs = window.document.getElementById(self.menu_addonPrefs_id);
                   element_addonPrefs.addEventListener("popupshowing", self);
                 } else {
-                  // Setup the options button/menu in the add-on manager, if it is already open.
-                  self.setupAddonManager(
-                    self.getAddonManagerFromWindow(window),
-                    true
-                  );
                   // 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) {          
+              onUnloadWindow(window) {
               }
             });
           }
@@ -442,11 +819,11 @@
     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 (this.getThunderbirdVersion().major < 78) {
+        if (getThunderbirdVersion().major < 78) {
           let element_addonPrefs = window.document.getElementById(this.menu_addonPrefs_id);
           element_addonPrefs.removeEventListener("popupshowing", this);
           // Remove our entry.
@@ -454,19 +831,24 @@
           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";
-          }              
+            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) {
+          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 (this.getThunderbirdVersion().major < 88) {
+            if (getThunderbirdVersion().major < 88) {
               // Remove options menu in 78-87
               for (let card of cards) {
                 let addonOptionsLegacyEntry = card.querySelector(".extension-options-legacy");
@@ -483,13 +865,13 @@
               }
             }
           }
-          
+
           // Remove tabmonitor
           if (window[this.uniqueRandomID].hasTabMonitor) {
             this.getTabMail(window).unregisterTabMonitor(this.tabMonitor);
             window[this.uniqueRandomID].hasTabMonitor = false;
           }
-                    
+
         }
       }
       // Stop listening for new windows.
@@ -529,3 +911,7 @@
     console.log("BootstrapLoader for " + this.extension.id + " unloaded!");
   }
 };
+
+var BootstrapLoader = getThunderbirdVersion().major < 111
+  ? BootstrapLoader_102
+  : BootstrapLoader_115;
diff -Nru eas4tbsync-4.1.5/content/api/BootstrapLoader/schema.json eas4tbsync-4.7/content/api/BootstrapLoader/schema.json
--- eas4tbsync-4.1.5/content/api/BootstrapLoader/schema.json	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/api/BootstrapLoader/schema.json	2023-08-18 16:51:43.000000000 +0200
@@ -35,7 +35,7 @@
             "type": "array",
             "items": {
               "type": "array",
-              "items" : {
+              "items": {
                 "type": "string"
               }
             },
@@ -58,4 +58,4 @@
       }
     ]
   }
-]
+]
\ Kein Zeilenumbruch am Dateiende.
diff -Nru eas4tbsync-4.1.5/content/bootstrap.js eas4tbsync-4.7/content/bootstrap.js
--- eas4tbsync-4.1.5/content/bootstrap.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/bootstrap.js	2023-08-18 16:51:43.000000000 +0200
@@ -32,14 +32,8 @@
 
     Services.obs.addObserver(onInitDoneObserver, "tbsync.observer.initialized", false);
 
-    // The startup of TbSync is delayed until all add-ons have called their startup(),
-    // so all providers have registered the "tbsync.observer.initialized" observer.
-    // Once TbSync has finished its startup, all providers will be notified (also if
-    // TbSync itself is restarted) to load themself.
-    // If this is not startup, we need load manually.
-    if (reason != APP_STARTUP) {
-        onInitDoneObserver.observe();
-    }
+    // Did we miss the observer?
+    onInitDoneObserver.observe();
 }
 
 function shutdown(data, reason) {
diff -Nru eas4tbsync-4.1.5/content/includes/calendarsync.js eas4tbsync-4.7/content/includes/calendarsync.js
--- eas4tbsync-4.1.5/content/includes/calendarsync.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/includes/calendarsync.js	2023-08-18 16:51:43.000000000 +0200
@@ -348,11 +348,10 @@
         //C can be reconstucted from TB STATUS
         //O can be reconstructed by looking at the original value, or (if not present) by comparing EAS ownerID with TB ownerID
 
-        let countAttendees = {};
-        let attendees = item.getAttendees(countAttendees);
-        //if (!(isException && asversion == "2.5")) { //MeetingStatus is not supported in exceptions in EAS 2.5        
+        let attendees = item.getAttendees();
+        //if (!(isException && asversion == "2.5")) { //MeetingStatus is not supported in exceptions in EAS 2.5
         if (!isException) { //Exchange 2010 does not seem to support MeetingStatus at all in exceptions
-            if (countAttendees == 0) wbxml.atag("MeetingStatus", "0");
+            if (attendees.length == 0) wbxml.atag("MeetingStatus", "0");
             else {
                 //get owner information
                 let isReceived = false;
@@ -369,11 +368,11 @@
                 }
             }
         }
-        
+
         //Attendees
-        let TB_responseType = null;        
+        let TB_responseType = null;
         if (!(isException && asversion == "2.5")) { //attendees are not supported in exceptions in EAS 2.5
-            if (countAttendees.value > 0) {
+            if (attendees.length > 0) { //We should use it instead of countAttendees.value
                 wbxml.otag("Attendees");
                     for (let attendee of attendees) {
                         wbxml.otag("Attendee");
diff -Nru eas4tbsync-4.1.5/content/includes/network.js eas4tbsync-4.7/content/includes/network.js
--- eas4tbsync-4.1.5/content/includes/network.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/includes/network.js	2023-08-18 16:51:43.000000000 +0200
@@ -76,16 +76,15 @@
       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";,
-          // changed in beta 1.14.1, according to
-          // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#default-and-consent
-          scope : "offline_access https://outlook.office.com/.default";, //"offline_access https://outlook.office.com/EAS.AccessAsUser.All";,
-          client_id : "2980deeb-7460-4723-864a-f9b0f10cd992",
+          client_id : customID != "" ? customID : "2980deeb-7460-4723-864a-f9b0f10cd992",
         }
         break;
       
@@ -93,7 +92,21 @@
         return null;
     }
 
-    let oauth = new OAuth2(config.auth_uri, config.token_uri, config.scope, config.client_id, config.client_secret);
+    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
@@ -691,7 +704,7 @@
                 wbxml.otag("Set");
                     wbxml.atag("Model", "Computer");
                     wbxml.atag("FriendlyName", "TbSync on Device " + syncData.accountData.getAccountProperty("deviceId").substring(4));
-                    wbxml.atag("OS", OS.Constants.Sys.Name);
+                    wbxml.atag("OS", Services.appinfo.OS);
                     wbxml.atag("UserAgent", syncData.accountData.getAccountProperty("useragent"));
                 wbxml.ctag();
             wbxml.ctag();
diff -Nru eas4tbsync-4.1.5/content/includes/tasksync.js eas4tbsync-4.7/content/includes/tasksync.js
--- eas4tbsync-4.1.5/content/includes/tasksync.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/includes/tasksync.js	2023-08-18 16:51:43.000000000 +0200
@@ -73,14 +73,38 @@
         eas.sync.mapEasPropertyToThunderbird ("Sensitivity", "CLASS", data, item);
         eas.sync.mapEasPropertyToThunderbird ("Importance", "PRIORITY", data, item);
 
+        let msTodoCompat = eas.prefs.getBoolPref("msTodoCompat");
+        
         item.clearAlarms();
-        if (data.ReminderSet && data.ReminderTime && data.UtcStartDate) {        
-            let UtcDate = eas.tools.createDateTime(data.UtcStartDate);
+        if (data.ReminderSet && data.ReminderTime) {
             let UtcAlarmDate = eas.tools.createDateTime(data.ReminderTime);
             let alarm = new CalAlarm();
-            alarm.related = Components.interfaces.calIAlarm.ALARM_RELATED_START; //TB saves new alarms as offsets, so we add them as such as well
-            alarm.offset = UtcAlarmDate.subtractDate(UtcDate);
             alarm.action = "DISPLAY";
+            
+            if (msTodoCompat)
+            {
+                // Microsoft To-Do only uses due dates (no start dates) an doesn't have a time part in the due date
+                // dirty hack: Use the reminder date as due date and set a reminder exactly to the due date
+                // drawback: only works if due date and reminder is set to the same day - this could maybe checked here but I don't know how
+                item.entryDate = UtcAlarmDate;
+                item.dueDate = UtcAlarmDate;
+                alarm.related = Components.interfaces.calIAlarm.ALARM_RELATED_START;
+                alarm.offset = TbSync.lightning.cal.createDuration();
+                alarm.offset.inSeconds = 0;
+            }
+            else if (data.UtcStartDate)
+            {
+                let UtcDate = eas.tools.createDateTime(data.UtcStartDate);
+                alarm.related = Components.interfaces.calIAlarm.ALARM_RELATED_START;
+                alarm.offset = UtcAlarmDate.subtractDate(UtcDate);
+            }
+            else
+            {
+                // Alternative solution for Microsoft To-Do:
+                // alarm correctly set but because time part of due date is always "0:00", all tasks for today are shown as overdue
+                alarm.related = Components.interfaces.calIAlarm.ALARM_RELATED_ABSOLUTE;
+                alarm.alarmDate = UtcAlarmDate;
+            }
             item.addAlarm(alarm);
         }
         
diff -Nru eas4tbsync-4.1.5/content/includes/tools.js eas4tbsync-4.7/content/includes/tools.js
--- eas4tbsync-4.1.5/content/includes/tools.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/includes/tools.js	2023-08-18 16:51:43.000000000 +0200
@@ -303,10 +303,8 @@
         if (test.timezoneOffset/-60 == curOffset) return test.timezone;
         
         //third try all others
-        let enumerator = tzService.timezoneIds;
-        while (enumerator.hasMore()) {
-            let id = enumerator.getNext();
-            let test = utcDateTime.getInTimezone(tzService.getTimezone(id));
+        for (let timezoneId of tzService.timezoneIds) {
+            let test = utcDateTime.getInTimezone(tzService.getTimezone(timezoneId));
             TbSync.dump("Matching TZ via current offset: " + test.timezone.tzid + " @ " + curOffset, test.timezoneOffset/-60);
             if (test.timezoneOffset/-60 == curOffset) return test.timezone;
         }
@@ -334,17 +332,15 @@
                 let tzService = TbSync.lightning.cal.timezoneService;
 
                 //cache timezones data from internal IANA data
-                let enumerator = tzService.timezoneIds;
-                while (enumerator.hasMore()) {
-                    let id = enumerator.getNext();
-                    let timezone = tzService.getTimezone(id);
+                for (let timezoneId of tzService.timezoneIds) {
+                    let timezone = tzService.getTimezone(timezoneId);
                     let tzInfo = eas.tools.getTimezoneInfo(timezone);
 
                     eas.cachedTimezoneData.bothOffsets[tzInfo.std.offset+":"+tzInfo.dst.offset] = timezone;
                     eas.cachedTimezoneData.stdOffset[tzInfo.std.offset] = timezone;
 
-                    eas.cachedTimezoneData.abbreviations[tzInfo.std.abbreviation] = id;
-                    eas.cachedTimezoneData.iana[id] = tzInfo;
+                    eas.cachedTimezoneData.abbreviations[tzInfo.std.abbreviation] = timezoneId;
+                    eas.cachedTimezoneData.iana[timezoneId] = tzInfo;
                     
                     //TbSync.dump("TZ ("+ tzInfo.std.id + " :: " + tzInfo.dst.id +  " :: " + tzInfo.std.displayname + " :: " + tzInfo.dst.displayname + " :: " + tzInfo.std.offset + " :: " + tzInfo.dst.offset + ")", tzService.getTimezone(id));
                 }
diff -Nru eas4tbsync-4.1.5/content/manager/createAccount.xhtml eas4tbsync-4.7/content/manager/createAccount.xhtml
--- eas4tbsync-4.1.5/content/manager/createAccount.xhtml	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/manager/createAccount.xhtml	2023-08-18 16:51:43.000000000 +0200
@@ -2,8 +2,6 @@
 <?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
 
 <window
-    width="500"
-    height="600"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     xmlns:html="http://www.w3.org/1999/xhtml";
     onload="tbSyncEasNewAccount.onLoad();"
diff -Nru eas4tbsync-4.1.5/content/provider.js eas4tbsync-4.7/content/provider.js
--- eas4tbsync-4.1.5/content/provider.js	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/content/provider.js	2023-08-18 16:51:43.000000000 +0200
@@ -38,8 +38,10 @@
         let branch = Services.prefs.getDefaultBranch("extensions.eas4tbsync.");
         branch.setIntPref("timeout", 90000);
         branch.setIntPref("maxitems", 50);
+        branch.setBoolPref("msTodoCompat", false);
         branch.setCharPref("clientID.type", "TbSync");
         branch.setCharPref("clientID.useragent", "Thunderbird ActiveSync");    
+        branch.setCharPref("oauth.clientID", "");
 
         eas.defaultTimezone = null;
         eas.utcTimezone = null;
@@ -102,18 +104,15 @@
             }
 
             let tzService = TbSync.lightning.cal.timezoneService;
-            let enumerator = tzService.timezoneIds;
-            while (enumerator.hasMore()) {
-                let id = enumerator.getNext();
-                if (!eas.ianaToWindowsTimezoneMap[id]) {
-                    TbSync.eventlog.add("info", eventLogInfo, "The IANA timezone <"+id+"> cannot be mapped to any Exchange timezone.");
+            for (let timezoneId of tzService.timezoneIds) {
+                if (!eas.ianaToWindowsTimezoneMap[timezoneId]) {
+                    TbSync.eventlog.add("info", eventLogInfo, "The IANA timezone <"+timezoneId+"> cannot be mapped to any Exchange timezone.");
                 }
             }
-
             
             //If an EAS calendar is currently NOT associated with an email identity, try to associate, 
             //but do not change any explicitly set association
-            // - A) find email identity and accociate (which sets organizer to that user identity)
+            // - A) find email identity and associate (which sets organizer to that user identity)
             // - B) overwrite default organizer with current best guess
             //TODO: Do this after email accounts changed, not only on restart? 
             let providerData = new TbSync.ProviderData("eas");
@@ -440,6 +439,14 @@
         // Fall through, if there was no error.
         return new TbSync.StatusData();   
     }
+
+
+    /**
+     * Return the custom OAuth2 ClientID.
+     */
+    static getCustomeOauthClientID() {
+        return eas.prefs.getCharPref("oauth.clientID");
+    }
 }
 
 
diff -Nru eas4tbsync-4.1.5/debian/changelog eas4tbsync-4.7/debian/changelog
--- eas4tbsync-4.1.5/debian/changelog	2022-10-14 13:46:15.000000000 +0200
+++ eas4tbsync-4.7/debian/changelog	2023-10-22 12:57:50.000000000 +0200
@@ -1,3 +1,25 @@
+eas4tbsync (4.7-1~deb12u1) bookworm; urgency=medium
+
+  * [4af7393] Adjust version of dependencies
+  *     Prepared for release in bookworm (proposed-updates)
+
+ -- Mechtilde Stehmann <mechtilde@debian.org>  Sun, 22 Oct 2023 12:57:50 +0200
+
+eas4tbsync (4.7-1) unstable; urgency=medium
+
+  * [a18dc06] Changed compression for tar.gz
+  * [4286976] New upstream version 4.7
+  *     Removed double entries in the version before
+
+ -- Mechtilde Stehmann <mechtilde@debian.org>  Tue, 12 Sep 2023 20:12:53 +0200
+
+eas4tbsync (4.1.5+git20230524+c788967-1) experimental; urgency=medium
+
+  * [976c125] Changed compression for using zip file
+  * [b9b95cd] New upstream version 4.1.5+git20230524+c788967
+
+ -- Mechtilde Stehmann <mechtilde@debian.org>  Sat, 29 Jul 2023 20:48:42 +0200
+
 eas4tbsync (4.1.5-1) unstable; urgency=medium
 
   * [dc50cf2] New upstream version 4.1.5
diff -Nru eas4tbsync-4.1.5/debian/control eas4tbsync-4.7/debian/control
--- eas4tbsync-4.1.5/debian/control	2022-09-24 13:33:37.000000000 +0200
+++ eas4tbsync-4.7/debian/control	2023-10-22 12:47:25.000000000 +0200
@@ -15,8 +15,8 @@
 Package: webext-eas4tbsync
 Architecture: all
 Depends: ${misc:Depends}
- , thunderbird (>= 1:102.2)
- , webext-tbsync (>= 4.0)
+ , thunderbird (>= 1:115.3)
+ , webext-tbsync (>= 4.7)
 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.1.5/debian/gbp.conf eas4tbsync-4.7/debian/gbp.conf
--- eas4tbsync-4.1.5/debian/gbp.conf	2022-09-24 10:34:50.000000000 +0200
+++ eas4tbsync-4.7/debian/gbp.conf	2023-09-12 20:07:05.000000000 +0200
@@ -4,7 +4,7 @@
 # use pristine-tar:
 pristine-tar = True
 # generate gz compressed orig file
-compression = gz
+# compression = xz
 debian-branch = debian/sid
 upstream-branch = upstream
 
diff -Nru eas4tbsync-4.1.5/Makefile eas4tbsync-4.7/Makefile
--- eas4tbsync-4.1.5/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ eas4tbsync-4.7/Makefile	2023-08-18 16:51:43.000000000 +0200
@@ -0,0 +1,25 @@
+# This file is part of EAS-4-TbSync.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+PACKAGE_NAME=EAS-4-TbSync
+
+ARCHIVE_NAME=$(PACKAGE_NAME).xpi
+
+PACKAGE_FILES= \
+	content \
+	_locales \
+	manifest.json \
+	CONTRIBUTORS.md \
+	LICENSE README.md \
+	background.js
+
+all: clean $(PACKAGE_FILES)
+	zip -r $(ARCHIVE_NAME) $(PACKAGE_FILES)
+
+clean:
+	rm -f $(ARCHIVE_NAME)
+
+.PHONY: clean
diff -Nru eas4tbsync-4.1.5/manifest.json eas4tbsync-4.7/manifest.json
--- eas4tbsync-4.1.5/manifest.json	2022-10-13 11:05:56.000000000 +0200
+++ eas4tbsync-4.7/manifest.json	2023-08-18 16:51:43.000000000 +0200
@@ -2,13 +2,13 @@
   "applications": {
     "gecko": {
       "id": "eas4tbsync@jobisoft.de",
-      "strict_min_version": "102.3.0",
-      "strict_max_version": "102.*"
+      "strict_min_version": "102.7.0",
+      "strict_max_version": "115.*"
     }
   },
   "manifest_version": 2,
   "name": "__MSG_extensionName__",
-  "version": "4.1.5",
+  "version": "4.7",
   "author": "John Bieling",
   "homepage_url": "https://github.com/jobisoft/EAS-4-TbSync/";,
   "default_locale": "en-US",

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature


--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 12.3

Hi,

Each of the updates discussed in these requests was included in this
morning's 12.3 bookworm point release.

Regards,

Adam

--- End Message ---

Reply to: