Version in base suite: 4.7-1~deb12u1 Base version: tbsync_4.7-1~deb12u1 Target version: tbsync_4.12-1~deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/t/tbsync/tbsync_4.7-1~deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/t/tbsync/tbsync_4.12-1~deb12u1.dsc README.md | 36 _locales/bg/messages.json | 15 _locales/cs/messages.json | 3 _locales/de/messages.json | 5 _locales/en-US/messages.json | 3 _locales/es/messages.json | 3 _locales/et/messages.json | 3 _locales/fr/messages.json | 3 _locales/gl/messages.json | 3 _locales/hu/messages.json | 3 _locales/it/messages.json | 3 _locales/ja/messages.json | 3 _locales/ko/messages.json | 3 _locales/pl/messages.json | 3 _locales/pt_BR/messages.json | 3 _locales/ro/messages.json | 3 _locales/ru/messages.json | 3 _locales/sv/messages.json | 3 content/HttpRequest.jsm | 755 -------------------- content/OverlayManager.jsm | 3 content/api/BootstrapLoader/CHANGELOG.md | 4 content/api/BootstrapLoader/implementation.js | 8 content/manager/accountManager.js | 3 content/manager/accounts.js | 3 content/manager/editAccount.js | 3 content/manager/eventlog/eventlog.js | 3 content/manager/manageProvider.js | 2 content/modules/addressbook.js | 948 ++++++++++++-------------- content/modules/core.js | 5 content/modules/db.js | 49 - content/modules/eventlog.js | 2 content/modules/io.js | 10 content/modules/lightning.js | 14 content/modules/manager.js | 6 content/modules/messenger.js | 5 content/modules/network.js | 2 content/modules/passwordManager.js | 8 content/modules/providers.js | 10 content/modules/public.js | 4 content/modules/tools.js | 2 content/overlays/messenger.js | 2 content/passwordPrompt/passwordPrompt.js | 2 content/scripts/bootstrap.js | 6 content/tbsync.jsm | 15 debian/changelog | 26 debian/control | 9 debian/copyright | 6 manifest.json | 14 48 files changed, 633 insertions(+), 1397 deletions(-) diff -Nru tbsync-4.7/README.md tbsync-4.12/README.md --- tbsync-4.7/README.md 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/README.md 2024-08-27 16:32:08.000000000 +0000 @@ -1,40 +1,6 @@ # TbSync -1. [Introduction](https://github.com/jobisoft/TbSync#introduction) -2. [Where is this going?](https://github.com/jobisoft/TbSync#where-is-this-going) -3. [External data sources](https://github.com/jobisoft/TbSync#external-data-sources) -4. [Icon sources and attributions](https://github.com/jobisoft/TbSync#icon-sources-and-attributions) - -## Introduction - -[TbSync](https://addons.thunderbird.net/addon/tbsync/) is a central user interface to manage cloud accounts and to synchronize their contact, task and calendar information with [Thunderbird](https://www.thunderbird.net/). Its main objective is to simplify the setup process for such accounts. The following providers (protocols) are currently supported: -* CalDAV & CardDAV, via [DAV-4-TbSync](https://github.com/jobisoft/DAV-4-TbSync) -[[compatibility list (DAV)](https://github.com/jobisoft/DAV-4-TbSync/wiki/Compatibility-list-(DAV))] -* Exchange ActiveSync (EAS v2.5 & v14.0), via [EAS-4-TbSync](https://github.com/jobisoft/EAS-4-TbSync) -[[compatibility list (EAS)](https://github.com/jobisoft/EAS-4-TbSync/wiki/Compatibility-list-(EAS))] - -Further details can be found in the [wiki](https://github.com/jobisoft/TbSync/wiki) of the TbSync project and in the [how-to-get-started guide](https://github.com/jobisoft/TbSync/wiki/How-to-get-started). - -If you like TbSync and want to support its development, please consider a donation. - -[![](https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif)](https://www.paypal.me/johnbieling) - - -## Want to add or fix a localization? -To help translating this project, please visit [crowdin.com](https://crowdin.com/profile/jobisoft), where the localizations are managed. If you want to add a new language, just contact me and I will set it up. - -Here are some general information regarding translations: - -* [Localization content best practices](https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_content_best_practices) -* [Summary table of quotation marks per language](https://en.wikipedia.org/wiki/Quotation_mark#Summary_table) -* [Transvision](https://transvision.mozfr.org/) provides translations for various languages -* by Thunderbird supported [locale codes](https://searchfox.org/comm-central/source/mail/locales/all-locales) - - -## Where is this going? - -I want to adapt Thunderbirds WebExtension APIs to simplify the addition of additional address book and calendar providers. I plan to keep TbSync as a central UI. - +[TbSync](https://addons.thunderbird.net/addon/tbsync/) was a central user interface to manage cloud accounts and to synchronize their contact, task and calendar information with [Thunderbird](https://www.thunderbird.net/). Its main objective was to simplify the setup process for such accounts. Thunderbird is working on a new account-hub UI, which will make TbSync obsolete. ## Icon sources and attributions diff -Nru tbsync-4.7/_locales/bg/messages.json tbsync-4.12/_locales/bg/messages.json --- tbsync-4.7/_locales/bg/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/bg/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -96,7 +96,7 @@ "message": "Настройки на регистрацията" }, "manager.catman.text": { - "message": "TbSync синхронизира и категории контакти, които са заместител за несинхронизираните списъци с контакти на Thunderbird. За да разширите възможностите на адресния указател на Thunderbird, инсталирайте добавката 'Category Manager'. Тя позволява вмъкването на контакти в много категории (групи) и др. Добавката може да се инсталира от официалното депо за добавки на Mozilla:" + "message": "TbSync синхронизира и категории контакти, които са заместител за несинхронизираните списъци с контакти на Thunderbird. За да разширите възможностите на адресния указател на Thunderbird, инсталирайте добавката „Category Manager“. Тя позволява вмъкването на контакти в много категории / групи и др. Добавката може да се инсталира от официалното депо за добавки на Mozilla:" }, "manager.community": { "message": "Общество/Потребители" @@ -177,7 +177,7 @@ "message": "Разработчици и преводачи" }, "manager.supporter.details": { - "message": "Детайли" + "message": "Подробности" }, "manager.supporter.sponsors": { "message": "Предоставили регистрации за експерименти" @@ -234,7 +234,7 @@ "message": "Потребителско име:" }, "popup.opensettings": { - "message": "TbSync отвори управлението на регистрациите" + "message": "Отвори TbSync управлението на регистрациите" }, "prompt.DeleteAccount": { "message": "Сигурни ли сте, че искате да изтриете регистрация ##accountName##?" @@ -261,7 +261,7 @@ "message": "Не можах да се свържа с OAuth 2.0 сървъра." }, "status.OAuthServerError": { - "message": " OAuth 2.0 сървъра отвърна с ##replace.1##" + "message": " OAuth 2.0 сървърът отвърна с ##replace.1##" }, "status.aborted": { "message": "Не е синхронизиран" @@ -285,7 +285,7 @@ "message": "На сървъра не бяха намерени ресурси." }, "status.notargets": { - "message": "Синхронизирането прекъсна, тъй като на компютъра ви не можеха да се създадат нови ресурси." + "message": "Синхронизирането прекъсна, тъй като на компютъра Ви не можеха да се създадат нови ресурси." }, "status.notsyncronized": { "message": "Регистрацията трябва да бъде синхронизира." @@ -305,6 +305,9 @@ "status.syncing": { "message": "Синхронизиране" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "От тази информация на следващата стъпка ще се отвори незавършено писмо, което можете да променяте (например да включите снимки от екрана). Едва чрез изпращане на писмото, докладът за грешка ще замине." }, @@ -357,7 +360,7 @@ "message": "Синхронизирай информацията от всички TbSync регистрации със сървърите" }, "password.ok": { - "message": "ОК" + "message": "Добре" }, "password.cancel": { "message": "Отказ" diff -Nru tbsync-4.7/_locales/cs/messages.json tbsync-4.12/_locales/cs/messages.json --- tbsync-4.7/_locales/cs/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/cs/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Probíhá synchronizace" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "Z informací zde uvedených bude v dalším kroku vygenerován e-mail, který můžete následně upravit (například přidání screenshotů). Zasláním e-mailu bude teprve zaslána chybová zpráva." }, diff -Nru tbsync-4.7/_locales/de/messages.json tbsync-4.12/_locales/de/messages.json --- tbsync-4.7/_locales/de/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/de/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronisierung" }, + "status.451": { + "message": "Server meldet Weiterleitung. Erneuter Versuch in 5 Sekunden." + }, "supportwizard.footer": { "message": "Aus den hier angegebenen Informationen wird im nächsten Schritt eine E-Mail erzeugt, die Sie nachträglich noch bearbeiten können (z.B. Bildschirmfotos o.Ä. hinzufügen). Erst durch das Absenden der E-Mail, wird der Fehlerbericht verschickt." }, @@ -357,7 +360,7 @@ "message": "Gleiche alle TbSync-Konten mit den Servern ab" }, "password.ok": { - "message": "OK" + "message": "Ok" }, "password.cancel": { "message": "Abbrechen" diff -Nru tbsync-4.7/_locales/en-US/messages.json tbsync-4.12/_locales/en-US/messages.json --- tbsync-4.7/_locales/en-US/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/en-US/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronizing" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "From the information given here, an e-mail will be generated in the next step, which you can subsequently edit (for example, adding screenshots or similar). Only by actually sending that e-mail, the error report will be sent." }, diff -Nru tbsync-4.7/_locales/es/messages.json tbsync-4.12/_locales/es/messages.json --- tbsync-4.7/_locales/es/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/es/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Sincronizando" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "En el paso siguiente, la información proporcionada aquí se incluirá en un mensaje de correo electrónico que podrá modificar (por ejemplo, para añadir capturas de pantalla). El informe de error no se enviará hasta que envíe ese mensaje de correo." }, diff -Nru tbsync-4.7/_locales/et/messages.json tbsync-4.12/_locales/et/messages.json --- tbsync-4.7/_locales/et/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/et/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronizing" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "From the information given here, an e-mail will be generated in the next step, which you can subsequently edit (for example, adding screenshots or similar). Only by actually sending that e-mail, the error report will be sent." }, diff -Nru tbsync-4.7/_locales/fr/messages.json tbsync-4.12/_locales/fr/messages.json --- tbsync-4.7/_locales/fr/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/fr/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronisation en cours" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "Un courriel sera généré à partir des informations fournies ici. Vous aurez la possibilité de le modifier par la suite (par exemple, pour y ajouter des captures d'écrans). Ce n'est que lorsque vous aurez envoyé l'e-mail que le rapport d'erreur nous sera envoyé." }, diff -Nru tbsync-4.7/_locales/gl/messages.json tbsync-4.12/_locales/gl/messages.json --- tbsync-4.7/_locales/gl/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/gl/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -308,6 +308,9 @@ "supportwizard.footer": { "message": "No seguinte paso, coa información facilitada aquí, xerarase unha mensaxe de correo que poderás editar (por exemplo, engadindo capturas de pantalla ou algo parecido). O informe de erro só será enviando cando envíes esa mensaxe de correo." }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.label.description": { "message": "Descrición detallada do erro:" }, diff -Nru tbsync-4.7/_locales/hu/messages.json tbsync-4.12/_locales/hu/messages.json --- tbsync-4.7/_locales/hu/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/hu/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -308,6 +308,9 @@ "supportwizard.footer": { "message": "Az itt megadott információból e-mail készül, amelyet aztán szerkeszthet (például képernyőképeket adhat hozzá). Csak akkor lesz elküldve hibajelentés, ha tényleg elküldi azt az e-mailt." }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.label.description": { "message": "Hiba részletes leírása:" }, diff -Nru tbsync-4.7/_locales/it/messages.json tbsync-4.12/_locales/it/messages.json --- tbsync-4.7/_locales/it/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/it/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -308,6 +308,9 @@ "supportwizard.footer": { "message": "Nel prossimo passaggio sarà generato un messaggio di posta elettronica a partire dalle informazioni fornite qui; puoi modificarlo in seguito (ad esempio, aggiungendo schermate o simili). La segnalazione d'errore verrà inviata solo inviando tale messaggio." }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.label.description": { "message": "Descrizione dettagliata dell'errore:'" }, diff -Nru tbsync-4.7/_locales/ja/messages.json tbsync-4.12/_locales/ja/messages.json --- tbsync-4.7/_locales/ja/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/ja/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "同期中" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "ここに記載されている情報から、次のステップで電子メールが生成され、その後編集することができます (例えば、スクリーンショットや類似したものを追加するなど)。 そのメールを実際に送信することによってのみ、エラーレポートが送信されます。" }, diff -Nru tbsync-4.7/_locales/ko/messages.json tbsync-4.12/_locales/ko/messages.json --- tbsync-4.7/_locales/ko/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/ko/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronizing" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "From the information given here, an e-mail will be generated in the next step, which you can subsequently edit (for example, adding screenshots or similar). Only by actually sending that e-mail, the error report will be sent." }, diff -Nru tbsync-4.7/_locales/pl/messages.json tbsync-4.12/_locales/pl/messages.json --- tbsync-4.7/_locales/pl/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/pl/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronizuję" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "Z podanych tutaj informacji w następnym kroku zostanie wygenerowany e-mail, który możesz następnie edytować (na przykład dodając zrzuty ekranu lub podobne). Tylko poprzez faktyczne wysłanie tej wiadomości zostanie wysłany raport o błędzie." }, diff -Nru tbsync-4.7/_locales/pt_BR/messages.json tbsync-4.12/_locales/pt_BR/messages.json --- tbsync-4.7/_locales/pt_BR/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/pt_BR/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Sincronizando" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "A partir das informações fornecidas aqui, um e-mail será gerado na próxima etapa, que você pode editar posteriormente (por exemplo, adicionando capturas de tela ou similar). Apenas enviando esse e-mail, o relatório de erro será enviado." }, diff -Nru tbsync-4.7/_locales/ro/messages.json tbsync-4.12/_locales/ro/messages.json --- tbsync-4.7/_locales/ro/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/ro/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronizing" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "From the information given here, an e-mail will be generated in the next step, which you can subsequently edit (for example, adding screenshots or similar). Only by actually sending that e-mail, the error report will be sent." }, diff -Nru tbsync-4.7/_locales/ru/messages.json tbsync-4.12/_locales/ru/messages.json --- tbsync-4.7/_locales/ru/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/ru/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Синхронизация" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "Из приведенной здесь информации на следующем шаге будет создано электронное письмо, которое вы можете впоследствии редактировать (например, добавлять скриншоты или аналогичное). Только при отправке этого электронного письма будет отправлен и сам отчет об ошибке." }, diff -Nru tbsync-4.7/_locales/sv/messages.json tbsync-4.12/_locales/sv/messages.json --- tbsync-4.7/_locales/sv/messages.json 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/_locales/sv/messages.json 2024-08-27 16:32:08.000000000 +0000 @@ -305,6 +305,9 @@ "status.syncing": { "message": "Synchronizing" }, + "status.451": { + "message": "Server issued redirect, retry in 5 seconds." + }, "supportwizard.footer": { "message": "From the information given here, an e-mail will be generated in the next step, which you can subsequently edit (for example, adding screenshots or similar). Only by actually sending that e-mail, the error report will be sent." }, diff -Nru tbsync-4.7/content/HttpRequest.jsm tbsync-4.12/content/HttpRequest.jsm --- tbsync-4.7/content/HttpRequest.jsm 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/HttpRequest.jsm 1970-01-01 00:00:00.000000000 +0000 @@ -1,755 +0,0 @@ -/* - * This file is part of TbSync, contributed by John Bieling. - * - * 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/. - * - * Limitations: - * ============ - * - no real event support (cannot add eventlisteners) - * - send only supports string body - * - onprogress not supported - * - readyState 2 & 3 not supported - * - * Note about HttpRequest.open(method, url, async, username, password): - * ============================================================================ - * If an Authorization header is specified, HttpRequest will use the - * given header. - * - * If no Authorization header is specified, but a username, HttpRequest - * will delegate the authentication process to nsIHttpChannel. If a password is - * specified as well, it will be used for authentication. If no password is - * specified, it will call the passwordCallback(username, realm, host) callback to - * request a password for the given username, host and realm send back from - * the server (in the WWW-Authenticate header). - * - */ - - "use strict"; - -var EXPORTED_SYMBOLS = ["HttpRequest"]; - -var bug669675 = []; -var containers = []; -var sandboxes = {}; - -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); - -var HttpRequest = class { - constructor() { - // a private object to store xhr related properties - this._xhr = {}; - - // HttpRequest supports two methods to receive data, using the - // streamLoader seems to be the more modern approach. - // BUT in order to overide MimeType, we need to call onStartRequest - this._xhr.useStreamLoader = false; - this._xhr.responseAsBase64 = false; - - this._xhr.loadFlags = Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; - this._xhr.headers = {}; - this._xhr.readyState = 0; - this._xhr.responseStatus = null; - this._xhr.responseStatusText = null; - this._xhr.responseText = null; - this._xhr.httpchannel = null; - this._xhr.method = null; - this._xhr.uri = null; - this._xhr.permanentlyRedirectedUrl = null; - this._xhr.username = ""; - this._xhr.password = ""; - this._xhr.overrideMimeType = null; - this._xhr.mozAnon = false; - this._xhr.mozBackgroundRequest = false; - this._xhr.timeout = 0; - this._xhr.redirectFlags = null; - this._xhr.containerReset = false; - this._xhr.containerRealm = "default"; - - this.onreadystatechange = function () {}; - this.onerror = function () {}; - this.onload = function () {}; - this.ontimeout = function () {}; - - // Redirects are handled internally, this callback is just called to - // inform the caller about the redirect. - // Flags: (https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIChannelEventSink) - // - REDIRECT_TEMPORARY = 1 << 0; - // - REDIRECT_PERMANENT = 1 << 1; - // - REDIRECT_INTERNAL = 1 << 2; - // TB enforces a redirect from http -> https without having received a redirect, - // but an STS header - // - REDIRECT_STS_UPGRADE = 1 << 3; - // This is a custom bit set by HttpRequest to indicate, that this redirect was missed by the nsIHttpChannel implementation - // probably due to a CORS violation (like https -> http) - // - REDIRECT_MISSED = 1 << 7; - this.onredirect = function(flags, newUri) {}; - - // Whenever a WWW-Authenticate header has been parsed, this callback is - // called to inform the caller about the found realm. - this.realmCallback = function (username, realm, host) {}; - - // Whenever a channel needs authentication, but the caller has only provided a username - // this callback is called to request the password. - this.passwordCallback = function (username, realm, host) {return null}; - - var self = this; - - this.notificationCallbacks = { - // nsIInterfaceRequestor - getInterface : function(aIID) { - if (aIID.equals(Components.interfaces.nsIAuthPrompt2)) { - // implement a custom nsIAuthPrompt2 - needed for auto authorization - if (!self._xhr.authPrompt) { - self._xhr.authPrompt = new HttpRequestPrompt(self._xhr.username, self._xhr.password, self.passwordCallback, self.realmCallback); - } - return self._xhr.authPrompt; - } else if (aIID.equals(Components.interfaces.nsIAuthPrompt)) { - // implement a custom nsIAuthPrompt - } else if (aIID.equals(Components.interfaces.nsIAuthPromptProvider)) { - // implement a custom nsIAuthPromptProvider - } else if (aIID.equals(Components.interfaces.nsIPrompt)) { - // implement a custom nsIPrompt - } else if (aIID.equals(Components.interfaces.nsIProgressEventSink)) { - // implement a custom nsIProgressEventSink - } else if (aIID.equals(Components.interfaces.nsIChannelEventSink)) { - // implement a custom nsIChannelEventSink - return self.redirect; - } - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - }; - - this.redirect = { - // nsIChannelEventSink implementation - asyncOnChannelRedirect: function(aOldChannel, aNewChannel, aFlags, aCallback) { - // Disallow redirects from https to http. - if (aOldChannel.URI.scheme == "https" && aNewChannel.URI.scheme == "http") { - // Using an unused error code according to https://developer.mozilla.org/en-US/docs/Mozilla/Errors. - // REJECTED_REDIRECT_FROM_HTTPS_TO_HTTP' - aCallback.onRedirectVerifyCallback(0x804B002F); - return; - } - - let uploadData; - let uploadContent; - if (aOldChannel instanceof Ci.nsIUploadChannel && - aOldChannel instanceof Ci.nsIHttpChannel && - aOldChannel.uploadStream) { - uploadData = aOldChannel.uploadStream; - uploadContent = aOldChannel.getRequestHeader("Content-Type"); - } - - aNewChannel.QueryInterface(Ci.nsIHttpChannel); - aOldChannel.QueryInterface(Ci.nsIHttpChannel); - - function copyHeader(aHdr) { - try { - let hdrValue = aOldChannel.getRequestHeader(aHdr); - if (hdrValue) { - aNewChannel.setRequestHeader(aHdr, hdrValue, false); - } - } catch (e) { - if (e.code != Components.results.NS_ERROR_NOT_AVAILIBLE) { - // The header could possibly not be available, ignore that - // case but throw otherwise - throw e; - } - } - } - - // Copy manually added headers - for (let header in self._xhr.headers) { - if (self._xhr.headers.hasOwnProperty(header)) { - copyHeader(header); - } - } - - prepHttpChannelUploadData( - aNewChannel, - aOldChannel.requestMethod, - uploadData, - uploadContent); - - self._xhr.redirectFlags = aFlags; - if (aFlags & Ci.nsIChannelEventSink.REDIRECT_PERMANENT) { - self._xhr.permanentlyRedirectedUrl = aNewChannel.URI.spec; - } - self.onredirect(aFlags, aNewChannel.URI); - aCallback.onRedirectVerifyCallback(Components.results.NS_OK); - } - }; - - this.listener = { - _buffer: [], - - //nsIStreamListener (aUseStreamLoader = false) - onStartRequest: function(aRequest) { - //Services.console.logStringMessage("[onStartRequest] " + aRequest.URI.spec); - this._buffer = []; - - if (self._xhr.overrideMimeType) { - aRequest.contentType = self._xhr.overrideMimeType; - } - }, - onDataAvailable: function (aRequest, aInputStream, aOffset, aCount) { - //Services.console.logStringMessage("[onDataAvailable] " + aRequest.URI.spec + " : " + aCount); - let buffer = new ArrayBuffer(aCount); - let stream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream); - stream.setInputStream(aInputStream); - stream.readArrayBuffer(aCount, buffer); - - // store the chunk - this._buffer.push(Array.from(new Uint8Array(buffer))); - }, - onStopRequest: function(aRequest, aStatusCode) { - //Services.console.logStringMessage("[onStopRequest] " + aRequest.URI.spec + " : " + aStatusCode); - // combine all binary chunks to create a flat byte array; - let combined = [].concat.apply([], this._buffer); - let data = convertByteArray(combined, self.responseAsBase64); - this.processResponse(aRequest.QueryInterface(Components.interfaces.nsIHttpChannel), aStatusCode, data); - }, - - - - //nsIStreamLoaderObserver (aUseStreamLoader = true) - onStreamComplete: function(aLoader, aContext, aStatus, aResultLength, aResult) { - let result = convertByteArray(aResult, self.responseAsBase64); - this.processResponse(aLoader.request.QueryInterface(Components.interfaces.nsIHttpChannel), aStatus, result); - }, - - processResponse: function(aChannel, aStatus, aResult) { - //Services.console.logStringMessage("[processResponse] " + aChannel.URI.spec + " : " + aStatus); - // do not set any channal response data, before we know we failed - // and before we know we do not have to rerun (due to bug 669675) - - let responseStatus = null; - try { - responseStatus = aChannel.responseStatus; - } catch (ex) { - switch (aStatus) { - case Components.results.NS_ERROR_NET_TIMEOUT: - self._xhr.httpchannel = aChannel; - self._xhr.responseText = aResult; - self._xhr.responseStatus = 0; - self._xhr.responseStatusText = ""; - self._xhr.readyState = 4; - self.onreadystatechange(); - self.ontimeout(); - return; - case Components.results.NS_BINDING_ABORTED: - case 0x804B002F: //Custom error (REJECTED_REDIRECT_FROM_HTTPS_TO_HTTP') - self._xhr.httpchannel = aChannel; - self._xhr.responseText = aResult; - self._xhr.responseStatus = 0; - self._xhr.responseStatusText = ""; - self._xhr.readyState = 0; - self.onreadystatechange(); - self.onerror(); - return; - case 0x805303F4: //NS_ERROR_DOM_BAD_URI - // Error on Strict-Transport-Security induced Redirect http -> https (these do not even show up in the console) - // The redirect has been done already and the channel is already the new channel. - // Due to CORS violation, it cannot be fulfilled. - if (self._xhr.redirectFlags && aChannel.URI.spec != self._xhr.uri.spec) { - self._xhr.uri = aChannel.URI; - self.send(self._xhr.data); - return; - } - default: - self._xhr.httpchannel = aChannel; - self._xhr.responseText = aResult; - self._xhr.responseStatus = 0; - self._xhr.responseStatusText = ""; - self._xhr.readyState = 4; - self.onreadystatechange(); - self.onerror(); - return; - } - } - - // Usually redirects are handled internally, but any CORS violating request is - // returning a 30x, if CORS is not allowed. This is not true for STS induced redirects (see above). - if ([301,302,307,308].includes(responseStatus)) { - // aChannel is still the old channel - let redirected = self.getResponseHeader("location"); - if (redirected && redirected != aChannel.URI.spec) { - let flag = Ci.nsIChannelEventSink.REDIRECT_TEMPORARY; - let uri = Services.io.newURI(redirected); - if ([301,308].includes(responseStatus)) { - flag = Ci.nsIChannelEventSink.REDIRECT_PERMANENT; - self._xhr.permanentlyRedirectedUrl = uri.spec; - } - // inform caller about the redirect and set the REDIRECT_MISSED bit - - self.onredirect(flag | 0x80, uri); - self._xhr.uri = uri; - self.send(self._xhr.data); - return; - } - } - - // mitigation for bug https://bugzilla.mozilla.org/show_bug.cgi?id=669675 - // we need to check, if nsIHttpChannel was in charge of auth: - // if there was no Authentication header provided by the user, but a username - // nsIHttpChannel should have added one. Is there one? - if ( - (responseStatus == 401) && - !self._xhr.mozAnon && - !self.hasRequestHeader("Authorization") && // no user defined header, so nsIHttpChannel should have called the authPrompt - self._xhr.username && // we can only add basic auth header if user - self._xhr.password // and pass are present - ) { - // check the actual Authorization headers send - let unauthenticated; - try { - let header = aChannel.getRequestHeader("Authorization"); - unauthenticated = false; - } catch (e) { - unauthenticated = true; - } - - if (unauthenticated) { - if (!bug669675.includes(self._xhr.uri.spec)) { - bug669675.push(self._xhr.uri.spec) - console.log("Mitigation for bug 669675 for URL <"+self._xhr.uri.spec+"> (Once per URL per session)"); - // rerun - self.send(self._xhr.data); - return; - } else { - console.log("Mitigation failed for URL <"+self._xhr.uri.spec+">"); - } - } - } - - self._xhr.httpchannel = aChannel; - self._xhr.responseText = aResult; - self._xhr.responseStatus = responseStatus; - self._xhr.responseStatusText = aChannel.responseStatusText; - self._xhr.readyState = 4; - self.onreadystatechange(); - self.onload(); - } - }; - } - - - - - /** public **/ - - open(method, url, async = true, username = "", password = "") { - this._xhr.method = method; - - try { - this._xhr.uri = Services.io.newURI(url); - } catch (e) { - Components.utils.reportError(e); - throw new Error("HttpRequest: Invalid URL <"+url+">"); - } - if (!async) throw new Error ("HttpRequest: Synchronous requests not implemented."); - - this._xhr.username = username; - this._xhr.password = password; - - this._xhr.readyState = 1; - this.onreadystatechange(); - - } - - // must be called after open, before send - setContainerRealm(v) { - this._xhr.containerRealm = v; - } - - // must be called after open, before send - clearContainerCache() { - this._xhr.containerReset = true; - } - - send(data) { - //store the data, so we can rerun - this._xhr.data = data; - this._xhr.redirectFlags = null; - - // The sandbox will have a loadingNode - let sandbox = getSandboxForOrigin(this._xhr.username, this._xhr.uri, this._xhr.containerRealm, this._xhr.containerReset); - - // The XHR in the sandbox will have the correct loadInfo, which will allow us - // to use cookies and a CodebasePrincipal for us to use userContextIds and to - // contact nextcloud servers (error 503). - // We will not use the XHR or the sandbox itself. - let XHR = new sandbox.XMLHttpRequest(); - XHR.open(this._xhr.method, this._xhr.uri.spec); - - // Create the channel with the loadInfo from the sandboxed XHR - let channel = Services.io.newChannelFromURIWithLoadInfo(this._xhr.uri, XHR.channel.loadInfo); - - /* - // as of TB67 newChannelFromURI needs to specify a loading node to have access to the cookie jars - // using the main window - // another option would be workers - let options = {}; - let mainWindow = Services.wm.getMostRecentWindow("mail:3pane"); - - let channel = Services.io.newChannelFromURI( - this._xhr.uri, - mainWindow.document, - Services.scriptSecurityManager.createContentPrincipal(this._xhr.uri, options), - null, - Components.interfaces.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - Components.interfaces.nsIContentPolicy.TYPE_OTHER); - */ - -/* - // enforce anonymous access if requested (will not work with proxy, see MDN) - if (this._xhr.mozAnon) { - channel.loadFlag |= Components.interfaces.nsIRequest.LOAD_ANONYMOUS; - } - - // set background request - if (this._xhr.mozBackgroundRequest) { - channel.loadFlag |= Components.interfaces.nsIRequest.LOAD_BACKGROUND; - } -*/ - this._xhr.httpchannel = channel.QueryInterface(Components.interfaces.nsIHttpChannel); - this._xhr.httpchannel.loadFlags |= this._xhr.loadFlags; - this._xhr.httpchannel.notificationCallbacks = this.notificationCallbacks; - - // Set default content type. - if (!this.hasRequestHeader("Content-Type")) { - this.setRequestHeader("Content-Type", "application/xml; charset=utf-8") - } - - // Set default accept value. - if (!this.hasRequestHeader("Accept")) { - this.setRequestHeader("Accept", "*/*"); - } - - // Set non-standard header to request authorization (https://github.com/jobisoft/DAV-4-TbSync/issues/106) - if (this._xhr.username) { - this.setRequestHeader("X-EnforceAuthentication", "True"); - } - - // calculate length of request and add header - if (data) { - let textEncoder = new TextEncoder(); - let encoded = textEncoder.encode(data); - this.setRequestHeader("Content-Length", encoded.length); - } - - // mitigation for bug 669675 - if ( - bug669675.includes(this._xhr.uri.spec) && - !this._xhr.mozAnon && - !this.hasRequestHeader("Authorization") && - this._xhr.username && - this._xhr.password - ) { - this.setRequestHeader("Authorization", "Basic " + b64EncodeUnicode(this._xhr.username + ':' + this._xhr.password)); - } - - // add all headers to the channel - for (let header in this._xhr.headers) { - if (this._xhr.headers.hasOwnProperty(header)) { - this._xhr.httpchannel.setRequestHeader(header, this._xhr.headers[header], false); - } - } - - // Will overwrite the Content-Type, so it must be called after the headers have been set. - prepHttpChannelUploadData(this._xhr.httpchannel, this._xhr.method, data, this.getRequestHeader("Content-Type")); - - if (this._xhr.useStreamLoader) { - let loader = Components.classes["@mozilla.org/network/stream-loader;1"].createInstance(Components.interfaces.nsIStreamLoader); - loader.init(this.listener); - this.listener = loader; - } - - this._startTimeout(); - this._xhr.httpchannel.asyncOpen(this.listener, this._xhr.httpchannel); - } - - get readyState() { return this._xhr.readyState; } - get responseURI() { return this._xhr.httpchannel.URI; } - get responseURL() { return this._xhr.httpchannel.URI.spec; } - get permanentlyRedirectedUrl() { return this._xhr.permanentlyRedirectedUrl; } - get responseText() { return this._xhr.responseText; } - get status() { return this._xhr.responseStatus; } - get statusText() { return this._xhr.responseStatusText; } - get channel() { return this._xhr.httpchannel; } - get loadFlags() { return this._xhr.loadFlags; } - get timeout() { return this._xhr.timeout; } - get mozBackgroundRequest() { return this._xhr.mozBackgroundRequest; } - get mozAnon() { return this._xhr.mozAnon; } - - set loadFlags(v) { this._xhr.loadFlags = v; } - set timeout(v) { this._xhr.timeout = v; } - set mozBackgroundRequest(v) { this._xhr.mozBackgroundRequest = (v === true); } - set mozAnon(v) { this._xhr.mozAnon = (v === true); } - - - // case insensitive method to check for headers - hasRequestHeader(header) { - let lowHeaders = Object.keys(this._xhr.headers).map(x => x.toLowerCase()); - return lowHeaders.includes(header.toLowerCase()); - } - - // if a header exists (case insensitive), it will be replaced (keeping the original capitalization) - setRequestHeader(header, value) { - let useHeader = header; - let lowHeader = header.toLowerCase(); - - for (let h in this._xhr.headers) { - if (this._xhr.headers.hasOwnProperty(h) && h.toLowerCase() == lowHeader) { - useHeader = h; - break; - } - } - this._xhr.headers[useHeader] = value; - } - - // checks if a header (case insensitive) has been set by setRequestHeader - that does not mean it has been added to the channel! - getRequestHeader(header) { - let lowHeader = header.toLowerCase(); - - for (let h in this._xhr.headers) { - if (this._xhr.headers.hasOwnProperty(h) && h.toLowerCase() == lowHeader) { - return this._xhr.headers[h]; - } - } - return null; - } - - getResponseHeader(header) { - try { - return this._xhr.httpchannel.getResponseHeader(header); - } catch (e) { - if (e.code != Components.results.NS_ERROR_NOT_AVAILIBLE) { - // The header could possibly not be available, ignore that - // case but throw otherwise - throw e; - } - } - return null; - } - - overrideMimeType(mime) { - this._xhr.overrideMimeType = mime; - } - - abort() { - this._cancel(Components.results.NS_BINDING_ABORTED); - } - - get responseAsBase64() { return this._xhr.responseAsBase64; } - set responseAsBase64(v) { this._xhr.responseAsBase64 = (v == true);} - - /* not used */ - - get responseXML() { throw new Error("HttpRequest: responseXML not implemented"); } - - get response() { throw new Error("HttpRequest: response not implemented"); } - set response(v) { throw new Error("HttpRequest: response not implemented"); } - - get responseType() { throw new Error("HttpRequest: response not implemented"); } - set responseType(v) { throw new Error("HttpRequest: response not implemented"); } - - get upload() { throw new Error("HttpRequest: upload not implemented"); } - set upload(v) { throw new Error("HttpRequest: upload not implemented"); } - - get withCredentials() { throw new Error("HttpRequest: withCredentials not implemented"); } - set withCredentials(v) { throw new Error("HttpRequest: withCredentials not implemented"); } - - - - - - - /** private helper methods **/ - - _startTimeout() { - let that = this; - - this._xhr.timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); - let event = { - notify: function(timer) { - that._cancel(Components.results.NS_ERROR_NET_TIMEOUT) - } - } - this._xhr.timer.initWithCallback( - event, - this._xhr.timeout, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); - } - - _cancel(error) { - if (this._xhr.httpchannel && error) { - this._xhr.httpchannel.cancel(error); - } - } -} - - - - - -var HttpRequestPrompt = class { - constructor(username, password, promptCallback, realmCallback) { - this.mCounts = 0; - this.mUsername = username; - this.mPassword = password; - this.mPromptCallback = promptCallback; - this.mRealmCallback = realmCallback; - } - - // boolean promptAuth(in nsIChannel aChannel, - // in uint32_t level, - // in nsIAuthInformation authInfo) - promptAuth (aChannel, aLevel, aAuthInfo) { - this.mRealmCallback(this.mUsername, aAuthInfo.realm, aChannel.URI.host); - if (this.mUsername && this.mPassword) { - console.log("Passing provided credentials for user <"+this.mUsername+"> to nsIHttpChannel."); - aAuthInfo.username = this.mUsername; - aAuthInfo.password = this.mPassword; - } else if (this.mUsername) { - console.log("Using passwordCallback callback to get password for user <"+this.mUsername+"> and realm <"+aAuthInfo.realm+"> @ host <"+aChannel.URI.host+">"); - let password = this.mPromptCallback(this.mUsername, aAuthInfo.realm, aChannel.URI.host); - if (password) { - aAuthInfo.username = this.mUsername; - aAuthInfo.password = password; - } else { - return false; - } - } else { - return false; - } - - // The provided password could be wrong, in whichcase - // we would be here more than once. - this.mCounts++ - return (this.mCounts < 2); - } -} - - - - -function getSandboxForOrigin(username, uri, containerRealm = "default", containerReset = false) { - let options = {}; - let origin = uri.scheme + "://" + uri.hostPort; - - // Note: - // Disabled check for username to always use containers, even if no username is given. - // There was an issue with NC (in its container) and google being in the default container, - // causing NC to fail with 503. Putting google inside a container as well fixed it. - options.userContextId = getContainerIdForUser(containerRealm + "::" + username); - origin = options.userContextId + "@" + origin; - if (containerReset) { - resetContainerWithId(options.userContextId); - } - - if (!sandboxes.hasOwnProperty(origin)) { - console.log("Creating sandbox for <"+origin+">"); - let principal = Services.scriptSecurityManager.createContentPrincipal(uri, options); - sandboxes[origin] = Components.utils.Sandbox(principal, { - wantXrays: true, - wantGlobalProperties: ["XMLHttpRequest"], - }); - } - - return sandboxes[origin]; -} - -function resetContainerWithId(id) { - Services.clearData.deleteDataFromOriginAttributesPattern({ userContextId: id }); -} - -function getContainerIdForUser(username) { - // 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(username) == -1) { - for (let i=0; i < containers.length; i++) { - resetContainerWithId(i + min); - } - containers = []; - } - - let idx = containers.indexOf(username); - return (idx == -1) ? containers.push(username) - 1 + min : (idx + min); -} - -// copied from cardbook -function b64EncodeUnicode (aString) { - return btoa(encodeURIComponent(aString).replace(/%([0-9A-F]{2})/g, function(match, p1) { - return String.fromCharCode('0x' + p1); - })); -} - -// copied from lightning -function prepHttpChannelUploadData(aHttpChannel, aMethod, aUploadData, aContentType) { - if (aUploadData) { - aHttpChannel.QueryInterface(Components.interfaces.nsIUploadChannel); - let stream; - if (aUploadData instanceof Components.interfaces.nsIInputStream) { - // Make sure the stream is reset - stream = aUploadData.QueryInterface(Components.interfaces.nsISeekableStream); - stream.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0); - } else { - // Otherwise its something that should be a string, convert it. - let converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - stream = converter.convertToInputStream(aUploadData.toString()); - } - - // If aContentType is empty, the protocol will assume that no content headers are to be - // added to the uploaded stream and that any required headers are already encoded in - // the stream. In the case of HTTP, if this parameter is non-empty, then its value will - // replace any existing Content-Type header on the HTTP request. In the case of FTP and - // FILE, this parameter is ignored. - aHttpChannel.setUploadStream(stream, aContentType, -1); - } - - //must be set after setUploadStream - //https://developer.mozilla.org/en-US/docs/Mozilla/Creating_sandboxed_HTTP_connections - aHttpChannel.QueryInterface(Ci.nsIHttpChannel); - aHttpChannel.requestMethod = aMethod; -} - -/** - * Convert a byte array to a string - copied from lightning - * - * @param {octet[]} aResult The bytes to convert - * @param {Boolean} responseAsBase64 Return a base64 encoded string - * @param {String} aCharset The character set of the bytes, defaults to utf-8 - * @param {Boolean} aThrow If true, the function will raise an exception on error - * @returns {?String} The string result, or null on error - */ -function convertByteArray(aResult, responseAsBase64 = false, aCharset="utf-8", aThrow) { - if (responseAsBase64) { - var bin = ''; - var bytes = Uint8Array.from(aResult); - var len = bytes.byteLength; - for (var i = 0; i < len; i++) { - bin += String.fromCharCode( bytes[ i ] ); - } - return btoa( bin ); // if we ever need raw, return bin - } else { - try { - return new TextDecoder(aCharset).decode(Uint8Array.from(aResult)); - } catch (e) { - if (aThrow) { - throw e; - } - } - } - return null; -} - diff -Nru tbsync-4.7/content/OverlayManager.jsm tbsync-4.12/content/OverlayManager.jsm --- tbsync-4.7/content/OverlayManager.jsm 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/OverlayManager.jsm 2024-08-27 16:32:08.000000000 +0000 @@ -6,12 +6,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; var EXPORTED_SYMBOLS = ["OverlayManager"]; var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); function OverlayManager(extension, options = {}) { this.registeredOverlays = {}; diff -Nru tbsync-4.7/content/api/BootstrapLoader/CHANGELOG.md tbsync-4.12/content/api/BootstrapLoader/CHANGELOG.md --- tbsync-4.7/content/api/BootstrapLoader/CHANGELOG.md 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/api/BootstrapLoader/CHANGELOG.md 2024-08-27 16:32:08.000000000 +0000 @@ -1,3 +1,7 @@ +Version: 1.22 +------------- +- adjusted to Thunderbird Supernova (Services is now in globalThis) + Version: 1.21 ------------- - Explicitly set hasAddonManagerEventListeners flag to false on uninstall diff -Nru tbsync-4.7/content/api/BootstrapLoader/implementation.js tbsync-4.12/content/api/BootstrapLoader/implementation.js --- tbsync-4.7/content/api/BootstrapLoader/implementation.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/api/BootstrapLoader/implementation.js 2024-08-27 16:32:08.000000000 +0000 @@ -2,7 +2,7 @@ * This file is provided by the addon-developer-support repository at * https://github.com/thundernest/addon-developer-support * - * Version: 1.21 + * Version 1.22 * * Author: John Bieling (john@thunderbird.net) * @@ -12,10 +12,8 @@ */ // Get various parts of the WebExtension framework that we need. -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("."); @@ -51,13 +49,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) ); } diff -Nru tbsync-4.7/content/manager/accountManager.js tbsync-4.12/content/manager/accountManager.js --- tbsync-4.7/content/manager/accountManager.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/manager/accountManager.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,9 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm"); var tbSyncAccountManager = { diff -Nru tbsync-4.7/content/manager/accounts.js tbsync-4.12/content/manager/accounts.js --- tbsync-4.7/content/manager/accounts.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/manager/accounts.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,9 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm"); var tbSyncAccounts = { diff -Nru tbsync-4.7/content/manager/editAccount.js tbsync-4.12/content/manager/editAccount.js --- tbsync-4.7/content/manager/editAccount.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/manager/editAccount.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,9 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm"); var tbSyncAccountSettings = { diff -Nru tbsync-4.7/content/manager/eventlog/eventlog.js tbsync-4.12/content/manager/eventlog/eventlog.js --- tbsync-4.7/content/manager/eventlog/eventlog.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/manager/eventlog/eventlog.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,9 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm"); var tbSyncEventLog = { diff -Nru tbsync-4.7/content/manager/manageProvider.js tbsync-4.12/content/manager/manageProvider.js --- tbsync-4.7/content/manager/manageProvider.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/manager/manageProvider.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,7 @@ * 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"); diff -Nru tbsync-4.7/content/modules/addressbook.js tbsync-4.12/content/modules/addressbook.js --- tbsync-4.7/content/modules/addressbook.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/addressbook.js 2024-08-27 16:32:08.000000000 +0000 @@ -5,20 +5,18 @@ * 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 { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); +var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm"); +var { AddrBookCard } = ChromeUtils.import("resource:///modules/AddrBookCard.jsm"); -XPCOMUtils.defineLazyModuleGetters(this, { - AddrBookCard: "resource:///modules/AddrBookCard.jsm" +ChromeUtils.defineESModuleGetters(this, { + FileUtils: "resource://gre/modules/FileUtils.sys.mjs", }); var addressbook = { - + _notifications: [ "addrbook-directory-updated", "addrbook-directory-deleted", @@ -32,19 +30,19 @@ "addrbook-list-created" ], - load : async function () { + load: async function () { for (let topic of this._notifications) { Services.obs.addObserver(this.addressbookObserver, topic); } }, - unload : async function () { + unload: async function () { for (let topic of this._notifications) { Services.obs.removeObserver(this.addressbookObserver, topic); } }, - getStringValue : function (ab, value, fallback) { + getStringValue: function (ab, value, fallback) { try { return ab.getStringValue(value, fallback); } catch (e) { @@ -55,8 +53,8 @@ searchDirectory: function (uri, search) { return new Promise((resolve, reject) => { let listener = { - cards : [], - + cards: [], + onSearchFinished(aResult, aErrorMsg) { resolve(this.cards); }, @@ -64,27 +62,27 @@ this.cards.push(aCard.QueryInterface(Components.interfaces.nsIAbCard)); } } - + let result = MailServices.ab.getDirectory(uri).search(search, "", listener); }); }, - - + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * AdvancedTargetData, an extended TargetData implementation, providers // * can use this as their own TargetData by extending it and just // * defining the extra methods // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - - AdvancedTargetData : class { - constructor(folderData) { + + AdvancedTargetData: class { + constructor(folderData) { this._folderData = folderData; this._targetObj = null; } - + // Check, if the target exists and return true/false. - hasTarget() { + hasTarget() { let target = this._folderData.getFolderProperty("target"); let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(target); return directory ? true : false; @@ -94,23 +92,23 @@ // be whatever you want and is returned by FolderData.targetData.getTarget(). // If the target does not exist, it should be created. Throw a simple Error, if that // failed. - async getTarget() { + async getTarget() { let target = this._folderData.getFolderProperty("target"); let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(target); - + if (!directory) { // create a new addressbook and store its UID in folderData directory = await TbSync.addressbook.prepareAndCreateAddressbook(this._folderData); if (!directory) throw new Error("notargets"); } - + if (!this._targetObj || this._targetObj.UID != directory.UID) this._targetObj = new TbSync.addressbook.AbDirectory(directory, this._folderData); return this._targetObj; } - + /** * Removes the target from the local storage. If it does not exist, return * silently. A call to ``hasTarget()`` should return false, after this has @@ -124,12 +122,12 @@ if (directory) { MailServices.ab.deleteAddressBook(directory.URI); } - } catch (e) {} + } catch (e) { } TbSync.db.clearChangeLog(target); - this._folderData.resetFolderProperty("target"); + this._folderData.resetFolderProperty("target"); } - + /** * Disconnects the target in the local storage from this TargetData, but * does not delete it, so it becomes a stale "left over" . A call @@ -140,18 +138,18 @@ let target = this._folderData.getFolderProperty("target"); let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(target); if (directory) { - let changes = TbSync.db.getItemsFromChangeLog(target, 0, "_by_user"); + let changes = TbSync.db.getItemsFromChangeLog(target, 0, "_by_user"); if (changes.length > 0) { this.targetName = this.targetName + " (*)"; - } + } directory.setStringValue("tbSyncIcon", "orphaned"); directory.setStringValue("tbSyncProvider", "orphaned"); directory.setStringValue("tbSyncAccountID", ""); } TbSync.db.clearChangeLog(target); - this._folderData.resetFolderProperty("target"); - } - + this._folderData.resetFolderProperty("target"); + } + set targetName(newName) { let target = this._folderData.getFolderProperty("target"); let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(target); @@ -161,7 +159,7 @@ throw new Error("notargets"); } } - + get targetName() { let target = this._folderData.getFolderProperty("target"); let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(target); @@ -171,7 +169,7 @@ throw new Error("notargets"); } } - + setReadOnly(value) { } @@ -179,15 +177,15 @@ // * * * * * * * * * * * * * * * * * // * AdvancedTargetData extension * // * * * * * * * * * * * * * * * * * - + get isAdvancedAddressbookTargetData() { return true; } - + get folderData() { return this._folderData; } - + // define a card property, which should be used for the changelog // basically your primary key for the abItem properties // UID will be used, if nothing specified @@ -196,9 +194,9 @@ } generatePrimaryKey() { - return TbSync.generateUUID(); + return TbSync.generateUUID(); } - + // enable or disable changelog get logUserChanges() { return true; @@ -212,7 +210,7 @@ break; } } - + cardObserver(aTopic, abCardItem) { switch (aTopic) { case "addrbook-contact-updated": @@ -229,13 +227,13 @@ case "addrbook-list-member-removed": //Services.console.logStringMessage("["+ aTopic + "] MemberName: " + abListMember.getProperty("DisplayName")); break; - + case "addrbook-list-deleted": case "addrbook-list-updated": //Services.console.logStringMessage("["+ aTopic + "] ListName: " + abListItem.getProperty("ListName")); break; - - case "addrbook-list-created": + + case "addrbook-list-created": //Services.console.logStringMessage("["+ aTopic + "] Created new X-DAV-UID for List <"+abListItem.getProperty("ListName")+">"); break; } @@ -248,18 +246,18 @@ let dirPrefId = MailServices.ab.newAddressBook(newname, "", 101); let directory = MailServices.ab.getDirectoryFromId(dirPrefId); return directory; - } + } }, - + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * AbItem and AbDirectory Classes // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - - AbItem : class { + + AbItem: class { constructor(abDirectory, item) { if (!abDirectory) throw new Error("AbItem::constructor is missing its first parameter!"); @@ -272,7 +270,7 @@ this._tempListDirectory = null; this._tempProperties = null; this._isMailList = false; - + if (item instanceof Components.interfaces.nsIAbDirectory) { this._tempListDirectory = item; this._isMailList = true; @@ -282,11 +280,11 @@ this._isMailList = item.isMailList; } } - + get abDirectory() { return this._abDirectory; } - + get isMailList() { return this._isMailList; } @@ -303,11 +301,11 @@ if (this._tempListDirectory) return this._tempListDirectory.UID; return this._card.UID; } - + get primaryKey() { //use UID as fallback let key = this._abDirectory.primaryKeyField; - return key ? this.getProperty(key) : this.UID; + return key ? this.getProperty(key) : this.UID; } set primaryKey(value) { @@ -320,24 +318,24 @@ clone() { //no real clone ... this is just here to match the calendar target return new TbSync.addressbook.AbItem(this._abDirectory, this._card); } - + toString() { - return this._card.displayName + " (" + this._card.firstName + ", " + this._card.lastName + ") <"+this._card.primaryEmail+">"; + return this._card.displayName + " (" + this._card.firstName + ", " + this._card.lastName + ") <" + this._card.primaryEmail + ">"; } - + // mailinglist aware method to get properties of cards // mailinglist properties cannot be stored in mailinglists themselves, so we store them in changelog getProperty(property, fallback = "") { if (property == "UID") return this.UID; - + if (this._isMailList) { const directListProperties = { ListName: "dirName", ListNickName: "listNickName", ListDescription: "description" }; - + let value; if (directListProperties.hasOwnProperty(property)) { try { @@ -349,7 +347,7 @@ } else { value = this._tempProperties ? this._tempProperties[property] : TbSync.db.getItemStatusFromChangeLog(this._abDirectory.UID + "#" + this.UID, property); } - return value || fallback; + return value || fallback; } else { return this._card.getProperty(property, fallback); } @@ -364,33 +362,33 @@ throw ("TbSync.addressbook.AbItem.setProperty: UID cannot be changed currently."); return; } - + if (this._isMailList) { const directListProperties = { ListName: "dirName", ListNickName: "listNickName", ListDescription: "description" }; - + if (directListProperties.hasOwnProperty(property)) { try { let mailListDirectory = this._tempListDirectory || MailServices.ab.getDirectory(this._card.mailListURI); mailListDirectory[directListProperties[property]] = value; } catch (e) { // list does not exists - } + } } else { - if (this._tempProperties) { - this._tempProperties[property] = value; - } else { - TbSync.db.addItemToChangeLog(this._abDirectory.UID + "#" + this.UID, property, value); - } + if (this._tempProperties) { + this._tempProperties[property] = value; + } else { + TbSync.db.addItemToChangeLog(this._abDirectory.UID + "#" + this.UID, property, value); + } } } else { this._card.setProperty(property, value); } } - + deleteProperty(property) { if (this._isMailList) { if (this._tempProperties) { @@ -402,18 +400,18 @@ this._card.deleteProperty(property); } } - - get changelogData() { + + get changelogData() { return TbSync.db.getItemDataFromChangeLog(this._abDirectory.UID, this.primaryKey); } - get changelogStatus() { + get changelogStatus() { return TbSync.db.getItemStatusFromChangeLog(this._abDirectory.UID, this.primaryKey); } - set changelogStatus(status) { + set changelogStatus(status) { let value = this.primaryKey; - + if (value) { if (!status) { TbSync.db.removeItemFromChangeLog(this._abDirectory.UID, value); @@ -424,7 +422,7 @@ TbSync.db.addItemToChangeLog(this._abDirectory.UID, value, status); } } - } + } @@ -443,9 +441,9 @@ } return members; } - + addListMembers(property, candidates) { - if (this._card && this._card.isMailList) { + if (this._card && this._card.isMailList) { let members = this.getMembersPropertyList(property); let mailListDirectory = MailServices.ab.getDirectory(this._card.mailListURI); @@ -472,88 +470,78 @@ let card = this._abDirectory._directory.getCardFromProperty(property, candidate, true); if (card) cardsToRemove.push(card); } - if (cardsToRemove.length > 0) mailListDirectory.deleteCards(cardsToRemove); + if (cardsToRemove.length > 0) mailListDirectory.deleteCards(cardsToRemove); } } - - addPhoto(photo, data, extension = "jpg", url = "") { - let dest = []; + + addPhoto(photo, data, extension = "jpg", url = "") { let card = this._card; let bookUID = this.abDirectory.UID; - // TbSync storage must be set as last let book64 = btoa(bookUID); - let photo64 = btoa(photo); + let photo64 = btoa(photo); let photoName64 = book64 + "_" + photo64 + "." + extension; - - dest.push(["Photos", photoName64]); - // I no longer see a reason for this - // dest.push(["TbSync","Photos", book64, photo64]); - - let filePath = ""; - for (let i=0; i < dest.length; i++) { - let file = FileUtils.getFile("ProfD", dest[i]); - - let foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); - foStream.init(file, 0x02 | 0x08 | 0x20, 0x180, 0); // write, create, truncate - let binary = ""; - try { - binary = atob(data.split(" ").join("")); - } catch (e) { - console.log("Failed to decode base64 string:", data); - } - foStream.write(binary, binary.length); - foStream.close(); - filePath = 'file:///' + file.path.replace(/\\/g, '\/').replace(/^\s*\/?/, '').replace(/\ /g, '%20'); + let file = new FileUtils.File(PathUtils.join(PathUtils.profileDir, "Photos", photoName64)); + let foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); + foStream.init(file, 0x02 | 0x08 | 0x20, 0x180, 0); // write, create, truncate + let binary = ""; + try { + binary = atob(data.split(" ").join("")); + } catch (e) { + console.log("Failed to decode base64 string:", data); } + foStream.write(binary, binary.length); + foStream.close(); + + let filePath = 'file:///' + file.path.replace(/\\/g, '\/').replace(/^\s*\/?/, '').replace(/\ /g, '%20'); card.setProperty("PhotoName", photoName64); card.setProperty("PhotoType", url ? "web" : "file"); card.setProperty("PhotoURI", url ? url : filePath); return filePath; } - getPhoto() { + getPhoto() { let card = this._card; let photo = card.getProperty("PhotoName", ""); let data = ""; if (photo) { try { - let file = FileUtils.getFile("ProfD", ["Photos", photo]); + let file = new FileUtils.File(PathUtils.join(PathUtils.profileDir, "Photos", photo)); let fiStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream); fiStream.init(file, -1, -1, false); - + let bstream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream); bstream.setInputStream(fiStream); data = btoa(bstream.readBytes(bstream.available())); fiStream.close(); - } catch (e) {} + } catch (e) { } } return data; } }, - AbDirectory : class { + AbDirectory: class { constructor(directory, folderData) { this._directory = directory; this._folderData = folderData; - } + } get directory() { return this._directory; } - + get logUserChanges() { return this._folderData.targetData.logUserChanges; } - + get primaryKeyField() { return this._folderData.targetData.primaryKeyField; } - + get UID() { return this._directory.UID; } @@ -578,21 +566,21 @@ abItem.setProperty(this.primaryKeyField, this._folderData.targetData.generatePrimaryKey()); //Services.console.logStringMessage("[AbDirectory::addItem] Generated primary key!"); } - + if (pretagChangelogWithByServerEntry) { abItem.changelogStatus = "added_by_server"; } - + if (abItem.isMailList && abItem._tempListDirectory) { let list = this._directory.addMailList(abItem._tempListDirectory); // the list has been added and we can now get the corresponding card via its UID let found = await this.getItemFromProperty("UID", list.UID); - + // clone and clear temporary properties - let props = {...abItem._tempProperties}; + let props = { ...abItem._tempProperties }; abItem._tempListDirectory = null; abItem._tempProperties = null; - + // store temporary properties for (const [property, value] of Object.entries(props)) { found.setProperty(property, value); @@ -606,25 +594,25 @@ throw new Error("Cannot re-add a list to a directory."); } } - + modifyItem(abItem, pretagChangelogWithByServerEntry = true) { // only add entry if the current entry does not start with _by_user let status = abItem.changelogStatus ? abItem.changelogStatus : ""; if (pretagChangelogWithByServerEntry && !status.endsWith("_by_user")) { abItem.changelogStatus = "modified_by_server"; } - - if (abItem.isMailList) { + + if (abItem.isMailList) { // get mailListDirectory let mailListDirectory = MailServices.ab.getDirectory(abItem._card.mailListURI); // store mailListDirectory.editMailListToDatabase(abItem._card); } else { - this._directory.modifyCard(abItem._card); + this._directory.modifyCard(abItem._card); } - } - + } + deleteItem(abItem, pretagChangelogWithByServerEntry = true) { if (pretagChangelogWithByServerEntry) { abItem.changelogStatus = "deleted_by_server"; @@ -644,12 +632,12 @@ if (card) { return new TbSync.addressbook.AbItem(this, card); } - + // search for list cards // we cannot search for the prop directly, because for mailinglists // they are not part of the card (expect UID) but stored in a custom storage - let searchList = "(IsMailList,=,TRUE)"; - let foundCards = await TbSync.addressbook.searchDirectory(this._directory.URI, "(or" + searchList+")"); + let searchList = "(IsMailList,=,TRUE)"; + let foundCards = await TbSync.addressbook.searchDirectory(this._directory.URI, "(or" + searchList + ")"); for (let aCard of foundCards) { let card = new TbSync.addressbook.AbItem(this, aCard); //does this list card have the req prop? @@ -659,11 +647,11 @@ } return null; } - - getAllItems () { + + getAllItems() { let rv = []; for (let card of this._directory.childCards) { - rv.push(new TbSync.addressbook.AbItem( this._directory, card )); + rv.push(new TbSync.addressbook.AbItem(this._directory, card)); } return rv; } @@ -672,36 +660,36 @@ - getAddedItemsFromChangeLog(maxitems = 0) { + getAddedItemsFromChangeLog(maxitems = 0) { return TbSync.db.getItemsFromChangeLog(this._directory.UID, maxitems, "added_by_user").map(item => item.itemId); } - getModifiedItemsFromChangeLog(maxitems = 0) { + getModifiedItemsFromChangeLog(maxitems = 0) { return TbSync.db.getItemsFromChangeLog(this._directory.UID, maxitems, "modified_by_user").map(item => item.itemId); } - - getDeletedItemsFromChangeLog(maxitems = 0) { + + getDeletedItemsFromChangeLog(maxitems = 0) { return TbSync.db.getItemsFromChangeLog(this._directory.UID, maxitems, "deleted_by_user").map(item => item.itemId); } - + getItemsFromChangeLog(maxitems = 0) { // Document what this returns return TbSync.db.getItemsFromChangeLog(this._directory.UID, maxitems, "_by_user"); } - removeItemFromChangeLog(id, moveToEndInsteadOfDelete = false) { + removeItemFromChangeLog(id, moveToEndInsteadOfDelete = false) { TbSync.db.removeItemFromChangeLog(this._directory.UID, id, moveToEndInsteadOfDelete); } - + clearChangelog() { TbSync.db.clearChangeLog(this._directory.UID); } - + }, - + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * Internal Functions // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -709,41 +697,41 @@ prepareAndCreateAddressbook: async function (folderData) { let target = folderData.getFolderProperty("target"); let provider = folderData.accountData.getAccountProperty("provider"); - + // Get cached or new unique name for new address book - let cachedName = folderData.getFolderProperty("targetName"); - let newname = cachedName == "" ? folderData.accountData.getAccountProperty("accountname") + " (" + folderData.getFolderProperty("foldername")+ ")" : cachedName; - + let cachedName = folderData.getFolderProperty("targetName"); + let newname = cachedName == "" ? folderData.accountData.getAccountProperty("accountname") + " (" + folderData.getFolderProperty("foldername") + ")" : cachedName; + //Create the new book with the unique name let directory = await folderData.targetData.createAddressbook(newname); if (directory && directory instanceof Components.interfaces.nsIAbDirectory) { directory.setStringValue("tbSyncProvider", provider); directory.setStringValue("tbSyncAccountID", folderData.accountData.accountID); - + // Prevent gContactSync to inject its stuff into New/EditCard dialogs // https://github.com/jdgeenen/gcontactsync/pull/127 directory.setStringValue("gContactSyncSkipped", "true"); - folderData.setFolderProperty("target", directory.UID); + folderData.setFolderProperty("target", directory.UID); folderData.setFolderProperty("targetName", directory.dirName); //notify about new created address book Services.obs.notifyObservers(null, 'tbsync.observer.addressbook.created', null) return directory; } - + return null; }, - getFolderFromDirectoryUID: function(bookUID) { - let folders = TbSync.db.findFolders({"target": bookUID}); + getFolderFromDirectoryUID: function (bookUID) { + let folders = TbSync.db.findFolders({ "target": bookUID }); if (folders.length == 1) { let accountData = new TbSync.AccountData(folders[0].accountID); return new TbSync.FolderData(accountData, folders[0].folderID); } return null; }, - - getDirectoryFromDirectoryUID: function(UID) { + + getDirectoryFromDirectoryUID: function (UID) { if (!UID) return null; @@ -751,399 +739,399 @@ if (directory instanceof Components.interfaces.nsIAbDirectory) { if (directory.UID == UID) return directory; } - } + } return null; }, - - getListInfoFromListUID: async function(UID) { + + getListInfoFromListUID: async function (UID) { for (let directory of MailServices.ab.directories) { if (directory instanceof Components.interfaces.nsIAbDirectory && !directory.isRemote) { let searchList = "(IsMailList,=,TRUE)"; - let foundCards = await TbSync.addressbook.searchDirectory(directory.URI, "(and" + searchList+")"); + let foundCards = await TbSync.addressbook.searchDirectory(directory.URI, "(and" + searchList + ")"); for (let listCard of foundCards) { //return after first found card - if (listCard.UID == UID) return {directory, listCard}; + if (listCard.UID == UID) return { directory, listCard }; } } - } + } throw new Error("List with UID <" + UID + "> does not exists"); }, - + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * Addressbook Observer and Listener // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - + addressbookObserver: { observe: async function (aSubject, aTopic, aData) { switch (aTopic) { // we do not need addrbook-created case "addrbook-directory-updated": case "addrbook-directory-deleted": - { - //aSubject: nsIAbDirectory (we can get URI and UID directly from the object, but the directory no longer exists) - aSubject.QueryInterface(Components.interfaces.nsIAbDirectory); - let bookUID = aSubject.UID; - - let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); - if (folderData - && folderData.targetData - && folderData.targetData.isAdvancedAddressbookTargetData) { - - switch(aTopic) { - case "addrbook-directory-updated": - { - //update name of target (if changed) - folderData.setFolderProperty("targetName", aSubject.dirName); - //update settings window, if open - Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID); + { + //aSubject: nsIAbDirectory (we can get URI and UID directly from the object, but the directory no longer exists) + aSubject.QueryInterface(Components.interfaces.nsIAbDirectory); + let bookUID = aSubject.UID; + + let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); + if (folderData + && folderData.targetData + && folderData.targetData.isAdvancedAddressbookTargetData) { + + switch (aTopic) { + case "addrbook-directory-updated": + { + //update name of target (if changed) + folderData.setFolderProperty("targetName", aSubject.dirName); + //update settings window, if open + Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID); + } + break; + + case "addrbook-directory-deleted": + { + //delete any pending changelog of the deleted book + TbSync.db.clearChangeLog(bookUID); + + //unselect book if deleted by user and update settings window, if open + if (folderData.getFolderProperty("selected")) { + folderData.setFolderProperty("selected", false); + //update settings window, if open + Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID); + } + + folderData.resetFolderProperty("target"); + } + break; } - break; - case "addrbook-directory-deleted": - { - //delete any pending changelog of the deleted book - TbSync.db.clearChangeLog(bookUID); - - //unselect book if deleted by user and update settings window, if open - if (folderData.getFolderProperty("selected")) { - folderData.setFolderProperty("selected", false); - //update settings window, if open - Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID); - } - - folderData.resetFolderProperty("target"); - } - break; + folderData.targetData.directoryObserver(aTopic); } - - folderData.targetData.directoryObserver(aTopic); } - } - break; + break; case "addrbook-contact-created": case "addrbook-contact-updated": case "addrbook-contact-deleted": - { - //aSubject: nsIAbCard - aSubject.QueryInterface(Components.interfaces.nsIAbCard); - //aData: 128-bit unique identifier for the parent directory - let bookUID = aData; - - let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); - if (folderData - && folderData.targetData - && folderData.targetData.isAdvancedAddressbookTargetData) { - - let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(bookUID); - let abDirectory = new TbSync.addressbook.AbDirectory(directory, folderData); - let abItem = new TbSync.addressbook.AbItem(abDirectory, aSubject); - let itemStatus = abItem.changelogStatus || ""; - - // during create the following can happen - // card has no primary key - // another process could try to mod - // -> we need to identify this card with an always available ID and block any other MODS until we free it again - // -> store creation type - - if (aTopic == "addrbook-contact-created" && itemStatus == "") { - // add this new card to changelog to keep track of it - TbSync.db.addItemToChangeLog(bookUID, aSubject.UID + "#DelayedUserCreation", Date.now()); - // new cards must get a NEW(!) primaryKey first - if (abDirectory.primaryKeyField) { - console.log("New primary Key generated!"); - abItem.setProperty(abDirectory.primaryKeyField, folderData.targetData.generatePrimaryKey()); - } - // special case: do not add "modified_by_server" - abDirectory.modifyItem(abItem, /*pretagChangelogWithByServerEntry */ false); - // We will see this card again as updated but delayed created - return; - } - - // during follow up MODs we can identify this card via - let delayedUserCreation = TbSync.db.getItemStatusFromChangeLog(bookUID, aSubject.UID + "#DelayedUserCreation"); - - // if we reach this point and if we have adelayedUserCreation, - // we can remove the delayedUserCreation marker and can - // continue to process this event as an addrbook-contact-created - let bTopic = aTopic; - if (delayedUserCreation) { - let age = Date.now() - delayedUserCreation; - if (age < 1500) { - bTopic = "addrbook-contact-created"; - } else { - TbSync.db.removeItemFromChangeLog(bookUID, aSubject.UID + "#DelayedUserCreation"); - } - } - - // if this card was created by us, it will be in the log - // we want to ignore any MOD for a freeze time, because - // gContactSync modifies our(!) contacts (GoogleID) after we added them, so they get - // turned into "modified_by_user" and will be send back to the server. - if (itemStatus && itemStatus.endsWith("_by_server")) { - let age = Date.now() - abItem.changelogData.timestamp; - if (age < 1500) { - // during freeze, local modifications are not possible + { + //aSubject: nsIAbCard + aSubject.QueryInterface(Components.interfaces.nsIAbCard); + //aData: 128-bit unique identifier for the parent directory + let bookUID = aData; + + let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); + if (folderData + && folderData.targetData + && folderData.targetData.isAdvancedAddressbookTargetData) { + + let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(bookUID); + let abDirectory = new TbSync.addressbook.AbDirectory(directory, folderData); + let abItem = new TbSync.addressbook.AbItem(abDirectory, aSubject); + let itemStatus = abItem.changelogStatus || ""; + + // during create the following can happen + // card has no primary key + // another process could try to mod + // -> we need to identify this card with an always available ID and block any other MODS until we free it again + // -> store creation type + + if (aTopic == "addrbook-contact-created" && itemStatus == "") { + // add this new card to changelog to keep track of it + TbSync.db.addItemToChangeLog(bookUID, aSubject.UID + "#DelayedUserCreation", Date.now()); + // new cards must get a NEW(!) primaryKey first + if (abDirectory.primaryKeyField) { + console.log("New primary Key generated!"); + abItem.setProperty(abDirectory.primaryKeyField, folderData.targetData.generatePrimaryKey()); + } + // special case: do not add "modified_by_server" + abDirectory.modifyItem(abItem, /*pretagChangelogWithByServerEntry */ false); + // We will see this card again as updated but delayed created return; - } else { - // remove blocking entry from changelog after freeze time is over (1.5s), - // and continue evaluating this event - abItem.changelogStatus = ""; } - } - - // From here on, we only process user changes as server changes are self freezed - // update changelog based on old status - switch (bTopic) { - case "addrbook-contact-created": - { - switch (itemStatus) { - case "added_by_user": - // late create notification - break; - - case "modified_by_user": - // late create notification - abItem.changelogStatus = "added_by_user"; - break; - - case "deleted_by_user": - // unprocessed delete for this card, undo the delete (moved out and back in) - abItem.changelogStatus = "modified_by_user"; - break; - - default: - // new card - abItem.changelogStatus = "added_by_user"; + + // during follow up MODs we can identify this card via + let delayedUserCreation = TbSync.db.getItemStatusFromChangeLog(bookUID, aSubject.UID + "#DelayedUserCreation"); + + // if we reach this point and if we have adelayedUserCreation, + // we can remove the delayedUserCreation marker and can + // continue to process this event as an addrbook-contact-created + let bTopic = aTopic; + if (delayedUserCreation) { + let age = Date.now() - delayedUserCreation; + if (age < 1500) { + bTopic = "addrbook-contact-created"; + } else { + TbSync.db.removeItemFromChangeLog(bookUID, aSubject.UID + "#DelayedUserCreation"); } } - break; - case "addrbook-contact-updated": - { - switch (itemStatus) { - case "added_by_user": - // unprocessed add for this card, keep status - break; - - case "modified_by_user": - // double notification, keep status - break; - - case "deleted_by_user": - // race? unprocessed delete for this card, moved out and back in and modified - default: - abItem.changelogStatus = "modified_by_user"; - break; + // if this card was created by us, it will be in the log + // we want to ignore any MOD for a freeze time, because + // gContactSync modifies our(!) contacts (GoogleID) after we added them, so they get + // turned into "modified_by_user" and will be send back to the server. + if (itemStatus && itemStatus.endsWith("_by_server")) { + let age = Date.now() - abItem.changelogData.timestamp; + if (age < 1500) { + // during freeze, local modifications are not possible + return; + } else { + // remove blocking entry from changelog after freeze time is over (1.5s), + // and continue evaluating this event + abItem.changelogStatus = ""; } } - break; - - case "addrbook-contact-deleted": - { - switch (itemStatus) { - case "added_by_user": - // unprocessed add for this card, revert - abItem.changelogStatus = ""; - return; - - case "deleted_by_user": - // double notification - break; - - case "modified_by_user": - // unprocessed mod for this card - default: - abItem.changelogStatus = "deleted_by_user"; - break; - } + + // From here on, we only process user changes as server changes are self freezed + // update changelog based on old status + switch (bTopic) { + case "addrbook-contact-created": + { + switch (itemStatus) { + case "added_by_user": + // late create notification + break; + + case "modified_by_user": + // late create notification + abItem.changelogStatus = "added_by_user"; + break; + + case "deleted_by_user": + // unprocessed delete for this card, undo the delete (moved out and back in) + abItem.changelogStatus = "modified_by_user"; + break; + + default: + // new card + abItem.changelogStatus = "added_by_user"; + } + } + break; + + case "addrbook-contact-updated": + { + switch (itemStatus) { + case "added_by_user": + // unprocessed add for this card, keep status + break; + + case "modified_by_user": + // double notification, keep status + break; + + case "deleted_by_user": + // race? unprocessed delete for this card, moved out and back in and modified + default: + abItem.changelogStatus = "modified_by_user"; + break; + } + } + break; + + case "addrbook-contact-deleted": + { + switch (itemStatus) { + case "added_by_user": + // unprocessed add for this card, revert + abItem.changelogStatus = ""; + return; + + case "deleted_by_user": + // double notification + break; + + case "modified_by_user": + // unprocessed mod for this card + default: + abItem.changelogStatus = "deleted_by_user"; + break; + } + } + break; } - break; - } - if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); - - // notify observers only if status changed - if (itemStatus != abItem.changelogStatus) { - folderData.targetData.cardObserver(bTopic, abItem); - } - return; - } - } - break; + if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); - case "addrbook-list-created": - case "addrbook-list-deleted": - { - //aSubject: nsIAbDirectory - aSubject.QueryInterface(Components.interfaces.nsIAbDirectory); - //aData: 128-bit unique identifier for the parent directory - let bookUID = aData; - - let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); - if (folderData - && folderData.targetData - && folderData.targetData.isAdvancedAddressbookTargetData) { - - let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(bookUID); - let abDirectory = new TbSync.addressbook.AbDirectory(directory, folderData); - let abItem = new TbSync.addressbook.AbItem(abDirectory, aSubject); - - let itemStatus = abItem.changelogStatus; - if (itemStatus && itemStatus.endsWith("_by_server")) { - //we caused this, ignore - abItem.changelogStatus = ""; + // notify observers only if status changed + if (itemStatus != abItem.changelogStatus) { + folderData.targetData.cardObserver(bTopic, abItem); + } return; } + } + break; - // update changelog based on old status - switch (aTopic) { - case "addrbook-list-created": - { - if (abDirectory.primaryKeyField) { - // Since we do not need to update a list, to make custom properties persistent, we do not need to use delayedUserCreation as with contacts. - abItem.setProperty(abDirectory.primaryKeyField, folderData.targetData.generatePrimaryKey()); - } - - switch (itemStatus) { - case "added_by_user": - // double notification, which is probably impossible, keep status - break; - - case "modified_by_user": - // late create notification - abItem.changelogStatus = "added_by_user"; - break; - - case "deleted_by_user": - // unprocessed delete for this card, undo the delete (moved out and back in) - abItem.changelogStatus = "modified_by_user"; - break; - - default: - // new list - abItem.changelogStatus = "added_by_user"; - break; - } + case "addrbook-list-created": + case "addrbook-list-deleted": + { + //aSubject: nsIAbDirectory + aSubject.QueryInterface(Components.interfaces.nsIAbDirectory); + //aData: 128-bit unique identifier for the parent directory + let bookUID = aData; + + let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); + if (folderData + && folderData.targetData + && folderData.targetData.isAdvancedAddressbookTargetData) { + + let directory = TbSync.addressbook.getDirectoryFromDirectoryUID(bookUID); + let abDirectory = new TbSync.addressbook.AbDirectory(directory, folderData); + let abItem = new TbSync.addressbook.AbItem(abDirectory, aSubject); + + let itemStatus = abItem.changelogStatus; + if (itemStatus && itemStatus.endsWith("_by_server")) { + //we caused this, ignore + abItem.changelogStatus = ""; + return; } - break; - case "addrbook-list-deleted": - { - switch (itemStatus) { - case "added_by_user": - // unprocessed add for this card, revert - abItem.changelogStatus = ""; - return; - - case "modified_by_user": - // unprocessed mod for this card - case "deleted_by_user": - // double notification - default: - abItem.changelogStatus = "deleted_by_user"; - break; - } - //remove properties of this ML stored in changelog - TbSync.db.clearChangeLog(abDirectory.UID + "#" + abItem.UID); + // update changelog based on old status + switch (aTopic) { + case "addrbook-list-created": + { + if (abDirectory.primaryKeyField) { + // Since we do not need to update a list, to make custom properties persistent, we do not need to use delayedUserCreation as with contacts. + abItem.setProperty(abDirectory.primaryKeyField, folderData.targetData.generatePrimaryKey()); + } + + switch (itemStatus) { + case "added_by_user": + // double notification, which is probably impossible, keep status + break; + + case "modified_by_user": + // late create notification + abItem.changelogStatus = "added_by_user"; + break; + + case "deleted_by_user": + // unprocessed delete for this card, undo the delete (moved out and back in) + abItem.changelogStatus = "modified_by_user"; + break; + + default: + // new list + abItem.changelogStatus = "added_by_user"; + break; + } + } + break; + + case "addrbook-list-deleted": + { + switch (itemStatus) { + case "added_by_user": + // unprocessed add for this card, revert + abItem.changelogStatus = ""; + return; + + case "modified_by_user": + // unprocessed mod for this card + case "deleted_by_user": + // double notification + default: + abItem.changelogStatus = "deleted_by_user"; + break; + } + //remove properties of this ML stored in changelog + TbSync.db.clearChangeLog(abDirectory.UID + "#" + abItem.UID); + } + break; } - break; - } - if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); - folderData.targetData.listObserver(aTopic, abItem, null); + if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); + folderData.targetData.listObserver(aTopic, abItem, null); + } } - } - break; + break; - case "addrbook-list-updated": - { - // aSubject: nsIAbDirectory - aSubject.QueryInterface(Components.interfaces.nsIAbDirectory); - // get the card representation of this list, including its parent directory - let listInfo = await TbSync.addressbook.getListInfoFromListUID(aSubject.UID); - let bookUID = listInfo.directory.UID; - - let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); - if (folderData - && folderData.targetData - && folderData.targetData.isAdvancedAddressbookTargetData) { - - let abDirectory = new TbSync.addressbook.AbDirectory(listInfo.directory, folderData); - let abItem = new TbSync.addressbook.AbItem(abDirectory, listInfo.listCard); - - let itemStatus = abItem.changelogStatus; - if (itemStatus && itemStatus.endsWith("_by_server")) { - //we caused this, ignore - abItem.changelogStatus = ""; - return; - } + case "addrbook-list-updated": + { + // aSubject: nsIAbDirectory + aSubject.QueryInterface(Components.interfaces.nsIAbDirectory); + // get the card representation of this list, including its parent directory + let listInfo = await TbSync.addressbook.getListInfoFromListUID(aSubject.UID); + let bookUID = listInfo.directory.UID; + + let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); + if (folderData + && folderData.targetData + && folderData.targetData.isAdvancedAddressbookTargetData) { + + let abDirectory = new TbSync.addressbook.AbDirectory(listInfo.directory, folderData); + let abItem = new TbSync.addressbook.AbItem(abDirectory, listInfo.listCard); + + let itemStatus = abItem.changelogStatus; + if (itemStatus && itemStatus.endsWith("_by_server")) { + //we caused this, ignore + abItem.changelogStatus = ""; + return; + } - // update changelog based on old status - switch (aTopic) { - case "addrbook-list-updated": - { - switch (itemStatus) { - case "added_by_user": - // unprocessed add for this card, keep status - break; - - case "modified_by_user": - // double notification, keep status - break; - - case "deleted_by_user": - // race? unprocessed delete for this card, moved out and back in and modified - default: - abItem.changelogStatus = "modified_by_user"; - break; - } + // update changelog based on old status + switch (aTopic) { + case "addrbook-list-updated": + { + switch (itemStatus) { + case "added_by_user": + // unprocessed add for this card, keep status + break; + + case "modified_by_user": + // double notification, keep status + break; + + case "deleted_by_user": + // race? unprocessed delete for this card, moved out and back in and modified + default: + abItem.changelogStatus = "modified_by_user"; + break; + } + } + break; } - break; + + if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); + folderData.targetData.listObserver(aTopic, abItem, null); } - - if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); - folderData.targetData.listObserver(aTopic, abItem, null); } - } - break; - + break; + // unknown, if called for programmatically added members as well, probably not case "addrbook-list-member-added": //exclude contact without Email - notification is wrongly send case "addrbook-list-member-removed": - { - //aSubject: nsIAbCard of Member - aSubject.QueryInterface(Components.interfaces.nsIAbCard); - //aData: 128-bit unique identifier for the list - let listInfo = await TbSync.addressbook.getListInfoFromListUID(aData); - let bookUID = listInfo.directory.UID; - - let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); - if (folderData - && folderData.targetData - && folderData.targetData.isAdvancedAddressbookTargetData) { - - let abDirectory = new TbSync.addressbook.AbDirectory(listInfo.directory, folderData); - let abItem = new TbSync.addressbook.AbItem(abDirectory, listInfo.listCard); - let abMember = new TbSync.addressbook.AbItem(abDirectory, aSubject); - - if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); - folderData.targetData.listObserver(aTopic, abItem, abMember); - - // removed, added members cause the list to be changed - let mailListDirectory = MailServices.ab.getDirectory(listInfo.listCard.mailListURI); - TbSync.addressbook.addressbookObserver.observe(mailListDirectory, "addrbook-list-updated", null); - return; + { + //aSubject: nsIAbCard of Member + aSubject.QueryInterface(Components.interfaces.nsIAbCard); + //aData: 128-bit unique identifier for the list + let listInfo = await TbSync.addressbook.getListInfoFromListUID(aData); + let bookUID = listInfo.directory.UID; + + let folderData = TbSync.addressbook.getFolderFromDirectoryUID(bookUID); + if (folderData + && folderData.targetData + && folderData.targetData.isAdvancedAddressbookTargetData) { + + let abDirectory = new TbSync.addressbook.AbDirectory(listInfo.directory, folderData); + let abItem = new TbSync.addressbook.AbItem(abDirectory, listInfo.listCard); + let abMember = new TbSync.addressbook.AbItem(abDirectory, aSubject); + + if (abDirectory.logUserChanges) TbSync.core.setTargetModified(folderData); + folderData.targetData.listObserver(aTopic, abItem, abMember); + + // removed, added members cause the list to be changed + let mailListDirectory = MailServices.ab.getDirectory(listInfo.listCard.mailListURI); + TbSync.addressbook.addressbookObserver.observe(mailListDirectory, "addrbook-list-updated", null); + return; + } } - } - break; + break; } } }, - + } diff -Nru tbsync-4.7/content/modules/core.js tbsync-4.12/content/modules/core.js --- tbsync-4.7/content/modules/core.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/core.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; var core = { @@ -121,7 +121,10 @@ overallStatusData = listStatusData; accountRerun = (listStatusData.type == TbSync.StatusData.ACCOUNT_RERUN) TbSync.eventlog.add(listStatusData.type, syncData.eventLogInfo, listStatusData.message, listStatusData.details); + await new Promise(r => TbSync.window.setTimeout(r, 5000)); continue; //jumps to the while condition check + } else { + overallStatusData = new TbSync.StatusData(); } // Removes all leftover cached folders and sets all other folders to a well defined cached = "0" diff -Nru tbsync-4.7/content/modules/db.js tbsync-4.12/content/modules/db.js --- tbsync-4.7/content/modules/db.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/db.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,9 +6,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; -var { DeferredTask } = ChromeUtils.import("resource://gre/modules/DeferredTask.jsm"); +var { DeferredTask } = ChromeUtils.importESModule("resource://gre/modules/DeferredTask.sys.mjs"); var db = { @@ -62,51 +62,6 @@ return "MZTB" + uuid; } - // try to migrate old accounts file from TB60 - if (!this.files["accounts"].found) { - try { - let accounts = await IOUtils.readJSON(TbSync.io.getAbsolutePath("accounts.json")); - for (let d of Object.values(accounts.data)) { - console.log("Migrating: " + JSON.stringify(d)); - - let settings = {}; - settings.status = "disabled"; - settings.provider = d.provider; - settings.https = (d.https == "1"); - - switch (d.provider) { - case "dav": - settings.calDavHost = d.host ? d.host : ""; - settings.cardDavHost = d.host2 ? d.host2 : ""; - settings.serviceprovider = d.serviceprovider; - settings.user = d.user; - settings.syncGroups = (d.syncGroups == "1"); - settings.useCalendarCache = (d.useCache == "1"); - break; - - case "eas": - settings.useragent = d.useragent; - settings.devicetype = d.devicetype; - settings.deviceId = getNewDeviceId4Migration(); - settings.asversionselected = d.asversionselected; - settings.asversion = d.asversion; - settings.host = d.host; - settings.user = d.user; - settings.servertype = d.servertype; - settings.seperator = d.seperator; - settings.provision = (d.provision == "1"); - settings.displayoverride = (d.displayoverride == "1"); - if (d.hasOwnProperty("galautocomplete")) settings.galautocomplete = (d.galautocomplete == "1"); - break; - } - - this.addAccount(d.accountname, settings); - } - } catch (e) { - Components.utils.reportError(e); - } - } - this.loaded = true; }, diff -Nru tbsync-4.7/content/modules/eventlog.js tbsync-4.12/content/modules/eventlog.js --- tbsync-4.7/content/modules/eventlog.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/eventlog.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; /** * diff -Nru tbsync-4.7/content/modules/io.js tbsync-4.12/content/modules/io.js --- tbsync-4.7/content/modules/io.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/io.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,8 +6,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; +ChromeUtils.defineESModuleGetters(this, { + FileUtils: "resource://gre/modules/FileUtils.sys.mjs", +}); + var io = { storageDirectory : PathUtils.join(PathUtils.profileDir, "TbSync"), @@ -23,7 +27,7 @@ }, initFile: function (filename) { - let file = FileUtils.getFile("ProfD", ["TbSync",filename]); + let file = new FileUtils.File(PathUtils.join(PathUtils.profileDir, "TbSync", filename)); //create a stream to write to that file let foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); foStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0666", 8), 0); // write, create, truncate @@ -31,7 +35,7 @@ }, appendToFile: function (filename, data) { - let file = FileUtils.getFile("ProfD", ["TbSync",filename]); + let file = new FileUtils.File(PathUtils.join(PathUtils.profileDir, "TbSync", filename)); //create a strem to write to that file let foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); foStream.init(file, 0x02 | 0x08 | 0x10, parseInt("0666", 8), 0); // write, create, append diff -Nru tbsync-4.7/content/modules/lightning.js tbsync-4.12/content/modules/lightning.js --- tbsync-4.7/content/modules/lightning.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/lightning.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,11 +6,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; - var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); - - XPCOMUtils.defineLazyModuleGetters(this, { +var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetters(this, { CalAlarm: "resource:///modules/CalAlarm.jsm", CalAttachment: "resource:///modules/CalAttachment.jsm", CalAttendee: "resource:///modules/CalAttendee.jsm", @@ -26,7 +26,7 @@ load: async function () { try { TbSync.lightning.cal = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm").cal; - TbSync.lightning.ICAL = ChromeUtils.import("resource:///modules/calendar/Ical.jsm").ICAL; + TbSync.lightning.ICAL = ChromeUtils.importESModule("resource:///modules/calendar/Ical.sys.mjs").default; let manager = TbSync.lightning.cal.manager; manager.addCalendarObserver(this.calendarObserver); manager.addObserver(this.calendarManagerObserver); @@ -254,8 +254,8 @@ this._tbCalendar = TbCalendar; this._item = item; - this._isTodo = (item instanceof Ci.calITodo); - this._isEvent = (item instanceof Ci.calIEvent); + this._isTodo = item.isTodo ? item.isTodo() : (item instanceof Ci.calITodo); + this._isEvent = item.isEvent ? item.isEvent() : (item instanceof Ci.calIEvent); } get tbCalendar() { diff -Nru tbsync-4.7/content/modules/manager.js tbsync-4.12/content/modules/manager.js --- tbsync-4.7/content/modules/manager.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/manager.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,8 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; - +"use strict"; + +var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm"); + var manager = { prefWindowObj: null, diff -Nru tbsync-4.7/content/modules/messenger.js tbsync-4.12/content/modules/messenger.js --- tbsync-4.7/content/modules/messenger.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/messenger.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; + +var { OverlayManager } = ChromeUtils.import("chrome://tbsync/content/OverlayManager.jsm"); + var messenger = { overlayManager : null, diff -Nru tbsync-4.7/content/modules/network.js tbsync-4.12/content/modules/network.js --- tbsync-4.7/content/modules/network.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/network.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; var network = { diff -Nru tbsync-4.7/content/modules/passwordManager.js tbsync-4.12/content/modules/passwordManager.js --- tbsync-4.7/content/modules/passwordManager.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/passwordManager.js 2024-08-27 16:32:08.000000000 +0000 @@ -5,8 +5,8 @@ * 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 passwordManager = { @@ -32,14 +32,14 @@ } }, - updateLoginInfo: function(origin, realm, oldUser, newUser, newPassword) { + updateLoginInfo: async function(origin, realm, oldUser, newUser, newPassword) { let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Components.interfaces.nsILoginInfo, "init"); this.removeLoginInfos(origin, realm, [oldUser, newUser]); let newLoginInfo = new nsLoginInfo(origin, null, realm, newUser, newPassword, "", ""); try { - Services.logins.addLogin(newLoginInfo); + await Services.logins.addLoginAsync(newLoginInfo); } catch (e) { TbSync.dump("Error adding loginInfo", e); } diff -Nru tbsync-4.7/content/modules/providers.js tbsync-4.12/content/modules/providers.js --- tbsync-4.7/content/modules/providers.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/providers.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,8 +6,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; - +"use strict"; + +var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); +var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm"); + var providers = { //list of default providers (available in add menu, even if not installed) @@ -15,9 +18,6 @@ "google" : { name: "Google's People API", homepageUrl: "https://addons.thunderbird.net/addon/google-4-tbsync/"}, - "dav" : { - name: "CalDAV & CardDAV", - homepageUrl: "https://addons.thunderbird.net/addon/dav-4-tbsync/"}, "eas" : { name: "Exchange ActiveSync", homepageUrl: "https://addons.thunderbird.net/addon/eas-4-tbsync/"}, diff -Nru tbsync-4.7/content/modules/public.js tbsync-4.12/content/modules/public.js --- tbsync-4.7/content/modules/public.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/public.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,12 +6,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; /** * */ - var StatusData = class { +var StatusData = class { /** * A StatusData instance must be used as return value by * :class:`Base.syncFolderList` and :class:`Base.syncFolder`. diff -Nru tbsync-4.7/content/modules/tools.js tbsync-4.12/content/modules/tools.js --- tbsync-4.7/content/modules/tools.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/modules/tools.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; var tools = { diff -Nru tbsync-4.7/content/overlays/messenger.js tbsync-4.12/content/overlays/messenger.js --- tbsync-4.7/content/overlays/messenger.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/overlays/messenger.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,7 @@ * 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"); diff -Nru tbsync-4.7/content/passwordPrompt/passwordPrompt.js tbsync-4.12/content/passwordPrompt/passwordPrompt.js --- tbsync-4.7/content/passwordPrompt/passwordPrompt.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/passwordPrompt/passwordPrompt.js 2024-08-27 16:32:08.000000000 +0000 @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; var tbSyncPassword = { diff -Nru tbsync-4.7/content/scripts/bootstrap.js tbsync-4.12/content/scripts/bootstrap.js --- tbsync-4.7/content/scripts/bootstrap.js 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/scripts/bootstrap.js 2024-08-27 16:32:08.000000000 +0000 @@ -5,10 +5,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -function startup(data, reason) { + + function startup(data, reason) { // possible reasons: APP_STARTUP, ADDON_ENABLE, ADDON_INSTALL, ADDON_UPGRADE, or ADDON_DOWNGRADE. // set default prefs @@ -40,7 +39,6 @@ TbSync.enabled = false; TbSync.unload().then(function() { Cu.unload("chrome://tbsync/content/tbsync.jsm"); - Cu.unload("chrome://tbsync/content/HttpRequest.jsm"); Cu.unload("chrome://tbsync/content/OverlayManager.jsm"); // HACK WARNING: // - the Addon Manager does not properly clear all addon related caches on update; diff -Nru tbsync-4.7/content/tbsync.jsm tbsync-4.12/content/tbsync.jsm --- tbsync-4.7/content/tbsync.jsm 2023-08-29 20:22:49.000000000 +0000 +++ tbsync-4.12/content/tbsync.jsm 2024-08-27 16:32:08.000000000 +0000 @@ -6,17 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; +"use strict"; var EXPORTED_SYMBOLS = ["TbSync"]; -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm"); -var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); -var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); -var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm"); -var { OverlayManager } = ChromeUtils.import("chrome://tbsync/content/OverlayManager.jsm"); - var TbSync = { enabled: false, @@ -39,8 +32,8 @@ // global load load: async function (window, addon, extension) { //public module and IO module needs to be loaded beforehand - Services.scriptloader.loadSubScript("chrome://tbsync/content/modules/public.js", this, "UTF-8"); - Services.scriptloader.loadSubScript("chrome://tbsync/content/modules/io.js", this, "UTF-8"); + Services.scriptloader.loadSubScript("chrome://tbsync/content/modules/public.js", TbSync, "UTF-8"); + Services.scriptloader.loadSubScript("chrome://tbsync/content/modules/io.js", TbSync, "UTF-8"); //clear debug log on start this.io.initFile("debug.log"); @@ -70,7 +63,7 @@ //load modules for (let module of this.modules) { try { - Services.scriptloader.loadSubScript("chrome://tbsync/content/modules/" + module.name + ".js", this, "UTF-8"); + Services.scriptloader.loadSubScript("chrome://tbsync/content/modules/" + module.name + ".js", TbSync, "UTF-8"); module.state = 1; this.dump("Loading module <" + module.name + ">", "OK"); } catch (e) { diff -Nru tbsync-4.7/debian/changelog tbsync-4.12/debian/changelog --- tbsync-4.7/debian/changelog 2023-10-14 07:22:21.000000000 +0000 +++ tbsync-4.12/debian/changelog 2024-09-14 16:12:51.000000000 +0000 @@ -1,3 +1,29 @@ +tbsync (4.12-1~deb12u1) bookworm; urgency=medium + + [ Mechtilde ] + * [b716540] Merge branch 'debian/sid' into debian/bookworm + * Prepared for release in bookworm (proposed-updates) + + -- Mechtilde Stehmann Sat, 14 Sep 2024 18:12:51 +0200 + +tbsync (4.12-1) unstable; urgency=medium + + [ Mechtilde ] + * [f3a1684] New upstream version 4.12 + * [2a72a9f] Bumped version of dependencies + + -- Mechtilde Stehmann Sat, 31 Aug 2024 17:33:27 +0200 + +tbsync (4.8-1) unstable; urgency=medium + + [ Mechtilde ] + * [958ffe6] New upstream version 4.8 + * [2a36c0a] Bumped standard version - no changes needed; + bumped version of dependencies + * [fbee43b] Bumped year in d/copyright + + -- Mechtilde Stehmann Wed, 15 May 2024 11:24:50 +0200 + tbsync (4.7-1~deb12u1) bookworm; urgency=medium [ Mechtilde ] diff -Nru tbsync-4.7/debian/control tbsync-4.12/debian/control --- tbsync-4.7/debian/control 2023-10-11 18:36:34.000000000 +0000 +++ tbsync-4.12/debian/control 2024-09-14 15:44:46.000000000 +0000 @@ -4,7 +4,7 @@ Maintainer: Debian Mozilla Extension Maintainers Uploaders: Mechtilde Stehmann Build-Depends: debhelper-compat (= 13), zip -Standards-Version: 4.6.1 +Standards-Version: 4.7.0 Rules-Requires-Root: no Vcs-Git: https://salsa.debian.org/webext-team/tbsync.git Vcs-Browser: https://salsa.debian.org/webext-team/tbsync @@ -13,9 +13,10 @@ Package: webext-tbsync Architecture: all Depends: ${misc:Depends} - , thunderbird (>= 1:115.3) -Recommends: webext-dav4tbsync (>= 4.7) - , webext-eas4tbsync (>= 4.7) + , thunderbird (>= 1:128.0) + , thunderbird (<= 1:128.x) +Recommends: webext-dav4tbsync (>= 4.8) + , webext-eas4tbsync (>= 4.11) Description: Thunderbird/Lightning Add-On to support MS Exchange Calendar etc. Synchronize Exchange ActiveSync accounts (contacts, tasks and calendars) to Thunderbird, supports Office 365, Outlook.com, diff -Nru tbsync-4.7/debian/copyright tbsync-4.12/debian/copyright --- tbsync-4.7/debian/copyright 2022-04-17 11:20:34.000000000 +0000 +++ tbsync-4.12/debian/copyright 2024-09-14 15:43:13.000000000 +0000 @@ -3,7 +3,7 @@ Source: https://github.com/jobisoft/TbSync Files: * -Copyright: 2017-2022 john.bieling@gmx.de +Copyright: 2017-2024 john.bieling@gmx.de License: MPL-2.0 Files: content/skin/tbsync.png @@ -39,7 +39,7 @@ License: CC0-1.0 Files: debian/* -Copyright: 2018-2022 Mechtilde Stehmann