Codebase list eas4tbsync / 0c1d562
New upstream version 1.8 Mechtilde Stehmann 4 years ago
28 changed file(s) with 960 addition(s) and 205 deletion(s). Raw diff Collapse all Expand all
44 More information can be found in the [wiki](https://github.com/jobisoft/EAS-4-TbSync/wiki/About:-Provider-for-Exchange-ActiveSync) of this repository
55
66 ## Want to add or fix a localization?
7 To help translating this project, please visit to [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.
7 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.
88
99
1010 ## External data sources
1414
1515 ## Icon sources and attributions
1616
17 #### CC0 Public Domain
18 * [365_*.png] by [Microsoft / Wikimedia](https://commons.wikimedia.org/w/index.php?curid=21546299), converted from [SVG to PNG](https://ezgif.com/svg-to-png)
19
1720 #### CC-BY 3.0
18 * [eas16.png] by [FatCow Web Hosting](https://www.iconfinder.com/icons/64484/exchange_ms_icon)
21 * [eas*.png] by [FatCow Web Hosting](https://www.iconfinder.com/icons/64484/exchange_ms_icon)
22 * [exchange_300.png] derived from [Microsoft Exchange Icon #270871](https://icon-library.net/icon/microsoft-exchange-icon-10.html), [resized](www.simpleimageresizer.com/)
77 <!ENTITY add.urldescription "It should be sufficient to provide just the basic server address (e.g.: mail.yourserver.com). However, it is also possible to provide the full URL (e.g.: https://mail.yourserver.com/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Server configuration:">
10 <!ENTITY add.description "Please enter a name for the new TbSync account, the user name of the ActiveSync account you want to add (probably an email address) and select one of the available server configuration options. ">
10 <!ENTITY add.description "Please select one of the available server configuration options and enter the requested details. ">
1111
1212 <!ENTITY pref.autodetect "best available">
1313
4040 <!ENTITY pref.seperator.description "Separator for multiline address field.">
4141 <!ENTITY pref.displayoverride "Override Display Name with “First Name” + “Second Name”">
4242
43 <!ENTITY servertype.auto "Automatic configuration via ActiveSync Autodiscover">
43 <!ENTITY servertype.auto "Automatic configuration">
44 <!ENTITY servertype.description.auto "The configuration for many ActiveSync servers can be discovered by providing just your email address.">
45
4446 <!ENTITY servertype.custom "Custom configuration">
47 <!ENTITY servertype.description.custom "Setup your account by manually providing the address of the server you want to connect.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Accounts connected to Office 365 use a modern authentication process called OAuth 2.0 which also supports Multi-Factor-Authentication (MFA).">
50
4551 <!ENTITY servertype.unlock "Double click to unlock all predefined server settings.">
4652
4753 <!ENTITY abCard.MiddleName "Middle name:">
77 <!ENTITY add.urldescription "Meist reicht die Angabe der einfachen Serveradresse (z.B.: mail.meinserver.de), alternativ kann aber auch die komplette URL angegeben werden (z.B.: https://mail.meinserver.de/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Server Konfiguration:">
10 <!ENTITY add.description "Bitte wählen Sie einen Namen für das neue TbSync Konto, tragen den Benutzername für das ActiveSync Konto welches Sie hinzufügen möchten (üblicher Weise eine E-Mail Adresse) ein und wählen Sie die passende Option für die Server Konfiguration aus. ">
10 <!ENTITY add.description "Bitte wählen Sie eine der verfügbaren Server-Konfigurationen aus und geben Sie die angeforderten Details ein. ">
1111
1212 <!ENTITY pref.autodetect "bestmöglich">
1313
4040 <!ENTITY pref.seperator.description "Separator für mehrzeilige Adressfelder: ">
4141 <!ENTITY pref.displayoverride "Überschreibe Anzeigename mit 'Vorname' + 'Nachname'">
4242
43 <!ENTITY servertype.auto "Automatische Konfiguration via ActiveSync Autodiscover">
43 <!ENTITY servertype.auto "Automatische Konfiguration">
44 <!ENTITY servertype.description.auto "Die Konfiguration vieler ActiveSync Server kann allein durch die Angabe Ihrer E-Mail-Adresse erfolgen.">
45
4446 <!ENTITY servertype.custom "Benutzerspezifische Konfiguration">
47 <!ENTITY servertype.description.custom "Richten Sie Ihr Konto ein, indem Sie die Adresse des Servers angeben, mit dem Sie sich verbinden möchten.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Konten, die mit Office 365 verbunden sind, verwenden den modernen OAuth 2.0 Authentifizierungsprozess, der auch Multi-Factor-Authentication (MFA) unterstützt.">
50
4551 <!ENTITY servertype.unlock "Doppelklick um die vordefinierten Servereinstellungen zu entsperren.">
4652
4753 <!ENTITY abCard.MiddleName "Zweiter Vorname:">
77 <!ENTITY add.urldescription "It should be sufficient to provide just the basic server address (e.g.: mail.yourserver.com). However, it is also possible to provide the full URL (e.g.: https://mail.yourserver.com/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Server configuration:">
10 <!ENTITY add.description "Please enter a name for the new TbSync account, the user name of the ActiveSync account you want to add (probably an email address) and select one of the available server configuration options. ">
10 <!ENTITY add.description "Please select one of the available server configuration options and enter the requested details. ">
1111
1212 <!ENTITY pref.autodetect "best available">
1313
4040 <!ENTITY pref.seperator.description "Separator for multiline address field.">
4141 <!ENTITY pref.displayoverride "Override Display Name with “First Name” + “Second Name”">
4242
43 <!ENTITY servertype.auto "Automatic configuration via ActiveSync Autodiscover">
43 <!ENTITY servertype.auto "Automatic configuration">
44 <!ENTITY servertype.description.auto "The configuration for many ActiveSync servers can be discovered by providing just your email address.">
45
4446 <!ENTITY servertype.custom "Custom configuration">
47 <!ENTITY servertype.description.custom "Setup your account by manually providing the address of the server you want to connect.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Accounts connected to Office 365 use a modern authentication process called OAuth 2.0 which also supports Multi-Factor-Authentication (MFA).">
50
4551 <!ENTITY servertype.unlock "Double click to unlock all predefined server settings.">
4652
4753 <!ENTITY abCard.MiddleName "Middle name:">
77 <!ENTITY add.urldescription "It should be sufficient to provide just the basic server address (e.g.: mail.yourserver.com). However, it is also possible to provide the full URL (e.g.: https://mail.yourserver.com/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Server configuration:">
10 <!ENTITY add.description "Please enter a name for the new TbSync account, the user name of the ActiveSync account you want to add (probably an email address) and select one of the available server configuration options. ">
10 <!ENTITY add.description "Please select one of the available server configuration options and enter the requested details. ">
1111
1212 <!ENTITY pref.autodetect "best available">
1313
4040 <!ENTITY pref.seperator.description "Separator for multiline address field.">
4141 <!ENTITY pref.displayoverride "Override Display Name with “First Name” + “Second Name”">
4242
43 <!ENTITY servertype.auto "Automatic configuration via ActiveSync Autodiscover">
43 <!ENTITY servertype.auto "Automatic configuration">
44 <!ENTITY servertype.description.auto "The configuration for many ActiveSync servers can be discovered by providing just your email address.">
45
4446 <!ENTITY servertype.custom "Custom configuration">
47 <!ENTITY servertype.description.custom "Setup your account by manually providing the address of the server you want to connect.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Accounts connected to Office 365 use a modern authentication process called OAuth 2.0 which also supports Multi-Factor-Authentication (MFA).">
50
4551 <!ENTITY servertype.unlock "Double click to unlock all predefined server settings.">
4652
4753 <!ENTITY abCard.MiddleName "Middle name:">
77 <!ENTITY add.urldescription "Megfelelőnek kell lennie ahhoz, hogy csak az alapkiszolgáló címét adja meg (például: posta.kiszolgálód.hu). Azonban a teljes URL-címet is megadhatjuk (például: https://posta.kiszolgálód.hu/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Kiszolgáló beállítások:">
10 <!ENTITY add.description "Adja meg az új Thunderbird-összehangolás-fiók nevét, a hozzáadni kívánt ActiveSync-fiók (valószínűleg egy e-mail cím) felhasználónevét, és válassza ki az elérhető kiszolgáló beállítások egyikét.">
10 <!ENTITY add.description "Please select one of the available server configuration options and enter the requested details. ">
1111
1212 <!ENTITY pref.autodetect "az elérhető legjobb">
1313
4040 <!ENTITY pref.seperator.description "Több címsort tartalmazó elválasztó.">
4141 <!ENTITY pref.displayoverride "A megjelenítési név felülbírálása „keresztnév” és „második keresztnév”">
4242
43 <!ENTITY servertype.auto "Önműködő beállítás az ActiveSync önműködő felismerésével">
43 <!ENTITY servertype.auto "Automatic configuration">
44 <!ENTITY servertype.description.auto "The configuration for many ActiveSync servers can be discovered by providing just your email address.">
45
4446 <!ENTITY servertype.custom "Egyéni beállítások">
47 <!ENTITY servertype.description.custom "Setup your account by manually providing the address of the server you want to connect.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Accounts connected to Office 365 use a modern authentication process called OAuth 2.0 which also supports Multi-Factor-Authentication (MFA).">
50
4551 <!ENTITY servertype.unlock "Dupla kattintson az összes előre meghatározott kiszolgáló beállításának feloldásához.">
4652
4753 <!ENTITY abCard.MiddleName "Második keresztnév:">
77 <!ENTITY add.urldescription "Dovrebbe essere sufficiente fornire solo il nome del server (ad es. mail.yourserver.com), ma è anche possibile fornire l'URL completo (ad es. https://mail.yourserver.com/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Configurazione server:">
10 <!ENTITY add.description "Immettere un nome per il nuovo account TbSync, il nome utente dell'account ActiveSync che si desidera aggiungere (probabilmente un indirizzo di posta elettronica) e selezionare una delle opzioni di configurazione server disponibili.">
10 <!ENTITY add.description "Please select one of the available server configuration options and enter the requested details. ">
1111
1212 <!ENTITY pref.autodetect "miglior opzione disponibile">
1313
4040 <!ENTITY pref.seperator.description "Separatore per i campi indirizzo multiriga.">
4141 <!ENTITY pref.displayoverride "Ignora il Nome visualizzato e mostra Nome + Cognome">
4242
43 <!ENTITY servertype.auto "Configurazione automatica tramite rilevamento automatico ActiveSync">
43 <!ENTITY servertype.auto "Automatic configuration">
44 <!ENTITY servertype.description.auto "The configuration for many ActiveSync servers can be discovered by providing just your email address.">
45
4446 <!ENTITY servertype.custom "Configurazione personalizzata">
47 <!ENTITY servertype.description.custom "Setup your account by manually providing the address of the server you want to connect.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Accounts connected to Office 365 use a modern authentication process called OAuth 2.0 which also supports Multi-Factor-Authentication (MFA).">
50
4551 <!ENTITY servertype.unlock "Cliccare due volte per sbloccare tutte le impostazioni server predefinite.">
4652
4753 <!ENTITY abCard.MiddleName "Secondo nome:">
44 <!ENTITY add.user "Nazwa użytkownika (adres e-mail):">
55 <!ENTITY add.password "Hasło:">
66 <!ENTITY add.url "Adres serwera:">
7 <!ENTITY add.urldescription "Powinno wystarczyć podanie tylko podstawowego adresu serwera (np.: mail.yourserver.com). Jednakże możliwe jest również podanie pełnego adresu URL (np.: https://mail.yourserver.com/Microsoft-Server-ActiveSync).">
7 <!ENTITY add.urldescription "Powinno wystarczyć podanie tylko podstawowego adresu serwera (np.: mail.twojserwer.com). Jednakże możliwe jest również podanie pełnego adresu URL (np.: https://mail.twojserwer.com/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Konfiguracja serwera:">
10 <!ENTITY add.description "Wprowadź nazwę nowego konta TbSync, nazwę użytkownika konta ActiveSync, które chcesz dodać (prawdopodobnie adres e-mail) i wybierz jedną z dostępnych opcji konfiguracji serwera. ">
10 <!ENTITY add.description "Wybierz jedną z dostępnych opcji konfiguracji serwera i wprowadź wymagane dane. ">
1111
1212 <!ENTITY pref.autodetect "najlepsza dostępna">
1313
1616 <!ENTITY pref.UserNameDescription "Nazwa użytkownika to zazwyczaj adres e-mail Twojego konta.">
1717 <!ENTITY pref.ServerName "Adres serwera">
1818 <!ENTITY pref.ActiveSyncVersion "Wersja ActiveSync">
19 <!ENTITY pref.ServerNameDescription "np. mail.yourserver.com">
19 <!ENTITY pref.ServerNameDescription "np. mail.twojserwer.com">
2020 <!ENTITY pref.usehttps "Użyj bezpiecznego połączenia (połącz przez https)">
2121 <!ENTITY pref.DeviceId "ID urządzenia ActiveSync">
2222 <!ENTITY pref.birthday "Wyślij informacje o urodzinach">
2929 <!ENTITY pref.ShowTrashedFolders "Pokaż foldery znalezione w koszu">
3030
3131 <!ENTITY pref.synclimit.all "wszystko">
32 <!ENTITY pref.synclimit.2weeks "sprzed 2 tygodni">
33 <!ENTITY pref.synclimit.1month "sprzed 4 tygodni">
34 <!ENTITY pref.synclimit.3month "sprzed 3 miesięcy">
35 <!ENTITY pref.synclimit.6month "sprzed 6 miesięcy">
32 <!ENTITY pref.synclimit.2weeks "z 2 tygodni">
33 <!ENTITY pref.synclimit.1month "z 4 tygodni">
34 <!ENTITY pref.synclimit.3month "z 3 miesięcy">
35 <!ENTITY pref.synclimit.6month "z 6 miesięcy">
3636 <!ENTITY pref.synclimit.description "Okres synchronizacji: ">
3737
3838 <!ENTITY pref.seperator.comma "Przecinek">
4040 <!ENTITY pref.seperator.description "Separator pola adresu wielowierszowego.">
4141 <!ENTITY pref.displayoverride "Zastąp nazwę wyświetlaną ciągiem “Imię“ + “Drugie imię”">
4242
43 <!ENTITY servertype.auto "Automatyczna konfiguracja poprzez ActiveSync Autodiscover">
43 <!ENTITY servertype.auto "Automatyczna konfiguracja">
44 <!ENTITY servertype.description.auto "Konfiguracja dla wielu serwerów ActiveSync może zostać odnaleziona poprzez podanie tylko adresu e-mail.">
45
4446 <!ENTITY servertype.custom "Konfiguracja niestandardowa">
47 <!ENTITY servertype.description.custom "Skonfiguruj konto, podając ręcznie adres serwera, z którym chcesz się połączyć.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Konta połączone z Office 365 korzystają z nowoczesnego procesu uwierzytelniania o nazwie OAuth 2.0, który obsługuje również uwierzytelnianie wielopoziomowe (MFA).">
50
4551 <!ENTITY servertype.unlock "Kliknij dwukrotnie, aby odblokować wszystkie predefiniowane ustawienia serwera.">
4652
4753 <!ENTITY abCard.MiddleName "Drugie imię:">
5359 <!ENTITY abCard.ManagerName "Menedżer:">
5460 <!ENTITY abCard.AssistantPhoneNumber "Telefon Asystenta:">
5561 <!ENTITY abCard.AssistantName "Asystent:">
56 <!ENTITY abCard.Spouse "Współmałżonek:">
62 <!ENTITY abCard.Spouse "Małżonek:">
5763 <!ENTITY abCard.Anniversary "Rocznica:">
5864
5965 <!ENTITY abCard.CarPhoneNumber "Telefon w Aucie:">
2828 syncstate.send.request.options=Oczekiwanie na opcje serwera
2929 syncstate.eval.response.options=Przetwarzanie opcji serwera
3030
31 syncstate.prepare.request.folders=Wysyłanie aktualizacja listy folderów
31 syncstate.prepare.request.folders=Wysyłanie aktualizacji listy folderów
3232 syncstate.send.request.folders=Oczekiwanie na aktualizację listy folderów
3333 syncstate.eval.response.folders=Przetwarzanie aktualizacji listy folderów
3434
3838
3939 syncstate.prepare.request.deletefolder=Przygotowanie do usunięcia folderu
4040 syncstate.send.request.deletefolder=Oczekiwanie na usunięcie folderu
41 syncstate.eval.response.deletefolder=Folder usunięto
41 syncstate.eval.response.deletefolder=Folder usunięty
4242
4343 syncstate.prepare.request.provision=Żądanie provision
4444 syncstate.send.request.provision=Oczekiwanie na provision
6868 status.disabled=Wyłączony
6969 status.notargets=Przerywam synchronizację, ponieważ nie można utworzyć celów synchronizacji.
7070 status.nouserhost=Brak nazwy użytkownika i/lub serwera. Podaj te wartości.
71 status.timeout=Przekroczona limit czasu połączenia.
71 status.timeout=Przekroczony limit czasu połączenia.
7272 status.networkerror=Nie można połączyć z serwerem.
7373 status.404=Użytkownik nie znaleziony (błąd HTTP 404).
7474 status.403=Serwer odrzucił połączenie (zabronione) (błąd HTTP 403).
9292 helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F
9393 status.network=Nie można połączyć z serwerem (##replace.1##).
9494
95 status.syncing=Synchronizuje
96 status.skipped=Jeszcze nie wspierane, pominięto
95 status.syncing=Synchronizuję
96 status.skipped=Jeszcze nie obsługiwane, pominięto
9797 status.aborted=Nie zsynchronizowane
9898 status.pending=Oczekiwanie na synchronizację
9999 status.modified=Zmiany lokalne
77 <!ENTITY add.urldescription "Deve ser suficiente fornecer apenas o endereço básico do servidor (ex.: mail.yourserver.com). No entanto, também é possível fornecer a URL completa (ex.: https://mail.yourserver.com/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Configuração do servidor:">
10 <!ENTITY add.description "Por favor, defina um nome para a nova conta TbSync, informe o nome de usuário (geralmente um endereço de e-mail), a senha e selecione uma das opções de configuração do servidor disponíveis.">
10 <!ENTITY add.description "Selecione uma das opções de configuração do servidor disponíveis e insira os detalhes solicitados.">
1111
1212 <!ENTITY pref.autodetect "melhor disponível">
1313
1818 <!ENTITY pref.ActiveSyncVersion "Versão do ActiveSync">
1919 <!ENTITY pref.ServerNameDescription "ex. mail.yourserver.com">
2020 <!ENTITY pref.usehttps "Utilizar conexão segura (conectar via https)">
21 <!ENTITY pref.DeviceId "ActiveSync device ID">
21 <!ENTITY pref.DeviceId "ID Dispositivo ActiveSync">
2222 <!ENTITY pref.birthday "Enviar informações de aniversário">
2323 <!ENTITY pref.generaloptions "Opções gerais">
2424 <!ENTITY pref.contactoptions "Opções de contato">
4040 <!ENTITY pref.seperator.description "Separador para campo de endereço de múltiplas linhas.">
4141 <!ENTITY pref.displayoverride "Substituir nome de exibição com Nome + Segundo nome">
4242
43 <!ENTITY servertype.auto "Configuração automática via Autodiscover do ActiveSync">
43 <!ENTITY servertype.auto "Configuração automática">
44 <!ENTITY servertype.description.auto "A configuração para muitos servidores do ActiveSync pode ser descoberta fornecendo apenas seu endereço de e-mail.">
45
4446 <!ENTITY servertype.custom "Configuração personalizada">
47 <!ENTITY servertype.description.custom "Configure sua conta fornecendo manualmente o endereço do servidor que você deseja conectar.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Contas conectadas ao Office 365 utilizam um processo de autenticação moderno chamado OAuth 2.0 que também suporta autenticação multi-Fator (MFA).">
50
4551 <!ENTITY servertype.unlock "Clique duas vezes para desbloquear todas as configurações do servidor predefinidas.">
4652
4753 <!ENTITY abCard.MiddleName "Nome do meio:">
143143 acl.readwrite=Ler e escrever no servidor
144144 acl.readonly=Acesso ao servidor somente leitura (reverter alterações locais)
145145
146 autocomplete.serverdirectory = global server directory
146 autocomplete.serverdirectory = diretório global do servidor
77 <!ENTITY add.urldescription "Достаточно указать только базовый адрес сервера (например: mail.yourserver.com). Однако также можно указать полный URL-адрес (например: https://mail.yourserver.com/Microsoft-Server-ActiveSync).">
88
99 <!ENTITY add.server "Конфигурация сервера:">
10 <!ENTITY add.description "Введите имя для нового аккаунта TbSync, имя пользователя аккаунта ActiveSync которое вы хотите добавить (возможно, адрес электронной почты), и выберите один из доступных параметров конфигурации сервера. ">
10 <!ENTITY add.description "Пожалуйста, выберите один из доступных параметров конфигурации сервера и введите требуемую информацию. ">
1111
1212 <!ENTITY pref.autodetect "лучшее из доступных">
1313
4040 <!ENTITY pref.seperator.description "Сепаратор для многострочного поля адреса.">
4141 <!ENTITY pref.displayoverride "Переопределить отображаемое имя с помощью “Имя” + “Фамилия”">
4242
43 <!ENTITY servertype.auto "Автоматическая настройка с помощью ActiveSync автоопределения">
43 <!ENTITY servertype.auto "Автоматическая настройка">
44 <!ENTITY servertype.description.auto "Для определения конфигурации множества серверов ActiveSync достаточно указать только адрес Вашей электронной почты.">
45
4446 <!ENTITY servertype.custom "Ручная настройка">
47 <!ENTITY servertype.description.custom "Настройте Вашу учетную запись, вручную указав адрес сервера, к которому Вы хотите подключиться.">
48 <!ENTITY servertype.office365 "Microsoft Office 365">
49 <!ENTITY servertype.description.office365 "Учетные записи, подключенные к Office 365, используют современный процесс аутентификации под названием OAuth 2.0, который также поддерживает многофакторную аутентификацию (MFA).">
50
4551 <!ENTITY servertype.unlock "Дважды щелкните, чтобы разблокировать все предопределенные настройки сервера.">
4652
4753 <!ENTITY abCard.MiddleName "Отчество:">
0 /* This Source Code Form is subject to the terms of the Mozilla Public
1 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
2 * You can obtain one at http://mozilla.org/MPL/2.0/. */
3
4 /**
5 * Provides OAuth 2.0 authentication.
6 * @see RFC 6749
7 */
8 var EXPORTED_SYMBOLS = ["OAuth2_1"];
9
10 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
11 const { Log4Moz } = ChromeUtils.import("resource:///modules/gloda/log4moz.js");
12
13 Cu.importGlobalProperties(["fetch"]);
14
15 // Only allow one connecting window per endpoint.
16 var gConnecting = {};
17
18 function OAuth2_1(aBaseURI, aScope, aAppKey, aAppSecret) {
19 this.authURI = aBaseURI + "oauth2/auth";
20 this.tokenURI = aBaseURI + "oauth2/token";
21 this.consumerKey = aAppKey;
22 this.consumerSecret = aAppSecret;
23 this.scope = aScope;
24 this.extraAuthParams = [];
25
26 this.log = Log4Moz.getConfiguredLogger("TBOAuth");
27 }
28
29 OAuth2_1.prototype = {
30 consumerKey: null,
31 consumerSecret: null,
32 completionURI: "http://localhost",
33 requestWindowURI: "chrome://messenger/content/browserRequest.xul",
34 requestWindowFeatures: "chrome,private,centerscreen,width=980,height=750",
35 requestWindowTitle: "",
36 scope: null,
37
38 accessToken: null,
39 refreshToken: null,
40 tokenExpires: 0,
41
42 connect(aSuccess, aFailure, aWithUI, aRefresh) {
43 this.connectSuccessCallback = aSuccess;
44 this.connectFailureCallback = aFailure;
45
46 if (!aRefresh && this.accessToken) {
47 aSuccess();
48 } else if (this.refreshToken) {
49 this.requestAccessToken(this.refreshToken, true);
50 } else {
51 if (!aWithUI) {
52 aFailure('{ "error": "auth_noui" }');
53 return;
54 }
55 if (gConnecting[this.authURI]) {
56 aFailure("Window already open");
57 return;
58 }
59 this.requestAuthorization();
60 }
61 },
62
63 requestAuthorization() {
64 let params = new URLSearchParams({
65 response_type: "code",
66 client_id: this.consumerKey,
67 redirect_uri: this.completionURI,
68 });
69
70 // The scope is optional.
71 if (this.scope) {
72 params.append("scope", this.scope);
73 }
74
75 for (let [name, value] of this.extraAuthParams) {
76 params.append(name, value);
77 }
78
79 let authEndpointURI = this.authURI + "?" + params.toString();
80 this.log.info(
81 "Interacting with the resource owner to obtain an authorization grant " +
82 "from the authorization endpoint: " +
83 authEndpointURI
84 );
85
86 this._browserRequest = {
87 account: this,
88 url: authEndpointURI,
89 _active: true,
90 iconURI: "",
91 cancelled() {
92 if (!this._active) {
93 return;
94 }
95
96 this.account.finishAuthorizationRequest();
97 this.account.onAuthorizationFailed(
98 Cr.NS_ERROR_ABORT,
99 '{ "error": "cancelled"}'
100 );
101 },
102
103 loaded(aWindow, aWebProgress) {
104 if (!this._active) {
105 return;
106 }
107
108 this._listener = {
109 window: aWindow,
110 webProgress: aWebProgress,
111 _parent: this.account,
112
113 QueryInterface: ChromeUtils.generateQI([
114 Ci.nsIWebProgressListener,
115 Ci.nsISupportsWeakReference,
116 ]),
117
118 _cleanUp() {
119 this.webProgress.removeProgressListener(this);
120 this.window.close();
121 delete this.window;
122 },
123
124 _checkForRedirect(aURL) {
125 if (aURL.indexOf(this._parent.completionURI) != 0) {
126 return;
127 }
128
129 this._parent.finishAuthorizationRequest();
130 this._parent.onAuthorizationReceived(aURL);
131 },
132
133 onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
134 const wpl = Ci.nsIWebProgressListener;
135 if (aStateFlags & (wpl.STATE_START | wpl.STATE_IS_NETWORK)) {
136 this._checkForRedirect(aRequest.name);
137 }
138 },
139 onLocationChange(aWebProgress, aRequest, aLocation) {
140 this._checkForRedirect(aLocation.spec);
141 },
142 onProgressChange() {},
143 onStatusChange() {},
144 onSecurityChange() {},
145 };
146 aWebProgress.addProgressListener(
147 this._listener,
148 Ci.nsIWebProgress.NOTIFY_ALL
149 );
150 aWindow.document.title = this.account.requestWindowTitle;
151 },
152 };
153
154 this.wrappedJSObject = this._browserRequest;
155 gConnecting[this.authURI] = true;
156 Services.ww.openWindow(
157 null,
158 this.requestWindowURI,
159 null,
160 this.requestWindowFeatures,
161 this
162 );
163 },
164 finishAuthorizationRequest() {
165 gConnecting[this.authURI] = false;
166 if (!("_browserRequest" in this)) {
167 return;
168 }
169
170 this._browserRequest._active = false;
171 if ("_listener" in this._browserRequest) {
172 this._browserRequest._listener._cleanUp();
173 }
174 delete this._browserRequest;
175 },
176
177 // @see RFC 6749 section 4.1.2: Authorization Response
178 onAuthorizationReceived(aURL) {
179 this.log.info("OAuth2_1 authorization received: url=" + aURL);
180 let params = new URLSearchParams(aURL.split("?", 2)[1]);
181 if (params.has("code")) {
182 this.requestAccessToken(params.get("code"), false);
183 } else {
184 this.onAuthorizationFailed(null, aURL);
185 }
186 },
187
188 onAuthorizationFailed(aError, aData) {
189 this.connectFailureCallback(aData);
190 },
191
192 /**
193 * Request a new access token, or refresh an existing one.
194 * @param {string} aCode - The token issued to the client.
195 * @param {boolean} aRefresh - Whether it's a refresh of a token or not.
196 */
197 requestAccessToken(aCode, aRefresh) {
198 // @see RFC 6749 section 4.1.3. Access Token Request
199 // @see RFC 6749 section 6. Refreshing an Access Token
200
201 let data = new URLSearchParams();
202 data.append("client_id", this.consumerKey);
203 if (this.consumerSecret) data.append("client_secret", this.consumerSecret);
204
205 if (aRefresh) {
206 this.log.info(
207 `Making a refresh request to the token endpoint: ${this.tokenURI}`
208 );
209 data.append("grant_type", "refresh_token");
210 data.append("refresh_token", aCode);
211 } else {
212 this.log.info(
213 `Making access token request to the token endpoint: ${this.tokenURI}`
214 );
215 data.append("grant_type", "authorization_code");
216 data.append("code", aCode);
217 data.append("redirect_uri", this.completionURI);
218 }
219
220 fetch(this.tokenURI, {
221 method: "POST",
222 cache: "no-cache",
223 body: data,
224 })
225 .then(response => response.json())
226 .then(result => {
227 if ("error" in result) {
228 // RFC 6749 section 5.2. Error Response
229 this.log.info(
230 `The authorization server returned an error response: ${JSON.stringify(
231 result
232 )}`
233 );
234 this.connectFailureCallback(result);
235 return;
236 }
237
238 // RFC 6749 section 5.1. Successful Response
239 this.log.info("The authorization server issued an access token.");
240 this.accessToken = result.access_token;
241 if ("refresh_token" in result) {
242 this.refreshToken = result.refresh_token;
243 }
244 if ("expires_in" in result) {
245 this.tokenExpires = new Date().getTime() + result.expires_in * 1000;
246 } else {
247 this.tokenExpires = Number.MAX_VALUE;
248 }
249 this.connectSuccessCallback();
250 })
251 .catch(err => {
252 this.log.info(`Connection to authorization server failed: ${err}`);
253 this.connectFailureCallback(err);
254 });
255 },
256 };
157157
158158 eas.sync.setItemRecurrence(item, syncdata, data);
159159
160 // BusyStatus is always representing the status of the current user in terms of availability.
161 // It has nothing to do with the status of a meeting. The user could be just the organizer, but does not need to attend, so he would be free.
162 // The correct map is between BusyStatus and TRANSP (show time as avail, busy, unset)
163 // A new event always sets TRANSP to busy, so unset is indeed a good way to store Tentiative
164 // However:
165 // - EAS Meetingstatus only knows ACTIVE or CANCELLED, but not CONFIRMED or TENTATIVE
166 // - TB STATUS has UNSET, CONFIRMED, TENTATIVE, CANCELLED
167 // -> Special case: User sets BusyStatus to TENTIATIVE -> TRANSP is unset and also set STATUS to TENTATIVE
168 // The TB STATUS is the correct map for EAS Meetingstatus and should be unset, if it is not a meeting EXCEPT if set to TENTATIVE
169 let tbStatus = (data.BusyStatus && data.BusyStatus == "1" ? "TENTATIVE" : null);
170
160171 if (data.MeetingStatus) {
161172 //store original EAS value
162173 item.setProperty("X-EAS-MeetingStatus", data.MeetingStatus);
164175 let M = data.MeetingStatus & 0x1;
165176 let R = data.MeetingStatus & 0x2;
166177 let C = data.MeetingStatus & 0x4;
167
168 //we can map M+C to TB STATUS (TENTATIVE, CONFIRMED, CANCELLED, unset)
169 //if it is not a meeting -> unset
170 //if it is a meeting -> CANCELLED or CONFIRMED
171 if (M) item.setProperty("STATUS", (C ? "CANCELLED" : "CONFIRMED"));
172 else item.deleteProperty("STATUS");
178
179 // We can map M+C to TB STATUS (TENTATIVE, CONFIRMED, CANCELLED, unset).
180 if (M) {
181 if (C) tbStatus = "CANCELLED";
182 else if (!tbStatus) tbStatus = "CONFIRMED"; // do not override "TENTIATIVE"
183 }
173184
174185 //we can also use the R information, to update our fallbackOrganizerName
175186 if (!R && data.OrganizerName) syncdata.target.calendar.setProperty("fallbackOrganizerName", data.OrganizerName);
176187 }
177188
189 if (tbStatus) item.setProperty("STATUS", tbStatus)
190 else item.deleteProperty("STATUS");
191
178192 //TODO: attachements (needs EAS 16.0!)
179
180193 },
181194
182195
247260 //Body
248261 wbxml.append(eas.sync.getItemBody(item, syncdata));
249262
250 //BusyStatus (TRANSP)
251 wbxml.atag("BusyStatus", eas.sync.mapThunderbirdPropertyToEas("TRANSP", "BusyStatus", item));
252
263 //BusyStatus (Free, Tentative, Busy) is taken from TRANSP (busy, free, unset=tentative)
264 //However if STATUS is set to TENTATIVE, overide TRANSP and set BusyStatus to TENTATIVE
265 if (item.hasProperty("STATUS") && item.getProperty("STATUS") == "TENTATIVE") {
266 wbxml.atag("BusyStatus","1");
267 } else {
268 wbxml.atag("BusyStatus", eas.sync.mapThunderbirdPropertyToEas("TRANSP", "BusyStatus", item));
269 }
270
253271 //Organizer
254272 if (!isException) {
255273 if (item.organizer && item.organizer.commonName) wbxml.atag("OrganizerName", item.organizer.commonName);
77
88 "use strict";
99
10 var { OAuth2_1 } = ChromeUtils.import("chrome://eas4tbsync/content/OAuth2_1.jsm");
11
1012 var network = {
1113
1214 getEasURL: function(accountData) {
4244 // Also update the username of this account. Add dedicated username setter?
4345 accountData.setAccountProperty("user", newUsername);
4446 },
47
48 removeLoginData: function() {
49 TbSync.passwordManager.removeLoginInfos(this.host, "TbSync/EAS");
50 }
4551 };
4652 return authData;
4753 },
48
49
50
51
52
53
54
55
56
54
55 // prepare and patch OAuth2 object
56 getOAuthObj: function(configObject = null) {
57 let accountname, user, host, accountID;
58
59 let accountData = (configObject && configObject.hasOwnProperty("accountData")) ? configObject.accountData : null;
60 if (accountData) {
61 accountname = accountData.getAccountProperty("accountname");
62 user = accountData.getAccountProperty("user");
63 host = accountData.getAccountProperty("host");
64 accountID = accountData.accountID;
65 } else {
66 accountname = (configObject && configObject.hasOwnProperty("accountname")) ? configObject.accountname : "";
67 user = (configObject && configObject.hasOwnProperty("user")) ? configObject.user : "";
68 host = (configObject && configObject.hasOwnProperty("host")) ? configObject.host : "";
69 accountID = "";
70 }
71
72 let config = {};
73 switch (host) {
74 case "outlook.office365.com":
75 config = {
76 auth_uri : "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
77 token_uri : "https://login.microsoftonline.com/common/oauth2/v2.0/token",
78 redirect_uri : "https://login.microsoftonline.com/common/oauth2/nativeclient",
79 scope : "offline_access https://outlook.office.com/EAS.AccessAsUser.All",
80 client_id : "2980deeb-7460-4723-864a-f9b0f10cd992",
81 }
82 break;
83
84 default:
85 return null;
86 }
87
88 let oauth = new OAuth2_1("", config.scope, config.client_id, config.client_secret);
89 oauth.requestWindowFeatures = "chrome,private,centerscreen,width=500,height=750";
90
91 // The v2 endpoints are different and need manual override
92 oauth.authURI = config.auth_uri ;
93 oauth.tokenURI = config.token_uri;
94 oauth.completionURI = config.redirect_uri;
95
96 oauth.extraAuthParams = [
97 ["prompt", "consent"],
98 ["login_hint", user],
99 ];
100
101 if (accountname) {
102 oauth.requestWindowTitle = "TbSync account <" + accountname + "> requests authorization.";
103 } else {
104 oauth.requestWindowTitle = "A TbSync account requests authorization.";
105 }
106
107
108
109
110 /* Adding custom methods to the oauth object */
111
112 oauth.asyncConnect = async function(rv) {
113 let self = this;
114 rv.error = "";
115 rv.tokens = "";
116
117 // If multiple resources need to authenticate they will all end here, even though they
118 // might share the same token. Due to the async nature, each process will refresh
119 // "its own" token again, which is not needed. We force clear the token here and each
120 // final connect process will actually check the acccessToken and abort the refresh,
121 // if it is already there, generated by some other process.
122 if (self.accessToken) self.accessToken = "";
123
124 try {
125 await new Promise(function(resolve, reject) {
126 // refresh = false will do nothing and resolve immediately, if an accessToken
127 // exists already, which must have been generated by another process, as
128 // we cleared it beforehand.
129 self.connect(resolve, reject, /* with UI */ true, /* refresh */ false);
130 });
131 rv.tokens = self.tokens;
132 return true;
133 } catch (e) {
134 rv.error = eas.tools.isString(e) ? e : JSON.stringify(e);
135 }
136
137 try {
138 switch (JSON.parse(rv.error).error) {
139 case "invalid_grant":
140 self.accessToken = "";
141 self.refreshToken = "";
142 return true;
143
144 case "cancelled":
145 rv.error = "OAuthAbortError";
146 break;
147
148 default:
149 rv.error = "OAuthServerError::"+rv.error;
150 break;
151 }
152 } catch (e) {
153 rv.error = "OAuthServerError::"+rv.error;
154 Components.utils.reportError(e);
155 }
156 return false;
157 };
158
159 oauth.isExpired = function() {
160 const OAUTH_GRACE_TIME = 30 * 1000;
161 return (this.tokenExpires - OAUTH_GRACE_TIME < new Date().getTime());
162 };
163
164 const OAUTHVALUES = [
165 ["access", "", "accessToken"],
166 ["refresh", "", "refreshToken"],
167 ["expires", Number.MAX_VALUE, "tokenExpires"],
168 ];
169
170 // returns a JSON string containing all the oauth values
171 Object.defineProperty(oauth, "tokens", {
172 get: function() {
173 let tokensObj = {};
174 for (let oauthValue of OAUTHVALUES) {
175 // use the system value or if not defined the default
176 tokensObj[oauthValue[0]] = this[oauthValue[2]] || oauthValue[1];
177 }
178 return JSON.stringify(tokensObj);
179 },
180 enumerable: true,
181 });
182
183 if (accountData) {
184 // authData allows us to access the password manager values belonging to this account/calendar
185 // simply by authdata.username and authdata.password
186 oauth.authData = TbSync.providers.eas.network.getAuthData(accountData);
187
188 oauth.parseAndSanitizeTokenString = function(tokenString) {
189 let _tokensObj = {};
190 try {
191 _tokensObj = JSON.parse(tokenString);
192 } catch (e) {}
193
194 let tokensObj = {};
195 for (let oauthValue of OAUTHVALUES) {
196 // use the provided value or if not defined the default
197 tokensObj[oauthValue[0]] = (_tokensObj && _tokensObj.hasOwnProperty(oauthValue[0]))
198 ? _tokensObj[oauthValue[0]]
199 : oauthValue[1];
200 }
201 return tokensObj;
202 };
203
204 // Define getter/setter to act on the password manager password value belonging to this account/calendar
205 for (let oauthValue of OAUTHVALUES) {
206 Object.defineProperty(oauth, oauthValue[2], {
207 get: function() {
208 return this.parseAndSanitizeTokenString(this.authData.password)[oauthValue[0]];
209 },
210 set: function(val) {
211 let tokens = this.parseAndSanitizeTokenString(this.authData.password);
212 let valueChanged = (val != tokens[oauthValue[0]])
213 if (valueChanged) {
214 tokens[oauthValue[0]] = val;
215 this.authData.updateLoginData(this.authData.user, JSON.stringify(tokens));
216 }
217 },
218 enumerable: true,
219 });
220 }
221 }
222
223 return oauth;
224 },
225
226 getOAuthValue: function(currentTokenString, type = "access") {
227 try {
228 let tokens = JSON.parse(currentTokenString);
229 if (tokens.hasOwnProperty(type))
230 return tokens[type];
231 } catch (e) {
232 //NOOP
233 }
234 return "";
235 },
57236
58237 sendRequest: async function (wbxml, command, syncData, allowSoftFail = false) {
59238 let ALLOWED_RETRIES = {
62241 }
63242
64243 let rv = {};
244 let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData });
245 let syncState = syncData.getSyncState().state;
246
65247 for (;;) {
66248
67249 if (rv.errorType) {
70252 if (ALLOWED_RETRIES[rv.errorType] > 0) {
71253 ALLOWED_RETRIES[rv.errorType]--;
72254
255
73256 switch (rv.errorType) {
74257
75258 case "PasswordPrompt":
76259 {
77 let authData = eas.network.getAuthData(syncData.accountData);
78 let promptData = {
79 windowID: "auth:" + syncData.accountData.accountID,
80 accountname: syncData.accountData.getAccountProperty("accountname"),
81 usernameLocked: syncData.accountData.isConnected(),
82 username: authData.user
83 }
84260
85 let syncState = syncData.getSyncState().state;
86 syncData.setSyncState("passwordprompt");
87 let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
88 if (credentials) {
89 // Update login data and try again.
90 authData.updateLoginData(credentials.username, credentials.password);
91 syncData.setSyncState(syncState);
92 retry = true;
261 if (oauthData) {
262 oauthData.accessToken = "";
263 retry = true;
264 } else {
265 let authData = eas.network.getAuthData(syncData.accountData);
266 syncData.setSyncState("passwordprompt");
267 let promptData = {
268 windowID: "auth:" + syncData.accountData.accountID,
269 accountname: syncData.accountData.getAccountProperty("accountname"),
270 usernameLocked: syncData.accountData.isConnected(),
271 username: authData.user
272 }
273 let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
274 if (credentials) {
275 retry = true;
276 authData.updateLoginData(credentials.username, credentials.password);
277 }
93278 }
94279 }
95280 break;
96281
97282 case "NetworkError":
98283 {
99 // Could not connect to server. Can we rerun autodiscover?
100 if (syncData.accountData.getAccountProperty( "servertype") == "auto") {
284 // Could not connect to server. Can we rerun autodiscover?
285 // Note: Autodiscover is currently not supported by OAuth
286 if (syncData.accountData.getAccountProperty( "servertype") == "auto" && !oauthData) {
101287 let errorcode = await eas.network.updateServerConnectionViaAutodiscover(syncData);
102288 console.log("ERR: " + errorcode);
103289 if (errorcode == 200) {
120306 if (!retry) throw rv.errorObj;
121307 }
122308
309 // check OAuth situation before connecting
310 if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
311 syncData.setSyncState("oauthprompt");
312 let _rv = {}
313 if (!(await oauthData.asyncConnect(_rv))) {
314 throw eas.sync.finish("error", _rv.error);
315 }
316 }
317
318 // Return to original syncstate
319 if (syncState != syncData.getSyncState().state) {
320 syncData.setSyncState(syncState);
321 }
123322 rv = await this.sendRequestPromise(wbxml, command, syncData, allowSoftFail);
124323
125324 if (rv.errorType) {
160359 syncData.req.overrideMimeType("text/plain");
161360 syncData.req.setRequestHeader("User-Agent", userAgent);
162361 syncData.req.setRequestHeader("Content-Type", "application/vnd.ms-sync.wbxml");
163 syncData.req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(connection.user + ':' + connection.password));
362 if (connection.password) {
363 if (eas.network.getOAuthObj({ accountData: syncData.accountData })) {
364 syncData.req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(connection.password, "access"));
365 } else {
366 syncData.req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(connection.user + ':' + connection.password));
367 }
368 }
369
164370 if (syncData.accountData.getAccountProperty("asversion") == "2.5") {
165371 syncData.req.setRequestHeader("MS-ASProtocolVersion", "2.5");
166372 } else {
702908 let command = "Search";
703909
704910 let authData = eas.network.getAuthData(accountData);
911 let oauthData = eas.network.getOAuthObj({ accountData });
705912 let userAgent = accountData.getAccountProperty("useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
706913 let deviceType = accountData.getAccountProperty("devicetype");
707914 let deviceId = accountData.getAccountProperty("deviceId");
708915
709916 TbSync.dump("Sending (EAS v" + accountData.getAccountProperty("asversion") +")", "POST " + eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +deviceType + '&DeviceId=' + deviceId, true);
710917
711 try {
712 let response = await new Promise(function(resolve, reject) {
713 // Create request handler - API changed with TB60 to new XMKHttpRequest()
714 let req = new XMLHttpRequest();
715 req.mozBackgroundRequest = true;
716 req.open("POST", eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +encodeURIComponent(deviceType) + '&DeviceId=' + deviceId, true);
717 req.overrideMimeType("text/plain");
718 req.setRequestHeader("User-Agent", userAgent);
719 req.setRequestHeader("Content-Type", "application/vnd.ms-sync.wbxml");
720 req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(authData.user + ':' + authData.password));
721 if (accountData.getAccountProperty("asversion") == "2.5") {
722 req.setRequestHeader("MS-ASProtocolVersion", "2.5");
723 } else {
724 req.setRequestHeader("MS-ASProtocolVersion", "14.0");
725 }
726 req.setRequestHeader("Content-Length", wbxml.length);
727 if (accountData.getAccountProperty("provision")) {
728 req.setRequestHeader("X-MS-PolicyKey", accountData.getAccountProperty("policykey"));
729 TbSync.dump("PolicyKey used", accountData.getAccountProperty("policykey"));
730 }
731
732 req.timeout = eas.Base.getConnectionTimeout();
733
734 req.ontimeout = function () {
735 reject("GAL Search timeout");
736 };
737
738 req.onerror = function () {
739 reject("GAL Search Error");
740 };
741
742 req.onload = function() {
743 let response = req.responseText;
918 for (let i=0; i < 2; i++) {
919 // check OAuth situation before connecting
920 if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
921 let _rv = {}
922 if (!(await oauthData.asyncConnect(_rv))) {
923 throw eas.sync.finish("error", _rv.error);
924 }
925 }
926
927 try {
928 let response = await new Promise(function(resolve, reject) {
929 // Create request handler - API changed with TB60 to new XMKHttpRequest()
930 let req = new XMLHttpRequest();
931 req.mozBackgroundRequest = true;
932 req.open("POST", eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +encodeURIComponent(deviceType) + '&DeviceId=' + deviceId, true);
933 req.overrideMimeType("text/plain");
934 req.setRequestHeader("User-Agent", userAgent);
935 req.setRequestHeader("Content-Type", "application/vnd.ms-sync.wbxml");
744936
745 switch(req.status) {
746
747 case 200: //OK
748 eas.network.logXML(response, "Received (GAL Search");
749
750 //What to do on error? IS this an error? Yes!
751 if (response.length !== 0 && response.substr(0, 4) !== String.fromCharCode(0x03, 0x01, 0x6A, 0x00)) {
752 TbSync.dump("Recieved Data", "Expecting WBXML but got junk (request status = " + req.status + ", ready state = " + req.readyState + "\n>>>>>>>>>>\n" + response + "\n<<<<<<<<<<\n");
753 reject("GAL Search Response Invalid");
754 } else {
755 resolve(response);
756 }
757 break;
758
759 default:
760 reject("GAL Search Failed: " + req.status);
937 if (authData.password) {
938 if (eas.network.getOAuthObj({ accountData })) {
939 req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(authData.password, "access"));
940 } else {
941 req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(authData.user + ':' + authData.password));
942 }
761943 }
762 };
763
764 req.send(wbxml);
765
766 });
767 return response;
768 } catch (e) {
769 Components.utils.reportError(e);
770 return;
944
945 if (accountData.getAccountProperty("asversion") == "2.5") {
946 req.setRequestHeader("MS-ASProtocolVersion", "2.5");
947 } else {
948 req.setRequestHeader("MS-ASProtocolVersion", "14.0");
949 }
950 req.setRequestHeader("Content-Length", wbxml.length);
951 if (accountData.getAccountProperty("provision")) {
952 req.setRequestHeader("X-MS-PolicyKey", accountData.getAccountProperty("policykey"));
953 TbSync.dump("PolicyKey used", accountData.getAccountProperty("policykey"));
954 }
955
956 req.timeout = eas.Base.getConnectionTimeout();
957
958 req.ontimeout = function () {
959 reject("GAL Search timeout");
960 };
961
962 req.onerror = function () {
963 reject("GAL Search Error");
964 };
965
966 req.onload = function() {
967 let response = req.responseText;
968
969 switch(req.status) {
970
971 case 200: //OK
972 eas.network.logXML(response, "Received (GAL Search");
973
974 //What to do on error? IS this an error? Yes!
975 if (response.length !== 0 && response.substr(0, 4) !== String.fromCharCode(0x03, 0x01, 0x6A, 0x00)) {
976 TbSync.dump("Recieved Data", "Expecting WBXML but got junk (request status = " + req.status + ", ready state = " + req.readyState + "\n>>>>>>>>>>\n" + response + "\n<<<<<<<<<<\n");
977 reject("GAL Search Response Invalid");
978 } else {
979 resolve(response);
980 }
981 break;
982
983 case 401: // bad auth
984 resolve("401");
985 break;
986
987 default:
988 reject("GAL Search Failed: " + req.status);
989 }
990 };
991
992 req.send(wbxml);
993
994 });
995
996 if (response === "401") {
997 // try to recover from bad auth via token refresh
998 if (oauthData) {
999 oauthData.accessToken = "";
1000 continue;
1001 }
1002 }
1003
1004 return response;
1005 } catch (e) {
1006 Components.utils.reportError(e);
1007 return;
1008 }
7711009 }
7721010 },
7731011
7911029
7921030 let allowedRetries = 5;
7931031 let retry;
1032 let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData });
1033
7941034 do {
7951035 retry = false;
1036
1037 // Check OAuth situation before connecting
1038 if (oauthData && (!oauthData.accessToken || oauthData.isExpired())) {
1039 let _rv = {};
1040 syncData.setSyncState("oauthprompt");
1041 if (!(await oauthData.asyncConnect(_rv))) {
1042 throw eas.sync.finish("error", _rv.error);
1043 }
1044 }
7961045
7971046 let result = await new Promise(function(resolve,reject) {
7981047 syncData.req = new XMLHttpRequest();
8001049 syncData.req.open("OPTIONS", eas.network.getEasURL(syncData.accountData), true);
8011050 syncData.req.overrideMimeType("text/plain");
8021051 syncData.req.setRequestHeader("User-Agent", userAgent);
803 syncData.req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(authData.user + ':' + authData.password));
1052 if (authData.password) {
1053 if (eas.network.getOAuthObj({ accountData: syncData.accountData })) {
1054 syncData.req.setRequestHeader("Authorization", 'Bearer ' + eas.network.getOAuthValue(authData.password, "access"));
1055 } else {
1056 syncData.req.setRequestHeader("Authorization", 'Basic ' + TbSync.tools.b64encode(authData.user + ':' + authData.password));
1057 }
1058 }
8041059 syncData.req.timeout = eas.Base.getConnectionTimeout();
8051060
8061061 syncData.req.ontimeout = function () {
8081063 };
8091064
8101065 syncData.req.onerror = function () {
1066 let responseData = {};
1067 responseData["MS-ASProtocolVersions"] = syncData.req.getResponseHeader("MS-ASProtocolVersions");
1068 responseData["MS-ASProtocolCommands"] = syncData.req.getResponseHeader("MS-ASProtocolCommands");
1069
1070 TbSync.dump("EAS OPTIONS with response (status: "+syncData.req.status+")", "\n" +
1071 "responseText: " + syncData.req.responseText + "\n" +
1072 "responseHeader(MS-ASProtocolVersions): " + responseData["MS-ASProtocolVersions"]+"\n" +
1073 "responseHeader(MS-ASProtocolCommands): " + responseData["MS-ASProtocolCommands"]);
8111074 resolve();
8121075 };
8131076
8541117
8551118 if (result && result.hasOwnProperty("errorType") && result.errorType == "PasswordPrompt") {
8561119 if (allowedRetries > 0) {
857 let authData = eas.network.getAuthData(syncData.accountData);
858 let promptData = {
859 windowID: "auth:" + syncData.accountData.accountID,
860 accountname: syncData.accountData.getAccountProperty("accountname"),
861 usernameLocked: syncData.accountData.isConnected(),
862 username: authData.user
863 }
864
865 let syncState = syncData.getSyncState().state;
866 syncData.setSyncState("passwordprompt");
867 let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
868 if (credentials) {
869 // Update login data and try again.
870 authData.updateLoginData(credentials.username, credentials.password);
871 syncData.setSyncState(syncState);
872 retry = true;
1120 if (oauthData) {
1121 oauthData.accessToken = "";
1122 retry = true;
1123 } else {
1124 syncData.setSyncState("passwordprompt");
1125 let authData = eas.network.getAuthData(syncData.accountData);
1126 let promptData = {
1127 windowID: "auth:" + syncData.accountData.accountID,
1128 accountname: syncData.accountData.getAccountProperty("accountname"),
1129 usernameLocked: syncData.accountData.isConnected(),
1130 username: authData.user
1131 }
1132 let credentials = await TbSync.passwordManager.asyncPasswordPrompt(promptData, eas.openWindows);
1133 if (credentials) {
1134 authData.updateLoginData(credentials.username, credentials.password);
1135 retry = true;
1136 }
8731137 }
8741138 }
8751139
10341298 if (method == "POST") {
10351299 req.setRequestHeader("Content-Length", xml.length);
10361300 req.setRequestHeader("Content-Type", "text/xml");
1037 if (secure) req.setRequestHeader("Authorization", "Basic " + TbSync.tools.b64encode(connection.user + ":" + password));
1301 if (secure && password) {
1302 // OAUTH accounts cannot authenticate against the standard discovery services
1303 // updateServerConnectionViaAutodiscover() is not passing them on
1304 req.setRequestHeader("Authorization", "Basic " + TbSync.tools.b64encode(connection.user + ":" + password));
1305 }
10381306 }
10391307
10401308 req.ontimeout = function () {
11041372 else req.send(xml);
11051373
11061374 });
1107 }
1375 },
1376
1377 getServerConnectionViaAutodiscoverV2JsonRequest: function (url, maxtimeout) {
1378 TbSync.dump("Querry EAS autodiscover V2 URL", url);
1379
1380 return new Promise(function(resolve,reject) {
1381
1382 let userAgent = eas.prefs.getCharPref("clientID.useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2
1383
1384 // Create request handler - API changed with TB60 to new XMKHttpRequest()
1385 let req = new XMLHttpRequest();
1386 req.mozBackgroundRequest = true;
1387 req.open("GET", url, true);
1388 req.timeout = maxtimeout;
1389 req.setRequestHeader("User-Agent", userAgent);
1390
1391 req.ontimeout = function () {
1392 TbSync.dump("EAS autodiscover V2 with timeout", "\n" + url + " => \n" + req.responseURL);
1393 resolve({"url":req.responseURL, "error":"timeout", "server":""});
1394 };
1395
1396 req.onerror = function () {
1397 let error = TbSync.network.createTCPErrorFromFailedXHR(req);
1398 if (!error) error = req.responseText;
1399 TbSync.dump("EAS autodiscover V2 with error ("+error+")", "\n" + url + " => \n" + req.responseURL);
1400 resolve({"url":req.responseURL, "error":error, "server":""});
1401 };
1402
1403 req.onload = function() {
1404 if (req.status === 200) {
1405 let data = JSON.parse(req.responseText);
1406
1407 if (data && data.Url) {
1408 resolve({"url":req.responseURL, "error":"", "server": eas.network.stripAutodiscoverUrl(data.Url)});
1409 } else {
1410 resolve({"url":req.responseURL, "error":"invalid", "server":""});
1411 }
1412 return;
1413 }
1414
1415 resolve({"url":req.responseURL, "error":req.status, "server":""});
1416 };
1417
1418 req.send();
1419 });
1420 }
11081421 }
7474 if (syncData.accountData.getAccountProperty("asversion", "") == "" || (Date.now() - syncData.accountData.getAccountProperty("lastEasOptionsUpdate")) > 86400000 ) {
7575 await eas.network.getServerOptions(syncData);
7676 }
77
77
7878 //only update the actual used asversion, if we are currently not connected or it has not yet been set
7979 if (syncData.accountData.getAccountProperty("asversion", "") == "" || !syncData.accountData.isConnected()) {
8080 //eval the currently in the UI selected EAS version
3939 this.elementServertype = document.getElementById('tbsync.newaccount.servertype');
4040
4141 document.documentElement.getButton("back").hidden = true;
42 document.documentElement.getButton("finish").disabled = true;
43 document.documentElement.getButton("finish").label = TbSync.getString("newaccount.add_auto","eas");
42 this.onUserDropdown();
4443
4544 document.getElementById("tbsync.error").hidden = true;
4645 document.getElementById("tbsync.spinner").hidden = true;
4746
48 document.getElementById('tbsync.newaccount.url.box').style.visibility = (this.elementServertype.value != "custom") ? "hidden" : "visible";
49 document.getElementById("tbsync.newaccount.name").focus();
50
5147 document.addEventListener("wizardfinish", tbSyncEasNewAccount.onFinish.bind(this));
5248 document.addEventListener("wizardcancel", tbSyncEasNewAccount.onCancel.bind(this));
5349 },
5753
5854 onUserTextInput: function () {
5955 document.getElementById("tbsync.error").hidden = true;
60 if (this.elementServertype.value != "custom") {
61 document.documentElement.getButton("finish").disabled = (this.elementName.value.trim() == "" || this.elementUser.value == "" || this.elementPass.value == "");
62 } else {
63 document.documentElement.getButton("finish").disabled = (this.elementName.value.trim() == "" || this.elementUser.value == "" || this.elementPass.value == "" || this.elementUrl.value.trim() == "");
56 switch (this.elementServertype.value) {
57 case "select":
58 document.documentElement.getButton("finish").disabled = true;
59 break;
60
61 case "auto":
62 document.documentElement.getButton("finish").disabled = (this.elementName.value.trim() == "" || this.elementUser.value == "" || this.elementPass.value == "");
63 break;
64
65 case "office365":
66 document.documentElement.getButton("finish").disabled = (this.elementName.value.trim() == "" || this.elementUser.value == "");
67 break;
68
69 case "custom":
70 default:
71 document.documentElement.getButton("finish").disabled = (this.elementName.value.trim() == "" || this.elementUser.value == "" || this.elementPass.value == "" || this.elementUrl.value.trim() == "");
72 break;
6473 }
6574 },
6675
6776 onUserDropdown: function () {
68 document.documentElement.getButton("finish").label = TbSync.getString("newaccount.add_" + this.elementServertype.value,"eas");
69 document.getElementById('tbsync.newaccount.url.box').style.visibility = (this.elementServertype.value != "custom") ? "hidden" : "visible";
70 this.onUserTextInput();
77 if (this.elementServertype) {
78 switch (this.elementServertype.value) {
79 case "select":
80 document.getElementById('tbsync.newaccount.user.box').style.visibility = "hidden";
81 document.getElementById('tbsync.newaccount.url.box').style.visibility = "hidden";
82 document.getElementById('tbsync.newaccount.password.box').style.visibility = "hidden";
83 document.documentElement.getButton("finish").label = TbSync.getString("newaccount.add_custom","eas");
84 break;
85
86 case "auto":
87 document.getElementById('tbsync.newaccount.user.box').style.visibility = "visible";
88 document.getElementById('tbsync.newaccount.url.box').style.visibility = "hidden";
89 document.getElementById('tbsync.newaccount.password.box').style.visibility = "visible";
90 document.documentElement.getButton("finish").label = TbSync.getString("newaccount.add_auto","eas");
91 break;
92
93 case "office365":
94 document.getElementById('tbsync.newaccount.user.box').style.visibility = "visible";
95 document.getElementById('tbsync.newaccount.url.box').style.visibility = "hidden";
96 document.getElementById('tbsync.newaccount.password.box').style.visibility = "hidden";
97 document.documentElement.getButton("finish").label = TbSync.getString("newaccount.add_custom","eas");
98 break;
99
100 case "custom":
101 default:
102 document.getElementById('tbsync.newaccount.user.box').style.visibility = "visible";
103 document.getElementById('tbsync.newaccount.url.box').style.visibility = "visible";
104 document.getElementById('tbsync.newaccount.password.box').style.visibility = "visible";
105 document.documentElement.getButton("finish").label = TbSync.getString("newaccount.add_custom","eas");
106 break;
107 }
108 this.onUserTextInput();
109 //document.getElementById("tbsync.newaccount.name").focus();
110 }
71111 },
72112
73113 onFinish: function (event) {
80120
81121 validate: async function () {
82122 let user = this.elementUser.value;
83 let password = this.elementPass.value;
84123 let servertype = this.elementServertype.value;
85124 let accountname = this.elementName.value.trim();
86 let url = this.elementUrl.value.trim();
87
88 if (servertype == "auto" && user.split("@").length != 2) {
125
126 let url = (servertype == "custom") ?this.elementUrl.value.trim() : "";
127 let password = (servertype == "auto" || servertype == "custom") ? this.elementPass.value : "";
128
129 if ((servertype == "auto" || servertype == "office365") && user.split("@").length != 2) {
89130 alert(TbSync.getString("autodiscover.NeedEmail","eas"))
90131 return;
91132 }
95136
96137 //document.getElementById("tbsync.newaccount.wizard").canRewind = false;
97138 document.getElementById("tbsync.error").hidden = true;
98 document.getElementById("tbsync.spinner").hidden = false;
99139 document.documentElement.getButton("cancel").disabled = true;
100140 document.documentElement.getButton("finish").disabled = true;
101141 document.getElementById("tbsync.newaccount.name").disabled = true;
102142 document.getElementById("tbsync.newaccount.user").disabled = true;
103143 document.getElementById("tbsync.newaccount.password").disabled = true;
104144 document.getElementById("tbsync.newaccount.servertype").disabled = true;
145
146 tbSyncEasNewAccount.startTime = Date.now();
147 tbSyncEasNewAccount.updateAutodiscoverStatus();
148 document.getElementById("tbsync.spinner").hidden = false;
105149
106150 //do autodiscover
107 if (servertype == "auto") {
151 if (servertype == "office365" || servertype == "auto") {
108152 let updateTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
109153 updateTimer.initWithCallback({notify : function () {tbSyncEasNewAccount.updateAutodiscoverStatus()}}, 1000, 3);
110154
111 tbSyncEasNewAccount.startTime = Date.now();
112 tbSyncEasNewAccount.updateAutodiscoverStatus();
113
114 let result = await eas.network.getServerConnectionViaAutodiscover(user, password, tbSyncEasNewAccount.maxTimeout*1000);
155 if (servertype == "office365") {
156 let v2 = await eas.network.getServerConnectionViaAutodiscoverV2JsonRequest("https://autodiscover-s.outlook.com/autodiscover/autodiscover.json?Email="+encodeURIComponent(user)+"&Protocol=ActiveSync");
157 let oauthData = eas.network.getOAuthObj({ host: v2.server, user, accountname });
158 if (oauthData) {
159 // ask for token
160 document.getElementById("tbsync.spinner").hidden = true;
161 let _rv = {};
162 if (await oauthData.asyncConnect(_rv)) {
163 password = _rv.tokens;
164 } else {
165 error = TbSync.getString("status." + _rv.error, "eas");
166 }
167 document.getElementById("tbsync.spinner").hidden = false;
168 url=v2.server;
169 } else {
170 error = TbSync.getString("status.404", "eas");
171 }
172 } else {
173 let result = await eas.network.getServerConnectionViaAutodiscover(user, password, tbSyncEasNewAccount.maxTimeout*1000);
174 if (result.server) {
175 user = result.user;
176 url = result.server;
177 } else {
178 error = result.error; // is a localized string
179 }
180 }
181
115182 updateTimer.cancel();
116
117 if (result.server) {
118 user = result.user;
119 url = result.server;
120 } else {
121 error = result.error;
122 }
123183 }
124184
125185 //now validate the information
126186 if (!error) {
187 if (!password) error = TbSync.getString("status.401", "eas");
127188 }
128189
129190 //add if valid
153214 },
154215
155216 updateAutodiscoverStatus: function () {
156 let offset = Math.round(((Date.now()-tbSyncEasNewAccount.startTime)/1000));
217 let offset = Math.round(((Date.now() - tbSyncEasNewAccount.startTime)/1000));
157218 let timeout = (offset>2) ? " (" + (tbSyncEasNewAccount.maxTimeout - offset) + ")" : "";
158219
159220 document.getElementById('tbsync.newaccount.autodiscoverstatus').value = TbSync.getString("autodiscover.Querying","eas") + timeout;
77 ]>
88
99 <wizard
10 width="600"
11 height="500"
10 width="500"
11 height="600"
1212 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
1313 title="&add.title;"
1414 id="tbsync.newaccount.wizard"
1919 <script type="application/javascript" src="chrome://eas4tbsync/content/manager/createAccount.js"/>
2020
2121 <wizardpage onFirstPage="true" label="&add.shortdescription;">
22 <description style="width: 450px">&add.description;</description>
22 <description style="width: 350px">&add.description;</description>
2323
24 <grid style="margin-top:1ex">
24 <richlistbox height="250" id="tbsync.newaccount.servertype" seltype="single" style="margin-top:1ex" onselect="tbSyncEasNewAccount.onUserDropdown();">
25 <richlistitem value="auto" style="padding: 4px">
26 <vbox><image src="chrome://eas4tbsync/skin/eas32.png" style="margin:1ex" /></vbox>
27 <vbox flex="1">
28 <label class="header" value="&servertype.auto;" />
29 <description>&servertype.description.auto;</description>
30 </vbox>
31 </richlistitem>
32
33 <richlistitem value="custom" style="padding: 4px">
34 <vbox><image src="chrome://eas4tbsync/skin/eas32.png" style="margin:1ex" /></vbox>
35 <vbox flex="1">
36 <label class="header" value="&servertype.custom;" />
37 <description>&servertype.description.custom;</description>
38 </vbox>
39 </richlistitem>
40
41 <richlistitem value="office365" style="padding: 4px">
42 <vbox><image src="chrome://eas4tbsync/skin/365_32.png" style="margin:1ex" /></vbox>
43 <vbox flex="1">
44 <label class="header" value="&servertype.office365;" />
45 <description>&servertype.description.office365;</description>
46 </vbox>
47 </richlistitem>
48 </richlistbox>
49
50 <grid style="margin-top:1em">
2551 <columns>
2652 <column flex="1" />
2753 <column flex="2" />
2854 </columns>
2955 <rows>
30 <row style="margin-bottom:1ex;">
31 <vbox pack="center"><label value="&add.server;" /></vbox>
32 <menulist id="tbsync.newaccount.servertype" oncommand="tbSyncEasNewAccount.onUserDropdown();">
33 <menupopup>
34 <menuitem label="&servertype.auto;" value="auto" />
35 <menuitem label="&servertype.custom;" value="custom" />
36 <!--menuseparator/>
37 <menuitem label="&servertype.outlook.com;" value="outlook.com" /-->
38 </menupopup>
39 </menulist>
40 </row>
41
4256 <row>
4357 <vbox pack="center"><label value="&add.name;" /></vbox>
4458 <textbox id="tbsync.newaccount.name" oninput="tbSyncEasNewAccount.onUserTextInput();"/>
4559 </row>
46 <row>
60 <row id="tbsync.newaccount.user.box">
4761 <vbox pack="center"><label value="&add.user;" /></vbox>
4862 <textbox id="tbsync.newaccount.user" oninput="tbSyncEasNewAccount.onUserTextInput();"/>
4963 </row>
50 <row>
64 <row id="tbsync.newaccount.password.box">
5165 <vbox pack="center"><label value="&add.password;" /></vbox>
5266 <textbox id="tbsync.newaccount.password" type="password" oninput="tbSyncEasNewAccount.onUserTextInput();"/>
5367 </row>
2424 }
2525
2626 //returning false will prevent injection
27 return (MailServices.ab.getDirectory(aParentDirURI).getStringValue("tbSyncProvider", "") == "eas");
27 return (TbSync.addressbook.getStringValue(MailServices.ab.getDirectory(aParentDirURI), "tbSyncProvider", "") == "eas");
2828 },
2929
3030
3737
3838 //function to get correct uri of current card for global book as well for mailLists
3939 let abUri = TbSync.providers.eas.tools.getSelectedUri(window.GetSelectedDirectory(), aCard);
40 let show = (MailServices.ab.getDirectory(abUri).getStringValue("tbSyncProvider", "") == "eas");
40 let show = (TbSync.addressbook.getStringValue(MailServices.ab.getDirectory(abUri), "tbSyncProvider", "") == "eas");
4141
4242 let email3Box = window.document.getElementById("cvEmail3Box");
4343 if (email3Box) {
139139 // Close all open windows of this provider.
140140 for (let id in eas.openWindows) {
141141 if (eas.openWindows.hasOwnProperty(id)) {
142 eas.openWindows[id].close();
142 try {
143 eas.openWindows[id].close();
144 } catch(e) {
145 //NOOP
146 }
143147 }
144148 }
145149 }
163167 * Returns location of a provider icon.
164168 */
165169 static getProviderIcon(size, accountData = null) {
170 let base = (accountData && accountData.getAccountProperty("servertype") == "office365") ? "365_" : "eas";
171
166172 switch (size) {
167173 case 16:
168 return "chrome://eas4tbsync/skin/eas16.png";
174 return "chrome://eas4tbsync/skin/" + base + "16.png";
169175 case 32:
170 return "chrome://eas4tbsync/skin/eas32.png";
176 return "chrome://eas4tbsync/skin/" + base + "32.png";
171177 default :
172 return "chrome://eas4tbsync/skin/eas64.png";
178 return "chrome://eas4tbsync/skin/" + base + "64.png";
173179 }
174180 }
175181
306312
307313
308314 /**
315 * Is called everytime an account of this provider is deleted in the
316 * manager UI.
317 */
318 static onDeleteAccount(accountData) {
319 eas.network.getAuthData(accountData).removeLoginData();
320 }
321
322
323 /**
309324 * Implement this method, if this provider should add additional entries
310325 * to the autocomplete list while typing something into the address field
311326 * of the message composer.
545560 eas.sync.resetFolderSyncInfo(this.folderData);
546561
547562 if (directory && directory instanceof Components.interfaces.nsIAbDirectory && directory.dirPrefId == dirPrefId) {
548 directory.setStringValue("tbSyncIcon", "eas");
563 directory.setStringValue("tbSyncIcon", "eas" + (this.folderData.accountData.getAccountProperty("servertype") == "office365" ? "_365" : ""));
549564 return directory;
550565 }
551566 return null;
77 },
88 "manifest_version": 2,
99 "name": "__MSG_extensionName__",
10 "version": "1.6",
10 "version": "1.8",
1111 "author": "John Bieling",
1212 "homepage_url": "https://github.com/jobisoft/EAS-4-TbSync/",
1313 "default_locale": "en-US",
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 53.59 64.356" width="53.589577" height="64.356148">
2 <g transform="translate(-216.07358,-549.28882)">
3 <g transform="matrix(1.8232952,0,0,1.8232952,-597.71681,-124.12247)">
4 <g transform="translate(0,-91.137241)">
5 <g fill="#eb3c00" transform="matrix(0.74069815,0,0,0.74069815,98.5698,-8.2505871)">
6 <path d="m469.87,671.03,0-28.52,25.229-9.3238,13.711,4.3877,0,38.392-13.711,4.133-25.229-9.0691,25.229,3.0361,0-33.201-16.454,3.8392,0,22.487z"/>
7 </g>
8 </g>
9 </g>
10 </g>
11 </svg>
Binary diff not shown
Binary diff not shown
Binary diff not shown
33 margin-inline-end: 2px;
44 list-style-image: url("chrome://eas4tbsync/skin/eas16.png");
55 }
6 treechildren::-moz-tree-image(DirCol, eas_365) {
7 margin-inline-end: 2px;
8 list-style-image: url("chrome://eas4tbsync/skin/365_16.png");
9 }
610
711 .abMenuItem[AddrBook="true"][TbSyncIcon="eas"] {
812 list-style-image: url("chrome://eas4tbsync/skin/eas16.png");
13 }
14 .abMenuItem[AddrBook="true"][TbSyncIcon="eas_365"] {
15 list-style-image: url("chrome://eas4tbsync/skin/365_16.png");
916 }
1017
1118 /* autocomplete styles */
Binary diff not shown