Merge branch 'pr/453'

Authentication changes

 message to explain why this merge is necessary,
This commit is contained in:
cketti 2014-03-05 06:19:55 +01:00
commit ff5edf43d4
48 changed files with 887 additions and 861 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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";
/**

View File

@ -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) {

View File

@ -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());

View File

@ -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) {

View File

@ -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

View 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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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");
}