New upstream version 1.8
Mechtilde Stehmann
4 years ago
4 | 4 | More information can be found in the [wiki](https://github.com/jobisoft/EAS-4-TbSync/wiki/About:-Provider-for-Exchange-ActiveSync) of this repository |
5 | 5 | |
6 | 6 | ## 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. | |
8 | 8 | |
9 | 9 | |
10 | 10 | ## External data sources |
14 | 14 | |
15 | 15 | ## Icon sources and attributions |
16 | 16 | |
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 | ||
17 | 20 | #### 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/) |
7 | 7 | <!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)."> |
8 | 8 | |
9 | 9 | <!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. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "best available"> |
13 | 13 | |
40 | 40 | <!ENTITY pref.seperator.description "Separator for multiline address field."> |
41 | 41 | <!ENTITY pref.displayoverride "Override Display Name with “First Name” + “Second Name”"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Double click to unlock all predefined server settings."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Middle name:"> |
7 | 7 | <!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)."> |
8 | 8 | |
9 | 9 | <!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. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "bestmöglich"> |
13 | 13 | |
40 | 40 | <!ENTITY pref.seperator.description "Separator für mehrzeilige Adressfelder: "> |
41 | 41 | <!ENTITY pref.displayoverride "Überschreibe Anzeigename mit 'Vorname' + 'Nachname'"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Doppelklick um die vordefinierten Servereinstellungen zu entsperren."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Zweiter Vorname:"> |
7 | 7 | <!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)."> |
8 | 8 | |
9 | 9 | <!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. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "best available"> |
13 | 13 | |
40 | 40 | <!ENTITY pref.seperator.description "Separator for multiline address field."> |
41 | 41 | <!ENTITY pref.displayoverride "Override Display Name with “First Name” + “Second Name”"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Double click to unlock all predefined server settings."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Middle name:"> |
7 | 7 | <!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)."> |
8 | 8 | |
9 | 9 | <!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. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "best available"> |
13 | 13 | |
40 | 40 | <!ENTITY pref.seperator.description "Separator for multiline address field."> |
41 | 41 | <!ENTITY pref.displayoverride "Override Display Name with “First Name” + “Second Name”"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Double click to unlock all predefined server settings."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Middle name:"> |
7 | 7 | <!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)."> |
8 | 8 | |
9 | 9 | <!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. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "az elérhető legjobb"> |
13 | 13 | |
40 | 40 | <!ENTITY pref.seperator.description "Több címsort tartalmazó elválasztó."> |
41 | 41 | <!ENTITY pref.displayoverride "A megjelenítési név felülbírálása „keresztnév” és „második keresztnév”"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Dupla kattintson az összes előre meghatározott kiszolgáló beállításának feloldásához."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Második keresztnév:"> |
7 | 7 | <!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)."> |
8 | 8 | |
9 | 9 | <!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. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "miglior opzione disponibile"> |
13 | 13 | |
40 | 40 | <!ENTITY pref.seperator.description "Separatore per i campi indirizzo multiriga."> |
41 | 41 | <!ENTITY pref.displayoverride "Ignora il Nome visualizzato e mostra Nome + Cognome"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Cliccare due volte per sbloccare tutte le impostazioni server predefinite."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Secondo nome:"> |
4 | 4 | <!ENTITY add.user "Nazwa użytkownika (adres e-mail):"> |
5 | 5 | <!ENTITY add.password "Hasło:"> |
6 | 6 | <!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)."> | |
8 | 8 | |
9 | 9 | <!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. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "najlepsza dostępna"> |
13 | 13 | |
16 | 16 | <!ENTITY pref.UserNameDescription "Nazwa użytkownika to zazwyczaj adres e-mail Twojego konta."> |
17 | 17 | <!ENTITY pref.ServerName "Adres serwera"> |
18 | 18 | <!ENTITY pref.ActiveSyncVersion "Wersja ActiveSync"> |
19 | <!ENTITY pref.ServerNameDescription "np. mail.yourserver.com"> | |
19 | <!ENTITY pref.ServerNameDescription "np. mail.twojserwer.com"> | |
20 | 20 | <!ENTITY pref.usehttps "Użyj bezpiecznego połączenia (połącz przez https)"> |
21 | 21 | <!ENTITY pref.DeviceId "ID urządzenia ActiveSync"> |
22 | 22 | <!ENTITY pref.birthday "Wyślij informacje o urodzinach"> |
29 | 29 | <!ENTITY pref.ShowTrashedFolders "Pokaż foldery znalezione w koszu"> |
30 | 30 | |
31 | 31 | <!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"> | |
36 | 36 | <!ENTITY pref.synclimit.description "Okres synchronizacji: "> |
37 | 37 | |
38 | 38 | <!ENTITY pref.seperator.comma "Przecinek"> |
40 | 40 | <!ENTITY pref.seperator.description "Separator pola adresu wielowierszowego."> |
41 | 41 | <!ENTITY pref.displayoverride "Zastąp nazwę wyświetlaną ciągiem “Imię“ + “Drugie imię”"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Kliknij dwukrotnie, aby odblokować wszystkie predefiniowane ustawienia serwera."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Drugie imię:"> |
53 | 59 | <!ENTITY abCard.ManagerName "Menedżer:"> |
54 | 60 | <!ENTITY abCard.AssistantPhoneNumber "Telefon Asystenta:"> |
55 | 61 | <!ENTITY abCard.AssistantName "Asystent:"> |
56 | <!ENTITY abCard.Spouse "Współmałżonek:"> | |
62 | <!ENTITY abCard.Spouse "Małżonek:"> | |
57 | 63 | <!ENTITY abCard.Anniversary "Rocznica:"> |
58 | 64 | |
59 | 65 | <!ENTITY abCard.CarPhoneNumber "Telefon w Aucie:"> |
28 | 28 | syncstate.send.request.options=Oczekiwanie na opcje serwera |
29 | 29 | syncstate.eval.response.options=Przetwarzanie opcji serwera |
30 | 30 | |
31 | syncstate.prepare.request.folders=Wysyłanie aktualizacja listy folderów | |
31 | syncstate.prepare.request.folders=Wysyłanie aktualizacji listy folderów | |
32 | 32 | syncstate.send.request.folders=Oczekiwanie na aktualizację listy folderów |
33 | 33 | syncstate.eval.response.folders=Przetwarzanie aktualizacji listy folderów |
34 | 34 | |
38 | 38 | |
39 | 39 | syncstate.prepare.request.deletefolder=Przygotowanie do usunięcia folderu |
40 | 40 | 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 | |
42 | 42 | |
43 | 43 | syncstate.prepare.request.provision=Żądanie provision |
44 | 44 | syncstate.send.request.provision=Oczekiwanie na provision |
68 | 68 | status.disabled=Wyłączony |
69 | 69 | status.notargets=Przerywam synchronizację, ponieważ nie można utworzyć celów synchronizacji. |
70 | 70 | 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. | |
72 | 72 | status.networkerror=Nie można połączyć z serwerem. |
73 | 73 | status.404=Użytkownik nie znaleziony (błąd HTTP 404). |
74 | 74 | status.403=Serwer odrzucił połączenie (zabronione) (błąd HTTP 403). |
92 | 92 | helplink.security=https://github.com/jobisoft/TbSync/wiki/How-to-use-TbSync-with-self-signed-or-otherwise-untrusted-certificates%3F |
93 | 93 | status.network=Nie można połączyć z serwerem (##replace.1##). |
94 | 94 | |
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 | |
97 | 97 | status.aborted=Nie zsynchronizowane |
98 | 98 | status.pending=Oczekiwanie na synchronizację |
99 | 99 | status.modified=Zmiany lokalne |
7 | 7 | <!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)."> |
8 | 8 | |
9 | 9 | <!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."> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "melhor disponível"> |
13 | 13 | |
18 | 18 | <!ENTITY pref.ActiveSyncVersion "Versão do ActiveSync"> |
19 | 19 | <!ENTITY pref.ServerNameDescription "ex. mail.yourserver.com"> |
20 | 20 | <!ENTITY pref.usehttps "Utilizar conexão segura (conectar via https)"> |
21 | <!ENTITY pref.DeviceId "ActiveSync device ID"> | |
21 | <!ENTITY pref.DeviceId "ID Dispositivo ActiveSync"> | |
22 | 22 | <!ENTITY pref.birthday "Enviar informações de aniversário"> |
23 | 23 | <!ENTITY pref.generaloptions "Opções gerais"> |
24 | 24 | <!ENTITY pref.contactoptions "Opções de contato"> |
40 | 40 | <!ENTITY pref.seperator.description "Separador para campo de endereço de múltiplas linhas."> |
41 | 41 | <!ENTITY pref.displayoverride "Substituir nome de exibição com Nome + Segundo nome"> |
42 | 42 | |
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 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Clique duas vezes para desbloquear todas as configurações do servidor predefinidas."> |
46 | 52 | |
47 | 53 | <!ENTITY abCard.MiddleName "Nome do meio:"> |
143 | 143 | acl.readwrite=Ler e escrever no servidor |
144 | 144 | acl.readonly=Acesso ao servidor somente leitura (reverter alterações locais) |
145 | 145 | |
146 | autocomplete.serverdirectory = global server directory | |
146 | autocomplete.serverdirectory = diretório global do servidor |
7 | 7 | <!ENTITY add.urldescription "Достаточно указать только базовый адрес сервера (например: mail.yourserver.com). Однако также можно указать полный URL-адрес (например: https://mail.yourserver.com/Microsoft-Server-ActiveSync)."> |
8 | 8 | |
9 | 9 | <!ENTITY add.server "Конфигурация сервера:"> |
10 | <!ENTITY add.description "Введите имя для нового аккаунта TbSync, имя пользователя аккаунта ActiveSync которое вы хотите добавить (возможно, адрес электронной почты), и выберите один из доступных параметров конфигурации сервера. "> | |
10 | <!ENTITY add.description "Пожалуйста, выберите один из доступных параметров конфигурации сервера и введите требуемую информацию. "> | |
11 | 11 | |
12 | 12 | <!ENTITY pref.autodetect "лучшее из доступных"> |
13 | 13 | |
40 | 40 | <!ENTITY pref.seperator.description "Сепаратор для многострочного поля адреса."> |
41 | 41 | <!ENTITY pref.displayoverride "Переопределить отображаемое имя с помощью “Имя” + “Фамилия”"> |
42 | 42 | |
43 | <!ENTITY servertype.auto "Автоматическая настройка с помощью ActiveSync автоопределения"> | |
43 | <!ENTITY servertype.auto "Автоматическая настройка"> | |
44 | <!ENTITY servertype.description.auto "Для определения конфигурации множества серверов ActiveSync достаточно указать только адрес Вашей электронной почты."> | |
45 | ||
44 | 46 | <!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 | ||
45 | 51 | <!ENTITY servertype.unlock "Дважды щелкните, чтобы разблокировать все предопределенные настройки сервера."> |
46 | 52 | |
47 | 53 | <!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 | }; |
157 | 157 | |
158 | 158 | eas.sync.setItemRecurrence(item, syncdata, data); |
159 | 159 | |
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 | ||
160 | 171 | if (data.MeetingStatus) { |
161 | 172 | //store original EAS value |
162 | 173 | item.setProperty("X-EAS-MeetingStatus", data.MeetingStatus); |
164 | 175 | let M = data.MeetingStatus & 0x1; |
165 | 176 | let R = data.MeetingStatus & 0x2; |
166 | 177 | 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 | } | |
173 | 184 | |
174 | 185 | //we can also use the R information, to update our fallbackOrganizerName |
175 | 186 | if (!R && data.OrganizerName) syncdata.target.calendar.setProperty("fallbackOrganizerName", data.OrganizerName); |
176 | 187 | } |
177 | 188 | |
189 | if (tbStatus) item.setProperty("STATUS", tbStatus) | |
190 | else item.deleteProperty("STATUS"); | |
191 | ||
178 | 192 | //TODO: attachements (needs EAS 16.0!) |
179 | ||
180 | 193 | }, |
181 | 194 | |
182 | 195 | |
247 | 260 | //Body |
248 | 261 | wbxml.append(eas.sync.getItemBody(item, syncdata)); |
249 | 262 | |
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 | ||
253 | 271 | //Organizer |
254 | 272 | if (!isException) { |
255 | 273 | if (item.organizer && item.organizer.commonName) wbxml.atag("OrganizerName", item.organizer.commonName); |
7 | 7 | |
8 | 8 | "use strict"; |
9 | 9 | |
10 | var { OAuth2_1 } = ChromeUtils.import("chrome://eas4tbsync/content/OAuth2_1.jsm"); | |
11 | ||
10 | 12 | var network = { |
11 | 13 | |
12 | 14 | getEasURL: function(accountData) { |
42 | 44 | // Also update the username of this account. Add dedicated username setter? |
43 | 45 | accountData.setAccountProperty("user", newUsername); |
44 | 46 | }, |
47 | ||
48 | removeLoginData: function() { | |
49 | TbSync.passwordManager.removeLoginInfos(this.host, "TbSync/EAS"); | |
50 | } | |
45 | 51 | }; |
46 | 52 | return authData; |
47 | 53 | }, |
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 | }, | |
57 | 236 | |
58 | 237 | sendRequest: async function (wbxml, command, syncData, allowSoftFail = false) { |
59 | 238 | let ALLOWED_RETRIES = { |
62 | 241 | } |
63 | 242 | |
64 | 243 | let rv = {}; |
244 | let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData }); | |
245 | let syncState = syncData.getSyncState().state; | |
246 | ||
65 | 247 | for (;;) { |
66 | 248 | |
67 | 249 | if (rv.errorType) { |
70 | 252 | if (ALLOWED_RETRIES[rv.errorType] > 0) { |
71 | 253 | ALLOWED_RETRIES[rv.errorType]--; |
72 | 254 | |
255 | ||
73 | 256 | switch (rv.errorType) { |
74 | 257 | |
75 | 258 | case "PasswordPrompt": |
76 | 259 | { |
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 | } | |
84 | 260 | |
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 | } | |
93 | 278 | } |
94 | 279 | } |
95 | 280 | break; |
96 | 281 | |
97 | 282 | case "NetworkError": |
98 | 283 | { |
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) { | |
101 | 287 | let errorcode = await eas.network.updateServerConnectionViaAutodiscover(syncData); |
102 | 288 | console.log("ERR: " + errorcode); |
103 | 289 | if (errorcode == 200) { |
120 | 306 | if (!retry) throw rv.errorObj; |
121 | 307 | } |
122 | 308 | |
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 | } | |
123 | 322 | rv = await this.sendRequestPromise(wbxml, command, syncData, allowSoftFail); |
124 | 323 | |
125 | 324 | if (rv.errorType) { |
160 | 359 | syncData.req.overrideMimeType("text/plain"); |
161 | 360 | syncData.req.setRequestHeader("User-Agent", userAgent); |
162 | 361 | 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 | ||
164 | 370 | if (syncData.accountData.getAccountProperty("asversion") == "2.5") { |
165 | 371 | syncData.req.setRequestHeader("MS-ASProtocolVersion", "2.5"); |
166 | 372 | } else { |
702 | 908 | let command = "Search"; |
703 | 909 | |
704 | 910 | let authData = eas.network.getAuthData(accountData); |
911 | let oauthData = eas.network.getOAuthObj({ accountData }); | |
705 | 912 | let userAgent = accountData.getAccountProperty("useragent"); //plus calendar.useragent.extra = Lightning/5.4.5.2 |
706 | 913 | let deviceType = accountData.getAccountProperty("devicetype"); |
707 | 914 | let deviceId = accountData.getAccountProperty("deviceId"); |
708 | 915 | |
709 | 916 | TbSync.dump("Sending (EAS v" + accountData.getAccountProperty("asversion") +")", "POST " + eas.network.getEasURL(accountData) + '?Cmd=' + command + '&User=' + encodeURIComponent(authData.user) + '&DeviceType=' +deviceType + '&DeviceId=' + deviceId, true); |
710 | 917 | |
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"); | |
744 | 936 | |
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 | } | |
761 | 943 | } |
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 | } | |
771 | 1009 | } |
772 | 1010 | }, |
773 | 1011 | |
791 | 1029 | |
792 | 1030 | let allowedRetries = 5; |
793 | 1031 | let retry; |
1032 | let oauthData = eas.network.getOAuthObj({ accountData: syncData.accountData }); | |
1033 | ||
794 | 1034 | do { |
795 | 1035 | 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 | } | |
796 | 1045 | |
797 | 1046 | let result = await new Promise(function(resolve,reject) { |
798 | 1047 | syncData.req = new XMLHttpRequest(); |
800 | 1049 | syncData.req.open("OPTIONS", eas.network.getEasURL(syncData.accountData), true); |
801 | 1050 | syncData.req.overrideMimeType("text/plain"); |
802 | 1051 | 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 | } | |
804 | 1059 | syncData.req.timeout = eas.Base.getConnectionTimeout(); |
805 | 1060 | |
806 | 1061 | syncData.req.ontimeout = function () { |
808 | 1063 | }; |
809 | 1064 | |
810 | 1065 | 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"]); | |
811 | 1074 | resolve(); |
812 | 1075 | }; |
813 | 1076 | |
854 | 1117 | |
855 | 1118 | if (result && result.hasOwnProperty("errorType") && result.errorType == "PasswordPrompt") { |
856 | 1119 | 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 | } | |
873 | 1137 | } |
874 | 1138 | } |
875 | 1139 | |
1034 | 1298 | if (method == "POST") { |
1035 | 1299 | req.setRequestHeader("Content-Length", xml.length); |
1036 | 1300 | 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 | } | |
1038 | 1306 | } |
1039 | 1307 | |
1040 | 1308 | req.ontimeout = function () { |
1104 | 1372 | else req.send(xml); |
1105 | 1373 | |
1106 | 1374 | }); |
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 | } | |
1108 | 1421 | } |
74 | 74 | if (syncData.accountData.getAccountProperty("asversion", "") == "" || (Date.now() - syncData.accountData.getAccountProperty("lastEasOptionsUpdate")) > 86400000 ) { |
75 | 75 | await eas.network.getServerOptions(syncData); |
76 | 76 | } |
77 | ||
77 | ||
78 | 78 | //only update the actual used asversion, if we are currently not connected or it has not yet been set |
79 | 79 | if (syncData.accountData.getAccountProperty("asversion", "") == "" || !syncData.accountData.isConnected()) { |
80 | 80 | //eval the currently in the UI selected EAS version |
39 | 39 | this.elementServertype = document.getElementById('tbsync.newaccount.servertype'); |
40 | 40 | |
41 | 41 | 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(); | |
44 | 43 | |
45 | 44 | document.getElementById("tbsync.error").hidden = true; |
46 | 45 | document.getElementById("tbsync.spinner").hidden = true; |
47 | 46 | |
48 | document.getElementById('tbsync.newaccount.url.box').style.visibility = (this.elementServertype.value != "custom") ? "hidden" : "visible"; | |
49 | document.getElementById("tbsync.newaccount.name").focus(); | |
50 | ||
51 | 47 | document.addEventListener("wizardfinish", tbSyncEasNewAccount.onFinish.bind(this)); |
52 | 48 | document.addEventListener("wizardcancel", tbSyncEasNewAccount.onCancel.bind(this)); |
53 | 49 | }, |
57 | 53 | |
58 | 54 | onUserTextInput: function () { |
59 | 55 | 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; | |
64 | 73 | } |
65 | 74 | }, |
66 | 75 | |
67 | 76 | 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 | } | |
71 | 111 | }, |
72 | 112 | |
73 | 113 | onFinish: function (event) { |
80 | 120 | |
81 | 121 | validate: async function () { |
82 | 122 | let user = this.elementUser.value; |
83 | let password = this.elementPass.value; | |
84 | 123 | let servertype = this.elementServertype.value; |
85 | 124 | 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) { | |
89 | 130 | alert(TbSync.getString("autodiscover.NeedEmail","eas")) |
90 | 131 | return; |
91 | 132 | } |
95 | 136 | |
96 | 137 | //document.getElementById("tbsync.newaccount.wizard").canRewind = false; |
97 | 138 | document.getElementById("tbsync.error").hidden = true; |
98 | document.getElementById("tbsync.spinner").hidden = false; | |
99 | 139 | document.documentElement.getButton("cancel").disabled = true; |
100 | 140 | document.documentElement.getButton("finish").disabled = true; |
101 | 141 | document.getElementById("tbsync.newaccount.name").disabled = true; |
102 | 142 | document.getElementById("tbsync.newaccount.user").disabled = true; |
103 | 143 | document.getElementById("tbsync.newaccount.password").disabled = true; |
104 | 144 | document.getElementById("tbsync.newaccount.servertype").disabled = true; |
145 | ||
146 | tbSyncEasNewAccount.startTime = Date.now(); | |
147 | tbSyncEasNewAccount.updateAutodiscoverStatus(); | |
148 | document.getElementById("tbsync.spinner").hidden = false; | |
105 | 149 | |
106 | 150 | //do autodiscover |
107 | if (servertype == "auto") { | |
151 | if (servertype == "office365" || servertype == "auto") { | |
108 | 152 | let updateTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); |
109 | 153 | updateTimer.initWithCallback({notify : function () {tbSyncEasNewAccount.updateAutodiscoverStatus()}}, 1000, 3); |
110 | 154 | |
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 | ||
115 | 182 | updateTimer.cancel(); |
116 | ||
117 | if (result.server) { | |
118 | user = result.user; | |
119 | url = result.server; | |
120 | } else { | |
121 | error = result.error; | |
122 | } | |
123 | 183 | } |
124 | 184 | |
125 | 185 | //now validate the information |
126 | 186 | if (!error) { |
187 | if (!password) error = TbSync.getString("status.401", "eas"); | |
127 | 188 | } |
128 | 189 | |
129 | 190 | //add if valid |
153 | 214 | }, |
154 | 215 | |
155 | 216 | updateAutodiscoverStatus: function () { |
156 | let offset = Math.round(((Date.now()-tbSyncEasNewAccount.startTime)/1000)); | |
217 | let offset = Math.round(((Date.now() - tbSyncEasNewAccount.startTime)/1000)); | |
157 | 218 | let timeout = (offset>2) ? " (" + (tbSyncEasNewAccount.maxTimeout - offset) + ")" : ""; |
158 | 219 | |
159 | 220 | document.getElementById('tbsync.newaccount.autodiscoverstatus').value = TbSync.getString("autodiscover.Querying","eas") + timeout; |
7 | 7 | ]> |
8 | 8 | |
9 | 9 | <wizard |
10 | width="600" | |
11 | height="500" | |
10 | width="500" | |
11 | height="600" | |
12 | 12 | xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
13 | 13 | title="&add.title;" |
14 | 14 | id="tbsync.newaccount.wizard" |
19 | 19 | <script type="application/javascript" src="chrome://eas4tbsync/content/manager/createAccount.js"/> |
20 | 20 | |
21 | 21 | <wizardpage onFirstPage="true" label="&add.shortdescription;"> |
22 | <description style="width: 450px">&add.description;</description> | |
22 | <description style="width: 350px">&add.description;</description> | |
23 | 23 | |
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"> | |
25 | 51 | <columns> |
26 | 52 | <column flex="1" /> |
27 | 53 | <column flex="2" /> |
28 | 54 | </columns> |
29 | 55 | <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 | ||
42 | 56 | <row> |
43 | 57 | <vbox pack="center"><label value="&add.name;" /></vbox> |
44 | 58 | <textbox id="tbsync.newaccount.name" oninput="tbSyncEasNewAccount.onUserTextInput();"/> |
45 | 59 | </row> |
46 | <row> | |
60 | <row id="tbsync.newaccount.user.box"> | |
47 | 61 | <vbox pack="center"><label value="&add.user;" /></vbox> |
48 | 62 | <textbox id="tbsync.newaccount.user" oninput="tbSyncEasNewAccount.onUserTextInput();"/> |
49 | 63 | </row> |
50 | <row> | |
64 | <row id="tbsync.newaccount.password.box"> | |
51 | 65 | <vbox pack="center"><label value="&add.password;" /></vbox> |
52 | 66 | <textbox id="tbsync.newaccount.password" type="password" oninput="tbSyncEasNewAccount.onUserTextInput();"/> |
53 | 67 | </row> |
24 | 24 | } |
25 | 25 | |
26 | 26 | //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"); | |
28 | 28 | }, |
29 | 29 | |
30 | 30 |
37 | 37 | |
38 | 38 | //function to get correct uri of current card for global book as well for mailLists |
39 | 39 | 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"); | |
41 | 41 | |
42 | 42 | let email3Box = window.document.getElementById("cvEmail3Box"); |
43 | 43 | if (email3Box) { |
139 | 139 | // Close all open windows of this provider. |
140 | 140 | for (let id in eas.openWindows) { |
141 | 141 | if (eas.openWindows.hasOwnProperty(id)) { |
142 | eas.openWindows[id].close(); | |
142 | try { | |
143 | eas.openWindows[id].close(); | |
144 | } catch(e) { | |
145 | //NOOP | |
146 | } | |
143 | 147 | } |
144 | 148 | } |
145 | 149 | } |
163 | 167 | * Returns location of a provider icon. |
164 | 168 | */ |
165 | 169 | static getProviderIcon(size, accountData = null) { |
170 | let base = (accountData && accountData.getAccountProperty("servertype") == "office365") ? "365_" : "eas"; | |
171 | ||
166 | 172 | switch (size) { |
167 | 173 | case 16: |
168 | return "chrome://eas4tbsync/skin/eas16.png"; | |
174 | return "chrome://eas4tbsync/skin/" + base + "16.png"; | |
169 | 175 | case 32: |
170 | return "chrome://eas4tbsync/skin/eas32.png"; | |
176 | return "chrome://eas4tbsync/skin/" + base + "32.png"; | |
171 | 177 | default : |
172 | return "chrome://eas4tbsync/skin/eas64.png"; | |
178 | return "chrome://eas4tbsync/skin/" + base + "64.png"; | |
173 | 179 | } |
174 | 180 | } |
175 | 181 | |
306 | 312 | |
307 | 313 | |
308 | 314 | /** |
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 | /** | |
309 | 324 | * Implement this method, if this provider should add additional entries |
310 | 325 | * to the autocomplete list while typing something into the address field |
311 | 326 | * of the message composer. |
545 | 560 | eas.sync.resetFolderSyncInfo(this.folderData); |
546 | 561 | |
547 | 562 | 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" : "")); | |
549 | 564 | return directory; |
550 | 565 | } |
551 | 566 | return null; |
7 | 7 | }, |
8 | 8 | "manifest_version": 2, |
9 | 9 | "name": "__MSG_extensionName__", |
10 | "version": "1.6", | |
10 | "version": "1.8", | |
11 | 11 | "author": "John Bieling", |
12 | 12 | "homepage_url": "https://github.com/jobisoft/EAS-4-TbSync/", |
13 | 13 | "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
3 | 3 | margin-inline-end: 2px; |
4 | 4 | list-style-image: url("chrome://eas4tbsync/skin/eas16.png"); |
5 | 5 | } |
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 | } | |
6 | 10 | |
7 | 11 | .abMenuItem[AddrBook="true"][TbSyncIcon="eas"] { |
8 | 12 | 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"); | |
9 | 16 | } |
10 | 17 | |
11 | 18 | /* autocomplete styles */ |
Binary diff not shown