Version in base suite: 1.7-1 Base version: tbsync_1.7-1 Target version: tbsync_2.11-1~deb10u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/t/tbsync/tbsync_1.7-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/t/tbsync/tbsync_2.11-1~deb10u1.dsc /srv/release.debian.org/tmp/1T3ioFy55C/tbsync-2.11/screenshots/CalDAV-UserName.png |binary /srv/release.debian.org/tmp/1T3ioFy55C/tbsync-2.11/screenshots/custom_provider.PNG |binary /srv/release.debian.org/tmp/1T3ioFy55C/tbsync-2.11/screenshots/missing_provider.PNG |binary tbsync-2.11/CONTRIBUTORS.md | 16 tbsync-2.11/Makebeta | 39 tbsync-2.11/Makefile.bat | 10 tbsync-2.11/README.md | 29 tbsync-2.11/UPDATE68.md | 24 tbsync-2.11/_locales/Readme.txt | 8 tbsync-2.11/_locales/bg/messages.json | 5 tbsync-2.11/_locales/bg/tbSync.dtd | 83 tbsync-2.11/_locales/bg/tbSync.properties | 67 tbsync-2.11/_locales/de/messages.json | 5 tbsync-2.11/_locales/de/tbSync.dtd | 83 tbsync-2.11/_locales/de/tbSync.properties | 67 tbsync-2.11/_locales/en-US/messages.json | 5 tbsync-2.11/_locales/en-US/tbSync.dtd | 83 tbsync-2.11/_locales/en-US/tbSync.properties | 67 tbsync-2.11/_locales/fr/messages.json | 5 tbsync-2.11/_locales/fr/tbSync.dtd | 83 tbsync-2.11/_locales/fr/tbSync.properties | 67 tbsync-2.11/_locales/hu/messages.json | 5 tbsync-2.11/_locales/hu/tbSync.dtd | 83 tbsync-2.11/_locales/hu/tbSync.properties | 67 tbsync-2.11/_locales/it/messages.json | 5 tbsync-2.11/_locales/it/tbSync.dtd | 83 tbsync-2.11/_locales/it/tbSync.properties | 67 tbsync-2.11/_locales/pl/messages.json | 5 tbsync-2.11/_locales/pl/tbSync.dtd | 83 tbsync-2.11/_locales/pl/tbSync.properties | 67 tbsync-2.11/_locales/pt_BR/messages.json | 5 tbsync-2.11/_locales/pt_BR/tbSync.dtd | 83 tbsync-2.11/_locales/pt_BR/tbSync.properties | 67 tbsync-2.11/_locales/ru/messages.json | 5 tbsync-2.11/_locales/ru/tbSync.dtd | 83 tbsync-2.11/_locales/ru/tbSync.properties | 66 tbsync-2.11/beta-release-channel-update.json | 13 tbsync-2.11/beta-release-channel-update.rdf | 26 tbsync-2.11/bootstrap.js | 178 tbsync-2.11/chrome.manifest | 19 tbsync-2.11/content/HttpRequest.jsm | 754 +++ tbsync-2.11/content/OverlayManager.jsm | 780 ++-- tbsync-2.11/content/abAutoComplete.js | 221 - tbsync-2.11/content/db.js | 433 -- tbsync-2.11/content/manager/about.xul | 58 tbsync-2.11/content/manager/accountManager.js | 248 - tbsync-2.11/content/manager/accounts.js | 1279 ++---- tbsync-2.11/content/manager/accounts.xul | 44 tbsync-2.11/content/manager/catman.xul | 2 tbsync-2.11/content/manager/editAccount.js | 740 +-- tbsync-2.11/content/manager/editAccount.xul | 14 tbsync-2.11/content/manager/errorlog/errorlog.js | 155 tbsync-2.11/content/manager/errorlog/errorlog.xul | 21 tbsync-2.11/content/manager/eventlog/eventlog.js | 157 tbsync-2.11/content/manager/eventlog/eventlog.xul | 21 tbsync-2.11/content/manager/help.xul | 17 tbsync-2.11/content/manager/installProvider.xul | 2 tbsync-2.11/content/manager/manageProvider.js | 52 tbsync-2.11/content/manager/missingProvider.xul | 2 tbsync-2.11/content/manager/password.js | 49 tbsync-2.11/content/manager/password.xul | 39 tbsync-2.11/content/manager/support-wizard/support-wizard.xul | 42 tbsync-2.11/content/manager/supporter.xul | 40 tbsync-2.11/content/modules/abAutoComplete.js | 233 + tbsync-2.11/content/modules/addressbook.js | 1160 ++++++ tbsync-2.11/content/modules/cardbook.js | 20 tbsync-2.11/content/modules/core.js | 332 + tbsync-2.11/content/modules/db.js | 465 ++ tbsync-2.11/content/modules/eventlog.js | 153 tbsync-2.11/content/modules/io.js | 41 tbsync-2.11/content/modules/lightning.js | 831 ++++ tbsync-2.11/content/modules/manager.js | 387 ++ tbsync-2.11/content/modules/messenger.js | 106 tbsync-2.11/content/modules/network.js | 114 tbsync-2.11/content/modules/passwordManager.js | 78 tbsync-2.11/content/modules/providers.js | 184 tbsync-2.11/content/modules/public.js | 698 +++ tbsync-2.11/content/modules/tools.js | 80 tbsync-2.11/content/overlays/abCSS.xul | 8 tbsync-2.11/content/overlays/abNewCardIconsOverlay.js | 45 tbsync-2.11/content/overlays/abNewCardIconsOverlay.xul | 10 tbsync-2.11/content/overlays/abNewCardWindowOverlay.js | 51 tbsync-2.11/content/overlays/abNewCardWindowOverlay.xul | 10 tbsync-2.11/content/overlays/abServerSearch.js | 162 tbsync-2.11/content/overlays/abServerSearch.xul | 10 tbsync-2.11/content/overlays/addressbookiconsoverlay.js | 48 tbsync-2.11/content/overlays/calendar-event-dialog-attendees.js | 34 tbsync-2.11/content/overlays/messenger.js | 33 tbsync-2.11/content/overlays/messenger.xul | 19 tbsync-2.11/content/overlays/messengercompose.js | 75 tbsync-2.11/content/passwordPrompt/passwordPrompt.js | 45 tbsync-2.11/content/passwordPrompt/passwordPrompt.xul | 37 tbsync-2.11/content/tbsync.jsm | 1933 ---------- tbsync-2.11/crowdin.yml | 5 tbsync-2.11/debian/changelog | 109 tbsync-2.11/debian/compat | 1 tbsync-2.11/debian/control | 18 tbsync-2.11/debian/copyright | 33 tbsync-2.11/debian/rules | 11 tbsync-2.11/debian/salsa-ci.yml | 3 tbsync-2.11/debian/source/lintian-overrides | 6 tbsync-2.11/debian/upstream/metadata | 3 tbsync-2.11/debian/watch | 6 tbsync-2.11/debian/webext-tbsync.docs | 4 tbsync-2.11/debian/webext-tbsync.install | 8 tbsync-2.11/debian/webext-tbsync.links | 2 tbsync-2.11/install.rdf | 121 tbsync-2.11/locale/de/tbSync.dtd | 77 tbsync-2.11/locale/de/tbSync.strings | 35 tbsync-2.11/locale/en-US/tbSync.dtd | 77 tbsync-2.11/locale/en-US/tbSync.strings | 35 tbsync-2.11/locale/hu/tbSync.dtd | 77 tbsync-2.11/locale/hu/tbSync.strings | 35 tbsync-2.11/locale/it/tbSync.dtd | 77 tbsync-2.11/locale/it/tbSync.strings | 35 tbsync-2.11/locale/pt-BR/tbSync.dtd | 77 tbsync-2.11/locale/pt-BR/tbSync.strings | 35 tbsync-2.11/locale/ru/tbSync.dtd | 77 tbsync-2.11/locale/ru/tbSync.strings | 35 tbsync-2.11/manifest.json | 26 tbsync-2.11/skin/ab.css | 8 tbsync-2.11/skin/fix_dropdown_1534697.css | 4 tbsync-2.11/unused/abDirectory.js | 325 + tbsync-2.11/unused/abDirectory2.js | 339 + tbsync-2.11/unused/abDirectoryFactory.js | 120 125 files changed, 10202 insertions(+), 5950 deletions(-) diff -Nru tbsync-1.7/CONTRIBUTORS.md tbsync-2.11/CONTRIBUTORS.md --- tbsync-1.7/CONTRIBUTORS.md 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/CONTRIBUTORS.md 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,16 @@ +## Creator +* John Bieling + +## Contributors +* John Bieling +* Jan Dagefoerde +* Nam Ldmpub + + +## Translators +* John Bieling (de, en-US) +* Wanderlei Hüttel (pt-BR) +* Alessandro Menti (it) +* Óvári (hu) +* Alexey Sinitsyn (ru) +* Daniel Wróblewski (pl) diff -Nru tbsync-1.7/Makebeta tbsync-2.11/Makebeta --- tbsync-1.7/Makebeta 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/Makebeta 2020-02-19 19:34:30.000000000 +0000 @@ -8,29 +8,30 @@ # $1 link to base web server : https://tbsync.jobisoft.de/beta # $2 local path of base web server : /var/www/jobisoft.de/tbsync/beta # $3 name of XPI : TbSync.xpi -# $4 name of update.rdf : update-tbsync.rdf git clean -df git checkout -- . git pull -version=$(cat install.rdf | grep -oPm1 "(?<=)[^<]+") -vlevels=$(grep -o '\.' <<< "$version" | grep -c .) +version=$(cat manifest.json | jq -r .version) +updatefile=update-tbsync.json -sed -i "s/%VERSION%/$version/g" "beta-release-channel-update.rdf" -sed -i "s|%LINK%|$1/$3|g" "beta-release-channel-update.rdf" - -#if [ $vlevels -gt 1 ] -#then - echo "Releasing version $version via beta release channel (will include updateURL)" - sed -i "s| | \n $1/$4|g" "install.rdf" - sed -i "s|| [Beta Release Channel]|g" "install.rdf" -#else -# echo "This is a stable release (will NOT include updateURL)" -#fi - -cp beta-release-channel-update.rdf $2/$4 - -rm $3 -zip -r $3 content locale README.md bootstrap.js install.rdf chrome.manifest LICENSE skin +sed -i "s/%VERSION%/$version/g" "beta-release-channel-update.json" +sed -i "s|%LINK%|$1/$3|g" "beta-release-channel-update.json" +sed -i "s/apiVersion: \"/apiVersion: \"Beta /g" "content/tbsync.jsm" +sed -i "s|https://addons.thunderbird.net/addon/dav-4-tbsync/|$1/DAV-4-TbSync.xpi|g" "content/modules/providers.js" +sed -i "s|https://addons.thunderbird.net/addon/eas-4-tbsync/|$1/EAS-4-TbSync.xpi|g" "content/modules/providers.js" + + +echo "Releasing version $version via beta release channel (will include updateURL)" +sed -i "s|\"name\": \"TbSync\",|\"name\": \"TbSync [Beta Release Channel]\",|g" "manifest.json" +sed -i "s|\"gecko\": {|\"gecko\": {\n \"update_url\": \"$1/$updatefile\",|g" "manifest.json" + +cp beta-release-channel-update.json $2/$updatefile + +rm -f $3 +rm -f $3.tar.gz +zip -r $3 content _locales skin chrome.manifest manifest.json bootstrap.js LICENSE CONTRIBUTORS.md +tar cfvz $3.tar.gz content _locales skin chrome.manifest manifest.json bootstrap.js LICENSE CONTRIBUTORS.md cp $3 $2/$3 +cp $3.tar.gz $2/$3.tar.gz diff -Nru tbsync-1.7/Makefile.bat tbsync-2.11/Makefile.bat --- tbsync-1.7/Makefile.bat 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/Makefile.bat 2020-02-19 19:34:30.000000000 +0000 @@ -6,6 +6,14 @@ REM file, You can obtain one at http://mozilla.org/MPL/2.0/. del TbSync-beta.xpi -"C:\Program Files\7-Zip\7zG.exe" a -tzip TbSync-beta.xpi content locale skin chrome.manifest install.rdf LICENSE README.md bootstrap.js +"C:\Program Files\7-Zip\7zG.exe" a -tzip TbSync-beta.xpi content _locales skin chrome.manifest manifest.json LICENSE README.md bootstrap.js CONTRIBUTORS.md +REM Copy sources to doc repository +rd /s /q ..\Provider-4-TbSync\docs\sources +mkdir ..\Provider-4-TbSync\docs\sources + +copy content\OverlayManager.jsm ..\Provider-4-TbSync\docs\sources\ + +Xcopy /E /I content\passwordPrompt ..\Provider-4-TbSync\docs\sources\passwordPrompt\ +Xcopy /E /I content\modules ..\Provider-4-TbSync\docs\sources\modules\ diff -Nru tbsync-1.7/README.md tbsync-2.11/README.md --- tbsync-1.7/README.md 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/README.md 2020-02-19 19:34:30.000000000 +0000 @@ -17,30 +17,45 @@ 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.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FL89EHHQZ2CFL&source=url) +[![](https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif)](https://www.paypal.me/johnbieling) -## Translations and localizations + +## 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) -If you encounter a misspelled translation key, do not correct it but report it to me. The translation keys are only used as variables in the code and are not visible. But changing a translation key requires all other translations and all references to that key in the code to be changed as well. Since translation keys are sometimes build up by string concatenation, it is not as easy as doing a global find and replace. ## Where is this going? I started to work on TbSync, because we needed ActiveSync (EAS) support in Thunderbird. Soon after, I realized that the current situation for sync accounts is very confusing in terms of user experience: There was no central place to set up sync accounts. The same DAV account had to be setup in lightning and again in the sogo-connector or in CardBook. EWS accounts are setup differently again and for google we need 3 different add-ons for contacts, calendars and tasks. -With TbSync I want to unify that: A central manager to setup sync accounts (DAV, EAS, EWS, Google, ...) and get contacts, tasks and calendars. I knew that I will not be able to re-create and maintain all the different providers for TbSync by myself. I thus created (and still work on) a TbSync API, which allows other add-ons to hook into TbSync and re-use most of the glue code. My DAV provider is a proof-of-concept of that API (and a replacement for the sogo-connector, which was not working with TB60 anymore). +With TbSync I want to unify that: A central manager to setup sync accounts (DAV, EAS, EWS, Google, ...) and get contacts, tasks and calendars. I knew that I will not be able to re-create and maintain all the different providers for TbSync by myself. I thus created a TbSync Provider API, which allows other add-ons to hook into TbSync and re-use most of the glue code. I am in contact with Thunderbird staff and we are trying to get TbSync integrated directly into Thunderbird. No ETA yet. -The next step is to [cooperate with CardBook](https://github.com/jobisoft/TbSync/issues/105), so it does not matter, if the user wants to use the "old" Thunderbird address book or the new vCard address book. Every provider available for TbSync should be able to sync into CardBook as well. I hope we get this done before the end of this year. +Future plans: +* cooperate with [CardBook](https://github.com/jobisoft/TbSync/issues/105), so it does not matter, if the user wants to use the standard Thunderbird address book or the cardbook address book. +* add support for Google +* add support for [EteSync](https://www.etesync.com/) +* support the [EWS community](https://github.com/ExchangeCalendar/exchangecalendar), which is interested in turning their add-on into a provider for TbSync. + +All this requires funding. If you like TbSync and want to support its development, please consider a donation. + + +## Adding support for other sync protocolls by creating a TbSync provider add-on + +All the information needed, to build a new provider add-on for TbSync and thus extending its sync capabilities, can be found here: -Later I want to support the [EWS community](https://github.com/ExchangeCalendar/exchangecalendar), which is interested in turning their add-on into a provider for TbSync. +``` +https://tbsync.readthedocs.org +``` -After that, I would like to create or help others to create a google provider for TbSync. We will see how that goes, nothing is planed yet. ## Icon sources and attributions diff -Nru tbsync-1.7/UPDATE68.md tbsync-2.11/UPDATE68.md --- tbsync-1.7/UPDATE68.md 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/UPDATE68.md 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,24 @@ +# Update instructions for Thunderbird 68 + +TbSync has been mostly rewritten for Thunderbird 68 (the next major release being due in a few weeks). To ensure a seamless transition from Thunderbird 60 to Thunderbird 68, I recommend to do the following **before** upgrading to Thunderbird 68: + +* synchronize all your TbSync accounts +* disable all your TbSync accounts + +After the upgrade to Thunderbird 68 has completed, your TbSync accounts can be enabled again. + +## How to disable TbSync accounts + +Each TbSync account can be disabled by unchecking the box shown in the following image: + +![](https://user-images.githubusercontent.com/5830621/63053657-9a2c6d80-bee2-11e9-9019-7035830a873b.png)] + +## Why is this necessary ? + +It could happen, that after the upgrade your synchronized address books and calendars still exists in Thunderbird and can be used as before, but are no longer connected to your servers. If you make local changes, they will never make it to your servers. So these changes will be lost. + +That is why I ask to disable all accounts during the upgrade from Thunderbird 60 to Thunderbird 68. After re-enabling your accounts in Thunderbird 68, they will start a clean sync which ensures a proper connection between Thunderbird and your servers. + +## System-specific tips + +On Arch Linux and derivatives, you can add a `pacman` hook [like this one](https://gist.github.com/MayeulC/400adbfba72effc29fca4d8666fc4571) to print a reminder and cancel the thunderbird upgrade when it becomes available. diff -Nru tbsync-1.7/_locales/Readme.txt tbsync-2.11/_locales/Readme.txt --- tbsync-1.7/_locales/Readme.txt 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/Readme.txt 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,8 @@ +Want to add or fix a localization? + +To help translating this project, please visit + + 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. diff -Nru tbsync-1.7/_locales/bg/messages.json tbsync-2.11/_locales/bg/messages.json --- tbsync-1.7/_locales/bg/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/bg/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "TbSync е добавка за управление на регистрации и синхронизиране със сървъри на вашите контакти, задачи и събития с Thunderbird." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/bg/tbSync.dtd tbsync-2.11/_locales/bg/tbSync.dtd --- tbsync-1.7/_locales/bg/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/bg/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/bg/tbSync.properties tbsync-2.11/_locales/bg/tbSync.properties --- tbsync-1.7/_locales/bg/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/bg/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Празен ход +info.error=Грешка + +google.translate.code=bg + +prompt.DeleteAccount=Сигурни ли сте, че искате да изтриете регистрация ##accountName##? +prompt.Disable=Сигурни ли сте, че искате да изключите тази регистрация? Всички местни промени, които още не са синхронизирани, ще се изпарят! +prompt.Unsubscribe=Сигурни ли сте, че за този ресурс не желаете да сте повече абониран? Всички местни промени, които още не са синхронизирани, ще се изпарят! +prompt.Erase=Сигурни ли сте, че тази регистрация от непознат доставчик искате да я премахнете от списъка с регистрации? + +accountacctions.delete=Изтриване на регистрация "##accountname##" +accountacctions.sync=Синхронизиране на регистрация "##accountname##" +accountacctions.enable=Включване на регистрация "##accountname##" и свързване със сървъра +accountacctions.disable=Изключване на регистрация "##accountname##" + +manager.tryagain=Нов опит за връзка със сървъра +manager.connecting=Осъществяване на връзка +manager.resource=Ресурс +manager.status=Статус +manager.help=Помощ + +addressbook.searchall=Претърсване на всички адресни указатели +addressbook.searchthis=Претърсване на този адресен указател +addressbook.searchgal=Претърсване на този адресен указател и на глобалния указател на сървъра (##replace.1##) + +installProvider.header=Доставчик "##replace.1##" за TbSync не е инсталиран. +supportwizard.provider=Доставчик: ##replace.1## + +OopsMessage=Ужас! TbSync не можа да се зареди! +UnableToTraceError=Не е възможно да се изследва грешката, тъй като не всички действия са протоколирани. Желаете ли да започне подробно протоколиране на събитията, за да може грешката да се анализира и отстрани? +RestartThunderbirdAndTryAgain=Режимът за подробно протоколиране на събитията е включен. Рестартирайте Thunderbird и опитайте да отворите отново TbSync. +HelpFixStartupError=За отстраняване на тази грешка може да изпратите доклад на разработчика. Желаете ли да ви покажем доклада за изпращане? +NoDebugLog=Липсват съществени протоколирани съобщения. Включете режима за подробно протоколиране, рестартирайте Thunderbird и повторете всички стъпки за да се стигне отново до грешка. + +status.apiError=API Грешка при програмирането +status.disabled=Регистрацията и синхронизацията са изключени. + +status.syncing=Синхронизиране +status.skipped=Не се поддържа, пропуснато +status.aborted=Не е синхронизиран +status.pending=Чакане за синхронизация +status.modified=Местни промени + +syncstate.syncing=Започване на синхронизацията +syncstate.preparing=Подготовка на следващия ресурс за синхронизация +syncstate.done=Подготовка на следващия ресурс за синхронизация +syncstate.accountdone=Синхронизирането на регистрацията приключи +syncstate.passwordprompt=Подкана за въвеждане на информация за регистрация +syncstate.oauthprompt=OAuth 2.0 удостоверяване + +status.success=Добре +status.notargets=Синхронизирането прекъсна, тъй като на компютъра ви не можеха да се създадат нови ресурси. +status.nolightning=Добавката Lightning не е инсталирана, календарът е неизползваем. +status.notsyncronized=Регистрацията трябва да бъде синхронизира. +status.foldererror=Поне при един ресурс се появи грешка при синхронизирането. Проверете протокола със събията за подробности. +status.no-folders-found-on-server=На сървъра не бяха намерени ресурси. +status.security=Грешка при осъществяването на защитена връзка. Използвате ли собственоръчно направен сертификат или сертификат, на който Thunderbird няма доверие? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Не можеше да се установи връзка със сървъра (##replace.1##). +status.JavaScriptError=Javascript грешка! Проверете протокола със събитията за подробности. + +status.OAuthNetworkError=Не можах да се свържа с OAuth 2.0 сървъра. +status.OAuthHttpError=Удостоверяването по OAuth 2.0 не стана (HTTP грешка ##replace.1##). +status.OAuthAbortError=Процесът на OAuth 2.0 удоствоверяване беше прекъснат от потребителя. +status.OAuthServerError= OAuth 2.0 сървъра отвърна с ##replace.1## + +target.orphaned=Прекъсната връзка diff -Nru tbsync-1.7/_locales/de/messages.json tbsync-2.11/_locales/de/messages.json --- tbsync-1.7/_locales/de/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/de/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "TbSync ist eine zentrale Benutzeroberfläche zur Verwaltung von Cloud-Konten und zur Synchronisierung ihrer Kontakt-, Aufgaben- und Kalenderinformationen mit Thunderbird." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/de/tbSync.dtd tbsync-2.11/_locales/de/tbSync.dtd --- tbsync-1.7/_locales/de/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/de/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/de/tbSync.properties tbsync-2.11/_locales/de/tbSync.properties --- tbsync-1.7/_locales/de/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/de/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Leerlauf +info.error=Fehler + +google.translate.code=de + +prompt.DeleteAccount=Sind Sie sicher, dass Sie das Konto ##accountName## löschen möchten? +prompt.Disable=Sind Sie sicher, dass Sie dieses Konto deaktivieren möchten? Alle lokalen Veränderungen, die noch nicht synchronisiert wurden, gehen dabei verloren! +prompt.Unsubscribe=Sind Sie sicher, dass Sie dieses Element nicht länger abonnieren möchten? Alle lokalen Veränderungen, die noch nicht synchronisiert wurden, gehen dabei verloren! +prompt.Erase=Sind Sie sicher, dass Sie dieses Konto eines unbekannten Providers aus der Kontoliste entfernen möchten? + +accountacctions.delete=Konto "##accountname##" löschen +accountacctions.sync=Konto "##accountname##" synchronisieren +accountacctions.enable=Konto "##accountname##" aktivieren & Server kontaktieren +accountacctions.disable=Konto "##accountname##" deaktivieren + +manager.tryagain=Erneut versuchen mit dem Server zu verbinden +manager.connecting=Verbindung wird hergestellt +manager.resource=Ressource +manager.status=Status +manager.help=Hilfe + +addressbook.searchall=Alle Adressbücher durchsuchen +addressbook.searchthis=Dieses Adressbuch durchsuchen +addressbook.searchgal=Dieses Adressbuch und das globale Serververzeichnis durchsuchen (##replace.1##) + +installProvider.header=Provider "##replace.1##" für TbSync ist noch nicht installiert. +supportwizard.provider=Provider: ##replace.1## + +OopsMessage=Oops! TbSync konnte nicht starten! +UnableToTraceError=Es ist im Augenblick nicht möglich, diesen Fehler zu untersuchen, da der TbSync Debug-Modus nicht aktiviert ist. Soll der Debug-Modus aktiviert werden, damit dieser Fehler untersucht und behoben werden kann? +RestartThunderbirdAndTryAgain=Der TbSync Debug-Modus wurde aktiviert, bitte starten Sie Thunderbird neu und versuchen Sie dann noch einmal TbSync zu öffnen. +HelpFixStartupError=Um diesen Fehler zu beheben, können Sie einen Fehlerbericht an den Entwickler von TbSync schicken. Soll der Fehlerbericht jetzt angefertigt werden? +NoDebugLog=Es liegen keine aussagekräftigen Debug-Meldungen vor. Bitte aktivieren Sie den Debug-Modus, starten Thunderbird neu und wiederholen dann alle Schritte um das fehlerhafte Verhalten zu reproduzieren. + +status.apiError=API Implementierungsfehler +status.disabled=Konto ist deaktiviert, Synchronisation ist ausgeschaltet. + +status.syncing=Synchronisierung +status.skipped=Nicht unterstützt +status.aborted=Nicht synchronisiert +status.pending=Warten auf Synchronisation +status.modified=Lokale Änderungen + +syncstate.syncing=Initiiere Synchronisation +syncstate.preparing=Bereite das nächste Element für die Synchronisation vor +syncstate.done=Bereite das nächste Element für die Synchronisation vor +syncstate.accountdone=Kontosynchronisation abgeschlossen +syncstate.passwordprompt=Aufforderung zur Eingabe der Anmeldeinformationen +syncstate.oauthprompt=OAuth 2.0 Authentifizierung + +status.success=Ok +status.notargets=Synchronisation abgebrochen da die Elemente zum Synchronisieren nicht erstellt werden konnten. +status.nolightning=Lightning Add-On nicht installiert, Kalender nicht nutzbar. +status.notsyncronized=Konto muss synchronisiert werden. +status.foldererror=Bei mindestens einer Resource trat ein Synchronisationsfehler auf. Bitte prüfen sie das Ereignisprotokoll für weitere Details. +status.no-folders-found-on-server=Auf dem Server wurden keine Resourcen gefunden. +status.security=Fehler beim Aufbau einer sicherern Verbindung. Benutzen Sie eventuell ein selbst signiertes Zertifikat oder ein andersartiges nicht vertrauenswürdiges Zertifikat welches nicht in Thunderbird importiert ist? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Verbindung zum Server fehlgeschlagen (##replace.1##). +status.JavaScriptError=Javascript Fehler! Bitte prüfen Sie das Ereignisprotokoll für weitere Details. + +status.OAuthNetworkError=Verbindung zum OAuth 2.0 Authentifizierungsserver nicht möglich. +status.OAuthHttpError=OAuth 2.0 Authentifizierungsprozess fehlgeschlagen (HTTP Error ##replace.1##). +status.OAuthAbortError=OAuth 2.0 Authentifizierungsprozess vom Benutzer abgebrochen. +status.OAuthServerError= OAuth 2.0 Authentifizierungsserver meldet: ##replace.1## + +target.orphaned=Verbindung getrennt diff -Nru tbsync-1.7/_locales/en-US/messages.json tbsync-2.11/_locales/en-US/messages.json --- tbsync-1.7/_locales/en-US/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/en-US/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "TbSync is a central user interface to manage cloud accounts and to synchronize their contact, task and calendar information with Thunderbird." + } +} diff -Nru tbsync-1.7/_locales/en-US/tbSync.dtd tbsync-2.11/_locales/en-US/tbSync.dtd --- tbsync-1.7/_locales/en-US/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/en-US/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/en-US/tbSync.properties tbsync-2.11/_locales/en-US/tbSync.properties --- tbsync-1.7/_locales/en-US/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/en-US/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Idle +info.error=Error + +google.translate.code=en + +prompt.DeleteAccount=Are you sure you want to delete account ##accountName##? +prompt.Disable=Are you sure you want to disable this account? All local modifications, which have not been synced yet, will be lost! +prompt.Unsubscribe=Are you sure you want to unsubscribe this item? All local modifications, which have not been synced yet, will be lost! +prompt.Erase=Are you sure you want to remove this account of an unknown provider from the accounts lists? + +accountacctions.delete=Delete account “##accountname##” +accountacctions.sync=Synchronize account “##accountname##” +accountacctions.enable=Enable account “##accountname##” & try to connect to server +accountacctions.disable=Disable account “##accountname##” + +manager.tryagain=Try again to connect server +manager.connecting=Connecting to server +manager.resource=Resource +manager.status=Status +manager.help=Help + +addressbook.searchall=Search all address books +addressbook.searchthis=Search this address book +addressbook.searchgal=Search this address book and the global directory (##replace.1##) + +installProvider.header=Provider “##replace.1##” for TbSync is not yet installed. +supportwizard.provider=Provider: ##replace.1## + +OopsMessage=Oops! TbSync was not able to start! +UnableToTraceError=It is not possible to trace this error, because debug log is currently not enabled. Do you want to enable debug log now, to help fix this error? +RestartThunderbirdAndTryAgain=TbSync debug log has been enabled, please restart Thunderbird and again try to open TbSync. +HelpFixStartupError=To help fix this error, you could send a debug log to the TbSync developer. Prepare that email now? +NoDebugLog=Could not find any useful debug messages. Please activate debug mode, restart Thunderbird and repeat all the steps needed to trigger the erroneous behavior. + +status.apiError=API implementation error +status.disabled=Account is not enabled, synchronization is disabled. + +status.syncing=Synchronizing +status.skipped=Not yet supported, skipped +status.aborted=Not synchronized +status.pending=Waiting to be synchronized +status.modified=Local modifications + +syncstate.syncing=Initialize synchronization +syncstate.preparing=Preparing next item for synchronization +syncstate.done=Preparing next item for synchronization +syncstate.accountdone=Finished account +syncstate.passwordprompt=Prompting for credentials +syncstate.oauthprompt=OAuth 2.0 authentication + +status.success=OK +status.notargets=Aborting synchronization, because sync targets could not be created. +status.nolightning=Lightning add-on not installed, calendars are not supported. +status.notsyncronized=Account needs to be synchronized, at least one item is out of sync. +status.foldererror=At least one resource encountered a synchronization error. Please check the event log for more details. +status.no-folders-found-on-server=Could not find any resources on the server. +status.security=Could not establish a secure connection. Are you using a self-signed or otherwise untrusted certificate without importing it into Thunderbird? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Could not connect to server (##replace.1##). +status.JavaScriptError=Javascript Error! Please check the event log for more details. + +status.OAuthNetworkError=Could not connect to OAuth 2.0 authentication server. +status.OAuthHttpError=OAuth 2.0 authentication process failed (HTTP error ##replace.1##). +status.OAuthAbortError=OAuth 2.0 authentication process aborted by user. +status.OAuthServerError= OAuth 2.0 authentication server returned: ##replace.1## + +target.orphaned=Disconnected diff -Nru tbsync-1.7/_locales/fr/messages.json tbsync-2.11/_locales/fr/messages.json --- tbsync-1.7/_locales/fr/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/fr/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "TbSync est une interface permettant de gérer de manière centralisée ses comptes cloud et d'en synchroniser les contacts, tâches et agendas avec Thunderbird." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/fr/tbSync.dtd tbsync-2.11/_locales/fr/tbSync.dtd --- tbsync-1.7/_locales/fr/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/fr/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/fr/tbSync.properties tbsync-2.11/_locales/fr/tbSync.properties --- tbsync-1.7/_locales/fr/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/fr/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Inactif +info.error=Erreur + +google.translate.code=fr + +prompt.DeleteAccount=Êtes-vous certain de vouloir supprimer le compte ##accountName##? +prompt.Disable=Êtes-vous certain de vouloir désactiver ce compte? Toutes les modifications locales non encore synchronisées seront perdues! +prompt.Unsubscribe=Êtes-vous certain de vouloir supprimer l'abonnement à cet élément? Toutes les modifications locales non encore synchronisées seront perdues! +prompt.Erase=Êtes-vous certain de vouloir supprimer ce compte d'un provider inconnu de la liste des comptes? + +accountacctions.delete=Supprimer le compte "##accountname##" +accountacctions.sync=Synchroniser le compte "##accountname##" +accountacctions.enable=Activer le compte "##accountname##" et tenter de se connecter au serveur +accountacctions.disable=Désactiver le compte "##accountname##" + +manager.tryagain=Essayer à nouveau de se connecter au serveur +manager.connecting=Connexion au serveur en cours +manager.resource=Ressource +manager.status=Statut +manager.help=Aide + +addressbook.searchall=Rechercher dans tous les carnets d'adresses +addressbook.searchthis=Rechercher dans ce carnet d'adresse +addressbook.searchgal=Recherche dans ce carnet d'adresse et dans l'annuaire global (##replace.1##) + +installProvider.header=Le provider TbSync "##replace.1##" n'est pas encore installé. +supportwizard.provider=Provider: ##replace.1## + +OopsMessage=Oups! TbSync n'a pas réussi à démarrer! +UnableToTraceError=Il n'est pas possible de localiser cette erreur, parce que le journal de débogage n'est actuellement pas activé. Désirez-vous activer le journal de débogage maintenant, afin d'aider à résoudre cette erreur? +RestartThunderbirdAndTryAgain=Le journal de débogage de TbSync a été activé. Veuillez redémarrer Thunderbird et essayer à nouveau d'ouvrir TbSync. +HelpFixStartupError=Pour aider à la résolution de cette erreur, vous pourriez envoyer le journal de débogage au développeur de TbSync. Voulez-vous qu'on prépare un courriel à cette fin? +NoDebugLog=Il n'a pas été possible de trouver un message de débogage utile. Merci d'activer le mode de débogage, de redémarrer Thunderbird puis de répéter les étapes qui ont menées au comportement anormal du programme. + +status.apiError=Erreur dans l'implémentation de l'API +status.disabled=Le compte n'est pas activé, la synchronisation est désactivée. + +status.syncing=Synchronisation en cours +status.skipped=Non encore supporté, ignoré. +status.aborted=Non synchronisé +status.pending=En attente de synchronisation +status.modified=Modifications locales présentes + +syncstate.syncing=Initialisation de la synchronisation +syncstate.preparing=Préparation de la synchronisation du prochain élément +syncstate.done=Préparation de la synchronisation du prochain élément +syncstate.accountdone=Synchronisation du compte finie +syncstate.passwordprompt=Demande d'insertion du mot de passe +syncstate.oauthprompt=Authentification OAuth 2.0 + +status.success=OK +status.notargets=Annulation de la synchronisation en cours: il est impossibble de créer les destinations de synchronisation. +status.nolightning=Le module complémentaire Lightning nest pas installé, les calendriers ne seront donc pas supportés. +status.notsyncronized=Le compte doit être synchronisé: au moins un éléments est désynchronisé. +status.foldererror=Au moins une ressource a rencontré une erreur de synchronisation. Veuillez lire le journal de débogage pour plus de détails. +status.no-folders-found-on-server=Impossible de trouver les ressources sur le serveur. +status.security=Impossible d'établir une connexion sécurisée. Votre serveur utilise-t-il un certificat auto-signé ou non certifié de confiance, que vous n'avez pas importé dans Thunderbird? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-non-rusted-certificats%3F +status.network=Impossibile de se connecter au serveur (##replace.1##). +status.JavaScriptError=Erreur Javascript! Veuillez lire le journal de débogage pour plus de détails. + +status.OAuthNetworkError=Impossible de se connecter au serveur d'authentification OAuth 2.0. +status.OAuthHttpError=Le processus d'authentification OAuth 2.0 a échoué (erreur HTTP ##replace.1##). +status.OAuthAbortError=Processus d'authentification OAuth 2.0 abandonné par l'utilisateur. +status.OAuthServerError= Le serveur d'authentification OAuth 2.0 a donné le retour suivant: ##replace.1## + +target.orphaned=Connexion rompue diff -Nru tbsync-1.7/_locales/hu/messages.json tbsync-2.11/_locales/hu/messages.json --- tbsync-1.7/_locales/hu/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/hu/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "A Thunderbird-összehangolás egy központi felhasználói felület kezeli a felhőfiókokat, és összehangolja névjegyzékait, feladatait és naptáradatait a Thunderbird programmal." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/hu/tbSync.dtd tbsync-2.11/_locales/hu/tbSync.dtd --- tbsync-1.7/_locales/hu/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/hu/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/hu/tbSync.properties tbsync-2.11/_locales/hu/tbSync.properties --- tbsync-1.7/_locales/hu/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/hu/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Tétlen +info.error=Hiba + +google.translate.code=hu + +prompt.DeleteAccount=Biztos benne, hogy törölni szeretne fiók ##accountName##? +prompt.Disable=Biztosan letiltja ezt a fiókot? Minden helyi módosítás, amely még nincs összehangolva, elveszik! +prompt.Unsubscribe=Biztosan le szeretné iratkozni ezt az elemet? Minden helyi módosítás, amely még nincs összehangolva, elveszik! +prompt.Erase=Biztosan eltávolít egy ismeretlen szolgáltató fiókját a fiókok listáiról? + +accountacctions.delete=A(z) „##accountname##” fiók törlése +accountacctions.sync=A(z) „##accountname##” fiók összehangolása +accountacctions.enable=A(z) „##accountname##” fiók engedélyezze és próbálja meg csatlakozni a kiszolgálóhoz +accountacctions.disable=A(z) „##accountname##” fiók letiltása + +manager.tryagain=Próbálkozzon újra a kiszolgálóhoz való csatlakozással +manager.connecting=Kiszolgáló összekapcsolása +manager.resource=Mappa +manager.status=Állapot +manager.help=Segítség + +addressbook.searchall=Keresése az összes névjegyzékben +addressbook.searchthis=Keresése csak ezt a címjegyzékben +addressbook.searchgal=Keresése ezt a névjegyzékben és a globális könyvtárban (##replace.1##) + +installProvider.header=A(z) „##replace.1##” szolgáltató a Thunderbird-összehangoláshoz még nincs telepítve. +supportwizard.provider=Szolgáltató: ##replace.1## + +OopsMessage=Hoppá, valami hiba történt a Thunderbird-összehangolás indításakor. +UnableToTraceError=Nem lehet nyomon követni ezt a hibát, mert a hibakeresési napló jelenleg nincs engedélyezve. Szeretné engedélyezni a hibakeresési naplót, hogy segítsen javítani a hibát? +RestartThunderbirdAndTryAgain=A Thunderbird-összehangolás hibakeresési napló engedélyezve van, kérjük, indítsa újra a Thunderbird programot, és próbálkozzon újra a Thunderbird-összehangolás megnyitásával. +HelpFixStartupError=A hiba javításához hibakeresési naplót küldhet a Thunderbird-összehangolás fejlesztőnek. Készüljön fel most arra az e-mailre? +NoDebugLog=Nem találtak hasznos hibaelhárító üzeneteket. Engedélyezze a hibakeresési módot, indítsa újra a Thunderbird programot, és ismételje meg a hibás viselkedést elindító lépéseket. + +status.apiError=API végrehajtási hiba +status.disabled=A fiók nem engedélyezett, az összehangolás le van tiltva. + +status.syncing=Összehangolás folyamatban +status.skipped=Még nem támogatott, kimarad +status.aborted=Nincs összehangolva +status.pending=Várakozás összehangolásra +status.modified=Helyi módosítások + +syncstate.syncing=Összehangolás előkészítése +syncstate.preparing=Az összehangolás következő elem előkészítése +syncstate.done=Az összehangolás következő elem előkészítése +syncstate.accountdone=Fiók befejezve +syncstate.passwordprompt=Hitelesítő adatok megadásának kérése +syncstate.oauthprompt=OAuth 2.0 hitelesítés + +status.success=Rendben +status.notargets=A összehangolás abbahagyása, mert a összehangolási célokat nem lehetett létrehozni. +status.nolightning=A Lightning-t kiegészítő nincs telepítve, a naptárak nem támogatottak. +status.notsyncronized=A fiókját összehangolni kell. Legalább egy elem összehangolatlan. +status.foldererror=Legalább egy erőforrás szinkronizálási hibát észlelt. További részletekért nézze meg az eseménynaplót. +status.no-folders-found-on-server=A kiszolgálón nem található erőforrás. +status.security=Biztonsági kapcsolat létrehozása nem sikerült. Ön aláírta vagy egyébként nem megbízható tanúsítványt importálta a Thunderbirdbe? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Nem tudott csatlakozni a kiszolgálóhoz (##replace.1##). +status.JavaScriptError=Javascript hiba! További részletekért nézze meg az eseménynaplót. + +status.OAuthNetworkError=Nem sikerült csatlakozni az OAuth 2.0 hitelesítési kiszolgálóhoz. +status.OAuthHttpError=Az OAuth 2.0 hitelesítési folyamat sikertelen (HTTP hibakód: ##replace.1##). +status.OAuthAbortError=Az OAuth 2.0 hitelesítési folyamatot a felhasználó megszakította. +status.OAuthServerError= Az OAuth 2.0 hitelesítési kiszolgáló visszatért: ##replace.1## + +target.orphaned=A kapcsolat megszakadt diff -Nru tbsync-1.7/_locales/it/messages.json tbsync-2.11/_locales/it/messages.json --- tbsync-1.7/_locales/it/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/it/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "TbSync è un'interfaccia utente che consente di gestire in modo centralizzato account cloud e di sincronizzare i loro contatti, attività e calendari con Thunderbird." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/it/tbSync.dtd tbsync-2.11/_locales/it/tbSync.dtd --- tbsync-1.7/_locales/it/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/it/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/it/tbSync.properties tbsync-2.11/_locales/it/tbSync.properties --- tbsync-1.7/_locales/it/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/it/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Inattivo +info.error=Errore + +google.translate.code=it + +prompt.DeleteAccount=Eliminare l'account ##accountName##? +prompt.Disable=Disabilitare quest'account? Tutte le modifiche locali non ancora sincronizzate andranno perse! +prompt.Unsubscribe=Annullare la sottoscrizione a questo elemento? Tutte le modifiche locali non ancora sincronizzate andranno perse! +prompt.Erase=Rimuovere quest'account di un provider sconosciuto dall'elenco degli account? + +accountacctions.delete=Elimina l'account "##accountname##" +accountacctions.sync=Sincronizza l'account "##accountname##" +accountacctions.enable=Abilita l'account "##accountname##" e tenta la connessione al server +accountacctions.disable=Disabilita l'account "##accountname##" + +manager.tryagain=Riprova a collegarti al server +manager.connecting=Connessione al server in corso +manager.resource=Cartella +manager.status=Stato +manager.help=Aiuto + +addressbook.searchall=Cerca in tutte le rubriche +addressbook.searchthis=Cerca in questa rubrica +addressbook.searchgal=Cerca in questa rubrica e nella directory globale (##replace.1##) + +installProvider.header=Il provider "##replace.1##" per TbSync non è ancora installato. +supportwizard.provider=Provider: ##replace.1## + +OopsMessage=Oops! TbSync non è stato in grado di avviarsi! +UnableToTraceError=Non è possibile localizzare quest'errore perché i log di debug non sono attualmente abilitati. Abilitare ora il log di debug per contribuire a correggere quest'errore? +RestartThunderbirdAndTryAgain=Il log di debug di TbSync è stato abilitato, riavviare Thunderbird e riprovare ad aprire TbSync. +HelpFixStartupError=Per contribuire a correggere quest'errore potresti inviare un log di debug allo sviluppatore di TbSync. Preparare tale messaggio di posta elettronica ora? +NoDebugLog=Non è stato possibile trovare alcun messaggio di debug utile. Attiva la modalità di debug, riavvia Thunderbird e ripeti tutti i passaggi richiesti per scatenare il comportamento errato. + +status.apiError=Errore di implementazione dell'API +status.disabled=L'account non è abilitato, la sincronizzazione è disabilitata. + +status.syncing=Sincronizzazione in corso +status.skipped=Non ancora supportato, omesso +status.aborted=Non sincronizzato +status.pending=In attesa di sincronizzazione +status.modified=Modifiche locali presenti + +syncstate.syncing=Inizializzazione sincronizzazione in corso +syncstate.preparing=Preparazione prossimo elemento per la sincronizzazione in corso +syncstate.done=Preparazione prossimo elemento per la sincronizzazione in corso +syncstate.accountdone=Sincronizzazione account completata +syncstate.passwordprompt=Richiesta di inserire le credenziali +syncstate.oauthprompt=Autenticazione OAuth 2.0 + +status.success=OK +status.notargets=Interruzione sincronizzazione in corso: non è possibile creare le destinazioni sincronizzazione. +status.nolightning=Componente aggiuntivo Lightning non installato, i calendari non sono supportati. +status.notsyncronized=L'account deve essere sincronizzato, almeno un elemento non è sincronizzato. +status.foldererror=Almeno una risorsa ha riscontrato un errore di sincronizzazione. Si prega di controllare il registro eventi per maggiori dettagli. +status.no-folders-found-on-server=Impossibile trovare risorse sul server. +status.security=Impossibile stabilire una connessione sicura. Si sta utilizzando un certificato autofirmato o non affidabile senza averlo importato in Thunderbird? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Impossibile collegarsi al server (##replace.1##). +status.JavaScriptError=Errore Javascript! Si prega di controllare il registro eventi per maggiori dettagli. + +status.OAuthNetworkError=Impossibile connettersi al server di autenticazione OAuth 2.0. +status.OAuthHttpError=Processo di autenticazione OAuth 2.0 non riuscito (errore HTTP ##replace.1##). +status.OAuthAbortError=Processo di autenticazione OAuth 2.0 interrotto dall'utente. +status.OAuthServerError= Il server di autenticazione OAuth 2.0 ha restituito: ##replace.1## + +target.orphaned=Connessione disconnessa diff -Nru tbsync-1.7/_locales/pl/messages.json tbsync-2.11/_locales/pl/messages.json --- tbsync-1.7/_locales/pl/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/pl/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "TbSync to centralny interfejs użytkownika do zarządzania kontami w chmurze i synchronizowania ich danych kontaktowych, zadań i kalendarza z Thunderbird." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/pl/tbSync.dtd tbsync-2.11/_locales/pl/tbSync.dtd --- tbsync-1.7/_locales/pl/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/pl/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/pl/tbSync.properties tbsync-2.11/_locales/pl/tbSync.properties --- tbsync-1.7/_locales/pl/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/pl/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Bezczynny +info.error=Błąd + +google.translate.code=pl + +prompt.DeleteAccount=Czy na pewno chcesz usunąć konto ##accountName##? +prompt.Disable=Czy na pewno chcesz wyłączyć to konto? Wszystkie lokalne modyfikacje, które nie zostały jeszcze zsynchronizowane, zostaną utracone! +prompt.Unsubscribe=Czy na pewno chcesz anulować subskrypcję tego elementu? Wszystkie lokalne modyfikacje, które nie zostały jeszcze zsynchronizowane, zostaną utracone! +prompt.Erase=Czy jesteś pewien, że chcesz usunąć to konto nieznanego dostawcy z list kont? + +accountacctions.delete=Usuń konto “##accountname##” +accountacctions.sync=Synchronizuj konto “##accountname##” +accountacctions.enable=Włącz konto “##accountname##” i spróbuj połączyć się z serwerem +accountacctions.disable=Wyłącz konto “##accountname##” + +manager.tryagain=Spróbuj ponownie połączyć z serwerem +manager.connecting=Łączenie z serwerem +manager.resource=Zasób +manager.status=Status +manager.help=Pomoc + +addressbook.searchall=Wyszukuj we wszystkich książkach adresowych +addressbook.searchthis=Wyszukuj w tej książce adresowej +addressbook.searchgal=Wyszukuj w tej książce adresowej i w katalogu globalnym (##replace.1##) + +installProvider.header=Dostawca “##replace.1##” dla TbSync nie jest jeszcze zainstalowany. +supportwizard.provider=Dostawca: ##replace.1## + +OopsMessage=Ups! TbSync nie mógł się uruchomić! +UnableToTraceError=Nie można prześledzić tego błędu, ponieważ dziennik debugowania nie jest obecnie włączony. Czy chcesz teraz włączyć dziennik debugowania, aby pomóc naprawić ten błąd? +RestartThunderbirdAndTryAgain=Dziennik debugowania TbSync został włączony, zrestartuj Thunderbirda i ponownie spróbuj otworzyć TbSync. +HelpFixStartupError=Aby naprawić ten błąd, możesz wysłać dziennik debugowania do programisty TbSync. Przygotować ten e-mail teraz? +NoDebugLog=Nie można znaleźć żadnych przydatnych wiadomości debugowania. Aktywuj tryb debugowania, uruchom ponownie Thunderbirda i powtórz wszystkie kroki potrzebne do wywołania błędnego zachowania. + +status.apiError=Błąd implementacji API +status.disabled=Konto nie jest włączone, synchronizacja jest wyłączona. + +status.syncing=Synchronizuję +status.skipped=Jeszcze nie wspierane, pominięto +status.aborted=Nie zsynchronizowane +status.pending=Oczekiwanie na synchronizację +status.modified=Zmiany lokalne + +syncstate.syncing=Zainicjuj synchronizację +syncstate.preparing=Przygotowuję następny element do synchronizacji +syncstate.done=Przygotowuję następny element do synchronizacji +syncstate.accountdone=Konto gotowe +syncstate.passwordprompt=Monitowanie o dane logowania +syncstate.oauthprompt=Uwierzytelnianie OAuth 2.0 + +status.success=OK +status.notargets=Przerywanie synchronizacji, ponieważ nie można utworzyć celów synchronizacji. +status.nolightning=Dodatek Lightning nie zainstalowany, kalendarze nie są obsługiwane. +status.notsyncronized=Konto musi zostać zsynchronizowane, co najmniej jeden element nie jest zsynchronizowany. +status.foldererror=Przynajmniej jeden zasób napotkał błąd synchronizacji. Sprawdź dziennik zdarzeń, aby uzyskać więcej informacji. +status.no-folders-found-on-server=Nie można znaleźć żadnych zasobów na serwerze. +status.security=Nie można ustanowić bezpiecznego połączenia. Czy używasz certyfikatu self-signed lub innego niezaufanego certyfikatu bez importowania go do Thunderbirda? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Nie można połączyć z serwerem (##replace.1##). +status.JavaScriptError=Błąd Javascript! Sprawdź dziennik zdarzeń, aby uzyskać więcej informacji. + +status.OAuthNetworkError=Nie można połączyć się z serwerem uwierzytelniania OAuth 2.0. +status.OAuthHttpError=Proces uwierzytelniania OAuth 2.0 nie powiódł się (błąd HTTP ##replace.1##). +status.OAuthAbortError=Proces uwierzytelniania OAuth 2.0 przerwany przez użytkownika. +status.OAuthServerError= Serwer uwierzytelniania OAuth 2.0 zwrócił: ##replace.1## + +target.orphaned=Rozłączony diff -Nru tbsync-1.7/_locales/pt_BR/messages.json tbsync-2.11/_locales/pt_BR/messages.json --- tbsync-1.7/_locales/pt_BR/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/pt_BR/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "O TbSync é uma central para gerenciar contas em nuvem e para sincronizar as informações de contatos, tarefas e calendários com o Thunderbird." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/pt_BR/tbSync.dtd tbsync-2.11/_locales/pt_BR/tbSync.dtd --- tbsync-1.7/_locales/pt_BR/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/pt_BR/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/pt_BR/tbSync.properties tbsync-2.11/_locales/pt_BR/tbSync.properties --- tbsync-1.7/_locales/pt_BR/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/pt_BR/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,67 @@ +info.idle=Ocioso +info.error=Erro + +google.translate.code=pt + +prompt.DeleteAccount=Tem certeza de que deseja excluir a conta ##accountName##? +prompt.Disable=Tem certeza de que deseja desativar esta conta? Todas as modificações locais, que ainda não foram sincronizadas, serão perdidas! +prompt.Unsubscribe=Tem certeza de que deseja cancelar a inscrição deste item? Todas as modificações locais, que ainda não foram sincronizadas, serão perdidas! +prompt.Erase=Tem certeza de que deseja remover essa conta de um provedor desconhecido das listas de contas? + +accountacctions.delete=Excluir conta "##accountname##" +accountacctions.sync=Sincronizar conta "##accountname##" +accountacctions.enable=Ativar conta "##accountname##" e tentar conectar-se ao servidor +accountacctions.disable=Desativar conta "##accountname##" + +manager.tryagain=Tentar conectar no servidor novamente +manager.connecting=Conectando ao servidor +manager.resource=Recurso +manager.status=Status +manager.help=Ajuda + +addressbook.searchall=Pesquisar todos os catálogos de endereços +addressbook.searchthis=Pesquisar este catálogo de endereços +addressbook.searchgal=Pesquisar este catálogo de endereços e o diretório global (##replace.1##) + +installProvider.header=O provedor "##replace.1##" para o TbSync ainda não está instalado. +supportwizard.provider=Provedor: ##replace.1## + +OopsMessage=Erro! O TbSync não pode iniciar! +UnableToTraceError=Não é possível rastrear este erro, porque o log de depuração não está habilitado no momento. Deseja ativar o log de depuração agora para ajudar a corrigir esse erro? +RestartThunderbirdAndTryAgain=O log de depuração do TbSync foi ativado, reinicie o Thunderbird e tente abrir novamente o TbSync. +HelpFixStartupError=Para ajudar a corrigir esse erro, você poderia enviar um log de depuração para o desenvolvedor do TbSync. Enviar esse email agora? +NoDebugLog=Não foi possível encontrar nenhuma mensagem de depuração útil. Por favor, ative o modo de depuração, reinicie o Thunderbird e repita todas as etapas necessárias para acionar o comportamento errado. + +status.apiError=Erro de implementação da API +status.disabled=A conta não está ativada, a sincronização está desativada. + +status.syncing=Sincronizando +status.skipped=Ainda não suportado, ignorado +status.aborted=Não sincronizado +status.pending=Aguardando para ser sincronizado +status.modified=Modificações locais + +syncstate.syncing=Inicializar sincronização +syncstate.preparing=Preparando o próximo item para sincronização +syncstate.done=Preparando o próximo item para sincronização +syncstate.accountdone=Conta finalizada +syncstate.passwordprompt=Solicitação para inserir credenciais +syncstate.oauthprompt=Autenticação OAuth 2.0 + +status.success=OK +status.notargets=Anulando a sincronização, porque os destinos de sincronização não puderam ser criados. +status.nolightning=O complemento Lightning não está instalado, os calendários não serão suportados. +status.notsyncronized=Conta precisa ser sincronizada, pelo menos, um item não está sincronizado. +status.foldererror=Pelo menos um recurso encontrou um erro de sincronização. Por favor, verifique o log de eventos para mais detalhes. +status.no-folders-found-on-server=Não foi possível encontrar nenhum recurso no servidor. +status.security=Não foi possível estabelecer uma conexão segura. Você está usando um certificado autoassinado ou não confiável sem importá-lo para o Thunderbird? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Não foi possível conectar-se ao servidor (##replace.1##). +status.JavaScriptError=Erro de Javascript! Por favor, verifique o log de eventos para mais detalhes. + +status.OAuthNetworkError=Não foi possível conectar ao servidor de autenticação OAuth 2.0. +status.OAuthHttpError=O processo de autenticação OAuth 2.0 falhou (HTTP error ##replace.1##). +status.OAuthAbortError=Processo de autenticação OAuth 2.0 abortado pelo usuário. +status.OAuthServerError= O servidor de autenticação OAuth 2.0 retornou: ##replace.1## + +target.orphaned=Conexão desconectada diff -Nru tbsync-1.7/_locales/ru/messages.json tbsync-2.11/_locales/ru/messages.json --- tbsync-1.7/_locales/ru/messages.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/ru/messages.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "extensionDescription": { + "message": "TbSync - это центральный пользовательский интерфейс для управления облачными учетными записями и синхронизации данных контактов, задач и календаря с Thunderbird." + } +} \ No newline at end of file diff -Nru tbsync-1.7/_locales/ru/tbSync.dtd tbsync-2.11/_locales/ru/tbSync.dtd --- tbsync-1.7/_locales/ru/tbSync.dtd 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/ru/tbSync.dtd 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru tbsync-1.7/_locales/ru/tbSync.properties tbsync-2.11/_locales/ru/tbSync.properties --- tbsync-1.7/_locales/ru/tbSync.properties 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/_locales/ru/tbSync.properties 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,66 @@ +info.idle=Ожидание +info.error=Ошибка + +google.translate.code=ru + +prompt.DeleteAccount=Вы уверены, что хотите удалить аккаунт ##accountName##? +prompt.Disable=Вы действительно хотите запретить этот аккаунт? Все локальные изменения, которые еще не синхронизированы, будут потеряны! +prompt.Unsubscribe=Вы действительно хотите отменить подписку на этот элемент? Все локальные изменения, которые еще не синхронизированы, будут потеряны! +prompt.Erase=Вы действительно хотите удалить этот аккаунт неизвестного провайдера из списка аккаунтов? + +accountacctions.delete=Удалить аккаунт “##accountname##” +accountacctions.sync=Синхронизировать аккаунт “##accountname##” +accountacctions.enable=Разрешить аккаунт “##accountname##” и подключиться к серверу +accountacctions.disable=Запретить аккаунт “##accountname##” + +manager.tryagain=Повторить подсоединение к серверу +manager.connecting=Подсоединиться к серверу +manager.resource=Папка +manager.status=Статус +manager.help=помощь + +addressbook.searchall=Поиск по всем адресным книгам +addressbook.searchthis=Поиск в этой адресной книге +addressbook.searchgal=Поиск в этой адресной книге и в глобальном каталоге (##replace.1##) + +installProvider.header=Провайдер “##replace.1##” для TbSync не установлен. +supportwizard.provider=Провайдер: ##replace.1## + +OopsMessage=Неудача! TbSync не смог запуститься! +UnableToTraceError=Невозможно проследить эту ошибку, поскольку журнал отладки в данный момент не включен. Вы хотите включить журнал отладки сейчас, чтобы исправить эту ошибку? +RestartThunderbirdAndTryAgain=Журнал отладки TbSync включен, перезапустите Thunderbird и снова попытайтесь открыть TbSync. +HelpFixStartupError=Чтобы исправить эту ошибку, вы можете отправить журнал отладки разработчику TbSync. Подготовьте это письмо сейчас? +NoDebugLog=Не удалось найти пригодные отладочные сообщения. Включите режим отладки, перезапустите Thunderbird и повторите все шаги, необходимые для запуска ошибочного поведения. + +status.apiError=Ошибка реализации API +status.disabled=Аккаунт не включен, синхронизация отключена. + +status.syncing=Синхронизация +status.skipped=Пока не поддерживается, пропущено +status.aborted=Не синхронизировано +status.pending=Ожидание пока синхронизируется +status.modified=Локальные изменения + +syncstate.syncing=Инициализация синхронизации +syncstate.preparing=Подготовка следующего элемента для синхронизации +syncstate.done=Подготовка следующего элемента для синхронизации +syncstate.accountdone=Завершенный аккаунт +syncstate.passwordprompt=Запрос на ввод учетных данных + +status.success=Готово +status.notargets=Отмена синхронизации, поскольку цели синхронизации не могут быть созданы. +status.nolightning=Lightning Add-On не установлен, календари не поддерживаются. +status.notsyncronized=Аккаунт должен быть синхронизирован, по крайней мере один элемент не синхронизирован. +status.foldererror=По крайней мере один ресурс обнаружил ошибку синхронизации. Пожалуйста, проверьте журнал событий для более подробной информации. +status.no-folders-found-on-server=Не удалось найти какие-либо ресурсы на сервере. +status.security=Не удалось установить безопасное соединение. Вы используете самоподписанный или ненадежный сертификат без импорта его в Thunderbird? (##replace.1##) +helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F +status.network=Не удалось подключиться к серверу (##replace.1##). +status.JavaScriptError=Ошибка JavaScript! Пожалуйста, проверьте журнал событий для более подробной информации. + +status.OAuthNetworkError=Could not connect to OAuth 2.0 authentication server. +status.OAuthHttpError=OAuth 2.0 authentication process failed (HTTP error ##replace.1##). +status.OAuthAbortError=OAuth 2.0 authentication process aborted by user. +status.OAuthServerError= OAuth 2.0 authentication server returned: ##replace.1## + +target.orphaned=Соединение разорвано diff -Nru tbsync-1.7/beta-release-channel-update.json tbsync-2.11/beta-release-channel-update.json --- tbsync-1.7/beta-release-channel-update.json 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/beta-release-channel-update.json 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,13 @@ +{ + "addons": { + "tbsync@jobisoft.de": { + "updates": [ + { "version": "%VERSION%", + "update_info_url": "https://github.com/jobisoft/TbSync/releases", + "update_link": "%LINK%", + "applications": { + "gecko": { "strict_min_version": "68.0" } } } + ] + } + } +} \ No newline at end of file diff -Nru tbsync-1.7/beta-release-channel-update.rdf tbsync-2.11/beta-release-channel-update.rdf --- tbsync-1.7/beta-release-channel-update.rdf 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/beta-release-channel-update.rdf 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ - - - - - - - - - %VERSION% - - - {3550f703-e582-4d05-9a08-453d09bdfdc6} - 60.0 - 60.* - %LINK% - https://github.com/jobisoft/TbSync/releases - - - - - - - - - - diff -Nru tbsync-1.7/bootstrap.js tbsync-2.11/bootstrap.js --- tbsync-1.7/bootstrap.js 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/bootstrap.js 2020-02-19 19:34:30.000000000 +0000 @@ -1,6 +1,3 @@ -/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ - /* * This file is part of TbSync. * @@ -9,25 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//no need to create namespace, we are in a sandbox - -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/Task.jsm"); -Components.utils.import("resource://gre/modules/osfile.jsm"); - -//Observer to catch loading of thunderbird main window -let onLoadObserver = { - observe: function(aSubject, aTopic, aData) { - let mainWindow = Services.wm.getMostRecentWindow("mail:3pane"); - if (mainWindow) { - //init TbSync - mainWindow.tbSyncReference = tbSync; - tbSync.init(mainWindow); - } else { - tbSync.dump("FAIL", "Could not init TbSync, because mail:3pane window not found."); - } - } -} +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); function install(data, reason) { } @@ -36,90 +16,90 @@ } function startup(data, reason) { - //Do not do anything, if version > 60 - if (Services.vc.compare(Services.appinfo.platformVersion, "60.*") <= 0) { - //possible reasons: APP_STARTUP, ADDON_ENABLE, ADDON_INSTALL, ADDON_UPGRADE, or ADDON_DOWNGRADE. - - //set default prefs - let branch = Services.prefs.getDefaultBranch("extensions.tbsync."); - branch.setIntPref("timeout", 90000); - branch.setBoolPref("debug.testoptions", false); - - branch.setBoolPref("log.toconsole", false); - branch.setBoolPref("log.tofile", false); - branch.setIntPref("log.userdatalevel", 0); //0 - metadata (no incomming xml/wbxml, only parsed data without userdata (except failing items)) 1 - including userdata, 2 - raw xml , 3 - raw wbxml - - Components.utils.import("chrome://tbsync/content/tbsync.jsm"); - - //Map local writeAsyncJSON into tbSync - tbSync.writeAsyncJSON = writeAsyncJSON; - - //add startup observers - Services.obs.addObserver(onLoadObserver, "mail-startup-done", false); - Services.obs.addObserver(onLoadObserver, "tbsync.init", false); - - tbSync.addonData = data; - - if (reason != APP_STARTUP) { - //during startup, we wait until mail-startup-done fired, for all other reasons we need to fire our own init - Services.obs.notifyObservers(null, 'tbsync.init', null) - } - - //DO NOT ADD ANYTHING HERE! - //The final init of TbSync was triggered by issuing a "tbsync.init". If that is done, it will issue a "tbsync.init.done". - //So if there is stuff to do after init is done, add it at the local onLoadDoneObserver - } + // possible reasons: APP_STARTUP, ADDON_ENABLE, ADDON_INSTALL, ADDON_UPGRADE, or ADDON_DOWNGRADE. + + // set default prefs + let defaults = Services.prefs.getDefaultBranch("extensions.tbsync."); + defaults.setBoolPref("debug.testoptions", false); + defaults.setBoolPref("log.toconsole", false); + defaults.setIntPref("log.userdatalevel", 0); //0 - off 1 - userdata only on errors 2 - including full userdata, 3 - extra infos + + // Check if the main window has finished loading + let windows = Services.wm.getEnumerator("mail:3pane"); + while (windows.hasMoreElements()) { + let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + WindowListener.loadIntoWindow(domWindow); + } + + // Wait for any new windows to open. + Services.wm.addListener(WindowListener); + + //DO NOT ADD ANYTHING HERE! } function shutdown(data, reason) { - if (Services.vc.compare(Services.appinfo.platformVersion, "60.*") <= 0) { - //possible reasons: APP_SHUTDOWN, ADDON_DISABLE, ADDON_UNINSTALL, ADDON_UPGRADE, or ADDON_DOWNGRADE + //possible reasons: APP_SHUTDOWN, ADDON_DISABLE, ADDON_UNINSTALL, ADDON_UPGRADE, or ADDON_DOWNGRADE - //remove startup observer - Services.obs.removeObserver(onLoadObserver, "mail-startup-done"); - Services.obs.removeObserver(onLoadObserver, "tbsync.init"); - - //call cleanup of the tbSync module - tbSync.cleanup(); - - let mainWindow = Services.wm.getMostRecentWindow("mail:3pane"); - delete mainWindow.tbSyncReference; - - //abort write timers and write current file content to disk - if (tbSync.enabled) { - tbSync.db.changelogTimer.cancel(); - tbSync.db.accountsTimer.cancel(); - tbSync.db.foldersTimer.cancel(); - writeAsyncJSON(tbSync.db.accounts, tbSync.db.accountsFile); - writeAsyncJSON(tbSync.db.folders, tbSync.db.foldersFile); - writeAsyncJSON(tbSync.db.changelog, tbSync.db.changelogFile); - } - - //unload tbSync module - tbSync.dump("TbSync shutdown","Unloading TbSync module."); - Components.utils.unload("chrome://tbsync/content/tbsync.jsm"); - Components.utils.unload("chrome://tbsync/content/OverlayManager.jsm"); - - // HACK WARNING: - // - the Addon Manager does not properly clear all addon related caches on update; - // - in order to fully update images and locales, their caches need clearing here - Services.obs.notifyObservers(null, "chrome-flush-caches", null); - } + var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm"); + + let windows = Services.wm.getEnumerator("mail:3pane"); + while (windows.hasMoreElements()) { + let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + WindowListener.unloadFromWindow(domWindow); + } + + // Stop listening for any new windows to open. + Services.wm.removeListener(WindowListener); + + TbSync.enabled = false; + + //unload TbSync module + TbSync.dump("TbSync shutdown","Unloading TbSync modules."); + 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; + // - in order to fully update images and locales, their caches need clearing here + Services.obs.notifyObservers(null, "chrome-flush-caches", null); + }); } +var WindowListener = { + + async loadIntoWindow(window) { + if (window.document.readyState != "complete") { + // Make sure the window load has completed. + await new Promise(resolve => { + window.addEventListener("load", resolve, { once: true }); + }); + } + + // Check if the opened window is the one we want to modify. + if (window.document.documentElement.getAttribute("windowtype") === "mail:3pane") { + // the main window has loaded, continue with init + var { TbSync } = ChromeUtils.import("chrome://tbsync/content/tbsync.jsm"); + if (!TbSync.enabled) TbSync.load(window); + } + }, + unloadFromWindow(window) { + }, -function writeAsyncJSON (obj, filename) { - let filepath = tbSync.getAbsolutePath(filename); - let storageDirectory = tbSync.storageDirectory; - let json = tbSync.encoder.encode(JSON.stringify(obj)); - - //no tbSync function/methods inside spawn, because it could run after tbSync was unloaded - Task.spawn(function* () { - //MDN states, instead of checking if dir exists, just create it and catch error on exist (but it does not even throw) - yield OS.File.makeDir(storageDirectory); - yield OS.File.writeAtomic(filepath, json, {tmpPath: filepath + ".tmp"}); - }).catch(Components.utils.reportError); -} + // nsIWindowMediatorListener functions + onOpenWindow(xulWindow) { + // A new window has opened. + let domWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); + // The domWindow.document.documentElement.getAttribute("windowtype") is not set before the load, so we cannot check it here + this.loadIntoWindow(domWindow); + }, + + onCloseWindow(xulWindow) { + }, + + onWindowTitleChange(xulWindow, newTitle) { + }, +}; diff -Nru tbsync-1.7/chrome.manifest tbsync-2.11/chrome.manifest --- tbsync-1.7/chrome.manifest 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/chrome.manifest 2020-02-19 19:34:30.000000000 +0000 @@ -1,8 +1,13 @@ content tbsync content/ -skin tbsync classic/1.0 skin/ -locale tbsync de locale/de/ -locale tbsync en-US locale/en-US/ -locale tbsync hu locale/hu/ -locale tbsync it locale/it/ -locale tbsync ru locale/ru/ -locale tbsync pt-BR locale/pt-BR/ + +skin tbsync classic/1.0 skin/ + +locale tbsync bg _locales/bg/ +locale tbsync de _locales/de/ +locale tbsync en-US _locales/en-US/ +locale tbsync fr _locales/fr/ +locale tbsync hu _locales/hu/ +locale tbsync it _locales/it/ +locale tbsync pl _locales/pl/ +locale tbsync pt-BR _locales/pt_BR/ +locale tbsync ru _locales/ru/ diff -Nru tbsync-1.7/content/HttpRequest.jsm tbsync-2.11/content/HttpRequest.jsm --- tbsync-1.7/content/HttpRequest.jsm 1970-01-01 00:00:00.000000000 +0000 +++ tbsync-2.11/content/HttpRequest.jsm 2020-02-19 19:34:30.000000000 +0000 @@ -0,0 +1,754 @@ +/* + * 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.createCodebasePrincipal(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; + + if (username) { + 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.createCodebasePrincipal(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-1.7/content/OverlayManager.jsm tbsync-2.11/content/OverlayManager.jsm --- tbsync-1.7/content/OverlayManager.jsm 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/content/OverlayManager.jsm 2020-02-19 19:34:30.000000000 +0000 @@ -10,243 +10,243 @@ var EXPORTED_SYMBOLS = ["OverlayManager"]; -Components.utils.import("resource://gre/modules/NetUtil.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/Task.jsm"); +var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); function OverlayManager(options = {}) { - this.registeredOverlays = {}; - this.overlays = {}; - this.stylesheets = {}; - this.options = {verbose: 0}; - - let userOptions = Object.keys(options); - for (let i=0; i < userOptions.length; i++) { - this.options[userOptions[i]] = options[userOptions[i]]; - } - - - -/* - could be replaced in TB61: - - https://dxr.mozilla.org/comm-central/rev/18881dd127e3b0c0d3f97390c9094e309d4dd9c1/mail/test/resources/jsbridge/jsbridge/extension/bootstrap.js#17 - - https://dxr.mozilla.org/comm-central/rev/18881dd127e3b0c0d3f97390c9094e309d4dd9c1/common/src/extensionSupport.jsm#151 -*/ - this.windowListener = { - that : this, - onOpenWindow: function(xulWindow) { - let window = xulWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow); - - function onWindowLoad() { - //window.removeEventListener("load", onWindowLoad); - this.injectAllOverlays(window); - } - window.addEventListener("load", onWindowLoad.bind(this.that)); - }, - onCloseWindow: function(xulWindow) { }, - onWindowTitleChange: function(xulWindow, newTitle) { } - }; + this.registeredOverlays = {}; + this.overlays = {}; + this.stylesheets = {}; + this.options = {verbose: 0}; + + let userOptions = Object.keys(options); + for (let i=0; i < userOptions.length; i++) { + this.options[userOptions[i]] = options[userOptions[i]]; + } - - this.startObserving = function () { - let windows = Services.wm.getEnumerator(null); - while (windows.hasMoreElements()) { - let window = windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow); - //inject overlays for this window - this.injectAllOverlays(window); - } + this.windowListener = { + that: this, + onOpenWindow: function(xulWindow) { + let window = xulWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow); + this.that.injectAllOverlays(window); + }, + onCloseWindow: function(xulWindow) { }, + onWindowTitleChange: function(xulWindow, newTitle) { } + }; + + + + + this.startObserving = function () { + let windows = Services.wm.getEnumerator(null); + while (windows.hasMoreElements()) { + let window = windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow); + //inject overlays for this window + this.injectAllOverlays(window); + } - Services.wm.addListener(this.windowListener); - }; + Services.wm.addListener(this.windowListener); + }; - this.stopObserving = function () { - Services.wm.removeListener(this.windowListener); + this.stopObserving = function () { + Services.wm.removeListener(this.windowListener); - let windows = Services.wm.getEnumerator(null); - while (windows.hasMoreElements()) { - let window = windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow); - //remove overlays (if any) - this.removeAllOverlays(window); - } - }; + let windows = Services.wm.getEnumerator(null); + while (windows.hasMoreElements()) { + let window = windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow); + //remove overlays (if any) + this.removeAllOverlays(window); + } + }; - this.hasRegisteredOverlays = function (window) { - return this.registeredOverlays.hasOwnProperty(window.location.href); - }; - - this.registerOverlay = Task.async (function* (dst, overlay) { - if (overlay.startsWith("chrome://")) { - let xul = yield this.readChromeFile(overlay); - let rootNode = this.getDataFromXULString(null, xul); - - //get urls of stylesheets to load them - let styleSheetUrls = this.getStyleSheetUrls(rootNode); - for (let i=0; i1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file is empty!"); - return null; - } + this.getDataFromXULString = function (window, str) { + let data = null; + let xul = ""; + if (str == "") { + if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file is empty!"); + return null; + } - let oParser = (Services.vc.compare(Services.appinfo.platformVersion, "61.*") >= 0) ? new DOMParser() : Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser); - try { - xul = oParser.parseFromString(str, "application/xml"); - } catch (e) { - //however, domparser does not throw an error, it returns an error document - //https://developer.mozilla.org/de/docs/Web/API/DOMParser - //just in case - if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file could not be parsed correctly, something is wrong.\n" + str); - return null; - } + let oParser = new DOMParser(); + try { + xul = oParser.parseFromString(str, "application/xml"); + } catch (e) { + //however, domparser does not throw an error, it returns an error document + //https://developer.mozilla.org/de/docs/Web/API/DOMParser + //just in case + if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file could not be parsed correctly, something is wrong.\n" + str); + return null; + } - //check if xul is error document - if (xul.documentElement.nodeName == "parsererror") { - if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file could not be parsed correctly, something is wrong.\n" + str); - return null; - } - - if (xul.documentElement.nodeName != "overlay") { - if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file does not look like an overlay (root node is not overlay).\n" + str); - return null; - } - - return xul; - }; + //check if xul is error document + if (xul.documentElement.nodeName == "parsererror") { + if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file could not be parsed correctly, something is wrong.\n" + str); + return null; + } + + if (xul.documentElement.nodeName != "overlay") { + if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A provided XUL file does not look like an overlay (root node is not overlay).\n" + str); + return null; + } + + return xul; + }; - this.injectAllOverlays = function (window, _href = null) { - let href = (_href === null) ? window.location.href : _href; - for (let i=0; this.registeredOverlays[href] && i < this.registeredOverlays[href].length; i++) { - this.injectOverlay(window, this.registeredOverlays[href][i]); - } - }; + this.injectAllOverlays = async function (window, _href = null) { + if (window.document.readyState != "complete") { + // Make sure the window load has completed. + await new Promise(resolve => { + window.addEventListener("load", resolve, { once: true }); + }); + } - this.removeAllOverlays = function (window) { - if (!this.hasRegisteredOverlays(window)) - return; - - for (let i=0; i < this.registeredOverlays[window.location.href].length; i++) { - this.removeOverlay(window, this.registeredOverlays[window.location.href][i]); - } - }; + let href = (_href === null) ? window.location.href : _href; + for (let i=0; this.registeredOverlays[href] && i < this.registeredOverlays[href].length; i++) { + this.injectOverlay(window, this.registeredOverlays[href][i]); + } + }; + this.removeAllOverlays = function (window) { + if (!this.hasRegisteredOverlays(window)) + return; + + for (let i=0; i < this.registeredOverlays[window.location.href].length; i++) { + this.removeOverlay(window, this.registeredOverlays[window.location.href][i]); + } + }; - this.injectOverlay = function (window, overlay) { - if (!window.hasOwnProperty("injectedOverlays")) window.injectedOverlays = []; + this.injectOverlay = function (window, overlay) { + if (!window.hasOwnProperty("injectedOverlays")) window.injectedOverlays = []; - if (window.injectedOverlays.includes(overlay)) { - if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] NOT Injecting: " + overlay); - return; - } - - let rootNode = this.overlays[overlay]; + if (window.injectedOverlays.includes(overlay)) { + if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] NOT Injecting: " + overlay); + return; + } + + let rootNode = this.overlays[overlay]; - if (rootNode) { - let overlayNode = rootNode.documentElement; - if (overlayNode) { - //get and load scripts - let scripts = this.getScripts(rootNode, overlayNode); - for (let i=0; i < scripts.length; i++){ - if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Loading: " + scripts[i]); - Services.scriptloader.loadSubScript(scripts[i], window); - } - - //eval onbeforeinject, if that returns false, inject is aborted - let inject = true; - if (overlayNode.hasAttribute("onbeforeinject")) { - let onbeforeinject = overlayNode.getAttribute("onbeforeinject"); - if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Executing: " + onbeforeinject); - // the source for this eval is part of this XPI, cannot be changed by user. - inject = window.eval(onbeforeinject); - } - - if (inject) { - if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] Injecting: " + overlay); - window.injectedOverlays.push(overlay); - - //get urls of stylesheets to add preloaded files - let styleSheetUrls = this.getStyleSheetUrls(rootNode); - for (let i=0; i3) Services.console.logStringMessage("[OverlayManager] Stylesheet: " + styleSheetUrls[i]); - } - - this.insertXulOverlay(window, overlayNode.children); - - //execute oninject - if (overlayNode.hasAttribute("oninject")) { - let oninject = overlayNode.getAttribute("oninject"); - if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Executing: " + oninject); - // the source for this eval is part of this XPI, cannot be changed by user. - window.eval(oninject); - } - } - } + if (rootNode) { + let overlayNode = rootNode.documentElement; + if (overlayNode) { + //get and load scripts + let scripts = this.getScripts(rootNode, overlayNode); + for (let i=0; i < scripts.length; i++){ + if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Loading: " + scripts[i]); + Services.scriptloader.loadSubScript(scripts[i], window); + } + + //eval onbeforeinject, if that returns false, inject is aborted + let inject = true; + if (overlayNode.hasAttribute("onbeforeinject")) { + let onbeforeinject = overlayNode.getAttribute("onbeforeinject"); + if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Executing: " + onbeforeinject); + // the source for this eval is part of this XPI, cannot be changed by user. + inject = window.eval(onbeforeinject); + } + + if (inject) { + if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] Injecting: " + overlay); + window.injectedOverlays.push(overlay); + + //get urls of stylesheets to add preloaded files + let styleSheetUrls = this.getStyleSheetUrls(rootNode); + for (let i=0; i3) Services.console.logStringMessage("[OverlayManager] Stylesheet: " + styleSheetUrls[i]); + } + + this.insertXulOverlay(window, overlayNode.children); + + //execute oninject + if (overlayNode.hasAttribute("oninject")) { + let oninject = overlayNode.getAttribute("oninject"); + if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Executing: " + oninject); + // the source for this eval is part of this XPI, cannot be changed by user. + window.eval(oninject); + } } - }; + } + } + }; - this.removeOverlay = function (window, overlay) { - if (!window.hasOwnProperty("injectedOverlays")) window.injectedOverlays = []; + this.removeOverlay = function (window, overlay) { + if (!window.hasOwnProperty("injectedOverlays")) window.injectedOverlays = []; - if (!window.injectedOverlays.includes(overlay)) { - if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] NOT Removing: " + overlay); - return; - } + if (!window.injectedOverlays.includes(overlay)) { + if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] NOT Removing: " + overlay); + return; + } - if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] Removing: " + overlay); - window.injectedOverlays = window.injectedOverlays.filter(e => (e != overlay)); - + if (this.options.verbose>2) Services.console.logStringMessage("[OverlayManager] Removing: " + overlay); + window.injectedOverlays = window.injectedOverlays.filter(e => (e != overlay)); + // let rootNode = this.getDataFromXULString(window, this.overlays[overlay]); - let rootNode = this.overlays[overlay]; - let overlayNode = rootNode.documentElement; - - if (overlayNode.hasAttribute("onremove")) { - let onremove = overlayNode.getAttribute("onremove"); - if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Executing: " + onremove); - // the source for this eval is part of this XPI, cannot be changed by user. - window.eval(onremove); - } + let rootNode = this.overlays[overlay]; + let overlayNode = rootNode.documentElement; + + if (overlayNode.hasAttribute("onremove")) { + let onremove = overlayNode.getAttribute("onremove"); + if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Executing: " + onremove); + // the source for this eval is part of this XPI, cannot be changed by user. + window.eval(onremove); + } - this.removeXulOverlay(window, overlayNode.children); + this.removeXulOverlay(window, overlayNode.children); - //get urls of stylesheets to remove styte tag - let styleSheetUrls = this.getStyleSheetUrls(rootNode); - for (let i=0; i1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A top level <" + node.nodeName+ "> element does not have an ID. Skipped"); - continue; - } - - //check for inline script tags - if (node.nodeName == "script") { - let element = this.createXulElement(window, node, "html:script"); //force as html:script - window.document.documentElement.appendChild(element); - continue; - } - - //check for inline style - if (node.nodeName == "style") { - let element = this.createXulElement(window, node, "html:style"); //force as html:style - window.document.documentElement.appendChild(element); - continue; - } - - if (node.hasAttribute("appendto")) hookMode = "appendto"; - if (node.hasAttribute("insertbefore")) hookMode ="insertbefore"; - if (node.hasAttribute("insertafter")) hookMode = "insertafter"; - - if (hookMode) { - hookName = node.getAttribute(hookMode); - hookElement = window.document.getElementById(hookName); - - if (!hookElement) { - if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: The hook element <"+hookName+"> of top level overlay element <"+ node.nodeName+"> does not exist. Skipped"); - continue; - } - } else { - hookMode = "appendto"; - hookName = "ROOT"; - hookElement = window.document.documentElement; - } - } - - element = this.createXulElement(window, node); - if (node.hasChildNodes) this.insertXulOverlay(window, node.children, element); - - if (parentElement) { - // this is a child level XUL element which needs to be added to to its parent - parentElement.appendChild(element); - } else { - // this is a toplevel element, which needs to be added at insertafter or insertbefore - switch (hookMode) { - case "appendto": - hookElement.appendChild(element); - break; - case "insertbefore": - hookElement.parentNode.insertBefore(element, hookElement); - break; - case "insertafter": - hookElement.parentNode.insertBefore(element, hookElement.nextSibling); - break; - default: - if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: Top level overlay element <"+ node.nodeName+"> uses unknown hook type <"+hookMode+">. Skipped."); - continue; - } - if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Adding <"+element.id+"> ("+element.tagName+") " + hookMode + " <" + hookName + ">"); - } - } + this.insertXulOverlay = function (window, nodes, parentElement = null) { + /* + The passed nodes value could be an entire window.document in a single node (type 9) or a + single element node (type 1) as returned by getElementById. It could however also + be an array of nodes as returned by getElementsByTagName or a nodeList as returned + by childNodes. In that case node.length is defined. + */ + let nodeList = []; + if (nodes.length === undefined) nodeList.push(nodes); + else nodeList = nodes; + + // nodelist contains all childs + for (let node of nodeList) { + let element = null; + let hookMode = null; + let hookName = null; + let hookElement = null; + + if (node.nodeName == "script" && node.hasAttribute("src")) { + //skip, since they are handled by getScripts() + } else if (node.nodeName == "toolbarpalette") { + // handle toolbarpalette tags + } else if (node.nodeType == 1) { + + if (!parentElement) { //misleading: if it does not have a parentElement, it is a top level element + //Adding top level elements without id is not allowed, because we need to be able to remove them! + if (!node.hasAttribute("id")) { + if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: A top level <" + node.nodeName+ "> element does not have an ID. Skipped"); + continue; + } + + //check for inline script tags + if (node.nodeName == "script") { + let element = this.createXulElement(window, node, "html:script"); //force as html:script + window.document.documentElement.appendChild(element); + continue; + } + + //check for inline style + if (node.nodeName == "style") { + let element = this.createXulElement(window, node, "html:style"); //force as html:style + window.document.documentElement.appendChild(element); + continue; + } + + if (node.hasAttribute("appendto")) hookMode = "appendto"; + if (node.hasAttribute("insertbefore")) hookMode ="insertbefore"; + if (node.hasAttribute("insertafter")) hookMode = "insertafter"; + + if (hookMode) { + hookName = node.getAttribute(hookMode); + hookElement = window.document.getElementById(hookName); + + if (!hookElement) { + if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: The hook element <"+hookName+"> of top level overlay element <"+ node.nodeName+"> does not exist. Skipped"); + continue; + } + } else { + hookMode = "appendto"; + hookName = "ROOT"; + hookElement = window.document.documentElement; + } } - }; - - this.removeXulOverlay = function (window, nodes, parentElement = null) { - //only scan toplevel elements and remove them - let nodeList = []; - if (nodes.length === undefined) nodeList.push(nodes); - else nodeList = nodes; - // nodelist contains all childs - for (let node of nodeList) { - let element = null; - switch(node.nodeType) { - case 1: - if (node.hasAttribute("id")) { - let element = window.document.getElementById(node.getAttribute("id")); - if (element) { - element.parentNode.removeChild(element); - } - } - break; + element = this.createXulElement(window, node); + if (node.hasChildNodes) this.insertXulOverlay(window, node.children, element); + + if (parentElement) { + // this is a child level XUL element which needs to be added to to its parent + parentElement.appendChild(element); + } else { + // this is a toplevel element, which needs to be added at insertafter or insertbefore + switch (hookMode) { + case "appendto": + hookElement.appendChild(element); + break; + case "insertbefore": + hookElement.parentNode.insertBefore(element, hookElement); + break; + case "insertafter": + hookElement.parentNode.insertBefore(element, hookElement.nextSibling); + break; + default: + if (this.options.verbose>1) Services.console.logStringMessage("[OverlayManager] BAD XUL: Top level overlay element <"+ node.nodeName+"> uses unknown hook type <"+hookMode+">. Skipped."); + continue; + } + if (this.options.verbose>3) Services.console.logStringMessage("[OverlayManager] Adding <"+element.id+"> ("+element.tagName+") " + hookMode + " <" + hookName + ">"); + } + } + } + }; + + this.removeXulOverlay = function (window, nodes, parentElement = null) { + //only scan toplevel elements and remove them + let nodeList = []; + if (nodes.length === undefined) nodeList.push(nodes); + else nodeList = nodes; + + // nodelist contains all childs + for (let node of nodeList) { + let element = null; + switch(node.nodeType) { + case 1: + if (node.hasAttribute("id")) { + let element = window.document.getElementById(node.getAttribute("id")); + if (element) { + element.parentNode.removeChild(element); } - } - }; + } + break; + } + } + }; @@ -445,31 +445,31 @@ - //read file from within the XPI package - this.readChromeFile = function (aURL) { - return new Promise((resolve, reject) => { - let uri = Services.io.newURI(aURL); - let channel = Services.io.newChannelFromURI2(uri, - null, - Services.scriptSecurityManager.getSystemPrincipal(), - null, - Components.interfaces.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS, - Components.interfaces.nsIContentPolicy.TYPE_OTHER); + //read file from within the XPI package + this.readChromeFile = function (aURL) { + return new Promise((resolve, reject) => { + let uri = Services.io.newURI(aURL); + let channel = Services.io.newChannelFromURI(uri, + null, + Services.scriptSecurityManager.getSystemPrincipal(), + null, + Components.interfaces.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS, + Components.interfaces.nsIContentPolicy.TYPE_OTHER); - NetUtil.asyncFetch(channel, (inputStream, status) => { - if (!Components.isSuccessCode(status)) { - reject(status); - return; - } + NetUtil.asyncFetch(channel, (inputStream, status) => { + if (!Components.isSuccessCode(status)) { + reject(status); + return; + } - try { - let data = NetUtil.readInputStreamToString(inputStream, inputStream.available()); - resolve(data); - } catch (ex) { - reject(ex); - } - }); - }); - }; - + try { + let data = NetUtil.readInputStreamToString(inputStream, inputStream.available()); + resolve(data); + } catch (ex) { + reject(ex); + } + }); + }); + }; + } diff -Nru tbsync-1.7/content/abAutoComplete.js tbsync-2.11/content/abAutoComplete.js --- tbsync-1.7/content/abAutoComplete.js 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/content/abAutoComplete.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,221 +0,0 @@ -/* - * This file is part of TbSync. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - - "use strict"; - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); - -//derived from https://dxr.mozilla.org/comm-central/source/mozilla/accessible/tests/mochitest/autocomplete.js -var abAutoComplete = { - - tbSyncAutoCompleteSearch : null, - - /** - * Register 'tbSyncAutoCompleteSearch' AutoCompleteSearch. - */ - init : function () { - abAutoComplete.tbSyncAutoCompleteSearch = new abAutoComplete.Search("tbSyncAutoCompleteSearch"); - abAutoComplete.register(abAutoComplete.tbSyncAutoCompleteSearch, "AutoCompleteSearch"); - }, - - /** - * Unregister 'tbSyncAutoCompleteSearch' AutoCompleteSearch. - */ - shutdown : function () { - abAutoComplete.unregister(abAutoComplete.tbSyncAutoCompleteSearch); - abAutoComplete.tbSyncAutoCompleteSearch.cid = null; - abAutoComplete.tbSyncAutoCompleteSearch = null; - }, - - - /** - * Register the given AutoCompleteSearch. - * - * @param aSearch [in] AutoCompleteSearch object - * @param aDescription [in] description of the search object - */ - register : function (aSearch, aDescription) { - var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name; - - var uuidGenerator = Components.classes["@mozilla.org/uuid-generator;1"].getService(Components.interfaces.nsIUUIDGenerator); - var cid = uuidGenerator.generateUUID(); - - var componentManager = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar); - componentManager.registerFactory(cid, aDescription, name, aSearch); - - // Keep the id on the object so we can unregister later. - aSearch.cid = cid; - }, - - /** - * Unregister the given AutoCompleteSearch. - */ - unregister : function (aSearch) { - var componentManager = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar); - componentManager.unregisterFactory(aSearch.cid, aSearch); - }, - - - /** - * nsIAutoCompleteSearch implementation. - * - * @param aName [in] the name of autocomplete search - */ - Search : function (aName) { - this.name = aName; - }, - - - - - /** - * nsIAutoCompleteResult implementation. - */ - Result : function (aValues, aComments) { - this.values = aValues; - this.comments = aComments; - - if (this.values.length > 0) - this.searchResult = Components.interfaces.nsIAutoCompleteResult.RESULT_SUCCESS; - else - this.searchResult = Components.interfaces.nsIAutoCompleteResult.NOMATCH; - }, - -} - - - - - -abAutoComplete.Search.prototype = { - constructor: abAutoComplete.Search, - - // nsIAutoCompleteSearch implementation - startSearch : Task.async (function* (aSearchString, aSearchParam, aPreviousResult, aListener) { - var result = yield this.getAutoCompleteResultFor(aSearchString); - aListener.onSearchResult(this, result); - }), - - stopSearch() {}, - - // nsISupports implementation - QueryInterface: XPCOMUtils.generateQI(["nsIFactory", "nsIAutoCompleteSearch"]), //ChromeUtils - - // nsIFactory implementation - createInstance(outer, iid) { - return this.QueryInterface(iid); - }, - - // Search name. Used by AutoCompleteController. - name: null, - - /** - * Return AutoCompleteResult for the given search string. - */ - getAutoCompleteResultFor : Task.async (function* (aSearchString) { - //check each account and init server request - let accounts = tbSync.db.getAccounts(); - let requests = []; - let values = []; - let comments = []; - - if (aSearchString.length > 3) { - for (let i=0; iaccountID][folderID] = {row} - - accountsTimer: Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer), - foldersTimer: Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer), - changelogTimer: Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer), - - writeDelay : 6000, - - saveAccounts: function () { - db.accountsTimer.cancel(); - db.accountsTimer.init(db.writeJSON, db.writeDelay + 1, 0); - }, - - saveFolders: function () { - db.foldersTimer.cancel(); - db.foldersTimer.init(db.writeJSON, db.writeDelay + 2, 0); - }, - - saveChangelog: function () { - db.changelogTimer.cancel(); - db.changelogTimer.init(db.writeJSON, db.writeDelay + 3, 0); - }, - - writeJSON : { - observe: function(subject, topic, data) { - if (tbSync.enabled) { - switch (subject.delay) { //use delay setting to find out, which file is to be saved - case (db.writeDelay + 1): tbSync.writeAsyncJSON(db.accounts, db.accountsFile); break; - case (db.writeDelay + 2): tbSync.writeAsyncJSON(db.folders, db.foldersFile); break; - case (db.writeDelay + 3): tbSync.writeAsyncJSON(db.changelog, db.changelogFile); break; - } - } - } - }, - - - - - // CHANGELOG FUNCTIONS - - getItemStatusFromChangeLog: function (parentId, itemId) { - for (let i=0; i-1; i-- ) { - if (this.changelog[i].parentId == parentId && this.changelog[i].itemId == itemId) { - let row = this.changelog.splice(i,1); - if (moveToEnd) this.changelog.push(row[0]); - this.saveChangelog(); - return; - } - } - }, - - removeAllItemsFromChangeLogWithStatus: function (parentId, status) { - for (let i=this.changelog.length-1; i>-1; i-- ) { - if (this.changelog[i].parentId == parentId && this.changelog[i].status == status) { - let row = this.changelog.splice(i,1); - } - } - this.saveChangelog(); - }, - - // Remove all cards of a parentId from ChangeLog - clearChangeLog: function (parentId) { - for (let i=this.changelog.length-1; i>-1; i-- ) { - if (this.changelog[i].parentId == parentId) this.changelog.splice(i,1); - } - this.saveChangelog(); - }, - - getItemsFromChangeLog: function (parentId, maxnumbertosend, status = null) { - //maxnumbertosend = 0 will return all results - let log = []; - let counts = 0; - for (let i=0; i tbSync.loadedProviders.hasOwnProperty(this.accounts.data[account].provider)).sort((a, b) => a - b); - accounts.allIDs = Object.keys(this.accounts.data).sort((a, b) => a - b) - accounts.data = this.accounts.data; - return accounts; - }, - - getAccount: function (account) { - //check if account is known - if (this.accounts.data.hasOwnProperty(account) == false ) { - throw "Unknown account!" + "\nThrown by db.getAccount("+account+ ")"; - } else { - return this.accounts.data[account]; - } - }, - - isValidAccountSetting: function (provider, name) { - //provider is hardcoded and always true - if (name == "provider") - return true; - - //check if provider is installed - if (!tbSync.loadedProviders.hasOwnProperty(provider)) { - tbSync.dump("Error @ isValidAccountSetting", "Unknown provider <"+provider+">!"); - return false; - } - - if (tbSync[provider].getDefaultAccountEntries().hasOwnProperty(name)) { - return true; - } else { - tbSync.dump("Error @ isValidAccountSetting", "Unknown account setting <"+name+">!"); - return false; - } - - }, - - getAccountSetting: function (account, name) { - // if the requested account does not exist, getAccount() will fail - let data = this.getAccount(account); - - //check if field is allowed and get value or default value if setting is not set - if (this.isValidAccountSetting(data.provider, name)) { - if (data.hasOwnProperty(name)) return data[name]; - else return tbSync[data.provider].getDefaultAccountEntries()[name]; - } - }, - - setAccountSetting: function (account , name, value) { - // if the requested account does not exist, getAccount() will fail - let data = this.getAccount(account); - - //check if field is allowed, and set given value - if (this.isValidAccountSetting(data.provider, name)) { - this.accounts.data[account][name] = value.toString(); - } - this.saveAccounts(); - }, - - resetAccountSetting: function (account , name) { - // if the requested account does not exist, getAccount() will fail - let data = this.getAccount(account); - let defaults = tbSync[data.provider].getDefaultAccountEntries(); - - //check if field is allowed, and set given value - if (this.isValidAccountSetting(data.provider, name)) { - this.accounts.data[account][name] = defaults[name]; - } - this.saveAccounts(); - }, - - - - - // FOLDER FUNCTIONS - - addFolder: function(account, data) { - let provider = this.getAccountSetting(account, "provider"); - - //create folder with default settings - let newFolderSettings = tbSync[provider].getDefaultFolderEntries(account); - - //add custom settings - for (let d in data) { - if (data.hasOwnProperty(d)) { - newFolderSettings[d] = data[d]; - } - } - - //merge cached/persistent values (if there exists a folder with the given folderID) - let folder = this.getFolder(account, newFolderSettings.folderID); - if (folder !== null) { - let persistentSettings = tbSync[provider].getPersistentFolderSettings(); - for (let s=0; s < persistentSettings.length; s++) { - if (folder[persistentSettings[s]]) newFolderSettings[persistentSettings[s]] = folder[persistentSettings[s]]; - } - } - - if (!this.folders.hasOwnProperty(account)) this.folders[account] = {}; - this.folders[account][newFolderSettings.folderID] = newFolderSettings; - this.saveFolders(); - }, - - deleteFolder: function(account, folderID) { - delete (this.folders[account][folderID]); - //if there are no more folders, delete entire account entry - if (Object.keys(this.folders[account]).length === 0) delete (this.folders[account]); - this.saveFolders(); - }, - - //get all folders of a given account - getFolders: function (account) { - if (!this.folders.hasOwnProperty(account)) this.folders[account] = {}; - return this.folders[account]; - }, - - //get a specific folder - getFolder: function(account, folderID) { - //does the folder exist? - if (this.folders.hasOwnProperty(account) && this.folders[account].hasOwnProperty(folderID)) return this.folders[account][folderID]; - else return null; - }, - - isValidFolderSetting: function (account, field) { - if (["cached"].includes(field)) //internal properties, do not need to be defined by user/provider - return true; - - //check if provider is installed - let provider = this.getAccountSetting(account, "provider"); - if (!tbSync.loadedProviders.hasOwnProperty(provider)) { - tbSync.dump("Error @ isValidFolderSetting", "Unknown provider <"+provider+"> for account <"+account+">!"); - return false; - } - - if (tbSync[provider].getDefaultFolderEntries(account).hasOwnProperty(field)) { - return true; - } else { - tbSync.dump("Error @ isValidFolderSetting", "Unknown folder setting <"+field+"> for account <"+account+">!"); - return false; - } - }, - - getFolderSetting: function(account, folderID, field) { - //does the field exist? - let folder = this.getFolder(account, folderID); - if (folder === null) throw "Unknown folder <"+folderID+">!"; - - if (this.isValidFolderSetting(account, field)) { - if (folder.hasOwnProperty(field)) { - return folder[field]; - } else { - let provider = this.getAccountSetting(account, "provider"); - let defaultFolder = tbSync[provider].getDefaultFolderEntries(account); - //handle internal fields, that do not have a default value (see isValidFolderSetting) - return (defaultFolder[field] ? defaultFolder[field] : ""); - } - } - }, - - setFolderSetting: function (account, folderID, field, value) { - //this function can update ALL folders for a given account (if folderID == "") or just a specific folder - if (this.isValidFolderSetting(account, field)) { - if (folderID == "") { - for (let fID in this.folders[account]) { - this.folders[account][fID][field] = value.toString(); - } - } else { - this.folders[account][folderID][field] = value.toString(); - } - this.saveFolders(); - } - }, - - resetFolderSetting: function (account, folderID, field) { - let provider = this.getAccountSetting(account, "provider"); - let defaults = tbSync[provider].getDefaultFolderEntries(account); - //this function can update ALL folders for a given account (if folderID == "") or just a specific folder - if (this.isValidFolderSetting(account, field)) { - if (folderID == "") { - for (let fID in this.folders[account]) { - //handle internal fields, that do not have a default value (see isValidFolderSetting) - this.folders[account][fID][field] = defaults[field] ? defaults[field] : ""; - } - } else { - //handle internal fields, that do not have a default value (see isValidFolderSetting) - this.folders[account][folderID][field] = defaults[field] ? defaults[field] : ""; - } - this.saveFolders(); - } - }, - - findFoldersWithSetting: function (_folderFields, _folderValues, _accountFields = [], _accountValues = []) { - //Find values based on one (string) or more (array) field conditions in folder and account data. - //folderValues element may contain "," to seperate multiple field values for matching (OR) - let data = []; - let folderFields = []; - let folderValues = []; - let accountFields = []; - let accountValues = []; - - //turn string parameters into arrays - if (Array.isArray(_folderFields)) folderFields = _folderFields; else folderFields.push(_folderFields); - if (Array.isArray(_folderValues)) folderValues = _folderValues; else folderValues.push(_folderValues); - if (Array.isArray(_accountFields)) accountFields = _accountFields; else accountFields.push(_accountFields); - if (Array.isArray(_accountValues)) accountValues = _accountValues; else accountValues.push(_accountValues); - - //fallback to old interface (name, value, account = "") - if (accountFields.length == 1 && accountValues.length == 0) { - accountValues.push(accountFields[0]); - accountFields[0] = "account"; - } - - for (let aID in this.folders) { - //is this a leftover folder of an account, which no longer there? - if (!this.accounts.data.hasOwnProperty(aID)) { - delete (this.folders[aID]); - this.saveFolders(); - continue; - } - - //skip this folder, if it belongs to an account currently not supported (provider not loaded) - if (!tbSync.loadedProviders.hasOwnProperty(this.getAccountSetting(aID, "provider"))) { - continue; - } - - //does this account match account search options? - let accountmatch = true; - for (let a = 0; a < accountFields.length && accountmatch; a++) { - accountmatch = (this.getAccountSetting(aID, accountFields[a]) == accountValues[a]); - } - - if (accountmatch) { - for (let fID in this.folders[aID]) { - //does this folder match folder search options? - let foldermatch = true; - for (let f = 0; f < folderFields.length && foldermatch; f++) { - foldermatch = folderValues[f].split(",").includes(this.getFolderSetting(aID, fID, folderFields[f])); - } - if (foldermatch) data.push(this.folders[aID][fID]); - } - } - } - - //still a reference to the original data - return data; - }, - - - - - - - init: Task.async (function* () { - - tbSync.dump("INIT","DB"); - - //DB Concept: - //-- on application start, data is read async from json file into object - //-- add-on only works on object - //-- each time data is changed, an async write job is initiated 2s in the future and is resceduled, if another request arrives within that time - - //load changelog from file - try { - let data = yield OS.File.read(tbSync.getAbsolutePath(db.changelogFile)); - db.changelog = JSON.parse(tbSync.decoder.decode(data)); - } catch (ex) { - //if there is no file, there is no file... - } - - //load accounts from file - try { - let data = yield OS.File.read(tbSync.getAbsolutePath(db.accountsFile)); - db.accounts = JSON.parse(tbSync.decoder.decode(data)); - } catch (ex) { - //if there is no file, there is no file... - } - - //load folders from file - try { - let data = yield OS.File.read(tbSync.getAbsolutePath(db.foldersFile)); - db.folders = JSON.parse(tbSync.decoder.decode(data)); - } catch (ex) { - //if there is no file, there is no file... - } - - }), - - -}; diff -Nru tbsync-1.7/content/manager/about.xul tbsync-2.11/content/manager/about.xul --- tbsync-1.7/content/manager/about.xul 2019-02-26 16:25:49.000000000 +0000 +++ tbsync-2.11/content/manager/about.xul 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ - - - - - - - - - - - -