mirror of
https://github.com/moparisthebest/k-9
synced 2025-02-17 07:30:16 -05:00
Merge branch 'pr/453'
Authentication changes message to explain why this merge is necessary,
This commit is contained in:
commit
ff5edf43d4
@ -389,10 +389,6 @@ Si us plau, envia\'ns els errors, contribueix a millorar-lo a
|
||||
<string name="account_setup_incoming_security_label">Tipus de seguretat</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Tipus d\'autenticació</string>
|
||||
<string name="account_setup_incoming_security_none_label">Cap</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (si és disponible)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (sempre)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (si és disponible)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (sempre)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Quan esborro missatges</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">No els esborris del servidor</string>
|
||||
|
@ -393,10 +393,6 @@ Posílejte prosím chybová hlášení, přispívejte novými funkcemi a ptejte
|
||||
<string name="account_setup_incoming_security_label">Typ zabezpečení</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Typ ověření</string>
|
||||
<string name="account_setup_incoming_security_none_label">Žádné</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (je-li dostupné)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (vždy)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (je-li dostupné)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (vždy)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Když smažu zprávu</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Nemazat ji na serveru</string>
|
||||
|
@ -389,10 +389,6 @@ Vær venlig at sende fejlrapporter, anmodning om nye funktioner, og spørgsmål
|
||||
<string name="account_setup_incoming_security_label">Sikkerhed</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Autentifikations type</string>
|
||||
<string name="account_setup_incoming_security_none_label">Ingen</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (hvis tilgængelig)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (altid)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (hvis tilgængelig)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (altid)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Når jeg sletter en mail</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Undlad at slette på server</string>
|
||||
|
@ -387,10 +387,6 @@ Um Fehler zu melden, neue Funktionen vorzuschlagen oder Fragen zu stellen, besuc
|
||||
<string name="account_setup_incoming_security_label">Sicherheitstyp</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Authentifizierungstyp</string>
|
||||
<string name="account_setup_incoming_security_none_label">Keine Verschlüsselung</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (falls verfügbar)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (immer)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (falls verfügbar)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (immer)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Beim Löschen von Nachrichten</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Nie von Server löschen</string>
|
||||
|
@ -388,10 +388,6 @@
|
||||
<string name="account_setup_incoming_security_label">Ασφάλεια</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Πιστοποίηση</string>
|
||||
<string name="account_setup_incoming_security_none_label">Καμιά</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (αν υπάρχει)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (πάντοτε)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (αν υπάρχει)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (πάντοτε)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Κατά τη διαγραφή μηνύματος</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Να μη διαγράφεται στο server</string>
|
||||
|
@ -388,10 +388,6 @@ Por favor, envía los errores detectados, contribuye con nuevas funcionalidades
|
||||
<string name="account_setup_incoming_security_label">Tipo de Seguridad</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Tipo de autentificación</string>
|
||||
<string name="account_setup_incoming_security_none_label">Ninguna</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (si disponible)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (siempre)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (si disponible)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (siempre)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Borrado de mensajes</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">No borrar del servidor</string>
|
||||
|
@ -389,10 +389,6 @@ Virheraportit, osallistuminen projektiin ja kysymykset: Mene osoitteeseen
|
||||
<string name="account_setup_incoming_security_label">Suojauksen tyyppi</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Todennuksen tyyppi</string>
|
||||
<string name="account_setup_incoming_security_none_label">Ei mitään</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (jos käytettävissä)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (aina)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (jos käytettävissä)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (aina)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Kun viesti poistetaan</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Älä poista palvelimelta</string>
|
||||
|
@ -414,10 +414,6 @@ de plus</string>
|
||||
<string name="account_setup_incoming_security_label">Type de sécurité</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Type d\'authentification</string>
|
||||
<string name="account_setup_incoming_security_none_label">Aucun</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (si disponible)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (toujours)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (si disponible)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (toujours)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Supprimer les messages du serveur\u00A0:</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Jamais</string>
|
||||
|
@ -388,10 +388,6 @@ Por favor, envía os erros detectados, contribúe con novas funcionalidas e preg
|
||||
<string name="account_setup_incoming_security_label">Tipo de Seguridade</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Tipo de autentificación</string>
|
||||
<string name="account_setup_incoming_security_none_label">Ningunha</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (se dispoñible)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (sempre)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (se dispoñible)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (sempre)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Borrado de mesaxes</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Non borrar do servidor</string>
|
||||
|
@ -389,10 +389,6 @@ Hibajelentéseivel hozzájárul az újabb verziók tökéletesítéséhez, kérd
|
||||
<string name="account_setup_incoming_security_label">Kapcsolat biztonsága</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Hitelesítés típus</string>
|
||||
<string name="account_setup_incoming_security_none_label">Nincs</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (ha elérhető)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (mindig)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (ha elérhető)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (mindig)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Üzenet törlésekor</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Ne törlődjön a szerverről</string>
|
||||
|
@ -388,10 +388,6 @@ Invia le tue segnalazioni, suggerisci nuove funzionalità e chiedi informazioni
|
||||
<string name="account_setup_incoming_security_label">Tipo di protezione</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Tipo di autenticazione</string>
|
||||
<string name="account_setup_incoming_security_none_label">Nessuna</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (se disponibile)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (sempre)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (se disponibile)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (sempre)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Quando si elimina un messaggio</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Non eliminare dal server</string>
|
||||
|
@ -390,10 +390,6 @@
|
||||
<string name="account_setup_incoming_security_label">סוג אבטחה</string>
|
||||
<string name="account_setup_incoming_auth_type_label">סוג אימות</string>
|
||||
<string name="account_setup_incoming_security_none_label">כלום</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (אם זמין)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (תמיד)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (אם זמין)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (תמיד)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">כאשר אני מוחק הודעה</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">אל תמחוק בשרת</string>
|
||||
|
@ -389,10 +389,6 @@ K-9 は大多数のメールクライアントと同様に、ほとんどのフ
|
||||
<string name="account_setup_incoming_security_label">保護された接続</string>
|
||||
<string name="account_setup_incoming_auth_type_label">認証タイプ</string>
|
||||
<string name="account_setup_incoming_security_none_label">使用しない</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">可能なら SSL/TLS を使用する</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS を使用する</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">可能なら STARTTLS を使用する</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS を使用する</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">メール削除時の動作</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">サーバでは削除しない</string>
|
||||
|
@ -387,10 +387,6 @@ K-9 메일은 대부분의 무료 hotmail 계정을 지원하지 않으며, 다
|
||||
<string name="account_setup_incoming_security_label">보안 연결</string>
|
||||
<string name="account_setup_incoming_auth_type_label">인증 방식</string>
|
||||
<string name="account_setup_incoming_security_none_label">없음</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (유효할 경우)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (모든 인증서 허용)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (유효할 경우)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (모든 인증서 허용)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">메시지 삭제 시</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">서버에는 메일을 삭제하지 않음</string>
|
||||
|
@ -388,10 +388,6 @@ Praneškite apie klaidas, pridėkite naujų galimybių ir užduokite klausimus m
|
||||
<string name="account_setup_incoming_security_label">Saugumas</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Tapatumo nustatymas</string>
|
||||
<string name="account_setup_incoming_security_none_label">Joks</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (jei įmanoma)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (visada)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (jei įmanoma)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (visada)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Kai pašalinu laišką</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Nešalinti serveryje</string>
|
||||
|
@ -388,10 +388,6 @@ Graag foutrapporten, bijdrage nieuwe functies en vragen stellen op
|
||||
<string name="account_setup_incoming_security_label">Beveiligings type</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Authenticatie type</string>
|
||||
<string name="account_setup_incoming_security_none_label">Geen</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (indien beschikbaar)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (altijd)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (indien beschikbaar)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (altijd)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Wanneer ik een bericht verwijder</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Verwijder niet van server</string>
|
||||
|
@ -399,10 +399,6 @@ Wszelkie zgłoszenia usterek, zapytania oraz nowe pomysły prosimy przesyłać z
|
||||
<string name="account_setup_incoming_security_label">Zabezpieczenia</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Rodzaj uwierzytelnienia</string>
|
||||
<string name="account_setup_incoming_security_none_label">Brak</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (jeśli dostępne)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (zawsze)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (jeśli dostępne)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (zawsze)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Gdy skasuję wiadomość</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Nie usuwaj z serwera</string>
|
||||
|
@ -388,10 +388,6 @@ Por favor, nos envie relatórios de bugs, contribua para novas melhorias e faça
|
||||
<string name="account_setup_incoming_security_label">Tipo de segurança</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Tipo de autenticação</string>
|
||||
<string name="account_setup_incoming_security_none_label">Nenhum</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (se disponível)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (sempre)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (se disponível)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (sempre)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Quando eu excluir uma mensagem</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Não excluí-la do servidor</string>
|
||||
|
@ -389,10 +389,6 @@ K-9 Mail — почтовый клиент для Android.
|
||||
<string name="account_setup_incoming_security_label">Безопасность</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Аутентификация</string>
|
||||
<string name="account_setup_incoming_security_none_label">Нет</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (если доступно)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (всегда)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (если доступно)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (всегда)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Удалённое, на сервере</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Оставить</string>
|
||||
|
@ -388,10 +388,6 @@ Prosím, nahlasujte prípadné chyby, prispievajte novými funkciami a pýtajte
|
||||
<string name="account_setup_incoming_security_label">Zabezpečenie</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Overenie</string>
|
||||
<string name="account_setup_incoming_security_none_label">Žiadne</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (ak je k dispozícii)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (vždy)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (ak je k dispozícii)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (vždy)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Akcia po vymazaní správy</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Ponechať na serveri</string>
|
||||
|
@ -389,10 +389,6 @@ Vänligen skicka felrapporter, hjälp till med nya funktioner och ställ frågor
|
||||
<string name="account_setup_incoming_security_label">Säkerhetstyp</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Autentiseringstyp</string>
|
||||
<string name="account_setup_incoming_security_none_label">Ingen</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (om tillgängligt)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (alltid)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (om tillgängligt)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (alltid)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">När jag raderar ett brev</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Radera inte på servern</string>
|
||||
|
@ -388,10 +388,6 @@ Lütfen hata raporlarınızı, istediğiniz yeni özellikleri ve sorularınızı
|
||||
<string name="account_setup_incoming_security_label">Güvenlik tipi</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Kimlik doğrulama tipi</string>
|
||||
<string name="account_setup_incoming_security_none_label">Hiçbiri</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (Varsa)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (daima)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (Varsa)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (Daima)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Bir mesaj sildiğim zaman</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Sunucudan silme</string>
|
||||
|
@ -388,10 +388,6 @@ K-9 Mail це поштовий клієнт з відкритим вихідни
|
||||
<string name="account_setup_incoming_security_label">Тип системи захисту</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Метод автентифікації</string>
|
||||
<string name="account_setup_incoming_security_none_label">Немає</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (якщо доступно)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (завжди)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (якщо доступно)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (завжди)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">Коли повідомлення видалено</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Не видаляти на сервері</string>
|
||||
|
@ -387,10 +387,6 @@ K-9改进的功能包括:
|
||||
<string name="account_setup_incoming_security_label">加密方法</string>
|
||||
<string name="account_setup_incoming_auth_type_label">身份验证方法</string>
|
||||
<string name="account_setup_incoming_security_none_label">无</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS(如果可能的话)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS(总是)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS(如果可能的话)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS(总是)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">当我删除邮件时</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">不要从服务器上删除</string>
|
||||
|
@ -361,10 +361,6 @@
|
||||
<string name="account_setup_incoming_security_label">加密類型</string>
|
||||
<string name="account_setup_incoming_auth_type_label">身份驗證類型</string>
|
||||
<string name="account_setup_incoming_security_none_label">無</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS(如果可用)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS(預設)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS(如果可用)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS(預設)</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">當我刪除郵件時</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">不要從伺服器上刪除</string>
|
||||
|
@ -385,6 +385,10 @@ Please submit bug reports, contribute new features and ask questions at
|
||||
<string name="account_setup_account_type_imap_action">IMAP</string>
|
||||
<string name="account_setup_account_type_webdav_action">Exchange (WebDAV)</string>
|
||||
|
||||
<string name="account_setup_auth_type_normal_password">Normal password</string>
|
||||
<string name="account_setup_auth_type_insecure_password">Password, transmitted insecurely</string>
|
||||
<string name="account_setup_auth_type_encrypted_password">Encrypted password</string>
|
||||
|
||||
<string name="account_setup_incoming_title">Incoming server settings</string>
|
||||
<string name="account_setup_incoming_username_label">Username</string>
|
||||
<string name="account_setup_incoming_password_label">Password</string>
|
||||
@ -395,10 +399,8 @@ Please submit bug reports, contribute new features and ask questions at
|
||||
<string name="account_setup_incoming_security_label">Security</string>
|
||||
<string name="account_setup_incoming_auth_type_label">Authentication</string>
|
||||
<string name="account_setup_incoming_security_none_label">None</string>
|
||||
<string name="account_setup_incoming_security_ssl_optional_label">SSL/TLS (if available)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS (always)</string>
|
||||
<string name="account_setup_incoming_security_tls_optional_label">STARTTLS (if available)</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS (always)</string>
|
||||
<string name="account_setup_incoming_security_ssl_label">SSL/TLS</string>
|
||||
<string name="account_setup_incoming_security_tls_label">STARTTLS</string>
|
||||
|
||||
<string name="account_setup_incoming_delete_policy_label">When I delete a message</string>
|
||||
<string name="account_setup_incoming_delete_policy_never_label">Do not delete on server</string>
|
||||
|
@ -36,16 +36,12 @@
|
||||
|
||||
Valid incoming uri schemes are:
|
||||
imap IMAP with no transport security.
|
||||
imap+tls IMAP with optional TLS transport security.
|
||||
If TLS is not available the connection is made as "imap"
|
||||
imap+tls+ IMAP with required TLS transport security.
|
||||
If TLS is not available the conneciton fails.
|
||||
imap+ssl+ IMAP with required SSL transport security.
|
||||
If SSL is not available the connection fails.
|
||||
|
||||
pop3 POP3 with no transport security.
|
||||
pop3+tls POP3 with optional TLS transport security.
|
||||
If TLS is not available the connection is made as "pop3"
|
||||
pop3+tls+ POP3 with required TLS transport security.
|
||||
If TLS is not available the conneciton fails.
|
||||
pop3+ssl+ POP3 with required SSL transport security.
|
||||
@ -53,8 +49,6 @@
|
||||
|
||||
Valid outgoing uri schemes are:
|
||||
smtp SMTP with no transport security.
|
||||
smtp+tls SMTP with optional TLS transport security.
|
||||
If TLS is not available the connection is made as "smtp"
|
||||
smtp+tls+ SMTP with required TLS transport security.
|
||||
If TLS is not available the conneciton fails.
|
||||
smtp+ssl+ SMTP with required SSL transport security.
|
||||
@ -127,8 +121,8 @@
|
||||
<outgoing uri="smtp+ssl+://smtp.east.cox.net" username="$user" />
|
||||
</provider>
|
||||
<provider id="dotmac" label=".Mac" domain="mac.com">
|
||||
<incoming uri="imap+tls//mail.mac.com" username="$email" />
|
||||
<outgoing uri="smtp+tls://smtp.mac.com" username="$email" />
|
||||
<incoming uri="imap+ssl+://imap.mail.me.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.mail.me.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="earthlink" label="Earthlink" domain="earthlink.net">
|
||||
<incoming uri="pop3://pop.earthlink.net" username="$email" />
|
||||
@ -154,10 +148,9 @@
|
||||
<incoming uri="imap+ssl+://imap-mail.outlook.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp-mail.outlook.com" username="$email" />
|
||||
</provider>
|
||||
<!-- Note: Mac Mail.app sets IMAP to mail.mac.com, but it's the same server -->
|
||||
<provider id="mobileme" label="MobileMe" domain="me.com">
|
||||
<incoming uri="imap+tls://mail.me.com" username="$email" />
|
||||
<outgoing uri="smtp+tls://smtp.me.com" username="$email" />
|
||||
<incoming uri="imap+ssl+://imap.mail.me.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.mail.me.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="netzero" label="NetZero" domain="netzero.com">
|
||||
<incoming uri="pop3://pop.netzero.com" username="$user" />
|
||||
@ -296,12 +289,12 @@
|
||||
<outgoing uri="smtp://smtp.wp.pl" username="$user" />
|
||||
</provider>
|
||||
<provider id="interia" label="Interia" domain="interia.pl">
|
||||
<incoming uri="pop3+ssl://poczta.interia.pl" username="$user" />
|
||||
<outgoing uri="smtp+ssl://poczta.interia.pl" username="$user" />
|
||||
<incoming uri="pop3+ssl+://poczta.interia.pl" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://poczta.interia.pl" username="$user" />
|
||||
</provider>
|
||||
<provider id="o2" label="O2" domain="o2.pl">
|
||||
<incoming uri="pop3+ssl://poczta.o2.pl" username="$user" />
|
||||
<outgoing uri="smtp+ssl://poczta.o2.pl" username="$user" />
|
||||
<incoming uri="pop3+ssl+://poczta.o2.pl" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://poczta.o2.pl" username="$user" />
|
||||
</provider>
|
||||
|
||||
<!-- Japanese -->
|
||||
@ -323,28 +316,18 @@
|
||||
<!-- Korean -->
|
||||
<provider id="naver" label="Naver" domain="naver.com"
|
||||
note="@string/provider_note_naver">
|
||||
<incoming uri="imap+ssl://imap.naver.com" username="$user" />
|
||||
<outgoing uri="smtp+tls://smtp.naver.com:587" username="$user" />
|
||||
<incoming uri="imap+ssl+://imap.naver.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.naver.com:587" username="$user" />
|
||||
</provider>
|
||||
<provider id="hanmail" label="Hanmail" domain="hanmail.net"
|
||||
note="@string/provider_note_hanmail">
|
||||
<incoming uri="imap+ssl://imap.hanmail.net" username="$user" />
|
||||
<outgoing uri="smtp+ssl://smtp.hanmail.net" username="$user" />
|
||||
<incoming uri="imap+ssl+://imap.hanmail.net" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.hanmail.net" username="$user" />
|
||||
</provider>
|
||||
<provider id="daum" label="Hanmail" domain="daum.net"
|
||||
note="@string/provider_note_hanmail">
|
||||
<incoming uri="imap+ssl://imap.hanmail.net" username="$user" />
|
||||
<outgoing uri="smtp+ssl://smtp.hanmail.net" username="$user" />
|
||||
</provider>
|
||||
<provider id="paran" label="Paran" domain="paran.com"
|
||||
note="@string/provider_note_paran">
|
||||
<incoming uri="imap+ssl://imap.paran.com" username="$email" />
|
||||
<outgoing uri="smtp+tls://smtp.paran.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="nate" label="Nate" domain="nate.com"
|
||||
note="@string/provider_note_nate">
|
||||
<incoming uri="imap+ssl://imap.nate.com" username="$user" />
|
||||
<outgoing uri="smtp+tls://smtp.mail.nate.com" username="$user" />
|
||||
<incoming uri="imap+ssl+://imap.hanmail.net" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.hanmail.net" username="$user" />
|
||||
</provider>
|
||||
|
||||
<!-- Russia -->
|
||||
@ -538,11 +521,11 @@
|
||||
|
||||
<!-- Developers' vanity providers -->
|
||||
<provider id="fsck.com" label="Jesse's personal mail" domain="fsck.com" >
|
||||
<incoming uri="imap+ssl://fsck.com" username="$user" />
|
||||
<outgoing uri="smtp+tls://mail.bestpractical.com:2525" />
|
||||
<incoming uri="imap+ssl+://fsck.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://mail.bestpractical.com:2525" />
|
||||
</provider>
|
||||
<provider id="bestpractical.com" label="Best Practical Solutions" domain="bestpractical.com" >
|
||||
<incoming uri="imap+ssl://imap.bestpractical.com" username="$user" />
|
||||
<outgoing uri="smtp+tls://smtp.bestpractical.com:2525" />
|
||||
<incoming uri="imap+ssl+://imap.bestpractical.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.bestpractical.com:2525" />
|
||||
</provider>
|
||||
</providers>
|
||||
|
@ -177,8 +177,9 @@ public class K9 extends Application {
|
||||
/**
|
||||
* Can create messages containing stack traces that can be forwarded
|
||||
* to the development team.
|
||||
*
|
||||
* Feature is enabled when DEBUG == true
|
||||
*/
|
||||
public static boolean ENABLE_ERROR_FOLDER = true;
|
||||
public static String ERROR_FOLDER_NAME = "K9mail-errors";
|
||||
|
||||
/**
|
||||
|
@ -55,6 +55,11 @@ public class AccountSetupAccountType extends K9Activity implements OnClickListen
|
||||
URI uri = new URI(mAccount.getStoreUri());
|
||||
uri = new URI("pop3", uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
|
||||
mAccount.setStoreUri(uri.toString());
|
||||
|
||||
uri = new URI(mAccount.getTransportUri());
|
||||
uri = new URI("smtp", uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
|
||||
mAccount.setTransportUri(uri.toString());
|
||||
|
||||
AccountSetupIncoming.actionIncomingSettings(this, mAccount, mMakeDefault);
|
||||
finish();
|
||||
} catch (Exception use) {
|
||||
@ -68,6 +73,11 @@ public class AccountSetupAccountType extends K9Activity implements OnClickListen
|
||||
URI uri = new URI(mAccount.getStoreUri());
|
||||
uri = new URI("imap", uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
|
||||
mAccount.setStoreUri(uri.toString());
|
||||
|
||||
uri = new URI(mAccount.getTransportUri());
|
||||
uri = new URI("smtp", uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
|
||||
mAccount.setTransportUri(uri.toString());
|
||||
|
||||
AccountSetupIncoming.actionIncomingSettings(this, mAccount, mMakeDefault);
|
||||
finish();
|
||||
} catch (Exception use) {
|
||||
|
@ -15,9 +15,11 @@ import android.widget.*;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
|
||||
import com.fsck.k9.*;
|
||||
import com.fsck.k9.Account.FolderMode;
|
||||
import com.fsck.k9.activity.K9Activity;
|
||||
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.ConnectionSecurity;
|
||||
import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Store;
|
||||
@ -26,6 +28,7 @@ import com.fsck.k9.mail.store.Pop3Store;
|
||||
import com.fsck.k9.mail.store.WebDavStore;
|
||||
import com.fsck.k9.mail.store.ImapStore.ImapStoreSettings;
|
||||
import com.fsck.k9.mail.store.WebDavStore.WebDavStoreSettings;
|
||||
import com.fsck.k9.service.MailService;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
@ -38,32 +41,13 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
private static final String EXTRA_ACCOUNT = "account";
|
||||
private static final String EXTRA_MAKE_DEFAULT = "makeDefault";
|
||||
|
||||
private static final int[] POP3_PORTS = {
|
||||
110, 995, 995, 110, 110
|
||||
};
|
||||
private static final String POP3_PORT = "110";
|
||||
private static final String POP3_SSL_PORT = "995";
|
||||
private static final String IMAP_PORT = "143";
|
||||
private static final String IMAP_SSL_PORT = "993";
|
||||
private static final String WEBDAV_PORT = "80";
|
||||
private static final String WEBDAV_SSL_PORT = "443";
|
||||
|
||||
private static final int[] IMAP_PORTS = {
|
||||
143, 993, 993, 143, 143
|
||||
};
|
||||
|
||||
private static final int[] WEBDAV_PORTS = {
|
||||
80, 443, 443, 443, 443
|
||||
};
|
||||
|
||||
private static final ConnectionSecurity[] CONNECTION_SECURITY_TYPES = {
|
||||
ConnectionSecurity.NONE,
|
||||
ConnectionSecurity.SSL_TLS_OPTIONAL,
|
||||
ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
ConnectionSecurity.STARTTLS_OPTIONAL,
|
||||
ConnectionSecurity.STARTTLS_REQUIRED
|
||||
};
|
||||
|
||||
private static final String[] AUTH_TYPES = {
|
||||
"PLAIN", "CRAM_MD5"
|
||||
};
|
||||
|
||||
|
||||
private int[] mAccountPorts;
|
||||
private String mStoreType;
|
||||
private EditText mUsernameView;
|
||||
private EditText mPasswordView;
|
||||
@ -83,6 +67,10 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
private CheckBox mCompressionWifi;
|
||||
private CheckBox mCompressionOther;
|
||||
private CheckBox mSubscribedFoldersOnly;
|
||||
private ArrayAdapter<AuthType> mAuthTypeAdapter;
|
||||
private String mDefaultPort = "";
|
||||
private String mDefaultSslPort = "";
|
||||
private ConnectionSecurity[] mConnectionSecurityChoices = ConnectionSecurity.values();
|
||||
|
||||
public static void actionIncomingSettings(Activity context, Account account, boolean makeDefault) {
|
||||
Intent i = new Intent(context, AccountSetupIncoming.class);
|
||||
@ -139,32 +127,8 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
}
|
||||
});
|
||||
|
||||
SpinnerOption securityTypes[] = {
|
||||
new SpinnerOption(0, getString(R.string.account_setup_incoming_security_none_label)),
|
||||
new SpinnerOption(1,
|
||||
getString(R.string.account_setup_incoming_security_ssl_optional_label)),
|
||||
new SpinnerOption(2, getString(R.string.account_setup_incoming_security_ssl_label)),
|
||||
new SpinnerOption(3,
|
||||
getString(R.string.account_setup_incoming_security_tls_optional_label)),
|
||||
new SpinnerOption(4, getString(R.string.account_setup_incoming_security_tls_label)),
|
||||
};
|
||||
|
||||
// This needs to be kept in sync with the list at the top of the file.
|
||||
// that makes me somewhat unhappy
|
||||
SpinnerOption authTypeSpinnerOptions[] = {
|
||||
new SpinnerOption(0, AUTH_TYPES[0]),
|
||||
new SpinnerOption(1, AUTH_TYPES[1])
|
||||
};
|
||||
|
||||
ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
|
||||
android.R.layout.simple_spinner_item, securityTypes);
|
||||
securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
mSecurityTypeView.setAdapter(securityTypesAdapter);
|
||||
|
||||
ArrayAdapter<SpinnerOption> authTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
|
||||
android.R.layout.simple_spinner_item, authTypeSpinnerOptions);
|
||||
authTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
mAuthTypeView.setAdapter(authTypesAdapter);
|
||||
mAuthTypeAdapter = AuthType.getArrayAdapter(this);
|
||||
mAuthTypeView.setAdapter(mAuthTypeAdapter);
|
||||
|
||||
/*
|
||||
* Calls validateFields() which enables or disables the Next button
|
||||
@ -217,18 +181,17 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
mPasswordView.setText(settings.password);
|
||||
}
|
||||
|
||||
if (settings.authenticationType != null) {
|
||||
for (int i = 0; i < AUTH_TYPES.length; i++) {
|
||||
if (AUTH_TYPES[i].equals(settings.authenticationType)) {
|
||||
SpinnerOption.setSpinnerOptionValue(mAuthTypeView, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateAuthPlainTextFromSecurityType(settings.connectionSecurity);
|
||||
|
||||
// The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter
|
||||
int position = mAuthTypeAdapter.getPosition(settings.authenticationType);
|
||||
mAuthTypeView.setSelection(position, false);
|
||||
|
||||
mStoreType = settings.type;
|
||||
if (Pop3Store.STORE_TYPE.equals(settings.type)) {
|
||||
serverLabelView.setText(R.string.account_setup_incoming_pop_server_label);
|
||||
mAccountPorts = POP3_PORTS;
|
||||
mDefaultPort = POP3_PORT;
|
||||
mDefaultSslPort = POP3_SSL_PORT;
|
||||
findViewById(R.id.imap_path_prefix_section).setVisibility(View.GONE);
|
||||
findViewById(R.id.webdav_advanced_header).setVisibility(View.GONE);
|
||||
findViewById(R.id.webdav_mailbox_alias_section).setVisibility(View.GONE);
|
||||
@ -240,7 +203,8 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER);
|
||||
} else if (ImapStore.STORE_TYPE.equals(settings.type)) {
|
||||
serverLabelView.setText(R.string.account_setup_incoming_imap_server_label);
|
||||
mAccountPorts = IMAP_PORTS;
|
||||
mDefaultPort = IMAP_PORT;
|
||||
mDefaultSslPort = IMAP_SSL_PORT;
|
||||
|
||||
ImapStoreSettings imapSettings = (ImapStoreSettings) settings;
|
||||
|
||||
@ -260,7 +224,11 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
}
|
||||
} else if (WebDavStore.STORE_TYPE.equals(settings.type)) {
|
||||
serverLabelView.setText(R.string.account_setup_incoming_webdav_server_label);
|
||||
mAccountPorts = WEBDAV_PORTS;
|
||||
mDefaultPort = WEBDAV_PORT;
|
||||
mDefaultSslPort = WEBDAV_SSL_PORT;
|
||||
mConnectionSecurityChoices = new ConnectionSecurity[] {
|
||||
ConnectionSecurity.NONE,
|
||||
ConnectionSecurity.SSL_TLS_REQUIRED };
|
||||
|
||||
// Hide the unnecessary fields
|
||||
findViewById(R.id.imap_path_prefix_section).setVisibility(View.GONE);
|
||||
@ -288,12 +256,13 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
throw new Exception("Unknown account type: " + mAccount.getStoreUri());
|
||||
}
|
||||
|
||||
ArrayAdapter<ConnectionSecurity> securityTypesAdapter = new ArrayAdapter<ConnectionSecurity>(this,
|
||||
android.R.layout.simple_spinner_item, mConnectionSecurityChoices);
|
||||
securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
mSecurityTypeView.setAdapter(securityTypesAdapter);
|
||||
|
||||
// Select currently configured security type
|
||||
for (int i = 0; i < CONNECTION_SECURITY_TYPES.length; i++) {
|
||||
if (CONNECTION_SECURITY_TYPES[i] == settings.connectionSecurity) {
|
||||
SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, i);
|
||||
}
|
||||
}
|
||||
mSecurityTypeView.setSelection(settings.connectionSecurity.ordinal(), false);
|
||||
|
||||
/*
|
||||
* Updates the port when the user changes the security type. This allows
|
||||
@ -353,9 +322,35 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
}
|
||||
|
||||
private void updatePortFromSecurityType() {
|
||||
if (mAccountPorts != null) {
|
||||
int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
|
||||
mPortView.setText(Integer.toString(mAccountPorts[securityType]));
|
||||
ConnectionSecurity securityType = (ConnectionSecurity) mSecurityTypeView.getSelectedItem();
|
||||
mPortView.setText(getDefaultPort(securityType));
|
||||
updateAuthPlainTextFromSecurityType(securityType);
|
||||
}
|
||||
|
||||
private String getDefaultPort(ConnectionSecurity securityType) {
|
||||
String port;
|
||||
switch (securityType) {
|
||||
case NONE:
|
||||
case STARTTLS_REQUIRED:
|
||||
port = mDefaultPort;
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
port = mDefaultSslPort;
|
||||
break;
|
||||
default:
|
||||
Log.e(K9.LOG_TAG, "Unhandled ConnectionSecurity type encountered");
|
||||
port = "";
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
private void updateAuthPlainTextFromSecurityType(ConnectionSecurity securityType) {
|
||||
switch (securityType) {
|
||||
case NONE:
|
||||
AuthType.PLAIN.useInsecureText(true, mAuthTypeAdapter);
|
||||
break;
|
||||
default:
|
||||
AuthType.PLAIN.useInsecureText(false, mAuthTypeAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,6 +358,16 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (Intent.ACTION_EDIT.equals(getIntent().getAction())) {
|
||||
boolean isPushCapable = false;
|
||||
try {
|
||||
Store store = mAccount.getRemoteStore();
|
||||
isPushCapable = store.isPushCapable();
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Could not get remote store", e);
|
||||
}
|
||||
if (isPushCapable && mAccount.getFolderPushMode() != FolderMode.NONE) {
|
||||
MailService.actionRestartPushers(this, null);
|
||||
}
|
||||
mAccount.save(Preferences.getPreferences(this));
|
||||
finish();
|
||||
} else {
|
||||
@ -402,12 +407,11 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
||||
|
||||
protected void onNext() {
|
||||
try {
|
||||
ConnectionSecurity connectionSecurity = CONNECTION_SECURITY_TYPES[
|
||||
(Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value];
|
||||
ConnectionSecurity connectionSecurity = (ConnectionSecurity) mSecurityTypeView.getSelectedItem();
|
||||
|
||||
String username = mUsernameView.getText().toString();
|
||||
String password = mPasswordView.getText().toString();
|
||||
String authType = ((SpinnerOption)mAuthTypeView.getSelectedItem()).label;
|
||||
AuthType authType = (AuthType) mAuthTypeView.getSelectedItem();
|
||||
String host = mServerView.getText().toString();
|
||||
int port = Integer.parseInt(mPortView.getText().toString());
|
||||
|
||||
|
@ -17,13 +17,14 @@ import com.fsck.k9.*;
|
||||
import com.fsck.k9.activity.K9Activity;
|
||||
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.ConnectionSecurity;
|
||||
import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Transport;
|
||||
import com.fsck.k9.mail.transport.SmtpTransport;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
OnCheckedChangeListener {
|
||||
@ -31,29 +32,8 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
|
||||
private static final String EXTRA_MAKE_DEFAULT = "makeDefault";
|
||||
|
||||
private static final int smtpPorts[] = {
|
||||
587, 465, 465, 587, 587
|
||||
};
|
||||
|
||||
private static final String smtpSchemes[] = {
|
||||
"smtp", "smtp+ssl", "smtp+ssl+", "smtp+tls", "smtp+tls+"
|
||||
};
|
||||
/*
|
||||
private static final int webdavPorts[] =
|
||||
{
|
||||
80, 443, 443, 443, 443
|
||||
};
|
||||
private static final String webdavSchemes[] =
|
||||
{
|
||||
"webdav", "webdav+ssl", "webdav+ssl+", "webdav+tls", "webdav+tls+"
|
||||
};
|
||||
*/
|
||||
private static final String authTypes[] = {
|
||||
SmtpTransport.AUTH_AUTOMATIC,
|
||||
SmtpTransport.AUTH_LOGIN,
|
||||
SmtpTransport.AUTH_PLAIN,
|
||||
SmtpTransport.AUTH_CRAM_MD5,
|
||||
};
|
||||
private static final String SMTP_PORT = "587";
|
||||
private static final String SMTP_SSL_PORT = "465";
|
||||
|
||||
private EditText mUsernameView;
|
||||
private EditText mPasswordView;
|
||||
@ -63,6 +43,7 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
private ViewGroup mRequireLoginSettingsView;
|
||||
private Spinner mSecurityTypeView;
|
||||
private Spinner mAuthTypeView;
|
||||
private ArrayAdapter<AuthType> mAuthTypeAdapter;
|
||||
private Button mNextButton;
|
||||
private Account mAccount;
|
||||
private boolean mMakeDefault;
|
||||
@ -117,30 +98,13 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
mNextButton.setOnClickListener(this);
|
||||
mRequireLoginView.setOnCheckedChangeListener(this);
|
||||
|
||||
SpinnerOption securityTypes[] = {
|
||||
new SpinnerOption(0, getString(R.string.account_setup_incoming_security_none_label)),
|
||||
new SpinnerOption(1,
|
||||
getString(R.string.account_setup_incoming_security_ssl_optional_label)),
|
||||
new SpinnerOption(2, getString(R.string.account_setup_incoming_security_ssl_label)),
|
||||
new SpinnerOption(3,
|
||||
getString(R.string.account_setup_incoming_security_tls_optional_label)),
|
||||
new SpinnerOption(4, getString(R.string.account_setup_incoming_security_tls_label)),
|
||||
};
|
||||
|
||||
SpinnerOption authTypeSpinnerOptions[] = new SpinnerOption[authTypes.length];
|
||||
for (int i = 0; i < authTypes.length; i++) {
|
||||
authTypeSpinnerOptions[i] = new SpinnerOption(i, authTypes[i]);
|
||||
}
|
||||
|
||||
ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
|
||||
android.R.layout.simple_spinner_item, securityTypes);
|
||||
ArrayAdapter<ConnectionSecurity> securityTypesAdapter = new ArrayAdapter<ConnectionSecurity>(this,
|
||||
android.R.layout.simple_spinner_item, ConnectionSecurity.values());
|
||||
securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
mSecurityTypeView.setAdapter(securityTypesAdapter);
|
||||
|
||||
ArrayAdapter<SpinnerOption> authTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
|
||||
android.R.layout.simple_spinner_item, authTypeSpinnerOptions);
|
||||
authTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
mAuthTypeView.setAdapter(authTypesAdapter);
|
||||
mAuthTypeAdapter = AuthType.getArrayAdapter(this);
|
||||
mAuthTypeView.setAdapter(mAuthTypeAdapter);
|
||||
|
||||
/*
|
||||
* Calls validateFields() which enables or disables the Next button
|
||||
@ -182,21 +146,9 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
}
|
||||
|
||||
try {
|
||||
URI uri = new URI(mAccount.getTransportUri());
|
||||
String username = null;
|
||||
String password = null;
|
||||
String authType = null;
|
||||
if (uri.getUserInfo() != null) {
|
||||
String[] userInfoParts = uri.getUserInfo().split(":");
|
||||
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
if (userInfoParts.length > 1) {
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
}
|
||||
if (userInfoParts.length > 2) {
|
||||
authType = userInfoParts[2];
|
||||
}
|
||||
}
|
||||
ServerSettings settings = Transport.decodeTransportUri(mAccount.getTransportUri());
|
||||
String username = settings.username;
|
||||
String password = settings.password;
|
||||
|
||||
if (username != null) {
|
||||
mUsernameView.setText(username);
|
||||
@ -207,20 +159,14 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
mPasswordView.setText(password);
|
||||
}
|
||||
|
||||
if (authType != null) {
|
||||
for (int i = 0; i < authTypes.length; i++) {
|
||||
if (authTypes[i].equals(authType)) {
|
||||
SpinnerOption.setSpinnerOptionValue(mAuthTypeView, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateAuthPlainTextFromSecurityType(settings.connectionSecurity);
|
||||
|
||||
// The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter
|
||||
int position = mAuthTypeAdapter.getPosition(settings.authenticationType);
|
||||
mAuthTypeView.setSelection(position, false);
|
||||
|
||||
// Select currently configured security type
|
||||
for (int i = 0; i < smtpSchemes.length; i++) {
|
||||
if (smtpSchemes[i].equals(uri.getScheme())) {
|
||||
SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, i);
|
||||
}
|
||||
}
|
||||
mSecurityTypeView.setSelection(settings.connectionSecurity.ordinal(), false);
|
||||
|
||||
/*
|
||||
* Updates the port when the user changes the security type. This allows
|
||||
@ -242,12 +188,12 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
public void onNothingSelected(AdapterView<?> parent) { /* unused */ }
|
||||
});
|
||||
|
||||
if (uri.getHost() != null) {
|
||||
mServerView.setText(uri.getHost());
|
||||
if (settings.host != null) {
|
||||
mServerView.setText(settings.host);
|
||||
}
|
||||
|
||||
if (uri.getPort() != -1) {
|
||||
mPortView.setText(Integer.toString(uri.getPort()));
|
||||
if (settings.port != -1) {
|
||||
mPortView.setText(Integer.toString(settings.port));
|
||||
} else {
|
||||
updatePortFromSecurityType();
|
||||
}
|
||||
@ -280,8 +226,36 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
}
|
||||
|
||||
private void updatePortFromSecurityType() {
|
||||
int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
|
||||
mPortView.setText(Integer.toString(smtpPorts[securityType]));
|
||||
ConnectionSecurity securityType = (ConnectionSecurity) mSecurityTypeView.getSelectedItem();
|
||||
mPortView.setText(getDefaultSmtpPort(securityType));
|
||||
updateAuthPlainTextFromSecurityType(securityType);
|
||||
}
|
||||
|
||||
private String getDefaultSmtpPort(ConnectionSecurity securityType) {
|
||||
String port;
|
||||
switch (securityType) {
|
||||
case NONE:
|
||||
case STARTTLS_REQUIRED:
|
||||
port = SMTP_PORT;
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
port = SMTP_SSL_PORT;
|
||||
break;
|
||||
default:
|
||||
port = "";
|
||||
Log.e(K9.LOG_TAG, "Unhandled ConnectionSecurity type encountered");
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
private void updateAuthPlainTextFromSecurityType(ConnectionSecurity securityType) {
|
||||
switch (securityType) {
|
||||
case NONE:
|
||||
AuthType.PLAIN.useInsecureText(true, mAuthTypeAdapter);
|
||||
break;
|
||||
default:
|
||||
AuthType.PLAIN.useInsecureText(false, mAuthTypeAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -298,34 +272,25 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
||||
}
|
||||
|
||||
protected void onNext() {
|
||||
int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
|
||||
URI uri;
|
||||
try {
|
||||
String usernameEnc = URLEncoder.encode(mUsernameView.getText().toString(), "UTF-8");
|
||||
String passwordEnc = URLEncoder.encode(mPasswordView.getText().toString(), "UTF-8");
|
||||
|
||||
String userInfo = null;
|
||||
String authType = ((SpinnerOption)mAuthTypeView.getSelectedItem()).label;
|
||||
if (mRequireLoginView.isChecked()) {
|
||||
userInfo = usernameEnc + ":" + passwordEnc + ":" + authType;
|
||||
}
|
||||
String newHost = mServerView.getText().toString();
|
||||
int newPort = Integer.parseInt(mPortView.getText().toString());
|
||||
uri = new URI(smtpSchemes[securityType], userInfo, newHost, newPort, null, null, null);
|
||||
mAccount.deleteCertificate(newHost, newPort, CheckDirection.OUTGOING);
|
||||
mAccount.setTransportUri(uri.toString());
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This really shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc);
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* It's unrecoverable if we cannot create a URI from components that
|
||||
* we validated to be safe.
|
||||
*/
|
||||
failure(e);
|
||||
ConnectionSecurity securityType = (ConnectionSecurity) mSecurityTypeView.getSelectedItem();
|
||||
String uri;
|
||||
String username = null;
|
||||
String password = null;
|
||||
AuthType authType = null;
|
||||
if (mRequireLoginView.isChecked()) {
|
||||
username = mUsernameView.getText().toString();
|
||||
password = mPasswordView.getText().toString();
|
||||
authType = (AuthType) mAuthTypeView.getSelectedItem();
|
||||
}
|
||||
|
||||
String newHost = mServerView.getText().toString();
|
||||
int newPort = Integer.parseInt(mPortView.getText().toString());
|
||||
String type = SmtpTransport.TRANSPORT_TYPE;
|
||||
ServerSettings server = new ServerSettings(type, newHost, newPort, securityType, authType, username, password);
|
||||
uri = Transport.createTransportUri(server);
|
||||
mAccount.deleteCertificate(newHost, newPort, CheckDirection.OUTGOING);
|
||||
mAccount.setTransportUri(uri);
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
|
@ -27,6 +27,7 @@ import android.app.PendingIntent;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@ -2627,7 +2628,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyUserIfCertificateProblem(Context context, Exception e,
|
||||
public static void notifyUserIfCertificateProblem(Context context, Exception e,
|
||||
Account account, boolean incoming) {
|
||||
if (!(e instanceof CertificateValidationException)) {
|
||||
return;
|
||||
@ -2647,7 +2648,7 @@ public class MessagingController implements Runnable {
|
||||
final PendingIntent pi = PendingIntent.getActivity(context,
|
||||
account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
final String title = context.getString(
|
||||
R.string.notification_certificate_error_title, account.getName());
|
||||
R.string.notification_certificate_error_title, account.getDescription());
|
||||
|
||||
final NotificationCompat.Builder builder = new NotificationBuilder(context);
|
||||
builder.setSmallIcon(R.drawable.ic_notify_new_mail);
|
||||
@ -2683,9 +2684,6 @@ public class MessagingController implements Runnable {
|
||||
static long uidfill = 0;
|
||||
static AtomicBoolean loopCatch = new AtomicBoolean();
|
||||
public void addErrorMessage(Account account, String subject, Throwable t) {
|
||||
if (!loopCatch.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (t == null) {
|
||||
return;
|
||||
@ -2693,6 +2691,17 @@ public class MessagingController implements Runnable {
|
||||
|
||||
CharArrayWriter baos = new CharArrayWriter(t.getStackTrace().length * 10);
|
||||
PrintWriter ps = new PrintWriter(baos);
|
||||
try {
|
||||
Application context = K9.app;
|
||||
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(
|
||||
context.getPackageName(), 0);
|
||||
ps.format("K9-Mail version: %s\r\n", packageInfo.versionName);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
ps.format("Device make: %s\r\n", Build.MANUFACTURER);
|
||||
ps.format("Device model: %s\r\n", Build.MODEL);
|
||||
ps.format("Android version: %s\r\n\r\n", Build.VERSION.RELEASE);
|
||||
t.printStackTrace(ps);
|
||||
ps.close();
|
||||
|
||||
@ -2703,13 +2712,11 @@ public class MessagingController implements Runnable {
|
||||
addErrorMessage(account, subject, baos.toString());
|
||||
} catch (Throwable it) {
|
||||
Log.e(K9.LOG_TAG, "Could not save error message to " + account.getErrorFolderName(), it);
|
||||
} finally {
|
||||
loopCatch.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void addErrorMessage(Account account, String subject, String body) {
|
||||
if (!K9.ENABLE_ERROR_FOLDER) {
|
||||
if (!K9.DEBUG) {
|
||||
return;
|
||||
}
|
||||
if (!loopCatch.compareAndSet(false, true)) {
|
||||
@ -5037,7 +5044,7 @@ public class MessagingController implements Runnable {
|
||||
* @param ringAndVibrate
|
||||
* {@code true}, if ringtone/vibration are allowed. {@code false}, otherwise.
|
||||
*/
|
||||
private void configureNotification(NotificationCompat.Builder builder, String ringtone,
|
||||
private static void configureNotification(NotificationCompat.Builder builder, String ringtone,
|
||||
long[] vibrationPattern, Integer ledColor, int ledSpeed, boolean ringAndVibrate) {
|
||||
|
||||
// if it's quiet time, then we shouldn't be ringing, buzzing or flashing
|
||||
|
83
src/com/fsck/k9/mail/AuthType.java
Normal file
83
src/com/fsck/k9/mail/AuthType.java
Normal file
@ -0,0 +1,83 @@
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.ArrayAdapter;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
|
||||
public enum AuthType {
|
||||
/*
|
||||
* The names of these authentication types are saved as strings when
|
||||
* settings are exported and are also saved as part of the Server URI stored
|
||||
* in the account settings.
|
||||
*
|
||||
* PLAIN and CRAM_MD5 originally referred to specific SASL authentication
|
||||
* mechanisms. Their meaning has since been broadened to mean authentication
|
||||
* with unencrypted and encrypted passwords, respectively. Nonetheless,
|
||||
* their original names have been retained for backward compatibility with
|
||||
* user settings.
|
||||
*/
|
||||
|
||||
PLAIN(R.string.account_setup_auth_type_normal_password){
|
||||
|
||||
@Override
|
||||
public void useInsecureText(boolean insecure, ArrayAdapter<AuthType> authTypesAdapter) {
|
||||
if (insecure) {
|
||||
mResourceId = R.string.account_setup_auth_type_insecure_password;
|
||||
} else {
|
||||
mResourceId = R.string.account_setup_auth_type_normal_password;
|
||||
}
|
||||
authTypesAdapter.notifyDataSetChanged();
|
||||
}
|
||||
},
|
||||
|
||||
CRAM_MD5(R.string.account_setup_auth_type_encrypted_password),
|
||||
|
||||
/*
|
||||
* The following are obsolete authentication settings that were used with
|
||||
* SMTP. They are no longer presented to the user as options, but they may
|
||||
* still exist in a user's settings from a previous version or may be found
|
||||
* when importing settings.
|
||||
*/
|
||||
AUTOMATIC(0),
|
||||
|
||||
LOGIN(0);
|
||||
|
||||
static public ArrayAdapter<AuthType> getArrayAdapter(Context context) {
|
||||
AuthType[] authTypes = {PLAIN, CRAM_MD5};
|
||||
ArrayAdapter<AuthType> authTypesAdapter = new ArrayAdapter<AuthType>(context,
|
||||
android.R.layout.simple_spinner_item, authTypes);
|
||||
authTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
return authTypesAdapter;
|
||||
}
|
||||
|
||||
int mResourceId;
|
||||
|
||||
private AuthType(int id) {
|
||||
mResourceId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to select an appropriate localized text label for the
|
||||
* {@code AuthType.PLAIN} option presented to users.
|
||||
*
|
||||
* @param insecure
|
||||
* <p>
|
||||
* A value of {@code true} will use "Normal password".
|
||||
* <p>
|
||||
* A value of {@code false} will use
|
||||
* "Password, transmitted insecurely"
|
||||
*/
|
||||
public void useInsecureText(boolean insecure, ArrayAdapter<AuthType> authTypesAdapter) {
|
||||
// Do nothing. Overridden in AuthType.PLAIN
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (mResourceId == 0) {
|
||||
return name();
|
||||
} else {
|
||||
return K9.app.getString(mResourceId);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,21 +17,21 @@ public class Authentication {
|
||||
* @param b64Nonce The nonce as base64-encoded string.
|
||||
* @return The CRAM-MD5 response.
|
||||
*
|
||||
* @throws AuthenticationFailedException If something went wrong.
|
||||
* @throws MessagingException If something went wrong.
|
||||
*
|
||||
* @see Authentication#computeCramMd5Bytes(String, String, byte[])
|
||||
*/
|
||||
public static String computeCramMd5(String username, String password, String b64Nonce)
|
||||
throws AuthenticationFailedException {
|
||||
throws MessagingException {
|
||||
|
||||
try {
|
||||
byte[] b64NonceBytes = b64Nonce.getBytes(US_ASCII);
|
||||
byte[] b64CRAM = computeCramMd5Bytes(username, password, b64NonceBytes);
|
||||
return new String(b64CRAM, US_ASCII);
|
||||
} catch (AuthenticationFailedException e) {
|
||||
} catch (MessagingException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationFailedException("This shouldn't happen", e);
|
||||
throw new MessagingException("This shouldn't happen", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,17 +44,17 @@ public class Authentication {
|
||||
* @param b64Nonce The nonce as base64-encoded byte array.
|
||||
* @return The CRAM-MD5 response as byte array.
|
||||
*
|
||||
* @throws AuthenticationFailedException If something went wrong.
|
||||
* @throws MessagingException If something went wrong.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc2195">RFC 2195</a>
|
||||
*/
|
||||
public static byte[] computeCramMd5Bytes(String username, String password, byte[] b64Nonce)
|
||||
throws AuthenticationFailedException {
|
||||
throws MessagingException {
|
||||
|
||||
try {
|
||||
byte[] nonce = Base64.decodeBase64(b64Nonce);
|
||||
|
||||
byte[] secretBytes = password.getBytes(US_ASCII);
|
||||
byte[] secretBytes = password.getBytes();
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
if (secretBytes.length > 64) {
|
||||
secretBytes = md.digest(secretBytes);
|
||||
@ -74,12 +74,12 @@ public class Authentication {
|
||||
byte[] result = md.digest(firstPass);
|
||||
|
||||
String plainCRAM = username + " " + new String(Hex.encodeHex(result));
|
||||
byte[] b64CRAM = Base64.encodeBase64(plainCRAM.getBytes(US_ASCII));
|
||||
byte[] b64CRAM = Base64.encodeBase64(plainCRAM.getBytes());
|
||||
|
||||
return b64CRAM;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationFailedException("Something went wrong during CRAM-MD5 computation", e);
|
||||
throw new MessagingException("Something went wrong during CRAM-MD5 computation", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,21 @@
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
/**
|
||||
* The currently available connection security types.
|
||||
*
|
||||
* <p>
|
||||
* Right now this enum is only used by {@link ServerSettings} and converted to store- or
|
||||
* transport-specific constants in the different {@link Store} and {@link Transport}
|
||||
* implementations. In the future we probably want to change this and use
|
||||
* {@code ConnectionSecurity} exclusively.
|
||||
* </p>
|
||||
*/
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
|
||||
public enum ConnectionSecurity {
|
||||
NONE,
|
||||
STARTTLS_OPTIONAL,
|
||||
STARTTLS_REQUIRED,
|
||||
SSL_TLS_OPTIONAL,
|
||||
SSL_TLS_REQUIRED
|
||||
NONE(R.string.account_setup_incoming_security_none_label),
|
||||
STARTTLS_REQUIRED(R.string.account_setup_incoming_security_tls_label),
|
||||
SSL_TLS_REQUIRED(R.string.account_setup_incoming_security_ssl_label);
|
||||
|
||||
private final int mResourceId;
|
||||
|
||||
private ConnectionSecurity(int id) {
|
||||
mResourceId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return K9.app.getString(mResourceId);
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class ServerSettings {
|
||||
*
|
||||
* {@code null} if not applicable for the store or transport.
|
||||
*/
|
||||
public final String authenticationType;
|
||||
public final AuthType authenticationType;
|
||||
|
||||
/**
|
||||
* The username part of the credentials needed to authenticate to the server.
|
||||
@ -91,7 +91,7 @@ public class ServerSettings {
|
||||
* see {@link ServerSettings#password}
|
||||
*/
|
||||
public ServerSettings(String type, String host, int port,
|
||||
ConnectionSecurity connectionSecurity, String authenticationType, String username,
|
||||
ConnectionSecurity connectionSecurity, AuthType authenticationType, String username,
|
||||
String password) {
|
||||
this.type = type;
|
||||
this.host = host;
|
||||
@ -124,7 +124,7 @@ public class ServerSettings {
|
||||
* see {@link ServerSettings#extra}
|
||||
*/
|
||||
public ServerSettings(String type, String host, int port,
|
||||
ConnectionSecurity connectionSecurity, String authenticationType, String username,
|
||||
ConnectionSecurity connectionSecurity, AuthType authenticationType, String username,
|
||||
String password, Map<String, String> extra) {
|
||||
this.type = type;
|
||||
this.host = host;
|
||||
|
@ -30,13 +30,13 @@ public class Hex {
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an array of bytes into an array of characters representing the hexidecimal values of each byte in order.
|
||||
* Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
|
||||
* The returned array will be double the length of the passed array, as it takes two characters to represent any
|
||||
* given byte.
|
||||
*
|
||||
* @param data
|
||||
* a byte[] to convert to Hex characters
|
||||
* @return A char[] containing hexidecimal characters
|
||||
* @return A char[] containing lower-case hexadecimal characters
|
||||
*/
|
||||
public static char[] encodeHex(byte[] data) {
|
||||
|
||||
|
@ -27,6 +27,7 @@ import java.nio.charset.CodingErrorAction;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -65,10 +66,12 @@ import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.helper.StringUtils;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.helper.power.TracingPowerManager;
|
||||
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.Authentication;
|
||||
import com.fsck.k9.mail.AuthenticationFailedException;
|
||||
import com.fsck.k9.mail.Body;
|
||||
@ -84,6 +87,7 @@ import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.Pusher;
|
||||
import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Store;
|
||||
import com.fsck.k9.mail.filter.Base64;
|
||||
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
||||
import com.fsck.k9.mail.filter.FixedLengthInputStream;
|
||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||
@ -110,14 +114,6 @@ import com.jcraft.jzlib.ZOutputStream;
|
||||
public class ImapStore extends Store {
|
||||
public static final String STORE_TYPE = "IMAP";
|
||||
|
||||
public static final int CONNECTION_SECURITY_NONE = 0;
|
||||
public static final int CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
||||
public static final int CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
||||
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
||||
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
||||
|
||||
public enum AuthType { PLAIN, CRAM_MD5 }
|
||||
|
||||
private static final int IDLE_READ_TIMEOUT_INCREMENT = 5 * 60 * 1000;
|
||||
private static final int IDLE_FAILURE_COUNT_LIMIT = 10;
|
||||
private static int MAX_DELAY_TIME = 5 * 60 * 1000; // 5 minutes
|
||||
@ -128,6 +124,10 @@ public class ImapStore extends Store {
|
||||
private Set<Flag> mPermanentFlagsIndex = new HashSet<Flag>();
|
||||
|
||||
private static final String CAPABILITY_IDLE = "IDLE";
|
||||
private static final String CAPABILITY_AUTH_CRAM_MD5 = "AUTH=CRAM-MD5";
|
||||
private static final String CAPABILITY_AUTH_PLAIN = "AUTH=PLAIN";
|
||||
private static final String CAPABILITY_LOGINDISABLED = "LOGINDISABLED";
|
||||
private static final String CAPABILITY_LITERAL_PLUS = "LITERAL+";
|
||||
private static final String COMMAND_IDLE = "IDLE";
|
||||
private static final String CAPABILITY_NAMESPACE = "NAMESPACE";
|
||||
private static final String COMMAND_NAMESPACE = "NAMESPACE";
|
||||
@ -147,18 +147,16 @@ public class ImapStore extends Store {
|
||||
*
|
||||
* <p>Possible forms:</p>
|
||||
* <pre>
|
||||
* imap://auth:user:password@server:port CONNECTION_SECURITY_NONE
|
||||
* imap+tls://auth:user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
* imap+tls+://auth:user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
||||
* imap+ssl+://auth:user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
||||
* imap+ssl://auth:user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
||||
* imap://auth:user:password@server:port ConnectionSecurity.NONE
|
||||
* imap+tls+://auth:user:password@server:port ConnectionSecurity.STARTTLS_REQUIRED
|
||||
* imap+ssl+://auth:user:password@server:port ConnectionSecurity.SSL_TLS_REQUIRED
|
||||
* </pre>
|
||||
*/
|
||||
public static ImapStoreSettings decodeUri(String uri) {
|
||||
String host;
|
||||
int port;
|
||||
ConnectionSecurity connectionSecurity;
|
||||
String authenticationType = null;
|
||||
AuthType authenticationType = null;
|
||||
String username = null;
|
||||
String password = null;
|
||||
String pathPrefix = null;
|
||||
@ -172,21 +170,27 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
String scheme = imapUri.getScheme();
|
||||
/*
|
||||
* Currently available schemes are:
|
||||
* imap
|
||||
* imap+tls+
|
||||
* imap+ssl+
|
||||
*
|
||||
* The following are obsolete schemes that may be found in pre-existing
|
||||
* settings from earlier versions or that may be found when imported. We
|
||||
* continue to recognize them and re-map them appropriately:
|
||||
* imap+tls
|
||||
* imap+ssl
|
||||
*/
|
||||
if (scheme.equals("imap")) {
|
||||
connectionSecurity = ConnectionSecurity.NONE;
|
||||
port = 143;
|
||||
} else if (scheme.equals("imap+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_OPTIONAL;
|
||||
port = 143;
|
||||
} else if (scheme.equals("imap+tls+")) {
|
||||
} else if (scheme.startsWith("imap+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED;
|
||||
port = 143;
|
||||
} else if (scheme.equals("imap+ssl+")) {
|
||||
} else if (scheme.startsWith("imap+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
|
||||
port = 993;
|
||||
} else if (scheme.equals("imap+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_OPTIONAL;
|
||||
port = 993;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
|
||||
}
|
||||
@ -204,14 +208,14 @@ public class ImapStore extends Store {
|
||||
|
||||
if (userinfo.endsWith(":")) {
|
||||
// Password is empty. This can only happen after an account was imported.
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]).name();
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]);
|
||||
username = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
} else if (userInfoParts.length == 2) {
|
||||
authenticationType = AuthType.PLAIN.name();
|
||||
authenticationType = AuthType.PLAIN;
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
} else {
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]).name();
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]);
|
||||
username = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
password = URLDecoder.decode(userInfoParts[2], "UTF-8");
|
||||
}
|
||||
@ -268,15 +272,9 @@ public class ImapStore extends Store {
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
case SSL_TLS_OPTIONAL:
|
||||
scheme = "imap+ssl";
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
scheme = "imap+ssl+";
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
scheme = "imap+tls";
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
scheme = "imap+tls+";
|
||||
break;
|
||||
@ -286,15 +284,9 @@ public class ImapStore extends Store {
|
||||
break;
|
||||
}
|
||||
|
||||
AuthType authType;
|
||||
try {
|
||||
authType = AuthType.valueOf(server.authenticationType);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid authentication type: " +
|
||||
server.authenticationType);
|
||||
}
|
||||
AuthType authType = server.authenticationType;
|
||||
|
||||
String userInfo = authType.toString() + ":" + userEnc + ":" + passwordEnc;
|
||||
String userInfo = authType.name() + ":" + userEnc + ":" + passwordEnc;
|
||||
try {
|
||||
Map<String, String> extra = server.getExtra();
|
||||
String path = null;
|
||||
@ -329,7 +321,7 @@ public class ImapStore extends Store {
|
||||
public final String pathPrefix;
|
||||
|
||||
protected ImapStoreSettings(String host, int port, ConnectionSecurity connectionSecurity,
|
||||
String authenticationType, String username, String password,
|
||||
AuthType authenticationType, String username, String password,
|
||||
boolean autodetectNamespace, String pathPrefix) {
|
||||
super(STORE_TYPE, host, port, connectionSecurity, authenticationType, username,
|
||||
password);
|
||||
@ -357,7 +349,7 @@ public class ImapStore extends Store {
|
||||
private int mPort;
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
private int mConnectionSecurity;
|
||||
private ConnectionSecurity mConnectionSecurity;
|
||||
private AuthType mAuthType;
|
||||
private volatile String mPathPrefix;
|
||||
private volatile String mCombinedPrefix = null;
|
||||
@ -376,7 +368,7 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConnectionSecurity() {
|
||||
public ConnectionSecurity getConnectionSecurity() {
|
||||
return mConnectionSecurity;
|
||||
}
|
||||
|
||||
@ -462,25 +454,9 @@ public class ImapStore extends Store {
|
||||
mHost = settings.host;
|
||||
mPort = settings.port;
|
||||
|
||||
switch (settings.connectionSecurity) {
|
||||
case NONE:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_NONE;
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
break;
|
||||
case SSL_TLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
break;
|
||||
}
|
||||
mConnectionSecurity = settings.connectionSecurity;
|
||||
|
||||
mAuthType = AuthType.valueOf(settings.authenticationType);
|
||||
mAuthType = settings.authenticationType;
|
||||
mUsername = settings.username;
|
||||
mPassword = settings.password;
|
||||
|
||||
@ -2429,7 +2405,7 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
try {
|
||||
int connectionSecurity = mSettings.getConnectionSecurity();
|
||||
ConnectionSecurity connectionSecurity = mSettings.getConnectionSecurity();
|
||||
|
||||
// Try all IPv4 and IPv6 addresses of the host
|
||||
InetAddress[] addresses = InetAddress.getAllByName(mSettings.getHost());
|
||||
@ -2443,15 +2419,13 @@ public class ImapStore extends Store {
|
||||
SocketAddress socketAddress = new InetSocketAddress(addresses[i],
|
||||
mSettings.getPort());
|
||||
|
||||
if (connectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
|
||||
connectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||
if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
boolean secure = connectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
sslContext
|
||||
.init(null,
|
||||
new TrustManager[] { TrustManagerFactory.get(
|
||||
mSettings.getHost(),
|
||||
mSettings.getPort(), secure) },
|
||||
mSettings.getPort()) },
|
||||
new SecureRandom());
|
||||
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
||||
} else {
|
||||
@ -2476,7 +2450,7 @@ public class ImapStore extends Store {
|
||||
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
|
||||
1024));
|
||||
mParser = new ImapResponseParser(mIn);
|
||||
mOut = mSocket.getOutputStream();
|
||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
|
||||
|
||||
capabilities.clear();
|
||||
ImapResponse nullResponse = mParser.readResponse();
|
||||
@ -2496,19 +2470,17 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
if (mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
|| mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED) {
|
||||
if (mSettings.getConnectionSecurity() == ConnectionSecurity.STARTTLS_REQUIRED) {
|
||||
|
||||
if (hasCapability("STARTTLS")) {
|
||||
// STARTTLS
|
||||
executeSimpleCommand("STARTTLS");
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
boolean secure = mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
sslContext.init(null,
|
||||
new TrustManager[] { TrustManagerFactory.get(
|
||||
mSettings.getHost(),
|
||||
mSettings.getPort(), secure) },
|
||||
mSettings.getPort()) },
|
||||
new SecureRandom());
|
||||
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket,
|
||||
mSettings.getHost(), mSettings.getPort(), true);
|
||||
@ -2516,37 +2488,55 @@ public class ImapStore extends Store {
|
||||
mIn = new PeekableInputStream(new BufferedInputStream(mSocket
|
||||
.getInputStream(), 1024));
|
||||
mParser = new ImapResponseParser(mIn);
|
||||
mOut = mSocket.getOutputStream();
|
||||
} else if (mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED) {
|
||||
throw new MessagingException("TLS not supported but required");
|
||||
}
|
||||
}
|
||||
|
||||
mOut = new BufferedOutputStream(mOut, 1024);
|
||||
|
||||
try {
|
||||
if (mSettings.getAuthType() == AuthType.CRAM_MD5) {
|
||||
authCramMD5();
|
||||
// The authCramMD5 method called on the previous line does not allow for handling updated capabilities
|
||||
// sent by the server. So, to make sure we update to the post-authentication capability list
|
||||
// we fetch the capabilities here.
|
||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
|
||||
// Per RFC 2595 (3.1): Once TLS has been started, reissue CAPABILITY command
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "Updating capabilities after CRAM-MD5 authentication for " + getLogId());
|
||||
Log.i(K9.LOG_TAG, "Updating capabilities after STARTTLS for " + getLogId());
|
||||
capabilities.clear();
|
||||
List<ImapResponse> responses = receiveCapabilities(executeSimpleCommand(COMMAND_CAPABILITY));
|
||||
if (responses.size() != 2) {
|
||||
throw new MessagingException("Invalid CAPABILITY response received");
|
||||
}
|
||||
|
||||
} else if (mSettings.getAuthType() == AuthType.PLAIN) {
|
||||
receiveCapabilities(executeSimpleCommand(String.format("LOGIN %s %s", ImapStore.encodeString(mSettings.getUsername()), ImapStore.encodeString(mSettings.getPassword())), true));
|
||||
} else {
|
||||
/*
|
||||
* This exception triggers a "Certificate error"
|
||||
* notification that takes the user to the incoming
|
||||
* server settings for review. This might be needed if
|
||||
* the account was configured with an obsolete
|
||||
* "STARTTLS (if available)" setting.
|
||||
*/
|
||||
throw new CertificateValidationException(
|
||||
"STARTTLS connection security not available",
|
||||
new CertificateException());
|
||||
}
|
||||
authSuccess = true;
|
||||
} catch (ImapException ie) {
|
||||
throw new AuthenticationFailedException(ie.getAlertText(), ie);
|
||||
|
||||
} catch (MessagingException me) {
|
||||
throw new AuthenticationFailedException(null, me);
|
||||
}
|
||||
|
||||
switch (mSettings.getAuthType()) {
|
||||
case CRAM_MD5:
|
||||
if (hasCapability(CAPABILITY_AUTH_CRAM_MD5)) {
|
||||
authCramMD5();
|
||||
} else {
|
||||
throw new MessagingException(
|
||||
"Server doesn't support encrypted passwords using CRAM-MD5.");
|
||||
}
|
||||
break;
|
||||
|
||||
case PLAIN:
|
||||
if (hasCapability(CAPABILITY_AUTH_PLAIN)) {
|
||||
saslAuthPlain();
|
||||
} else if (!hasCapability(CAPABILITY_LOGINDISABLED)) {
|
||||
login();
|
||||
} else {
|
||||
throw new MessagingException(
|
||||
"Server doesn't support unencrypted passwords using AUTH=PLAIN and LOGIN is disabled.");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new MessagingException(
|
||||
"Unhandled authentication method found in the server settings (bug).");
|
||||
}
|
||||
authSuccess = true;
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, CAPABILITY_COMPRESS_DEFLATE + " = " + hasCapability(CAPABILITY_COMPRESS_DEFLATE));
|
||||
}
|
||||
@ -2664,48 +2654,123 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
protected void authCramMD5() throws AuthenticationFailedException, MessagingException {
|
||||
try {
|
||||
String tag = sendCommand("AUTHENTICATE CRAM-MD5", false);
|
||||
byte[] buf = new byte[1024];
|
||||
int b64NonceLen = 0;
|
||||
for (int i = 0; i < buf.length; i++) {
|
||||
buf[i] = (byte)mIn.read();
|
||||
if (buf[i] == 0x0a) {
|
||||
b64NonceLen = i;
|
||||
break;
|
||||
}
|
||||
protected void login() throws IOException, MessagingException {
|
||||
boolean hasLiteralPlus = hasCapability(CAPABILITY_LITERAL_PLUS);
|
||||
String tag;
|
||||
byte[] username = mSettings.getUsername().getBytes();
|
||||
byte[] password = mSettings.getPassword().getBytes();
|
||||
tag = sendCommand(String.format(Locale.US, "LOGIN {%d%s}",
|
||||
username.length, hasLiteralPlus ? "+" : ""), true);
|
||||
if (!hasLiteralPlus) {
|
||||
readContinuationResponse(tag);
|
||||
}
|
||||
if (b64NonceLen == 0) {
|
||||
throw new AuthenticationFailedException("Error negotiating CRAM-MD5: nonce too long.");
|
||||
mOut.write(username);
|
||||
mOut.write(String.format(Locale.US, " {%d%s}\r\n", password.length,
|
||||
hasLiteralPlus ? "+" : "").getBytes());
|
||||
if (!hasLiteralPlus) {
|
||||
mOut.flush();
|
||||
readContinuationResponse(tag);
|
||||
}
|
||||
byte[] b64NonceTrim = new byte[b64NonceLen - 2];
|
||||
System.arraycopy(buf, 1, b64NonceTrim, 0, b64NonceLen - 2);
|
||||
|
||||
byte[] b64CRAM = Authentication.computeCramMd5Bytes(mSettings.getUsername(),
|
||||
mSettings.getPassword(), b64NonceTrim);
|
||||
|
||||
mOut.write(b64CRAM);
|
||||
mOut.write(new byte[] { 0x0d, 0x0a });
|
||||
mOut.write(password);
|
||||
mOut.write('\r');
|
||||
mOut.write('\n');
|
||||
mOut.flush();
|
||||
try {
|
||||
receiveCapabilities(readStatusResponse(tag, "LOGIN", null));
|
||||
} catch (MessagingException e) {
|
||||
throw new AuthenticationFailedException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
int respLen = 0;
|
||||
for (int i = 0; i < buf.length; i++) {
|
||||
buf[i] = (byte)mIn.read();
|
||||
if (buf[i] == 0x0a) {
|
||||
respLen = i;
|
||||
break;
|
||||
protected void authCramMD5() throws MessagingException, IOException {
|
||||
String command = "AUTHENTICATE CRAM-MD5";
|
||||
String tag = sendCommand(command, false);
|
||||
ImapResponse response = readContinuationResponse(tag);
|
||||
if (response.size() != 1 || !(response.get(0) instanceof String)) {
|
||||
throw new MessagingException("Invalid Cram-MD5 nonce received");
|
||||
}
|
||||
byte[] b64Nonce = response.getString(0).getBytes();
|
||||
byte[] b64CRAM = Authentication.computeCramMd5Bytes(
|
||||
mSettings.getUsername(), mSettings.getPassword(), b64Nonce);
|
||||
|
||||
mOut.write(b64CRAM);
|
||||
mOut.write('\r');
|
||||
mOut.write('\n');
|
||||
mOut.flush();
|
||||
try {
|
||||
receiveCapabilities(readStatusResponse(tag, command, null));
|
||||
} catch (MessagingException e) {
|
||||
throw new AuthenticationFailedException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected void saslAuthPlain() throws IOException, MessagingException {
|
||||
String command = "AUTHENTICATE PLAIN";
|
||||
String tag = sendCommand(command, false);
|
||||
readContinuationResponse(tag);
|
||||
mOut.write(Base64.encodeBase64(("\000" + mSettings.getUsername()
|
||||
+ "\000" + mSettings.getPassword()).getBytes()));
|
||||
mOut.write('\r');
|
||||
mOut.write('\n');
|
||||
mOut.flush();
|
||||
try {
|
||||
receiveCapabilities(readStatusResponse(tag, command, null));
|
||||
} catch (MessagingException e) {
|
||||
throw new AuthenticationFailedException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected ImapResponse readContinuationResponse(String tag)
|
||||
throws IOException, MessagingException {
|
||||
ImapResponse response;
|
||||
do {
|
||||
response = readResponse();
|
||||
if (response.mTag != null) {
|
||||
if (response.mTag.equalsIgnoreCase(tag)) {
|
||||
throw new MessagingException(
|
||||
"Command continuation aborted: " + response);
|
||||
} else {
|
||||
Log.w(K9.LOG_TAG, "After sending tag " + tag
|
||||
+ ", got tag response from previous command "
|
||||
+ response + " for " + getLogId());
|
||||
}
|
||||
}
|
||||
} while (!response.mCommandContinuationRequested);
|
||||
return response;
|
||||
}
|
||||
|
||||
String toMatch = tag + " OK";
|
||||
String respStr = new String(buf, 0, respLen);
|
||||
if (!respStr.startsWith(toMatch)) {
|
||||
throw new AuthenticationFailedException("CRAM-MD5 error: " + respStr);
|
||||
protected ArrayList<ImapResponse> readStatusResponse(String tag,
|
||||
String commandToLog, UntaggedHandler untaggedHandler)
|
||||
throws IOException, MessagingException {
|
||||
ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>();
|
||||
ImapResponse response;
|
||||
do {
|
||||
response = mParser.readResponse();
|
||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
|
||||
Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
|
||||
|
||||
if (response.mTag != null && !response.mTag.equalsIgnoreCase(tag)) {
|
||||
Log.w(K9.LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response + " for " + getLogId());
|
||||
Iterator<ImapResponse> iter = responses.iterator();
|
||||
while (iter.hasNext()) {
|
||||
ImapResponse delResponse = iter.next();
|
||||
if (delResponse.mTag != null || delResponse.size() < 2
|
||||
|| (!ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXISTS") && !ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXPUNGE"))) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
response.mTag = null;
|
||||
continue;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new AuthenticationFailedException("CRAM-MD5 Auth Failed.", ioe);
|
||||
if (untaggedHandler != null) {
|
||||
untaggedHandler.handleAsyncUntaggedResponse(response);
|
||||
}
|
||||
responses.add(response);
|
||||
} while (response.mTag == null);
|
||||
if (response.size() < 1 || !ImapResponseParser.equalsIgnoreCase(response.get(0), "OK")) {
|
||||
throw new ImapException("Command: " + commandToLog + "; response: " + response.toString(), response.getAlertText());
|
||||
}
|
||||
return responses;
|
||||
}
|
||||
|
||||
protected void setReadTimeout(int millis) throws SocketException {
|
||||
@ -2830,35 +2895,7 @@ public class ImapStore extends Store {
|
||||
//if (K9.DEBUG)
|
||||
// Log.v(K9.LOG_TAG, "Sent IMAP command " + commandToLog + " with tag " + tag + " for " + getLogId());
|
||||
|
||||
ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>();
|
||||
ImapResponse response;
|
||||
do {
|
||||
response = mParser.readResponse();
|
||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
|
||||
Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
|
||||
|
||||
if (response.mTag != null && !response.mTag.equalsIgnoreCase(tag)) {
|
||||
Log.w(K9.LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response + " for " + getLogId());
|
||||
Iterator<ImapResponse> iter = responses.iterator();
|
||||
while (iter.hasNext()) {
|
||||
ImapResponse delResponse = iter.next();
|
||||
if (delResponse.mTag != null || delResponse.size() < 2
|
||||
|| (!ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXISTS") && !ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXPUNGE"))) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
response.mTag = null;
|
||||
continue;
|
||||
}
|
||||
if (untaggedHandler != null) {
|
||||
untaggedHandler.handleAsyncUntaggedResponse(response);
|
||||
}
|
||||
responses.add(response);
|
||||
} while (response.mTag == null);
|
||||
if (response.size() < 1 || !ImapResponseParser.equalsIgnoreCase(response.get(0), "OK")) {
|
||||
throw new ImapException("Command: " + commandToLog + "; response: " + response.toString(), response.getAlertText());
|
||||
}
|
||||
return responses;
|
||||
return readStatusResponse(tag, commandToLog, untaggedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3102,6 +3139,7 @@ public class ImapStore extends Store {
|
||||
if (stop.get()) {
|
||||
Log.i(K9.LOG_TAG, "Got exception while idling, but stop is set for " + getLogId());
|
||||
} else {
|
||||
MessagingController.notifyUserIfCertificateProblem(K9.app, e, getAccount(), true);
|
||||
receiver.pushError("Push error for " + getName(), e);
|
||||
Log.e(K9.LOG_TAG, "Got exception while idling for " + getLogId(), e);
|
||||
int delayTimeInt = delayTime.get();
|
||||
|
@ -9,6 +9,8 @@ import com.fsck.k9.controller.MessageRetrievalListener;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.*;
|
||||
|
||||
import com.fsck.k9.mail.filter.Base64;
|
||||
import com.fsck.k9.mail.filter.Hex;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
import com.fsck.k9.net.ssl.TrustManagerFactory;
|
||||
import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||
@ -16,11 +18,16 @@ import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
@ -33,21 +40,11 @@ import java.util.Set;
|
||||
public class Pop3Store extends Store {
|
||||
public static final String STORE_TYPE = "POP3";
|
||||
|
||||
public static final int CONNECTION_SECURITY_NONE = 0;
|
||||
public static final int CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
||||
public static final int CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
||||
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
||||
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
||||
|
||||
private enum AuthType {
|
||||
PLAIN,
|
||||
CRAM_MD5
|
||||
}
|
||||
|
||||
private static final String STLS_COMMAND = "STLS";
|
||||
private static final String USER_COMMAND = "USER";
|
||||
private static final String PASS_COMMAND = "PASS";
|
||||
private static final String CAPA_COMMAND = "CAPA";
|
||||
private static final String AUTH_COMMAND = "AUTH";
|
||||
private static final String STAT_COMMAND = "STAT";
|
||||
private static final String LIST_COMMAND = "LIST";
|
||||
private static final String UIDL_COMMAND = "UIDL";
|
||||
@ -58,20 +55,19 @@ public class Pop3Store extends Store {
|
||||
|
||||
private static final String STLS_CAPABILITY = "STLS";
|
||||
private static final String UIDL_CAPABILITY = "UIDL";
|
||||
private static final String PIPELINING_CAPABILITY = "PIPELINING";
|
||||
private static final String USER_CAPABILITY = "USER";
|
||||
private static final String TOP_CAPABILITY = "TOP";
|
||||
private static final String SASL_CAPABILITY = "SASL";
|
||||
private static final String AUTH_PLAIN_CAPABILITY = "PLAIN";
|
||||
private static final String AUTH_CRAM_MD5_CAPABILITY = "CRAM-MD5";
|
||||
|
||||
/**
|
||||
* Decodes a Pop3Store URI.
|
||||
*
|
||||
* <p>Possible forms:</p>
|
||||
* <pre>
|
||||
* pop3://user:password@server:port CONNECTION_SECURITY_NONE
|
||||
* pop3+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
* pop3+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
||||
* pop3+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
||||
* pop3+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
||||
* pop3://user:password@server:port ConnectionSecurity.NONE
|
||||
* pop3+tls+://user:password@server:port ConnectionSecurity.STARTTLS_REQUIRED
|
||||
* pop3+ssl+://user:password@server:port ConnectionSecurity.SSL_TLS_REQUIRED
|
||||
* </pre>
|
||||
*/
|
||||
public static ServerSettings decodeUri(String uri) {
|
||||
@ -89,21 +85,27 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
String scheme = pop3Uri.getScheme();
|
||||
/*
|
||||
* Currently available schemes are:
|
||||
* pop3
|
||||
* pop3+tls+
|
||||
* pop3+ssl+
|
||||
*
|
||||
* The following are obsolete schemes that may be found in pre-existing
|
||||
* settings from earlier versions or that may be found when imported. We
|
||||
* continue to recognize them and re-map them appropriately:
|
||||
* pop3+tls
|
||||
* pop3+ssl
|
||||
*/
|
||||
if (scheme.equals("pop3")) {
|
||||
connectionSecurity = ConnectionSecurity.NONE;
|
||||
port = 110;
|
||||
} else if (scheme.equals("pop3+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_OPTIONAL;
|
||||
port = 110;
|
||||
} else if (scheme.equals("pop3+tls+")) {
|
||||
} else if (scheme.startsWith("pop3+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED;
|
||||
port = 110;
|
||||
} else if (scheme.equals("pop3+ssl+")) {
|
||||
} else if (scheme.startsWith("pop3+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
|
||||
port = 995;
|
||||
} else if (scheme.equals("pop3+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_OPTIONAL;
|
||||
port = 995;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
|
||||
}
|
||||
@ -114,7 +116,7 @@ public class Pop3Store extends Store {
|
||||
port = pop3Uri.getPort();
|
||||
}
|
||||
|
||||
String authType = AuthType.PLAIN.name();
|
||||
AuthType authType = AuthType.PLAIN;
|
||||
if (pop3Uri.getUserInfo() != null) {
|
||||
try {
|
||||
int userIndex = 0, passwordIndex = 1;
|
||||
@ -125,7 +127,7 @@ public class Pop3Store extends Store {
|
||||
// after an account was imported (so authType and username are present).
|
||||
userIndex++;
|
||||
passwordIndex++;
|
||||
authType = userInfoParts[0];
|
||||
authType = AuthType.valueOf(userInfoParts[0]);
|
||||
}
|
||||
username = URLDecoder.decode(userInfoParts[userIndex], "UTF-8");
|
||||
if (userInfoParts.length > passwordIndex) {
|
||||
@ -166,15 +168,9 @@ public class Pop3Store extends Store {
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
case SSL_TLS_OPTIONAL:
|
||||
scheme = "pop3+ssl";
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
scheme = "pop3+ssl+";
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
scheme = "pop3+tls";
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
scheme = "pop3+tls+";
|
||||
break;
|
||||
@ -184,14 +180,7 @@ public class Pop3Store extends Store {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
AuthType.valueOf(server.authenticationType);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Invalid authentication type (" +
|
||||
server.authenticationType + ")");
|
||||
}
|
||||
|
||||
String userInfo = server.authenticationType + ":" + userEnc + ":" + passwordEnc;
|
||||
String userInfo = server.authenticationType.name() + ":" + userEnc + ":" + passwordEnc;
|
||||
try {
|
||||
return new URI(scheme, userInfo, server.host, server.port, null, null,
|
||||
null).toString();
|
||||
@ -206,7 +195,7 @@ public class Pop3Store extends Store {
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
private AuthType mAuthType;
|
||||
private int mConnectionSecurity;
|
||||
private ConnectionSecurity mConnectionSecurity;
|
||||
private HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
|
||||
private Pop3Capabilities mCapabilities;
|
||||
|
||||
@ -231,27 +220,11 @@ public class Pop3Store extends Store {
|
||||
mHost = settings.host;
|
||||
mPort = settings.port;
|
||||
|
||||
switch (settings.connectionSecurity) {
|
||||
case NONE:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_NONE;
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
break;
|
||||
case SSL_TLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
break;
|
||||
}
|
||||
mConnectionSecurity = settings.connectionSecurity;
|
||||
|
||||
mUsername = settings.username;
|
||||
mPassword = settings.password;
|
||||
mAuthType = AuthType.valueOf(settings.authenticationType);
|
||||
mAuthType = settings.authenticationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -327,13 +300,11 @@ public class Pop3Store extends Store {
|
||||
|
||||
try {
|
||||
SocketAddress socketAddress = new InetSocketAddress(mHost, mPort);
|
||||
if (mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
|
||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||
if (mConnectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
final boolean secure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
sslContext.init(null,
|
||||
new TrustManager[] { TrustManagerFactory.get(mHost,
|
||||
mPort, secure) }, new SecureRandom());
|
||||
mPort) }, new SecureRandom());
|
||||
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
||||
} else {
|
||||
mSocket = new Socket();
|
||||
@ -348,21 +319,18 @@ public class Pop3Store extends Store {
|
||||
throw new MessagingException("Unable to connect socket");
|
||||
}
|
||||
|
||||
// Eat the banner
|
||||
executeSimpleCommand(null);
|
||||
String serverGreeting = executeSimpleCommand(null);
|
||||
|
||||
if (mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
|| mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED) {
|
||||
mCapabilities = getCapabilities();
|
||||
mCapabilities = getCapabilities();
|
||||
if (mConnectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) {
|
||||
|
||||
if (mCapabilities.stls) {
|
||||
executeSimpleCommand(STLS_COMMAND);
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
sslContext.init(null,
|
||||
new TrustManager[] { TrustManagerFactory.get(
|
||||
mHost, mPort, secure) },
|
||||
mHost, mPort) },
|
||||
new SecureRandom());
|
||||
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket, mHost,
|
||||
mPort, true);
|
||||
@ -372,31 +340,42 @@ public class Pop3Store extends Store {
|
||||
if (!isOpen()) {
|
||||
throw new MessagingException("Unable to connect socket");
|
||||
}
|
||||
} else if (mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED) {
|
||||
throw new MessagingException("TLS not supported but required");
|
||||
mCapabilities = getCapabilities();
|
||||
} else {
|
||||
/*
|
||||
* This exception triggers a "Certificate error"
|
||||
* notification that takes the user to the incoming
|
||||
* server settings for review. This might be needed if
|
||||
* the account was configured with an obsolete
|
||||
* "STARTTLS (if available)" setting.
|
||||
*/
|
||||
throw new CertificateValidationException(
|
||||
"STARTTLS connection security not available",
|
||||
new CertificateException());
|
||||
}
|
||||
}
|
||||
|
||||
if (mAuthType == AuthType.CRAM_MD5) {
|
||||
try {
|
||||
String b64Nonce = executeSimpleCommand("AUTH CRAM-MD5").replace("+ ", "");
|
||||
|
||||
String b64CRAM = Authentication.computeCramMd5(mUsername, mPassword, b64Nonce);
|
||||
executeSimpleCommand(b64CRAM);
|
||||
|
||||
} catch (MessagingException me) {
|
||||
throw new AuthenticationFailedException(null, me);
|
||||
switch (mAuthType) {
|
||||
case PLAIN:
|
||||
if (mCapabilities.authPlain) {
|
||||
authPlain();
|
||||
} else {
|
||||
login();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
executeSimpleCommand(USER_COMMAND + " " + mUsername);
|
||||
executeSimpleCommand(PASS_COMMAND + " " + mPassword, true);
|
||||
} catch (MessagingException me) {
|
||||
throw new AuthenticationFailedException(null, me);
|
||||
break;
|
||||
|
||||
case CRAM_MD5:
|
||||
if (mCapabilities.cramMD5) {
|
||||
authCramMD5();
|
||||
} else {
|
||||
authAPOP(serverGreeting);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new MessagingException(
|
||||
"Unhandled authentication method found in the server settings (bug).");
|
||||
}
|
||||
|
||||
mCapabilities = getCapabilities();
|
||||
} catch (SSLException e) {
|
||||
throw new CertificateValidationException(e.getMessage(), e);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
@ -415,6 +394,67 @@ public class Pop3Store extends Store {
|
||||
mUidToMsgNumMap.clear();
|
||||
}
|
||||
|
||||
private void login() throws MessagingException {
|
||||
executeSimpleCommand(USER_COMMAND + " " + mUsername);
|
||||
try {
|
||||
executeSimpleCommand(PASS_COMMAND + " " + mPassword, true);
|
||||
} catch (Pop3ErrorResponse e) {
|
||||
throw new AuthenticationFailedException(
|
||||
"POP3 login authentication failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void authPlain() throws MessagingException {
|
||||
executeSimpleCommand("AUTH PLAIN");
|
||||
try {
|
||||
byte[] encodedBytes = Base64.encodeBase64(("\000" + mUsername
|
||||
+ "\000" + mPassword).getBytes());
|
||||
executeSimpleCommand(new String(encodedBytes), true);
|
||||
} catch (Pop3ErrorResponse e) {
|
||||
throw new AuthenticationFailedException(
|
||||
"POP3 SASL auth PLAIN authentication failed: "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void authAPOP(String serverGreeting) throws MessagingException {
|
||||
// regex based on RFC 2449 (3.) "Greeting"
|
||||
String timestamp = serverGreeting.replaceFirst(
|
||||
"^\\+OK *(?:\\[[^\\]]+\\])?[^<]*(<[^>]*>)?[^<]*$", "$1");
|
||||
if ("".equals(timestamp)) {
|
||||
throw new MessagingException(
|
||||
"APOP authentication is not supported");
|
||||
}
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new MessagingException(
|
||||
"MD5 failure during POP3 auth APOP", e);
|
||||
}
|
||||
byte[] digest = md.digest((timestamp + mPassword).getBytes());
|
||||
String hexDigest = new String(Hex.encodeHex(digest));
|
||||
try {
|
||||
executeSimpleCommand("APOP " + mUsername + " " + hexDigest, true);
|
||||
} catch (Pop3ErrorResponse e) {
|
||||
throw new AuthenticationFailedException(
|
||||
"POP3 APOP authentication failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void authCramMD5() throws MessagingException {
|
||||
String b64Nonce = executeSimpleCommand("AUTH CRAM-MD5").replace("+ ", "");
|
||||
|
||||
String b64CRAM = Authentication.computeCramMd5(mUsername, mPassword, b64Nonce);
|
||||
try {
|
||||
executeSimpleCommand(b64CRAM, true);
|
||||
} catch (Pop3ErrorResponse e) {
|
||||
throw new AuthenticationFailedException(
|
||||
"POP3 CRAM-MD5 authentication failed: "
|
||||
+ e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return (mIn != null && mOut != null && mSocket != null
|
||||
@ -984,22 +1024,54 @@ public class Pop3Store extends Store {
|
||||
|
||||
private Pop3Capabilities getCapabilities() throws IOException {
|
||||
Pop3Capabilities capabilities = new Pop3Capabilities();
|
||||
try {
|
||||
/*
|
||||
* Try sending an AUTH command with no arguments.
|
||||
*
|
||||
* The server may respond with a list of supported SASL
|
||||
* authentication mechanisms.
|
||||
*
|
||||
* Ref.: http://tools.ietf.org/html/draft-myers-sasl-pop3-05
|
||||
*
|
||||
* While this never became a standard, there are servers that
|
||||
* support it, and Thunderbird includes this check.
|
||||
*/
|
||||
String response = executeSimpleCommand(AUTH_COMMAND);
|
||||
while ((response = readLine()) != null) {
|
||||
if (response.equals(".")) {
|
||||
break;
|
||||
}
|
||||
response = response.toUpperCase(Locale.US);
|
||||
if (response.equals(AUTH_PLAIN_CAPABILITY)) {
|
||||
capabilities.authPlain = true;
|
||||
} else if (response.equals(AUTH_CRAM_MD5_CAPABILITY)) {
|
||||
capabilities.cramMD5 = true;
|
||||
}
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
// Assume AUTH command with no arguments is not supported.
|
||||
}
|
||||
try {
|
||||
String response = executeSimpleCommand(CAPA_COMMAND);
|
||||
while ((response = readLine()) != null) {
|
||||
if (response.equals(".")) {
|
||||
break;
|
||||
}
|
||||
if (response.equalsIgnoreCase(STLS_CAPABILITY)) {
|
||||
response = response.toUpperCase(Locale.US);
|
||||
if (response.equals(STLS_CAPABILITY)) {
|
||||
capabilities.stls = true;
|
||||
} else if (response.equalsIgnoreCase(UIDL_CAPABILITY)) {
|
||||
} else if (response.equals(UIDL_CAPABILITY)) {
|
||||
capabilities.uidl = true;
|
||||
} else if (response.equalsIgnoreCase(PIPELINING_CAPABILITY)) {
|
||||
capabilities.pipelining = true;
|
||||
} else if (response.equalsIgnoreCase(USER_CAPABILITY)) {
|
||||
capabilities.user = true;
|
||||
} else if (response.equalsIgnoreCase(TOP_CAPABILITY)) {
|
||||
} else if (response.equals(TOP_CAPABILITY)) {
|
||||
capabilities.top = true;
|
||||
} else if (response.startsWith(SASL_CAPABILITY)) {
|
||||
List<String> saslAuthMechanisms = Arrays.asList(response.split(" "));
|
||||
if (saslAuthMechanisms.contains(AUTH_PLAIN_CAPABILITY)) {
|
||||
capabilities.authPlain = true;
|
||||
}
|
||||
if (saslAuthMechanisms.contains(AUTH_CRAM_MD5_CAPABILITY)) {
|
||||
capabilities.cramMD5 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1116,20 +1188,20 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
static class Pop3Capabilities {
|
||||
public boolean cramMD5;
|
||||
public boolean authPlain;
|
||||
public boolean stls;
|
||||
public boolean top;
|
||||
public boolean user;
|
||||
public boolean uidl;
|
||||
public boolean pipelining;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("STLS %b, TOP %b, USER %b, UIDL %b, PIPELINING %b",
|
||||
return String.format("CRAM-MD5 %b, PLAIN %b, STLS %b, TOP %b, UIDL %b",
|
||||
cramMD5,
|
||||
authPlain,
|
||||
stls,
|
||||
top,
|
||||
user,
|
||||
uidl,
|
||||
pipelining);
|
||||
uidl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ public class WebDavSocketFactory implements LayeredSocketFactory {
|
||||
public WebDavSocketFactory(String host, int port, boolean secure) throws NoSuchAlgorithmException, KeyManagementException {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, new TrustManager[] {
|
||||
TrustManagerFactory.get(host, port, secure)
|
||||
TrustManagerFactory.get(host, port)
|
||||
}, new SecureRandom());
|
||||
mSocketFactory = sslContext.getSocketFactory();
|
||||
mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
|
||||
|
@ -57,13 +57,6 @@ import java.util.zip.GZIPInputStream;
|
||||
public class WebDavStore extends Store {
|
||||
public static final String STORE_TYPE = "WebDAV";
|
||||
|
||||
// Security options
|
||||
private static final short CONNECTION_SECURITY_NONE = 0;
|
||||
private static final short CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
||||
private static final short CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
||||
private static final short CONNECTION_SECURITY_SSL_OPTIONAL = 3;
|
||||
private static final short CONNECTION_SECURITY_SSL_REQUIRED = 4;
|
||||
|
||||
// Authentication types
|
||||
private static final short AUTH_TYPE_NONE = 0;
|
||||
private static final short AUTH_TYPE_BASIC = 1;
|
||||
@ -89,11 +82,8 @@ public class WebDavStore extends Store {
|
||||
*
|
||||
* <p>Possible forms:</p>
|
||||
* <pre>
|
||||
* webdav://user:password@server:port CONNECTION_SECURITY_NONE
|
||||
* webdav+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
* webdav+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
||||
* webdav+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
||||
* webdav+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
||||
* webdav://user:password@server:port ConnectionSecurity.NONE
|
||||
* webdav+ssl+://user:password@server:port ConnectionSecurity.SSL_TLS_REQUIRED
|
||||
* </pre>
|
||||
*/
|
||||
public static WebDavStoreSettings decodeUri(String uri) {
|
||||
@ -116,16 +106,22 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
String scheme = webDavUri.getScheme();
|
||||
/*
|
||||
* Currently available schemes are:
|
||||
* webdav
|
||||
* webdav+ssl+
|
||||
*
|
||||
* The following are obsolete schemes that may be found in pre-existing
|
||||
* settings from earlier versions or that may be found when imported. We
|
||||
* continue to recognize them and re-map them appropriately:
|
||||
* webdav+tls
|
||||
* webdav+tls+
|
||||
* webdav+ssl
|
||||
*/
|
||||
if (scheme.equals("webdav")) {
|
||||
connectionSecurity = ConnectionSecurity.NONE;
|
||||
} else if (scheme.equals("webdav+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_OPTIONAL;
|
||||
} else if (scheme.equals("webdav+ssl+")) {
|
||||
} else if (scheme.startsWith("webdav+")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
|
||||
} else if (scheme.equals("webdav+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_OPTIONAL;
|
||||
} else if (scheme.equals("webdav+tls+")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
|
||||
}
|
||||
@ -210,18 +206,9 @@ public class WebDavStore extends Store {
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
case SSL_TLS_OPTIONAL:
|
||||
scheme = "webdav+ssl";
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
scheme = "webdav+ssl+";
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
scheme = "webdav+tls";
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
scheme = "webdav+tls+";
|
||||
break;
|
||||
default:
|
||||
case NONE:
|
||||
scheme = "webdav";
|
||||
@ -270,7 +257,7 @@ public class WebDavStore extends Store {
|
||||
public final String mailboxPath;
|
||||
|
||||
protected WebDavStoreSettings(String host, int port, ConnectionSecurity connectionSecurity,
|
||||
String authenticationType, String username, String password, String alias,
|
||||
AuthType authenticationType, String username, String password, String alias,
|
||||
String path, String authPath, String mailboxPath) {
|
||||
super(STORE_TYPE, host, port, connectionSecurity, authenticationType, username,
|
||||
password);
|
||||
@ -298,7 +285,7 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
|
||||
private short mConnectionSecurity;
|
||||
private ConnectionSecurity mConnectionSecurity;
|
||||
private String mUsername; /* Stores the username for authentications */
|
||||
private String mAlias; /* Stores the alias for the user's mailbox */
|
||||
private String mPassword; /* Stores the password for authentications */
|
||||
@ -334,23 +321,7 @@ public class WebDavStore extends Store {
|
||||
mHost = settings.host;
|
||||
mPort = settings.port;
|
||||
|
||||
switch (settings.connectionSecurity) {
|
||||
case NONE:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_NONE;
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
break;
|
||||
case SSL_TLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
break;
|
||||
}
|
||||
mConnectionSecurity = settings.connectionSecurity;
|
||||
|
||||
mUsername = settings.username;
|
||||
mPassword = settings.password;
|
||||
@ -383,16 +354,13 @@ public class WebDavStore extends Store {
|
||||
// The inbox path would look like: "https://mail.domain.com/Exchange/alias/Inbox".
|
||||
mUrl = getRoot() + mPath + mMailboxPath;
|
||||
|
||||
mSecure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
mSecure = mConnectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED;
|
||||
mAuthString = "Basic " + Utility.base64Encode(mUsername + ":" + mPassword);
|
||||
}
|
||||
|
||||
private String getRoot() {
|
||||
String root;
|
||||
if (mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED ||
|
||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
|
||||
mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL ||
|
||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||
if (mConnectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
|
||||
root = "https";
|
||||
} else {
|
||||
root = "http";
|
||||
|
@ -19,6 +19,7 @@ import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -27,44 +28,28 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.net.*;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SmtpTransport extends Transport {
|
||||
public static final String TRANSPORT_TYPE = "SMTP";
|
||||
|
||||
public static final int CONNECTION_SECURITY_NONE = 0;
|
||||
public static final int CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
||||
public static final int CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
||||
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
||||
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
||||
|
||||
public static final String AUTH_PLAIN = "PLAIN";
|
||||
|
||||
public static final String AUTH_CRAM_MD5 = "CRAM_MD5";
|
||||
|
||||
public static final String AUTH_LOGIN = "LOGIN";
|
||||
|
||||
public static final String AUTH_AUTOMATIC = "AUTOMATIC";
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a SmtpTransport URI.
|
||||
*
|
||||
* <p>Possible forms:</p>
|
||||
* <pre>
|
||||
* smtp://user:password@server:port CONNECTION_SECURITY_NONE
|
||||
* smtp+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
* smtp+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
||||
* smtp+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
||||
* smtp+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
||||
* smtp://user:password@server:port ConnectionSecurity.NONE
|
||||
* smtp+tls+://user:password@server:port ConnectionSecurity.STARTTLS_REQUIRED
|
||||
* smtp+ssl+://user:password@server:port ConnectionSecurity.SSL_TLS_REQUIRED
|
||||
* </pre>
|
||||
*/
|
||||
public static ServerSettings decodeUri(String uri) {
|
||||
String host;
|
||||
int port;
|
||||
ConnectionSecurity connectionSecurity;
|
||||
String authenticationType = AUTH_AUTOMATIC;
|
||||
AuthType authType = AuthType.PLAIN;
|
||||
String username = null;
|
||||
String password = null;
|
||||
|
||||
@ -76,21 +61,27 @@ public class SmtpTransport extends Transport {
|
||||
}
|
||||
|
||||
String scheme = smtpUri.getScheme();
|
||||
/*
|
||||
* Currently available schemes are:
|
||||
* smtp
|
||||
* smtp+tls+
|
||||
* smtp+ssl+
|
||||
*
|
||||
* The following are obsolete schemes that may be found in pre-existing
|
||||
* settings from earlier versions or that may be found when imported. We
|
||||
* continue to recognize them and re-map them appropriately:
|
||||
* smtp+tls
|
||||
* smtp+ssl
|
||||
*/
|
||||
if (scheme.equals("smtp")) {
|
||||
connectionSecurity = ConnectionSecurity.NONE;
|
||||
port = 587;
|
||||
} else if (scheme.equals("smtp+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_OPTIONAL;
|
||||
port = 587;
|
||||
} else if (scheme.equals("smtp+tls+")) {
|
||||
} else if (scheme.startsWith("smtp+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED;
|
||||
port = 587;
|
||||
} else if (scheme.equals("smtp+ssl+")) {
|
||||
} else if (scheme.startsWith("smtp+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
|
||||
port = 465;
|
||||
} else if (scheme.equals("smtp+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_OPTIONAL;
|
||||
port = 465;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
|
||||
}
|
||||
@ -109,7 +100,7 @@ public class SmtpTransport extends Transport {
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
}
|
||||
if (userInfoParts.length > 2) {
|
||||
authenticationType = userInfoParts[2];
|
||||
authType = AuthType.valueOf(userInfoParts[2]);
|
||||
}
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
@ -118,7 +109,7 @@ public class SmtpTransport extends Transport {
|
||||
}
|
||||
|
||||
return new ServerSettings(TRANSPORT_TYPE, host, port, connectionSecurity,
|
||||
authenticationType, username, password);
|
||||
authType, username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,15 +138,9 @@ public class SmtpTransport extends Transport {
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
case SSL_TLS_OPTIONAL:
|
||||
scheme = "smtp+ssl";
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
scheme = "smtp+ssl+";
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
scheme = "smtp+tls";
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
scheme = "smtp+tls+";
|
||||
break;
|
||||
@ -165,15 +150,11 @@ public class SmtpTransport extends Transport {
|
||||
break;
|
||||
}
|
||||
|
||||
String authType = server.authenticationType;
|
||||
if (!(AUTH_AUTOMATIC.equals(authType) ||
|
||||
AUTH_LOGIN.equals(authType) ||
|
||||
AUTH_PLAIN.equals(authType) ||
|
||||
AUTH_CRAM_MD5.equals(authType))) {
|
||||
throw new IllegalArgumentException("Invalid authentication type: " + authType);
|
||||
String userInfo = userEnc + ":" + passwordEnc;
|
||||
AuthType authType = server.authenticationType;
|
||||
if (authType != null) {
|
||||
userInfo += ":" + authType.name();
|
||||
}
|
||||
|
||||
String userInfo = userEnc + ":" + passwordEnc + ":" + authType;
|
||||
try {
|
||||
return new URI(scheme, userInfo, server.host, server.port, null, null,
|
||||
null).toString();
|
||||
@ -187,8 +168,8 @@ public class SmtpTransport extends Transport {
|
||||
int mPort;
|
||||
String mUsername;
|
||||
String mPassword;
|
||||
String mAuthType;
|
||||
int mConnectionSecurity;
|
||||
AuthType mAuthType;
|
||||
ConnectionSecurity mConnectionSecurity;
|
||||
Socket mSocket;
|
||||
PeekableInputStream mIn;
|
||||
OutputStream mOut;
|
||||
@ -206,23 +187,7 @@ public class SmtpTransport extends Transport {
|
||||
mHost = settings.host;
|
||||
mPort = settings.port;
|
||||
|
||||
switch (settings.connectionSecurity) {
|
||||
case NONE:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_NONE;
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
break;
|
||||
case SSL_TLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
break;
|
||||
}
|
||||
mConnectionSecurity = settings.connectionSecurity;
|
||||
|
||||
mAuthType = settings.authenticationType;
|
||||
mUsername = settings.username;
|
||||
@ -232,20 +197,20 @@ public class SmtpTransport extends Transport {
|
||||
@Override
|
||||
public void open() throws MessagingException {
|
||||
try {
|
||||
boolean secureConnection = false;
|
||||
InetAddress[] addresses = InetAddress.getAllByName(mHost);
|
||||
for (int i = 0; i < addresses.length; i++) {
|
||||
try {
|
||||
SocketAddress socketAddress = new InetSocketAddress(addresses[i], mPort);
|
||||
if (mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED ||
|
||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||
if (mConnectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
sslContext.init(null,
|
||||
new TrustManager[] { TrustManagerFactory.get(
|
||||
mHost, mPort, secure) },
|
||||
mHost, mPort) },
|
||||
new SecureRandom());
|
||||
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
||||
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
||||
secureConnection = true;
|
||||
} else {
|
||||
mSocket = new Socket();
|
||||
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
||||
@ -264,7 +229,7 @@ public class SmtpTransport extends Transport {
|
||||
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
|
||||
|
||||
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(), 1024));
|
||||
mOut = mSocket.getOutputStream();
|
||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
|
||||
|
||||
// Eat the banner
|
||||
executeSimpleCommand(null);
|
||||
@ -288,104 +253,128 @@ public class SmtpTransport extends Transport {
|
||||
}
|
||||
}
|
||||
|
||||
List<String> results = sendHello(localHost);
|
||||
HashMap<String,String> extensions = sendHello(localHost);
|
||||
|
||||
m8bitEncodingAllowed = results.contains("8BITMIME");
|
||||
m8bitEncodingAllowed = extensions.containsKey("8BITMIME");
|
||||
|
||||
|
||||
if (mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
|| mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED) {
|
||||
if (results.contains("STARTTLS")) {
|
||||
if (mConnectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) {
|
||||
if (extensions.containsKey("STARTTLS")) {
|
||||
executeSimpleCommand("STARTTLS");
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
sslContext.init(null,
|
||||
new TrustManager[] { TrustManagerFactory.get(mHost,
|
||||
mPort, secure) }, new SecureRandom());
|
||||
mPort) }, new SecureRandom());
|
||||
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket, mHost,
|
||||
mPort, true);
|
||||
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
|
||||
1024));
|
||||
mOut = mSocket.getOutputStream();
|
||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
|
||||
/*
|
||||
* Now resend the EHLO. Required by RFC2487 Sec. 5.2, and more specifically,
|
||||
* Exim.
|
||||
*/
|
||||
results = sendHello(localHost);
|
||||
} else if (mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED) {
|
||||
throw new MessagingException("TLS not supported but required");
|
||||
extensions = sendHello(localHost);
|
||||
secureConnection = true;
|
||||
} else {
|
||||
/*
|
||||
* This exception triggers a "Certificate error"
|
||||
* notification that takes the user to the incoming
|
||||
* server settings for review. This might be needed if
|
||||
* the account was configured with an obsolete
|
||||
* "STARTTLS (if available)" setting.
|
||||
*/
|
||||
throw new CertificateValidationException(
|
||||
"STARTTLS connection security not available",
|
||||
new CertificateException());
|
||||
}
|
||||
}
|
||||
|
||||
boolean useAuthLogin = AUTH_LOGIN.equals(mAuthType);
|
||||
boolean useAuthPlain = AUTH_PLAIN.equals(mAuthType);
|
||||
boolean useAuthCramMD5 = AUTH_CRAM_MD5.equals(mAuthType);
|
||||
|
||||
// Automatically choose best authentication method if none was explicitly selected
|
||||
boolean useAutomaticAuth = !(useAuthLogin || useAuthPlain || useAuthCramMD5);
|
||||
|
||||
boolean authLoginSupported = false;
|
||||
boolean authPlainSupported = false;
|
||||
boolean authCramMD5Supported = false;
|
||||
for (String result : results) {
|
||||
if (result.matches(".*AUTH.*LOGIN.*$")) {
|
||||
authLoginSupported = true;
|
||||
}
|
||||
if (result.matches(".*AUTH.*PLAIN.*$")) {
|
||||
authPlainSupported = true;
|
||||
}
|
||||
if (result.matches(".*AUTH.*CRAM-MD5.*$")) {
|
||||
authCramMD5Supported = true;
|
||||
}
|
||||
if (result.matches(".*SIZE \\d*$")) {
|
||||
try {
|
||||
mLargestAcceptableMessage = Integer.parseInt(result.substring(result.lastIndexOf(' ') + 1));
|
||||
} catch (Exception e) {
|
||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
|
||||
Log.d(K9.LOG_TAG, "Tried to parse " + result + " and get an int out of the last word", e);
|
||||
}
|
||||
if (extensions.containsKey("AUTH")) {
|
||||
List<String> saslMech = Arrays.asList(extensions.get("AUTH").split(" "));
|
||||
authLoginSupported = saslMech.contains("LOGIN");
|
||||
authPlainSupported = saslMech.contains("PLAIN");
|
||||
authCramMD5Supported = saslMech.contains("CRAM-MD5");
|
||||
}
|
||||
if (extensions.containsKey("SIZE")) {
|
||||
try {
|
||||
mLargestAcceptableMessage = Integer.parseInt(extensions.get("SIZE"));
|
||||
} catch (Exception e) {
|
||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
|
||||
Log.d(K9.LOG_TAG, "Tried to parse " + extensions.get("SIZE") + " and get an int", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mUsername != null && mUsername.length() > 0 &&
|
||||
mPassword != null && mPassword.length() > 0) {
|
||||
if (useAuthCramMD5 || (useAutomaticAuth && authCramMD5Supported)) {
|
||||
if (!authCramMD5Supported && K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
|
||||
Log.d(K9.LOG_TAG, "Using CRAM_MD5 as authentication method although the " +
|
||||
"server didn't advertise support for it in EHLO response.");
|
||||
}
|
||||
saslAuthCramMD5(mUsername, mPassword);
|
||||
} else if (useAuthPlain || (useAutomaticAuth && authPlainSupported)) {
|
||||
if (!authPlainSupported && K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
|
||||
Log.d(K9.LOG_TAG, "Using PLAIN as authentication method although the " +
|
||||
"server didn't advertise support for it in EHLO response.");
|
||||
}
|
||||
try {
|
||||
|
||||
switch (mAuthType) {
|
||||
|
||||
/*
|
||||
* LOGIN is an obsolete option which is unavailable to users,
|
||||
* but it still may exist in a user's settings from a previous
|
||||
* version, or it may have been imported.
|
||||
*/
|
||||
case LOGIN:
|
||||
case PLAIN:
|
||||
// try saslAuthPlain first, because it supports UTF-8 explicitly
|
||||
if (authPlainSupported) {
|
||||
saslAuthPlain(mUsername, mPassword);
|
||||
} catch (MessagingException ex) {
|
||||
// PLAIN is a special case. Historically, PLAIN has represented both PLAIN and LOGIN; only the
|
||||
// protocol being advertised by the server would be used, with PLAIN taking precedence. Instead
|
||||
// of using only the requested protocol, we'll try PLAIN and then try LOGIN.
|
||||
if (useAuthPlain && authLoginSupported) {
|
||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
|
||||
Log.d(K9.LOG_TAG, "Using legacy PLAIN authentication behavior and trying LOGIN.");
|
||||
}
|
||||
} else if (authLoginSupported) {
|
||||
saslAuthLogin(mUsername, mPassword);
|
||||
} else {
|
||||
throw new MessagingException("Authentication methods SASL PLAIN and LOGIN are unavailable.");
|
||||
}
|
||||
break;
|
||||
|
||||
case CRAM_MD5:
|
||||
if (authCramMD5Supported) {
|
||||
saslAuthCramMD5(mUsername, mPassword);
|
||||
} else {
|
||||
throw new MessagingException("Authentication method CRAM-MD5 is unavailable.");
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* AUTOMATIC is an obsolete option which is unavailable to users,
|
||||
* but it still may exist in a user's settings from a previous
|
||||
* version, or it may have been imported.
|
||||
*/
|
||||
case AUTOMATIC:
|
||||
if (secureConnection) {
|
||||
// try saslAuthPlain first, because it supports UTF-8 explicitly
|
||||
if (authPlainSupported) {
|
||||
saslAuthPlain(mUsername, mPassword);
|
||||
} else if (authLoginSupported) {
|
||||
saslAuthLogin(mUsername, mPassword);
|
||||
} else if (authCramMD5Supported) {
|
||||
saslAuthCramMD5(mUsername, mPassword);
|
||||
} else {
|
||||
// If it was auto detected and failed, continue throwing the exception back up.
|
||||
throw ex;
|
||||
throw new MessagingException("No supported authentication methods available.");
|
||||
}
|
||||
} else {
|
||||
if (authCramMD5Supported) {
|
||||
saslAuthCramMD5(mUsername, mPassword);
|
||||
} else {
|
||||
/*
|
||||
* We refuse to insecurely transmit the password
|
||||
* using the obsolete AUTOMATIC setting because of
|
||||
* the potential for a MITM attack. Affected users
|
||||
* must choose a different setting.
|
||||
*/
|
||||
throw new MessagingException(
|
||||
"Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable.");
|
||||
}
|
||||
}
|
||||
} else if (useAuthLogin || (useAutomaticAuth && authLoginSupported)) {
|
||||
if (!authPlainSupported && K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
|
||||
Log.d(K9.LOG_TAG, "Using LOGIN as authentication method although the " +
|
||||
"server didn't advertise support for it in EHLO response.");
|
||||
}
|
||||
saslAuthLogin(mUsername, mPassword);
|
||||
} else {
|
||||
throw new MessagingException("No valid authentication mechanism found.");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new MessagingException("Unhandled authentication method found in the server settings (bug).");
|
||||
}
|
||||
}
|
||||
} catch (SSLException e) {
|
||||
@ -410,19 +399,24 @@ public class SmtpTransport extends Transport {
|
||||
* @param host
|
||||
* The EHLO/HELO parameter as defined by the RFC.
|
||||
*
|
||||
* @return The list of capabilities as returned by the EHLO command or an empty list.
|
||||
* @return A (possibly empty) {@code HashMap<String,String>} of extensions (upper case) and
|
||||
* their parameters (possibly 0 length) as returned by the EHLO command
|
||||
*
|
||||
* @throws IOException
|
||||
* In case of a network error.
|
||||
* @throws MessagingException
|
||||
* In case of a malformed response.
|
||||
*/
|
||||
private List<String> sendHello(String host) throws IOException, MessagingException {
|
||||
private HashMap<String,String> sendHello(String host) throws IOException, MessagingException {
|
||||
HashMap<String, String> extensions = new HashMap<String, String>();
|
||||
try {
|
||||
//TODO: We currently assume the extension keywords returned by the server are always
|
||||
// uppercased. But the RFC allows mixed-case keywords!
|
||||
|
||||
return executeSimpleCommand("EHLO " + host);
|
||||
List<String> results = executeSimpleCommand("EHLO " + host);
|
||||
// Remove the EHLO greeting response
|
||||
results.remove(0);
|
||||
for (String result : results) {
|
||||
String[] pair = result.split(" ", 2);
|
||||
extensions.put(pair[0].toUpperCase(Locale.US), pair.length == 1 ? "" : pair[1]);
|
||||
}
|
||||
} catch (NegativeSmtpReplyException e) {
|
||||
if (K9.DEBUG) {
|
||||
Log.v(K9.LOG_TAG, "Server doesn't support the EHLO command. Trying HELO...");
|
||||
@ -434,8 +428,7 @@ public class SmtpTransport extends Transport {
|
||||
Log.w(K9.LOG_TAG, "Server doesn't support the HELO command. Continuing anyway.");
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<String>(0);
|
||||
return extensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -500,8 +493,7 @@ public class SmtpTransport extends Transport {
|
||||
executeSimpleCommand("DATA");
|
||||
|
||||
EOLConvertingOutputStream msgOut = new EOLConvertingOutputStream(
|
||||
new LineWrapOutputStream(new SmtpDataStuffing(
|
||||
new BufferedOutputStream(mOut, 1024)), 1000));
|
||||
new LineWrapOutputStream(new SmtpDataStuffing(mOut), 1000));
|
||||
|
||||
message.writeTo(msgOut);
|
||||
|
||||
@ -714,7 +706,7 @@ public class SmtpTransport extends Transport {
|
||||
|
||||
List<String> respList = executeSimpleCommand("AUTH CRAM-MD5");
|
||||
if (respList.size() != 1) {
|
||||
throw new AuthenticationFailedException("Unable to negotiate CRAM-MD5");
|
||||
throw new MessagingException("Unable to negotiate CRAM-MD5");
|
||||
}
|
||||
|
||||
String b64Nonce = respList.get(0);
|
||||
@ -722,8 +714,8 @@ public class SmtpTransport extends Transport {
|
||||
|
||||
try {
|
||||
executeSimpleCommand(b64CRAMString, true);
|
||||
} catch (MessagingException me) {
|
||||
throw new AuthenticationFailedException("Unable to negotiate MD5 CRAM");
|
||||
} catch (NegativeSmtpReplyException exception) {
|
||||
throw new AuthenticationFailedException(exception.getMessage(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
package com.fsck.k9.mail.transport.imap;
|
||||
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.ConnectionSecurity;
|
||||
import com.fsck.k9.mail.store.ImapStore;
|
||||
import com.fsck.k9.mail.store.ImapStore.AuthType;
|
||||
import com.fsck.k9.mail.store.ImapStore.ImapConnection;
|
||||
|
||||
/**
|
||||
@ -12,7 +13,7 @@ public interface ImapSettings {
|
||||
|
||||
int getPort();
|
||||
|
||||
int getConnectionSecurity();
|
||||
ConnectionSecurity getConnectionSecurity();
|
||||
|
||||
AuthType getAuthType();
|
||||
|
||||
|
@ -21,23 +21,9 @@ public final class TrustManagerFactory {
|
||||
private static final String LOG_TAG = "TrustManagerFactory";
|
||||
|
||||
private static X509TrustManager defaultTrustManager;
|
||||
private static X509TrustManager unsecureTrustManager;
|
||||
|
||||
private static LocalKeyStore keyStore;
|
||||
|
||||
private static class SimpleX509TrustManager implements X509TrustManager {
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SecureX509TrustManager implements X509TrustManager {
|
||||
private static final Map<String, SecureX509TrustManager> mTrustManager =
|
||||
@ -126,14 +112,12 @@ public final class TrustManagerFactory {
|
||||
} catch (KeyStoreException e) {
|
||||
Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
|
||||
}
|
||||
unsecureTrustManager = new SimpleX509TrustManager();
|
||||
}
|
||||
|
||||
private TrustManagerFactory() {
|
||||
}
|
||||
|
||||
public static X509TrustManager get(String host, int port, boolean secure) {
|
||||
return secure ? SecureX509TrustManager.getInstance(host, port) :
|
||||
unsecureTrustManager;
|
||||
public static X509TrustManager get(String host, int port) {
|
||||
return SecureX509TrustManager.getInstance(host, port);
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ public class SettingsExporter {
|
||||
writeElement(serializer, PORT_ELEMENT, Integer.toString(incoming.port));
|
||||
}
|
||||
writeElement(serializer, CONNECTION_SECURITY_ELEMENT, incoming.connectionSecurity.name());
|
||||
writeElement(serializer, AUTHENTICATION_TYPE_ELEMENT, incoming.authenticationType);
|
||||
writeElement(serializer, AUTHENTICATION_TYPE_ELEMENT, incoming.authenticationType.name());
|
||||
writeElement(serializer, USERNAME_ELEMENT, incoming.username);
|
||||
// XXX For now we don't export the password
|
||||
//writeElement(serializer, PASSWORD_ELEMENT, incoming.password);
|
||||
@ -257,7 +257,7 @@ public class SettingsExporter {
|
||||
writeElement(serializer, PORT_ELEMENT, Integer.toString(outgoing.port));
|
||||
}
|
||||
writeElement(serializer, CONNECTION_SECURITY_ELEMENT, outgoing.connectionSecurity.name());
|
||||
writeElement(serializer, AUTHENTICATION_TYPE_ELEMENT, outgoing.authenticationType);
|
||||
writeElement(serializer, AUTHENTICATION_TYPE_ELEMENT, outgoing.authenticationType.name());
|
||||
writeElement(serializer, USERNAME_ELEMENT, outgoing.username);
|
||||
// XXX For now we don't export the password
|
||||
//writeElement(serializer, PASSWORD_ELEMENT, outgoing.password);
|
||||
|
@ -23,6 +23,7 @@ import com.fsck.k9.Identity;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.ConnectionSecurity;
|
||||
import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Store;
|
||||
@ -971,7 +972,8 @@ public class SettingsImporter {
|
||||
} else if (SettingsExporter.CONNECTION_SECURITY_ELEMENT.equals(element)) {
|
||||
server.connectionSecurity = getText(xpp);
|
||||
} else if (SettingsExporter.AUTHENTICATION_TYPE_ELEMENT.equals(element)) {
|
||||
server.authenticationType = getText(xpp);
|
||||
String text = getText(xpp);
|
||||
server.authenticationType = AuthType.valueOf(text);
|
||||
} else if (SettingsExporter.USERNAME_ELEMENT.equals(element)) {
|
||||
server.username = getText(xpp);
|
||||
} else if (SettingsExporter.PASSWORD_ELEMENT.equals(element)) {
|
||||
@ -1108,6 +1110,16 @@ public class SettingsImporter {
|
||||
|
||||
private static ConnectionSecurity convertConnectionSecurity(String connectionSecurity) {
|
||||
try {
|
||||
/*
|
||||
* TODO:
|
||||
* Add proper settings validation and upgrade capability for server settings.
|
||||
* Once that exists, move this code into a SettingsUpgrader.
|
||||
*/
|
||||
if ("SSL_TLS_OPTIONAL".equals(connectionSecurity)) {
|
||||
return ConnectionSecurity.SSL_TLS_REQUIRED;
|
||||
} else if ("STARTTLS_OPTIONAL".equals(connectionSecurity)) {
|
||||
return ConnectionSecurity.STARTTLS_REQUIRED;
|
||||
}
|
||||
return ConnectionSecurity.valueOf(connectionSecurity);
|
||||
} catch (Exception e) {
|
||||
return ConnectionSecurity.NONE;
|
||||
@ -1140,7 +1152,7 @@ public class SettingsImporter {
|
||||
public String host;
|
||||
public String port;
|
||||
public String connectionSecurity;
|
||||
public String authenticationType;
|
||||
public AuthType authenticationType;
|
||||
public String username;
|
||||
public String password;
|
||||
public ImportedSettings extras;
|
||||
|
@ -2,6 +2,8 @@ package com.fsck.k9.mail.store;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.ConnectionSecurity;
|
||||
import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Store;
|
||||
@ -13,7 +15,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
String uri = "imap://PLAIN:user:pass@server:143/0%7CcustomPathPrefix";
|
||||
ServerSettings settings = Store.decodeStoreUri(uri);
|
||||
|
||||
assertEquals("PLAIN", settings.authenticationType);
|
||||
assertEquals(AuthType.PLAIN, settings.authenticationType);
|
||||
assertEquals("user", settings.username);
|
||||
assertEquals("pass", settings.password);
|
||||
assertEquals("server", settings.host);
|
||||
@ -26,7 +28,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
String uri = "imap://PLAIN:user:pass@server:143/";
|
||||
ServerSettings settings = Store.decodeStoreUri(uri);
|
||||
|
||||
assertEquals("PLAIN", settings.authenticationType);
|
||||
assertEquals(AuthType.PLAIN, settings.authenticationType);
|
||||
assertEquals("user", settings.username);
|
||||
assertEquals("pass", settings.password);
|
||||
assertEquals("server", settings.host);
|
||||
@ -38,7 +40,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
String uri = "imap://PLAIN:user:pass@server:143/customPathPrefix";
|
||||
ServerSettings settings = Store.decodeStoreUri(uri);
|
||||
|
||||
assertEquals("PLAIN", settings.authenticationType);
|
||||
assertEquals(AuthType.PLAIN, settings.authenticationType);
|
||||
assertEquals("user", settings.username);
|
||||
assertEquals("pass", settings.password);
|
||||
assertEquals("server", settings.host);
|
||||
@ -51,7 +53,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
String uri = "imap://PLAIN:user:pass@server:143/0%7C";
|
||||
ServerSettings settings = Store.decodeStoreUri(uri);
|
||||
|
||||
assertEquals("PLAIN", settings.authenticationType);
|
||||
assertEquals(AuthType.PLAIN, settings.authenticationType);
|
||||
assertEquals("user", settings.username);
|
||||
assertEquals("pass", settings.password);
|
||||
assertEquals("server", settings.host);
|
||||
@ -64,7 +66,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
String uri = "imap://PLAIN:user:pass@server:143/1%7CcustomPathPrefix";
|
||||
ServerSettings settings = Store.decodeStoreUri(uri);
|
||||
|
||||
assertEquals("PLAIN", settings.authenticationType);
|
||||
assertEquals(AuthType.PLAIN, settings.authenticationType);
|
||||
assertEquals("user", settings.username);
|
||||
assertEquals("pass", settings.password);
|
||||
assertEquals("server", settings.host);
|
||||
@ -80,7 +82,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
extra.put("pathPrefix", "customPathPrefix");
|
||||
|
||||
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
|
||||
ConnectionSecurity.NONE, "PLAIN", "user", "pass", extra);
|
||||
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", extra);
|
||||
|
||||
String uri = Store.createStoreUri(settings);
|
||||
|
||||
@ -93,7 +95,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
extra.put("pathPrefix", "");
|
||||
|
||||
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
|
||||
ConnectionSecurity.NONE, "PLAIN", "user", "pass", extra);
|
||||
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", extra);
|
||||
|
||||
String uri = Store.createStoreUri(settings);
|
||||
|
||||
@ -102,7 +104,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
|
||||
public void testCreateStoreUriImapNoExtra() {
|
||||
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
|
||||
ConnectionSecurity.NONE, "PLAIN", "user", "pass");
|
||||
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass");
|
||||
|
||||
String uri = Store.createStoreUri(settings);
|
||||
|
||||
@ -114,7 +116,7 @@ public class ImapStoreUriTest extends TestCase {
|
||||
extra.put("autoDetectNamespace", "true");
|
||||
|
||||
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
|
||||
ConnectionSecurity.NONE, "PLAIN", "user", "pass", extra);
|
||||
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", extra);
|
||||
|
||||
String uri = Store.createStoreUri(settings);
|
||||
|
||||
|
@ -214,27 +214,27 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
|
||||
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
|
||||
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2);
|
||||
|
||||
X509TrustManager trustManager1 = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager2 = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT2, true);
|
||||
X509TrustManager trustManager1 = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
|
||||
X509TrustManager trustManager2 = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT2);
|
||||
trustManager2.checkServerTrusted(new X509Certificate[] { mCert2 }, "authType");
|
||||
trustManager1.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
|
||||
}
|
||||
|
||||
public void testSelfSignedCertificateMatchingHost() throws Exception {
|
||||
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
|
||||
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
|
||||
}
|
||||
|
||||
public void testSelfSignedCertificateNotMatchingHost() throws Exception {
|
||||
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
|
||||
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
|
||||
}
|
||||
|
||||
public void testWrongCertificate() throws Exception {
|
||||
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
|
||||
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
|
||||
}
|
||||
|
||||
@ -242,44 +242,44 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
|
||||
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||
mKeyStore.addCertificate(MATCHING_HOST, PORT2, mCert2);
|
||||
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
|
||||
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
|
||||
}
|
||||
|
||||
public void testUntrustedCertificateChain() throws Exception {
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
|
||||
assertCertificateRejection(trustManager, new X509Certificate[] { mCert3, mCaCert });
|
||||
}
|
||||
|
||||
public void testLocallyTrustedCertificateChain() throws Exception {
|
||||
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert3);
|
||||
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
|
||||
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
|
||||
}
|
||||
|
||||
public void testLocallyTrustedCertificateChainNotMatchingHost() throws Exception {
|
||||
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert3);
|
||||
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
|
||||
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
|
||||
}
|
||||
|
||||
public void testGloballyTrustedCertificateChain() throws Exception {
|
||||
X509TrustManager trustManager = TrustManagerFactory.get("www.linux.com", PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get("www.linux.com", PORT1);
|
||||
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mStarfieldCert };
|
||||
trustManager.checkServerTrusted(certificates, "authType");
|
||||
}
|
||||
|
||||
public void testGloballyTrustedCertificateNotMatchingHost() throws Exception {
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
|
||||
assertCertificateRejection(trustManager, new X509Certificate[] { mLinuxComCert, mStarfieldCert });
|
||||
}
|
||||
|
||||
public void testGloballyTrustedCertificateNotMatchingHostOverride() throws Exception {
|
||||
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mLinuxComCert);
|
||||
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
|
||||
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mStarfieldCert };
|
||||
trustManager.checkServerTrusted(certificates, "authType");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user