diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index ef762dd54041e5ca58f8bd17f8d15415243f5a41..9592db5454b05bb4b7435443364635f65b5210a7 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -3,7 +3,7 @@
If the app is not behaving like it should, it's not necessarily a bug. Please consult the following resources before
submitting a new issue.
-* [User Manual](https://k9mail.app/documentation/)
+* [User Manual](https://docs.k9mail.app/)
* [Frequently Asked Questions](https://forum.k9mail.app/c/faq)
* [Support Forum](https://forum.k9mail.app/)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index eea3360e3268f894b36fb0620bd8a2cf5022af07..5823e398a512e21c3e5fdcc28652586d92bc217e 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,2 +1 @@
-liberapay: k9mail
-github: cketti
+custom: "https://mzla.link/k9-give"
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index a746ed32f7cd59898e0963aec13b70253747dbc0..5b59297a1824b6d8652c9a580b5af005926a37cc 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -57,7 +57,7 @@ body:
id: expected
attributes:
label: Expected behavior
- description: After following the steps, what did you think K-9 Mail would do?
+ description: After following the steps, what did you think Mail would do?
validations:
required: true
- type: textarea
diff --git a/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/KoinModule.kt b/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/KoinModule.kt
index 839e8dc42c0bfd3166a29845ae7f7ef88ee4db0f..11ee30e24f78670e531ea40ff9bfc0fd70364896 100644
--- a/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/KoinModule.kt
+++ b/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/KoinModule.kt
@@ -4,5 +4,5 @@ import org.koin.dsl.module
val autodiscoveryProvidersXmlModule = module {
factory { ProvidersXmlProvider(context = get()) }
- factory { ProvidersXmlDiscovery(xmlProvider = get()) }
+ factory { ProvidersXmlDiscovery(xmlProvider = get(), oAuthConfigurationProvider = get()) }
}
diff --git a/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscovery.kt b/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscovery.kt
index 23764d24103f5164000ad115978e337e863a0b8c..b2dd7bd2194b92bbdb7b2886a09f373a0dbc5292 100644
--- a/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscovery.kt
+++ b/app/autodiscovery/providersxml/src/main/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscovery.kt
@@ -9,14 +9,14 @@ import com.fsck.k9.autodiscovery.api.DiscoveryTarget
import com.fsck.k9.helper.EmailHelper
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ConnectionSecurity
-import com.fsck.k9.mail.oauth.OAuth2Provider
+import com.fsck.k9.oauth.OAuthConfigurationProvider
import com.fsck.k9.preferences.Protocols
-import java.net.URI
import org.xmlpull.v1.XmlPullParser
import timber.log.Timber
class ProvidersXmlDiscovery(
- private val xmlProvider: ProvidersXmlProvider
+ private val xmlProvider: ProvidersXmlProvider,
+ private val oAuthConfigurationProvider: OAuthConfigurationProvider
) : ConnectionSettingsDiscovery {
override fun discover(email: String, target: DiscoveryTarget): DiscoveryResults? {
@@ -92,20 +92,13 @@ class ProvidersXmlDiscovery(
val username = incomingUsernameTemplate.fillInUsernameTemplate(email, user, domain)
- val xoauth2 = OAuth2Provider.isXOAuth2(domain)
- val xoauth2Label = if (xoauth2) AuthType.XOAUTH2.name else ""
- val xoauth2Colon = if (xoauth2) ":" else ""
-
val security = when {
incomingUriTemplate.startsWith("imap+ssl") -> ConnectionSecurity.SSL_TLS_REQUIRED
incomingUriTemplate.startsWith("imap+tls") -> ConnectionSecurity.STARTTLS_REQUIRED
else -> error("Connection security required")
}
- val incomingUri = with(URI(incomingUriTemplate)) {
- URI(scheme, "$xoauth2Label$xoauth2Colon$username", host, port, null, null, null).toString()
- }
- val uri = Uri.parse(incomingUri)
+ val uri = Uri.parse(incomingUriTemplate)
val host = uri.host ?: error("Host name required")
val port = if (uri.port == -1) {
if (security == ConnectionSecurity.STARTTLS_REQUIRED) 143 else 993
@@ -113,19 +106,19 @@ class ProvidersXmlDiscovery(
uri.port
}
- val authType = if (xoauth2) AuthType.XOAUTH2 else AuthType.PLAIN
+ val authType = if (oAuthConfigurationProvider.getConfiguration(host) != null) {
+ AuthType.XOAUTH2
+ } else {
+ AuthType.PLAIN
+ }
+
return DiscoveredServerSettings(Protocols.IMAP, host, port, security, authType, username)
}
private fun Provider.toOutgoingServerSettings(email: String): DiscoveredServerSettings? {
-
val user = EmailHelper.getLocalPartFromEmailAddress(email) ?: return null
val domain = EmailHelper.getDomainFromEmailAddress(email) ?: return null
- val xoauth2 = OAuth2Provider.isXOAuth2(domain)
- val xoauth2Label = if (xoauth2) AuthType.XOAUTH2.name else ""
- val xoauth2Colon = if (xoauth2) ":" else ""
-
val username = outgoingUsernameTemplate.fillInUsernameTemplate(email, user, domain)
val security = when {
@@ -134,11 +127,7 @@ class ProvidersXmlDiscovery(
else -> error("Connection security required")
}
- val outgoingUri = with(URI(outgoingUriTemplate)) {
- URI(scheme, "$username$xoauth2Colon$xoauth2Label", host, port, null, null, null).toString()
- }
-
- val uri = Uri.parse(outgoingUri)
+ val uri = Uri.parse(outgoingUriTemplate)
val host = uri.host ?: error("Host name required")
val port = if (uri.port == -1) {
if (security == ConnectionSecurity.STARTTLS_REQUIRED) 587 else 465
@@ -146,7 +135,12 @@ class ProvidersXmlDiscovery(
uri.port
}
- val authType = if (xoauth2) AuthType.XOAUTH2 else AuthType.PLAIN
+ val authType = if (oAuthConfigurationProvider.getConfiguration(host) != null) {
+ AuthType.XOAUTH2
+ } else {
+ AuthType.PLAIN
+ }
+
return DiscoveredServerSettings(Protocols.SMTP, host, port, security, authType, username)
}
diff --git a/app/autodiscovery/providersxml/src/main/res/xml/providers.xml b/app/autodiscovery/providersxml/src/main/res/xml/providers.xml
index 48cc76093683ca5912c02f81c9dbf9c61baf3244..2b75500327d5405943efbef906f449db869f8e92 100644
--- a/app/autodiscovery/providersxml/src/main/res/xml/providers.xml
+++ b/app/autodiscovery/providersxml/src/main/res/xml/providers.xml
@@ -254,10 +254,6 @@
-
-
-
-
@@ -717,4 +713,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/autodiscovery/providersxml/src/test/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscoveryTest.kt b/app/autodiscovery/providersxml/src/test/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscoveryTest.kt
index 5f36f322391e4ae21cca418245947e0262e01adc..4e7d3f329e63388c252178e3466f0f30e90619a6 100644
--- a/app/autodiscovery/providersxml/src/test/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscoveryTest.kt
+++ b/app/autodiscovery/providersxml/src/test/java/com/fsck/k9/autodiscovery/providersxml/ProvidersXmlDiscoveryTest.kt
@@ -5,12 +5,15 @@ import com.fsck.k9.RobolectricTest
import com.fsck.k9.autodiscovery.api.DiscoveryTarget
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ConnectionSecurity
+import com.fsck.k9.oauth.OAuthConfiguration
+import com.fsck.k9.oauth.OAuthConfigurationProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class ProvidersXmlDiscoveryTest : RobolectricTest() {
private val xmlProvider = ProvidersXmlProvider(ApplicationProvider.getApplicationContext())
- private val providersXmlDiscovery = ProvidersXmlDiscovery(xmlProvider)
+ private val oAuthConfigurationProvider = createOAuthConfigurationProvider()
+ private val providersXmlDiscovery = ProvidersXmlDiscovery(xmlProvider, oAuthConfigurationProvider)
@Test
fun discover_withGmailDomain_shouldReturnCorrectSettings() {
@@ -39,4 +42,21 @@ class ProvidersXmlDiscoveryTest : RobolectricTest() {
assertThat(connectionSettings).isNull()
}
+
+ private fun createOAuthConfigurationProvider(): OAuthConfigurationProvider {
+ val googleConfig = OAuthConfiguration(
+ clientId = "irrelevant",
+ scopes = listOf("irrelevant"),
+ authorizationEndpoint = "irrelevant",
+ tokenEndpoint = "irrelevant",
+ redirectUri = "irrelevant"
+ )
+
+ return OAuthConfigurationProvider(
+ configurations = mapOf(
+ listOf("imap.gmail.com", "smtp.gmail.com") to googleConfig,
+ ),
+ googleConfiguration = googleConfig
+ )
+ }
}
diff --git a/app/core/src/main/java/com/fsck/k9/Account.kt b/app/core/src/main/java/com/fsck/k9/Account.kt
index c1e66a281915b85e8c0c3651f0037f17cf2a81a5..454007a79d33dd59a4d53ddf069b7ff6480f9f0c 100644
--- a/app/core/src/main/java/com/fsck/k9/Account.kt
+++ b/app/core/src/main/java/com/fsck/k9/Account.kt
@@ -2,11 +2,9 @@ package com.fsck.k9
import com.fsck.k9.backend.api.SyncConfig.ExpungePolicy
import com.fsck.k9.mail.Address
-import com.fsck.k9.mail.NetworkType
import com.fsck.k9.mail.ServerSettings
import java.util.Calendar
import java.util.Date
-import java.util.concurrent.ConcurrentHashMap
/**
* Account stores all of the settings for a single account defined by the user. Each account is defined by a UUID.
@@ -36,6 +34,10 @@ class Account(override val uuid: String) : BaseAccount {
internalOutgoingServerSettings = value
}
+ @get:Synchronized
+ @set:Synchronized
+ var oAuthState: String? = null
+
/**
* Storage provider ID, used to locate and manage the underlying DB/file storage.
*/
@@ -225,7 +227,10 @@ class Account(override val uuid: String) : BaseAccount {
@set:Synchronized
var idleRefreshMinutes = 0
- private val compressionMap: MutableMap = ConcurrentHashMap()
+ @get:JvmName("useCompression")
+ @get:Synchronized
+ @set:Synchronized
+ var useCompression = true
@get:Synchronized
@set:Synchronized
@@ -500,20 +505,6 @@ class Account(override val uuid: String) : BaseAccount {
this.sortAscending[sortType] = sortAscending
}
- @Synchronized
- fun setCompression(networkType: NetworkType, useCompression: Boolean) {
- compressionMap[networkType] = useCompression
- }
-
- @Synchronized
- fun useCompression(networkType: NetworkType): Boolean {
- return compressionMap[networkType] ?: return true
- }
-
- fun getCompressionMap(): Map {
- return compressionMap.toMap()
- }
-
@Synchronized
fun replaceIdentities(identities: List) {
this.identities = identities.toMutableList()
diff --git a/app/core/src/main/java/com/fsck/k9/AccountPreferenceSerializer.kt b/app/core/src/main/java/com/fsck/k9/AccountPreferenceSerializer.kt
index 7fe5ea654a4ebcd07ae12ba88508ee981e0593b5..810753e7965e823c0c64915d4c1d5d6a46f13ac5 100644
--- a/app/core/src/main/java/com/fsck/k9/AccountPreferenceSerializer.kt
+++ b/app/core/src/main/java/com/fsck/k9/AccountPreferenceSerializer.kt
@@ -15,7 +15,6 @@ import com.fsck.k9.Account.ShowPictures
import com.fsck.k9.Account.SortType
import com.fsck.k9.Account.SpecialFolderSelection
import com.fsck.k9.helper.Utility
-import com.fsck.k9.mail.NetworkType
import com.fsck.k9.mailstore.StorageManager
import com.fsck.k9.preferences.Storage
import com.fsck.k9.preferences.StorageEditor
@@ -37,6 +36,7 @@ class AccountPreferenceSerializer(
outgoingServerSettings = serverSettingsSerializer.deserialize(
storage.getString("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY", "")
)
+ oAuthState = storage.getString("$accountUuid.oAuthState", null)
localStorageProviderId = storage.getString("$accountUuid.localStorageProvider", storageManager.defaultProviderId)
name = storage.getString("$accountUuid.description", null)
alwaysBcc = storage.getString("$accountUuid.alwaysBcc", alwaysBcc)
@@ -120,10 +120,7 @@ class AccountPreferenceSerializer(
isDefaultQuotedTextShown = storage.getBoolean("$accountUuid.defaultQuotedTextShown", DEFAULT_QUOTED_TEXT_SHOWN)
isReplyAfterQuote = storage.getBoolean("$accountUuid.replyAfterQuote", DEFAULT_REPLY_AFTER_QUOTE)
isStripSignature = storage.getBoolean("$accountUuid.stripSignature", DEFAULT_STRIP_SIGNATURE)
- for (type in NetworkType.values()) {
- val useCompression = storage.getBoolean("$accountUuid.useCompression.$type", true)
- setCompression(type, useCompression)
- }
+ useCompression = storage.getBoolean("$accountUuid.useCompression", true)
importedAutoExpandFolder = storage.getString("$accountUuid.autoExpandFolderName", null)
@@ -244,6 +241,7 @@ class AccountPreferenceSerializer(
with(account) {
editor.putString("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY", serverSettingsSerializer.serialize(incomingServerSettings))
editor.putString("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY", serverSettingsSerializer.serialize(outgoingServerSettings))
+ editor.putString("$accountUuid.oAuthState", oAuthState)
editor.putString("$accountUuid.localStorageProvider", localStorageProviderId)
editor.putString("$accountUuid.description", name)
editor.putString("$accountUuid.alwaysBcc", alwaysBcc)
@@ -334,13 +332,7 @@ class AccountPreferenceSerializer(
editor.putLong("$accountUuid.lastFolderListRefreshTime", lastFolderListRefreshTime)
editor.putBoolean("$accountUuid.isFinishedSetup", isFinishedSetup)
- val compressionMap = getCompressionMap()
- for (type in NetworkType.values()) {
- val useCompression = compressionMap[type]
- if (useCompression != null) {
- editor.putBoolean("$accountUuid.useCompression.$type", useCompression)
- }
- }
+ editor.putBoolean("$accountUuid.useCompression", useCompression)
}
saveIdentities(account, storage, editor)
@@ -369,6 +361,7 @@ class AccountPreferenceSerializer(
editor.remove("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY")
editor.remove("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY")
+ editor.remove("$accountUuid.oAuthState")
editor.remove("$accountUuid.description")
editor.remove("$accountUuid.name")
editor.remove("$accountUuid.email")
@@ -456,10 +449,8 @@ class AccountPreferenceSerializer(
editor.remove("$accountUuid.lastSyncTime")
editor.remove("$accountUuid.lastFolderListRefreshTime")
editor.remove("$accountUuid.isFinishedSetup")
+ editor.remove("$accountUuid.useCompression")
- for (type in NetworkType.values()) {
- editor.remove("$accountUuid.useCompression." + type.name)
- }
deleteIdentities(account, storage, editor)
// TODO: Remove preference settings that may exist for individual folders in the account.
}
diff --git a/app/core/src/main/java/com/fsck/k9/K9.kt b/app/core/src/main/java/com/fsck/k9/K9.kt
index ba824857c362402b076fdeda1cc6a97ddb0e6b5a..b0abc49e9a79211026b1a941703733da9333127d 100644
--- a/app/core/src/main/java/com/fsck/k9/K9.kt
+++ b/app/core/src/main/java/com/fsck/k9/K9.kt
@@ -301,6 +301,7 @@ object K9 : EarlyInit {
override fun debugSensitive(): Boolean = isSensitiveDebugLoggingEnabled
})
+ com.fsck.k9.logging.Timber.logger = TimberLogger()
checkCachedDatabaseVersion(context)
diff --git a/app/core/src/main/java/com/fsck/k9/KoinModule.kt b/app/core/src/main/java/com/fsck/k9/KoinModule.kt
index 05c30287cc1146b04577fcce4ad85c4829eafb72..85cacac5b97cbe63f83790bf0b5a4d6e12fc9bba 100644
--- a/app/core/src/main/java/com/fsck/k9/KoinModule.kt
+++ b/app/core/src/main/java/com/fsck/k9/KoinModule.kt
@@ -2,7 +2,7 @@ package com.fsck.k9
import android.content.Context
import com.fsck.k9.helper.Contacts
-import com.fsck.k9.mail.ssl.DefaultTrustedSocketFactory
+import com.fsck.k9.helper.DefaultTrustedSocketFactory
import com.fsck.k9.mail.ssl.LocalKeyStore
import com.fsck.k9.mail.ssl.TrustManagerFactory
import com.fsck.k9.mail.ssl.TrustedSocketFactory
diff --git a/app/core/src/main/java/com/fsck/k9/Preferences.kt b/app/core/src/main/java/com/fsck/k9/Preferences.kt
index f7eaab83d92a56e823589467f1bcdea0aeb6e700..eca0f09fb1a7f1621f077ddef1d2eb4a94188ce7 100644
--- a/app/core/src/main/java/com/fsck/k9/Preferences.kt
+++ b/app/core/src/main/java/com/fsck/k9/Preferences.kt
@@ -201,7 +201,7 @@ class Preferences internal constructor(
val defaultAccount: Account?
get() = accounts.firstOrNull()
- fun saveAccount(account: Account) {
+ override fun saveAccount(account: Account) {
ensureAssignedAccountNumber(account)
processChangedValues(account)
diff --git a/app/core/src/main/java/com/fsck/k9/TimberLogger.kt b/app/core/src/main/java/com/fsck/k9/TimberLogger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..80c1af8c50d47c9631578f82fc1c5f31bf2ecf99
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/TimberLogger.kt
@@ -0,0 +1,121 @@
+package com.fsck.k9
+
+import android.os.Build
+import com.fsck.k9.logging.Logger
+import java.util.regex.Pattern
+import timber.log.Timber
+
+class TimberLogger : Logger {
+ override fun v(message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.v(message, *args)
+ }
+
+ override fun v(t: Throwable?, message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.v(t, message, *args)
+ }
+
+ override fun v(t: Throwable?) {
+ setTimberTag()
+ Timber.v(t)
+ }
+
+ override fun d(message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.d(message, *args)
+ }
+
+ override fun d(t: Throwable?, message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.d(t, message, *args)
+ }
+
+ override fun d(t: Throwable?) {
+ setTimberTag()
+ Timber.d(t)
+ }
+
+ override fun i(message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.i(message, *args)
+ }
+
+ override fun i(t: Throwable?, message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.i(t, message, *args)
+ }
+
+ override fun i(t: Throwable?) {
+ setTimberTag()
+ Timber.i(t)
+ }
+
+ override fun w(message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.w(message, *args)
+ }
+
+ override fun w(t: Throwable?, message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.w(t, message, *args)
+ }
+
+ override fun w(t: Throwable?) {
+ setTimberTag()
+ Timber.w(t)
+ }
+
+ override fun e(message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.e(message, *args)
+ }
+
+ override fun e(t: Throwable?, message: String?, vararg args: Any?) {
+ setTimberTag()
+ Timber.e(t, message, *args)
+ }
+
+ override fun e(t: Throwable?) {
+ setTimberTag()
+ Timber.e(t)
+ }
+
+ private fun setTimberTag() {
+ val tag = Throwable().stackTrace
+ .first { it.className !in IGNORE_CLASSES }
+ .let(::createStackElementTag)
+
+ // We explicitly set a tag, otherwise Timber will always derive the tag "TimberLogger".
+ Timber.tag(tag)
+ }
+
+ private fun createStackElementTag(element: StackTraceElement): String {
+ var tag = element.className.substringAfterLast('.')
+ val matcher = ANONYMOUS_CLASS.matcher(tag)
+ if (matcher.find()) {
+ tag = matcher.replaceAll("")
+ }
+
+ // Tag length limit was removed in API 26.
+ return if (tag.length <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= 26) {
+ tag
+ } else {
+ tag.substring(0, MAX_TAG_LENGTH)
+ }
+ }
+
+ companion object {
+ private const val MAX_TAG_LENGTH = 23
+ private val ANONYMOUS_CLASS = Pattern.compile("(\\$\\d+)+$")
+
+ private val IGNORE_CLASSES = setOf(
+ Timber::class.java.name,
+ Timber.Forest::class.java.name,
+ Timber.Tree::class.java.name,
+ Timber.DebugTree::class.java.name,
+ TimberLogger::class.java.name,
+ com.fsck.k9.logging.Timber::class.java.name
+ )
+ }
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/ssl/DefaultTrustedSocketFactory.java b/app/core/src/main/java/com/fsck/k9/helper/DefaultTrustedSocketFactory.java
similarity index 97%
rename from mail/common/src/main/java/com/fsck/k9/mail/ssl/DefaultTrustedSocketFactory.java
rename to app/core/src/main/java/com/fsck/k9/helper/DefaultTrustedSocketFactory.java
index 4c9857e024a4f5eebae5be2edae1dd62137700f5..ce046daa9e1e5a93f251f341b6ae99b192f9c93b 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/ssl/DefaultTrustedSocketFactory.java
+++ b/app/core/src/main/java/com/fsck/k9/helper/DefaultTrustedSocketFactory.java
@@ -1,4 +1,4 @@
-package com.fsck.k9.mail.ssl;
+package com.fsck.k9.helper;
import java.io.IOException;
@@ -14,6 +14,8 @@ import android.net.SSLCertificateSocketFactory;
import android.text.TextUtils;
import com.fsck.k9.mail.MessagingException;
+import com.fsck.k9.mail.ssl.TrustManagerFactory;
+import com.fsck.k9.mail.ssl.TrustedSocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/ssl/KeyChainKeyManager.java b/app/core/src/main/java/com/fsck/k9/helper/KeyChainKeyManager.java
similarity index 99%
rename from mail/common/src/main/java/com/fsck/k9/mail/ssl/KeyChainKeyManager.java
rename to app/core/src/main/java/com/fsck/k9/helper/KeyChainKeyManager.java
index bfd90c196c9960b58861787dcd3f160d26b4756b..77e9fc4f834ac2460a97d142920237b2b68f636b 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/ssl/KeyChainKeyManager.java
+++ b/app/core/src/main/java/com/fsck/k9/helper/KeyChainKeyManager.java
@@ -1,5 +1,5 @@
-package com.fsck.k9.mail.ssl;
+package com.fsck.k9.helper;
import java.net.Socket;
import java.security.Principal;
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/ListHeaders.java b/app/core/src/main/java/com/fsck/k9/helper/ListHeaders.java
similarity index 96%
rename from mail/common/src/main/java/com/fsck/k9/mail/internet/ListHeaders.java
rename to app/core/src/main/java/com/fsck/k9/helper/ListHeaders.java
index 2bed0bafcc4885c6ef3b5963c6f398119c1dc381..52f16846078c0a4c4abc0c7eae3c2bd393d00af1 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/internet/ListHeaders.java
+++ b/app/core/src/main/java/com/fsck/k9/helper/ListHeaders.java
@@ -1,4 +1,4 @@
-package com.fsck.k9.mail.internet;
+package com.fsck.k9.helper;
import android.net.Uri;
@@ -8,7 +8,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import com.fsck.k9.helper.MailTo;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Message;
diff --git a/app/core/src/main/java/com/fsck/k9/helper/ListUnsubscribeHelper.kt b/app/core/src/main/java/com/fsck/k9/helper/ListUnsubscribeHelper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..08090265372531485ba941b1414b8a84986acc3e
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/helper/ListUnsubscribeHelper.kt
@@ -0,0 +1,55 @@
+package com.fsck.k9.helper
+
+import android.net.Uri
+import com.fsck.k9.mail.Message
+import java.util.regex.Pattern
+
+object ListUnsubscribeHelper {
+ private const val LIST_UNSUBSCRIBE_HEADER = "List-Unsubscribe"
+ private val MAILTO_CONTAINER_PATTERN = Pattern.compile("<(mailto:.+?)>")
+ private val HTTPS_CONTAINER_PATTERN = Pattern.compile("<(https:.+?)>")
+
+ // As K-9 Mail is an email client, we prefer a mailto: unsubscribe method
+ // but if none is found, a https URL is acceptable too
+ fun getPreferredListUnsubscribeUri(message: Message): UnsubscribeUri? {
+ val headerValues = message.getHeader(LIST_UNSUBSCRIBE_HEADER)
+ if (headerValues.isEmpty()) {
+ return null
+ }
+ val listUnsubscribeUris = mutableListOf()
+ for (headerValue in headerValues) {
+ val uri = extractUri(headerValue) ?: continue
+
+ if (uri.scheme == "mailto") {
+ return MailtoUnsubscribeUri(uri)
+ }
+
+ // If we got here it must be HTTPS
+ listUnsubscribeUris.add(uri)
+ }
+
+ if (listUnsubscribeUris.isNotEmpty()) {
+ return HttpsUnsubscribeUri(listUnsubscribeUris[0])
+ }
+
+ return null
+ }
+
+ private fun extractUri(headerValue: String?): Uri? {
+ if (headerValue == null || headerValue.isEmpty()) {
+ return null
+ }
+
+ var matcher = MAILTO_CONTAINER_PATTERN.matcher(headerValue)
+ if (matcher.find()) {
+ return Uri.parse(matcher.group(1))
+ }
+
+ matcher = HTTPS_CONTAINER_PATTERN.matcher(headerValue)
+ if (matcher.find()) {
+ return Uri.parse(matcher.group(1))
+ }
+
+ return null
+ }
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/helper/MailTo.java b/app/core/src/main/java/com/fsck/k9/helper/MailTo.java
similarity index 100%
rename from mail/common/src/main/java/com/fsck/k9/helper/MailTo.java
rename to app/core/src/main/java/com/fsck/k9/helper/MailTo.java
diff --git a/app/core/src/main/java/com/fsck/k9/helper/MimeTypeUtil.java b/app/core/src/main/java/com/fsck/k9/helper/MimeTypeUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..57266fa000fa4b750a789e9e38f06b31f8aacced
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/helper/MimeTypeUtil.java
@@ -0,0 +1,925 @@
+package com.fsck.k9.helper;
+
+
+import java.util.Locale;
+
+import org.jetbrains.annotations.NotNull;
+
+
+public class MimeTypeUtil {
+ public static final String DEFAULT_ATTACHMENT_MIME_TYPE = "application/octet-stream";
+ public static final String K9_SETTINGS_MIME_TYPE = "application/x-k9settings";
+
+ /*
+ * http://www.w3schools.com/media/media_mimeref.asp
+ * +
+ * http://www.stdicon.com/mimetypes
+ */
+ static final String[][] MIME_TYPE_BY_EXTENSION_MAP = new String[][] {
+ //* Do not delete the next three lines
+ { "", DEFAULT_ATTACHMENT_MIME_TYPE },
+ { "k9s", K9_SETTINGS_MIME_TYPE },
+ { "txt", "text/plain" },
+ //* Do not delete the previous three lines
+ { "123", "application/vnd.lotus-1-2-3" },
+ { "323", "text/h323" },
+ { "3dml", "text/vnd.in3d.3dml" },
+ { "3g2", "video/3gpp2" },
+ { "3gp", "video/3gpp" },
+ { "aab", "application/x-authorware-bin" },
+ { "aac", "audio/x-aac" },
+ { "aam", "application/x-authorware-map" },
+ { "a", "application/octet-stream" },
+ { "aas", "application/x-authorware-seg" },
+ { "abw", "application/x-abiword" },
+ { "acc", "application/vnd.americandynamics.acc" },
+ { "ace", "application/x-ace-compressed" },
+ { "acu", "application/vnd.acucobol" },
+ { "acutc", "application/vnd.acucorp" },
+ { "acx", "application/internet-property-stream" },
+ { "adp", "audio/adpcm" },
+ { "aep", "application/vnd.audiograph" },
+ { "afm", "application/x-font-type1" },
+ { "afp", "application/vnd.ibm.modcap" },
+ { "ai", "application/postscript" },
+ { "aif", "audio/x-aiff" },
+ { "aifc", "audio/x-aiff" },
+ { "aiff", "audio/x-aiff" },
+ { "air", "application/vnd.adobe.air-application-installer-package+zip" },
+ { "ami", "application/vnd.amiga.ami" },
+ { "apk", "application/vnd.android.package-archive" },
+ { "application", "application/x-ms-application" },
+ { "apr", "application/vnd.lotus-approach" },
+ { "asc", "application/pgp-signature" },
+ { "asf", "video/x-ms-asf" },
+ { "asm", "text/x-asm" },
+ { "aso", "application/vnd.accpac.simply.aso" },
+ { "asr", "video/x-ms-asf" },
+ { "asx", "video/x-ms-asf" },
+ { "atc", "application/vnd.acucorp" },
+ { "atom", "application/atom+xml" },
+ { "atomcat", "application/atomcat+xml" },
+ { "atomsvc", "application/atomsvc+xml" },
+ { "atx", "application/vnd.antix.game-component" },
+ { "au", "audio/basic" },
+ { "avi", "video/x-msvideo" },
+ { "aw", "application/applixware" },
+ { "axs", "application/olescript" },
+ { "azf", "application/vnd.airzip.filesecure.azf" },
+ { "azs", "application/vnd.airzip.filesecure.azs" },
+ { "azw", "application/vnd.amazon.ebook" },
+ { "bas", "text/plain" },
+ { "bat", "application/x-msdownload" },
+ { "bcpio", "application/x-bcpio" },
+ { "bdf", "application/x-font-bdf" },
+ { "bdm", "application/vnd.syncml.dm+wbxml" },
+ { "bh2", "application/vnd.fujitsu.oasysprs" },
+ { "bin", "application/octet-stream" },
+ { "bmi", "application/vnd.bmi" },
+ { "bmp", "image/bmp" },
+ { "book", "application/vnd.framemaker" },
+ { "box", "application/vnd.previewsystems.box" },
+ { "boz", "application/x-bzip2" },
+ { "bpk", "application/octet-stream" },
+ { "btif", "image/prs.btif" },
+ { "bz2", "application/x-bzip2" },
+ { "bz", "application/x-bzip" },
+ { "c4d", "application/vnd.clonk.c4group" },
+ { "c4f", "application/vnd.clonk.c4group" },
+ { "c4g", "application/vnd.clonk.c4group" },
+ { "c4p", "application/vnd.clonk.c4group" },
+ { "c4u", "application/vnd.clonk.c4group" },
+ { "cab", "application/vnd.ms-cab-compressed" },
+ { "car", "application/vnd.curl.car" },
+ { "cat", "application/vnd.ms-pki.seccat" },
+ { "cct", "application/x-director" },
+ { "cc", "text/x-c" },
+ { "ccxml", "application/ccxml+xml" },
+ { "cdbcmsg", "application/vnd.contact.cmsg" },
+ { "cdf", "application/x-cdf" },
+ { "cdkey", "application/vnd.mediastation.cdkey" },
+ { "cdx", "chemical/x-cdx" },
+ { "cdxml", "application/vnd.chemdraw+xml" },
+ { "cdy", "application/vnd.cinderella" },
+ { "cer", "application/x-x509-ca-cert" },
+ { "cgm", "image/cgm" },
+ { "chat", "application/x-chat" },
+ { "chm", "application/vnd.ms-htmlhelp" },
+ { "chrt", "application/vnd.kde.kchart" },
+ { "cif", "chemical/x-cif" },
+ { "cii", "application/vnd.anser-web-certificate-issue-initiation" },
+ { "cla", "application/vnd.claymore" },
+ { "class", "application/java-vm" },
+ { "clkk", "application/vnd.crick.clicker.keyboard" },
+ { "clkp", "application/vnd.crick.clicker.palette" },
+ { "clkt", "application/vnd.crick.clicker.template" },
+ { "clkw", "application/vnd.crick.clicker.wordbank" },
+ { "clkx", "application/vnd.crick.clicker" },
+ { "clp", "application/x-msclip" },
+ { "cmc", "application/vnd.cosmocaller" },
+ { "cmdf", "chemical/x-cmdf" },
+ { "cml", "chemical/x-cml" },
+ { "cmp", "application/vnd.yellowriver-custom-menu" },
+ { "cmx", "image/x-cmx" },
+ { "cod", "application/vnd.rim.cod" },
+ { "com", "application/x-msdownload" },
+ { "conf", "text/plain" },
+ { "cpio", "application/x-cpio" },
+ { "cpp", "text/x-c" },
+ { "cpt", "application/mac-compactpro" },
+ { "crd", "application/x-mscardfile" },
+ { "crl", "application/pkix-crl" },
+ { "crt", "application/x-x509-ca-cert" },
+ { "csh", "application/x-csh" },
+ { "csml", "chemical/x-csml" },
+ { "csp", "application/vnd.commonspace" },
+ { "css", "text/css" },
+ { "cst", "application/x-director" },
+ { "csv", "text/csv" },
+ { "c", "text/plain" },
+ { "cu", "application/cu-seeme" },
+ { "curl", "text/vnd.curl" },
+ { "cww", "application/prs.cww" },
+ { "cxt", "application/x-director" },
+ { "cxx", "text/x-c" },
+ { "daf", "application/vnd.mobius.daf" },
+ { "dataless", "application/vnd.fdsn.seed" },
+ { "davmount", "application/davmount+xml" },
+ { "dcr", "application/x-director" },
+ { "dcurl", "text/vnd.curl.dcurl" },
+ { "dd2", "application/vnd.oma.dd2+xml" },
+ { "ddd", "application/vnd.fujixerox.ddd" },
+ { "deb", "application/x-debian-package" },
+ { "def", "text/plain" },
+ { "deploy", "application/octet-stream" },
+ { "der", "application/x-x509-ca-cert" },
+ { "dfac", "application/vnd.dreamfactory" },
+ { "dic", "text/x-c" },
+ { "diff", "text/plain" },
+ { "dir", "application/x-director" },
+ { "dis", "application/vnd.mobius.dis" },
+ { "dist", "application/octet-stream" },
+ { "distz", "application/octet-stream" },
+ { "djv", "image/vnd.djvu" },
+ { "djvu", "image/vnd.djvu" },
+ { "dll", "application/x-msdownload" },
+ { "dmg", "application/octet-stream" },
+ { "dms", "application/octet-stream" },
+ { "dna", "application/vnd.dna" },
+ { "doc", "application/msword" },
+ { "docm", "application/vnd.ms-word.document.macroenabled.12" },
+ { "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
+ { "dot", "application/msword" },
+ { "dotm", "application/vnd.ms-word.template.macroenabled.12" },
+ { "dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" },
+ { "dp", "application/vnd.osgi.dp" },
+ { "dpg", "application/vnd.dpgraph" },
+ { "dsc", "text/prs.lines.tag" },
+ { "dtb", "application/x-dtbook+xml" },
+ { "dtd", "application/xml-dtd" },
+ { "dts", "audio/vnd.dts" },
+ { "dtshd", "audio/vnd.dts.hd" },
+ { "dump", "application/octet-stream" },
+ { "dvi", "application/x-dvi" },
+ { "dwf", "model/vnd.dwf" },
+ { "dwg", "image/vnd.dwg" },
+ { "dxf", "image/vnd.dxf" },
+ { "dxp", "application/vnd.spotfire.dxp" },
+ { "dxr", "application/x-director" },
+ { "ecelp4800", "audio/vnd.nuera.ecelp4800" },
+ { "ecelp7470", "audio/vnd.nuera.ecelp7470" },
+ { "ecelp9600", "audio/vnd.nuera.ecelp9600" },
+ { "ecma", "application/ecmascript" },
+ { "edm", "application/vnd.novadigm.edm" },
+ { "edx", "application/vnd.novadigm.edx" },
+ { "efif", "application/vnd.picsel" },
+ { "ei6", "application/vnd.pg.osasli" },
+ { "elc", "application/octet-stream" },
+ { "eml", "message/rfc822" },
+ { "emma", "application/emma+xml" },
+ { "eol", "audio/vnd.digital-winds" },
+ { "eot", "application/vnd.ms-fontobject" },
+ { "eps", "application/postscript" },
+ { "epub", "application/epub+zip" },
+ { "es3", "application/vnd.eszigno3+xml" },
+ { "esf", "application/vnd.epson.esf" },
+ { "espass", "application/vnd.espass-espass+zip" },
+ { "et3", "application/vnd.eszigno3+xml" },
+ { "etx", "text/x-setext" },
+ { "evy", "application/envoy" },
+ { "exe", "application/octet-stream" },
+ { "ext", "application/vnd.novadigm.ext" },
+ { "ez2", "application/vnd.ezpix-album" },
+ { "ez3", "application/vnd.ezpix-package" },
+ { "ez", "application/andrew-inset" },
+ { "f4v", "video/x-f4v" },
+ { "f77", "text/x-fortran" },
+ { "f90", "text/x-fortran" },
+ { "fbs", "image/vnd.fastbidsheet" },
+ { "fdf", "application/vnd.fdf" },
+ { "fe_launch", "application/vnd.denovo.fcselayout-link" },
+ { "fg5", "application/vnd.fujitsu.oasysgp" },
+ { "fgd", "application/x-director" },
+ { "fh4", "image/x-freehand" },
+ { "fh5", "image/x-freehand" },
+ { "fh7", "image/x-freehand" },
+ { "fhc", "image/x-freehand" },
+ { "fh", "image/x-freehand" },
+ { "fif", "application/fractals" },
+ { "fig", "application/x-xfig" },
+ { "fli", "video/x-fli" },
+ { "flo", "application/vnd.micrografx.flo" },
+ { "flr", "x-world/x-vrml" },
+ { "flv", "video/x-flv" },
+ { "flw", "application/vnd.kde.kivio" },
+ { "flx", "text/vnd.fmi.flexstor" },
+ { "fly", "text/vnd.fly" },
+ { "fm", "application/vnd.framemaker" },
+ { "fnc", "application/vnd.frogans.fnc" },
+ { "for", "text/x-fortran" },
+ { "fpx", "image/vnd.fpx" },
+ { "frame", "application/vnd.framemaker" },
+ { "fsc", "application/vnd.fsc.weblaunch" },
+ { "fst", "image/vnd.fst" },
+ { "ftc", "application/vnd.fluxtime.clip" },
+ { "f", "text/x-fortran" },
+ { "fti", "application/vnd.anser-web-funds-transfer-initiation" },
+ { "fvt", "video/vnd.fvt" },
+ { "fzs", "application/vnd.fuzzysheet" },
+ { "g3", "image/g3fax" },
+ { "gac", "application/vnd.groove-account" },
+ { "gdl", "model/vnd.gdl" },
+ { "geo", "application/vnd.dynageo" },
+ { "gex", "application/vnd.geometry-explorer" },
+ { "ggb", "application/vnd.geogebra.file" },
+ { "ggt", "application/vnd.geogebra.tool" },
+ { "ghf", "application/vnd.groove-help" },
+ { "gif", "image/gif" },
+ { "gim", "application/vnd.groove-identity-message" },
+ { "gmx", "application/vnd.gmx" },
+ { "gnumeric", "application/x-gnumeric" },
+ { "gph", "application/vnd.flographit" },
+ { "gqf", "application/vnd.grafeq" },
+ { "gqs", "application/vnd.grafeq" },
+ { "gram", "application/srgs" },
+ { "gre", "application/vnd.geometry-explorer" },
+ { "grv", "application/vnd.groove-injector" },
+ { "grxml", "application/srgs+xml" },
+ { "gsf", "application/x-font-ghostscript" },
+ { "gtar", "application/x-gtar" },
+ { "gtm", "application/vnd.groove-tool-message" },
+ { "gtw", "model/vnd.gtw" },
+ { "gv", "text/vnd.graphviz" },
+ { "gz", "application/x-gzip" },
+ { "h261", "video/h261" },
+ { "h263", "video/h263" },
+ { "h264", "video/h264" },
+ { "hbci", "application/vnd.hbci" },
+ { "hdf", "application/x-hdf" },
+ { "hh", "text/x-c" },
+ { "hlp", "application/winhlp" },
+ { "hpgl", "application/vnd.hp-hpgl" },
+ { "hpid", "application/vnd.hp-hpid" },
+ { "hps", "application/vnd.hp-hps" },
+ { "hqx", "application/mac-binhex40" },
+ { "hta", "application/hta" },
+ { "htc", "text/x-component" },
+ { "h", "text/plain" },
+ { "htke", "application/vnd.kenameaapp" },
+ { "html", "text/html" },
+ { "htm", "text/html" },
+ { "htt", "text/webviewhtml" },
+ { "hvd", "application/vnd.yamaha.hv-dic" },
+ { "hvp", "application/vnd.yamaha.hv-voice" },
+ { "hvs", "application/vnd.yamaha.hv-script" },
+ { "icc", "application/vnd.iccprofile" },
+ { "ice", "x-conference/x-cooltalk" },
+ { "icm", "application/vnd.iccprofile" },
+ { "ico", "image/x-icon" },
+ { "ics", "text/calendar" },
+ { "ief", "image/ief" },
+ { "ifb", "text/calendar" },
+ { "ifm", "application/vnd.shana.informed.formdata" },
+ { "iges", "model/iges" },
+ { "igl", "application/vnd.igloader" },
+ { "igs", "model/iges" },
+ { "igx", "application/vnd.micrografx.igx" },
+ { "iif", "application/vnd.shana.informed.interchange" },
+ { "iii", "application/x-iphone" },
+ { "imp", "application/vnd.accpac.simply.imp" },
+ { "ims", "application/vnd.ms-ims" },
+ { "ins", "application/x-internet-signup" },
+ { "in", "text/plain" },
+ { "ipk", "application/vnd.shana.informed.package" },
+ { "irm", "application/vnd.ibm.rights-management" },
+ { "irp", "application/vnd.irepository.package+xml" },
+ { "iso", "application/octet-stream" },
+ { "isp", "application/x-internet-signup" },
+ { "itp", "application/vnd.shana.informed.formtemplate" },
+ { "ivp", "application/vnd.immervision-ivp" },
+ { "ivu", "application/vnd.immervision-ivu" },
+ { "jad", "text/vnd.sun.j2me.app-descriptor" },
+ { "jam", "application/vnd.jam" },
+ { "jar", "application/java-archive" },
+ { "java", "text/x-java-source" },
+ { "jfif", "image/pipeg" },
+ { "jisp", "application/vnd.jisp" },
+ { "jlt", "application/vnd.hp-jlyt" },
+ { "jnlp", "application/x-java-jnlp-file" },
+ { "joda", "application/vnd.joost.joda-archive" },
+ { "jpeg", "image/jpeg" },
+ { "jpe", "image/jpeg" },
+ { "jpg", "image/jpeg" },
+ { "jpgm", "video/jpm" },
+ { "jpgv", "video/jpeg" },
+ { "jpm", "video/jpm" },
+ { "js", "application/x-javascript" },
+ { "json", "application/json" },
+ { "kar", "audio/midi" },
+ { "karbon", "application/vnd.kde.karbon" },
+ { "kfo", "application/vnd.kde.kformula" },
+ { "kia", "application/vnd.kidspiration" },
+ { "kil", "application/x-killustrator" },
+ { "kml", "application/vnd.google-earth.kml+xml" },
+ { "kmz", "application/vnd.google-earth.kmz" },
+ { "kne", "application/vnd.kinar" },
+ { "knp", "application/vnd.kinar" },
+ { "kon", "application/vnd.kde.kontour" },
+ { "kpr", "application/vnd.kde.kpresenter" },
+ { "kpt", "application/vnd.kde.kpresenter" },
+ { "ksh", "text/plain" },
+ { "ksp", "application/vnd.kde.kspread" },
+ { "ktr", "application/vnd.kahootz" },
+ { "ktz", "application/vnd.kahootz" },
+ { "kwd", "application/vnd.kde.kword" },
+ { "kwt", "application/vnd.kde.kword" },
+ { "latex", "application/x-latex" },
+ { "lbd", "application/vnd.llamagraphics.life-balance.desktop" },
+ { "lbe", "application/vnd.llamagraphics.life-balance.exchange+xml" },
+ { "les", "application/vnd.hhe.lesson-player" },
+ { "lha", "application/octet-stream" },
+ { "link66", "application/vnd.route66.link66+xml" },
+ { "list3820", "application/vnd.ibm.modcap" },
+ { "listafp", "application/vnd.ibm.modcap" },
+ { "list", "text/plain" },
+ { "log", "text/plain" },
+ { "lostxml", "application/lost+xml" },
+ { "lrf", "application/octet-stream" },
+ { "lrm", "application/vnd.ms-lrm" },
+ { "lsf", "video/x-la-asf" },
+ { "lsx", "video/x-la-asf" },
+ { "ltf", "application/vnd.frogans.ltf" },
+ { "lvp", "audio/vnd.lucent.voice" },
+ { "lwp", "application/vnd.lotus-wordpro" },
+ { "lzh", "application/octet-stream" },
+ { "m13", "application/x-msmediaview" },
+ { "m14", "application/x-msmediaview" },
+ { "m1v", "video/mpeg" },
+ { "m2a", "audio/mpeg" },
+ { "m2v", "video/mpeg" },
+ { "m3a", "audio/mpeg" },
+ { "m3u", "audio/x-mpegurl" },
+ { "m4u", "video/vnd.mpegurl" },
+ { "m4v", "video/x-m4v" },
+ { "ma", "application/mathematica" },
+ { "mag", "application/vnd.ecowin.chart" },
+ { "maker", "application/vnd.framemaker" },
+ { "man", "text/troff" },
+ { "mathml", "application/mathml+xml" },
+ { "mb", "application/mathematica" },
+ { "mbk", "application/vnd.mobius.mbk" },
+ { "mbox", "application/mbox" },
+ { "mc1", "application/vnd.medcalcdata" },
+ { "mcd", "application/vnd.mcd" },
+ { "mcurl", "text/vnd.curl.mcurl" },
+ { "mdb", "application/x-msaccess" },
+ { "mdi", "image/vnd.ms-modi" },
+ { "mesh", "model/mesh" },
+ { "me", "text/troff" },
+ { "mfm", "application/vnd.mfmp" },
+ { "mgz", "application/vnd.proteus.magazine" },
+ { "mht", "message/rfc822" },
+ { "mhtml", "message/rfc822" },
+ { "mid", "audio/midi" },
+ { "midi", "audio/midi" },
+ { "mif", "application/vnd.mif" },
+ { "mime", "message/rfc822" },
+ { "mj2", "video/mj2" },
+ { "mjp2", "video/mj2" },
+ { "mlp", "application/vnd.dolby.mlp" },
+ { "mmd", "application/vnd.chipnuts.karaoke-mmd" },
+ { "mmf", "application/vnd.smaf" },
+ { "mmr", "image/vnd.fujixerox.edmics-mmr" },
+ { "mny", "application/x-msmoney" },
+ { "mobi", "application/x-mobipocket-ebook" },
+ { "movie", "video/x-sgi-movie" },
+ { "mov", "video/quicktime" },
+ { "mp2a", "audio/mpeg" },
+ { "mp2", "video/mpeg" },
+ { "mp3", "audio/mpeg" },
+ { "mp4a", "audio/mp4" },
+ { "mp4s", "application/mp4" },
+ { "mp4", "video/mp4" },
+ { "mp4v", "video/mp4" },
+ { "mpa", "video/mpeg" },
+ { "mpc", "application/vnd.mophun.certificate" },
+ { "mpeg", "video/mpeg" },
+ { "mpe", "video/mpeg" },
+ { "mpg4", "video/mp4" },
+ { "mpga", "audio/mpeg" },
+ { "mpg", "video/mpeg" },
+ { "mpkg", "application/vnd.apple.installer+xml" },
+ { "mpm", "application/vnd.blueice.multipass" },
+ { "mpn", "application/vnd.mophun.application" },
+ { "mpp", "application/vnd.ms-project" },
+ { "mpt", "application/vnd.ms-project" },
+ { "mpv2", "video/mpeg" },
+ { "mpy", "application/vnd.ibm.minipay" },
+ { "mqy", "application/vnd.mobius.mqy" },
+ { "mrc", "application/marc" },
+ { "mscml", "application/mediaservercontrol+xml" },
+ { "mseed", "application/vnd.fdsn.mseed" },
+ { "mseq", "application/vnd.mseq" },
+ { "msf", "application/vnd.epson.msf" },
+ { "msh", "model/mesh" },
+ { "msi", "application/x-msdownload" },
+ { "ms", "text/troff" },
+ { "msty", "application/vnd.muvee.style" },
+ { "mts", "model/vnd.mts" },
+ { "mus", "application/vnd.musician" },
+ { "musicxml", "application/vnd.recordare.musicxml+xml" },
+ { "mvb", "application/x-msmediaview" },
+ { "mxf", "application/mxf" },
+ { "mxl", "application/vnd.recordare.musicxml" },
+ { "mxml", "application/xv+xml" },
+ { "mxs", "application/vnd.triscape.mxs" },
+ { "mxu", "video/vnd.mpegurl" },
+ { "nb", "application/mathematica" },
+ { "nc", "application/x-netcdf" },
+ { "ncx", "application/x-dtbncx+xml" },
+ { "n-gage", "application/vnd.nokia.n-gage.symbian.install" },
+ { "ngdat", "application/vnd.nokia.n-gage.data" },
+ { "nlu", "application/vnd.neurolanguage.nlu" },
+ { "nml", "application/vnd.enliven" },
+ { "nnd", "application/vnd.noblenet-directory" },
+ { "nns", "application/vnd.noblenet-sealer" },
+ { "nnw", "application/vnd.noblenet-web" },
+ { "npx", "image/vnd.net-fpx" },
+ { "nsf", "application/vnd.lotus-notes" },
+ { "nws", "message/rfc822" },
+ { "oa2", "application/vnd.fujitsu.oasys2" },
+ { "oa3", "application/vnd.fujitsu.oasys3" },
+ { "o", "application/octet-stream" },
+ { "oas", "application/vnd.fujitsu.oasys" },
+ { "obd", "application/x-msbinder" },
+ { "obj", "application/octet-stream" },
+ { "oda", "application/oda" },
+ { "odb", "application/vnd.oasis.opendocument.database" },
+ { "odc", "application/vnd.oasis.opendocument.chart" },
+ { "odf", "application/vnd.oasis.opendocument.formula" },
+ { "odft", "application/vnd.oasis.opendocument.formula-template" },
+ { "odg", "application/vnd.oasis.opendocument.graphics" },
+ { "odi", "application/vnd.oasis.opendocument.image" },
+ { "odp", "application/vnd.oasis.opendocument.presentation" },
+ { "ods", "application/vnd.oasis.opendocument.spreadsheet" },
+ { "odt", "application/vnd.oasis.opendocument.text" },
+ { "oga", "audio/ogg" },
+ { "ogg", "audio/ogg" },
+ { "ogv", "video/ogg" },
+ { "ogx", "application/ogg" },
+ { "onepkg", "application/onenote" },
+ { "onetmp", "application/onenote" },
+ { "onetoc2", "application/onenote" },
+ { "onetoc", "application/onenote" },
+ { "opf", "application/oebps-package+xml" },
+ { "oprc", "application/vnd.palm" },
+ { "org", "application/vnd.lotus-organizer" },
+ { "osf", "application/vnd.yamaha.openscoreformat" },
+ { "osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml" },
+ { "otc", "application/vnd.oasis.opendocument.chart-template" },
+ { "otf", "application/x-font-otf" },
+ { "otg", "application/vnd.oasis.opendocument.graphics-template" },
+ { "oth", "application/vnd.oasis.opendocument.text-web" },
+ { "oti", "application/vnd.oasis.opendocument.image-template" },
+ { "otm", "application/vnd.oasis.opendocument.text-master" },
+ { "otp", "application/vnd.oasis.opendocument.presentation-template" },
+ { "ots", "application/vnd.oasis.opendocument.spreadsheet-template" },
+ { "ott", "application/vnd.oasis.opendocument.text-template" },
+ { "oxt", "application/vnd.openofficeorg.extension" },
+ { "p10", "application/pkcs10" },
+ { "p12", "application/x-pkcs12" },
+ { "p7b", "application/x-pkcs7-certificates" },
+ { "p7c", "application/x-pkcs7-mime" },
+ { "p7m", "application/x-pkcs7-mime" },
+ { "p7r", "application/x-pkcs7-certreqresp" },
+ { "p7s", "application/x-pkcs7-signature" },
+ { "pas", "text/x-pascal" },
+ { "pbd", "application/vnd.powerbuilder6" },
+ { "pbm", "image/x-portable-bitmap" },
+ { "pcf", "application/x-font-pcf" },
+ { "pcl", "application/vnd.hp-pcl" },
+ { "pclxl", "application/vnd.hp-pclxl" },
+ { "pct", "image/x-pict" },
+ { "pcurl", "application/vnd.curl.pcurl" },
+ { "pcx", "image/x-pcx" },
+ { "pdb", "application/vnd.palm" },
+ { "pdf", "application/pdf" },
+ { "pfa", "application/x-font-type1" },
+ { "pfb", "application/x-font-type1" },
+ { "pfm", "application/x-font-type1" },
+ { "pfr", "application/font-tdpfr" },
+ { "pfx", "application/x-pkcs12" },
+ { "pgm", "image/x-portable-graymap" },
+ { "pgn", "application/x-chess-pgn" },
+ { "pgp", "application/pgp-encrypted" },
+ { "pic", "image/x-pict" },
+ { "pkg", "application/octet-stream" },
+ { "pki", "application/pkixcmp" },
+ { "pkipath", "application/pkix-pkipath" },
+ { "pkpass", "application/vnd-com.apple.pkpass" },
+ { "pko", "application/ynd.ms-pkipko" },
+ { "plb", "application/vnd.3gpp.pic-bw-large" },
+ { "plc", "application/vnd.mobius.plc" },
+ { "plf", "application/vnd.pocketlearn" },
+ { "pls", "application/pls+xml" },
+ { "pl", "text/plain" },
+ { "pma", "application/x-perfmon" },
+ { "pmc", "application/x-perfmon" },
+ { "pml", "application/x-perfmon" },
+ { "pmr", "application/x-perfmon" },
+ { "pmw", "application/x-perfmon" },
+ { "png", "image/png" },
+ { "pnm", "image/x-portable-anymap" },
+ { "portpkg", "application/vnd.macports.portpkg" },
+ { "pot,", "application/vnd.ms-powerpoint" },
+ { "pot", "application/vnd.ms-powerpoint" },
+ { "potm", "application/vnd.ms-powerpoint.template.macroenabled.12" },
+ { "potx", "application/vnd.openxmlformats-officedocument.presentationml.template" },
+ { "ppa", "application/vnd.ms-powerpoint" },
+ { "ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12" },
+ { "ppd", "application/vnd.cups-ppd" },
+ { "ppm", "image/x-portable-pixmap" },
+ { "pps", "application/vnd.ms-powerpoint" },
+ { "ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12" },
+ { "ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" },
+ { "ppt", "application/vnd.ms-powerpoint" },
+ { "pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12" },
+ { "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
+ { "pqa", "application/vnd.palm" },
+ { "prc", "application/x-mobipocket-ebook" },
+ { "pre", "application/vnd.lotus-freelance" },
+ { "prf", "application/pics-rules" },
+ { "ps", "application/postscript" },
+ { "psb", "application/vnd.3gpp.pic-bw-small" },
+ { "psd", "image/vnd.adobe.photoshop" },
+ { "psf", "application/x-font-linux-psf" },
+ { "p", "text/x-pascal" },
+ { "ptid", "application/vnd.pvi.ptid1" },
+ { "pub", "application/x-mspublisher" },
+ { "pvb", "application/vnd.3gpp.pic-bw-var" },
+ { "pwn", "application/vnd.3m.post-it-notes" },
+ { "pwz", "application/vnd.ms-powerpoint" },
+ { "pya", "audio/vnd.ms-playready.media.pya" },
+ { "pyc", "application/x-python-code" },
+ { "pyo", "application/x-python-code" },
+ { "py", "text/x-python" },
+ { "pyv", "video/vnd.ms-playready.media.pyv" },
+ { "qam", "application/vnd.epson.quickanime" },
+ { "qbo", "application/vnd.intu.qbo" },
+ { "qfx", "application/vnd.intu.qfx" },
+ { "qps", "application/vnd.publishare-delta-tree" },
+ { "qt", "video/quicktime" },
+ { "qwd", "application/vnd.quark.quarkxpress" },
+ { "qwt", "application/vnd.quark.quarkxpress" },
+ { "qxb", "application/vnd.quark.quarkxpress" },
+ { "qxd", "application/vnd.quark.quarkxpress" },
+ { "qxl", "application/vnd.quark.quarkxpress" },
+ { "qxt", "application/vnd.quark.quarkxpress" },
+ { "ra", "audio/x-pn-realaudio" },
+ { "ram", "audio/x-pn-realaudio" },
+ { "rar", "application/x-rar-compressed" },
+ { "ras", "image/x-cmu-raster" },
+ { "rcprofile", "application/vnd.ipunplugged.rcprofile" },
+ { "rdf", "application/rdf+xml" },
+ { "rdz", "application/vnd.data-vision.rdz" },
+ { "rep", "application/vnd.businessobjects" },
+ { "res", "application/x-dtbresource+xml" },
+ { "rgb", "image/x-rgb" },
+ { "rif", "application/reginfo+xml" },
+ { "rl", "application/resource-lists+xml" },
+ { "rlc", "image/vnd.fujixerox.edmics-rlc" },
+ { "rld", "application/resource-lists-diff+xml" },
+ { "rm", "application/vnd.rn-realmedia" },
+ { "rmi", "audio/midi" },
+ { "rmp", "audio/x-pn-realaudio-plugin" },
+ { "rms", "application/vnd.jcp.javame.midlet-rms" },
+ { "rnc", "application/relax-ng-compact-syntax" },
+ { "roff", "text/troff" },
+ { "rpm", "application/x-rpm" },
+ { "rpss", "application/vnd.nokia.radio-presets" },
+ { "rpst", "application/vnd.nokia.radio-preset" },
+ { "rq", "application/sparql-query" },
+ { "rs", "application/rls-services+xml" },
+ { "rsd", "application/rsd+xml" },
+ { "rss", "application/rss+xml" },
+ { "rtf", "application/rtf" },
+ { "rtx", "text/richtext" },
+ { "saf", "application/vnd.yamaha.smaf-audio" },
+ { "sbml", "application/sbml+xml" },
+ { "sc", "application/vnd.ibm.secure-container" },
+ { "scd", "application/x-msschedule" },
+ { "scm", "application/vnd.lotus-screencam" },
+ { "scq", "application/scvp-cv-request" },
+ { "scs", "application/scvp-cv-response" },
+ { "sct", "text/scriptlet" },
+ { "scurl", "text/vnd.curl.scurl" },
+ { "sda", "application/vnd.stardivision.draw" },
+ { "sdc", "application/vnd.stardivision.calc" },
+ { "sdd", "application/vnd.stardivision.impress" },
+ { "sdkd", "application/vnd.solent.sdkm+xml" },
+ { "sdkm", "application/vnd.solent.sdkm+xml" },
+ { "sdp", "application/sdp" },
+ { "sdw", "application/vnd.stardivision.writer" },
+ { "see", "application/vnd.seemail" },
+ { "seed", "application/vnd.fdsn.seed" },
+ { "sema", "application/vnd.sema" },
+ { "semd", "application/vnd.semd" },
+ { "semf", "application/vnd.semf" },
+ { "ser", "application/java-serialized-object" },
+ { "setpay", "application/set-payment-initiation" },
+ { "setreg", "application/set-registration-initiation" },
+ { "sfd-hdstx", "application/vnd.hydrostatix.sof-data" },
+ { "sfs", "application/vnd.spotfire.sfs" },
+ { "sgl", "application/vnd.stardivision.writer-global" },
+ { "sgml", "text/sgml" },
+ { "sgm", "text/sgml" },
+ { "sh", "application/x-sh" },
+ { "shar", "application/x-shar" },
+ { "shf", "application/shf+xml" },
+ { "sic", "application/vnd.wap.sic" },
+ { "sig", "application/pgp-signature" },
+ { "silo", "model/mesh" },
+ { "sis", "application/vnd.symbian.install" },
+ { "sisx", "application/vnd.symbian.install" },
+ { "sit", "application/x-stuffit" },
+ { "si", "text/vnd.wap.si" },
+ { "sitx", "application/x-stuffitx" },
+ { "skd", "application/vnd.koan" },
+ { "skm", "application/vnd.koan" },
+ { "skp", "application/vnd.koan" },
+ { "skt", "application/vnd.koan" },
+ { "slc", "application/vnd.wap.slc" },
+ { "sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12" },
+ { "sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide" },
+ { "slt", "application/vnd.epson.salt" },
+ { "sl", "text/vnd.wap.sl" },
+ { "smf", "application/vnd.stardivision.math" },
+ { "smi", "application/smil+xml" },
+ { "smil", "application/smil+xml" },
+ { "snd", "audio/basic" },
+ { "snf", "application/x-font-snf" },
+ { "so", "application/octet-stream" },
+ { "spc", "application/x-pkcs7-certificates" },
+ { "spf", "application/vnd.yamaha.smaf-phrase" },
+ { "spl", "application/x-futuresplash" },
+ { "spot", "text/vnd.in3d.spot" },
+ { "spp", "application/scvp-vp-response" },
+ { "spq", "application/scvp-vp-request" },
+ { "spx", "audio/ogg" },
+ { "src", "application/x-wais-source" },
+ { "srx", "application/sparql-results+xml" },
+ { "sse", "application/vnd.kodak-descriptor" },
+ { "ssf", "application/vnd.epson.ssf" },
+ { "ssml", "application/ssml+xml" },
+ { "sst", "application/vnd.ms-pkicertstore" },
+ { "stc", "application/vnd.sun.xml.calc.template" },
+ { "std", "application/vnd.sun.xml.draw.template" },
+ { "s", "text/x-asm" },
+ { "stf", "application/vnd.wt.stf" },
+ { "sti", "application/vnd.sun.xml.impress.template" },
+ { "stk", "application/hyperstudio" },
+ { "stl", "application/vnd.ms-pki.stl" },
+ { "stm", "text/html" },
+ { "str", "application/vnd.pg.format" },
+ { "stw", "application/vnd.sun.xml.writer.template" },
+ { "sus", "application/vnd.sus-calendar" },
+ { "susp", "application/vnd.sus-calendar" },
+ { "sv4cpio", "application/x-sv4cpio" },
+ { "sv4crc", "application/x-sv4crc" },
+ { "svd", "application/vnd.svd" },
+ { "svg", "image/svg+xml" },
+ { "svgz", "image/svg+xml" },
+ { "swa", "application/x-director" },
+ { "swf", "application/x-shockwave-flash" },
+ { "swi", "application/vnd.arastra.swi" },
+ { "sxc", "application/vnd.sun.xml.calc" },
+ { "sxd", "application/vnd.sun.xml.draw" },
+ { "sxg", "application/vnd.sun.xml.writer.global" },
+ { "sxi", "application/vnd.sun.xml.impress" },
+ { "sxm", "application/vnd.sun.xml.math" },
+ { "sxw", "application/vnd.sun.xml.writer" },
+ { "tao", "application/vnd.tao.intent-module-archive" },
+ { "t", "application/x-troff" },
+ { "tar", "application/x-tar" },
+ { "tcap", "application/vnd.3gpp2.tcap" },
+ { "tcl", "application/x-tcl" },
+ { "teacher", "application/vnd.smart.teacher" },
+ { "tex", "application/x-tex" },
+ { "texi", "application/x-texinfo" },
+ { "texinfo", "application/x-texinfo" },
+ { "text", "text/plain" },
+ { "tfm", "application/x-tex-tfm" },
+ { "tgz", "application/x-gzip" },
+ { "tiff", "image/tiff" },
+ { "tif", "image/tiff" },
+ { "tmo", "application/vnd.tmobile-livetv" },
+ { "torrent", "application/x-bittorrent" },
+ { "tpl", "application/vnd.groove-tool-template" },
+ { "tpt", "application/vnd.trid.tpt" },
+ { "tra", "application/vnd.trueapp" },
+ { "trm", "application/x-msterminal" },
+ { "tr", "text/troff" },
+ { "tsv", "text/tab-separated-values" },
+ { "ttc", "application/x-font-ttf" },
+ { "ttf", "application/x-font-ttf" },
+ { "twd", "application/vnd.simtech-mindmapper" },
+ { "twds", "application/vnd.simtech-mindmapper" },
+ { "txd", "application/vnd.genomatix.tuxedo" },
+ { "txf", "application/vnd.mobius.txf" },
+ { "txt", "text/plain" },
+ { "u32", "application/x-authorware-bin" },
+ { "udeb", "application/x-debian-package" },
+ { "ufd", "application/vnd.ufdl" },
+ { "ufdl", "application/vnd.ufdl" },
+ { "uls", "text/iuls" },
+ { "umj", "application/vnd.umajin" },
+ { "unityweb", "application/vnd.unity" },
+ { "uoml", "application/vnd.uoml+xml" },
+ { "uris", "text/uri-list" },
+ { "uri", "text/uri-list" },
+ { "urls", "text/uri-list" },
+ { "ustar", "application/x-ustar" },
+ { "utz", "application/vnd.uiq.theme" },
+ { "uu", "text/x-uuencode" },
+ { "vcd", "application/x-cdlink" },
+ { "vcf", "text/x-vcard" },
+ { "vcg", "application/vnd.groove-vcard" },
+ { "vcs", "text/x-vcalendar" },
+ { "vcx", "application/vnd.vcx" },
+ { "vis", "application/vnd.visionary" },
+ { "viv", "video/vnd.vivo" },
+ { "vor", "application/vnd.stardivision.writer" },
+ { "vox", "application/x-authorware-bin" },
+ { "vrml", "x-world/x-vrml" },
+ { "vsd", "application/vnd.visio" },
+ { "vsf", "application/vnd.vsf" },
+ { "vss", "application/vnd.visio" },
+ { "vst", "application/vnd.visio" },
+ { "vsw", "application/vnd.visio" },
+ { "vtu", "model/vnd.vtu" },
+ { "vxml", "application/voicexml+xml" },
+ { "w3d", "application/x-director" },
+ { "wad", "application/x-doom" },
+ { "wav", "audio/x-wav" },
+ { "wax", "audio/x-ms-wax" },
+ { "wbmp", "image/vnd.wap.wbmp" },
+ { "wbs", "application/vnd.criticaltools.wbs+xml" },
+ { "wbxml", "application/vnd.wap.wbxml" },
+ { "wcm", "application/vnd.ms-works" },
+ { "wdb", "application/vnd.ms-works" },
+ { "wiz", "application/msword" },
+ { "wks", "application/vnd.ms-works" },
+ { "wma", "audio/x-ms-wma" },
+ { "wmd", "application/x-ms-wmd" },
+ { "wmf", "application/x-msmetafile" },
+ { "wmlc", "application/vnd.wap.wmlc" },
+ { "wmlsc", "application/vnd.wap.wmlscriptc" },
+ { "wmls", "text/vnd.wap.wmlscript" },
+ { "wml", "text/vnd.wap.wml" },
+ { "wm", "video/x-ms-wm" },
+ { "wmv", "video/x-ms-wmv" },
+ { "wmx", "video/x-ms-wmx" },
+ { "wmz", "application/x-ms-wmz" },
+ { "wpd", "application/vnd.wordperfect" },
+ { "wpl", "application/vnd.ms-wpl" },
+ { "wps", "application/vnd.ms-works" },
+ { "wqd", "application/vnd.wqd" },
+ { "wri", "application/x-mswrite" },
+ { "wrl", "x-world/x-vrml" },
+ { "wrz", "x-world/x-vrml" },
+ { "wsdl", "application/wsdl+xml" },
+ { "wspolicy", "application/wspolicy+xml" },
+ { "wtb", "application/vnd.webturbo" },
+ { "wvx", "video/x-ms-wvx" },
+ { "x32", "application/x-authorware-bin" },
+ { "x3d", "application/vnd.hzn-3d-crossword" },
+ { "xaf", "x-world/x-vrml" },
+ { "xap", "application/x-silverlight-app" },
+ { "xar", "application/vnd.xara" },
+ { "xbap", "application/x-ms-xbap" },
+ { "xbd", "application/vnd.fujixerox.docuworks.binder" },
+ { "xbm", "image/x-xbitmap" },
+ { "xdm", "application/vnd.syncml.dm+xml" },
+ { "xdp", "application/vnd.adobe.xdp+xml" },
+ { "xdw", "application/vnd.fujixerox.docuworks" },
+ { "xenc", "application/xenc+xml" },
+ { "xer", "application/patch-ops-error+xml" },
+ { "xfdf", "application/vnd.adobe.xfdf" },
+ { "xfdl", "application/vnd.xfdl" },
+ { "xht", "application/xhtml+xml" },
+ { "xhtml", "application/xhtml+xml" },
+ { "xhvml", "application/xv+xml" },
+ { "xif", "image/vnd.xiff" },
+ { "xla", "application/vnd.ms-excel" },
+ { "xlam", "application/vnd.ms-excel.addin.macroenabled.12" },
+ { "xlb", "application/vnd.ms-excel" },
+ { "xlc", "application/vnd.ms-excel" },
+ { "xlm", "application/vnd.ms-excel" },
+ { "xls", "application/vnd.ms-excel" },
+ { "xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12" },
+ { "xlsm", "application/vnd.ms-excel.sheet.macroenabled.12" },
+ { "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
+ { "xlt", "application/vnd.ms-excel" },
+ { "xltm", "application/vnd.ms-excel.template.macroenabled.12" },
+ { "xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" },
+ { "xlw", "application/vnd.ms-excel" },
+ { "xml", "application/xml" },
+ { "xo", "application/vnd.olpc-sugar" },
+ { "xof", "x-world/x-vrml" },
+ { "xop", "application/xop+xml" },
+ { "xpdl", "application/xml" },
+ { "xpi", "application/x-xpinstall" },
+ { "xpm", "image/x-xpixmap" },
+ { "xpr", "application/vnd.is-xpr" },
+ { "xps", "application/vnd.ms-xpsdocument" },
+ { "xpw", "application/vnd.intercon.formnet" },
+ { "xpx", "application/vnd.intercon.formnet" },
+ { "xsl", "application/xml" },
+ { "xslt", "application/xslt+xml" },
+ { "xsm", "application/vnd.syncml+xml" },
+ { "xspf", "application/xspf+xml" },
+ { "xul", "application/vnd.mozilla.xul+xml" },
+ { "xvm", "application/xv+xml" },
+ { "xvml", "application/xv+xml" },
+ { "xwd", "image/x-xwindowdump" },
+ { "xyz", "chemical/x-xyz" },
+ { "z", "application/x-compress" },
+ { "zaz", "application/vnd.zzazz.deck+xml" },
+ { "zip", "application/zip" },
+ { "zir", "application/vnd.zul" },
+ { "zirz", "application/vnd.zul" },
+ { "zmm", "application/vnd.handheld-entertainment+xml" }
+ };
+
+ public static boolean isDefaultMimeType(String mimeType) {
+ return isSameMimeType(mimeType, DEFAULT_ATTACHMENT_MIME_TYPE);
+ }
+
+ public static String getMimeTypeByExtension(String filename) {
+ String returnedType = null;
+ String extension = null;
+
+ if (filename != null && filename.lastIndexOf('.') != -1) {
+ extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(Locale.US);
+ returnedType = android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ }
+ // If the MIME type set by the user's mailer is application/octet-stream, try to figure
+ // out whether there's a sane file type extension.
+ if (returnedType != null && !isSameMimeType(returnedType, DEFAULT_ATTACHMENT_MIME_TYPE)) {
+ return returnedType;
+ } else if (extension != null) {
+ for (String[] contentTypeMapEntry : MIME_TYPE_BY_EXTENSION_MAP) {
+ if (contentTypeMapEntry[0].equals(extension)) {
+ return contentTypeMapEntry[1];
+ }
+ }
+ }
+
+ return DEFAULT_ATTACHMENT_MIME_TYPE;
+ }
+
+ public static String getExtensionByMimeType(@NotNull String mimeType) {
+ String lowerCaseMimeType = mimeType.toLowerCase(Locale.US);
+ for (String[] contentTypeMapEntry : MIME_TYPE_BY_EXTENSION_MAP) {
+ if (contentTypeMapEntry[1].equals(lowerCaseMimeType)) {
+ return contentTypeMapEntry[0];
+ }
+ }
+
+ return null;
+ }
+
+ public static boolean isSupportedImageType(String mimeType) {
+ return isSameMimeType(mimeType, "image/jpeg") || isSameMimeType(mimeType, "image/png") ||
+ isSameMimeType(mimeType, "image/gif") || isSameMimeType(mimeType, "image/webp");
+ }
+
+ public static boolean isSupportedImageExtension(String filename) {
+ String mimeType = getMimeTypeByExtension(filename);
+ return isSupportedImageType(mimeType);
+ }
+
+ public static boolean isSameMimeType(String mimeType, String otherMimeType) {
+ return mimeType != null && mimeType.equalsIgnoreCase(otherMimeType);
+ }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/helper/ReplyToParser.java b/app/core/src/main/java/com/fsck/k9/helper/ReplyToParser.java
index 7cbb15e714f48e4bcd58c482dbbd3d065f951c10..2d53a81392df7b9501bca6149215bd9ccafab796 100644
--- a/app/core/src/main/java/com/fsck/k9/helper/ReplyToParser.java
+++ b/app/core/src/main/java/com/fsck/k9/helper/ReplyToParser.java
@@ -12,7 +12,6 @@ import com.fsck.k9.Account;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.Message.RecipientType;
-import com.fsck.k9.mail.internet.ListHeaders;
public class ReplyToParser {
diff --git a/app/core/src/main/java/com/fsck/k9/helper/UnsubscribeUri.kt b/app/core/src/main/java/com/fsck/k9/helper/UnsubscribeUri.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d335d2edd101774c91911e24a42541ccff130902
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/helper/UnsubscribeUri.kt
@@ -0,0 +1,10 @@
+package com.fsck.k9.helper
+
+import android.net.Uri
+
+sealed interface UnsubscribeUri {
+ val uri: Uri
+}
+
+data class MailtoUnsubscribeUri(override val uri: Uri) : UnsubscribeUri
+data class HttpsUnsubscribeUri(override val uri: Uri) : UnsubscribeUri
diff --git a/app/core/src/main/java/com/fsck/k9/logging/LogFileWriter.kt b/app/core/src/main/java/com/fsck/k9/logging/LogFileWriter.kt
index d81c9e16611ab7b3507d66392440b0b572e1c67a..831637708c60816293d6a86a109a3a11c7c14bb3 100644
--- a/app/core/src/main/java/com/fsck/k9/logging/LogFileWriter.kt
+++ b/app/core/src/main/java/com/fsck/k9/logging/LogFileWriter.kt
@@ -26,7 +26,7 @@ class LogcatLogFileWriter(
private fun writeLogBlocking(contentUri: Uri) {
Timber.v("Writing logcat output to content URI: %s", contentUri)
- val outputStream = contentResolver.openOutputStream(contentUri)
+ val outputStream = contentResolver.openOutputStream(contentUri, "wt")
?: error("Error opening contentUri for writing")
outputStream.use {
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/AttachmentViewInfo.java b/app/core/src/main/java/com/fsck/k9/mailstore/AttachmentViewInfo.java
index a3403094b28673759cf99cc12d12c40d62fad6cd..47a5fa81611e632310a3190ad240edbd3b06fa5b 100644
--- a/app/core/src/main/java/com/fsck/k9/mailstore/AttachmentViewInfo.java
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/AttachmentViewInfo.java
@@ -3,8 +3,8 @@ package com.fsck.k9.mailstore;
import android.net.Uri;
+import com.fsck.k9.helper.MimeTypeUtil;
import com.fsck.k9.mail.Part;
-import com.fsck.k9.mail.internet.MimeUtility;
public class AttachmentViewInfo {
@@ -48,8 +48,8 @@ public class AttachmentViewInfo {
return false;
}
- return MimeUtility.isSupportedImageType(mimeType) || (
- MimeUtility.isSameMimeType(MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE, mimeType) &&
- MimeUtility.isSupportedImageExtension(displayName));
+ return MimeTypeUtil.isSupportedImageType(mimeType) || (
+ MimeTypeUtil.isSameMimeType(MimeTypeUtil.DEFAULT_ATTACHMENT_MIME_TYPE, mimeType) &&
+ MimeTypeUtil.isSupportedImageExtension(displayName));
}
}
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java b/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java
index 74e66529b86ee4407b6468dad3e222bcc0b18b6d..ed4e39093c4bb5ac5ea6c4ae7842560d4072ce4b 100644
--- a/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java
@@ -4,6 +4,7 @@ package com.fsck.k9.mailstore;
import java.util.Collections;
import java.util.List;
+import com.fsck.k9.helper.UnsubscribeUri;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
@@ -24,6 +25,7 @@ public class MessageViewInfo {
public final List attachments;
public final String extraText;
public final List extraAttachments;
+ public final UnsubscribeUri preferredUnsubscribeUri;
public MessageViewInfo(
@@ -32,7 +34,8 @@ public class MessageViewInfo {
String text, List attachments,
CryptoResultAnnotation cryptoResultAnnotation,
AttachmentResolver attachmentResolver,
- String extraText, List extraAttachments) {
+ String extraText, List extraAttachments,
+ UnsubscribeUri preferredUnsubscribeUri) {
this.message = message;
this.isMessageIncomplete = isMessageIncomplete;
this.rootPart = rootPart;
@@ -44,13 +47,15 @@ public class MessageViewInfo {
this.attachments = attachments;
this.extraText = extraText;
this.extraAttachments = extraAttachments;
+ this.preferredUnsubscribeUri = preferredUnsubscribeUri;
}
static MessageViewInfo createWithExtractedContent(Message message, Part rootPart, boolean isMessageIncomplete,
- String text, List attachments, AttachmentResolver attachmentResolver) {
+ String text, List attachments, AttachmentResolver attachmentResolver,
+ UnsubscribeUri preferredUnsubscribeUri) {
return new MessageViewInfo(
message, isMessageIncomplete, rootPart, null, false, text, attachments, null, attachmentResolver, null,
- Collections.emptyList());
+ Collections.emptyList(), preferredUnsubscribeUri);
}
public static MessageViewInfo createWithErrorState(Message message, boolean isMessageIncomplete) {
@@ -59,7 +64,7 @@ public class MessageViewInfo {
Part emptyPart = new MimeBodyPart(emptyBody, "text/plain");
String subject = message.getSubject();
return new MessageViewInfo(message, isMessageIncomplete, emptyPart, subject, false, null, null, null, null,
- null, null);
+ null, null, null);
} catch (MessagingException e) {
throw new AssertionError(e);
}
@@ -67,7 +72,7 @@ public class MessageViewInfo {
public static MessageViewInfo createForMetadataOnly(Message message, boolean isMessageIncomplete) {
String subject = message.getSubject();
- return new MessageViewInfo(message, isMessageIncomplete, null, subject, false, null, null, null, null, null, null);
+ return new MessageViewInfo(message, isMessageIncomplete, null, subject, false, null, null, null, null, null, null, null);
}
MessageViewInfo withCryptoData(CryptoResultAnnotation rootPartAnnotation, String extraViewableText,
@@ -76,14 +81,15 @@ public class MessageViewInfo {
message, isMessageIncomplete, rootPart, subject, isSubjectEncrypted, text, attachments,
rootPartAnnotation,
attachmentResolver,
- extraViewableText, extraAttachmentInfos
+ extraViewableText, extraAttachmentInfos,
+ preferredUnsubscribeUri
);
}
MessageViewInfo withSubject(String subject, boolean isSubjectEncrypted) {
return new MessageViewInfo(
message, isMessageIncomplete, rootPart, subject, isSubjectEncrypted, text, attachments,
- cryptoResultAnnotation, attachmentResolver, extraText, extraAttachments
+ cryptoResultAnnotation, attachmentResolver, extraText, extraAttachments, preferredUnsubscribeUri
);
}
}
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java b/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java
index dc18704e76527a50819bc51d12c3f1db6ebf8e0c..b98c801464f971a3e9f03cf643cd9f2525abc25e 100644
--- a/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java
@@ -12,6 +12,8 @@ import androidx.annotation.WorkerThread;
import com.fsck.k9.CoreResourceProvider;
import com.fsck.k9.crypto.MessageCryptoStructureDetector;
+import com.fsck.k9.helper.ListUnsubscribeHelper;
+import com.fsck.k9.helper.UnsubscribeUri;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message;
@@ -144,8 +146,11 @@ public class MessageViewInfoExtractor {
boolean isMessageIncomplete =
!message.isSet(Flag.X_DOWNLOADED_FULL) || MessageExtractor.hasMissingParts(message);
+ UnsubscribeUri preferredUnsubscribeUri = ListUnsubscribeHelper.INSTANCE.getPreferredListUnsubscribeUri(message);
+
return MessageViewInfo.createWithExtractedContent(
- message, contentPart, isMessageIncomplete, viewable.html, attachmentInfos, attachmentResolver);
+ message, contentPart, isMessageIncomplete, viewable.html, attachmentInfos, attachmentResolver,
+ preferredUnsubscribeUri);
}
private ViewableExtractedText extractViewableAndAttachments(List parts,
diff --git a/app/core/src/main/java/com/fsck/k9/message/extractors/AttachmentInfoExtractor.java b/app/core/src/main/java/com/fsck/k9/message/extractors/AttachmentInfoExtractor.java
index 3f47997ab53f699c0fb2989366c725b24ec2d117..c570b5a256a0024a1226bd66f1e65e27bab6ce69 100644
--- a/app/core/src/main/java/com/fsck/k9/message/extractors/AttachmentInfoExtractor.java
+++ b/app/core/src/main/java/com/fsck/k9/message/extractors/AttachmentInfoExtractor.java
@@ -12,6 +12,7 @@ import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.fsck.k9.helper.MimeTypeUtil;
import timber.log.Timber;
import androidx.annotation.WorkerThread;
@@ -120,7 +121,7 @@ public class AttachmentInfoExtractor {
if (name == null) {
String extension = null;
if (mimeType != null) {
- extension = MimeUtility.getExtensionByMimeType(mimeType);
+ extension = MimeTypeUtil.getExtensionByMimeType(mimeType);
}
name = "noname" + ((extension != null) ? "." + extension : "");
}
diff --git a/app/core/src/main/java/com/fsck/k9/message/extractors/BasicPartInfoExtractor.kt b/app/core/src/main/java/com/fsck/k9/message/extractors/BasicPartInfoExtractor.kt
index 6992a87a4a1bd344786e1e913afb5fd74f76f898..9643a06f312dfb34c27ef799ac53175d35a6cf85 100644
--- a/app/core/src/main/java/com/fsck/k9/message/extractors/BasicPartInfoExtractor.kt
+++ b/app/core/src/main/java/com/fsck/k9/message/extractors/BasicPartInfoExtractor.kt
@@ -1,8 +1,8 @@
package com.fsck.k9.message.extractors
+import com.fsck.k9.helper.MimeTypeUtil
import com.fsck.k9.mail.Part
import com.fsck.k9.mail.internet.MimeParameterDecoder
-import com.fsck.k9.mail.internet.MimeUtility
import com.fsck.k9.mail.internet.MimeValue
private const val FALLBACK_NAME = "noname"
@@ -31,7 +31,7 @@ class BasicPartInfoExtractor {
}
private fun String?.toDisplayName(): String {
- val extension = this?.let { mimeType -> MimeUtility.getExtensionByMimeType(mimeType) }
+ val extension = this?.let { mimeType -> MimeTypeUtil.getExtensionByMimeType(mimeType) }
return if (extension.isNullOrEmpty()) FALLBACK_NAME else "$FALLBACK_NAME.$extension"
}
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt
index a074c61774b8d3299f890213de8c5e9d9ea7895a..d041c528fea3564871e3a1623623962de6057e56 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt
@@ -30,8 +30,9 @@ internal object NotificationIds {
}
fun getAllMessageNotificationIds(account: Account): List {
- val singleMessageNotificationIdRange = (0 until NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS) +
- (getBaseNotificationId(account) + OFFSET_NEW_MAIL_SINGLE)
+ val singleMessageNotificationIdRange = (0 until NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS).map { index ->
+ getBaseNotificationId(account) + OFFSET_NEW_MAIL_SINGLE + index
+ }
return singleMessageNotificationIdRange.toList() + getNewMailSummaryNotificationId(account)
}
diff --git a/app/core/src/main/java/com/fsck/k9/oauth/OAuthConfiguration.kt b/app/core/src/main/java/com/fsck/k9/oauth/OAuthConfiguration.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d3a11ed18c75f5faf31c2947e01ae9b382c0e4b5
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/oauth/OAuthConfiguration.kt
@@ -0,0 +1,9 @@
+package com.fsck.k9.oauth
+
+data class OAuthConfiguration(
+ val clientId: String,
+ val scopes: List,
+ val authorizationEndpoint: String,
+ val tokenEndpoint: String,
+ val redirectUri: String
+)
diff --git a/app/core/src/main/java/com/fsck/k9/oauth/OAuthConfigurationProvider.kt b/app/core/src/main/java/com/fsck/k9/oauth/OAuthConfigurationProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f2b8b31897ee60e647672d9ad1ed629846c9cbd4
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/oauth/OAuthConfigurationProvider.kt
@@ -0,0 +1,22 @@
+package com.fsck.k9.oauth
+
+class OAuthConfigurationProvider(
+ private val configurations: Map, OAuthConfiguration>,
+ private val googleConfiguration: OAuthConfiguration
+) {
+ private val hostnameMapping: Map = buildMap {
+ for ((hostnames, configuration) in configurations) {
+ for (hostname in hostnames) {
+ put(hostname.lowercase(), configuration)
+ }
+ }
+ }
+
+ fun getConfiguration(hostname: String): OAuthConfiguration? {
+ return hostnameMapping[hostname.lowercase()]
+ }
+
+ fun isGoogle(hostname: String): Boolean {
+ return getConfiguration(hostname) == googleConfiguration
+ }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/AccountManager.kt b/app/core/src/main/java/com/fsck/k9/preferences/AccountManager.kt
index 5ae56c7e1d235458c7a47d2cc9d204193821e60a..4037932c770dcb9d0fc0bd4717aeeb4936fb1383 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/AccountManager.kt
+++ b/app/core/src/main/java/com/fsck/k9/preferences/AccountManager.kt
@@ -13,4 +13,5 @@ interface AccountManager {
fun moveAccount(account: Account, newPosition: Int)
fun addOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener)
fun removeOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener)
+ fun saveAccount(account: Account)
}
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java b/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java
index bb939fe095768711d7f0aa7ad29ce8b82566bc95..519b014da488fe1601e7f9285f9955be146f9c06 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java
@@ -207,13 +207,16 @@ public class AccountSettingsDescriptions {
new V(53, new StringSetting(null))
));
s.put("useCompression.MOBILE", Settings.versions(
- new V(1, new BooleanSetting(true))
+ new V(1, new BooleanSetting(true)),
+ new V(81, null)
));
s.put("useCompression.OTHER", Settings.versions(
- new V(1, new BooleanSetting(true))
+ new V(1, new BooleanSetting(true)),
+ new V(81, null)
));
s.put("useCompression.WIFI", Settings.versions(
- new V(1, new BooleanSetting(true))
+ new V(1, new BooleanSetting(true)),
+ new V(81, null)
));
s.put("vibrate", Settings.versions(
new V(1, new BooleanSetting(false))
@@ -270,6 +273,9 @@ public class AccountSettingsDescriptions {
s.put("notificationLight", Settings.versions(
new V(80, new EnumSetting<>(NotificationLight.class, NotificationLight.Disabled))
));
+ s.put("useCompression", Settings.versions(
+ new V(81, new BooleanSetting(true))
+ ));
// note that there is no setting for openPgpProvider, because this will have to be set up together
// with the actual provider after import anyways.
@@ -280,6 +286,7 @@ public class AccountSettingsDescriptions {
u.put(54, new SettingsUpgraderV54());
u.put(74, new SettingsUpgraderV74());
u.put(80, new SettingsUpgraderV80());
+ u.put(81, new SettingsUpgraderV81());
UPGRADERS = Collections.unmodifiableMap(u);
}
@@ -546,4 +553,22 @@ public class AccountSettingsDescriptions {
return SetsKt.setOf("led", "ledColor");
}
}
+
+ /**
+ * Rewrite the per-network type IMAP compression settings to a single setting.
+ */
+ private static class SettingsUpgraderV81 implements SettingsUpgrader {
+ @Override
+ public Set upgrade(Map settings) {
+ Boolean useCompressionWifi = (Boolean) settings.get("useCompression.WIFI");
+ Boolean useCompressionMobile = (Boolean) settings.get("useCompression.MOBILE");
+ Boolean useCompressionOther = (Boolean) settings.get("useCompression.OTHER");
+
+ boolean useCompression = useCompressionWifi != null && useCompressionMobile != null &&
+ useCompressionOther != null && useCompressionWifi && useCompressionMobile && useCompressionOther;
+ settings.put("useCompression", useCompression);
+
+ return SetsKt.setOf("useCompression.WIFI", "useCompression.MOBILE", "useCompression.OTHER");
+ }
+ }
}
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
index 8124a0c345ebdad258bd3cd9909bf13af8f892fc..db1674f7c4abd4bdc6e0dd336f3b9fa33ed21a73 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
@@ -36,7 +36,7 @@ public class Settings {
*
* @see SettingsExporter
*/
- public static final int VERSION = 80;
+ public static final int VERSION = 81;
static Map validate(int version, Map> settings,
Map importedSettings, boolean useDefaultValues) {
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/SettingsExporter.kt b/app/core/src/main/java/com/fsck/k9/preferences/SettingsExporter.kt
index 39983b13918858999acb1173a9d59011af00dff6..08bc5bbefc060b85c70cfa54055c5990636febae 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/SettingsExporter.kt
+++ b/app/core/src/main/java/com/fsck/k9/preferences/SettingsExporter.kt
@@ -33,7 +33,7 @@ class SettingsExporter(
updateNotificationSettings(accountUuids)
try {
- contentResolver.openOutputStream(uri)!!.use { outputStream ->
+ contentResolver.openOutputStream(uri, "wt")!!.use { outputStream ->
exportPreferences(outputStream, includeGlobals, accountUuids)
}
} catch (e: Exception) {
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java b/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java
index 661ab013cf5cdc4d31cb931d623d33a9b8a66e67..5e18598c3755327bfeffacbb024f5fa38b48b701 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java
@@ -77,17 +77,19 @@ public class SettingsImporter {
public final AccountDescription original;
public final AccountDescription imported;
public final boolean overwritten;
+ public final boolean authorizationNeeded;
public final boolean incomingPasswordNeeded;
public final boolean outgoingPasswordNeeded;
public final String incomingServerName;
public final String outgoingServerName;
private AccountDescriptionPair(AccountDescription original, AccountDescription imported,
- boolean overwritten, boolean incomingPasswordNeeded, boolean outgoingPasswordNeeded,
- String incomingServerName, String outgoingServerName) {
+ boolean overwritten, boolean authorizationNeeded, boolean incomingPasswordNeeded,
+ boolean outgoingPasswordNeeded, String incomingServerName, String outgoingServerName) {
this.original = original;
this.imported = imported;
this.overwritten = overwritten;
+ this.authorizationNeeded = authorizationNeeded;
this.incomingPasswordNeeded = incomingPasswordNeeded;
this.outgoingPasswordNeeded = outgoingPasswordNeeded;
this.incomingServerName = incomingServerName;
@@ -372,8 +374,11 @@ public class SettingsImporter {
String incomingServerName = incoming.host;
boolean incomingPasswordNeeded = AuthType.EXTERNAL != incoming.authenticationType &&
+ AuthType.XOAUTH2 != incoming.authenticationType &&
(incoming.password == null || incoming.password.isEmpty());
+ boolean authorizationNeeded = incoming.authenticationType == AuthType.XOAUTH2;
+
String incomingServerType = ServerTypeConverter.toServerSettingsType(account.incoming.type);
if (account.outgoing == null && !incomingServerType.equals(Protocols.WEBDAV)) {
// All account types except WebDAV need to provide outgoing server settings
@@ -395,15 +400,18 @@ public class SettingsImporter {
*/
String outgoingServerType = ServerTypeConverter.toServerSettingsType(outgoing.type);
outgoingPasswordNeeded = AuthType.EXTERNAL != outgoing.authenticationType &&
+ AuthType.XOAUTH2 != outgoing.authenticationType &&
!outgoingServerType.equals(Protocols.WEBDAV) &&
outgoing.username != null &&
!outgoing.username.isEmpty() &&
(outgoing.password == null || outgoing.password.isEmpty());
+ authorizationNeeded |= outgoing.authenticationType == AuthType.XOAUTH2;
+
outgoingServerName = outgoing.host;
}
- boolean createAccountDisabled = incomingPasswordNeeded || outgoingPasswordNeeded;
+ boolean createAccountDisabled = incomingPasswordNeeded || outgoingPasswordNeeded || authorizationNeeded;
if (createAccountDisabled) {
editor.putBoolean(accountKeyPrefix + "enabled", false);
}
@@ -465,7 +473,7 @@ public class SettingsImporter {
putString(editor, accountKeyPrefix + "messagesNotificationChannelVersion", messageNotificationChannelVersion);
AccountDescription imported = new AccountDescription(accountName, uuid);
- return new AccountDescriptionPair(original, imported, mergeImportedAccount,
+ return new AccountDescriptionPair(original, imported, mergeImportedAccount, authorizationNeeded,
incomingPasswordNeeded, outgoingPasswordNeeded, incomingServerName, outgoingServerName);
}
@@ -1061,11 +1069,12 @@ public class SettingsImporter {
String type = ServerTypeConverter.toServerSettingsType(importedServer.type);
int port = convertPort(importedServer.port);
ConnectionSecurity connectionSecurity = convertConnectionSecurity(importedServer.connectionSecurity);
+ String password = importedServer.authenticationType == AuthType.XOAUTH2 ? "" : importedServer.password;
Map extra = importedServer.extras != null ?
unmodifiableMap(importedServer.extras.settings) : emptyMap();
return new ServerSettings(type, importedServer.host, port, connectionSecurity,
- importedServer.authenticationType, importedServer.username, importedServer.password,
+ importedServer.authenticationType, importedServer.username, password,
importedServer.clientCertificateAlias, extra);
}
diff --git a/app/core/src/main/java/com/fsck/k9/provider/AttachmentProvider.java b/app/core/src/main/java/com/fsck/k9/provider/AttachmentProvider.java
index bee162ae0eac2609976edacd849612f7d4c16f27..21a91725b760ea0ba15956da26974525477e1364 100644
--- a/app/core/src/main/java/com/fsck/k9/provider/AttachmentProvider.java
+++ b/app/core/src/main/java/com/fsck/k9/provider/AttachmentProvider.java
@@ -15,13 +15,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.fsck.k9.DI;
+import com.fsck.k9.helper.MimeTypeUtil;
import com.fsck.k9.mailstore.LocalStoreProvider;
import timber.log.Timber;
import com.fsck.k9.Account;
import com.fsck.k9.Preferences;
import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mailstore.LocalStore;
import com.fsck.k9.mailstore.LocalStore.AttachmentInfo;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
@@ -156,7 +156,7 @@ public class AttachmentProvider extends ContentProvider {
}
} catch (MessagingException e) {
Timber.e(e, "Unable to retrieve LocalStore for %s", account);
- type = MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE;
+ type = MimeTypeUtil.DEFAULT_ATTACHMENT_MIME_TYPE;
}
return type;
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/ListHeadersTest.java b/app/core/src/test/java/com/fsck/k9/helper/ListHeadersTest.java
similarity index 95%
rename from mail/common/src/test/java/com/fsck/k9/mail/internet/ListHeadersTest.java
rename to app/core/src/test/java/com/fsck/k9/helper/ListHeadersTest.java
index ba6bfbee31bf1f6d40a61f43c769d6eef49a8131..1f0fdfcd6eca00f4267be2412ba7bc37222e92f3 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/internet/ListHeadersTest.java
+++ b/app/core/src/test/java/com/fsck/k9/helper/ListHeadersTest.java
@@ -1,18 +1,17 @@
-package com.fsck.k9.mail.internet;
+package com.fsck.k9.helper;
+import com.fsck.k9.RobolectricTest;
import com.fsck.k9.mail.Address;
-import com.fsck.k9.mail.K9LibRobolectricTestRunner;
import com.fsck.k9.mail.Message;
+import com.fsck.k9.mail.internet.MimeMessage;
import org.junit.Test;
-import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-@RunWith(K9LibRobolectricTestRunner.class)
-public class ListHeadersTest {
+public class ListHeadersTest extends RobolectricTest {
private static final String[] TEST_EMAIL_ADDRESSES = new String[] {
"prettyandsimple@example.com",
"very.common@example.com",
diff --git a/app/core/src/test/java/com/fsck/k9/helper/ListUnsubscribeHelperTest.kt b/app/core/src/test/java/com/fsck/k9/helper/ListUnsubscribeHelperTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4c327aefe1a08526a7b914140ebdc6d7ff265203
--- /dev/null
+++ b/app/core/src/test/java/com/fsck/k9/helper/ListUnsubscribeHelperTest.kt
@@ -0,0 +1,95 @@
+package com.fsck.k9.helper
+
+import androidx.core.net.toUri
+import com.fsck.k9.RobolectricTest
+import com.fsck.k9.mail.internet.MimeMessage
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertNull
+import org.junit.Test
+
+class ListUnsubscribeHelperTest : RobolectricTest() {
+ @Test
+ fun `get list unsubscribe url - should accept mailto`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ""
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertThat(result).isEqualTo(MailtoUnsubscribeUri("mailto:unsubscribe@example.com".toUri()))
+ }
+
+ @Test
+ fun `get list unsubscribe url - should prefer mailto 1`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ", "
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertThat(result).isEqualTo(MailtoUnsubscribeUri("mailto:unsubscribe@example.com".toUri()))
+ }
+
+ @Test
+ fun `get list unsubscribe url - should prefer mailto 2`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ", "
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertThat(result).isEqualTo(MailtoUnsubscribeUri("mailto:unsubscribe@example.com".toUri()))
+ }
+
+ @Test
+ fun `get list unsubscribe url - should allow https if no mailto`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ""
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertThat(result).isEqualTo(HttpsUnsubscribeUri("https://example.com/unsubscribe".toUri()))
+ }
+
+ @Test
+ fun `get list unsubscribe url - should correctly parse uncommon urls`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ""
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertThat(result).isEqualTo(HttpsUnsubscribeUri("https://domain.example/one,two".toUri()))
+ }
+
+ @Test
+ fun `get list unsubscribe url - should ignore unsafe entries 1`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ""
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertNull(result)
+ }
+
+ @Test
+ fun `get list unsubscribe url - should ignore unsafe entries 2`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ", "
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertThat(result).isEqualTo(HttpsUnsubscribeUri("https://example.com/unsubscribe".toUri()))
+ }
+
+ @Test
+ fun `get list unsubscribe url - should ignore empty`() {
+ val message = buildMimeMessageWithListUnsubscribeValue(
+ ""
+ )
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertNull(result)
+ }
+
+ @Test
+ fun `get list unsubscribe url - should ignore missing header`() {
+ val message = MimeMessage()
+ val result = ListUnsubscribeHelper.getPreferredListUnsubscribeUri(message)
+ assertNull(result)
+ }
+
+ private fun buildMimeMessageWithListUnsubscribeValue(value: String): MimeMessage {
+ val message = MimeMessage()
+ message.addHeader("List-Unsubscribe", value)
+ return message
+ }
+}
diff --git a/mail/common/src/test/java/com/fsck/k9/helper/MailToTest.java b/app/core/src/test/java/com/fsck/k9/helper/MailToTest.java
similarity index 97%
rename from mail/common/src/test/java/com/fsck/k9/helper/MailToTest.java
rename to app/core/src/test/java/com/fsck/k9/helper/MailToTest.java
index bca04c1c44b875fb71ae1ffeb4ce980f4e7bb2f0..d388d4136d5716ce093e802f3e88612f90617ef4 100644
--- a/mail/common/src/test/java/com/fsck/k9/helper/MailToTest.java
+++ b/app/core/src/test/java/com/fsck/k9/helper/MailToTest.java
@@ -1,20 +1,18 @@
package com.fsck.k9.helper;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
import android.net.Uri;
+import com.fsck.k9.RobolectricTest;
import com.fsck.k9.helper.MailTo.CaseInsensitiveParamWrapper;
import com.fsck.k9.mail.Address;
-import com.fsck.k9.mail.K9LibRobolectricTestRunner;
-
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -23,8 +21,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-@RunWith(K9LibRobolectricTestRunner.class)
-public class MailToTest {
+public class MailToTest extends RobolectricTest {
@Rule
public ExpectedException exception = ExpectedException.none();
@@ -235,4 +232,4 @@ public class MailToTest {
}
-}
\ No newline at end of file
+}
diff --git a/app/core/src/test/java/com/fsck/k9/helper/ReplyToParserTest.java b/app/core/src/test/java/com/fsck/k9/helper/ReplyToParserTest.java
index 501b2731b965eb759a871ff1604915109a80719d..6093ed15fdc6b0536d8ec36a0ae8b41e00513399 100644
--- a/app/core/src/test/java/com/fsck/k9/helper/ReplyToParserTest.java
+++ b/app/core/src/test/java/com/fsck/k9/helper/ReplyToParserTest.java
@@ -10,7 +10,6 @@ import com.fsck.k9.helper.ReplyToParser.ReplyToAddresses;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.Message.RecipientType;
-import com.fsck.k9.mail.internet.ListHeaders;
import org.junit.Before;
import org.junit.Test;
diff --git a/app/core/src/test/java/com/fsck/k9/logging/LogcatLogFileWriterTest.kt b/app/core/src/test/java/com/fsck/k9/logging/LogcatLogFileWriterTest.kt
index 2fcc6e93ff9ebc590d804dd821a2a79ef2a1b473..a4229bbb0d8fbd174172cc0efc5f9498c1ee16df 100644
--- a/app/core/src/test/java/com/fsck/k9/logging/LogcatLogFileWriterTest.kt
+++ b/app/core/src/test/java/com/fsck/k9/logging/LogcatLogFileWriterTest.kt
@@ -57,13 +57,13 @@ class LogcatLogFileWriterTest {
private fun createContentResolver(): ContentResolver {
return mock {
- on { openOutputStream(contentUri) } doReturn outputStream
+ on { openOutputStream(contentUri, "wt") } doReturn outputStream
}
}
private fun createThrowingContentResolver(exception: Exception): ContentResolver {
return mock {
- on { openOutputStream(contentUri) } doAnswer { throw exception }
+ on { openOutputStream(contentUri, "wt") } doAnswer { throw exception }
}
}
diff --git a/app/core/src/test/java/com/fsck/k9/notification/NotificationIdsTest.kt b/app/core/src/test/java/com/fsck/k9/notification/NotificationIdsTest.kt
index 9c1946b573f1f1e4d344f1f25b1f7a3ac40cdbdb..9c326bc52246e53a681fe8782596b0054160625a 100644
--- a/app/core/src/test/java/com/fsck/k9/notification/NotificationIdsTest.kt
+++ b/app/core/src/test/java/com/fsck/k9/notification/NotificationIdsTest.kt
@@ -81,11 +81,22 @@ class NotificationIdsTest {
assertThat(maxNotificationId1 + 1).isEqualTo(minNotificationId2)
}
- fun getGeneralNotificationIds(): List {
+ @Test
+ fun `all message notification IDs`() {
+ val account = createAccount(1)
+
+ val notificationIds = NotificationIds.getAllMessageNotificationIds(account)
+
+ assertThat(notificationIds).containsExactlyElementsIn(
+ getNewMessageNotificationIds(account) + NotificationIds.getNewMailSummaryNotificationId(account)
+ )
+ }
+
+ private fun getGeneralNotificationIds(): List {
return listOf(NotificationIds.PUSH_NOTIFICATION_ID)
}
- fun getAccountNotificationIds(account: Account): List {
+ private fun getAccountNotificationIds(account: Account): List {
return listOf(
NotificationIds.getSendFailedNotificationId(account),
NotificationIds.getCertificateErrorNotificationId(account, true),
@@ -94,7 +105,11 @@ class NotificationIdsTest {
NotificationIds.getAuthenticationErrorNotificationId(account, false),
NotificationIds.getFetchingMailNotificationId(account),
NotificationIds.getNewMailSummaryNotificationId(account),
- ) + (0 until MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS).map { index ->
+ ) + getNewMessageNotificationIds(account)
+ }
+
+ private fun getNewMessageNotificationIds(account: Account): List {
+ return (0 until MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS).map { index ->
NotificationIds.getSingleMessageNotificationId(account, index)
}
}
diff --git a/app/core/src/test/java/com/fsck/k9/sasl/OAuthBearerTest.kt b/app/core/src/test/java/com/fsck/k9/sasl/OAuthBearerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ff3bc7204da5f44df8c75ec3284d5d2191880aba
--- /dev/null
+++ b/app/core/src/test/java/com/fsck/k9/sasl/OAuthBearerTest.kt
@@ -0,0 +1,31 @@
+package com.fsck.k9.sasl
+
+import com.google.common.truth.Truth.assertThat
+import okio.ByteString.Companion.decodeBase64
+import org.junit.Test
+
+class OAuthBearerTest {
+ @Test
+ fun `username that does not need encoding`() {
+ val username = "user@domain.example"
+ val token = "token"
+
+ val result = buildOAuthBearerInitialClientResponse(username, token)
+
+ assertThat(result).isEqualTo("bixhPXVzZXJAZG9tYWluLmV4YW1wbGUsAWF1dGg9QmVhcmVyIHRva2VuAQE=")
+ assertThat(result.decodeBase64()?.utf8())
+ .isEqualTo("n,a=user@domain.example,\u0001auth=Bearer token\u0001\u0001")
+ }
+
+ @Test
+ fun `username contains equal sign that needs to be encoded`() {
+ val username = "user=name@domain.example"
+ val token = "token"
+
+ val result = buildOAuthBearerInitialClientResponse(username, token)
+
+ assertThat(result).isEqualTo("bixhPXVzZXI9M0RuYW1lQGRvbWFpbi5leGFtcGxlLAFhdXRoPUJlYXJlciB0b2tlbgEB")
+ assertThat(result.decodeBase64()?.utf8())
+ .isEqualTo("n,a=user=3Dname@domain.example,\u0001auth=Bearer token\u0001\u0001")
+ }
+}
diff --git a/app/k9mail/build.gradle b/app/k9mail/build.gradle
index 2daf4845b98fc41282dc07cefa05b7ae90450192..e55916d89a4eaf065353f5e29641f2aa305dcc54 100644
--- a/app/k9mail/build.gradle
+++ b/app/k9mail/build.gradle
@@ -48,8 +48,8 @@ android {
applicationId "foundation.e.mail"
testApplicationId "foundation.e.mail.tests"
- versionCode 30000
- versionName '6.001'
+ versionCode 31000
+ versionName '6.100'
// Keep in sync with the resource string array 'supported_languages'
resConfigs "in", "br", "ca", "cs", "cy", "da", "de", "et", "en", "en_GB", "es", "eo", "eu", "fr", "gd", "gl",
@@ -66,6 +66,12 @@ android {
signingConfigs {
release
+ debug {
+ keyAlias = "androiddebugkey"
+ keyPassword = "android"
+ storeFile = file("$rootProject.projectDir/debug.keystore")
+ storePassword = "android"
+ }
}
buildTypes {
@@ -77,16 +83,26 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- buildConfigField "boolean", "DEVELOPER_MODE", "false"
+ buildConfigField "String", "OAUTH_GMAIL_CLIENT_ID", "\"262622259280-hhmh92rhklkg2k1tjil69epo0o9a12jm.apps.googleusercontent.com\""
+ buildConfigField "String", "OAUTH_YAHOO_CLIENT_ID", "\"dj0yJmk9aHNUb3d2MW5TQnpRJmQ9WVdrOWVYbHpaRWM0YkdnbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PWIz\""
+ buildConfigField "String", "OAUTH_AOL_CLIENT_ID", "\"dj0yJmk9dUNqYXZhYWxOYkdRJmQ9WVdrOU1YQnZVRFZoY1ZrbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PWIw\""
+
+ manifestPlaceholders = ['appAuthRedirectScheme': 'foundation.e.mail']
}
debug {
applicationIdSuffix ".debug"
testCoverageEnabled rootProject.testCoverage
+ signingConfig signingConfigs.debug
+
minifyEnabled false
- buildConfigField "boolean", "DEVELOPER_MODE", "true"
+ buildConfigField "String", "OAUTH_GMAIL_CLIENT_ID", "\"262622259280-5qb3vtj68d5dtudmaif4g9vd3cpar8r3.apps.googleusercontent.com\""
+ buildConfigField "String", "OAUTH_YAHOO_CLIENT_ID", "\"dj0yJmk9ejRCRU1ybmZjQlVBJmQ9WVdrOVVrZEViak4xYmxZbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTZj\""
+ buildConfigField "String", "OAUTH_AOL_CLIENT_ID", "\"dj0yJmk9cHYydkJkTUxHcXlYJmQ9WVdrOWVHZHhVVXN4VVV3bWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTdm\""
+
+ manifestPlaceholders = ['appAuthRedirectScheme': 'foundation.e.mail.debug']
}
}
diff --git a/app/k9mail/src/main/AndroidManifest.xml b/app/k9mail/src/main/AndroidManifest.xml
index 494ee2619ebc3b09d36825b45a769d3e48848139..575306f922f42d9c7c7989c82b9d2a75223af114 100644
--- a/app/k9mail/src/main/AndroidManifest.xml
+++ b/app/k9mail/src/main/AndroidManifest.xml
@@ -304,6 +304,10 @@
+
+
() }
single { OpenPgpEncryptionExtractor.newInstance() }
single { K9StoragePersister(get()) }
+ single { createOAuthConfigurationProvider() }
}
val appModules = listOf(
@@ -39,6 +40,5 @@ val appModules = listOf(
notificationModule,
resourcesModule,
backendsModule,
- storageModule,
- oauth2Module
+ storageModule
)
diff --git a/app/k9mail/src/main/java/com/fsck/k9/auth/OAuthConfigurations.kt b/app/k9mail/src/main/java/com/fsck/k9/auth/OAuthConfigurations.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5d69afece3b09d928985fb3a373cf7cf33978aa0
--- /dev/null
+++ b/app/k9mail/src/main/java/com/fsck/k9/auth/OAuthConfigurations.kt
@@ -0,0 +1,39 @@
+package com.fsck.k9.auth
+
+import com.fsck.k9.BuildConfig
+import com.fsck.k9.oauth.OAuthConfiguration
+import com.fsck.k9.oauth.OAuthConfigurationProvider
+
+fun createOAuthConfigurationProvider(): OAuthConfigurationProvider {
+ val redirectUriSlash = BuildConfig.APPLICATION_ID + ":/oauth2redirect"
+ val redirectUriDoubleSlash = BuildConfig.APPLICATION_ID + "://oauth2redirect"
+
+ val googleConfig = OAuthConfiguration(
+ clientId = BuildConfig.OAUTH_GMAIL_CLIENT_ID,
+ scopes = listOf("https://mail.google.com/"),
+ authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenEndpoint = "https://oauth2.googleapis.com/token",
+ redirectUri = redirectUriSlash
+ )
+
+ return OAuthConfigurationProvider(
+ configurations = mapOf(
+ listOf("imap.gmail.com", "imap.googlemail.com", "smtp.gmail.com", "smtp.googlemail.com") to googleConfig,
+ listOf("imap.mail.yahoo.com", "smtp.mail.yahoo.com") to OAuthConfiguration(
+ clientId = BuildConfig.OAUTH_YAHOO_CLIENT_ID,
+ scopes = listOf("mail-w"),
+ authorizationEndpoint = "https://api.login.yahoo.com/oauth2/request_auth",
+ tokenEndpoint = "https://api.login.yahoo.com/oauth2/get_token",
+ redirectUri = redirectUriDoubleSlash
+ ),
+ listOf("imap.aol.com", "smtp.aol.com") to OAuthConfiguration(
+ clientId = BuildConfig.OAUTH_AOL_CLIENT_ID,
+ scopes = listOf("mail-w"),
+ authorizationEndpoint = "https://api.login.aol.com/oauth2/request_auth",
+ tokenEndpoint = "https://api.login.aol.com/oauth2/get_token",
+ redirectUri = redirectUriDoubleSlash
+ ),
+ ),
+ googleConfiguration = googleConfig
+ )
+}
diff --git a/app/k9mail/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt b/app/k9mail/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt
index c62a638d3ef6d22d0585342df10cc8099b38754a..2b14b1b0ffdaa751d3ad2c0900115b75bcd6cdcd 100644
--- a/app/k9mail/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt
+++ b/app/k9mail/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt
@@ -1,14 +1,12 @@
package com.fsck.k9.backends
import android.content.Context
-import android.net.ConnectivityManager
import com.fsck.k9.Account
import com.fsck.k9.backend.BackendFactory
import com.fsck.k9.backend.api.Backend
import com.fsck.k9.backend.imap.ImapBackend
import com.fsck.k9.backend.imap.ImapPushConfigProvider
-import com.fsck.k9.mail.NetworkType
-import com.fsck.k9.mail.oauth.OAuth2TokenProvider
+import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.power.PowerManager
import com.fsck.k9.mail.ssl.TrustedSocketFactory
import com.fsck.k9.mail.store.imap.IdleRefreshManager
@@ -22,13 +20,12 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class ImapBackendFactory(
- private val context: Context,
private val accountManager: AccountManager,
private val powerManager: PowerManager,
private val idleRefreshManager: IdleRefreshManager,
private val backendStorageFactory: K9BackendStorageFactory,
private val trustedSocketFactory: TrustedSocketFactory,
- private val oAuth2TokenProvider: OAuth2TokenProvider
+ private val context: Context
) : BackendFactory {
override fun createBackend(account: Account): Backend {
val accountName = account.displayName
@@ -49,13 +46,17 @@ class ImapBackendFactory(
}
private fun createImapStore(account: Account): ImapStore {
- val oAuth2TokenProvider: OAuth2TokenProvider? = oAuth2TokenProvider
+ val oAuth2TokenProvider = if (account.incomingServerSettings.authenticationType == AuthType.XOAUTH2) {
+ RealOAuth2TokenProvider(context, accountManager, account)
+ } else {
+ null
+ }
+
val config = createImapStoreConfig(account)
return ImapStore.create(
account.incomingServerSettings,
config,
trustedSocketFactory,
- context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,
oAuth2TokenProvider
)
}
@@ -67,13 +68,18 @@ class ImapBackendFactory(
override fun isSubscribedFoldersOnly() = account.isSubscribedFoldersOnly
- override fun useCompression(type: NetworkType) = account.useCompression(type)
+ override fun useCompression() = account.useCompression
}
}
private fun createSmtpTransport(account: Account): SmtpTransport {
val serverSettings = account.outgoingServerSettings
- val oauth2TokenProvider: OAuth2TokenProvider? = oAuth2TokenProvider
+ val oauth2TokenProvider = if (serverSettings.authenticationType == AuthType.XOAUTH2) {
+ RealOAuth2TokenProvider(context, accountManager, account)
+ } else {
+ null
+ }
+
return SmtpTransport(serverSettings, trustedSocketFactory, oauth2TokenProvider)
}
diff --git a/app/k9mail/src/main/java/com/fsck/k9/backends/KoinModule.kt b/app/k9mail/src/main/java/com/fsck/k9/backends/KoinModule.kt
index 1b947b8ded21e02b921ba3305c4967a5552a0914..b23177632c14a23eef1754d6fc761f93ec4f7260 100644
--- a/app/k9mail/src/main/java/com/fsck/k9/backends/KoinModule.kt
+++ b/app/k9mail/src/main/java/com/fsck/k9/backends/KoinModule.kt
@@ -5,7 +5,9 @@ import app.k9mail.dev.developmentModuleAdditions
import com.fsck.k9.backend.BackendManager
import com.fsck.k9.backend.imap.BackendIdleRefreshManager
import com.fsck.k9.backend.imap.SystemAlarmManager
+import com.fsck.k9.helper.DefaultTrustedSocketFactory
import com.fsck.k9.mail.store.imap.IdleRefreshManager
+import com.fsck.k9.mail.store.webdav.SniHostSetter
import org.koin.dsl.module
val backendsModule = module {
@@ -20,19 +22,30 @@ val backendsModule = module {
}
single {
ImapBackendFactory(
- context = get(),
accountManager = get(),
powerManager = get(),
idleRefreshManager = get(),
backendStorageFactory = get(),
trustedSocketFactory = get(),
- oAuth2TokenProvider = get(),
+ context = get()
)
}
single { AndroidAlarmManager(context = get(), alarmManager = get()) }
single { BackendIdleRefreshManager(alarmManager = get()) }
single { Pop3BackendFactory(get(), get()) }
- single { WebDavBackendFactory(get(), get(), get()) }
+ single {
+ WebDavBackendFactory(
+ backendStorageFactory = get(),
+ trustManagerFactory = get(),
+ sniHostSetter = get(),
+ folderRepository = get()
+ )
+ }
+ single {
+ SniHostSetter { factory, socket, hostname ->
+ DefaultTrustedSocketFactory.setSniHost(factory, socket, hostname)
+ }
+ }
developmentModuleAdditions()
}
diff --git a/app/k9mail/src/main/java/com/fsck/k9/backends/RealOAuth2TokenProvider.kt b/app/k9mail/src/main/java/com/fsck/k9/backends/RealOAuth2TokenProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b802d8cd6fb46534ead668d50dcd06422ef3f6d1
--- /dev/null
+++ b/app/k9mail/src/main/java/com/fsck/k9/backends/RealOAuth2TokenProvider.kt
@@ -0,0 +1,120 @@
+package com.fsck.k9.backends
+
+import android.content.Context
+import android.os.Bundle
+import android.os.Handler
+import android.os.HandlerThread
+import com.fsck.k9.Account
+import com.fsck.k9.mail.AuthenticationFailedException
+import com.fsck.k9.mail.oauth.OAuth2TokenProvider
+import com.fsck.k9.preferences.AccountManager
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import net.openid.appauth.AuthState
+import net.openid.appauth.AuthorizationException
+import net.openid.appauth.AuthorizationService
+
+class RealOAuth2TokenProvider(
+ context: Context,
+ private val accountManager: AccountManager,
+ private val account: Account
+) : OAuth2TokenProvider {
+
+ companion object {
+ const val GOOGLE_ACCOUNT_TYPE = "e.foundation.webdav.google"
+ const val EELO_ACCOUNT_TYPE = "e.foundation.webdav.eelo"
+ const val ACCOUNT_EMAIL_ADDRESS_KEY = "email_address"
+ const val AUTH_TOKEN_TYPE = "oauth2-access-token"
+ }
+
+ private val authService = AuthorizationService(context)
+ private var requestFreshToken = false
+
+ private val osAccountManager by lazy {
+ android.accounts.AccountManager.get(context)
+ }
+
+ override fun getToken(email: String, timeoutMillis: Long): String {
+ getTokenFromAccountManager(email)?.let {
+ return it
+ }
+
+ val latch = CountDownLatch(1)
+ var token: String? = null
+ var exception: AuthorizationException? = null
+
+ val authState = account.oAuthState?.let { AuthState.jsonDeserialize(it) }
+ ?: throw AuthenticationFailedException("Login required")
+
+ if (requestFreshToken) {
+ authState.needsTokenRefresh = true
+ }
+
+ val oldAccessToken = authState.accessToken
+
+ authState.performActionWithFreshTokens(authService) { accessToken: String?, _, authException: AuthorizationException? ->
+ token = accessToken
+ exception = authException
+
+ latch.countDown()
+ }
+
+ latch.await(timeoutMillis, TimeUnit.MILLISECONDS)
+
+ if (exception != null || token != oldAccessToken) {
+ requestFreshToken = false
+ account.oAuthState = authState.jsonSerializeString()
+ accountManager.saveAccount(account)
+ }
+
+ exception?.let { authException ->
+ throw AuthenticationFailedException(
+ message = "Failed to fetch an access token",
+ throwable = authException,
+ messageFromServer = authException.error
+ )
+ }
+
+ return token ?: throw AuthenticationFailedException("Failed to fetch an access token")
+ }
+
+ override fun invalidateToken() {
+ requestFreshToken = true
+ }
+
+ private fun getTokenFromAccountManager(emailId: String): String? {
+
+ var authToken: String? = null
+ val handlerThread = HandlerThread("callbackThread")
+
+ handlerThread.start()
+ val handler = Handler(handlerThread.looper)
+
+ val accounts: ArrayList = arrayListOf()
+ for (account in osAccountManager.getAccountsByType(GOOGLE_ACCOUNT_TYPE)) {
+ accounts.add(account)
+ }
+
+ for (account in osAccountManager.getAccountsByType(EELO_ACCOUNT_TYPE)) {
+ accounts.add(account)
+ }
+
+ for (account in accounts) {
+ val accountEmailId = osAccountManager.getUserData(account, ACCOUNT_EMAIL_ADDRESS_KEY)
+ if (emailId == accountEmailId) {
+ val latch = CountDownLatch(1)
+ osAccountManager.getAuthToken(
+ account, AUTH_TOKEN_TYPE, Bundle(), false,
+ { future ->
+ authToken = future?.result?.getString(android.accounts.AccountManager.KEY_AUTHTOKEN)
+ latch.countDown()
+ },
+ handler
+ )
+ latch.await()
+ }
+ }
+
+ return authToken
+ }
+}
diff --git a/app/k9mail/src/main/java/com/fsck/k9/backends/WebDavBackendFactory.kt b/app/k9mail/src/main/java/com/fsck/k9/backends/WebDavBackendFactory.kt
index 0098b96e468d38b506cb0ea82f36c08c77af6d4b..0e4e588e335a1fcda65b1ef11ceeb42b106aff68 100644
--- a/app/k9mail/src/main/java/com/fsck/k9/backends/WebDavBackendFactory.kt
+++ b/app/k9mail/src/main/java/com/fsck/k9/backends/WebDavBackendFactory.kt
@@ -6,6 +6,7 @@ import com.fsck.k9.backend.api.Backend
import com.fsck.k9.backend.webdav.WebDavBackend
import com.fsck.k9.mail.ssl.TrustManagerFactory
import com.fsck.k9.mail.store.webdav.DraftsFolderProvider
+import com.fsck.k9.mail.store.webdav.SniHostSetter
import com.fsck.k9.mail.store.webdav.WebDavStore
import com.fsck.k9.mail.transport.WebDavTransport
import com.fsck.k9.mailstore.FolderRepository
@@ -14,6 +15,7 @@ import com.fsck.k9.mailstore.K9BackendStorageFactory
class WebDavBackendFactory(
private val backendStorageFactory: K9BackendStorageFactory,
private val trustManagerFactory: TrustManagerFactory,
+ private val sniHostSetter: SniHostSetter,
private val folderRepository: FolderRepository
) : BackendFactory {
override fun createBackend(account: Account): Backend {
@@ -21,8 +23,8 @@ class WebDavBackendFactory(
val backendStorage = backendStorageFactory.createBackendStorage(account)
val serverSettings = account.incomingServerSettings
val draftsFolderProvider = createDraftsFolderProvider(account)
- val webDavStore = WebDavStore(trustManagerFactory, serverSettings, draftsFolderProvider)
- val webDavTransport = WebDavTransport(trustManagerFactory, serverSettings, draftsFolderProvider)
+ val webDavStore = WebDavStore(trustManagerFactory, sniHostSetter, serverSettings, draftsFolderProvider)
+ val webDavTransport = WebDavTransport(trustManagerFactory, sniHostSetter, serverSettings, draftsFolderProvider)
return WebDavBackend(accountName, backendStorage, webDavStore, webDavTransport)
}
diff --git a/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java b/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java
index 0a0c290b80445bc0cbc5445c40087d6ddccaf430..6ea08aad686927841d9d1ab5c26965924d52135e 100644
--- a/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java
+++ b/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java
@@ -21,7 +21,7 @@ import timber.log.Timber;
public class K9StoragePersister implements StoragePersister {
- private static final int DB_VERSION = 17;
+ private static final int DB_VERSION = 18;
private static final String DB_NAME = "preferences_storage";
private final Context context;
diff --git a/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrationTo18.kt b/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrationTo18.kt
new file mode 100644
index 0000000000000000000000000000000000000000..15a98f95217a4a2bb10c2bc885bfc646eafbacec
--- /dev/null
+++ b/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrationTo18.kt
@@ -0,0 +1,36 @@
+package com.fsck.k9.preferences.migrations
+
+import android.database.sqlite.SQLiteDatabase
+
+/**
+ * Rewrite the per-network type IMAP compression settings to a single setting.
+ */
+class StorageMigrationTo18(
+ private val db: SQLiteDatabase,
+ private val migrationsHelper: StorageMigrationsHelper
+) {
+ fun rewriteImapCompressionSettings() {
+ val accountUuidsListValue = migrationsHelper.readValue(db, "accountUuids")
+ if (accountUuidsListValue == null || accountUuidsListValue.isEmpty()) {
+ return
+ }
+
+ val accountUuids = accountUuidsListValue.split(",")
+ for (accountUuid in accountUuids) {
+ rewriteImapCompressionSetting(accountUuid)
+ }
+ }
+
+ private fun rewriteImapCompressionSetting(accountUuid: String) {
+ val useCompressionWifi = migrationsHelper.readValue(db, "$accountUuid.useCompression.WIFI").toBoolean()
+ val useCompressionMobile = migrationsHelper.readValue(db, "$accountUuid.useCompression.MOBILE").toBoolean()
+ val useCompressionOther = migrationsHelper.readValue(db, "$accountUuid.useCompression.OTHER").toBoolean()
+
+ val useCompression = useCompressionWifi && useCompressionMobile && useCompressionOther
+ migrationsHelper.writeValue(db, "$accountUuid.useCompression", useCompression.toString())
+
+ migrationsHelper.writeValue(db, "$accountUuid.useCompression.WIFI", null)
+ migrationsHelper.writeValue(db, "$accountUuid.useCompression.MOBILE", null)
+ migrationsHelper.writeValue(db, "$accountUuid.useCompression.OTHER", null)
+ }
+}
diff --git a/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrations.kt b/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrations.kt
index 8f435b07a64eca4944d9c0b8c4ae239c70820cb6..07a6a091b8d1340a21f4a377fc6c300c0bd2fd9d 100644
--- a/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrations.kt
+++ b/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrations.kt
@@ -23,5 +23,6 @@ internal object StorageMigrations {
if (oldVersion < 15) StorageMigrationTo15(db, migrationsHelper).rewriteIdleRefreshInterval()
if (oldVersion < 16) StorageMigrationTo16(db, migrationsHelper).changeDefaultRegisteredNameColor()
if (oldVersion < 17) StorageMigrationTo17(db, migrationsHelper).rewriteNotificationLightSettings()
+ if (oldVersion < 18) StorageMigrationTo18(db, migrationsHelper).rewriteImapCompressionSettings()
}
}
diff --git a/app/ui/legacy/build.gradle b/app/ui/legacy/build.gradle
index 7fb11f6c7cd2284021fb7234101672bdebf6e555..1403b14b51ae08b8e23a807b5751a1aa875261dc 100644
--- a/app/ui/legacy/build.gradle
+++ b/app/ui/legacy/build.gradle
@@ -40,6 +40,7 @@ dependencies {
implementation "com.mikepenz:fastadapter-extensions-drag:${versions.fastAdapter}"
implementation "com.mikepenz:fastadapter-extensions-utils:${versions.fastAdapter}"
implementation 'de.hdodenhof:circleimageview:3.1.0'
+ api 'net.openid:appauth:0.11.1'
implementation "commons-io:commons-io:${versions.commonsIo}"
implementation "androidx.core:core-ktx:${versions.androidxCore}"
@@ -79,6 +80,8 @@ android {
defaultConfig {
minSdkVersion buildConfig.minSdk
targetSdkVersion buildConfig.robolectricSdk
+
+ manifestPlaceholders = ['appAuthRedirectScheme': 'foundation.e.mail']
}
buildTypes {
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/KoinModule.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/KoinModule.kt
index 93734a006131fcdc92c73623d643ca2f2c590b02..f1f970380c8df68a3cf63d82a8f762eecad09742 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/KoinModule.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/KoinModule.kt
@@ -1,7 +1,10 @@
package com.fsck.k9.activity
+import com.fsck.k9.activity.setup.AuthViewModel
+import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val activityModule = module {
- single { MessageLoaderHelperFactory(get(), get()) }
+ single { MessageLoaderHelperFactory(messageViewInfoExtractorFactory = get(), htmlSettingsProvider = get()) }
+ viewModel { AuthViewModel(application = get(), accountManager = get(), oAuthConfigurationProvider = get()) }
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
index 3f668626e3f627f564bb680cef56f487e00e6a03..19c1aecf2e373e9df275a34ccca7aa6dc8defe55 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
@@ -1028,6 +1028,9 @@ open class MessageList :
} else if (id == R.id.move_to_drafts) {
messageViewFragment!!.onMoveToDrafts()
return true
+ } else if (id == R.id.unsubscribe) {
+ messageViewFragment!!.onUnsubscribe()
+ return true
} else if (id == R.id.show_headers) {
startActivity(MessageSourceActivity.createLaunchIntent(this, messageViewFragment!!.messageReference))
return true
@@ -1127,6 +1130,7 @@ open class MessageList :
menu.findItem(R.id.refile).isVisible = false
menu.findItem(R.id.toggle_unread).isVisible = false
menu.findItem(R.id.toggle_message_view_theme).isVisible = false
+ menu.findItem(R.id.unsubscribe).isVisible = false
menu.findItem(R.id.show_headers).isVisible = false
} else {
// hide prev/next buttons in split mode
@@ -1209,6 +1213,8 @@ open class MessageList :
if (messageViewFragment!!.isOutbox) {
menu.findItem(R.id.move_to_drafts).isVisible = true
}
+
+ menu.findItem(R.id.unsubscribe).isVisible = messageViewFragment!!.canMessageBeUnsubscribed()
}
// Set visibility of menu items related to the message list
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/loader/AttachmentInfoLoader.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/loader/AttachmentInfoLoader.java
index 16019086e1ffa4ab5e37614b8015113f346a9ee1..6750dd52b3ca111976b51273f4211d9e49da41e8 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/loader/AttachmentInfoLoader.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/loader/AttachmentInfoLoader.java
@@ -9,6 +9,7 @@ import android.net.Uri;
import android.provider.OpenableColumns;
import androidx.loader.content.AsyncTaskLoader;
+import com.fsck.k9.helper.MimeTypeUtil;
import com.fsck.k9.message.Attachment.LoadingState;
import timber.log.Timber;
@@ -81,11 +82,11 @@ public class AttachmentInfoLoader extends AsyncTaskLoader {
}
if (usableContentType == null) {
- usableContentType = MimeUtility.getMimeTypeByExtension(name);
+ usableContentType = MimeTypeUtil.getMimeTypeByExtension(name);
}
if (!sourceAttachment.allowMessageType && MimeUtility.isMessageType(usableContentType)) {
- usableContentType = MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE;
+ usableContentType = MimeTypeUtil.DEFAULT_ATTACHMENT_MIME_TYPE;
}
if (size <= 0) {
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/misc/Attachment.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/misc/Attachment.java
index e9fbce77735b60ed6af3806697b6346a4d149fe5..25a2b45d857fdf6322143b2c525fb6235bd0ebcd 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/misc/Attachment.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/misc/Attachment.java
@@ -4,7 +4,7 @@ import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
-import com.fsck.k9.mail.internet.MimeUtility;
+import com.fsck.k9.helper.MimeTypeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -108,9 +108,9 @@ public class Attachment implements Parcelable, com.fsck.k9.message.Attachment {
return false;
}
- return MimeUtility.isSupportedImageType(contentType) || (
- MimeUtility.isSameMimeType(MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE, contentType) &&
- MimeUtility.isSupportedImageExtension(filename));
+ return MimeTypeUtil.isSupportedImageType(contentType) || (
+ MimeTypeUtil.isSameMimeType(MimeTypeUtil.DEFAULT_ATTACHMENT_MIME_TYPE, contentType) &&
+ MimeTypeUtil.isSupportedImageExtension(filename));
}
private Attachment(Uri uri, LoadingState state, int loaderId, String contentType, boolean allowMessageType,
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.java
deleted file mode 100644
index b7c6dbf8fe64d20249755a643b1cea7b1a40df75..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.java
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * Copyright ECORP SAS 2022
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.fsck.k9.activity.setup;
-
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-
-import androidx.annotation.Nullable;
-import androidx.lifecycle.LifecycleOwner;
-import com.fsck.k9.Account;
-import com.fsck.k9.Core;
-import com.fsck.k9.DI;
-import com.fsck.k9.EmailAddressValidator;
-import com.fsck.k9.Preferences;
-import com.fsck.k9.account.AccountCreator;
-import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
-import com.fsck.k9.activity.setup.accountmanager.MailAutoConfigDiscoveryHelper;
-import com.fsck.k9.autodiscovery.api.DiscoveredServerSettings;
-import com.fsck.k9.autodiscovery.api.DiscoveryResults;
-import com.fsck.k9.autodiscovery.api.DiscoveryTarget;
-import com.fsck.k9.autodiscovery.providersxml.ProvidersXmlDiscovery;
-import com.fsck.k9.helper.Utility;
-import com.fsck.k9.mail.AuthType;
-import com.fsck.k9.mail.ServerSettings;
-import com.fsck.k9.mail.oauth.OAuth2Provider;
-import com.fsck.k9.mailstore.SpecialLocalFoldersCreator;
-import com.fsck.k9.ui.ConnectionSettings;
-import com.fsck.k9.ui.R;
-import com.fsck.k9.ui.base.K9Activity;
-import com.fsck.k9.ui.settings.ExtraAccountDiscovery;
-import com.fsck.k9.view.ClientCertificateSpinner;
-import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
-import com.google.android.material.textfield.TextInputEditText;
-import com.google.android.material.textfield.TextInputLayout;
-import com.lamonjush.fullscreenloadingdialog.FullScreenLoadingDialog;
-import timber.log.Timber;
-
-/**
- * Prompts the user for the email address and password.
- * Attempts to lookup default settings for the domain the user specified. If the
- * domain is known the settings are handed off to the AccountSetupCheckSettings
- * activity. If no settings are found the settings are handed off to the
- * AccountSetupAccountType activity.
- */
-public class AccountSetupBasics extends K9Activity
- implements OnClickListener, TextWatcher, OnCheckedChangeListener, OnClientCertificateChangedListener {
- private final static String EXTRA_ACCOUNT = "com.fsck.k9.AccountSetupBasics.account";
- private final static String STATE_KEY_CHECKED_INCOMING = "com.fsck.k9.AccountSetupBasics.checkedIncoming";
-
-
- private final ProvidersXmlDiscovery providersXmlDiscovery = DI.get(ProvidersXmlDiscovery.class);
- private final AccountCreator accountCreator = DI.get(AccountCreator.class);
- private final SpecialLocalFoldersCreator localFoldersCreator = DI.get(SpecialLocalFoldersCreator.class);
-
- private TextInputEditText mEmailView;
- private TextInputEditText mPasswordView;
- private TextInputLayout mPasswordLayoutView;
- private CheckBox mClientCertificateCheckBox;
- private ClientCertificateSpinner mClientCertificateSpinner;
- private Button mNextButton;
- private Button mManualSetupButton;
- private View mAdvandedOptionsFoldable;
- private Account mAccount;
- private ViewGroup mAllowClientCertificateView;
-
- private EmailAddressValidator mEmailValidator = new EmailAddressValidator();
- private boolean mCheckedIncoming = false;
- private boolean xoauth2;
-
- public static void actionNewAccount(Context context) {
- Intent i = new Intent(context, AccountSetupBasics.class);
- context.startActivity(i);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setLayout(R.layout.account_setup_basics);
- setTitle(R.string.account_setup_basics_title);
- mEmailView = findViewById(R.id.account_email);
- mPasswordView = findViewById(R.id.account_password);
- mPasswordLayoutView = findViewById(R.id.account_password_input_layout);
- mClientCertificateCheckBox = findViewById(R.id.account_client_certificate);
- mClientCertificateSpinner = findViewById(R.id.account_client_certificate_spinner);
- mAllowClientCertificateView = findViewById(R.id.account_allow_client_certificate);
-
- mNextButton = findViewById(R.id.next);
- mManualSetupButton = findViewById(R.id.manual_setup);
- mAdvandedOptionsFoldable = findViewById(R.id.foldable_advanced_options);
- mNextButton.setOnClickListener(this);
- mManualSetupButton.setOnClickListener(this);
- }
-
- private void initializeViewListeners() {
- mEmailView.addTextChangedListener(this);
- mPasswordView.addTextChangedListener(this);
- mClientCertificateCheckBox.setOnCheckedChangeListener(this);
- mClientCertificateSpinner.setOnClientCertificateChangedListener(this);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mAccount != null) {
- outState.putString(EXTRA_ACCOUNT, mAccount.getUuid());
- }
- outState.putBoolean(STATE_KEY_CHECKED_INCOMING, mCheckedIncoming);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
-
- if (savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
- String accountUuid = savedInstanceState.getString(EXTRA_ACCOUNT);
- mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
- }
-
- mCheckedIncoming = savedInstanceState.getBoolean(STATE_KEY_CHECKED_INCOMING);
-
- updateViewVisibility(mClientCertificateCheckBox.isChecked());
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
-
- /*
- * We wait until now to initialize the listeners because we didn't want
- * the OnCheckedChangeListener active while the
- * mClientCertificateCheckBox state was being restored because it could
- * trigger the pop-up of a ClientCertificateSpinner.chooseCertificate()
- * dialog.
- */
- initializeViewListeners();
- validateFields();
- }
-
- public void afterTextChanged(Editable s) {
- validateFields();
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void onClientCertificateChanged(String alias) {
- validateFields();
- }
-
- /**
- * Called when checking the client certificate CheckBox
- */
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- updateViewVisibility(isChecked);
- validateFields();
-
- // Have the user select the client certificate if not already selected
- if ((isChecked) && (mClientCertificateSpinner.getAlias() == null)) {
- mClientCertificateSpinner.chooseCertificate();
- }
- }
-
- private void updateViewVisibility(boolean usingCertificates) {
- if (usingCertificates) {
- // show client certificate spinner
- mAllowClientCertificateView.setVisibility(View.VISIBLE);
- } else {
- // hide client certificate spinner
- mAllowClientCertificateView.setVisibility(View.GONE);
- }
- }
-
- private void validateFields() {
- boolean clientCertificateChecked = mClientCertificateCheckBox.isChecked();
- String clientCertificateAlias = mClientCertificateSpinner.getAlias();
- String email = mEmailView.getText().toString();
-
- xoauth2 = false;
-
- if (email.contains("@")) {
- String[] split = email.split("@");
- if (split.length == 2) {
- String domain = split[1];
- xoauth2 = OAuth2Provider.Companion.isXOAuth2(domain);
-
- }
- }
-
- boolean valid = Utility.requiredFieldValid(mEmailView)
- && ((!clientCertificateChecked && (Utility.requiredFieldValid(mPasswordView) || xoauth2))
- || (clientCertificateChecked && clientCertificateAlias != null))
- && mEmailValidator.isValidAddressOnly(email);
-
- mNextButton.setEnabled(valid);
- mNextButton.setFocusable(valid);
- mManualSetupButton.setEnabled(valid);
-
- mPasswordLayoutView.setVisibility(xoauth2 ? View.GONE : View.VISIBLE);
- mAdvandedOptionsFoldable.setVisibility(xoauth2 ? View.GONE : View.VISIBLE);
- mManualSetupButton.setVisibility(xoauth2 ? View.GONE : View.VISIBLE);
-
- /*
- * Dim the next button's icon to 50% if the button is disabled.
- * TODO this can probably be done with a stateful drawable. Check into it.
- * android:state_enabled
- */
- Utility.setCompoundDrawablesAlpha(mNextButton, mNextButton.isEnabled() ? 255 : 128);
- }
-
- private String getOwnerName() {
- String name = null;
- try {
- name = getDefaultSenderName();
- } catch (Exception e) {
- Timber.e(e, "Could not get default account name");
- }
-
- if (name == null) {
- name = "";
- }
- return name;
- }
-
- private String getDefaultSenderName() {
- String name = null;
- Account account = Preferences.getPreferences(this).getDefaultAccount();
- if (account != null) {
- name = account.getSenderName();
- }
- return name;
- }
-
- private void finishAutoSetup(ConnectionSettings connectionSettings) {
- String email = mEmailView.getText().toString();
- String password = mPasswordView.getText().toString();
-
- if (mAccount == null) {
- mAccount = Preferences.getPreferences(this).newAccount();
- mAccount.setChipColor(accountCreator.pickColor());
- }
-
- mAccount.setSenderName(getOwnerName());
- mAccount.setEmail(email);
-
- ServerSettings incomingServerSettings = connectionSettings.getIncoming().newPassword(password);
- mAccount.setIncomingServerSettings(incomingServerSettings);
-
- ServerSettings outgoingServerSettings = connectionSettings.getOutgoing().newPassword(password);
- mAccount.setOutgoingServerSettings(outgoingServerSettings);
-
- mAccount.setDeletePolicy(accountCreator.getDefaultDeletePolicy(incomingServerSettings.type));
-
- localFoldersCreator.createSpecialLocalFolders(mAccount);
-
- // Check incoming here. Then check outgoing in onActivityResult()
- AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.INCOMING);
- }
-
- private ConnectionSettings providersXmlDiscoveryDiscover(String email, DiscoveryTarget discoveryTarget) {
- DiscoveryResults discoveryResults = providersXmlDiscovery.discover(email, DiscoveryTarget.INCOMING_AND_OUTGOING);
- if (discoveryResults == null || (discoveryResults.getIncoming().size() < 1 || discoveryResults.getOutgoing().size() < 1)) {
- return null;
- }
- DiscoveredServerSettings incoming = discoveryResults.getIncoming().get(0);
- DiscoveredServerSettings outgoing = discoveryResults.getOutgoing().get(0);
- return new ConnectionSettings(new ServerSettings(
- incoming.getProtocol(),
- incoming.getHost(),
- incoming.getPort(),
- incoming.getSecurity(),
- incoming.getAuthType(),
- incoming.getUsername(),
- null,
- null
- ), new ServerSettings(
- outgoing.getProtocol(),
- outgoing.getHost(),
- outgoing.getPort(),
- outgoing.getSecurity(),
- outgoing.getAuthType(),
- outgoing.getUsername(),
- null,
- null
- ));
- }
-
- private void onNext() {
- if (mClientCertificateCheckBox.isChecked()) {
- // Auto-setup doesn't support client certificates.
- onManualSetup();
- return;
- }
- Editable emailEditable = mEmailView.getText();
-
- if (emailEditable == null) {
- return;
- }
-
- String email = emailEditable.toString();
-
- OAuth2Provider provider = OAuth2Provider.Companion.getProvider(email);
-
- if(provider != null && provider.toString().trim().equals("GMAIL")){
- Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
- startActivity(intent);
- return;
- }
-
- ConnectionSettings extraConnectionSettings = ExtraAccountDiscovery.discover(email);
- if (extraConnectionSettings != null) {
- finishAutoSetup(extraConnectionSettings);
- return;
- }
-
- ConnectionSettings connectionSettings =
- providersXmlDiscoveryDiscover(email, DiscoveryTarget.INCOMING_AND_OUTGOING);
- if (connectionSettings != null) {
- finishAutoSetup(connectionSettings);
- return;
- }
- // We don't have predefine configuration, try to retrieve it by api call
- FullScreenLoadingDialog.getInstance()
- .setSpinKitColor(R.color.color_default_accent)
- .show(this);
- MailAutoConfigDiscoveryHelper.INSTANCE.retrieveConfiguration(email, this, config -> {
- FullScreenLoadingDialog.getInstance().dismiss();
- if (config != null) {
- finishAutoSetup(config);
- return;
- }
- // We can't find default settings for this account, start the manual setup process.
- onManualSetup();
- });
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode != AccountSetupCheckSettings.ACTIVITY_REQUEST_CODE) {
- super.onActivityResult(requestCode, resultCode, data);
- return;
- }
-
- if (resultCode == RESULT_OK) {
- if (!mCheckedIncoming) {
- //We've successfully checked incoming. Now check outgoing.
- mCheckedIncoming = true;
- AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
- } else {
- //We've successfully checked outgoing as well.
- mAccount.setName(mAccount.getEmail());
- Preferences.getPreferences(this).saveAccount(mAccount);
- Core.setServicesEnabled(this);
- AccountSetupNames.actionSetNames(this, mAccount);
- }
- }
- }
-
- private void onManualSetup() {
- String email = mEmailView.getText().toString();
-
- String password = null;
- String clientCertificateAlias = null;
- AuthType authenticationType;
-
- authenticationType = AuthType.PLAIN;
- password = mPasswordView.getText().toString();
- if (mClientCertificateCheckBox.isChecked()) {
- clientCertificateAlias = mClientCertificateSpinner.getAlias();
- if (mPasswordView.getText().toString().equals("")) {
- authenticationType = AuthType.EXTERNAL;
- password = null;
- }
- }
-
- if (mAccount == null) {
- mAccount = Preferences.getPreferences(this).newAccount();
- mAccount.setChipColor(accountCreator.pickColor());
- }
- mAccount.setSenderName(getOwnerName());
- mAccount.setEmail(email);
-
- InitialAccountSettings initialAccountSettings = new InitialAccountSettings(authenticationType, email, password,
- clientCertificateAlias);
-
- AccountSetupAccountType.actionSelectAccountType(this, mAccount, false, initialAccountSettings);
- }
-
- public void onClick(View v) {
- int id = v.getId();
- if (id == R.id.next) {
- onNext();
- } else if (id == R.id.manual_setup) {
- onManualSetup();
- }
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d1d3946c0f4d9c7ed0e6e02a84220fa607d6f0da
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt
@@ -0,0 +1,428 @@
+package com.fsck.k9.activity.setup
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.provider.Settings
+import android.text.Editable
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.CheckBox
+import androidx.core.view.isVisible
+import androidx.lifecycle.lifecycleScope
+import com.fsck.k9.Account
+import com.fsck.k9.Core
+import com.fsck.k9.EmailAddressValidator
+import com.fsck.k9.Preferences
+import com.fsck.k9.account.AccountCreator
+import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection
+import com.fsck.k9.activity.setup.accountmanager.MailAutoConfigDiscovery
+import com.fsck.k9.autodiscovery.api.DiscoveredServerSettings
+import com.fsck.k9.autodiscovery.api.DiscoveryTarget
+import com.fsck.k9.autodiscovery.providersxml.ProvidersXmlDiscovery
+import com.fsck.k9.helper.SimpleTextWatcher
+import com.fsck.k9.helper.Utility.requiredFieldValid
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.ServerSettings
+import com.fsck.k9.mailstore.SpecialLocalFoldersCreator
+import com.fsck.k9.oauth.OAuthConfigurationProvider
+import com.fsck.k9.ui.ConnectionSettings
+import com.fsck.k9.ui.R
+import com.fsck.k9.ui.base.K9Activity
+import com.fsck.k9.ui.getEnum
+import com.fsck.k9.ui.putEnum
+import com.fsck.k9.ui.settings.ExtraAccountDiscovery
+import com.fsck.k9.view.ClientCertificateSpinner
+import com.google.android.material.textfield.TextInputEditText
+import com.lamonjush.fullscreenloadingdialog.FullScreenLoadingDialog
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.koin.android.ext.android.inject
+
+/**
+ * Prompts the user for the email address and password.
+ *
+ * Attempts to lookup default settings for the domain the user specified. If the domain is known, the settings are
+ * handed off to the [AccountSetupCheckSettings] activity. If no settings are found, the settings are handed off to the
+ * [AccountSetupAccountType] activity.
+ */
+class AccountSetupBasics : K9Activity() {
+ private val providersXmlDiscovery: ProvidersXmlDiscovery by inject()
+ private val accountCreator: AccountCreator by inject()
+ private val localFoldersCreator: SpecialLocalFoldersCreator by inject()
+ private val preferences: Preferences by inject()
+ private val emailValidator: EmailAddressValidator by inject()
+
+ private val oAuthConfigurationProvider: OAuthConfigurationProvider by inject()
+
+ private lateinit var emailView: TextInputEditText
+ private lateinit var passwordView: TextInputEditText
+ private lateinit var passwordLayout: View
+ private lateinit var clientCertificateCheckBox: CheckBox
+ private lateinit var clientCertificateSpinner: ClientCertificateSpinner
+ private lateinit var advancedOptionsContainer: View
+ private lateinit var nextButton: Button
+ private lateinit var manualSetupButton: Button
+ private lateinit var allowClientCertificateView: ViewGroup
+
+ private var uiState = UiState.EMAIL_ADDRESS_ONLY
+ private var account: Account? = null
+ private var checkedIncoming = false
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setLayout(R.layout.account_setup_basics)
+ setTitle(R.string.account_setup_basics_title)
+
+ emailView = findViewById(R.id.account_email)
+ passwordView = findViewById(R.id.account_password)
+ passwordLayout = findViewById(R.id.account_password_layout)
+ clientCertificateCheckBox = findViewById(R.id.account_client_certificate)
+ clientCertificateSpinner = findViewById(R.id.account_client_certificate_spinner)
+ allowClientCertificateView = findViewById(R.id.account_allow_client_certificate)
+ advancedOptionsContainer = findViewById(R.id.foldable_advanced_options)
+ nextButton = findViewById(R.id.next)
+ manualSetupButton = findViewById(R.id.manual_setup)
+
+ manualSetupButton.setOnClickListener { onManualSetup() }
+ }
+
+ override fun onPostCreate(savedInstanceState: Bundle?) {
+ super.onPostCreate(savedInstanceState)
+
+ /*
+ * We wait until now to initialize the listeners because we didn't want the OnCheckedChangeListener active
+ * while the clientCertificateCheckBox state was being restored because it could trigger the pop-up of a
+ * ClientCertificateSpinner.chooseCertificate() dialog.
+ */
+ initializeViewListeners()
+ validateFields()
+
+ updateUi()
+ }
+
+ private fun initializeViewListeners() {
+ val textWatcher = object : SimpleTextWatcher() {
+ override fun afterTextChanged(s: Editable) {
+ val checkPassword = uiState == UiState.PASSWORD_FLOW
+ validateFields(checkPassword)
+ }
+ }
+
+ emailView.addTextChangedListener(textWatcher)
+ passwordView.addTextChangedListener(textWatcher)
+
+ clientCertificateCheckBox.setOnCheckedChangeListener { _, isChecked ->
+ updateViewVisibility(isChecked)
+ validateFields()
+
+ // Have the user select the client certificate if not already selected
+ if (isChecked && clientCertificateSpinner.alias == null) {
+ clientCertificateSpinner.chooseCertificate()
+ }
+ }
+
+ clientCertificateSpinner.setOnClientCertificateChangedListener {
+ validateFields()
+ }
+ }
+
+ private fun updateUi() {
+ when (uiState) {
+ UiState.EMAIL_ADDRESS_ONLY -> {
+ passwordLayout.isVisible = false
+ advancedOptionsContainer.isVisible = false
+ nextButton.setOnClickListener { attemptAutoSetupUsingOnlyEmailAddress() }
+ }
+ UiState.PASSWORD_FLOW -> {
+ passwordLayout.isVisible = true
+ advancedOptionsContainer.isVisible = true
+ nextButton.setOnClickListener { attemptAutoSetup() }
+ }
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+
+ outState.putEnum(STATE_KEY_UI_STATE, uiState)
+ outState.putString(EXTRA_ACCOUNT, account?.uuid)
+ outState.putBoolean(STATE_KEY_CHECKED_INCOMING, checkedIncoming)
+ }
+
+ override fun onRestoreInstanceState(savedInstanceState: Bundle) {
+ super.onRestoreInstanceState(savedInstanceState)
+
+ uiState = savedInstanceState.getEnum(STATE_KEY_UI_STATE, UiState.EMAIL_ADDRESS_ONLY)
+
+ val accountUuid = savedInstanceState.getString(EXTRA_ACCOUNT)
+ if (accountUuid != null) {
+ account = preferences.getAccount(accountUuid)
+ }
+
+ checkedIncoming = savedInstanceState.getBoolean(STATE_KEY_CHECKED_INCOMING)
+ updateViewVisibility(clientCertificateCheckBox.isChecked)
+ }
+
+ private fun updateViewVisibility(usingCertificates: Boolean) {
+ allowClientCertificateView.isVisible = usingCertificates
+ }
+
+ private fun validateFields(checkPassword: Boolean = true) {
+ val email = emailView.text?.toString().orEmpty()
+ val valid = requiredFieldValid(emailView) && emailValidator.isValidAddressOnly(email) &&
+ (!checkPassword || isPasswordFieldValid())
+
+ nextButton.isEnabled = valid
+ nextButton.isFocusable = valid
+ manualSetupButton.isEnabled = valid
+ }
+
+ private fun isPasswordFieldValid(): Boolean {
+ val clientCertificateChecked = clientCertificateCheckBox.isChecked
+ val clientCertificateAlias = clientCertificateSpinner.alias
+
+ return !clientCertificateChecked && requiredFieldValid(passwordView) ||
+ clientCertificateChecked && clientCertificateAlias != null
+ }
+
+ private fun attemptAutoSetupUsingOnlyEmailAddress() {
+ val email = emailView.text?.toString() ?: error("Email missing")
+
+ val extraConnectionSettings = ExtraAccountDiscovery.discover(email)
+ if (extraConnectionSettings != null) {
+ finishAutoSetup(extraConnectionSettings)
+ return
+ }
+
+ val connectionSettings = providersXmlDiscoveryDiscover(email)
+
+ if (connectionSettings != null &&
+ connectionSettings.incoming.authenticationType == AuthType.XOAUTH2 &&
+ connectionSettings.outgoing.authenticationType == AuthType.XOAUTH2
+ ) {
+ startOAuthFlow(connectionSettings)
+ } else {
+ startPasswordFlow()
+ }
+ }
+
+ private fun startOAuthFlow(connectionSettings: ConnectionSettings) {
+ val account = createAccount(connectionSettings)
+
+ if (oAuthConfigurationProvider.isGoogle(account.incomingServerSettings.host!!)) {
+ val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
+ startActivity(intent)
+ return
+ }
+
+ val intent = OAuthFlowActivity.buildLaunchIntent(this, account.uuid)
+ startActivityForResult(intent, REQUEST_CODE_OAUTH)
+ }
+
+ private fun startPasswordFlow() {
+ uiState = UiState.PASSWORD_FLOW
+
+ updateUi()
+ validateFields()
+
+ passwordView.requestFocus()
+ }
+
+ private fun attemptAutoSetup() {
+ if (clientCertificateCheckBox.isChecked) {
+ // Auto-setup doesn't support client certificates.
+ onManualSetup()
+ return
+ }
+
+ val email = emailView.text?.toString() ?: error("Email missing")
+
+ val extraConnectionSettings = ExtraAccountDiscovery.discover(email)
+ if (extraConnectionSettings != null) {
+ finishAutoSetup(extraConnectionSettings)
+ return
+ }
+
+ val connectionSettings = providersXmlDiscoveryDiscover(email)
+ if (connectionSettings != null) {
+ finishAutoSetup(connectionSettings)
+ return
+ }
+ providersApiDiscoveryDiscover(email)
+ }
+
+ private fun providersApiDiscoveryDiscover(email: String) {
+ // We don't have predefine configuration, try to retrieve it by api call
+ FullScreenLoadingDialog.getInstance()
+ .setSpinKitColor(R.color.color_default_accent)
+ .show(this)
+
+ lifecycleScope.launch(Dispatchers.IO) {
+ val config = MailAutoConfigDiscovery.retrieveConfigFromApi(email)
+ lifecycleScope.launch(Dispatchers.Main) {
+ FullScreenLoadingDialog.getInstance().dismiss()
+ if (config != null) {
+ finishAutoSetup(config)
+ } else {
+ // We don't have default settings for this account, start the manual setup process.
+ onManualSetup()
+ }
+ }
+ }
+ }
+
+ private fun finishAutoSetup(connectionSettings: ConnectionSettings) {
+ val account = createAccount(connectionSettings)
+
+ // Check incoming here. Then check outgoing in onActivityResult()
+ AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.INCOMING)
+ }
+
+ private fun createAccount(connectionSettings: ConnectionSettings): Account {
+ val email = emailView.text?.toString() ?: error("Email missing")
+ val password = passwordView.text?.toString()
+
+ val account = initAccount(email)
+
+ val incomingServerSettings = connectionSettings.incoming.newPassword(password)
+ account.incomingServerSettings = incomingServerSettings
+
+ val outgoingServerSettings = connectionSettings.outgoing.newPassword(password)
+ account.outgoingServerSettings = outgoingServerSettings
+
+ account.deletePolicy = accountCreator.getDefaultDeletePolicy(incomingServerSettings.type)
+
+ localFoldersCreator.createSpecialLocalFolders(account)
+
+ return account
+ }
+
+ private fun onManualSetup() {
+ val email = emailView.text?.toString() ?: error("Email missing")
+ var password: String? = passwordView.text?.toString()
+ var clientCertificateAlias: String? = null
+ var authenticationType: AuthType = AuthType.PLAIN
+
+ if (clientCertificateCheckBox.isChecked) {
+ clientCertificateAlias = clientCertificateSpinner.alias
+ if (password.isNullOrEmpty()) {
+ authenticationType = AuthType.EXTERNAL
+ password = null
+ }
+ }
+
+ val account = initAccount(email)
+
+ val initialAccountSettings = InitialAccountSettings(
+ authenticationType = authenticationType,
+ email = email,
+ password = password,
+ clientCertificateAlias = clientCertificateAlias
+ )
+
+ AccountSetupAccountType.actionSelectAccountType(this, account, makeDefault = false, initialAccountSettings)
+ }
+
+ private fun initAccount(email: String): Account {
+ val account = this.account ?: createAccount().also { this.account = it }
+
+ account.senderName = getOwnerName()
+ account.email = email
+ account.name = email
+ return account
+ }
+
+ private fun createAccount(): Account {
+ return preferences.newAccount().apply {
+ chipColor = accountCreator.pickColor()
+ }
+ }
+
+ private fun getOwnerName(): String {
+ return preferences.defaultAccount?.senderName ?: ""
+ }
+
+ private fun providersXmlDiscoveryDiscover(email: String): ConnectionSettings? {
+ val discoveryResults = providersXmlDiscovery.discover(email, DiscoveryTarget.INCOMING_AND_OUTGOING)
+ if (discoveryResults == null || discoveryResults.incoming.isEmpty() || discoveryResults.outgoing.isEmpty()) {
+ return null
+ }
+
+ val incomingServerSettings = discoveryResults.incoming.first().toServerSettings() ?: return null
+ val outgoingServerSettings = discoveryResults.outgoing.first().toServerSettings() ?: return null
+
+ return ConnectionSettings(incomingServerSettings, outgoingServerSettings)
+ }
+
+ public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ when (requestCode) {
+ REQUEST_CODE_CHECK_SETTINGS -> handleCheckSettingsResult(resultCode)
+ REQUEST_CODE_OAUTH -> handleSignInResult(resultCode)
+ else -> super.onActivityResult(requestCode, resultCode, data)
+ }
+ }
+
+ private fun handleCheckSettingsResult(resultCode: Int) {
+ if (resultCode != RESULT_OK) return
+
+ val account = this.account ?: error("Account instance missing")
+
+ if (!checkedIncoming) {
+ // We've successfully checked incoming. Now check outgoing.
+ checkedIncoming = true
+ AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.OUTGOING)
+ } else {
+ // We've successfully checked outgoing as well.
+ preferences.saveAccount(account)
+ Core.setServicesEnabled(applicationContext)
+
+ AccountSetupNames.actionSetNames(this, account)
+ }
+ }
+
+ private fun handleSignInResult(resultCode: Int) {
+ if (resultCode != RESULT_OK) return
+
+ val account = this.account ?: error("Account instance missing")
+
+ AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.INCOMING)
+ }
+
+ private enum class UiState {
+ EMAIL_ADDRESS_ONLY,
+ PASSWORD_FLOW
+ }
+
+ companion object {
+ private const val EXTRA_ACCOUNT = "com.fsck.k9.AccountSetupBasics.account"
+ private const val STATE_KEY_UI_STATE = "com.fsck.k9.AccountSetupBasics.uiState"
+ private const val STATE_KEY_CHECKED_INCOMING = "com.fsck.k9.AccountSetupBasics.checkedIncoming"
+ private const val REQUEST_CODE_CHECK_SETTINGS = AccountSetupCheckSettings.ACTIVITY_REQUEST_CODE
+ private const val REQUEST_CODE_OAUTH = Activity.RESULT_FIRST_USER + 1
+
+ @JvmStatic
+ fun actionNewAccount(context: Context) {
+ val intent = Intent(context, AccountSetupBasics::class.java)
+ context.startActivity(intent)
+ }
+ }
+}
+
+private fun DiscoveredServerSettings.toServerSettings(): ServerSettings? {
+ val authType = this.authType ?: return null
+ val username = this.username ?: return null
+
+ return ServerSettings(
+ type = protocol,
+ host = host,
+ port = port,
+ connectionSecurity = security,
+ authenticationType = authType,
+ username = username,
+ password = null,
+ clientCertificateAlias = null
+ )
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupCheckSettings.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupCheckSettings.java
deleted file mode 100644
index b8e2b1c173c9856b38fd988c26703392afb5b4b9..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupCheckSettings.java
+++ /dev/null
@@ -1,621 +0,0 @@
-
-package com.fsck.k9.activity.setup;
-
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.webkit.CookieManager;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentTransaction;
-import com.fsck.k9.Account;
-import com.fsck.k9.DI;
-import com.fsck.k9.LocalKeyStoreManager;
-import com.fsck.k9.Preferences;
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2NeedUserPromptException;
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2PromptRequestHandler;
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2CodeGrantFlowManager;
-import com.fsck.k9.controller.MessagingController;
-import com.fsck.k9.fragment.ConfirmationDialogFragment;
-import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
-import com.fsck.k9.mail.AuthenticationFailedException;
-import com.fsck.k9.mail.CertificateValidationException;
-import com.fsck.k9.mail.MailServerDirection;
-import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.filter.Hex;
-import com.fsck.k9.preferences.Protocols;
-import com.fsck.k9.ui.R;
-import com.fsck.k9.ui.base.K9Activity;
-import timber.log.Timber;
-
-
-/**
- * Checks the given settings to make sure that they can be used to send and
- * receive mail.
- *
- * XXX NOTE: The manifest for this app has it ignore config changes, because
- * it doesn't correctly deal with restarting while its thread is running.
- */
-public class AccountSetupCheckSettings extends K9Activity implements OnClickListener,
- ConfirmationDialogFragmentListener{
-
- public static final int ACTIVITY_REQUEST_CODE = 1;
-
- private static final String EXTRA_ACCOUNT = "account";
-
- private static final String EXTRA_CHECK_DIRECTION ="checkDirection";
-
- private String className=this.getClass().getName();
-
- public enum CheckDirection {
- INCOMING,
- OUTGOING;
-
- public MailServerDirection toMailServerDirection() {
- switch (this) {
- case INCOMING: return MailServerDirection.INCOMING;
- case OUTGOING: return MailServerDirection.OUTGOING;
- }
-
- throw new AssertionError("Unknown value: " + this);
- }
- }
-
- private final MessagingController messagingController = DI.get(MessagingController.class);
-
- private Handler mHandler = new Handler();
-
- private ProgressBar mProgressBar;
-
- private TextView mMessageView;
-
- private Account mAccount;
-
- private CheckDirection mDirection;
-
- private boolean mCanceled;
-
- private boolean mDestroyed;
-
- private Dialog authDialog;
-
- private OAuth2CodeGrantFlowManager oAuth2CodeGrantFlowManager;
-
- public static void actionCheckSettings(Activity context, Account account,
- CheckDirection direction) {
- Intent i = new Intent(context, AccountSetupCheckSettings.class);
- i.putExtra(EXTRA_ACCOUNT, account.getUuid());
- i.putExtra(EXTRA_CHECK_DIRECTION, direction);
- context.startActivityForResult(i, ACTIVITY_REQUEST_CODE);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setLayout(R.layout.account_setup_check_settings);
- mMessageView = findViewById(R.id.message);
- mProgressBar = findViewById(R.id.progress);
- findViewById(R.id.cancel).setOnClickListener(this);
-
- setMessage(R.string.account_setup_check_settings_retr_info_msg);
- mProgressBar.setIndeterminate(true);
-
- String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
- mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
- mDirection = (CheckDirection) getIntent().getSerializableExtra(EXTRA_CHECK_DIRECTION);
-
- oAuth2CodeGrantFlowManager = DI.get(OAuth2CodeGrantFlowManager.class);
- oAuth2CodeGrantFlowManager.setPromptRequestHandler(promptRequestHandler);
-
- new CheckAccountTask(mAccount).execute(mDirection);
- }
-
- private void handleCertificateValidationException(CertificateValidationException cve) {
- Timber.e(cve, "Error while testing settings");
-
- X509Certificate[] chain = cve.getCertChain();
- // Avoid NullPointerException in acceptKeyDialog()
- if (chain != null) {
- acceptKeyDialog(
- R.string.account_setup_failed_dlg_certificate_message_fmt,
- cve);
- } else {
- showErrorDialog(
- R.string.account_setup_failed_dlg_server_message_fmt,
- errorMessageForCertificateException(cve));
- }
- }
-
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- oAuth2CodeGrantFlowManager.setPromptRequestHandler(null);
- mDestroyed = true;
- mCanceled = true;
- }
-
- private void setMessage(final int resId) {
- mMessageView.setText(getString(resId));
- }
-
- private void acceptKeyDialog(final int msgResId, final CertificateValidationException ex) {
- mHandler.post(new Runnable() {
- public void run() {
- if (mDestroyed) {
- return;
- }
- String exMessage = "Unknown Error";
-
- if (ex != null) {
- if (ex.getCause() != null) {
- if (ex.getCause().getCause() != null) {
- exMessage = ex.getCause().getCause().getMessage();
-
- } else {
- exMessage = ex.getCause().getMessage();
- }
- } else {
- exMessage = ex.getMessage();
- }
- }
-
- mProgressBar.setIndeterminate(false);
- StringBuilder chainInfo = new StringBuilder(200);
- final X509Certificate[] chain = ex.getCertChain();
- // We already know chain != null (tested before calling this method)
- for (int i = 0; i < chain.length; i++) {
- // display certificate chain information
- //TODO: localize this strings
- chainInfo.append("Certificate chain[").append(i).append("]:\n");
- chainInfo.append("Subject: ").append(chain[i].getSubjectDN().toString()).append("\n");
-
- // display SubjectAltNames too
- // (the user may be mislead into mistrusting a certificate
- // by a subjectDN not matching the server even though a
- // SubjectAltName matches)
- try {
- final Collection < List >> subjectAlternativeNames = chain[i].getSubjectAlternativeNames();
- if (subjectAlternativeNames != null) {
- // The list of SubjectAltNames may be very long
- //TODO: localize this string
- StringBuilder altNamesText = new StringBuilder();
- altNamesText.append("Subject has ").append(subjectAlternativeNames.size()).append(" alternative names\n");
-
- // we need these for matching
- String incomingServerHost = mAccount.getIncomingServerSettings().host;
- String outgoingServerHost = mAccount.getOutgoingServerSettings().host;
-
- for (List> subjectAlternativeName : subjectAlternativeNames) {
- Integer type = (Integer)subjectAlternativeName.get(0);
- Object value = subjectAlternativeName.get(1);
- String name;
- switch (type) {
- case 0:
- Timber.w("SubjectAltName of type OtherName not supported.");
- continue;
- case 1: // RFC822Name
- name = (String)value;
- break;
- case 2: // DNSName
- name = (String)value;
- break;
- case 3:
- Timber.w("unsupported SubjectAltName of type x400Address");
- continue;
- case 4:
- Timber.w("unsupported SubjectAltName of type directoryName");
- continue;
- case 5:
- Timber.w("unsupported SubjectAltName of type ediPartyName");
- continue;
- case 6: // Uri
- name = (String)value;
- break;
- case 7: // ip-address
- name = (String)value;
- break;
- default:
- Timber.w("unsupported SubjectAltName of unknown type");
- continue;
- }
-
- // if some of the SubjectAltNames match the store or transport -host,
- // display them
- if (name.equalsIgnoreCase(incomingServerHost) || name.equalsIgnoreCase(outgoingServerHost)) {
- //TODO: localize this string
- altNamesText.append("Subject(alt): ").append(name).append(",...\n");
- } else if (name.startsWith("*.") && (
- incomingServerHost.endsWith(name.substring(2)) ||
- outgoingServerHost.endsWith(name.substring(2)))) {
- //TODO: localize this string
- altNamesText.append("Subject(alt): ").append(name).append(",...\n");
- }
- }
- chainInfo.append(altNamesText);
- }
- } catch (Exception e1) {
- // don't fail just because of subjectAltNames
- Timber.w(e1, "cannot display SubjectAltNames in dialog");
- }
-
- chainInfo.append("Issuer: ").append(chain[i].getIssuerDN().toString()).append("\n");
- String[] digestAlgorithms = new String[] {"SHA-1", "SHA-256", "SHA-512"};
-
- for (String algorithm : digestAlgorithms) {
- MessageDigest digest = null;
- try {
- digest = MessageDigest.getInstance(algorithm);
- } catch (NoSuchAlgorithmException e) {
- Timber.e(e, "Error while initializing MessageDigest (" + algorithm + ")");
- }
-
- if (digest != null) {
- digest.reset();
- try {
- String hash = Hex.encodeHex(digest.digest(chain[i].getEncoded()));
- chainInfo.append("Fingerprint ("+ algorithm +"): ").append("\n").append(hash).append("\n");
- } catch (CertificateEncodingException e) {
- Timber.e(e, "Error while encoding certificate");
- }
- }
- }
- }
-
- // TODO: refactor with DialogFragment.
- // This is difficult because we need to pass through chain[0] for onClick()
- new AlertDialog.Builder(AccountSetupCheckSettings.this)
- .setTitle(getString(R.string.account_setup_failed_dlg_invalid_certificate_title))
- //.setMessage(getString(R.string.account_setup_failed_dlg_invalid_certificate)
- .setMessage(getString(msgResId, exMessage)
- + " " + chainInfo.toString()
- )
- .setCancelable(true)
- .setPositiveButton(
- getString(R.string.account_setup_failed_dlg_invalid_certificate_accept),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- acceptCertificate(chain[0]);
- }
- })
- .setNegativeButton(
- getString(R.string.account_setup_failed_dlg_invalid_certificate_reject),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- })
- .show();
- }
- });
- }
-
- /**
- * Permanently accepts a certificate for the INCOMING or OUTGOING direction
- * by adding it to the local key store.
- *
- * @param certificate
- */
- private void acceptCertificate(X509Certificate certificate) {
- try {
- DI.get(LocalKeyStoreManager.class).addCertificate(mAccount, mDirection.toMailServerDirection(), certificate);
- } catch (CertificateException e) {
- showErrorDialog(
- R.string.account_setup_failed_dlg_certificate_message_fmt,
- e.getMessage() == null ? "" : e.getMessage());
- }
- AccountSetupCheckSettings.actionCheckSettings(AccountSetupCheckSettings.this, mAccount,
- mDirection);
- }
-
- @Override
- public void onActivityResult(int reqCode, int resCode, Intent data) {
- if (reqCode == ACTIVITY_REQUEST_CODE) {
- setResult(resCode);
- finish();
- } else {
- super.onActivityResult(reqCode, resCode, data);
- }
- }
-
- private void onCancel() {
- mCanceled = true;
- setMessage(R.string.account_setup_check_settings_canceling_msg);
- setResult(RESULT_CANCELED);
- finish();
- }
-
- public void onClick(View v) {
- if (v.getId() == R.id.cancel) {
- onCancel();
- }
- }
-
- private void showErrorDialog(final int msgResId, final Object... args) {
- mHandler.post(new Runnable() {
- public void run() {
- showDialogFragment(R.id.dialog_account_setup_error, getString(msgResId, args));
- }
- });
- }
-
- private void showDialogFragment(int dialogId, String customMessage) {
- if (mDestroyed) {
- return;
- }
- mProgressBar.setIndeterminate(false);
-
- DialogFragment fragment;
- if (dialogId == R.id.dialog_account_setup_error) {
- fragment = ConfirmationDialogFragment.newInstance(dialogId,
- getString(R.string.account_setup_failed_dlg_title),
- customMessage,
- getString(R.string.account_setup_failed_dlg_edit_details_action),
- getString(R.string.account_setup_failed_dlg_continue_action)
- );
- } else {
- throw new RuntimeException("Called showDialog(int) with unknown dialog id.");
- }
-
- FragmentTransaction ta = getSupportFragmentManager().beginTransaction();
- ta.add(fragment, getDialogTag(dialogId));
- ta.commitAllowingStateLoss();
-
- // TODO: commitAllowingStateLoss() is used to prevent https://code.google.com/p/android/issues/detail?id=23761
- // but is a bad...
- //fragment.show(ta, getDialogTag(dialogId));
- }
-
- private String getDialogTag(int dialogId) {
- return String.format(Locale.US, "dialog-%d", dialogId);
- }
-
- @Override
- public void doPositiveClick(int dialogId) {
- if (dialogId == R.id.dialog_account_setup_error) {
- finish();
- }
- }
-
- @Override
- public void doNegativeClick(int dialogId) {
- if (dialogId == R.id.dialog_account_setup_error) {
- mCanceled = false;
- setResult(RESULT_OK);
- finish();
- }
- }
-
- @Override
- public void dialogCancelled(int dialogId) {
- // nothing to do here...
- }
-
- private String errorMessageForCertificateException(CertificateValidationException e) {
- switch (e.getReason()) {
- case Expired: return getString(R.string.client_certificate_expired, e.getAlias(), e.getMessage());
- case MissingCapability: return getString(R.string.auth_external_error);
- case RetrievalFailure: return getString(R.string.client_certificate_retrieval_failure, e.getAlias());
- case UseMessage: return e.getMessage();
- case Unknown:
- default: return "";
- }
- }
-
- /**
- * FIXME: Don't use an AsyncTask to perform network operations.
- * See also discussion in https://github.com/k9mail/k-9/pull/560
- */
- private class CheckAccountTask extends AsyncTask {
- private final Account account;
-
- private CheckAccountTask(Account account) {
- this.account = account;
- }
-
- @Override
- protected Void doInBackground(CheckDirection... params) {
- final CheckDirection direction = params[0];
- try {
- /*
- * This task could be interrupted at any point, but network operations can block,
- * so relying on InterruptedException is not enough. Instead, check after
- * each potentially long-running operation.
- */
- if (cancelled()) {
- return null;
- }
-
- try {
- clearCertificateErrorNotifications(direction);
-
- checkServerSettings(direction);
-
- if (cancelled()) {
- return null;
- }
-
- setResult(RESULT_OK);
- finish();
- } catch (OAuth2NeedUserPromptException ignored) {
- //let the user do oauth2 flow procedure through webview
- Log.e(className, ignored.getMessage());
- }
-
- } catch (AuthenticationFailedException afe) {
- if (afe.getMessage().equals(AuthenticationFailedException.OAUTH2_ERROR_INVALID_REFRESH_TOKEN)) {
- //Do it it in another way
- oAuth2CodeGrantFlowManager.invalidateRefreshToken(mAccount.getEmail());
- runOnUiThread(() -> new CheckAccountTask(mAccount).execute(mDirection));
- } else {
- Timber.e(afe, "Error while testing settings");
- showErrorDialog(
- R.string.account_setup_failed_dlg_auth_message_fmt,
- afe.getMessage() == null ? "" : afe.getMessage());
- }
- } catch (CertificateValidationException cve) {
- handleCertificateValidationException(cve);
- } catch (Exception e) {
- Timber.e(e, "Error while testing settings");
- String message = e.getMessage() == null ? "" : e.getMessage();
- showErrorDialog(R.string.account_setup_failed_dlg_server_message_fmt, message);
- }
- return null;
- }
-
- private void clearCertificateErrorNotifications(CheckDirection direction) {
- final MessagingController ctrl = MessagingController.getInstance(getApplication());
- boolean incoming = (direction == CheckDirection.INCOMING);
- ctrl.clearCertificateErrorNotifications(account, incoming);
- }
-
- private boolean cancelled() {
- if (mDestroyed) {
- return true;
- }
- if (mCanceled) {
- finish();
- return true;
- }
- return false;
- }
-
- private void checkServerSettings(CheckDirection direction) throws MessagingException {
- switch (direction) {
- case INCOMING: {
- checkIncoming();
- break;
- }
- case OUTGOING: {
- checkOutgoing();
- break;
- }
- }
- }
-
- private void checkOutgoing() throws MessagingException {
- if (!isWebDavAccount()) {
- publishProgress(R.string.account_setup_check_settings_check_outgoing_msg);
- }
-
- messagingController.checkOutgoingServerSettings(account);
- }
-
- private void checkIncoming() throws MessagingException {
- if (isWebDavAccount()) {
- publishProgress(R.string.account_setup_check_settings_authenticate);
- } else {
- publishProgress(R.string.account_setup_check_settings_check_incoming_msg);
- }
-
- messagingController.checkIncomingServerSettings(account);
-
- if (isWebDavAccount()) {
- publishProgress(R.string.account_setup_check_settings_fetch);
- }
-
- messagingController.refreshFolderListSynchronous(account);
- Long inboxFolderId = account.getInboxFolderId();
- if (inboxFolderId != null) {
- messagingController.synchronizeMailbox(account, inboxFolderId, false, null);
- }
- }
-
- private boolean isWebDavAccount() {
- return account.getIncomingServerSettings().type.equals(Protocols.WEBDAV);
- }
-
- @Override
- protected void onProgressUpdate(Integer... values) {
- setMessage(values[0]);
- }
- }
-
- private final OAuth2PromptRequestHandler promptRequestHandler = new OAuth2PromptRequestHandler() {
-
- @Override
- public void handleRedirectUrl(WebViewClient webViewClient, String url) {
- openUrl(webViewClient, url);
- }
-
- @Override
- public void onObtainCodeSuccessful() {
- if (authDialog != null) {
- authDialog.dismiss();
- authDialog = null;
- }
- }
- @Override
- public void onObtainAccessTokenSuccessful() {
- //restart a settings check
- new CheckAccountTask(mAccount).execute(mDirection);
- }
-
- @Override
- public void onError(String errorMessage) {
- Toast.makeText(AccountSetupCheckSettings.this, errorMessage, Toast.LENGTH_LONG).show();
- finish();
- }
- };
-
- private void openUrl(WebViewClient webViewClient, String url) {
- runOnUiThread(() -> {
- CookieManager cookieManager = CookieManager.getInstance();
- //noinspection deprecation
- cookieManager.removeAllCookie();
-
- authDialog = new Dialog(this);
- authDialog.setContentView(R.layout.oauth_webview);
- WebView web = authDialog.findViewById(R.id.web_view);
- web.getSettings().setSaveFormData(false);
- web.getSettings().setJavaScriptEnabled(true);
- web.getSettings().setUserAgentString("K9 mail");
-
- web.setWebViewClient(webViewClient);
-
- web.getSettings().setUseWideViewPort(true);
-
- authDialog.setCancelable(false);
- authDialog.show();
-
- authDialog.setOnKeyListener((arg0, keyCode, event) -> {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- if (web.canGoBack()) {
- web.goBack();
- } else {
- onCancel();
- }
- return true;
- }
- return false;
- });
-
- web.loadUrl(url);
- });
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupCheckSettings.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupCheckSettings.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c6e88d1796213bf885df60f683ba01dae5d35a60
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupCheckSettings.kt
@@ -0,0 +1,514 @@
+package com.fsck.k9.activity.setup
+
+import android.app.Activity
+import android.app.AlertDialog
+import android.content.Intent
+import android.os.AsyncTask
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import android.widget.ProgressBar
+import android.widget.TextView
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.commit
+import com.fsck.k9.Account
+import com.fsck.k9.LocalKeyStoreManager
+import com.fsck.k9.Preferences
+import com.fsck.k9.controller.MessagingController
+import com.fsck.k9.fragment.ConfirmationDialogFragment
+import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.AuthenticationFailedException
+import com.fsck.k9.mail.CertificateValidationException
+import com.fsck.k9.mail.MailServerDirection
+import com.fsck.k9.mail.filter.Hex
+import com.fsck.k9.preferences.Protocols
+import com.fsck.k9.ui.R
+import com.fsck.k9.ui.base.K9Activity
+import com.fsck.k9.ui.observe
+import java.security.MessageDigest
+import java.security.NoSuchAlgorithmException
+import java.security.cert.CertificateEncodingException
+import java.security.cert.CertificateException
+import java.security.cert.X509Certificate
+import java.util.Locale
+import java.util.concurrent.Executors
+import org.koin.android.ext.android.inject
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import timber.log.Timber
+
+/**
+ * Checks the given settings to make sure that they can be used to send and receive mail.
+ *
+ * XXX NOTE: The manifest for this app has it ignore config changes, because it doesn't correctly deal with restarting
+ * while its thread is running.
+ */
+class AccountSetupCheckSettings : K9Activity(), ConfirmationDialogFragmentListener {
+ private val authViewModel: AuthViewModel by viewModel()
+
+ private val messagingController: MessagingController by inject()
+ private val preferences: Preferences by inject()
+ private val localKeyStoreManager: LocalKeyStoreManager by inject()
+
+ private val handler = Handler(Looper.myLooper()!!)
+
+ private lateinit var progressBar: ProgressBar
+ private lateinit var messageView: TextView
+
+ private lateinit var account: Account
+ private lateinit var direction: CheckDirection
+
+ @Volatile
+ private var canceled = false
+
+ @Volatile
+ private var destroyed = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setLayout(R.layout.account_setup_check_settings)
+
+ authViewModel.init(activityResultRegistry, lifecycle)
+
+ authViewModel.uiState.observe(this) { state ->
+ when (state) {
+ AuthFlowState.Idle -> {
+ return@observe
+ }
+ AuthFlowState.Success -> {
+ startCheckServerSettings()
+ }
+ AuthFlowState.Canceled -> {
+ showErrorDialog(R.string.account_setup_failed_dlg_oauth_flow_canceled)
+ }
+ is AuthFlowState.Failed -> {
+ showErrorDialog(R.string.account_setup_failed_dlg_oauth_flow_failed, state)
+ }
+ AuthFlowState.NotSupported -> {
+ showErrorDialog(R.string.account_setup_failed_dlg_oauth_not_supported)
+ }
+ AuthFlowState.BrowserNotFound -> {
+ showErrorDialog(R.string.account_setup_failed_dlg_browser_not_found)
+ }
+ }
+
+ authViewModel.authResultConsumed()
+ }
+
+ messageView = findViewById(R.id.message)
+ progressBar = findViewById(R.id.progress)
+ findViewById(R.id.cancel).setOnClickListener { onCancel() }
+
+ setMessage(R.string.account_setup_check_settings_retr_info_msg)
+ progressBar.isIndeterminate = true
+
+ val accountUuid = intent.getStringExtra(EXTRA_ACCOUNT) ?: error("Missing account UUID")
+ account = preferences.getAccount(accountUuid) ?: error("Could not find account")
+ direction = intent.getSerializableExtra(EXTRA_CHECK_DIRECTION) as CheckDirection?
+ ?: error("Missing CheckDirection")
+
+ if (savedInstanceState == null) {
+ if (needsAuthorization()) {
+ setMessage(R.string.account_setup_check_settings_authenticate)
+ authViewModel.login(account)
+ } else {
+ startCheckServerSettings()
+ }
+ }
+ }
+
+ private fun needsAuthorization(): Boolean {
+ return (
+ account.incomingServerSettings.authenticationType == AuthType.XOAUTH2 ||
+ account.outgoingServerSettings.authenticationType == AuthType.XOAUTH2
+ ) &&
+ !authViewModel.isAuthorized(account)
+ }
+
+ private fun startCheckServerSettings() {
+ CheckAccountTask(account).executeOnExecutor(Executors.newSingleThreadExecutor(), direction)
+ }
+
+ private fun handleCertificateValidationException(exception: CertificateValidationException) {
+ Timber.e(exception, "Error while testing settings")
+
+ val chain = exception.certChain
+
+ // Avoid NullPointerException in acceptKeyDialog()
+ if (chain != null) {
+ acceptKeyDialog(
+ R.string.account_setup_failed_dlg_certificate_message_fmt,
+ exception
+ )
+ } else {
+ showErrorDialog(
+ R.string.account_setup_failed_dlg_server_message_fmt,
+ errorMessageForCertificateException(exception)!!
+ )
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+
+ destroyed = true
+ canceled = true
+ }
+
+ private fun setMessage(resId: Int) {
+ messageView.text = getString(resId)
+ }
+
+ private fun acceptKeyDialog(msgResId: Int, exception: CertificateValidationException) {
+ handler.post {
+ if (destroyed) {
+ return@post
+ }
+
+ val errorMessage = exception.cause?.cause?.message ?: exception.cause?.message ?: exception.message
+
+ progressBar.isIndeterminate = false
+
+ val chainInfo = StringBuilder()
+ val chain = exception.certChain
+
+ // We already know chain != null (tested before calling this method)
+ for (i in chain.indices) {
+ // display certificate chain information
+ // TODO: localize this strings
+ chainInfo.append("Certificate chain[").append(i).append("]:\n")
+ chainInfo.append("Subject: ").append(chain[i].subjectDN.toString()).append("\n")
+
+ // display SubjectAltNames too
+ // (the user may be mislead into mistrusting a certificate
+ // by a subjectDN not matching the server even though a
+ // SubjectAltName matches)
+ try {
+ val subjectAlternativeNames = chain[i].subjectAlternativeNames
+ if (subjectAlternativeNames != null) {
+ // TODO: localize this string
+ val altNamesText = StringBuilder()
+ altNamesText.append("Subject has ")
+ .append(subjectAlternativeNames.size)
+ .append(" alternative names\n")
+
+ // we need these for matching
+ val incomingServerHost = account.incomingServerSettings.host!!
+ val outgoingServerHost = account.outgoingServerSettings.host!!
+ for (subjectAlternativeName in subjectAlternativeNames) {
+ val type = subjectAlternativeName[0] as Int
+ val value: Any? = subjectAlternativeName[1]
+ val name: String = when (type) {
+ 0 -> {
+ Timber.w("SubjectAltName of type OtherName not supported.")
+ continue
+ }
+ 1 -> value as String
+ 2 -> value as String
+ 3 -> {
+ Timber.w("unsupported SubjectAltName of type x400Address")
+ continue
+ }
+ 4 -> {
+ Timber.w("unsupported SubjectAltName of type directoryName")
+ continue
+ }
+ 5 -> {
+ Timber.w("unsupported SubjectAltName of type ediPartyName")
+ continue
+ }
+ 6 -> value as String
+ 7 -> value as String
+ else -> {
+ Timber.w("unsupported SubjectAltName of unknown type")
+ continue
+ }
+ }
+
+ // if some of the SubjectAltNames match the store or transport -host, display them
+ if (name.equals(incomingServerHost, ignoreCase = true) ||
+ name.equals(outgoingServerHost, ignoreCase = true)
+ ) {
+ // TODO: localize this string
+ altNamesText.append("Subject(alt): ").append(name).append(",...\n")
+ } else if (name.startsWith("*.") &&
+ (
+ incomingServerHost.endsWith(name.substring(2)) ||
+ outgoingServerHost.endsWith(name.substring(2))
+ )
+ ) {
+ // TODO: localize this string
+ altNamesText.append("Subject(alt): ").append(name).append(",...\n")
+ }
+ }
+ chainInfo.append(altNamesText)
+ }
+ } catch (e: Exception) {
+ // don't fail just because of subjectAltNames
+ Timber.w(e, "cannot display SubjectAltNames in dialog")
+ }
+
+ chainInfo.append("Issuer: ").append(chain[i].issuerDN.toString()).append("\n")
+ for (algorithm in arrayOf("SHA-1", "SHA-256", "SHA-512")) {
+ val digest = try {
+ MessageDigest.getInstance(algorithm)
+ } catch (e: NoSuchAlgorithmException) {
+ Timber.e(e, "Error while initializing MessageDigest ($algorithm)")
+ null
+ }
+
+ if (digest != null) {
+ digest.reset()
+ try {
+ val hash = Hex.encodeHex(digest.digest(chain[i].encoded))
+ chainInfo.append("Fingerprint ($algorithm): ").append("\n").append(hash).append("\n")
+ } catch (e: CertificateEncodingException) {
+ Timber.e(e, "Error while encoding certificate")
+ }
+ }
+ }
+ }
+
+ // TODO: refactor with DialogFragment.
+ // This is difficult because we need to pass through chain[0] for onClick()
+ AlertDialog.Builder(this@AccountSetupCheckSettings)
+ .setTitle(getString(R.string.account_setup_failed_dlg_invalid_certificate_title))
+ .setMessage(getString(msgResId, errorMessage) + " " + chainInfo.toString())
+ .setCancelable(true)
+ .setPositiveButton(R.string.account_setup_failed_dlg_invalid_certificate_accept) { _, _ ->
+ acceptCertificate(chain[0])
+ }
+ .setNegativeButton(R.string.account_setup_failed_dlg_invalid_certificate_reject) { _, _ ->
+ finish()
+ }
+ .show()
+ }
+ }
+
+ /**
+ * Permanently accepts a certificate for the INCOMING or OUTGOING direction by adding it to the local key store.
+ */
+ private fun acceptCertificate(certificate: X509Certificate) {
+ try {
+ localKeyStoreManager.addCertificate(account, direction.toMailServerDirection(), certificate)
+ } catch (e: CertificateException) {
+ showErrorDialog(R.string.account_setup_failed_dlg_certificate_message_fmt, e.message.orEmpty())
+ }
+
+ actionCheckSettings(this@AccountSetupCheckSettings, account, direction)
+ }
+
+ override fun onActivityResult(reqCode: Int, resCode: Int, data: Intent?) {
+ if (reqCode == ACTIVITY_REQUEST_CODE) {
+ setResult(resCode)
+ finish()
+ } else {
+ super.onActivityResult(reqCode, resCode, data)
+ }
+ }
+
+ private fun onCancel() {
+ canceled = true
+ setMessage(R.string.account_setup_check_settings_canceling_msg)
+
+ setResult(RESULT_CANCELED)
+ finish()
+ }
+
+ private fun showErrorDialog(msgResId: Int, vararg args: Any) {
+ handler.post {
+ showDialogFragment(R.id.dialog_account_setup_error, getString(msgResId, *args))
+ }
+ }
+
+ private fun showDialogFragment(dialogId: Int, customMessage: String) {
+ if (destroyed) return
+
+ progressBar.isIndeterminate = false
+
+ val fragment: DialogFragment = if (dialogId == R.id.dialog_account_setup_error) {
+ ConfirmationDialogFragment.newInstance(
+ dialogId,
+ getString(R.string.account_setup_failed_dlg_title),
+ customMessage,
+ getString(R.string.account_setup_failed_dlg_edit_details_action),
+ getString(R.string.account_setup_failed_dlg_continue_action)
+ )
+ } else {
+ throw RuntimeException("Called showDialog(int) with unknown dialog id.")
+ }
+
+ // TODO: commitAllowingStateLoss() is used to prevent https://code.google.com/p/android/issues/detail?id=23761
+ // but is a bad...
+ supportFragmentManager.commit(allowStateLoss = true) {
+ add(fragment, getDialogTag(dialogId))
+ }
+ }
+
+ private fun getDialogTag(dialogId: Int): String {
+ return String.format(Locale.US, "dialog-%d", dialogId)
+ }
+
+ override fun doPositiveClick(dialogId: Int) {
+ if (dialogId == R.id.dialog_account_setup_error) {
+ finish()
+ }
+ }
+
+ override fun doNegativeClick(dialogId: Int) {
+ if (dialogId == R.id.dialog_account_setup_error) {
+ canceled = false
+ setResult(RESULT_OK)
+ finish()
+ }
+ }
+
+ override fun dialogCancelled(dialogId: Int) = Unit
+
+ private fun errorMessageForCertificateException(e: CertificateValidationException): String? {
+ return when (e.reason) {
+ CertificateValidationException.Reason.Expired -> {
+ getString(R.string.client_certificate_expired, e.alias, e.message)
+ }
+ CertificateValidationException.Reason.MissingCapability -> {
+ getString(R.string.auth_external_error)
+ }
+ CertificateValidationException.Reason.RetrievalFailure -> {
+ getString(R.string.client_certificate_retrieval_failure, e.alias)
+ }
+ CertificateValidationException.Reason.UseMessage -> {
+ e.message
+ }
+ else -> {
+ ""
+ }
+ }
+ }
+
+ /**
+ * FIXME: Don't use an AsyncTask to perform network operations.
+ * See also discussion in https://github.com/k9mail/k-9/pull/560
+ */
+ private inner class CheckAccountTask(private val account: Account) : AsyncTask() {
+ override fun doInBackground(vararg params: CheckDirection) {
+ val direction = params[0]
+ try {
+ /*
+ * This task could be interrupted at any point, but network operations can block,
+ * so relying on InterruptedException is not enough. Instead, check after
+ * each potentially long-running operation.
+ */
+ if (isCanceled()) {
+ return
+ }
+
+ clearCertificateErrorNotifications(direction)
+
+ checkServerSettings(direction)
+
+ if (isCanceled()) {
+ return
+ }
+
+ setResult(RESULT_OK)
+ finish()
+ } catch (e: AuthenticationFailedException) {
+ Timber.e(e, "Error while testing settings")
+ showErrorDialog(R.string.account_setup_failed_dlg_auth_message_fmt, e.message.orEmpty())
+ } catch (e: CertificateValidationException) {
+ handleCertificateValidationException(e)
+ } catch (e: Exception) {
+ Timber.e(e, "Error while testing settings")
+ showErrorDialog(R.string.account_setup_failed_dlg_server_message_fmt, e.message.orEmpty())
+ }
+ }
+
+ private fun clearCertificateErrorNotifications(direction: CheckDirection) {
+ val incoming = direction == CheckDirection.INCOMING
+ messagingController.clearCertificateErrorNotifications(account, incoming)
+ }
+
+ private fun isCanceled(): Boolean {
+ if (destroyed) return true
+
+ if (canceled) {
+ finish()
+ return true
+ }
+
+ return false
+ }
+
+ private fun checkServerSettings(direction: CheckDirection) {
+ when (direction) {
+ CheckDirection.INCOMING -> checkIncoming()
+ CheckDirection.OUTGOING -> checkOutgoing()
+ }
+ }
+
+ private fun checkOutgoing() {
+ if (!isWebDavAccount) {
+ publishProgress(R.string.account_setup_check_settings_check_outgoing_msg)
+ }
+
+ messagingController.checkOutgoingServerSettings(account)
+ }
+
+ private fun checkIncoming() {
+ if (isWebDavAccount) {
+ publishProgress(R.string.account_setup_check_settings_authenticate)
+ } else {
+ publishProgress(R.string.account_setup_check_settings_check_incoming_msg)
+ }
+
+ messagingController.checkIncomingServerSettings(account)
+
+ if (isWebDavAccount) {
+ publishProgress(R.string.account_setup_check_settings_fetch)
+ }
+
+ messagingController.refreshFolderListSynchronous(account)
+
+ val inboxFolderId = account.inboxFolderId
+ if (inboxFolderId != null) {
+ messagingController.synchronizeMailbox(account, inboxFolderId, false, null)
+ }
+ }
+
+ private val isWebDavAccount: Boolean
+ get() = account.incomingServerSettings.type == Protocols.WEBDAV
+
+ override fun onProgressUpdate(vararg values: Int?) {
+ setMessage(values[0]!!)
+ }
+ }
+
+ enum class CheckDirection {
+ INCOMING, OUTGOING;
+
+ fun toMailServerDirection(): MailServerDirection {
+ return when (this) {
+ INCOMING -> MailServerDirection.INCOMING
+ OUTGOING -> MailServerDirection.OUTGOING
+ }
+ }
+ }
+
+ companion object {
+ const val ACTIVITY_REQUEST_CODE = 1
+
+ private const val EXTRA_ACCOUNT = "account"
+ private const val EXTRA_CHECK_DIRECTION = "checkDirection"
+
+ @JvmStatic
+ fun actionCheckSettings(context: Activity, account: Account, direction: CheckDirection) {
+ val intent = Intent(context, AccountSetupCheckSettings::class.java).apply {
+ putExtra(EXTRA_ACCOUNT, account.uuid)
+ putExtra(EXTRA_CHECK_DIRECTION, direction)
+ }
+
+ context.startActivityForResult(intent, ACTIVITY_REQUEST_CODE)
+ }
+ }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupComposition.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupComposition.java
index 719952ab2aa985a02b8012ee9b339ef4e5ef386b..10315606fd087ca4e6211251fa105bb2c34f3b79 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupComposition.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupComposition.java
@@ -3,12 +3,15 @@ package com.fsck.k9.activity.setup;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
+
+import androidx.annotation.NonNull;
import com.fsck.k9.Account;
import com.fsck.k9.Preferences;
import com.fsck.k9.ui.R;
@@ -48,6 +51,10 @@ public class AccountSetupComposition extends K9Activity {
setLayout(R.layout.account_setup_composition);
setTitle(R.string.account_settings_composition_title);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
/*
* If we're being reloaded we override the original account with the one
* we saved
@@ -101,6 +108,16 @@ public class AccountSetupComposition extends K9Activity {
}
}
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
private void saveSettings() {
mAccount.setEmail(mAccountEmail.getText().toString());
mAccount.setAlwaysBcc(mAccountAlwaysBcc.getText().toString());
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java
index 8009c42a48433c1fec1f0ef46851c3d22ce4b164..b8f971010e03ce364b519c83822356f54b13ecd7 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java
@@ -2,6 +2,9 @@
package com.fsck.k9.activity.setup;
+import java.util.Locale;
+import java.util.Map;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -9,6 +12,7 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -27,29 +31,22 @@ import com.fsck.k9.DI;
import com.fsck.k9.LocalKeyStoreManager;
import com.fsck.k9.Preferences;
import com.fsck.k9.account.AccountCreator;
-import com.fsck.k9.helper.EmailHelper;
-import com.fsck.k9.setup.ServerNameSuggester;
-import com.fsck.k9.ui.base.K9Activity;
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
-import com.fsck.k9.controller.MessagingController;
+import com.fsck.k9.helper.EmailHelper;
import com.fsck.k9.helper.Utility;
-import com.fsck.k9.job.K9JobManager;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.MailServerDirection;
-import com.fsck.k9.mail.NetworkType;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.store.imap.ImapStoreSettings;
import com.fsck.k9.mail.store.webdav.WebDavStoreSettings;
import com.fsck.k9.preferences.Protocols;
+import com.fsck.k9.setup.ServerNameSuggester;
import com.fsck.k9.ui.R;
+import com.fsck.k9.ui.base.K9Activity;
import com.fsck.k9.ui.base.extensions.TextInputLayoutHelper;
import com.fsck.k9.view.ClientCertificateSpinner;
import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
-
-import java.util.Locale;
-import java.util.Map;
-
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import timber.log.Timber;
@@ -63,8 +60,6 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
private static final String STATE_SECURITY_TYPE_POSITION = "stateSecurityTypePosition";
private static final String STATE_AUTH_TYPE_POSITION = "authTypePosition";
- private final MessagingController messagingController = DI.get(MessagingController.class);
- private final K9JobManager jobManager = DI.get(K9JobManager.class);
private final AccountCreator accountCreator = DI.get(AccountCreator.class);
private final ServerNameSuggester serverNameSuggester = DI.get(ServerNameSuggester.class);
@@ -89,9 +84,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
private Button mNextButton;
private Account mAccount;
private boolean mMakeDefault;
- private CheckBox mCompressionMobile;
- private CheckBox mCompressionWifi;
- private CheckBox mCompressionOther;
+ private CheckBox useCompressionCheckBox;
private CheckBox mSubscribedFoldersOnly;
private AuthTypeAdapter mAuthTypeAdapter;
private ConnectionSecurity[] mConnectionSecurityChoices = ConnectionSecurity.values();
@@ -138,9 +131,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
mWebdavAuthPathView = findViewById(R.id.webdav_auth_path);
mWebdavMailboxPathView = findViewById(R.id.webdav_mailbox_path);
mNextButton = findViewById(R.id.next);
- mCompressionMobile = findViewById(R.id.compression_mobile);
- mCompressionWifi = findViewById(R.id.compression_wifi);
- mCompressionOther = findViewById(R.id.compression_other);
+ useCompressionCheckBox = findViewById(R.id.use_compression);
mSubscribedFoldersOnly = findViewById(R.id.subscribed_folders_only);
mAllowClientCertificateView = findViewById(R.id.account_allow_client_certificate);
@@ -160,9 +151,6 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
}
});
- mAuthTypeAdapter = AuthTypeAdapter.get(this);
- mAuthTypeView.setAdapter(mAuthTypeAdapter);
-
/*
* Only allow digits in the port field.
*/
@@ -181,6 +169,10 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
}
+ boolean oAuthSupported = mAccount.getIncomingServerSettings().type.equals(Protocols.IMAP);
+ mAuthTypeAdapter = AuthTypeAdapter.get(this, oAuthSupported);
+ mAuthTypeView.setAdapter(mAuthTypeAdapter);
+
boolean editSettings = Intent.ACTION_EDIT.equals(getIntent().getAction());
if (editSettings) {
TextInputLayoutHelper.configureAuthenticatedPasswordToggle(
@@ -190,6 +182,10 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
getString(R.string.account_setup_basics_show_password_biometrics_subtitle),
getString(R.string.account_setup_basics_show_password_need_lock)
);
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
}
try {
@@ -222,8 +218,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
findViewById(R.id.webdav_mailbox_alias_section).setVisibility(View.GONE);
findViewById(R.id.webdav_owa_path_section).setVisibility(View.GONE);
findViewById(R.id.webdav_auth_path_section).setVisibility(View.GONE);
- findViewById(R.id.compression_section).setVisibility(View.GONE);
- findViewById(R.id.compression_label).setVisibility(View.GONE);
+ useCompressionCheckBox.setVisibility(View.GONE);
mSubscribedFoldersOnly.setVisibility(View.GONE);
} else if (settings.type.equals(Protocols.IMAP)) {
serverLayoutView.setHint(getString(R.string.account_setup_incoming_imap_server_label));
@@ -254,8 +249,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
findViewById(R.id.imap_path_prefix_section).setVisibility(View.GONE);
findViewById(R.id.account_auth_type_label).setVisibility(View.GONE);
findViewById(R.id.account_auth_type).setVisibility(View.GONE);
- findViewById(R.id.compression_section).setVisibility(View.GONE);
- findViewById(R.id.compression_label).setVisibility(View.GONE);
+ useCompressionCheckBox.setVisibility(View.GONE);
mSubscribedFoldersOnly.setVisibility(View.GONE);
String path = WebDavStoreSettings.getPath(settings);
@@ -287,7 +281,8 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
// Select currently configured security type
if (savedInstanceState == null) {
- mCurrentSecurityTypeViewPosition = securityTypesAdapter.getConnectionSecurityPosition(settings.connectionSecurity);
+ mCurrentSecurityTypeViewPosition =
+ securityTypesAdapter.getConnectionSecurityPosition(settings.connectionSecurity);
} else {
/*
@@ -305,9 +300,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
updateAuthPlainTextFromSecurityType(settings.connectionSecurity);
updateViewFromSecurity();
- mCompressionMobile.setChecked(mAccount.useCompression(NetworkType.MOBILE));
- mCompressionWifi.setChecked(mAccount.useCompression(NetworkType.WIFI));
- mCompressionOther.setChecked(mAccount.useCompression(NetworkType.OTHER));
+ useCompressionCheckBox.setChecked(mAccount.useCompression());
if (settings.host != null) {
mServerView.setText(settings.host);
@@ -327,11 +320,9 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
}
/**
- * Called at the end of either {@code onCreate()} or
- * {@code onRestoreInstanceState()}, after the views have been initialized,
- * so that the listeners are not triggered during the view initialization.
- * This avoids needless calls to {@code validateFields()} which is called
- * immediately after this is called.
+ * Called at the end of either {@code onCreate()} or {@code onRestoreInstanceState()}, after the views have been
+ * initialized, so that the listeners are not triggered during the view initialization. This avoids needless calls
+ * to {@code validateFields()} which is called immediately after this is called.
*/
private void initializeViewListeners() {
@@ -375,8 +366,8 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
validateFields();
AuthType selection = getSelectedAuthType();
- // Have the user select the client certificate if not already selected
- if ((AuthType.EXTERNAL == selection) && (mClientCertificateSpinner.getAlias() == null)) {
+ // Have the user select the client certificate if not already selected
+ if ((AuthType.EXTERNAL == selection) && (mClientCertificateSpinner.getAlias() == null)) {
// This may again invoke validateFields()
mClientCertificateSpinner.chooseCertificate();
} else {
@@ -423,11 +414,9 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
switch (getSelectedAuthType()) {
case EXTERNAL:
case XOAUTH2:
- // hide password fields, show client certificate fields
mPasswordLayoutView.setVisibility(View.GONE);
break;
default:
- // show password fields, hide client certificate fields
mPasswordLayoutView.setVisibility(View.VISIBLE);
break;
}
@@ -439,10 +428,11 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
*/
private void updateViewFromSecurity() {
ConnectionSecurity security = getSelectedSecurity();
- boolean isUsingTLS = ((ConnectionSecurity.SSL_TLS_REQUIRED == security) || (ConnectionSecurity.STARTTLS_REQUIRED == security));
- boolean isUsingXoauth2 = getSelectedAuthType() == AuthType.XOAUTH2;
+ boolean isUsingTLS = ((ConnectionSecurity.SSL_TLS_REQUIRED == security) ||
+ (ConnectionSecurity.STARTTLS_REQUIRED == security));
+ boolean isUsingOAuth = getSelectedAuthType() == AuthType.XOAUTH2;
- if (isUsingTLS && !isUsingXoauth2) {
+ if (isUsingTLS && !isUsingOAuth) {
mAllowClientCertificateView.setVisibility(View.VISIBLE);
} else {
mAllowClientCertificateView.setVisibility(View.GONE);
@@ -450,9 +440,8 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
}
/**
- * This is invoked only when the user makes changes to a widget, not when
- * widgets are changed programmatically. (The logic is simpler when you know
- * that this is the last thing called after an input change.)
+ * This is invoked only when the user makes changes to a widget, not when widgets are changed programmatically.
+ * (The logic is simpler when you know that this is the last thing called after an input change.)
*/
private void validateFields() {
AuthType authType = getSelectedAuthType();
@@ -513,12 +502,13 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
&& hasConnectionSecurity
&& hasValidCertificateAlias;
- boolean hasValidXOAuth2Settings = hasValidUserName
- && AuthType.XOAUTH2 == authType;
+ boolean hasValidOAuthSettings = hasValidUserName
+ && hasConnectionSecurity
+ && authType == AuthType.XOAUTH2;
mNextButton.setEnabled(Utility.domainFieldValid(mServerView)
&& Utility.requiredFieldValid(mPortView)
- && (hasValidPasswordSettings || hasValidExternalAuthSettings || hasValidXOAuth2Settings));
+ && (hasValidPasswordSettings || hasValidExternalAuthSettings || hasValidOAuthSettings));
Utility.setCompoundDrawablesAlpha(mNextButton, mNextButton.isEnabled() ? 255 : 128);
}
@@ -560,7 +550,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
String clientCertificateAlias = null;
AuthType authType = getSelectedAuthType();
if ((ConnectionSecurity.SSL_TLS_REQUIRED == getSelectedSecurity()) ||
- (ConnectionSecurity.STARTTLS_REQUIRED == getSelectedSecurity()) ) {
+ (ConnectionSecurity.STARTTLS_REQUIRED == getSelectedSecurity())) {
clientCertificateAlias = mClientCertificateSpinner.getAlias();
}
if (AuthType.EXTERNAL != authType) {
@@ -579,6 +569,16 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
}
}
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
protected void onNext() {
try {
ConnectionSecurity connectionSecurity = getSelectedSecurity();
@@ -590,7 +590,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
AuthType authType = getSelectedAuthType();
if ((ConnectionSecurity.SSL_TLS_REQUIRED == connectionSecurity) ||
- (ConnectionSecurity.STARTTLS_REQUIRED == connectionSecurity) ) {
+ (ConnectionSecurity.STARTTLS_REQUIRED == connectionSecurity)) {
clientCertificateAlias = mClientCertificateSpinner.getAlias();
}
if (authType != AuthType.EXTERNAL) {
@@ -617,9 +617,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
mAccount.setIncomingServerSettings(settings);
- mAccount.setCompression(NetworkType.MOBILE, mCompressionMobile.isChecked());
- mAccount.setCompression(NetworkType.WIFI, mCompressionWifi.isChecked());
- mAccount.setCompression(NetworkType.OTHER, mCompressionOther.isChecked());
+ mAccount.setUseCompression(useCompressionCheckBox.isChecked());
mAccount.setSubscribedFoldersOnly(mSubscribedFoldersOnly.isChecked());
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.INCOMING);
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java
index 3961748ba6330d1e01b22362fc63f7512d6a01f7..61bf5212392a2610adda24e39405e0a214ef5fd0 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java
@@ -10,6 +10,7 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -44,8 +45,9 @@ import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import timber.log.Timber;
+
public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
- OnCheckedChangeListener {
+ OnCheckedChangeListener {
private static final String EXTRA_ACCOUNT = "account";
private static final String STATE_SECURITY_TYPE_POSITION = "stateSecurityTypePosition";
@@ -127,7 +129,7 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
mSecurityTypeView.setAdapter(ConnectionSecurityAdapter.get(this));
- mAuthTypeAdapter = AuthTypeAdapter.get(this);
+ mAuthTypeAdapter = AuthTypeAdapter.get(this, true);
mAuthTypeView.setAdapter(mAuthTypeAdapter);
/*
@@ -157,13 +159,15 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
getString(R.string.account_setup_basics_show_password_biometrics_subtitle),
getString(R.string.account_setup_basics_show_password_need_lock)
);
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
}
try {
ServerSettings settings = mAccount.getOutgoingServerSettings();
- updateAuthPlainTextFromSecurityType(settings.connectionSecurity);
- updateViewFromSecurity(settings.connectionSecurity);
if (savedInstanceState == null) {
// The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter
mCurrentAuthTypeViewPosition = mAuthTypeAdapter.getAuthPosition(settings.authenticationType);
@@ -190,6 +194,9 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
}
mSecurityTypeView.setSelection(mCurrentSecurityTypeViewPosition, false);
+ updateAuthPlainTextFromSecurityType(getSelectedSecurity());
+ updateViewFromSecurity();
+
if (!settings.username.isEmpty()) {
mUsernameView.setText(settings.username);
mRequireLoginView.setChecked(true);
@@ -224,11 +231,9 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
}
/**
- * Called at the end of either {@code onCreate()} or
- * {@code onRestoreInstanceState()}, after the views have been initialized,
- * so that the listeners are not triggered during the view initialization.
- * This avoids needless calls to {@code validateFields()} which is called
- * immediately after this is called.
+ * Called at the end of either {@code onCreate()} or {@code onRestoreInstanceState()}, after the views have been
+ * initialized, so that the listeners are not triggered during the view initialization. This avoids needless calls
+ * to {@code validateFields()} which is called immediately after this is called.
*/
private void initializeViewListeners() {
@@ -251,7 +256,7 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
*/
if (mCurrentSecurityTypeViewPosition != position) {
updatePortFromSecurityType();
- updateViewFromSecurity(getSelectedSecurity());
+ updateViewFromSecurity();
boolean isInsecure = (ConnectionSecurity.NONE == getSelectedSecurity());
boolean isAuthExternal = (AuthType.EXTERNAL == getSelectedAuthType());
boolean loginNotRequired = !mRequireLoginView.isChecked();
@@ -292,6 +297,7 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
}
updateViewFromAuthType();
+ updateViewFromSecurity();
validateFields();
AuthType selection = getSelectedAuthType();
@@ -348,6 +354,16 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
validateFields();
}
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
/**
* Shows/hides password field
*/
@@ -355,22 +371,24 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
switch (getSelectedAuthType()) {
case EXTERNAL:
case XOAUTH2:
- // hide password fields
mPasswordLayoutView.setVisibility(View.GONE);
+ break;
default:
- // show password fields
mPasswordLayoutView.setVisibility(View.VISIBLE);
+ break;
}
}
/**
* Shows/hides client certificate spinner
*/
- private void updateViewFromSecurity(ConnectionSecurity security) {
- boolean isUsingTLS = ((ConnectionSecurity.SSL_TLS_REQUIRED == security) || (ConnectionSecurity.STARTTLS_REQUIRED == security));
- boolean isUsingXoauth2 = getSelectedAuthType() == AuthType.XOAUTH2;
+ private void updateViewFromSecurity() {
+ ConnectionSecurity security = getSelectedSecurity();
+ boolean isUsingTLS = ((ConnectionSecurity.SSL_TLS_REQUIRED == security) ||
+ (ConnectionSecurity.STARTTLS_REQUIRED == security));
+ boolean isUsingOAuth = getSelectedAuthType() == AuthType.XOAUTH2;
- if (isUsingTLS && !isUsingXoauth2) {
+ if (isUsingTLS && !isUsingOAuth) {
mAllowClientCertificateView.setVisibility(View.VISIBLE);
} else {
mAllowClientCertificateView.setVisibility(View.GONE);
@@ -379,9 +397,8 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
/**
- * This is invoked only when the user makes changes to a widget, not when
- * widgets are changed programmatically. (The logic is simpler when you know
- * that this is the last thing called after an input change.)
+ * This is invoked only when the user makes changes to a widget, not when widgets are changed programmatically.
+ * (The logic is simpler when you know that this is the last thing called after an input change.)
*/
private void validateFields() {
AuthType authType = getSelectedAuthType();
@@ -406,7 +423,7 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
mAuthTypeView.setSelection(mCurrentAuthTypeViewPosition, false);
mAuthTypeView.setOnItemSelectedListener(onItemSelectedListener);
updateViewFromAuthType();
- updateViewFromSecurity(getSelectedSecurity());
+ updateViewFromSecurity();
onItemSelectedListener = mSecurityTypeView.getOnItemSelectedListener();
mSecurityTypeView.setOnItemSelectedListener(null);
@@ -441,14 +458,15 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
&& hasConnectionSecurity
&& hasValidCertificateAlias;
- boolean hasValidXOAuth2Settings = hasValidUserName
- && AuthType.XOAUTH2 == authType;
+ boolean hasValidOAuthSettings = hasValidUserName
+ && hasConnectionSecurity
+ && authType == AuthType.XOAUTH2;
mNextButton
.setEnabled(Utility.domainFieldValid(mServerView)
&& Utility.requiredFieldValid(mPortView)
&& (!mRequireLoginView.isChecked()
- || hasValidPasswordSettings || hasValidExternalAuthSettings || hasValidXOAuth2Settings));
+ || hasValidPasswordSettings || hasValidExternalAuthSettings || hasValidOAuthSettings));
Utility.setCompoundDrawablesAlpha(mNextButton, mNextButton.isEnabled() ? 255 : 128);
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeAdapter.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeAdapter.java
index c094097552b7a352ad700033402aa880f6925dbb..2df6c5e683d6a8e5b3a172fe194671ef6c7efb3c 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeAdapter.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeAdapter.java
@@ -11,8 +11,14 @@ class AuthTypeAdapter extends ArrayAdapter {
super(context, resource, holders);
}
- public static AuthTypeAdapter get(Context context) {
- AuthType[] authTypes = new AuthType[]{AuthType.PLAIN, AuthType.CRAM_MD5, AuthType.EXTERNAL, AuthType.XOAUTH2};
+ public static AuthTypeAdapter get(Context context, boolean oAuthSupported) {
+ AuthType[] authTypes;
+ if (oAuthSupported) {
+ authTypes = new AuthType[] { AuthType.PLAIN, AuthType.CRAM_MD5, AuthType.EXTERNAL, AuthType.XOAUTH2 };
+ } else {
+ authTypes = new AuthType[] { AuthType.PLAIN, AuthType.CRAM_MD5, AuthType.EXTERNAL };
+ }
+
AuthTypeHolder[] holders = new AuthTypeHolder[authTypes.length];
for (int i = 0; i < authTypes.length; i++) {
holders[i] = new AuthTypeHolder(authTypes[i], context.getResources());
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeHolder.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeHolder.java
index eda9d1d122392f35ce28f70ba2ad2a1d55b3d9bc..a12739d55a3492b26fa7b5b0cee3297c5d8b2912 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeHolder.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthTypeHolder.java
@@ -42,7 +42,7 @@ class AuthTypeHolder {
case EXTERNAL:
return R.string.account_setup_auth_type_tls_client_certificate;
case XOAUTH2:
- return R.string.account_setup_auth_type_xoauth2;
+ return R.string.account_setup_auth_type_oauth2;
case AUTOMATIC:
case LOGIN:
default:
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthViewModel.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fb66d03c7b22c1a8343b642aeaefd5c4ad0de6c3
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthViewModel.kt
@@ -0,0 +1,261 @@
+package com.fsck.k9.activity.setup
+
+import android.app.Activity
+import android.app.Application
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.ActivityResultRegistry
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.core.net.toUri
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.viewModelScope
+import com.fsck.k9.Account
+import com.fsck.k9.oauth.OAuthConfiguration
+import com.fsck.k9.oauth.OAuthConfigurationProvider
+import com.fsck.k9.preferences.AccountManager
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import net.openid.appauth.AuthState
+import net.openid.appauth.AuthorizationException
+import net.openid.appauth.AuthorizationRequest
+import net.openid.appauth.AuthorizationResponse
+import net.openid.appauth.AuthorizationService
+import net.openid.appauth.AuthorizationServiceConfiguration
+import net.openid.appauth.ResponseTypeValues
+import timber.log.Timber
+
+private const val KEY_AUTHORIZATION = "app.k9mail_auth"
+
+class AuthViewModel(
+ application: Application,
+ private val accountManager: AccountManager,
+ private val oAuthConfigurationProvider: OAuthConfigurationProvider
+) : AndroidViewModel(application) {
+ private var authService: AuthorizationService? = null
+ private val authState = AuthState()
+
+ private var account: Account? = null
+
+ private lateinit var resultObserver: AppAuthResultObserver
+
+ private val _uiState = MutableStateFlow(AuthFlowState.Idle)
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ @Synchronized
+ private fun getAuthService(): AuthorizationService {
+ return authService ?: AuthorizationService(getApplication()).also { authService = it }
+ }
+
+ fun init(activityResultRegistry: ActivityResultRegistry, lifecycle: Lifecycle) {
+ resultObserver = AppAuthResultObserver(activityResultRegistry)
+ lifecycle.addObserver(resultObserver)
+ }
+
+ fun authResultConsumed() {
+ _uiState.update { AuthFlowState.Idle }
+ }
+
+ fun isAuthorized(account: Account): Boolean {
+ val authState = getOrCreateAuthState(account)
+ return authState.isAuthorized
+ }
+
+ fun isUsingGoogle(account: Account): Boolean {
+ return oAuthConfigurationProvider.isGoogle(account.incomingServerSettings.host!!)
+ }
+
+ private fun getOrCreateAuthState(account: Account): AuthState {
+ return try {
+ account.oAuthState?.let { AuthState.jsonDeserialize(it) } ?: AuthState()
+ } catch (e: Exception) {
+ Timber.e(e, "Error deserializing AuthState")
+ AuthState()
+ }
+ }
+
+ fun login(account: Account) {
+ this.account = account
+
+ viewModelScope.launch {
+ val config = findOAuthConfiguration(account)
+ if (config == null) {
+ _uiState.update { AuthFlowState.NotSupported }
+ return@launch
+ }
+
+ try {
+ startLogin(account, config)
+ } catch (e: ActivityNotFoundException) {
+ _uiState.update { AuthFlowState.BrowserNotFound }
+ }
+ }
+ }
+
+ private suspend fun startLogin(account: Account, config: OAuthConfiguration) {
+ val authRequestIntent = withContext(Dispatchers.IO) {
+ createAuthorizationRequestIntent(account.email, config)
+ }
+
+ resultObserver.login(authRequestIntent)
+ }
+
+ private fun createAuthorizationRequestIntent(email: String, config: OAuthConfiguration): Intent {
+ val serviceConfig = AuthorizationServiceConfiguration(
+ config.authorizationEndpoint.toUri(),
+ config.tokenEndpoint.toUri()
+ )
+
+ val authRequestBuilder = AuthorizationRequest.Builder(
+ serviceConfig,
+ config.clientId,
+ ResponseTypeValues.CODE,
+ config.redirectUri.toUri()
+ )
+
+ val scopeString = config.scopes.joinToString(separator = " ")
+ val authRequest = authRequestBuilder
+ .setScope(scopeString)
+ .setLoginHint(email)
+ .build()
+
+ val authService = getAuthService()
+
+ return authService.getAuthorizationRequestIntent(authRequest)
+ }
+
+ private fun findOAuthConfiguration(account: Account): OAuthConfiguration? {
+ return oAuthConfigurationProvider.getConfiguration(account.incomingServerSettings.host!!)
+ }
+
+ private fun onLoginResult(authorizationResult: AuthorizationResult?) {
+ if (authorizationResult == null) {
+ _uiState.update { AuthFlowState.Canceled }
+ return
+ }
+
+ authorizationResult.response?.let { response ->
+ authState.update(authorizationResult.response, authorizationResult.exception)
+ exchangeToken(response)
+ }
+
+ authorizationResult.exception?.let { authorizationException ->
+ _uiState.update {
+ AuthFlowState.Failed(
+ errorCode = authorizationException.error,
+ errorMessage = authorizationException.errorDescription
+ )
+ }
+ }
+ }
+
+ private fun exchangeToken(response: AuthorizationResponse) {
+ viewModelScope.launch(Dispatchers.IO) {
+ val authService = getAuthService()
+
+ val tokenRequest = response.createTokenExchangeRequest()
+ authService.performTokenRequest(tokenRequest) { tokenResponse, authorizationException ->
+ authState.update(tokenResponse, authorizationException)
+
+ val account = account!!
+ account.oAuthState = authState.jsonSerializeString()
+
+ viewModelScope.launch(Dispatchers.IO) {
+ accountManager.saveAccount(account)
+ }
+
+ if (authorizationException != null) {
+ _uiState.update {
+ AuthFlowState.Failed(
+ errorCode = authorizationException.error,
+ errorMessage = authorizationException.errorDescription
+ )
+ }
+ } else {
+ _uiState.update { AuthFlowState.Success }
+ }
+ }
+ }
+ }
+
+ @Synchronized
+ override fun onCleared() {
+ authService?.dispose()
+ authService = null
+ }
+
+ inner class AppAuthResultObserver(private val registry: ActivityResultRegistry) : DefaultLifecycleObserver {
+ private var authorizationLauncher: ActivityResultLauncher? = null
+ private var authRequestIntent: Intent? = null
+
+ override fun onCreate(owner: LifecycleOwner) {
+ authorizationLauncher = registry.register(KEY_AUTHORIZATION, AuthorizationContract(), ::onLoginResult)
+ authRequestIntent?.let { intent ->
+ authRequestIntent = null
+ login(intent)
+ }
+ }
+
+ override fun onDestroy(owner: LifecycleOwner) {
+ authorizationLauncher = null
+ }
+
+ fun login(authRequestIntent: Intent) {
+ val launcher = authorizationLauncher
+ if (launcher != null) {
+ launcher.launch(authRequestIntent)
+ } else {
+ this.authRequestIntent = authRequestIntent
+ }
+ }
+ }
+}
+
+private class AuthorizationContract : ActivityResultContract() {
+ override fun createIntent(context: Context, input: Intent): Intent {
+ return input
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): AuthorizationResult? {
+ return if (resultCode == Activity.RESULT_OK && intent != null) {
+ AuthorizationResult(
+ response = AuthorizationResponse.fromIntent(intent),
+ exception = AuthorizationException.fromIntent(intent)
+ )
+ } else {
+ null
+ }
+ }
+}
+
+private data class AuthorizationResult(
+ val response: AuthorizationResponse?,
+ val exception: AuthorizationException?
+)
+
+sealed interface AuthFlowState {
+ object Idle : AuthFlowState
+
+ object Success : AuthFlowState
+
+ object NotSupported : AuthFlowState
+
+ object BrowserNotFound : AuthFlowState
+
+ object Canceled : AuthFlowState
+
+ data class Failed(val errorCode: String?, val errorMessage: String?) : AuthFlowState {
+ override fun toString(): String {
+ return listOfNotNull(errorCode, errorMessage).joinToString(separator = " - ")
+ }
+ }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/OAuthFlowActivity.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/OAuthFlowActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b47729f2e2ce5cfb73ed191df7968d92dfacca81
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/OAuthFlowActivity.kt
@@ -0,0 +1,114 @@
+package com.fsck.k9.activity.setup
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.widget.Button
+import android.widget.ProgressBar
+import android.widget.TextView
+import androidx.core.view.isVisible
+import com.fsck.k9.Account
+import com.fsck.k9.preferences.AccountManager
+import com.fsck.k9.ui.R
+import com.fsck.k9.ui.base.K9Activity
+import com.fsck.k9.ui.observe
+import org.koin.android.ext.android.inject
+import org.koin.androidx.viewmodel.ext.android.viewModel
+
+class OAuthFlowActivity : K9Activity() {
+ private val authViewModel: AuthViewModel by viewModel()
+ private val accountManager: AccountManager by inject()
+
+ private lateinit var errorText: TextView
+ private lateinit var signInButton: Button
+ private lateinit var signInProgress: ProgressBar
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setLayout(R.layout.account_setup_oauth)
+ setTitle(R.string.account_setup_basics_title)
+
+ val accountUUid = intent.getStringExtra(EXTRA_ACCOUNT_UUID) ?: error("Missing account UUID")
+ val account = accountManager.getAccount(accountUUid) ?: error("Account not found")
+
+ errorText = findViewById(R.id.error_text)
+ signInProgress = findViewById(R.id.sign_in_progress)
+ signInButton = if (authViewModel.isUsingGoogle(account)) {
+ findViewById(R.id.google_sign_in_button)
+ } else {
+ findViewById(R.id.oauth_sign_in_button)
+ }
+
+ signInButton.isVisible = true
+ signInButton.setOnClickListener { startOAuthFlow(account) }
+
+ savedInstanceState?.let {
+ val signInRunning = it.getBoolean(STATE_PROGRESS)
+ signInButton.isVisible = !signInRunning
+ signInProgress.isVisible = signInRunning
+ }
+
+ authViewModel.init(activityResultRegistry, lifecycle)
+
+ authViewModel.uiState.observe(this) { state ->
+ handleUiUpdates(state)
+ }
+ }
+
+ private fun handleUiUpdates(state: AuthFlowState) {
+ when (state) {
+ AuthFlowState.Idle -> {
+ return
+ }
+ AuthFlowState.Success -> {
+ setResult(RESULT_OK)
+ finish()
+ }
+ AuthFlowState.Canceled -> {
+ displayErrorText(R.string.account_setup_failed_dlg_oauth_flow_canceled)
+ }
+ is AuthFlowState.Failed -> {
+ displayErrorText(R.string.account_setup_failed_dlg_oauth_flow_failed, state)
+ }
+ AuthFlowState.NotSupported -> {
+ displayErrorText(R.string.account_setup_failed_dlg_oauth_not_supported)
+ }
+ AuthFlowState.BrowserNotFound -> {
+ displayErrorText(R.string.account_setup_failed_dlg_browser_not_found)
+ }
+ }
+
+ authViewModel.authResultConsumed()
+ }
+
+ private fun displayErrorText(errorTextResId: Int, vararg args: Any?) {
+ signInProgress.isVisible = false
+ signInButton.isVisible = true
+ errorText.text = getString(errorTextResId, *args)
+ }
+
+ private fun startOAuthFlow(account: Account) {
+ signInButton.isVisible = false
+ signInProgress.isVisible = true
+ errorText.text = ""
+
+ authViewModel.login(account)
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putBoolean(STATE_PROGRESS, signInProgress.isVisible)
+ }
+
+ companion object {
+ private const val EXTRA_ACCOUNT_UUID = "accountUuid"
+
+ private const val STATE_PROGRESS = "signInProgress"
+
+ fun buildLaunchIntent(context: Context, accountUuid: String): Intent {
+ return Intent(context, OAuthFlowActivity::class.java).apply {
+ putExtra(EXTRA_ACCOUNT_UUID, accountUuid)
+ }
+ }
+ }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListAdapter.kt b/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListAdapter.kt
index 4b462b7ce429b9cb178e1a7e000279181bacdfc5..197f1dea1af1ba8f04d993047a3adc9d2164d7a8 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListAdapter.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListAdapter.kt
@@ -90,6 +90,12 @@ class MessageListAdapter internal constructor(
listItemListener.onToggleMessageSelection(messageListItem)
}
+ private fun recipientSigil(toMe: Boolean, ccMe: Boolean) = when {
+ toMe -> res.getString(R.string.messagelist_sent_to_me_sigil) + " "
+ ccMe -> res.getString(R.string.messagelist_sent_cc_me_sigil) + " "
+ else -> ""
+ }
+
override fun hasStableIds(): Boolean = true
override fun getCount(): Int = messages.size
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/dialog/ApgDeprecationWarningDialog.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/dialog/ApgDeprecationWarningDialog.java
deleted file mode 100644
index 093cff9207aebec58c019542fb249e90c1eb6a12..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/dialog/ApgDeprecationWarningDialog.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.fsck.k9.ui.dialog;
-
-
-import android.annotation.SuppressLint;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.text.method.LinkMovementMethod;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import com.fsck.k9.ui.R;
-
-
-public class ApgDeprecationWarningDialog extends AlertDialog {
- public ApgDeprecationWarningDialog(Context context) {
- super(context);
-
- LayoutInflater inflater = LayoutInflater.from(context);
-
- @SuppressLint("InflateParams")
- View contentView = inflater.inflate(R.layout.dialog_apg_deprecated, null);
-
- TextView textViewLearnMore = contentView.findViewById(R.id.apg_learn_more);
- makeTextViewLinksClickable(textViewLearnMore);
-
- setIcon(R.drawable.ic_apg_small);
- setTitle(R.string.apg_deprecated_title);
- setView(contentView);
- setButton(Dialog.BUTTON_POSITIVE, context.getString(R.string.apg_deprecated_ok), new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- cancel();
- }
- });
- }
-
- private void makeTextViewLinksClickable(TextView textView) {
- textView.setMovementMethod(LinkMovementMethod.getInstance());
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentController.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentController.java
index 1248e19e462cbfb4b4b9b9d351bfb92cbbc13a93..fab62cbab326b485e5fdd684f444ace7b60e7eb6 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentController.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentController.java
@@ -21,9 +21,9 @@ import com.fsck.k9.Account;
import com.fsck.k9.Preferences;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.SimpleMessagingListener;
+import com.fsck.k9.helper.MimeTypeUtil;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.Part;
-import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mailstore.AttachmentViewInfo;
import com.fsck.k9.mailstore.LocalMessage;
import com.fsck.k9.mailstore.LocalPart;
@@ -117,7 +117,7 @@ public class AttachmentController {
ContentResolver contentResolver = context.getContentResolver();
InputStream in = contentResolver.openInputStream(attachment.internalUri);
try {
- OutputStream out = contentResolver.openOutputStream(documentUri);
+ OutputStream out = contentResolver.openOutputStream(documentUri, "wt");
try {
IOUtils.copy(in, out);
out.flush();
@@ -140,11 +140,11 @@ public class AttachmentController {
}
String displayName = attachment.displayName;
- String inferredMimeType = MimeUtility.getMimeTypeByExtension(displayName);
+ String inferredMimeType = MimeTypeUtil.getMimeTypeByExtension(displayName);
IntentAndResolvedActivitiesCount resolvedIntentInfo;
String mimeType = attachment.mimeType;
- if (MimeUtility.isDefaultMimeType(mimeType)) {
+ if (MimeTypeUtil.isDefaultMimeType(mimeType)) {
resolvedIntentInfo = getViewIntentForMimeType(intentDataUri, inferredMimeType);
} else {
resolvedIntentInfo = getViewIntentForMimeType(intentDataUri, mimeType);
@@ -154,7 +154,7 @@ public class AttachmentController {
}
if (!resolvedIntentInfo.hasResolvedActivities()) {
- resolvedIntentInfo = getViewIntentForMimeType(intentDataUri, MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE);
+ resolvedIntentInfo = getViewIntentForMimeType(intentDataUri, MimeTypeUtil.DEFAULT_ATTACHMENT_MIME_TYPE);
}
return resolvedIntentInfo.getIntent();
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java
index d38cbab8c146cdcb6917ad3c0caebfa9b88e3eca..e1ce84d49d4e084b78e028ad9cf0c34e7c655534 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java
@@ -36,6 +36,9 @@ import com.fsck.k9.Account;
import com.fsck.k9.DI;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
+import com.fsck.k9.activity.MessageCompose;
+import com.fsck.k9.helper.MailtoUnsubscribeUri;
+import com.fsck.k9.helper.UnsubscribeUri;
import com.fsck.k9.ui.choosefolder.ChooseFolderActivity;
import com.fsck.k9.activity.MessageLoaderHelper;
import com.fsck.k9.activity.MessageLoaderHelper.MessageLoaderCallbacks;
@@ -96,6 +99,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
private MessageLoaderHelper messageLoaderHelper;
private MessageCryptoPresenter messageCryptoPresenter;
private Long showProgressThreshold;
+ private UnsubscribeUri preferredUnsubscribeUri;
/**
* Used to temporarily store the destination folder for refile operations if a confirmation
@@ -655,6 +659,23 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
return mMessageReference.getFolderId() != spamFolderId;
}
+ public boolean canMessageBeUnsubscribed() {
+ return preferredUnsubscribeUri != null;
+ }
+
+ public void onUnsubscribe() {
+ if (preferredUnsubscribeUri instanceof MailtoUnsubscribeUri) {
+ Intent intent = new Intent(mContext, MessageCompose.class);
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setData(preferredUnsubscribeUri.getUri());
+ intent.putExtra(MessageCompose.EXTRA_ACCOUNT, mMessageReference.getAccountUuid());
+ startActivity(intent);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW, preferredUnsubscribeUri.getUri());
+ startActivity(intent);
+ }
+ }
+
public Context getApplicationContext() {
return mContext;
}
@@ -779,12 +800,14 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
@Override
public void onMessageViewInfoLoadFinished(MessageViewInfo messageViewInfo) {
showMessage(messageViewInfo);
+ preferredUnsubscribeUri = messageViewInfo.preferredUnsubscribeUri;
showProgressThreshold = null;
}
@Override
public void onMessageViewInfoLoadFailed(MessageViewInfo messageViewInfo) {
showMessage(messageViewInfo);
+ preferredUnsubscribeUri = null;
showProgressThreshold = null;
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/OpenPgpAppSelectDialog.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/OpenPgpAppSelectDialog.java
index ec01b6ece7d2ab47c71482d50faa9d3b6c50c8f1..d9045bc4fa590413cffe410e317abe32e3b3e886 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/OpenPgpAppSelectDialog.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/OpenPgpAppSelectDialog.java
@@ -30,7 +30,6 @@ import com.fsck.k9.Preferences;
import com.fsck.k9.ui.R;
import com.fsck.k9.ui.base.K9Activity;
import com.fsck.k9.ui.base.ThemeType;
-import com.fsck.k9.ui.dialog.ApgDeprecationWarningDialog;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpProviderUtil;
import timber.log.Timber;
@@ -40,11 +39,8 @@ public class OpenPgpAppSelectDialog extends K9Activity {
private static final String EXTRA_ACCOUNT = "account";
private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain";
- private static final String PACKAGE_NAME_APG = "org.thialfihar.android.apg";
- private static final String APG_PROVIDER_PLACEHOLDER = "apg-placeholder";
public static final String FRAG_OPENPGP_SELECT = "openpgp_select";
- public static final String FRAG_APG_DEPRECATE = "apg_deprecate";
public static final String FRAG_OPENKEYCHAIN_INFO = "openkeychain_info";
private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
@@ -53,7 +49,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
String.format("https://play.google.com/store/apps/details?id=%s", OPENKEYCHAIN_PACKAGE)));
- private boolean isStopped;
private Account account;
public static void startOpenPgpChooserActivity(Context context, Account account) {
@@ -76,7 +71,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
@Override
protected void onStart() {
- isStopped = false;
super.onStart();
List openPgpProviderPackages = OpenPgpProviderUtil.getOpenPgpProviderPackages(this);
@@ -93,7 +87,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
@Override
protected void onStop() {
- isStopped = true;
super.onStop();
}
@@ -102,11 +95,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
fragment.show(getSupportFragmentManager(), FRAG_OPENPGP_SELECT);
}
- private void showApgDeprecationDialogFragment() {
- ApgDeprecationDialogFragment fragment = new ApgDeprecationDialogFragment();
- fragment.show(getSupportFragmentManager(), FRAG_APG_DEPRECATE);
- }
-
private void showOpenKeychainInfoFragment() {
OpenKeychainInfoFragment fragment = new OpenKeychainInfoFragment();
fragment.show(getSupportFragmentManager(), FRAG_OPENKEYCHAIN_INFO);
@@ -126,12 +114,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
getResources().getDrawable(R.drawable.ic_action_cancel_launchersize_light));
openPgpProviderList.add(noneEntry);
- if (isApgInstalled(getActivity())) {
- Drawable icon = getResources().getDrawable(R.drawable.ic_apg_small);
- openPgpProviderList.add(new OpenPgpProviderEntry(
- APG_PROVIDER_PLACEHOLDER, getString(R.string.apg), icon));
- }
-
// search for OpenPGP providers...
Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT_2);
List resInfo = getActivity().getPackageManager().queryIntentServices(intent, 0);
@@ -170,13 +152,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
}
}
- private boolean isApgInstalled(Context context) {
- Intent intent = new Intent("org.openintents.openpgp.IOpenPgpService");
- intent.setPackage(PACKAGE_NAME_APG);
- List resInfo = context.getPackageManager().queryIntentServices(intent, 0);
- return resInfo != null && !resInfo.isEmpty();
- }
-
@Override
public void onStop() {
super.onStop();
@@ -251,27 +226,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
}
}
- public static class ApgDeprecationDialogFragment extends DialogFragment {
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return new ApgDeprecationWarningDialog(getActivity());
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- dismiss();
- }
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- super.onDismiss(dialog);
-
- ((OpenPgpAppSelectDialog) getActivity()).onDismissApgDialog();
- }
- }
-
public static class OpenKeychainInfoFragment extends DialogFragment {
@NonNull
@Override
@@ -323,11 +277,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
}
public void onSelectProvider(String selectedPackage) {
- if (APG_PROVIDER_PLACEHOLDER.equals(selectedPackage)) {
- showApgDeprecationDialogFragment();
- return;
- }
-
persistOpenPgpProviderSetting(selectedPackage);
finish();
}
@@ -337,12 +286,6 @@ public class OpenPgpAppSelectDialog extends K9Activity {
Preferences.getPreferences(getApplicationContext()).saveAccount(account);
}
- public void onDismissApgDialog() {
- if (!isStopped) {
- showOpenPgpSelectDialogFragment();
- }
- }
-
private static class OpenPgpProviderEntry {
private String packageName;
private String simpleName;
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/AccountActivator.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/AccountActivator.kt
index f9f3ed2627dfa15d744d3a8f2b5490faee99c5c6..e7de2f3dc834164ef6ea5c94aae8af5236752adf 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/AccountActivator.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/AccountActivator.kt
@@ -18,7 +18,16 @@ class AccountActivator(
val account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
setAccountPasswords(account, incomingServerPassword, outgoingServerPassword)
+ enableAccount(account)
+ }
+
+ fun enableAccount(accountUuid: String) {
+ val account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
+
+ enableAccount(account)
+ }
+ private fun enableAccount(account: Account) {
// Start services if necessary
Core.setServicesEnabled(context)
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt
index 6a7260756ef7c279011a6077dd5fde61be8bb78b..330b1b5711078c250ea235a5917eef507283f83d 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt
@@ -14,6 +14,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
+import com.fsck.k9.activity.setup.OAuthFlowActivity
import com.fsck.k9.ui.R
import com.fsck.k9.ui.observeNotNull
import com.mikepenz.fastadapter.FastAdapter
@@ -103,6 +104,12 @@ class SettingsImportFragment : Fragment() {
StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
statusText.text = getString(R.string.settings_import_password_required)
}
+ StatusText.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> {
+ statusText.text = getString(R.string.settings_import_authorization_required)
+ }
+ StatusText.IMPORT_SUCCESS_PASSWORD_AND_AUTHORIZATION_REQUIRED -> {
+ statusText.text = getString(R.string.settings_import_authorization_and_password_required)
+ }
StatusText.IMPORT_READ_FAILURE -> {
statusText.text = getString(R.string.settings_import_read_failure)
}
@@ -142,6 +149,7 @@ class SettingsImportFragment : Fragment() {
is Action.Close -> closeImportScreen(action)
is Action.PickDocument -> pickDocument()
is Action.PasswordPrompt -> showPasswordPrompt(action)
+ is Action.StartAuthorization -> startAuthorization(action)
}
}
@@ -160,6 +168,15 @@ class SettingsImportFragment : Fragment() {
startActivityForResult(createDocumentIntent, REQUEST_PICK_DOCUMENT)
}
+ private fun startAuthorization(action: Action.StartAuthorization) {
+ val intent = OAuthFlowActivity.buildLaunchIntent(
+ context = requireContext(),
+ accountUuid = action.accountUuid
+ )
+
+ startActivityForResult(intent, REQUEST_AUTHORIZATION)
+ }
+
private fun showPasswordPrompt(action: Action.PasswordPrompt) {
val dialogFragment = PasswordPromptDialogFragment.create(
action.accountUuid,
@@ -183,6 +200,7 @@ class SettingsImportFragment : Fragment() {
when (requestCode) {
REQUEST_PICK_DOCUMENT -> handlePickDocumentResult(resultCode, data)
REQUEST_PASSWORD_PROMPT -> handlePasswordPromptResult(resultCode, data)
+ REQUEST_AUTHORIZATION -> handleAuthorizationResult(resultCode)
}
}
@@ -203,9 +221,16 @@ class SettingsImportFragment : Fragment() {
}
}
+ private fun handleAuthorizationResult(resultCode: Int) {
+ if (resultCode == Activity.RESULT_OK) {
+ viewModel.onReturnAfterAuthorization()
+ }
+ }
+
companion object {
private const val REQUEST_PICK_DOCUMENT = Activity.RESULT_FIRST_USER
private const val REQUEST_PASSWORD_PROMPT = Activity.RESULT_FIRST_USER + 1
+ private const val REQUEST_AUTHORIZATION = Activity.RESULT_FIRST_USER + 2
}
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportListItems.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportListItems.kt
index 71ac7d88d479fbc2ce2388f1c242700154bd3664..93f727a730bc224e719c2a6f36b8a3503a6b40ae 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportListItems.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportListItems.kt
@@ -32,8 +32,9 @@ abstract class ImportListItem(
val imageLevel = when (importStatus) {
ImportStatus.IMPORT_SUCCESS -> 0
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> 1
- ImportStatus.NOT_SELECTED -> 2
- ImportStatus.IMPORT_FAILURE -> 3
+ ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> 2
+ ImportStatus.NOT_SELECTED -> 3
+ ImportStatus.IMPORT_FAILURE -> 4
else -> error("Unexpected import status: $importStatus")
}
holder.statusIcon.setImageLevel(imageLevel)
@@ -41,6 +42,7 @@ abstract class ImportListItem(
val contentDescriptionStringResId = when (importStatus) {
ImportStatus.IMPORT_SUCCESS -> R.string.settings_import_status_success
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> R.string.settings_import_status_password_required
+ ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> R.string.settings_import_status_log_in_required
ImportStatus.NOT_SELECTED -> R.string.settings_import_status_not_imported
ImportStatus.IMPORT_FAILURE -> R.string.settings_import_status_error
else -> error("Unexpected import status: $importStatus")
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportUiModel.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportUiModel.kt
index 0c990550241b9a563670c4e74c125740a0736d83..817aed1c5df664964653c6adce8c3fa066408b7a 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportUiModel.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportUiModel.kt
@@ -67,13 +67,13 @@ class SettingsImportUiModel {
statusText = StatusText.IMPORT_SUCCESS
}
- private fun showPasswordRequiredText() {
+ private fun showActionRequiredText(actionText: StatusText) {
importButton = ButtonState.GONE
closeButton = ButtonState.ENABLED
closeButtonLabel = CloseButtonLabel.LATER
isImportProgressVisible = false
isSettingsListEnabled = true
- statusText = StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED
+ statusText = actionText
}
fun showReadFailureText() {
@@ -120,7 +120,7 @@ class SettingsImportUiModel {
fun setSettingsListState(position: Int, status: ImportStatus) {
settingsList[position].importStatus = status
- settingsList[position].enabled = status == ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED
+ settingsList[position].enabled = status.isActionRequired
}
private fun updateImportButtonFromSelection() {
@@ -141,17 +141,26 @@ class SettingsImportUiModel {
return
}
- val passwordsMissing = settingsList.any { it.importStatus == ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED }
- if (passwordsMissing) {
- showPasswordRequiredText()
- return
+ val passwordsMissing = settingsList.any {
+ it.importStatus == ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED
+ }
+ val authorizationRequired = settingsList.any {
+ it.importStatus == ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED
}
- val partialImportError = settingsList.any { it.importStatus == ImportStatus.IMPORT_FAILURE }
- if (partialImportError) {
- showPartialImportErrorText()
+ if (passwordsMissing && authorizationRequired) {
+ showActionRequiredText(StatusText.IMPORT_SUCCESS_PASSWORD_AND_AUTHORIZATION_REQUIRED)
+ } else if (passwordsMissing) {
+ showActionRequiredText(StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED)
+ } else if (authorizationRequired) {
+ showActionRequiredText(StatusText.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED)
} else {
- showSuccessText()
+ val partialImportError = settingsList.any { it.importStatus == ImportStatus.IMPORT_FAILURE }
+ if (partialImportError) {
+ showPartialImportErrorText()
+ } else {
+ showSuccessText()
+ }
}
}
}
@@ -165,15 +174,13 @@ sealed class SettingsListItem {
class Account(val accountIndex: Int, var displayName: String) : SettingsListItem()
}
-enum class ImportStatus {
- NOT_AVAILABLE,
- NOT_SELECTED,
- IMPORT_SUCCESS,
- IMPORT_SUCCESS_PASSWORD_REQUIRED,
- IMPORT_FAILURE;
-
- val isSuccess: Boolean
- get() = this == IMPORT_SUCCESS || this == IMPORT_SUCCESS_PASSWORD_REQUIRED
+enum class ImportStatus(val isSuccess: Boolean, val isActionRequired: Boolean) {
+ NOT_AVAILABLE(isSuccess = false, isActionRequired = false),
+ NOT_SELECTED(isSuccess = false, isActionRequired = false),
+ IMPORT_SUCCESS(isSuccess = true, isActionRequired = false),
+ IMPORT_SUCCESS_PASSWORD_REQUIRED(isSuccess = true, isActionRequired = true),
+ IMPORT_SUCCESS_AUTHORIZATION_REQUIRED(isSuccess = true, isActionRequired = true),
+ IMPORT_FAILURE(isSuccess = false, isActionRequired = false)
}
enum class ButtonState {
@@ -188,6 +195,8 @@ enum class StatusText {
IMPORTING_PROGRESS,
IMPORT_SUCCESS,
IMPORT_SUCCESS_PASSWORD_REQUIRED,
+ IMPORT_SUCCESS_AUTHORIZATION_REQUIRED,
+ IMPORT_SUCCESS_PASSWORD_AND_AUTHORIZATION_REQUIRED,
IMPORT_READ_FAILURE,
IMPORT_PARTIAL_FAILURE,
IMPORT_FAILURE
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportViewModel.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportViewModel.kt
index e6a456de4cc115e6e184f3439688a7d8ffed865e..35c744d2a457dbbe93dc809aaada8b8c11968b4c 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportViewModel.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportViewModel.kt
@@ -36,6 +36,7 @@ class SettingsImportViewModel(
private var accountsMap: MutableMap = mutableMapOf()
private val accountStateMap: MutableMap = mutableMapOf()
private var contentUri: Uri? = null
+ private var currentlyAuthorizingAccountUuid: String? = null
private val containsGeneralSettings: Boolean
get() = uiModel.settingsList.any { it is SettingsListItem.GeneralSettings }
@@ -69,6 +70,7 @@ class SettingsImportViewModel(
fun initializeFromSavedState(savedInstanceState: Bundle) {
contentUri = savedInstanceState.getParcelable(STATE_CONTENT_URI)
+ currentlyAuthorizingAccountUuid = savedInstanceState.getString(STATE_CURRENTLY_AUTHORIZING_ACCOUNT_UUID)
updateUiModel {
isSettingsListVisible = savedInstanceState.getBoolean(STATE_SETTINGS_LIST_VISIBLE)
@@ -145,6 +147,7 @@ class SettingsImportViewModel(
outState.putBoolean(STATE_LOADING_PROGRESS_VISIBLE, isLoadingProgressVisible)
outState.putBoolean(STATE_IMPORT_PROGRESS_VISIBLE, isImportProgressVisible)
outState.putEnum(STATE_STATUS_TEXT, statusText)
+ outState.putString(STATE_CURRENTLY_AUTHORIZING_ACCOUNT_UUID, currentlyAuthorizingAccountUuid)
if (hasDocumentBeenRead) {
val containsGeneralSettings = this@SettingsImportViewModel.containsGeneralSettings
@@ -200,6 +203,9 @@ class SettingsImportViewModel(
ImportStatus.NOT_AVAILABLE -> updateUiModel {
toggleSettingsListItemSelection(position)
}
+ ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> {
+ startAuthorization(settingsListItem as SettingsListItem.Account)
+ }
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
showPasswordPromptDialog(settingsListItem as SettingsListItem.Account)
}
@@ -222,6 +228,21 @@ class SettingsImportViewModel(
}
}
+ fun onReturnAfterAuthorization() {
+ currentlyAuthorizingAccountUuid?.let { accountUuid ->
+ currentlyAuthorizingAccountUuid = null
+ updateUiModel {
+ val index = getListIndexOfAccount(accountUuid)
+ setSettingsListState(index, ImportStatus.IMPORT_SUCCESS)
+ updateCloseButtonAndImportStatusText()
+ }
+
+ GlobalScope.launch(Dispatchers.IO) {
+ accountActivator.enableAccount(accountUuid)
+ }
+ }
+ }
+
private fun getListIndexOfAccount(accountUuid: String): Int {
return uiModel.settingsList.indexOfFirst {
it is SettingsListItem.Account && accountsMap[it.accountIndex] == accountUuid
@@ -333,7 +354,9 @@ class SettingsImportViewModel(
accountsMap[accountIndex] = accountPair.imported.uuid
listItem.displayName = accountPair.imported.name
- if (accountPair.incomingPasswordNeeded || accountPair.outgoingPasswordNeeded) {
+ if (accountPair.authorizationNeeded) {
+ setSettingsListState(index, ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED)
+ } else if (accountPair.incomingPasswordNeeded || accountPair.outgoingPasswordNeeded) {
accountStateMap[accountIndex] = AccountState(
accountPair.incomingServerName,
accountPair.outgoingServerName,
@@ -364,6 +387,14 @@ class SettingsImportViewModel(
}
}
+ private fun startAuthorization(settingsListItem: SettingsListItem.Account) {
+ val accountIndex = settingsListItem.accountIndex
+ val accountUuid = accountsMap[accountIndex]!!
+ currentlyAuthorizingAccountUuid = accountUuid
+
+ sendActionEvent(Action.StartAuthorization(accountUuid))
+ }
+
private fun showPasswordPromptDialog(settingsListItem: SettingsListItem.Account) {
val accountIndex = settingsListItem.accountIndex
@@ -431,12 +462,14 @@ class SettingsImportViewModel(
private const val STATE_CONTENT_URI = "contentUri"
private const val STATE_GENERAL_SETTINGS_IMPORT_STATUS = "generalSettingsImportStatus"
private const val STATE_ACCOUNT_LIST = "accountList"
+ private const val STATE_CURRENTLY_AUTHORIZING_ACCOUNT_UUID = "currentlyAuthorizingAccountUuid"
}
}
sealed class Action {
class Close(val importSuccess: Boolean) : Action()
object PickDocument : Action()
+ class StartAuthorization(val accountUuid: String) : Action()
class PasswordPrompt(
val accountUuid: String,
val accountName: String,
diff --git a/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_disabled.9.png b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_disabled.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..082201ea687e1ddc8fdf5bd7b549432872046be0
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_disabled.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_focus.9.png b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_focus.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a3c8937a1f8a7c175d6d145ed49977d1fc9f4ef
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_focus.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_normal.9.png b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_normal.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a5ddfb762a13bd5f52b970697e7d26ddfd9b26f
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_normal.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_pressed.9.png b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_pressed.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..82d16422c70ce338ca08e1ff1ead75b3fd519c06
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-hdpi/btn_google_signin_dark_pressed.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-hdpi/ic_apg_small.png b/app/ui/legacy/src/main/res/drawable-hdpi/ic_apg_small.png
deleted file mode 100644
index 29b41d89d2b6c1e4f60460a992a871640c5e8fc8..0000000000000000000000000000000000000000
Binary files a/app/ui/legacy/src/main/res/drawable-hdpi/ic_apg_small.png and /dev/null differ
diff --git a/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_disabled.9.png b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_disabled.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..b88019ed25141eec5e9a1c1fcd3f9c08fc42266f
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_disabled.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_focus.9.png b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_focus.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..5265221ad9a227347b3ab8ae17e4995c6ef10ba0
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_focus.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_normal.9.png b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_normal.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..6b601919ded34b833741aa172e5e343e43991bc9
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_normal.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_pressed.9.png b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_pressed.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..892674099cf6267f937baed8a8d3603153e0362a
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-mdpi/btn_google_signin_dark_pressed.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-mdpi/ic_apg_small.png b/app/ui/legacy/src/main/res/drawable-mdpi/ic_apg_small.png
deleted file mode 100644
index eec5d0ae18e3ec5d655d21f1e4c90ceedb70f34c..0000000000000000000000000000000000000000
Binary files a/app/ui/legacy/src/main/res/drawable-mdpi/ic_apg_small.png and /dev/null differ
diff --git a/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_disabled.9.png b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_disabled.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..5cac64fe76c7098f39f69e3a36a6f0e6a4b4eb21
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_disabled.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_focus.9.png b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_focus.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd52cae7c60677c460aa10e39223bf61aeb4af09
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_focus.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_normal.9.png b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_normal.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..e48e5a278c153964be6ea06268b884951dce5b1c
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_normal.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_pressed.9.png b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_pressed.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1ed3e21646838490e948febb300fcebbc958816
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xhdpi/btn_google_signin_dark_pressed.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xhdpi/ic_apg_small.png b/app/ui/legacy/src/main/res/drawable-xhdpi/ic_apg_small.png
deleted file mode 100644
index 7ca53f0ec6a4648b0468160c970a3445e2f7eb31..0000000000000000000000000000000000000000
Binary files a/app/ui/legacy/src/main/res/drawable-xhdpi/ic_apg_small.png and /dev/null differ
diff --git a/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_disabled.9.png b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_disabled.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..545fb14327619f5927b32732a3d36935a950cf41
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_disabled.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_focus.9.png b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_focus.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..6bc828acb4f1e794d9f290700a51c5495d93e149
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_focus.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_normal.9.png b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_normal.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..3fb4c67209d81d6656330093789f4d95dac5850e
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_normal.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_pressed.9.png b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_pressed.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..c5096f73abefd194f9447bdee55b829de483b97f
Binary files /dev/null and b/app/ui/legacy/src/main/res/drawable-xxhdpi/btn_google_signin_dark_pressed.9.png differ
diff --git a/app/ui/legacy/src/main/res/drawable-xxhdpi/ic_apg_small.png b/app/ui/legacy/src/main/res/drawable-xxhdpi/ic_apg_small.png
deleted file mode 100644
index 68df5fb5806ff996276eb2c7076a8b4d6dfc8e03..0000000000000000000000000000000000000000
Binary files a/app/ui/legacy/src/main/res/drawable-xxhdpi/ic_apg_small.png and /dev/null differ
diff --git a/app/ui/legacy/src/main/res/drawable-xxxhdpi/ic_apg_small.png b/app/ui/legacy/src/main/res/drawable-xxxhdpi/ic_apg_small.png
deleted file mode 100644
index de98c497b386f8724294bbe4f57657d79cf117fd..0000000000000000000000000000000000000000
Binary files a/app/ui/legacy/src/main/res/drawable-xxxhdpi/ic_apg_small.png and /dev/null differ
diff --git a/app/ui/legacy/src/main/res/drawable/btn_google_signin_dark.xml b/app/ui/legacy/src/main/res/drawable/btn_google_signin_dark.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bb0e15af7d57dce1ca8800be45fe44b289130d95
--- /dev/null
+++ b/app/ui/legacy/src/main/res/drawable/btn_google_signin_dark.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/app/ui/legacy/src/main/res/drawable/ic_import_status.xml b/app/ui/legacy/src/main/res/drawable/ic_import_status.xml
index e3d0dd694b5296e3b5d053074ee3a9d4bb8c48d9..6ce5727bdd65cbd36453bb42aec585cc19ac69dc 100644
--- a/app/ui/legacy/src/main/res/drawable/ic_import_status.xml
+++ b/app/ui/legacy/src/main/res/drawable/ic_import_status.xml
@@ -9,11 +9,15 @@
android:maxLevel="1"
android:minLevel="1" />
+
diff --git a/app/ui/legacy/src/main/res/drawable/ic_login.xml b/app/ui/legacy/src/main/res/drawable/ic_login.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8bee0a7de90754a0b0fe2019bec1575ee8f73da3
--- /dev/null
+++ b/app/ui/legacy/src/main/res/drawable/ic_login.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/ui/legacy/src/main/res/layout/account_setup_basics.xml b/app/ui/legacy/src/main/res/layout/account_setup_basics.xml
index f0cf0371a7d02eeef95020e337ef1fa14803d490..f88622f7f4c1e0c99774906e8021d666c0d3fbb0 100644
--- a/app/ui/legacy/src/main/res/layout/account_setup_basics.xml
+++ b/app/ui/legacy/src/main/res/layout/account_setup_basics.xml
@@ -37,7 +37,7 @@
diff --git a/app/ui/legacy/src/main/res/layout/account_setup_incoming.xml b/app/ui/legacy/src/main/res/layout/account_setup_incoming.xml
index 38bb5202d690ef60ba020e1850f9d1f722eb925c..3807ce8c82e96f1c51ea78f3430233503ecdae1d 100644
--- a/app/ui/legacy/src/main/res/layout/account_setup_incoming.xml
+++ b/app/ui/legacy/src/main/res/layout/account_setup_incoming.xml
@@ -247,43 +247,11 @@
-
-
-
-
-
-
-
-
-
-
+ android:layout_width="wrap_content"
+ android:text="@string/account_setup_incoming_use_compression" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/ui/legacy/src/main/res/layout/dialog_apg_deprecated.xml b/app/ui/legacy/src/main/res/layout/dialog_apg_deprecated.xml
deleted file mode 100644
index 4a3769372271e7effa49be1ed73f93b878ed86e6..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/res/layout/dialog_apg_deprecated.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/ui/legacy/src/main/res/menu/message_list_option.xml b/app/ui/legacy/src/main/res/menu/message_list_option.xml
index d2cecce81b441fb3111f08bc914f898ea672a8b9..0049b3750304f432e59ec0bdc2b37cdb590dbc26 100644
--- a/app/ui/legacy/src/main/res/menu/message_list_option.xml
+++ b/app/ui/legacy/src/main/res/menu/message_list_option.xml
@@ -147,6 +147,10 @@
+
+
diff --git a/app/ui/legacy/src/main/res/raw/changelog_master.xml b/app/ui/legacy/src/main/res/raw/changelog_master.xml
index abb0760cb922f93f81b93c25fa3856976e1631ca..78f4dab513aad79995e504b84d7bf122e9982165 100644
--- a/app/ui/legacy/src/main/res/raw/changelog_master.xml
+++ b/app/ui/legacy/src/main/res/raw/changelog_master.xml
@@ -5,6 +5,15 @@
Locale-specific versions are kept in res/raw-/changelog.xml.
-->
+
+ Added support for using OAuth 2.0 with Google accounts
+ Added support for using OAuth 2.0 with Yahoo and AOL accounts
+ Added "Unsubscribe" action that is displayed in the menu when viewing a message that contains an Unsubscribe header
+ Fixed a bug where unrelated notifications where cleared after synchronizing a folder containing no unread messages
+ Fixed rare problem with decoding format=flowed messages
+ Various other small bug fixes and improvements
+ Updated translations
+ Added support for setting the notification vibration pattern on Android 8+Added support for setting a custom notification light color on Android 8+
diff --git a/app/ui/legacy/src/main/res/values-ar/strings.xml b/app/ui/legacy/src/main/res/values-ar/strings.xml
index f86d48b8d8a08726dc06eac6128ece89758087a8..d342aa818ad55bb1e497370fc7c99ff3293340de 100644
--- a/app/ui/legacy/src/main/res/values-ar/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ar/strings.xml
@@ -8,7 +8,6 @@
Mail Unreadفريق The K-9 Dog Walkers
- حقوق النشر محفوظة 2008 - %s The K-9 Dog Walkers. جزء من حقوق النشر محفوظة 2006 - %s the Android Open Source Project.Apache License, Version 2.0المكتباتالرخصة
@@ -21,14 +20,12 @@
The account \"%s\" will be removed from Mail.الناشرين
- معلومات المراجعةالرسائل المقروءةالسماح لهذا التطبيق بقراءة البريد الإلكترونيحذف الرسائلالسماح لهذا التطبيق بحذف رسائل البريد الإلكترونيAbout Mailالحسابات
- المجلداتمتقدمةأنشِئرُد
@@ -37,12 +34,6 @@
إعادة توجيه كمُرفَقاتإختر حسابإختر مجلد
- تحديث %s:%s%s
- سحب الترويسات %s:%s%s
- إرسال %s%s
- معالجة %s:%s%s
- \u0020 %d\%d
- المُزامنه معطلة%d محددالتاليالسابق
@@ -50,10 +41,7 @@
حسنًاإلغأرسل
- أرسل مجددًاموضوع الرسالة فارغ، أنقر مرة أخرى لمواصلة الإرسال
- حدد
- إلغ التحديدرُدرُد على الكلاحذف
@@ -121,8 +109,6 @@
%.1f ميجا%.1f كيلو%d بايت
- تم تقليص الحساب \"%s من %s الى %s
- يتم ضغط \"%s\"رسالة جديدة%d ﻻ رسائل
@@ -190,20 +176,14 @@
إلى :نسخة كربونية :نسخة مخفية:
- افتح
- احفظغير قادر على حفظ المُرفَق.اظهر الصوريتعذر إيجاب تطبيق لعرض %s.تنزيل الرسالة كاملةً
- ﻻ يُمكن عرض الرسالةعبر %1$s
- تم تنزيل جميع الترويسات ولكن لا يوجد ترويسات إضافية لعرضها.
- فشل استرداد الترويسات الإضافية من قاعدة بيانات أو من الخادم.المزيد من هذا المُرسِلمِن طرف %s
- تشخيص \ حذف محتويات الرسالةتم إلغاء الرسالةتم الإحتفاظ بالرسالة كمسودةإظهار النجوم
@@ -231,10 +211,6 @@
إخفاء عميل البريداحذف مُعرف بريد كية-9 من ترويسات الرسالةاحذف التوقيت
- إخفاء العنوان في الإشعارات
- أبدًا
- عندما يكون الجهاز مغلقًا
- دائمًاإظهار زر ’الحذف‘أبدًاللإشعارات ذات الرسالة الواحدة
@@ -253,7 +229,9 @@
إعداد حساب جديدعنوان البريد الإليكترونيكلمة السِّر
- اظهر كلمة السِّر
+
+
+
الإعداد يدوياالمصادقة \u2026
@@ -286,12 +264,6 @@
ﻻ تحذفها على الخادماحذفها على الخادمإعتبارها كمقروءة على السيرفر
- إستخدام الضغط على شبكة :
- الجوال
- الواي فاي
- أخرى
- التخزين الخارجي (بطاقة الذاكرة)
- ذاكرة التخزين العادي الداخليةإزالة الرسائل المحذوفة من الخادوم أيضافورًاعند الإستجواب
@@ -304,11 +276,8 @@
مجلد البريد المزعجتوسيع المجلد تلقائيامسار OWA
- اختياريمسار المصادقة
- اختياريعلبة بريد التحويل
- اختياريإعدادات الخادم الخارجخادم SMTPالمَنفذ
@@ -318,7 +287,6 @@
كلمة السِّرالمصادقةخيارات الحساب
- تضغيط الحسابأبدًاكل 15 دقيقةكل 30 دقيقة
@@ -355,8 +323,6 @@
مواصلةمتقدمةإعدادات الحساب
- الحساب الإفتراضي
- إبعث الرسائل من هذا الحساب افتراضياإشعارات البريد الجديدمجلد الإخطاراتالكل
@@ -373,7 +339,6 @@
إظهار الإشعارات فقط عند تلقي رسائل مِن طرف مراسلين معروفينتحديد الرسالة كمقروءة بعد فتحهاتحديد الرسالة كمقروءة بعد فتحها الإطلاع عليها
- إعدادات الإشعاردائمًا اظهر الصورلامن المُتراسِلين
@@ -405,8 +370,6 @@
متصل بـ %sالضبط جارٍ...لون الحساب
- مِن دون لون
- لون ضوء التنبيهاتحجم المجلد المحلي1 KiB2 KiB
@@ -482,7 +445,6 @@
اسمكالتنبيهاتالاهتزاز
- الهتزاز عند وصول البريدنمط الإهتزازالإفتراضيالنمط ١
@@ -613,7 +575,6 @@
العنوانالوقت والتاريخنصّ الرسالة
- %d%%إنشاء الرسالةحقول إدخال النصالإفتراضي
@@ -655,6 +616,10 @@
فشل تصدير الإعداداتاستيراد الإعداداتاستيراد
+
+
+
+
لاحقًااستيراد الإعداداتاستيراد الإعدادات…
@@ -752,16 +717,9 @@
إعادة المحاولةخطأ أنثاء عملية فك تعمية الرسالة الإلكترونيةنص غير مُوَقَّع
- لذلك فإن دعم برنامج APG قد حُذف من بريد كيه-٩.
- توقف تطويره في بداية ٢٠١٤
- تحتوي على مشكلة أمنية غير محلولة.
- لمعرفة المزيد يمكنك الضغط هنا.
- حسنًا !
- APGهذه الرسالة مُعماةقائمة رسائل K-9يُحمل الرسائل…
- فشلت عملية جلب قائمة المجلداتالتعمية غير ممكنةتنشيط التعميةتعطيل التعمية
@@ -777,8 +735,6 @@
إرسال رسالة إلى:مُفعّلمُوقّف
- افتح
- اغلقلمعرفة المزيد
diff --git a/app/ui/legacy/src/main/res/values-be/strings.xml b/app/ui/legacy/src/main/res/values-be/strings.xml
index dcfc088ef42d00e767d25b46a08e624ec8da45fe..d765f438418c85cd0f97cdfc6a3b4b76d87c874b 100644
--- a/app/ui/legacy/src/main/res/values-be/strings.xml
+++ b/app/ui/legacy/src/main/res/values-be/strings.xml
@@ -240,6 +240,8 @@
Новы акаўнтАдрас электроннай поштыПароль
+
+
Уласнаручна
@@ -278,10 +280,6 @@
Пакінуць на серверыВыдаліць з сервераПазначыць на серверы як прачытанае
- Выкарыстоўваць сцісканне ў сетцы:
- Мабільныя
- Wi-Fi
- ІншыяСціранне выдаленых лістоў з сервераНеадкладнаПадчас праверкі
@@ -702,12 +700,16 @@
Абраць файлІмпартНалады паспяхова імпартаваныя
+
Калі ласка, увядзіце пароль
+
+
Не атрымалася імпартаваць наладыНе атрымалася прачытаць файл наладНе атрымалася імпартаваць некаторыя наладыПаспяхова імпартаванаПатрабуецца пароль
+
Не імпартаванаНе атрымалася імпартавацьПазней
@@ -903,14 +905,6 @@
Паказваюцца ўсе подпісыШыфраванне недаступна ў рэжыме \"Толькі подпіс\"!Непадпісаны тэкст
- Спыненне падтрымкі APG
- APG больш не распрацоўваецца!
- Праз гэта K-9 Mail больш не падтрымлівае APG.
- Распрацоўка скончаная ў пачатку 2014
- Ёсць нявырашаныя праблемы бяспекі
- Націсніце сюды, каб даведацца больш.
- Зразумела!
- APGЛіст зашыфраваныЛіст зашыфраваны OpenPGP\nКаб прачытаць, неабходна ўсталяваць і наладзіць праграму OpenPGP.Перайсці ў налады
diff --git a/app/ui/legacy/src/main/res/values-bg/strings.xml b/app/ui/legacy/src/main/res/values-bg/strings.xml
index 0ccfe1621d5abdc934658489cc51ee055e245c65..9d41fce2508ee37e58953ca201d111bc80c9eaf1 100644
--- a/app/ui/legacy/src/main/res/values-bg/strings.xml
+++ b/app/ui/legacy/src/main/res/values-bg/strings.xml
@@ -245,6 +245,8 @@
Настрой нов профилПощенски адресПарола
+
+
Ръчни настройки
@@ -283,10 +285,6 @@
Не изтривай от сървъраИзтривай от сървъраМаркирай като прочетено на сървъра
- Използвай компресия по мрежата:
- Мобилна
- Wi-Fi
- ДругаПремахни изтритите от сървъра съобщенияВеднагаПри проверка
@@ -694,12 +692,16 @@
Избери файлВнасянеУспешно импортирани настройки
+
Моля, въведете пароли
+
+
Импортирането на настройките не бе успешноФайлът за настройки не бе прочетенИмпортирането на някои настройки не бе успешноУспешно импортиранеИзисква се парола
+
Не се импортираНеуспешно импортиранеПо късно
@@ -889,14 +891,6 @@
Всички подписи ще бъдат показваниКриптирането не е възможно, когато се използва режима само подпис! Неподписан текст
- APG предупреждение за премахване
- APG вече не се поддържа!
- Поради това, поддръжката на APG е премахната от К-9 Mail.
- Разработването бе преустановено в началото на 2014
- Съдържа неоправени проблеми по сигурността
- Може да натиснете тук, за да научите повече.
- Ясно!
- APGИмейлът е криптиранТози имейл е криптиран използвайки OpenPGP.\nЗа да го прочетете, трябва да инсталирате и настроите OpenPGP приложението.Към настройки
diff --git a/app/ui/legacy/src/main/res/values-br/strings.xml b/app/ui/legacy/src/main/res/values-br/strings.xml
index 8c26a1b593e56dac313da5a0bedfe44282b55f40..d49f47bd14ec568dc056f02816637a181110e7c4 100644
--- a/app/ui/legacy/src/main/res/values-br/strings.xml
+++ b/app/ui/legacy/src/main/res/values-br/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- Diorroerien K-9
- Copyright 2008-%s Ar valeerien K-9 Dog. Lodennoù Copyright 2006-%s and Android Open Source Project.
+ Diorroerien MailLañvaz Apache, Handelv 2.0LevraouegoùLañvaz
@@ -16,19 +15,17 @@
Donemat war MailMail is the default mail client for /e/
- -- \nKaset eus ma fellgomzer Android gant K-9 Mail.
+ -- \nKaset eus ma fellgomzer Android gant Mail.
- Dilamet e vo ar gont \"%s\" eus K-9 Mail.
+ Dilamet e vo ar gont \"%s\" eus Mail.Aozerien
- Titouroù an azgweladurLenn ar posteloùAotren an arload-mañ da lenn ho posteloù.Dilemel posteloùAotren an arload-mañ da lenn ho posteloù.About MailKontoù
- TeuliadoùKemplezhoc’hSkridaozañRespont
@@ -37,12 +34,6 @@
Treuzkas evel stagadennDibab ar gontDibab an teuliad
- Kerc’hat %s:%s%s
- O kerc’hat talbennoù %s:%s%s
- O kas %s%s
- Keweriañ %s:%s%s
- \u0020%d/%d
- Goubredañ diweredekaet%d diuzetKenderc’helKent
@@ -50,10 +41,7 @@
OKNullañKas
- AdkasGoullo eo an danvez, klikit en-dro evit kas memestra
- Diuzañ
- DiziuzañRespontRespont d’an hollDilemel
@@ -113,8 +101,6 @@
Fazi kargañ ar gemennadennKargañ
%d ouzhpenn
- Digresket eo ar gont \"%s\" eus %s da %s
- O koazhañ ar gont \"%s\"Postel nevez%d gemennadenn nevez
@@ -182,20 +168,14 @@
Da:Cc:Bcc:
- Digeriñ
- EnrollañN\'haller ket enrollañ ar stagadenn.Diskouez ar skeudennoùN’haller ket kavout ur gwelerez evit %s.Pellgargañ ar gemennadenn glok
- N’haller ket skrammañ ar gemennadenndre %1$s
- An holl dalbennoù a zo bet pellgarget, met n’eus talbenn ouzhpenn ebet da ziskouez.
- C’hwitadenn en ur adkavout talbennoù ouzhpenn eus ar stlennvon pe an dafariad postel.Muioc’h a-berzh ar c’haser-mañA-berzh %s
- Diveugañ / Skarzhañ korf ar gemennadennKemennadenn dilezetKemennadenn enrollet evel brouilhedDiskouez ar steredennoù
@@ -229,10 +209,6 @@
Remove Mail User-Agent from mail headersKuzhat ar gwerzhid-eurArverañ UTC e plas ar gwerzhid-eur lec’hel e talbennoù ar postel hag ar respont
- Kuzhat an danvez er rebuziñ
- Morse
- P’eo prennet an trevnad
- AtavDiskouez an afell ’Dilemel’MorseEvit ar rebuzadurioù gant ur gemennadenn
@@ -252,7 +228,9 @@
Ouzhpennañ ur gontChomlec’h postelGer-tremen
- Diskouez ar ger-tremen
+
+
+
Arventennañ dre zornO kerc’hat titouroù ar gont\u2026
@@ -290,12 +268,6 @@
Na zilemel war an dafariadDilemel war an dafariadMerkañ evel lennet war an dafariad
- Arverañ ar c’hoazhañ war ar rouedad:
- Hezoug
- Wi-Fi
- All
- Kadaviñ diavaez (Kartenn SD)
- Kadaviñ diabarzh ordinalDilemel ar c’hemennadennoù diverket war an dafariadDiouzhtuEn ur gerc’hat
@@ -310,11 +282,8 @@
Diskouez an teuliadoù koumanantet hepkenKreskaat an teuliad ent emgefreekTreug OWA
- DiretTreug dilesa
- DiretLesanv ar voest postel
- DiretArventennoù an dafariad ec’hankañDafariad SMTPPorzh
@@ -326,7 +295,6 @@
%1$s = %2$s\" n’eo ket talvoudek gant \"%3$s = %4$sArventennoù didalvoudek: %sDibarzhioù ar gont
- Kont koazhetAliested kerc’hat ar posteloùMorseBep 15 munutenn
@@ -337,8 +305,6 @@
Bep 6 eurBep 12 eurBep 24 eur
- Gweredekaat ar push postel evit ar gont-mañ
- Mard eo skoret gant ho tafariad, kemennadennoù nevez a vo skrammet diouzhtu. An dibarzh-mañ a c’hall gwellaat pe fallaat an digonusted a-galz.Azgrenaat ar c’hennask IDLEBep 2 vunutennBep 3 munutenn
@@ -349,7 +315,6 @@
Bep 48 munutennBep 60 munutennMa rebuziñ pa zegouezh ar posteloù
- Ma rebuziñ p’emañ o kerc’hat ar posteloùNiver a gemennadennoù da ziskouez10 kemennadenn25 kemennadenn
@@ -371,8 +336,6 @@
Kenderc’helKemplezhoc’hArventennoù ar gont
- Kont dre ziouer
- Kas posteloù gant ar gont-mañ dre ziouerRebuzadurioù posteloù nevezTeuliadoù rebuzadurioùPep tra
@@ -390,8 +353,6 @@
Diskouez rebuzadurioù evit kemennadennoù darempredoù a anavezan hepkenMerkañ evel lennet pa vez digoretMerkañ ar gemennadenn evel lennet pa vez digoret anezho d’he gwelet
- Arventennoù rebuziñ
- Digeriñ arventennoù ar rebuzadurioù reizhiadDiskouez ar skeudennoù bepredKetDigant darempredoù
@@ -433,9 +394,6 @@
Aliested kerc’hat ar posteloùLiv ar gontLiv ar gont arveret er roll teuliadoù ha kontoù
- Liv ebet
- Liv al LED rebuziñ
- Liv diskouezet gant LED ho trevnad evit ar gont-mañ Ment an teuliad lec’helKerc’hat ar c’hemennadennoù bihannoc’h eget:ne vern ar ment (bevenn ebet)
@@ -507,7 +465,6 @@
AnvRebuzadurioùFroumal
- Froumal pa zegouezh ur postelDoareoù froumaldre ziouerdoare 1
@@ -625,10 +582,6 @@
Merdeiñ dre afelloù an ampledEr gwel kemennadennEr gwel roll
- %s%s
- - Anlennet
- An holl gemennadennoù
- Kemennadennoù an teuliadoù klaskusBoest degemer unanetHoll gemennadennoù an teuliadoù unanetUnanañ
@@ -661,8 +614,6 @@
DanvezEur ha deiziadKorf ar gemennadenn
- %d%%
- %1$s: %2$sSkridaozadur ar gemennadennMaeziennoù enankañ testennDre ziouer
@@ -718,6 +669,10 @@
C’hwitadenn war ezporzhiañ an arventennoùEnporzhiañ an arventennoùEnporzhiañ
+
+
+
+
Enporzhiañ an arventennoùOc’h enporzhiañ an arventennoù...Diskouez an niver a bosteloù anlennet evit...
@@ -898,21 +853,11 @@
An holl sinadurioù a vo skrammetN’haller ket enrinegañ er mod Sign-Only!Testenn ansinet
- Gouzav diamzeradur APG
- N’eo ket trezalc’het APG ken!
- Dre se n’eo ket skoret APG gant K-9 Mail.
- Harzhet eo bet an diorren e deroù 2014
- Enderc’hel a ra kudennoù diogelroez
- Gallout a rit klikañ amañevit gouzout hiroc’h.
- Komprenet ’m eus!
- APGN’eo ket enrineget ar postel-mañEnrineget eo bet ar postel-mañ gant OpenPGP.\nEvit gallout e lenn e rankit staliañ ha kefluniañ un arload OpenPGP keverlec’h.Mont d\'an arventennoù
- Roll kemennadennoù K-9
+ Roll kemennadennoù MailO kargañ kemennadennoù...
- C’hwitadenn en ur gerc’hat roll an teuliadoù
- Fazi en ur adkavout statud an degemerer digant ar pourchaser OpenPGP!N’eo ket posupl enrinegañUl lodenn eus an degemererien ne skoront ket ar c’heweriuster-mañ!Gweredekaat an enrinegañ
@@ -932,7 +877,7 @@
Arventennoù hollekArload OpenPGP staliet ebetStaliañ
- K-9 Mail a azgoulenn OpenKeychain evit an enrinegañ penn-ouzh-penn.
+ Mail a azgoulenn OpenKeychain evit an enrinegañ penn-ouzh-penn.Kemennadenn enrinegetEnrinegañ danvez ar c\'hemennadennoùGallout a ra bezañ anskor evit degemererien \'zo
@@ -957,8 +902,6 @@ Gallout a rit mirout ar gemennadenn-mañ hag implij anezhi evel un enrolladenn e
Degouezhet ez eus bet ur fazi en ur gas ar gemennadenn. Gwiriekait ho kennask d\'ar rouedad hag ho kefluniadur evit an dafariad ec\'hankat.GwereddekaatDiweredekaat
- Digeriñ
- SerriñAotren da haeziñ an darempredoùEvit bezañ gouest da ginnig darempredoù hag evit skrammañ anvioù ha skeudennoù an darempredoù en deus ezhomm an arload da haeziñ ho tarempredoù.
diff --git a/app/ui/legacy/src/main/res/values-ca/strings.xml b/app/ui/legacy/src/main/res/values-ca/strings.xml
index e71152720d2f6625befba005b6412e16f3a1875f..e66bfe37c8f149f1cc6a1ec90590a844185d0782 100644
--- a/app/ui/legacy/src/main/res/values-ca/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ca/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- Els passejadors de gossos K-9
- Copyright 2008-%s The K-9 Dog Walkers. Porcions de Copyright 2006-%s Projecte de codi obert d\'Android.
+ Els passejadors de gossos MailCodi fontLlicència d\'Apache, versió 2.0Projecte de codi obert
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.Autors
- Informació de la revisióLlegeix els correusPermet a aquesta aplicació llegir els correus.Elimina correusPermet que aquesta aplicació elimini correus.
- Quant al K-9 Mail
+ Quant al MailComptes
- CarpetesAvançatRedactaRespon
@@ -49,12 +46,6 @@
Reenvia com a adjuntTrieu un compteTria carpeta
- Comprova %s:%s%s
- Recuperant capçaleres %s:%s%s
- Enviant %s%s
- Proc %s:%s%s
- \u0020%d/%d
- Sincronització desactivada%d seleccionatsSegüentAnterior
@@ -62,10 +53,7 @@
D\'acordCancel·laEnvia
- Torna a enviarL\'assumpte és buit, feu clic un altre cop per enviar-lo així
- Selecciona
- DesseleccionaResponRespon a tothomElimina
@@ -101,6 +89,7 @@
Afegeix un estelElimina l\'estelCopia
+ No t\'hi subscriguisMostra les capçaleresAdreça copiada al porta-retalls
@@ -202,20 +191,14 @@
A:A/c:Bcc:
- Obre
- DesaNo s\'ha pogut desar l\'adjunt.Mostra les imatgesNo s\'ha pogut trobar un visualitzador per %s.Baixa el missatge complet
- No s\'ha pogut mostrar el missatgemitjançant %1$s
- S\'han baixat totes les capçaleres. No hi ha capçaleres addicionals per mostrar.
- La recuperació de capçaleres addicionals de la base de dades o del servidor de correu ha fallat.Més d\'aquest remitentDe %s
- Depura / neteja el cos del missatgeS\'ha descartat el missatgeS\'ha desat el missatge com a esborranyMostra els estels
@@ -247,13 +230,9 @@
Marca tots els missatges com a llegitsElimina (des de notificacions)Amaga el client de correu
- Suprimeix el nom K-9 User-Agent de les capçaleres del missatge
+ Suprimeix el nom Mail User-Agent de les capçaleres del missatgeAmaga el fus horariFes servir UTC en comptes de l\'hora local a les capçaleres dels missatges i a la capçalera de resposta
- Amaga l\'assumpte a les notificacions
- Mai
- Quan el telèfon està bloquejat
- SempreMostra el botó \"Elimina\"MaiPer a notificacions d\'un sol missatge
@@ -273,7 +252,15 @@
Configura un compte nouAdreça de correu electrònicContrasenya
- Mostra la contrasenya
+ Per usar aquest compte de correu electrònic amb el Mail, heu d\'iniciar-hi la sessió i concedir a l\'aplicació accés als vostres correus electrònics.
+
+ Inicia la sessió
+
+ Inicia la sessió amb Google
+
+ Per veure la contrasenya aquí, activeu el bloqueig de pantalla en aquest dispositiu.
+ Verifiqueu la identitat
+ Desbloqueu-ho per veure la contrasenya.Configuració manualRecuperant la informació del compte\u2026
@@ -293,6 +280,7 @@
Contrasenya, transmesa de forma no seguraContrasenya encriptadaCertificat del client
+ OAuth 2.0Configuració del servidor d\'entradaNom d\'usuariContrasenya
@@ -311,12 +299,7 @@
No l\'eliminis del servidorElimina\'l del servidorMarca\'ls com a llegits al servidor
- Utilitza compressió a la xarxa:
- Mòbil
- Wi-Fi
- Altres
- Emmagatzematge extern (targeta SD)
- Emmagatzematge intern
+ Usa compressióElimina missatges esborrats al servidorImmediatamentQuan es comprova
@@ -331,11 +314,8 @@
Només mostra carpetes subscritesAutoexpandeix la carpetaCamí OWA
- OpcionalCamí d\'autenticació
- OpcionalÀlies de la bústia
- OpcionalConfiguració del servidor de sortidaServidor SMTPPort
@@ -347,7 +327,6 @@
\"%1$s = %2$s\" no és vàlid amb \"%3$s = %4$s\"Configuració no vàlida: %sOpcions del compte
- Compacta el compteFreqüència de comprovacióMaiCada 15 minuts
@@ -358,8 +337,6 @@
Cada 6 horesCada 12 horesCada 24 hores
- Habilita la tramesa de correu per a aquest compte
- Si el vostre servidor ho admet, els missatges arribaran automàticament. Aquesta opció pot millorar o empitjorar el rendiment.Refresca la connexió IDLECada 2 minutsCada 3 minuts
@@ -370,7 +347,6 @@
Cada 48 minutsCada 60 minutsAvisa\'m quan arribi correu
- Avisa\'m mentre es comprova el correuNombre de missatges a mostrar10 missatges25 missatges
@@ -388,12 +364,14 @@
Nom d\'usuari o contrasenya incorrectes.\n(%s)El servidor ha mostrat un certificat SSL no vàlid. A vegades això és degut a una mala configuració del servidor. També pot ser degut que algú estigui intentant atacar-vos o al vostre servidor. Si no n\'esteu segurs feu clic a Rebutja i contacteu amb la gent que us administra el vostre servidor.\n\n(%s)No s\'ha pogut connectar amb el servidor.\n(%s)
+ Autorització cancel·lada
+ L\'autorització ha fallat amb l\'error següent: %s
+ Actualment, OAuth 2.0 no és compatible amb aquest proveïdor.
+ L\'aplicació no ha trobat cap navegador per utilitzar-lo per concedir accés al compte.Edita els detallsContinuaAvançatConfiguració del compte
- Compte per defecte
- Envia el correu per defecte des d\'aquest compte.Notificacions noves de correuCarpetes de notificacionsTot
@@ -415,8 +393,6 @@
Marca el missatge com a llegit després d\'haver-lo obert.Marca com a llegit quan se suprimeixiMarca un missatge com a llegit quan se suprimeix.
- Configuració de les notificacions
- Obre la configuració de les notificacions del sistemaSempre mostra les imatgesNoDels contactes
@@ -458,9 +434,6 @@
Freqüència de comprovació de les carpetesColor del compteTria el color del compte utilitzat a la llista de carpetes i de comptes
- Sense color
- Color del LED de notificació
- El color en què ha de fer pampallugues el LED del telèfon per a aquest compteMida de la carpeta localRecupera missatges fins a...1 KiB
@@ -679,10 +652,6 @@
Vista del missatgeA la vista de llistesMostra la safata d\'entrada unificada
- %s%s
- - No llegit
- Tots els missatges
- Tots els missatges en carpetes cercablesBústia unificadaTots els missatges en carpetes unificadesUnifica
@@ -715,8 +684,6 @@
AssumpteData i horaCos del missatge
- %d%%
- %1$s: %2$sRedacció de missatgesCamps d\'entrada de textPer defecte
@@ -774,12 +741,16 @@
Seleccioneu un fitxerImportaLa configuració s\'ha importat correctament.
+
Si us plau, escriviu les contrasenyes.
+
+
Ha fallat importar la configuració.Ha fallat llegir els fitxer de configuració.Ha fallat importar alguns paràmetres.Importació correctaCal la contrasenya.
+
No importatFallada d\'importacióMés tard
@@ -970,21 +941,11 @@
Es mostraran totes les signaturesEncriptació no disponible en mode de només signar!Text no signat
- Avís d\'obsolescència d\'APG
- APG ja no es manté!
- A causa d\'això, el suport d\'APG s\'ha eliminat del K-9 Mail.
- El desenvolupament es va aturar a principis de 2014.
- Conté problemes de seguretat no corregits.
- Podeu clicar aquí per saber-ne més.
- Ja ho tinc!
- APGAquest missatge està encriptat.Aquest missatge ha estat encriptat amb OpenPGP.\nPer llegir-lo, us caldrà instal·lar i configurar una aplicació d\'OpenPGP compatible.Ves a la configuracióLlista de missatges del K-9Carregant missatges...
- Ha fallat recuperar la llista de carpetes.
- Error en recuperar l\'estat del destinatari del proveïdor d\'OpenPGP!Encriptació no possibleAlguns dels destinataris seleccionats no admeten aquesta característica!Habilita l\'encriptació
@@ -1004,7 +965,7 @@
Configuració generalNo hi ha cap aplicació d\'OpenPGP instal·lada.Instal·la
- El K-9 Mail requereix OpenKeychain per a l\'encriptació d\'extrem a extrem.
+ El Mail requereix OpenKeychain per a l\'encriptació d\'extrem a extrem.Missatge encriptatEncripta l\'assumpte dels missatges.Pot no ser admès per alguns destinataris.
@@ -1030,8 +991,6 @@ Podeu guardar aquest missatge i usar-lo com a còpia de seguretat per a la vostr
Hi ha hagut un error mentre s\'enviava el missatge. Si us plau, comproveu la connexió de xarxa i la configuració del servidor de sortida.ActiuInactiu
- Obre
- TancaPermet l\'accés als contactesPer poder proporcionar suggeriments de contactes i mostrar-ne els noms i fotografies, l\'aplicació necessita accés als vostres contactes.
@@ -1042,7 +1001,7 @@ Podeu guardar aquest missatge i usar-lo com a còpia de seguretat per a la vostr
Inactiu fins que la xarxa estigui disponibleToca per obtenir més informació.Informació de Tramesa
- Quan es fa servir Tramesa, K-9 Mail manté una connexió amb el servidor de correu. L\'Android requereix mostrar una notificació en curs mentre l’aplicació estigui activa en segon pla. %s
+ Quan es fa servir Tramesa, Mail manté una connexió amb el servidor de correu. L\'Android requereix mostrar una notificació en curs mentre l’aplicació estigui activa en segon pla. %sTot i això, l\'Android també us permet ocultar la notificació.Apreneu-ne mésConfigureu la notificació
diff --git a/app/ui/legacy/src/main/res/values-cs/strings.xml b/app/ui/legacy/src/main/res/values-cs/strings.xml
index 66308a230006a8c1e77517e6b57d705cea3a8e97..5c909e9aff78d680cad771a6358b3b3100093786 100644
--- a/app/ui/legacy/src/main/res/values-cs/strings.xml
+++ b/app/ui/legacy/src/main/res/values-cs/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- Páníčci robopsů K-9
+ Páníčci robopsů MailZdrojový kódApache License, verze 2.0Projekt s otevřeným zdrojovým kódem
@@ -91,6 +91,7 @@
Přidat \u2605Odebrat \u2606Kopírovat
+ Zrušit přihlášení se k odběruZobraz hlavičkuAdresa zkopírována do schránky
@@ -256,6 +257,11 @@
Založit nový účetEmailová adresaHeslo
+ Pokud chcete tento e-mailový účet používat v Mail, je třeba se přihlásit a udělit přístup k e-mailům.
+
+ Přihlásit se
+
+ Přihlási se prostřednictvím GooglePokud si zde chcete zobrazit své heslo, zapněte zamykání obrazovky tohoto zařízení.Ověřte svou identitu
@@ -279,6 +285,7 @@
Heslo přenášeno bez zabezpečeníZašifrované hesloKlientský certifikát
+ OAuth 2.0Nastavení serveru příchozí poštyUživatelské jménoHeslo
@@ -297,10 +304,7 @@
Nemazat ji na serveruSmazat na serveruOznačit jako přečtenou na serveru
- Používat v síti kompresi:
- Mobilní
- Wi-Fi
- Jiná
+ KomprimovatOdstranit smazané zprávyIhnedPři dotazování
@@ -365,6 +369,10 @@
Uživatelské jméno nebo heslo není správné.\n(%s)Server nabízí neplatný SSL certifikát. Někdy může jít o chybu nastavení serveru, někdy o pokus útoku na vás nebo e-mailový server. Pokud nevíte o co jde, ťukněte na odmítnout a obraťte se na správce dotyčného e-mailového serveru.\n\n(%s)Nedaří se připojit k serveru. (%s)
+ Pověření zrušeno
+ Pověření se nezdařilo s následující chybou: %s
+ OAuth 2.0 v tuto chvíli pro tohoto poskytovatele není podporováno.
+ Aplikaci se nedaří najít webový prohlížeč, který použít pro udělení přístupu k vašemu účtu.Upravit podrobnostiPokračovatRozšířené
@@ -746,12 +754,16 @@
Vybrat souborImportNastavení úspěšně importována
+
Zadejte hesla
+
+
Nepodařilo se importovat nastaveníNepodařilo se číst soubor s nastavenímiNepodařilo se naimportovat některá nastaveníÚspěšně naimportovánoVyžadováno hesl
+
NenaimportovánoNezdar při importuPozději
@@ -948,14 +960,6 @@
Budou zobrazeny všechny podpisyŠifrování nedostupné v podpisovém módu!Nepodepsaný text
- Varování o ukončení podpory APG
- APG už není udržováno!
- Kvůli tomuto jsme odstranili podporu pro APG z K-9 Mailu.
- Vývoj skončil na začátku roku 2014
- Obsahuje neopravené bezpečnostní chyby
- Kliknutím sem se můžete dozvědět více.
- Rozumím!
- APGTento e-mail je šifrovanýTento email byl zašifrovaný pomocí OpenPGP.\nAbyste si ho mohl/a přečíst, potřebujete si nainstalovat a nakonfigurovat kompatibilní OpenPGP aplikaci.Přejít do nastavení
@@ -1015,7 +1019,7 @@ Tuto zprávu si můžete ponechat a použít jí jako zálohu svého tajného kl
Uspáno do doby, než bude k dispozici síťKlepněte, pokud se chcete dozvědět víc.Push informace
- Při používání Push, K-9 Mail udržuje spojení s e-mailový serverem. Systém Android vyžaduje zobrazování průběžného upozornění, zatímco je aplikace aktivní na pozadí. %s
+ Při používání Push, Mail udržuje spojení s e-mailový serverem. Systém Android vyžaduje zobrazování průběžného upozornění, zatímco je aplikace aktivní na pozadí. %sNicméně, Android vám také umožňuje toto upozornění skrýt.Zjistit víceNastavit upozorňování
diff --git a/app/ui/legacy/src/main/res/values-cy/strings.xml b/app/ui/legacy/src/main/res/values-cy/strings.xml
index 550598f13200c3167b19892d87e30792aed8a635..8baf66e21fcda4dae46ca4abe931b1eeb4e7bccc 100644
--- a/app/ui/legacy/src/main/res/values-cy/strings.xml
+++ b/app/ui/legacy/src/main/res/values-cy/strings.xml
@@ -7,14 +7,21 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
+ The Mail Dog WalkersCod ffynhonnellTrwydded Apache, Fersiwn 2.0Prosiect Cod AgoredGwefanFforwm defnyddiwyr
+ TwitterLlyfrgelloeddTrwydded
+ Log newidiadau
+ Methwyd llwytho\'r log newidiadau.
+ Fersiwn %s
+ Beth sy\'n newydd
+ Dangos newidiadau diweddar pan ddiweddarwyd yr ap
+ Darganfod beth sy\'n newydd yn y diweddariad hwnWelcome to MailMail is the default mail client for /e/
@@ -38,6 +45,8 @@
Anfon ymlaen fel atodiadDewis cyfrifDewis ffolder
+ Symud i...
+ Copïo i...%d wedi\'i dewisNesafBlaenorol
@@ -68,7 +77,9 @@
Ychwanegu cyfrifYsgrifennuChwilio
+ Chwilio ym mhobmanCanlyniadau Chwilio
+ Negeseuon newyddGosodiadauRheoli ffolderiGosodiadau cyfrif
@@ -79,6 +90,7 @@
Ychwanegu serenTynnu serenCopïo
+ Dad-tanysgrifioDangos penynnauCopïwyd y cyfeiriad i\'r clipfwrdd
@@ -86,6 +98,7 @@
Copïwyd y cyfeiriadau i\'r clipfwrddCopïwyd y cyfeiriadau i\'r clipfwrdd
+ Copïwyd testun y pwnc i\'r clipfwrddNewid i\'r thema dywyllNewid i\'r thema olauNodi heb ei darllen
@@ -127,6 +140,7 @@
ArchifoArchifo\'r CwblSbam
+ Gwall tystysgrifGwall tystysgrif ar gyfer %sGwiria osodiadau\'r gweinyddMethwyd a dilysu
@@ -136,6 +150,8 @@
Yn anfon negeseuon: %sYn anfon neges:
+ Cydweddu (Gwthio)
+ Dangosir wrth aros am negeseuon newyddNegeseuonHysbysiadau yn ymwneud â negeseuonAmrywiol
@@ -151,6 +167,9 @@
Logio gwybodaeth ddiagnostig ychwanegolLogio gwybodaeth sensitifGall dangos cyfrineiriau mewn logiau.
+ Allforio logiau
+ Allforiwyd yn llwyddiannus. Gall logiau gynnwys gwybodaeth sensitif. Byddwch yn ofalus gyda phwy rydych yn eu rhannu.
+ Methwyd yr allforio.Llwytho rhagor o negeseuonAt:%sPwnc
@@ -197,6 +216,7 @@
Defnyddio enwau pobol o dy gysylltiadau pan maen nhw ar gaelLliwio cysylltiadauLliwio enwau sydd yn dy gysylltiadau
+ Lliw enw\'r cyswlltFfontiau lled sefydlogDefnyddio ffont gyda lled sefydlog wrth ddangos negeseuon testun plaenAwto-ffitio negeseuon
@@ -224,6 +244,7 @@
Hysbysiadau ar y Sgrin CloiDim hysbysiadau ar y sgrin cloiEnw\'r ap
+ Nifer o negeseuon newyddAnfonwyr a nifer o negeseuonYr un fath a phan nad yw\'r sgrin ar gloiAmser Distawrwydd
@@ -235,7 +256,15 @@
Creu cyfrif newyddCyfeiriad e-bostCyfrinair
+ I ddefnyddio\'r cyfrif e-bost hwn â Mail, rhaid mewngofnodi a rhoi caniatâd i\'r ap gael mynediad at dy negeseuon e-bost.
+
+ Mewngofnodi
+
+ Mewngofnodi gyda Google
+ I weld eich cyfrinair fan hyn, galluogwch y clo sgrin ar y ddyfais hon.
+ Cadarnhewch mai chi sydd yno
+ Datgloi i weld eich cyfrifairGosod â llawYn nôl manylion y cyfrif\u2026
@@ -255,6 +284,7 @@
Cyfrinair, trosglwyddo\'n anniogelCyfrinair wedi\'i hamgodioTystysgrif cleient
+ OAuth 2.0Gosodiadau\'r gweinydd derbynEnw defnyddiwrCyfrinair
@@ -273,10 +303,7 @@
Peidio ei dileu ar y gweinyddDileu oddi ar y gweinyddNodi fel wedi\'i darllen ar y gweinydd
- Defnyddio cywasgiad ar rwydwaith:
- Symudol
- Wi-Fi
- Arall
+ Defnyddio cywasguDileu negeseuon sydd wedi eu dileu o\'r gweinyddYn sythWrth wirio am negeseuon
@@ -341,6 +368,9 @@
Enw defnyddiwr neu gyfrinair anghywir.\n(%s)Cyflwynodd y gweinydd tystysgrif SSL annilys. Weithiau mae hyn oherwydd camffurfweddiad y gweinydd. Weithiau mae o ganlyniad i rywun yn ceisio ymosod arnat ti neu dy weinydd e-bost. Os nad wyt yn siŵr be sy\'n digwydd, clicia "Gwrthod" a chysylltu\'r pobol sy\'n rheoli gweinydd dy e-bost.\n\n(%s)Methwyd cysylltu gyda\'r gweinydd.\n(%s)
+ Diddymwyd awdurdodi
+ Methodd awdurdodi gyda\'r gwall hwn: %s
+ Methodd yr ap hwn ddod o hyd i borwr i\'w ddefnyddio i roi mynediad at dy gyfrif.Golygu manylionParhauUwch
@@ -360,10 +390,15 @@
Dangos hysbysiadau ar gyfer negeseuon rwy\'n eu hanfonCysyllteion yn unigDangos hysbysiadau dim ond ar gyfer negeseuon gan fy nghysylltiadau i
+ Anwybyddu ceisiadau i sgwrsio
+ Peidio â dangos hysbysiadau am negeseuon sy\'n perthyn i sgwrs e-bostNodi fel wedi ei darllen wrth agorNodi neges fel ei bod wedi ei darllen wrth ei hagor i\'w darllen.Nodi wedi\'i darllen wrth dileuNodi neges wedi\'i darllen pan gaiff ei dileu
+ Categorïau hysbysiadau
+ Ffurfweddu hysbysiadau ar gyfer negeseuon newydd
+ Ffurfweddu hysbysiadau statws a gwallauDangos delweddau pob amserNaGan gysyllteion
@@ -489,11 +524,29 @@
Enw\'r cyfrifDy enwHysbysiadau
+ DirgrynuDirgrynu
+ Patrwm dirgrynuRhagosodedig
+ Patrwm 1
+ Patrwm 2
+ Patrwm 3
+ Patrwm 4
+ Patrwm 5Ail-adrodd dirgrynu
+ AnalluogwydSain neges newydd
+ Golau hysbysiad
+ AnalluogwydLliw y cyfrif
+ Lliw rhagosodedig y system
+ Gwyn
+ Coch
+ Gwyrdd
+ Glas
+ Melyn
+ Gwyrddlas
+ MagentaDewisiadau cyfansoddi negeseuonRhagosodiadau cyfansoddiGosod rhagosodiadau Oddi Wrth, Bcc a llofnod
@@ -603,6 +656,7 @@
Pori gyda\'r botymau sainWrth edrych ar negesWrth edrych ar restr
+ Dangos mewnflwch unedigMewnflwch UnedigHoll negeseuon pob cyfrif mewn un ffolder unedigUno
@@ -649,6 +703,7 @@
Methwyd anfon: %sCadw neges fel drafft?Cadw neu ddileu\'r neges hon?
+ Cadw neu Ddiddymu\'r newidiadau?Dileu\'r neges?Wyt ti\'n sicr dy fod am ddileu\'r neges hon?Dewisa destun i\'w gopïo.
@@ -695,12 +750,19 @@
Dewis ffeilMewnforioMewnforiwyd gosodiadau yn llwyddiannus
+
Rho gyfrineiriau
+
+ Mewngofnodwch
+
+ Mewngofnodwch a rhoi cyfrineiriauMethwyd â mewnforio gosodiadauMethwyd â darllen ffeil gosodiadauMethwyd â mewnforio rhai gosodiadauMewnforiwyd yn llwyddiannusAngen cyfrinair
+
+ Angen mewngofnodiHeb mewnforioGwall mewnforioYn hwyrach
@@ -813,6 +875,7 @@
BccAtOddi wrth
+ Ateb at<Derbyniwr Anhysbys><Anfonwr Anhysbys>Cartref
@@ -896,14 +959,6 @@
Caiff pob llofnod ei ddangosDyw amgryptio ddim ar gael yn y modd llofnod-yn-unig!Testun heb ei lofnodi
- Rhybudd Anghymeradwyaeth APG
- Dyw APG ddim yn cael ei cadw a chynnal bellach!
- Oherwydd hyn, mae cymorth ar gyfer APG wedi ei dynnu o K-9 Mail.
- Daeth datblygiad i ben yn gynnar yn 2014.
- Yn cynnwys materion diogelwch heb eu datrys
- Gelli di glicio yma i ddysgu mwy.
- Deall yn iawn!
- APGMae\'r neges e-bost hon wedi ei amgryptio.Mae\'r neges e-bost hon wedi ei amgryptio gydag OpenPGP.\nI\'w darllen, rhaid gosod a ffurfweddu ap OpenPGP sy\'n cydweddu.Mynd i Osodiadau
@@ -957,4 +1012,11 @@ Mae\'n bosib i ti gadw\'r neges hon a\'i ddefnyddio wrth gefn fel dy allwedd gyf
Caniatáu mynediad at gysylltiadauEr mwyn medru awgrymu cysylltiadau a dangos lluniau ac enwau cysylltiadau, mae ar yr ap angen mynediad at dy gysylltiadau.
+ Bu gwall wrth lwytho\'r data
+ Yn aros am negeseuon e-bost newydd
+ Yn cysgu nes bod rhwydwaith ar gael
+ Tapiwch i ddysgu rhagor.
+ Fodd bynnag, mae Android hefyd yn eich galluogi i guddio\'r hysbysiad.
+ Dysgu rhagor
+ Ffurfweddu hysbysiad
diff --git a/app/ui/legacy/src/main/res/values-da/strings.xml b/app/ui/legacy/src/main/res/values-da/strings.xml
index ee7f94e0bc36bb257290e626200f9abec4e31013..6e1f6d115005c802c2040a4746b3e6417a77e1e3 100644
--- a/app/ui/legacy/src/main/res/values-da/strings.xml
+++ b/app/ui/legacy/src/main/res/values-da/strings.xml
@@ -249,6 +249,8 @@
Opsætning af ny kontoMail addressePassword
+
+
Verificer din identitetManuel opsætning
@@ -288,10 +290,6 @@
Undlad at slette på serverSlet på serverMarker som læst på server
- Anvend kompression på netværket:
- Mobilt netværk
- Wi-Fi
- AndetRyd slettede mailsUmiddelbart efter sletningI forbindelse med hentning af mails
@@ -723,12 +721,16 @@
Vælg filImporterImporterede indstillinger
+
Indtast venligst kodeord
+
+
Importen af indstillinger fejledeIndlæsning af fil med indstillinger mislykkedesImport af nogle indstillinger fejledeImporteret korrrektKodeord påkrævet
+
Ikke importeretImport fejledeSenere
@@ -918,14 +920,6 @@
Alle signaturer visesKryptering er ikke tilgængelig i \"Signer-Kun\"-tilstand!Ikke-signeret tekst
- APG udfasningsadvarsel
- APG er ikke længere vedligeholdt!
- Grundet dette, understøttelse af APG er blevet fjernet fra K-9 Mail.
- Udviklingen stoppede tidlig 2014
- Indeholder ikke-rettede sikkerhedsfejl
- Du kan klikke her for at lære mere.
- Fint!
- APGDenne email er krypteretDenne email er krypteret med OpenPGP.\nFor at læse den skal du installere en kompatibel OpenPGP applikation.Gå til opsætning
diff --git a/app/ui/legacy/src/main/res/values-de/strings.xml b/app/ui/legacy/src/main/res/values-de/strings.xml
index 874740c59dd268c2186ee122a124174f64d3f353..0c91f166964d72dc01675a920ed6900668d00d90 100644
--- a/app/ui/legacy/src/main/res/values-de/strings.xml
+++ b/app/ui/legacy/src/main/res/values-de/strings.xml
@@ -1,1085 +1,1043 @@
-
-
-
- E-Mail
- E-Mail-Konten
- Ungelesene E-Mails
-
- The K-9 Dog Walkers
- \u00a9 2008-%s The K-9 Dog Walkers.
-Teile \u00a9 2006-%s Android Open Source Project.
- Quellcode
- Apache-Lizenz, Version 2.0
- Open Source Projekt
- Webseite
- Anwenderforum
- Fediverse
- Twitter
- Bibliotheken
- Lizenz
- Changelog
- Changelog konnte nicht geladen werden.
- Version %s
- Was gibt es Neues
- Zeige die neuesten Änderungen nach einem App-Update
- Finde heraus, was es Neues in diesem Release gibt
-
- Willkommen bei E-Mail
- E-Mail ist der Standard-E-Mail-Client für /e/
-
- -- Gesendet von /e/-Mail.
-
- Das Konto „%s“ wird aus der E-Mail-App gelöscht.
-
- Autoren
- Versionsinformationen
- E-Mails lesen
- Der Anwendung erlauben, Ihre E-Mails zu lesen.
- E-Mails löschen
- Der Anwendung erlauben, Ihre E-Mails zu löschen.
- Über E-Mail
- Konten
- Ordner
- Erweitert
- Verfassen
- Antworten
- Allen antworten
- Weiterleiten
- Als Anhang weiterleiten
- Konto auswählen
- Ordner auswählen
- Lade %s:%s%s
- Lade Kopfzeilen in %s:%s%s
- Sende %s%s
- Verarbeite %s:%s%s
- \u0020%d/%d
- Synchronisation deaktiviert
- %d ausgewählt
- Weiter
- Zurück
-
- OK
- Abbrechen
- Senden
- Erneut senden
- Betreff ist leer, bitte erneut drücken, um trotzdem zu senden
- Auswählen
- Abwählen
- Antworten
- Allen antworten
- Löschen
- Archivieren
- Spam
- Weiterleiten
- Als Anhang weiterleiten
- Als neue Nachricht bearbeiten
- Verschieben
- Zu Entwürfen verschieben
- Senden…
- Umsortieren…
- Fertig
- LÖSCHEN
- Als Entwurf speichern
- E-Mail abrufen
- Nachrichten senden
- Ordnerliste aktualisieren
- Ordner suchen
- Konto hinzufügen
- Verfassen
- Suchen
- Überall suchen
- Suchergebnisse
- Einstellungen
- Ordner verwalten
- Kontoeinstellungen
- Konto entfernen
- Als gelesen markieren
- Teilen
- Absender auswählen
- Als wichtig markieren
- Wichtig-Markierung entfernen
- Kopieren
- Kopfzeilen anzeigen
-
- Adresse in Zwischenablage kopiert
- Adressen in Zwischenablage kopiert
-
- Betreff in die Zwischenablage kopiert
- Dunkles Design verwenden
- Helles Design verwenden
- Als ungelesen markieren
- Empfangsbestätigung
- Eine Empfangsbestätigung wird angefordert
- Es wird keine Empfangsbestätigung angefordert
- Anhang hinzufügen
- Papierkorb leeren
- Bereinigen
- Über
- Einstellungen
-
- (Kein Betreff)
- Kein Absender
- Nachrichten werden geladen\u2026
- Verbindungsfehler
- Nachricht nicht gefunden
- Nachrichtenladefehler
- Bis zu %d weitere abrufen
- %.1f GB
- %.1f MB
- %.1f kB
- %d B
- Konto \'%s\' von %s auf %s komprimiert
- Konto \'%s\' wird komprimiert
- Neue E-Mail
-
- %d neue Nachricht
- %d neue Nachrichten
-
- %d Ungelesen (%s)
- + %1$d weitere auf %2$s
- Antworten
- Gelesen
- Alle als gelesen markieren
- Löschen
- Alle löschen
- Archiv
- Alle archivieren
- Spam
- Zertifikatsproblem (%s)
- Überprüfen Sie Ihre Servereinstellungen
- Authentifizierung fehlgeschlagen
- Authentifizierung fehlgeschlagen für %s. Überprüfen Sie Ihre Servereinstellungen.
- Neue E-Mails in %s:%s werden abgerufen
- E-Mails werden abgerufen
- E-Mail in %s wird gesendet
- E-Mail wird gesendet
- :
- Synchronisieren (Push)
- Wird während des Wartens auf neue Nachrichten angezeigt
- Nachrichtenanzeige
- Benachrichtigungen zu Nachrichten
- Verschiedenes
- Diverse Benachrichtigungen wie Fehler etc.
- Posteingang
- Postausgang
- Entwürfe
- Papierkorb
- Gesendet
- Nachrichten konnten nicht gesendet werden
- Version
- Fehlerprotokoll aktivieren
- Zusätzliche Informationen zur Fehlerdiagnose protokollieren
- Vertrauliche Informationen protokollieren
- Anmeldepasswörter bei Verbindungsaufbau protokollieren.
- Weitere Nachrichten laden
- An:%s
- Betreff:
- Nachrichtentext
- Signatur
- -------- Ursprüngliche Nachricht --------
- Betreff:
- Gesendet:
- Von:
- An:
- CC:
- %s schrieb:
- Am %1$s schrieb %2$s:
- Sie müssen mindestens einen Empfänger auswählen.
- Empfänger-Feld enthält unvollständigen Inhalt!
- Es wurde keine E-Mail-Adresse für diesen Kontakt gefunden.
- Einige Anhänge können nicht weitergeleitet werden, da diese nicht heruntergeladen wurden.
- Die Nachricht kann nicht weitergeleitet werden, da einige Anhänge nicht heruntergeladen wurden.
- Zitierte Nachricht einfügen
- Zitierten Text entfernen
- Zitierten Text bearbeiten
- Anhang entfernen
- Von: %s <%s>
- An:
- CC:
- BCC:
- Öffnen
- Speichern
- Anhang konnte nicht gespeichert werden.
- Bilder herunterladen
- Es wurde kein Anzeigeprogramm für %s gefunden.
- Gesamte Nachricht herunterladen
- Nachricht kann nicht angezeigt werden
- über %1$s
-
- Alle Kopfzeilen wurden heruntergeladen, es sind allerdings keine neuen Kopfzeilen vorhanden.
- Die weiteren Kopfzeilen konnten nicht aus der Datenbank oder vom Mailserver geladen werden.
- Mehr von diesem Absender
- Von %s
- Fehlersuche / Nachrichtentext löschen
- Nachricht wurde verworfen
- Nachricht wurde als Entwurf gespeichert
- Wichtige Nachrichten
- Sterne weisen auf wichtige Nachrichten hin
- Vorschauzeilen
- Namen anzeigen
- Namen anstatt der E-Mail-Adresse des Korrespondenten anzeigen
- Korrespondent über Betreff
- Namen der Korrespondenten über anstatt unter der Betreffzeile anzeigen
- Namen aus Adressbuch
- Bekannte Namen aus dem Adressbuch (\'Kontakte\') anzeigen
- Farbe von bekannten Namen
- Farbe wählen
- Monotype-Schriftart
- Schriftart mit gleicher Zeichenbreite für Nur-Text-Nachrichten verwenden
- Nachrichtendarstellung
- Nachrichten auf Bildschirmgröße verkleinern
- Nach Löschen zurück
- Nach Löschen zur Nachrichtenliste zurückkehren
- Nach Löschen nächste Nachricht
- Nach Löschen standardmäßig die nächste Nachricht anzeigen
- Bestätigungsdialog
- Bei Ausführung der ausgewählten Aktionen einen Bestätigungsdialog anzeigen
- Löschen
- Sternmarkierte Löschen (nur in Nachrichtenansicht)
- Spam
- Nachricht verwerfen
- Alle Nachrichten als gelesen markieren
- Löschen (aus Benachrichtigung)
- E-Mail-Client ausblenden
- Mail-Benutzer-Agent aus E-Mail-Kopfzeilen entfernen
- Zeitzone ausblenden
- UTC statt lokaler Zeitzone in E-Mail-Kopfzeilen und Antwort-Kopfzeilen verwenden
- Betreff in Benachrichtigungen verbergen
- Niemals
- Wenn der Bildschirm gesperrt ist
- Immer
- Löschen erlauben
- Nie
- Für einzelne Nachricht
- Immer
- Sperrbildschirm-Benachrichtigungen
- Keine Sperrbildschirm-Benachrichtigungen
- Anwendungsname
- Anzahl neuer Nachrichten
- Nachrichtenanzahl und Absender
- Wie bei entsperrtem Bildschirm
- Ruhezeit
- Klingeln, Vibrieren und Leuchten in der Nacht deaktivieren
- Benachrichtigungen deaktivieren
- Benachrichtigungen während der Ruhezeit komplett deaktivieren
- Ruhezeit startet
- Ruhezeit endet
- E-Mail-Konto einrichten
- E-Mail-Adresse
- Passwort
- Passwort anzeigen
- Manuelle Einrichtung
-
- Kontoinformationen werden abgerufen\u2026
- Einstellungen des Eingangsservers werden überprüft\u2026
- Einstellungen des Ausgangsservers werden überprüft\u2026
- Authentifizierung\u2026
- Kontoeinstellungen werden geladen\u2026
- Aktion wird abgebrochen\u2026
- Fast fertig!
- Kontoname (optional):
- Ihr Name (Anzeige bei ausgehenden Nachrichten):
- Kontotyp
- Art des Kontos?
- POP3-Konto
- IMAP-Konto
- Normales Passwort
- Passwort, unsicher übermittelt
- Verschlüsseltes Passwort
- Client-Zertifikat
- Einstellungen für Posteingangsserver
- Benutzername
- Passwort
- Client-Zertifikat
- POP3-Server
- IMAP-Server
- WebDAV(Exchange)-Server
- Port
- Sicherheitstyp
- Authentifizierungstyp
- Keine Verschlüsselung
- SSL/TLS
- STARTTLS
- \"%1$s = %2$s\" ist nicht gültig mit \"%3$s = %4$s\"
- Beim Löschen von Nachrichten
- Nie auf Server löschen
- Auch auf Server löschen
- Auf Server als gelesen markieren
- Komprimierung bei Netzwerk verwenden:
- Mobil
- WLAN
- Anderes
- Externer Speicher (SD-Karte)
- Interner Speicher
- Ordner bereinigen (Expunge)
- Sofort nach Verschieben oder Kopieren
- Bei jedem Abrufen
- Nur manuell
- IMAP-Namensraum automatisch ermitteln
- IMAP-Verzeichnispräfix
- Ordner für Entwürfe
- Ordner für gesendete Nachrichten
- Ordner für gelöschte Nachrichten
- Ordner für archivierte Nachrichten
- Ordner für Spam
- Nur ausgewählte Ordner anzeigen
- Startordner
- WebDAV-(Exchange-)Pfad
- Optional
- Authentifizierungspfad
- Optional
- Mailbox-Pfad
- Optional
- Einstellungen für Postausgangsserver
- SMTP-Server
- Port
- Sicherheitstyp
- Anmeldung erforderlich.
- Benutzername
- Passwort
- Authentifizierungsmethode
- \"%1$s = %2$s\" ist nicht gültig mit \"%3$s = %4$s\"
- Ungültige Einstellung: %s
- Kontooptionen
- Konto komprimieren
- E-Mail-Abfrage
- Nie (nur manuell)
- Alle 15 Minuten
- Alle 30 Minuten
- Jede Stunde
- Alle 2 Stunden
- Alle 3 Stunden
- Alle 6 Stunden
- Alle 12 Stunden
- Alle 24 Stunden
- Push-Mail für dieses Konto aktivieren
- Neue Nachrichten werden nach dem Eintreffen umgehend abgerufen, falls Ihr Server dies unterstützt. Diese Einstellung kann die Akkulaufzeit des Gerätes stark beeinflussen.
- Push-Verbindung erneuern
- Alle 2 Minuten
- Alle 3 Minuten
- Alle 6 Minuten
- Alle 12 Minuten
- Alle 24 Minuten
- Alle 36 Minuten
- Alle 48 Minuten
- Jede Stunde
- Benachrichtigen, wenn Nachrichten eingehen
- Benachrichtigen, wenn Nachrichten synchronisiert werden
- Anzahl sichtbarer Nachrichten
- 10 Nachrichten
- 25 Nachrichten
- 50 Nachrichten
- 100 Nachrichten
- 250 Nachrichten
- 500 Nachrichten
- 1000 Nachrichten
- 2500 Nachrichten
- 5000 Nachrichten
- 10000 Nachrichten
- Alle Nachrichten
- Nachrichten, die nicht mit dem Server synchronisiert werden, können nicht kopiert oder verschoben werden.
- Einrichtung konnte nicht abgeschlossen werden
- Benutzername oder Passwort ist falsch.\n(%s)
- Der Server verwendet ein ungültiges SSL-Zertifikat. Dies könnte daran liegen, dass der Server falsch konfiguriert ist. Allerdings könnte es auch daran liegen, dass jemand versucht Sie oder ihren E-Mail-Server anzugreifen. Falls Sie sich nicht sicher sind was der Grund ist, klicken Sie auf Abbrechen und kontaktieren Sie die Administratoren Ihres E-Mail-Servers. \n\n(%s)
- Verbindung zum Server nicht möglich.
-\n(%s)
- Details bearbeiten
- Weiter
- Erweitert
- Kontoeinstellungen
- Standardkonto
- Standardmäßig Nachrichten von diesem Konto aus senden
- Bei neuen Nachrichten
- Benachrichtigungsordner
- Alle
- Nur Hauptordner
- Haupt- und Nebenordner
- Alle außer Nebenordner
- Kein
- Benachrichtigung bei Synchronisation
- Ihre E-Mail-Adresse
- Benachrichtigung in der Statuszeile anzeigen, wenn eine neue Nachricht empfangen wird
- Benachrichtigung in der Statuszeile anzeigen, während nach neuen Nachrichten gesucht wird
- Bei eigenen Nachrichten
- Benachrichtigungen für Nachrichten, die eine Ihrer eigenen E-Mail-Adressen als Absender tragen
- Nur Kontakte
- Benachrichtigungen nur für Nachrichten von bekannten Kontakten anzeigen
- Beim Öffnen als gelesen markieren
- Nachricht als gelesen markieren, sobald sie zum Betrachten geöffnet wird
- Beim Löschen als gelesen markieren
- Nachricht als gelesen markieren, sobald sie gelöscht wird
- Benachrichtigungseinstellungen
- Benachrichtigungseinstellungen öffnen
- Bilder automatisch herunterladen
- Niemals
- Nur von Kontakten
- Immer
- E-Mail-Versand
- Nachricht beim Antworten zitieren
- Ursprüngliche Nachricht als Zitat in die Antwort einfügen.
- Antwort nach Zitat
- Die ursprüngliche Nachricht erscheint vor der Antwort.
- Signatur aus Zitat entfernen
- In Antworten werden Signaturen aus zitierten Nachrichten entfernt
- Formatierung
- Nur-Text (Bilder und Formatierungen entfernen)
- HTML (Bilder und Formatierungen bleiben erhalten)
- Automatisch
- CC/BCC immer anzeigen
- Empfangsbestätigung
- Immer eine Empfangsbestätigung anfordern
- Antwortkopf
- Normal (wie in Gmail, Pine)
- Erweitert (wie in Outlook, Yahoo!, Hotmail)
- Gesendete Nachrichten hochladen
- Nachrichten nach dem Senden in den Ordner Gesendet hochladen
- Allgemeine Einstellungen
- E-Mails lesen
- E-Mails abrufen
- Ordner
- Zitat-Präfix
- Ende-zu-Ende Verschlüsselung
- OpenPGP-Unterstützung aktivieren
- OpenPGP-App auswählen
- Ende-zu-Ende-Schlüssel konfigurieren
- Keine OpenPGP-App konfiguriert
- Verbunden mit %s
- Konfigurieren …
- Alle Entwürfe verschlüsselt speichern
- Alle Entwürfe werden verschlüsselt gespeichert
- Entwürfe nur verschlüsseln, wenn Verschlüsselung aktiviert ist
- Häufigkeit der E-Mail-Abfrage
- Kontofarbe
- Farbe, in der das Konto in der Konten- und Ordnerliste dargestellt wird
- Keine Farbe
- Farbe der Benachrichtigungs-LED
- Farbe, die für die Benachrichtigungs-LED bei Benachrichtigungen für dieses Konto verwendet wird
- Anzahl anzuzeigender Nachrichten
- Nachrichten automatisch laden bis
- 1 KiB
- 2 KiB
- 4 KiB
- 8 KiB
- 16 KiB
- 32 KiB
- 64 KiB
- 128 KiB
- 256 KiB
- 512 KiB
- 1 MiB
- 2 MiB
- 5 MiB
- 10 MiB
- Keine Beschränkung
- Nachrichten synchronisieren
- Alle (keine Beschränkung)
- von heute
- der letzten 2 Tage
- der letzten 3 Tage
- der letzten Woche
- der letzten 2 Wochen
- der letzten 3 Wochen
- des letzten Monats
- der letzten 2 Monate
- der letzten 3 Monate
- der letzten 6 Monate
- des letzten Jahres
- Angezeigte Ordner
- Alle
- Nur Hauptordner
- Haupt- und Nebenordner
- Alle außer Nebenordner
- Abzufragende Ordner
- Alle
- Nur Hauptordner
- Haupt- und Nebenordner
- Alle außer Nebenordner
- Keine
- Push-Ordner
- Alle Ordner
- Nur Hauptordner
- Haupt- und Nebenordner
- Alle außer Nebenordner
- Keine Ordner
- Zielordner für Kopieren/Verschieben
- Alle
- Nur Hauptordner
- Haupt- und Nebenordner
- Alle außer Nebenordner
- Löschungen vom Server übernehmen
- Nachrichten löschen, wenn sie vom Server gelöscht wurden
- Fehlende OpenPGP-App - wurde sie deinstalliert?
- Ordner-Einstellungen
- Oben anzeigen
- Ordner am Anfang der Ordnerliste anzeigen
- Anzeigeklasse
- Keine Klasse
- Hauptordner
- Nebenordner
- Abfrageklasse
- Keine
- Hauptordner
- Nebenordner
- Wie Anzeigeklasse
- Push-Klasse
- Keine Klasse
- Hauptordner
- Nebenordner
- Wie Abfrageklasse
- Benachrichtigungsklasse
- Keine Klasse
- Hauptordner
- Nebenordner
- Wie Push-Klasse
- Lokale Nachrichten löschen
- Posteingangsserver
- Einstellungen des Posteingangsservers bearbeiten
- Postausgangsserver
- Einstellungen des Postausgangsservers (SMTP) bearbeiten
- Kontoname
- Ihr Name
- Benachrichtigungen
- Vibration
- Vibration bei neuer Nachricht
- Vibrationsrhythmus
- Standard
- Rhythmus 1
- Rhythmus 2
- Rhythmus 3
- Rhythmus 4
- Rhythmus 5
- Anzahl der Vibrationen
- Klingelton
- LED-Blinken
- LED blinkt bei neuer Nachricht
- Verfassen von Nachrichten
- Verfassen von Nachrichten
- Standard-Absender, BCC-Empfänger und Signatur konfigurieren
- Identitäten verwalten
- Alternative Absenderadressen und Signaturen konfigurieren
- Identitäten verwalten
- Identität bearbeiten
- Identität bearbeiten
- Speichern
- Neue Identität erstellen
- Alle Nachrichten als BCC an
- Bearbeiten
- Nach oben verschieben
- Nach unten verschieben
- Ganz nach oben / Standard
- Entfernen
- Name der Identität
- (Optional)
- Ihr Name
- (Optional)
- E-Mail-Adresse
- (Erforderlich)
- Antwortadresse
- (Optional)
- Signatur
- (Optional)
- Signatur verwenden
- Signatur
- Standard-Identität
- Identität auswählen
- Sende als
- Sie können Ihre einzige Identität nicht löschen
- Sie können keine Identität ohne E-Mail-Adresse verwenden
- Älteste Nachrichten zuerst
- Neueste Nachrichten zuerst
- Alphabetisch nach Betreff (A-Z)
- Alphabetisch nach Betreff (Z-A)
- Alphabetisch nach Absender (A-Z)
- Alphabetisch nach Absender (Z-A)
- Markierte Nachrichten zuerst
- Nicht markierte Nachrichten zuerst
- Ungelesene Nachrichten zuerst
- Gelesene Nachrichten zuerst
- Nachrichten mit Anhängen zuerst
- Nachrichten ohne Anhänge zuerst
- Sortieren nach…
- Datum
- Ankunftsdatum
- Betreff
- Absender
- Wichtigkeit
- Gelesen/Ungelesen
- Anhang
- Konto entfernen
- Ungültiges Zertifikat
- Zertifikat akzeptieren
- Zertifikat ablehnen
- Entf (oder D) - Löschen\nR - Antworten\nA - Allen Antworten\nC - Verfassen\nF - Weiterleiten\nM - Verschieben\nV - Archivieren\nY - Kopieren\nZ - Als (un)gelesen markieren\nG - Wichtig\nO - Sortiertyp\nI - Sortierreihenfolge\nQ - Zurück zu den Ordnern\nS - Auswählen/Abwählen\nJ oder P - Vorherige Nachricht\nK oder N - Nächste Nachricht
- Entf (oder D) - Löschen\nC - Verfassen\nM - Verschieben\nV - Archivieren\nY - Kopieren\nZ - Als (un)gelesen markieren\nG - Wichtig\nO - Sortiertyp\nI - Sortierreihenfolge\nQ - Zurück zu den Ordnern\nS - Auswählen/Abwählen
- Ordnername enthält
- Ordner anzeigen …
- Alle Ordner anzeigen
- Nur Hauptordner anzeigen
- Haupt- und Nebenordner anzeigen
- Alle außer Nebenordner anzeigen
- Position der Signatur
- Vor zitierter Nachricht
- Nach zitierter Nachricht
- Zu verwendendes Anwendungsdesign
- Dunkel
- Hell
- Systemvorgabe verwenden
- Anzeige
- Globale Einstellungen
- Fehlersuche
- Datenschutz
- Netzwerk
- Interaktion
- Kontenliste
- Nachrichtenliste
- Nachrichten
- Design
- Nachrichten-Design
- Editor-Design
- Sprache
- Keine Einstellungen gefunden
- Festes Nachrichten-Design
- Nachrichten-Design während des Ansehens der Nachricht auswählen
- Immer das ausgewählte Nachrichten-Design verwenden
- Systemvorgabe
- Hintergrundsynchronisation
- Nie
- Immer
- Wenn \'Automatische Synchronisation\' aktiviert ist
- Alle auswählen
- Maximale Push-Ordner-Anzahl
- 5 Ordner
- 10 Ordner
- 25 Ordner
- 50 Ordner
- 100 Ordner
- 250 Ordner
- 500 Ordner
- 1000 Ordner
- Animationen
- Aufwendige visuelle Effekte benutzen
- Lauter/Leiser-Navigation
- Nachrichtenansicht
- Diverse Listenansichten
- Allen Posteingang anzeigen
- %s%s
- - Ungelesen
- Alle Nachrichten
- Alle Nachrichten in durchsuchbaren Ordnern
- Aller Posteingang
- Alle Nachrichten aus integrierten Ordnern
- In gem. Posteingang integrieren
- Alle Nachrichten in allen Posteingang anzeigen
- Durchsuchbare Ordner
- Alle
- Angezeigte
- Keine
- Keine
- Automatisch (%s)
- Schriftgröße
- Schriftgrößen festlegen
- Kontenliste
- Kontoname
- Kontobeschreibung
- Ordnerliste
- Ordnername
- Ordnerstatus
- Nachrichtenliste
- Betreff
- Absender
- Datum
- Vorschau
- Nachrichtenanzeige
- Absender
- Empfänger (To)
- Empfänger (CC)
- Blindkopie (BCC)
- Zusätzliche Header
- Betreff
- Uhrzeit und Datum
- Nachrichtentext
- %d%%
- %1$s: %2$s
- Nachricht verfassen
- Texteingabefelder
- Standard
- Winzig
- Sehr klein
- Kleiner
- Klein
- Mittel
- Groß
- Größer
- Keine geeignete Anwendung für diese Aktion gefunden.
- Senden fehlgeschlagen: %s
- Löschen bestätigen
- Diese Nachricht wirklich löschen\?
- Diese Änderungen löschen\?
- Löschen bestätigen
- Möchten Sie diese Nachricht löschen\?
- Text auswählen, um ihn zu kopieren.
- Lokale Nachrichten löschen?
- Dadurch werden alle lokalen Nachrichten aus dem Ordner entfernt. Es werden keine Nachrichten vom Server gelöscht.
- Nachrichten löschen
- Löschen bestätigen
- Möchten Sie diese Nachricht löschen?
-
- Möchten Sie diese Nachricht wirklich löschen?
- Möchten Sie wirklich %1$d Nachrichten löschen?
-
- LÖSCHEN
- ABBRECHEN
- Alle als gelesen markieren bestätigen
- Möchten Sie alle Nachrichten als gelesen markieren?
- Leeren des Papierkorbs bestätigen
- Möchten Sie den Papierkorb leeren?
- Ja
- Nein
- Als Spam markieren
-
- Möchten Sie wirklich diese Nachricht in den Spam-Ordner verschieben\?
- Möchten Sie wirklich %1$d Nachrichten in den Spam-Ordner verschieben\?
-
- Ja
- Nein
- Anhang wird heruntergeladen
- »
- ›
- Backup
- Verschiedenes
- Einstellungen exportieren
- Export
- Teilen
- Einstellungen werden exportiert…
- Einstellungen erfolgreich exportiert
- Einstellungen konnten nicht exportiert werden
- Einstellungen importieren
- Datei auswählen
- Importieren
- Einstellungen erfolgreich importiert
- Bitte geben Sie die Passwörter ein
- Einstellungen konnten nicht importiert werden
- Einstellungsdatei konnte nicht gelesen werden
- Einige Einstellungen konnten nicht importiert werden
- Erfolgreich importiert
- Passwort erforderlich
- Nicht importiert
- Importfehler
- Später
- Einstellungen importieren
- Einstellungen werden importiert…
-
- Um das Konto \"%s\" nutzen zu können, müssen Sie das Serverpasswort eingeben.
- Um das Konto \"%s\" nutzen zu können, müssen Sie die Serverpasswörter eingeben.
-
- Passwort des Posteingangsservers
- Passwort des Postausgangsservers
- Gleiches Passwort für den Postausgangsserver verwenden
- Servername: %s
- Anzahl ungelesener Nachrichten anzeigen für…
- Konto
- Das Konto, für das der Zähler für ungelesene Nachrichten angezeigt werden soll
- Aller Posteingang
- Ordneranzahl
- Den Zähler für ungelesene Nachrichten eines einzelnen Ordners zeigen
- Ordner
- Die Ordnerliste, für die der Zähler für ungelesene Nachrichten angezeigt werden soll
- Fertig
- %1$s - %2$s
- Kein Konto ausgewählt
- Kein Ordner ausgewählt
- Kein Nachrichtentext
- Öffnen
- Link teilen
- Link kopieren
- Link
- Linktext kopieren
- Linktext
- Bild
- Bild anzeigen
- Bild speichern
- Bild herunterladen
- Bild-URL kopieren
- Bild-URL
- Anrufen
- Im Adressbuch speichern
- Telefonnummer kopieren
- Telefonnummer
- E-Mail senden
- Im Adressbuch speichern
- E-Mail-Adresse kopieren
- E-Mail-Adresse
- Alle
- 10
- 25
- 50
- 100
- 250
- 500
- 1000
- Maximale Resultat-Anzahl
- Sende Anfrage an den Server
-
- %d Resultat wird abgerufen
- %d Resultate werden abgerufen
-
-
- %1$d von %2$d Resultaten werden abgerufen
- %1$d von %2$d Resultaten werden abgerufen
-
- Suche fehlgeschlagen
- Suche
- Serverseitige Suche
- Nachrichten nicht nur auf dem Gerät, sondern auch auf dem Server suchen
- Nachrichten auf Server suchen
- Für die Suche auf dem Server ist eine Netzverbindung nötig.
- Farbe ändern, wenn gelesen
- Ein anderer Hintergrund zeigt, dass die Nachricht gelesen wurde
- Nachrichten gruppieren
- Nachrichten eines Diskussionsstranges zusammenfassen
- Datenbankaktualisierung
- Datenbanken werden aktualisiert…
- Aktualisiere Datenbank für Konto \'%s\'
- Geteilte Ansicht
- Immer
- Nie
- Im Querformat
- Nachricht aus der Liste auswählen, um sie anzuzeigen
- Kontaktbilder anzeigen
- Kontaktbilder in der Nachrichtenliste anzeigen
- Alle als gelesen markieren
- Kontaktbilder einfärben
- Fehlende Kontaktbilder einfärben
- Sichtbare Nachrichtenaktionen
- Ausgewählte Aktionen im Menü der Nachrichtenanzeige anzeigen
- Anhang wird geladen…
- Nachricht senden
- Entwurf speichern
- Anhang wird heruntergeladen…
- Authentifizierung fehlgeschlagen. Der Server bietet nicht Simple Authentication and Security Layer, kurz SASL, an. Dies könnte durch ein Problem des Client-Zertifikats (Zertifikat abgelaufen, unbekannte Zertifizierungsstelle) oder ein Konfigurationsproblem ausgelöst werden.
-
- Client-Zertifikat verwenden
- Kein Client-Zertifikat
- Client-Zertifikatsauswahl entfernen
- Das Abrufen des Client-Zertifikats für den Pfad \"%s\" ist fehlgeschlagen
- Erweiterte Optionen
- Client-Zertifikat \"%1$s\" ist abgelaufen oder noch nicht gültig (%2$s)
-
- *Verschlüsselt*
- Vom Adressbuch hinzufügen
- CC:
- BCC:
- An:
- Von:
- <Unbekannter Empfänger>
- <Unbekannter Absender>
- Zuhause
- Arbeit
- Anderes
- Mobil
- Kein Ordner für Entwürfe für dieses Konto festgelegt!
- Kein Schlüssel für dieses Konto konfiguriert! Überprüfen Sie die Einstellungen.
- Verschlüsselungsdienst verwendet inkompatible Version. Bitte überprüfen Sie Ihre Einstellungen!
- Verbindung zu Verschlüsselungsdienst fehlgeschlagen. Überprüfen Sie die Einstellungen oder tippen Sie auf das Verschlüsselungs-Symbol um es erneut zu versuchen.
- Die Ende-zu-Ende-Verschlüsselung konnte nicht initialisiert werden, bitte überprüfen Sie Ihre Einstellungen.
- PGP/INLINE-Modus unterstützt keine Anhänge!
- PGP/INLINE aktivieren
- PGP/INLINE deaktivieren
- PGP Nur-Signieren aktivieren
- PGP Nur-Signieren deaktivieren
- PGP/INLINE-Modus
- Die E-Mail wurde im PGP/INLINE-Format gesendet.\nDies sollte nur für die Kompatibilität verwendet werden:
- Manche Clients unterstützen nur dieses Format
- Signaturen können während dem Versand kaputtgehen
- Anhänge werden nicht unterstützt
- Verstanden!
- Deaktivieren
- Aktiviert lassen
- Verstanden!
- Deaktivieren
- Aktiviert lassen
- PGP Nur-Signieren-Modus
- In diesem Modus wird Ihr PGP-Schlüssel verwendet, um eine kryptografische Signatur einer unverschlüsselten E-Mail zu erstellen.
- Dadurch wird die E-Mail nicht verschlüsselt, sondern es wird überprüft, ob sie von Ihrem Schlüssel gesendet wurde.
- Signaturen können kaputtgehen, wenn sie an Mailinglisten gesendet werden.
- Signaturen können als \'signature.asc\'-Anhänge in einigen Clients angezeigt werden.
- Verschlüsselte Nachrichten enthalten immer eine Signatur.
- Klartext
- Ende-zu-Ende-Signatur enthielt einen Fehler
- muss die Nachricht vollständig herunterladen, um die Signatur zu verarbeiten
- enthält eine nicht unterstützte Ende-zu-Ende-Signatur
- Nachricht ist verschlüsselt, aber in einem nicht unterstützten Format.
- Nachricht ist verschlüsselt, aber Entschlüsselung wurde abgebrochen.
- Ende-zu-Ende signierter Klartext
- von verifiziertem Signierer
- Signierter Klartext
- aber der Ende-zu-Ende-Schlüssel stimmte nicht mit dem Absender überein
- aber der Ende-zu-Ende-Schlüssel ist abgelaufen
- aber der Ende-zu-Ende-Schlüssel wurde widerrufen
- aber der Ende-zu-Ende-Schlüssel wird nicht als sicher angesehen
- von unbekanntem Ende-zu-Ende-Schlüssel
- Verschlüsselt
- aber es gab einen Entschlüsselungsfehler
- muss die Nachricht zur Entschlüsselung vollständig herunterladen
- aber keine Verschlüsselungs-App eingerichtet
- Verschlüsselt
- aber nicht Ende-zu-Ende
- Ende-zu-Ende verschlüsselt
- von verifiziertem Absender
- Verschlüsselt
- von unbekanntem Ende-zu-Ende-Schlüssel
- aber der Ende-zu-Ende-Schlüssel stimmte nicht mit dem Absender überein
- aber der Ende-zu-Ende-Schlüssel ist abgelaufen
- aber der Ende-zu-Ende-Schlüssel wurde widerrufen
- aber der Ende-zu-Ende-Schlüssel wird nicht als sicher angesehen
- aber die Ende-zu-Ende Daten sind fehlerhaft
- aber die Verschlüsselung wird nicht als sicher angesehen
- OK
- Schlüssel suchen
- Signierer anzeigen
- Absender anzeigen
- Details
- Entsperren
- Dieser Teil wurde nicht verschlüsselt und könnte unsicher sein.
- Ungeschützter Anhang
- Ladevorgang…
- Entschlüsselung abgebrochen.
- Wiederholen
- Verschlüsselte Nachricht muss für die Entschlüsselung heruntergeladen werden.
- Fehler beim Entschlüsseln der E-Mail
- Sonderzeichen werden derzeit nicht unterstützt!
- Fehler beim Einlesen der Adresse!
- Unverschlüsselte Signaturen ausblenden
- Nur verschlüsselte Signaturen werden angezeigt
- Alle Signaturen werden angezeigt
- Verschlüsselung ist im Nur-Signieren-Modus nicht verfügbar!
- Unsignierter Text
- APG Veraltungswarnung
- APG wird nicht mehr gepflegt!
- Aus diesem Grund wurde die Unterstützung für APG aus K-9 Mail entfernt.
- Entwicklung wurde Anfang 2014 eingestellt
- Enthält ungelöste Sicherheitsprobleme
- Klicken Sie hier, um mehr zu erfahren.
- Verstanden!
- APG
- Diese E-Mail ist verschlüsselt
- Diese E-Mail wurde mit OpenPGP verschlüsselt.
-\nUm sie lesen zu können, muss eine kompatible OpenPGP-App installiert und konfiguriert werden.
- Zu den Einstellungen
- E-Mail-Nachrichtenliste
- Nachrichten werden geladen…
- Abrufen der Ordnerliste ist fehlgeschlagen
- Fehler beim Abrufen des Empfängerstatus vom OpenPGP-Anbieter!
- Verschlüsselung nicht möglich
- Einige der ausgewählten Empfänger unterstützen diese Funktion nicht!
- Verschlüsselung aktivieren
- Verschlüsselung deaktivieren
- Durch die Verschlüsselung von Nachrichten wird sichergestellt, dass sie für den Empfänger und sonst niemanden lesbar sind.
- Die Verschlüsselung wird nur angezeigt, wenn sie von allen Empfängern unterstützt wird und diese Ihnen zuvor schon eine E-Mail gesendet haben.
- Schalten Sie die Verschlüsselung um, indem Sie auf dieses Symbol klicken.
- Verstanden
- Zurück
- Verschlüsselung deaktivieren
- OpenPGP-Verschlüsselung
- Autocrypt wechselseitiger Modus
- Autocrypt wechselseitiger Modus
- Nachrichten werden normalerweise auf Wunsch oder beim Antworten auf eine verschlüsselte Nachricht verschlüsselt.
- Wenn sowohl Sender als auch Empfänger den wechselseitigen Modus aktiviert haben, wird die Verschlüsselung automatisch aktiviert.
- Sie können hier klicken , um mehr zu erfahren.
- Allgemeine Einstellungen
- Keine OpenPGP-App installiert
- Installieren
- E-Mail benötigt OpenKeychain für die Ende-zu-Ende-Verschlüsselung.
- Verschlüsselte Nachricht
- Betreff der Nachricht verschlüsseln
- Möglicherweise nicht von allen Empfängern unterstützt
- Interner Fehler: Ungültiges Konto!
- Fehler beim Verbinden mit %s!
- Autocrypt-Setup-Nachricht senden
- Das Ende-zu-Ende-Setup auf sichere Art mit anderen Geräten teilen
- Autocrypt-Setup-Nachricht
- Eine Autocrypt-Einstellungs-Nachricht teilt Ihre Ende-zu-Ende-Einstellung auf sichere Art und Weise mit anderen Geräten.
- Setup-Nachricht senden
- Diese Nachricht wird an Ihre Adresse gesendet:
- Erstelle Setup-Nachricht…
- Sende Nachricht an:
- Um abzuschließen, öffnen Sie die Nachricht auf einem anderen Gerät und geben Sie den Setup-Code ein.
- Setup-Code anzeigen
- Autocrypt-Setup-Nachricht
- Diese Nachricht enthält alle Informationen, um Ihre Autocrypt-Einstellungen zusammen mit Ihrem geheimen Schlüssel von Ihrem ursprünglichen Gerät sicher zu übertragen.
+
+
+
+ Mail
+ K-9 Konten
+ K-9 Ungelesen
+
+ The K-9 Dog Walkers
+ Quellcode
+ Apache-Lizenz, Version 2.0
+ Open Source Projekt
+ Webseite
+ Anwenderforum
+ Fediverse
+ Twitter
+ Bibliotheken
+ Lizenz
+ Changelog
+ Changelog konnte nicht geladen werden.
+ Version %s
+ Was gibt es Neues
+ Zeige die neuesten Änderungen nach einem App-Update
+ Finde heraus, was es Neues in diesem Release gibt
+
+ Willkommen bei Mail
+
+Mail ist ein leistungsstarker, freier E-Mail-Client für Android.
+
+Seine verbesserten Funktionen beinhalten:
+
+
+
Push-Mail unter Verwendung von IMAP IDLE
+
Bessere Leistung
+
Nachrichtenneuordnung
+
E-Mail-Signaturen
+
Bcc-to-self
+
Ordnerabonnements
+
Synchronisation aller Ordner
+
Rücksendeadresskonfiguration
+
Tastaturkürzel
+
Bessere IMAP-Unterstützung
+
Speichern von Anhängen auf SD
+
Papierkorb leeren
+
Nachrichtensortierung
+
…und weitere
+
+
+Bitte beachten Sie, dass K-9 die meisten kostenlosen Hotmail-Konten nicht unterstützt und, wie viele andere E-Mail-Clients, einige Macken bei der Kommunikation mit Microsoft Exchange hat.
+
+Bitte senden Sie Fehlerberichte, Ideen für neue Funktionen und stellen Sie Fragen an
+https://github.com/k9mail/k-9/.
+
+ ]]>
+
+ -- \nDiese Nachricht wurde von meinem Android-Gerät mit Mail gesendet.
+
+ Das Konto \"%s\" wird aus Mail entfernt.
+
+ Autoren
+ Über Mail
+ Konten
+ Erweitert
+ Verfassen
+ Antworten
+ Allen antworten
+ Weiterleiten
+ Als Anhang weiterleiten
+ Konto auswählen
+ Ordner auswählen
+ Verschieben nach…
+ Kopieren nach…
+ %d ausgewählt
+ Weiter
+ Zurück
+
+ OK
+ Abbrechen
+ Senden
+ Betreff ist leer, bitte erneut drücken, um trotzdem zu senden
+ Antworten
+ Allen antworten
+ Löschen
+ Archivieren
+ Spam
+ Weiterleiten
+ Als Anhang weiterleiten
+ Als neue Nachricht bearbeiten
+ Verschieben
+ Zu Entwürfen verschieben
+ Senden…
+ Umsortieren…
+ Fertig
+ Verwerfen
+ Als Entwurf speichern
+ E-Mail abrufen
+ Nachrichten senden
+ Ordnerliste aktualisieren
+ Ordner suchen
+ Konto hinzufügen
+ Verfassen
+ Suchen
+ Überall suchen
+ Suchergebnisse
+ Neue Nachrichten
+ Einstellungen
+ Ordner verwalten
+ Kontoeinstellungen
+ Konto entfernen
+ Als gelesen markieren
+ Teilen
+ Absender auswählen
+ Als wichtig markieren
+ Wichtig-Markierung entfernen
+ Kopieren
+ Abmelden
+ Kopfzeilen anzeigen
+
+ Adresse in Zwischenablage kopiert
+ Adressen in Zwischenablage kopiert
+
+ Betreff in die Zwischenablage kopiert
+ Dunkles Design verwenden
+ Helles Design verwenden
+ Als ungelesen markieren
+ Empfangsbestätigung
+ Eine Empfangsbestätigung wird angefordert
+ Es wird keine Empfangsbestätigung angefordert
+ Anhang hinzufügen
+ Papierkorb leeren
+ Bereinigen
+ Über
+ Einstellungen
+
+ (Kein Betreff)
+ Kein Absender
+ Nachrichten werden geladen\u2026
+ Verbindungsfehler
+ Nachricht nicht gefunden
+ Nachrichtenladefehler
+ Bis zu %d weitere abrufen
+ %.1f GB
+ %.1f MB
+ %.1f kB
+ %d B
+ Neue E-Mail
+
+ %d neue Nachricht
+ %d neue Nachrichten
+
+ %d Ungelesen (%s)
+ + %1$d weitere auf %2$s
+ Antworten
+ Gelesen
+ Alle als gelesen markieren
+ Löschen
+ Alle löschen
+ Archiv
+ Alle archivieren
+ Spam
+ Zertifikatsfehler
+ Zertifikatsproblem (%s)
+ Überprüfen Sie Ihre Servereinstellungen
+ Authentifizierung fehlgeschlagen
+ Authentifizierung fehlgeschlagen für %s. Überprüfen Sie Ihre Servereinstellungen.
+ Neue E-Mails in %s:%s werden abgerufen
+ E-Mails werden abgerufen
+ E-Mail in %s wird gesendet
+ E-Mail wird gesendet
+ :
+ Synchronisieren (Push)
+ Wird während des Wartens auf neue Nachrichten angezeigt
+ Nachrichtenanzeige
+ Benachrichtigungen zu Nachrichten
+ Verschiedenes
+ Diverse Benachrichtigungen wie Fehler etc.
+ Posteingang
+ Postausgang
+ Entwürfe
+ Papierkorb
+ Gesendet
+ Nachrichten konnten nicht gesendet werden
+ Version
+ Fehlerprotokoll aktivieren
+ Zusätzliche Informationen zur Fehlerdiagnose protokollieren
+ Vertrauliche Informationen protokollieren
+ Passwörter können in Protokollen sichtbar sein.
+ Protokolle exportieren
+ Export erfolgreich. Die Protokolle können sensible Informationen enthalten. Achten Sie darauf, an wen Sie sie senden.
+ Export fehlgeschlagen.
+ Weitere Nachrichten laden
+ An:%s
+ Betreff
+ Nachrichtentext
+ Signatur
+ -------- Ursprüngliche Nachricht --------
+ Betreff:
+ Gesendet:
+ Von:
+ An:
+ CC:
+ %s schrieb:
+ Am %1$s schrieb %2$s:
+ Sie müssen mindestens einen Empfänger auswählen.
+ Empfänger-Feld enthält unvollständigen Inhalt!
+ Es wurde keine E-Mail-Adresse für diesen Kontakt gefunden.
+ Einige Anhänge können nicht weitergeleitet werden, da diese nicht heruntergeladen wurden.
+ Die Nachricht kann nicht weitergeleitet werden, da einige Anhänge nicht heruntergeladen wurden.
+ Zitierte Nachricht einfügen
+ Zitierten Text entfernen
+ Zitierten Text bearbeiten
+ Anhang entfernen
+ Von: %s <%s>
+ An:
+ CC:
+ BCC:
+ Anhang konnte nicht gespeichert werden
+ Bilder herunterladen
+ Es wurde kein Anzeigeprogramm für %s gefunden.
+ Gesamte Nachricht herunterladen
+ über %1$s
+ Mehr von diesem Absender
+ Von %s
+ Nachricht wurde verworfen
+ Nachricht wurde als Entwurf gespeichert
+ Wichtige Nachrichten
+ Sterne weisen auf wichtige Nachrichten hin.
+ Vorschauzeilen
+ Namen anzeigen
+ Namen anstatt der E-Mail-Adresse des Korrespondenten anzeigen
+ Korrespondent über Betreff
+ Namen der Korrespondenten über anstatt unter der Betreffzeile anzeigen
+ Namen aus Adressbuch
+ Bekannte Namen aus dem Adressbuch (\'Kontakte\') anzeigen
+ Farbe von bekannten Namen
+ Namen in Ihrer Kontaktliste einfärben
+ Farbe des Kontaktnamens
+ Monotype-Schriftart
+ Schriftart mit gleicher Zeichenbreite für Nur-Text-Nachrichten verwenden
+ Nachrichtendarstellung
+ Nachrichten auf Displaygröße verkleinern
+ Nach Löschen zurück
+ Nach Löschen zur Nachrichtenliste zurückkehren
+ Nach Löschen nächste Nachricht
+ Nach Löschen standardmäßig die nächste Nachricht anzeigen
+ Bestätigungsdialog
+ Bei Ausführung der ausgewählten Aktionen einen Bestätigungsdialog anzeigen
+ Löschen
+ Sternmarkierte Löschen (nur in Nachrichtenansicht)
+ Spam
+ Nachricht verwerfen
+ Alle Nachrichten als gelesen markieren
+ Löschen (aus Benachrichtigung)
+ E-Mail-Client ausblenden
+ K-9 Benutzer-Agent aus E-Mail-Kopfzeilen entfernen
+ Zeitzone ausblenden
+ UTC statt lokaler Zeitzone in E-Mail-Kopfzeilen und Antwort-Kopfzeilen verwenden
+ Löschen erlauben
+ Nie
+ Für einzelne Nachricht
+ Immer
+ Sperrbildschirm-Benachrichtigungen
+ Keine Sperrbildschirm-Benachrichtigungen
+ Anwendungsname
+ Anzahl der neuen Nachrichten
+ Nachrichtenanzahl und Absender
+ Wie bei entsperrtem Bildschirm
+ Ruhezeit
+ Klingeln, Vibrieren und Leuchten in der Nacht deaktivieren
+ Benachrichtigungen deaktivieren
+ Benachrichtigungen während der Ruhezeit komplett deaktivieren
+ Ruhezeit startet
+ Ruhezeit endet
+ E-Mail-Konto einrichten
+ E-Mail-Adresse
+ Passwort
+ Um dieses E-Mail-Konto mit Mail zu verwenden, müssen Sie sich anmelden und der App Zugriff auf Ihre E-Mails gewähren.
+
+ Anmelden
+
+ Mit Google anmelden
+
+ Um Ihr Passwort hier anzuzeigen, aktivieren Sie die Bildschirmsperre auf diesem Gerät.
+ Ihre Identität bestätigen
+ Entsperren, um Ihr Passwort anzuzeigen
+ Manuelle Einrichtung
+
+ Kontoinformationen werden abgerufen\u2026
+ Einstellungen des Eingangsservers werden überprüft\u2026
+ Einstellungen des Ausgangsservers werden überprüft\u2026
+ Authentifizierung\u2026
+ Kontoeinstellungen werden geladen\u2026
+ Aktion wird abgebrochen\u2026
+ Fast fertig!
+ Kontoname (optional):
+ Ihr Name (Anzeige bei ausgehenden Nachrichten):
+ Kontotyp
+ Art des Kontos?
+ POP3-Konto
+ IMAP-Konto
+ Normales Passwort
+ Passwort, unsicher übermittelt
+ Verschlüsseltes Passwort
+ Client-Zertifikat
+ OAuth 2.0
+ Einstellungen für Posteingangsserver
+ Benutzername
+ Passwort
+ Client-Zertifikat
+ POP3-Server
+ IMAP-Server
+ WebDAV(Exchange)-Server
+ Port
+ Sicherheitstyp
+ Authentifizierungstyp
+ Keine Verschlüsselung
+ SSL/TLS
+ STARTTLS
+ \"%1$s = %2$s\" ist nicht gültig mit \"%3$s = %4$s\"
+ Beim Löschen von Nachrichten
+ Nie auf Server löschen
+ Auch auf Server löschen
+ Auf Server als gelesen markieren
+ Komprimierung durchführen
+ Ordner bereinigen (Expunge)
+ Sofort nach Verschieben oder Kopieren
+ Bei jedem Abrufen
+ Nur manuell
+ IMAP-Namensraum automatisch ermitteln
+ IMAP-Verzeichnispräfix
+ Ordner für Entwürfe
+ Ordner für gesendete Nachrichten
+ Ordner für gelöschte Nachrichten
+ Ordner für archivierte Nachrichten
+ Ordner für Spam
+ Nur ausgewählte Ordner anzeigen
+ Startordner
+ WebDAV-(Exchange-)Pfad
+ Authentifizierungspfad
+ Mailbox-Pfad
+ Einstellungen für Postausgangsserver
+ SMTP-Server
+ Port
+ Sicherheitstyp
+ Anmeldung erforderlich
+ Benutzername
+ Passwort
+ Authentifizierungsmethode
+ \"%1$s = %2$s\" ist nicht gültig mit \"%3$s = %4$s\"
+ Ungültige Einstellung: %s
+ Kontooptionen
+ E-Mail-Abfrage
+ Nie (nur manuell)
+ Alle 15 Minuten
+ Alle 30 Minuten
+ Jede Stunde
+ Alle 2 Stunden
+ Alle 3 Stunden
+ Alle 6 Stunden
+ Alle 12 Stunden
+ Alle 24 Stunden
+ Push-Verbindung erneuern
+ Alle 2 Minuten
+ Alle 3 Minuten
+ Alle 6 Minuten
+ Alle 12 Minuten
+ Alle 24 Minuten
+ Alle 36 Minuten
+ Alle 48 Minuten
+ Jede Stunde
+ Benachrichtigen, wenn Nachrichten eingehen
+ Anzahl sichtbarer Nachrichten
+ 10 Nachrichten
+ 25 Nachrichten
+ 50 Nachrichten
+ 100 Nachrichten
+ 250 Nachrichten
+ 500 Nachrichten
+ 1000 Nachrichten
+ 2500 Nachrichten
+ 5000 Nachrichten
+ 10000 Nachrichten
+ Alle Nachrichten
+ Nachrichten, die nicht mit dem Server synchronisiert wurden, können nicht kopiert oder verschoben werden.
+ Einrichtung konnte nicht abgeschlossen werden
+ Benutzername oder Passwort ist falsch.\n(%s)
+ Der Server verwendet ein ungültiges SSL-Zertifikat. Dies könnte daran liegen, dass der Server falsch konfiguriert ist. Allerdings könnte es auch daran liegen, dass jemand versucht Sie oder ihren E-Mail-Server anzugreifen. Falls Sie sich nicht sicher sind was der Grund ist, klicken Sie auf Abbrechen und kontaktieren Sie die Administratoren Ihres E-Mail-Servers. \n\n(%s)
+ Verbindung zum Server nicht möglich.
+(%s)
+ Berechtigung aufgehoben
+ Die Autorisierung ist aufgrund des folgenden Fehlers fehlgeschlagen: %s
+ OAuth 2.0 wird von diesem Anbieter derzeit nicht unterstützt.
+ Die App konnte keinen Browser finden, der für den Zugriff auf Ihr Konto verwendet werden kann.
+ Details bearbeiten
+ Weiter
+ Erweitert
+ Kontoeinstellungen
+ Bei neuen Nachrichten
+ Benachrichtigungsordner
+ Alle
+ Nur Hauptordner
+ Haupt- und Nebenordner
+ Alle außer Nebenordner
+ Kein
+ Benachrichtigung bei Synchronisation
+ Ihre E-Mail-Adresse
+ Benachrichtigung in der Statuszeile anzeigen, wenn eine neue Nachricht empfangen wird
+ Benachrichtigung in der Statuszeile anzeigen, während nach neuen Nachrichten gesucht wird
+ Bei eigenen Nachrichten
+ Benachrichtigungen für Nachrichten, die eine Ihrer eigenen E-Mail-Adressen als Absender tragen
+ Nur Kontakte
+ Benachrichtigungen nur für Nachrichten von bekannten Kontakten anzeigen
+ Chat-Nachrichten ignorieren
+ Keine Benachrichtigungen für Nachrichten anzeigen, die zu einem E-Mail-Chat gehören
+ Beim Öffnen als gelesen markieren
+ Nachricht als gelesen markieren, sobald sie zum Betrachten geöffnet wird
+ Beim Löschen als gelesen markieren
+ Nachricht als gelesen markieren, sobald sie gelöscht wird
+ Benachrichtigungskategorien
+ Konfiguriere die Benachrichtigungen für neue Nachrichten
+ Konfiguriere Fehler- und Statusbenachrichtigungen
+ Bilder automatisch herunterladen
+ Niemals
+ Nur von Kontakten
+ Immer
+ E-Mail-Versand
+ Nachricht beim Antworten zitieren
+ Ursprüngliche Nachricht als Zitat in die Antwort einfügen
+ Antwort nach Zitat
+ Die ursprüngliche Nachricht erscheint vor der Antwort
+ Signatur aus Zitat entfernen
+ In Antworten werden Signaturen aus zitierten Nachrichten entfernt
+ Formatierung
+ Nur-Text (Bilder und Formatierungen entfernen)
+ HTML (Bilder und Formatierungen bleiben erhalten)
+ Automatisch
+ CC/BCC immer anzeigen
+ Empfangsbestätigung
+ Immer eine Empfangsbestätigung anfordern
+ Antwortkopf
+ Normal (wie in Gmail, Pine)
+ Erweitert (wie in Outlook, Yahoo!, Hotmail)
+ Gesendete Nachrichten hochladen
+ Nachrichten nach dem Senden in den Ordner Gesendet hochladen
+ Allgemeine Einstellungen
+ E-Mails lesen
+ E-Mails abrufen
+ Ordner
+ Zitat-Präfix
+ Ende-zu-Ende Verschlüsselung
+ OpenPGP-Unterstützung aktivieren
+ OpenPGP-App auswählen
+ Ende-zu-Ende-Schlüssel konfigurieren
+ Keine OpenPGP-App konfiguriert
+ Verbunden mit %s
+ Konfigurieren…
+ Alle Entwürfe verschlüsselt speichern
+ Alle Entwürfe werden verschlüsselt gespeichert
+ Entwürfe nur verschlüsseln, wenn Verschlüsselung aktiviert ist
+ Häufigkeit der E-Mail-Abfrage
+ Kontofarbe
+ Farbe, in der das Konto in der Konten- und Ordnerliste dargestellt wird
+ Anzahl anzuzeigender Nachrichten
+ Nachrichten automatisch laden bis
+ 1 KiB
+ 2 KiB
+ 4 KiB
+ 8 KiB
+ 16 KiB
+ 32 KiB
+ 64 KiB
+ 128 KiB
+ 256 KiB
+ 512 KiB
+ 1 MiB
+ 2 MiB
+ 5 MiB
+ 10 MiB
+ Keine Beschränkung
+ Nachrichten synchronisieren
+ Alle (keine Beschränkung)
+ von heute
+ der letzten 2 Tage
+ der letzten 3 Tage
+ der letzten Woche
+ der letzten 2 Wochen
+ der letzten 3 Wochen
+ des letzten Monats
+ der letzten 2 Monate
+ der letzten 3 Monate
+ der letzten 6 Monate
+ des letzten Jahres
+ Angezeigte Ordner
+ Alle
+ Nur Hauptordner
+ Haupt- und Nebenordner
+ Alle außer Nebenordner
+ Abzufragende Ordner
+ Alle
+ Nur Hauptordner
+ Haupt- und Nebenordner
+ Alle außer Nebenordner
+ Keine
+ Push-Ordner
+ Alle Ordner
+ Nur Hauptordner
+ Haupt- und Nebenordner
+ Alle außer Nebenordner
+ Keine Ordner
+ Zielordner für Kopieren/Verschieben
+ Alle
+ Nur Hauptordner
+ Haupt- und Nebenordner
+ Alle außer Nebenordner
+ Löschungen vom Server übernehmen
+ Nachrichten löschen, wenn sie vom Server gelöscht wurden
+ Fehlende OpenPGP-App - wurde sie deinstalliert?
+ Ordner-Einstellungen
+ Oben anzeigen
+ Ordner am Anfang der Ordnerliste anzeigen
+ Anzeigeklasse
+ Keine Klasse
+ Hauptordner
+ Nebenordner
+ Abfrageklasse
+ Keine
+ Hauptordner
+ Nebenordner
+ Wie Anzeigeklasse
+ Push-Klasse
+ Keine Klasse
+ Hauptordner
+ Nebenordner
+ Wie Abfrageklasse
+ Benachrichtigungsklasse
+ Keine Klasse
+ Hauptordner
+ Nebenordner
+ Wie Push-Klasse
+ Lokale Nachrichten löschen
+ Posteingangsserver
+ Einstellungen des Posteingangsservers bearbeiten
+ Postausgangsserver
+ Einstellungen des Postausgangsservers (SMTP) bearbeiten
+ Kontoname
+ Ihr Name
+ Benachrichtigungen
+ Vibration
+ Vibration
+ Vibrationsrhythmus
+ Standard
+ Rhythmus 1
+ Rhythmus 2
+ Rhythmus 3
+ Rhythmus 4
+ Rhythmus 5
+ Anzahl der Vibrationen
+ Deaktiviert
+ Klingelton
+ Benachrichtigungslicht
+ Deaktiviert
+ Kontofarbe
+ Systemstandardfarbe
+ Weiß
+ Rot
+ Grün
+ Blau
+ Gelb
+ Türkis
+ Magenta
+ Verfassen von Nachrichten
+ Verfassen von Nachrichten
+ Standard-Absender, BCC-Empfänger und Signatur konfigurieren
+ Identitäten verwalten
+ Alternative Absenderadressen und Signaturen konfigurieren
+ Identitäten verwalten
+ Identität bearbeiten
+ Identität bearbeiten
+ Speichern
+ Neue Identität erstellen
+ Alle Nachrichten als BCC an
+ Bearbeiten
+ Nach oben verschieben
+ Nach unten verschieben
+ Ganz nach oben / Standard
+ Entfernen
+ Name der Identität
+ (Optional)
+ Ihr Name
+ (Optional)
+ E-Mail-Adresse
+ (Erforderlich)
+ Antwortadresse
+ (Optional)
+ Signatur
+ (Optional)
+ Signatur verwenden
+ Signatur
+ Standard-Identität
+ Identität auswählen
+ Sende als
+ Sie können Ihre einzige Identität nicht löschen
+ Sie können keine Identität ohne E-Mail-Adresse verwenden
+ Älteste Nachrichten zuerst
+ Neueste Nachrichten zuerst
+ Alphabetisch nach Betreff (A-Z)
+ Alphabetisch nach Betreff (Z-A)
+ Alphabetisch nach Absender (A-Z)
+ Alphabetisch nach Absender (Z-A)
+ Markierte Nachrichten zuerst
+ Nicht markierte Nachrichten zuerst
+ Ungelesene Nachrichten zuerst
+ Gelesene Nachrichten zuerst
+ Nachrichten mit Anhängen zuerst
+ Nachrichten ohne Anhänge zuerst
+ Sortieren nach…
+ Datum
+ Ankunftsdatum
+ Betreff
+ Absender
+ Wichtigkeit
+ Gelesen/Ungelesen
+ Anhang
+ Konto entfernen
+ Ungültiges Zertifikat
+ Zertifikat akzeptieren
+ Zertifikat ablehnen
+ Entf (oder D) - Löschen\nR - Antworten\nA - Allen Antworten\nC - Verfassen\nF - Weiterleiten\nM - Verschieben\nV - Archivieren\nY - Kopieren\nZ - Als (un)gelesen markieren\nG - Wichtig\nO - Sortiertyp\nI - Sortierreihenfolge\nQ - Zurück zu den Ordnern\nS - Auswählen/Abwählen\nJ oder P - Vorherige Nachricht\nK oder N - Nächste Nachricht
+ Entf (oder D) - Löschen\nC - Verfassen\nM - Verschieben\nV - Archivieren\nY - Kopieren\nZ - Als (un)gelesen markieren\nG - Wichtig\nO - Sortiertyp\nI - Sortierreihenfolge\nQ - Zurück zu den Ordnern\nS - Auswählen/Abwählen
+ Ordnername enthält
+ Ordner
+ Alle Ordner anzeigen
+ Nur Hauptordner anzeigen
+ Haupt- und Nebenordner anzeigen
+ Alle außer Nebenordner anzeigen
+ Position der Signatur
+ Vor zitierter Nachricht
+ Nach zitierter Nachricht
+ Zu verwendendes Anwendungsdesign
+ Dunkel
+ Hell
+ Systemvorgabe verwenden
+ Anzeige
+ Globale Einstellungen
+ Debugging
+ Datenschutz
+ Netzwerk
+ Interaktion
+ Kontenliste
+ Nachrichtenliste
+ Nachrichten
+ Design
+ Nachrichten-Design
+ Editor-Design
+ Sprache
+ Keine Einstellungen gefunden
+ Festes Nachrichten-Design
+ Nachrichten-Design während des Ansehens der Nachricht auswählen
+ Immer das ausgewählte Nachrichten-Design verwenden
+ Systemvorgabe
+ Hintergrundsynchronisation
+ Nie
+ Immer
+ Wenn \'Automatische Synchronisation\' aktiviert ist
+ Alle auswählen
+ Maximale Push-Ordner-Anzahl
+ 5 Ordner
+ 10 Ordner
+ 25 Ordner
+ 50 Ordner
+ 100 Ordner
+ 250 Ordner
+ 500 Ordner
+ 1000 Ordner
+ Animationen
+ Aufwendige visuelle Effekte benutzen
+ Lauter/Leiser-Navigation
+ Nachrichtenansicht
+ Diverse Listenansichten
+ Gemeinsamen Posteingang anzeigen
+ Anzahl der Sterne anzeigen
+ Gemeinsamer Posteingang
+ Alle Nachrichten aus integrierten Ordnern
+ In gem. Posteingang integrieren
+ Nachrichten im gemeinsamen Posteingang anzeigen
+ Durchsuchbare Ordner
+ Alle
+ Angezeigte
+ Keine
+ Keine
+ Automatisch (%s)
+ Schriftgröße
+ Schriftgrößen festlegen
+ Kontenliste
+ Kontoname
+ Kontobeschreibung
+ Ordnerliste
+ Ordnername
+ Ordnerstatus
+ Nachrichtenliste
+ Betreff
+ Absender
+ Datum
+ Vorschau
+ Nachrichtenanzeige
+ Absender
+ Empfänger (To)
+ Empfänger (CC)
+ Blindkopie (BCC)
+ Zusätzliche Header
+ Betreff
+ Uhrzeit und Datum
+ Nachrichtentext
+ Nachricht verfassen
+ Texteingabefelder
+ Standard
+ Winzig
+ Sehr klein
+ Kleiner
+ Klein
+ Mittel
+ Groß
+ Größer
+ Keine geeignete Anwendung für diese Aktion gefunden.
+ Senden fehlgeschlagen: %s
+ Entwurf speichern?
+ Entwurf speichern oder verwerfen?
+ Änderungen speichern oder verwerfen?
+ Nachricht verwerfen?
+ Sind Sie sicher, dass Sie die Nachricht verwerfen möchten?
+ Text auswählen, um ihn zu kopieren.
+ Lokale Nachrichten löschen?
+ Dadurch werden alle lokalen Nachrichten aus dem Ordner entfernt. Es werden keine Nachrichten vom Server gelöscht.
+ Nachrichten löschen
+ Löschen bestätigen
+ Möchten Sie diese Nachricht löschen?
+
+ Möchten Sie diese Nachricht wirklich löschen?
+ Möchten Sie wirklich %1$d Nachrichten löschen?
+
+ Löschen
+ Nicht löschen
+ Alle als gelesen markieren bestätigen
+ Möchten Sie alle Nachrichten als gelesen markieren?
+ Leeren des Papierkorbs bestätigen
+ Möchten Sie den Papierkorb leeren?
+ Ja
+ Nein
+ Als Spam markieren
+
+ Möchten Sie diese Nachricht in den Spam-Ordner verschieben?
+ Möchten Sie diese Nachrichten in den Spam-Ordner verschieben?
+
+ Ja
+ Nein
+ Anhang wird heruntergeladen
+ »
+ ›
+ Backup
+ Verschiedenes
+ Einstellungen exportieren
+ Export
+ Teilen
+ Einstellungen werden exportiert…
+ Einstellungen erfolgreich exportiert
+ Einstellungen konnten nicht exportiert werden
+ Einstellungen importieren
+ Datei auswählen
+ Importieren
+ Einstellungen erfolgreich importiert
+
+ Bitte geben Sie die Passwörter ein
+
+ Bitte melden Sie sich an
+
+ Bitte melden Sie sich an und geben Sie Ihre Passwörter ein
+ Einstellungen konnten nicht importiert werden
+ Einstellungsdatei konnte nicht gelesen werden
+ Einige Einstellungen konnten nicht importiert werden
+ Erfolgreich importiert
+ Passwort erforderlich
+
+ Anmeldung erforderlich
+ Nicht importiert
+ Importfehler
+ Später
+ Einstellungen importieren
+ Einstellungen werden importiert…
+
+ Um das Konto \"%s\" nutzen zu können, müssen Sie das Serverpasswort eingeben.
+ Um das Konto \"%s\" nutzen zu können, müssen Sie die Serverpasswörter eingeben.
+
+ Passwort des Posteingangsservers
+ Passwort des Postausgangsservers
+ Gleiches Passwort für den Postausgangsserver verwenden
+ Servername: %s
+ Anzahl ungelesener Nachrichten anzeigen für…
+ Konto
+ Das Konto, für das der Zähler für ungelesene Nachrichten angezeigt werden soll
+ Gemeinsamer Posteingang
+ Ordneranzahl
+ Den Zähler für ungelesene Nachrichten eines einzelnen Ordners zeigen
+ Ordner
+ Die Ordnerliste, für die der Zähler für ungelesene Nachrichten angezeigt werden soll
+ Fertig
+ %1$s - %2$s
+ Kein Konto ausgewählt
+ Kein Ordner ausgewählt
+ Kein Nachrichtentext
+ Öffnen
+ Link teilen
+ Link kopieren
+ Link
+ Linktext kopieren
+ Linktext
+ Bild
+ Bild anzeigen
+ Bild speichern
+ Bild herunterladen
+ Bild-URL kopieren
+ Bild-URL
+ Anrufen
+ Im Adressbuch speichern
+ Telefonnummer kopieren
+ Telefonnummer
+ E-Mail senden
+ Im Adressbuch speichern
+ E-Mail-Adresse kopieren
+ E-Mail-Adresse
+ Alle
+ 10
+ 25
+ 50
+ 100
+ 250
+ 500
+ 1000
+ Maximale Resultat-Anzahl
+ Sende Anfrage an den Server
+
+ %d Resultat wird abgerufen
+ %d Resultate werden abgerufen
+
+
+ %1$d von %2$d Resultaten werden abgerufen
+ %1$d von %2$d Resultaten werden abgerufen
+
+ Suche fehlgeschlagen
+ Suche
+ Nachrichten auf Server suchen
+ Für die Suche auf dem Server ist eine Netzverbindung nötig.
+ Farbe ändern, wenn gelesen
+ Ein anderer Hintergrund zeigt, dass die Nachricht gelesen wurde
+ Nachrichten gruppieren
+ Nachrichten eines Diskussionsstranges zusammenfassen
+ Datenbankaktualisierung
+ Datenbanken werden aktualisiert…
+ Aktualisiere Datenbank für Konto \'%s\'
+ Geteilte Ansicht
+ Immer
+ Nie
+ Im Querformat
+ Nachricht aus der Liste auswählen, um sie anzuzeigen
+ Kontaktbilder anzeigen
+ Kontaktbilder in der Nachrichtenliste anzeigen
+ Alle als gelesen markieren
+ Kontaktbilder einfärben
+ Fehlende Kontaktbilder einfärben
+ Sichtbare Nachrichtenaktionen
+ Ausgewählte Aktionen im Menü der Nachrichtenanzeige anzeigen
+ Anhang wird geladen…
+ Nachricht senden
+ Entwurf speichern
+ Anhang wird heruntergeladen…
+ Authentifizierung fehlgeschlagen. Der Server bietet nicht Simple Authentication and Security Layer, kurz SASL, an. Dies könnte durch ein Problem des Client-Zertifikats (Zertifikat abgelaufen, unbekannte Zertifizierungsstelle) oder ein Konfigurationsproblem ausgelöst werden.
+
+ Client-Zertifikat verwenden
+ Kein Client-Zertifikat
+ Client-Zertifikatsauswahl entfernen
+ Das Abrufen des Client-Zertifikats für den Pfad \"%s\" ist fehlgeschlagen
+ Erweiterte Optionen
+ Client-Zertifikat \"%1$s\" ist abgelaufen oder noch nicht gültig (%2$s)
+
+ *Verschlüsselt*
+ Vom Adressbuch hinzufügen
+ CC
+ BCC
+ An
+ Von
+ Antworten an
+ <Unbekannter Empfänger>
+ <Unbekannter Absender>
+ Zuhause
+ Arbeit
+ Anderes
+ Mobil
+ Kein Ordner für Entwürfe für dieses Konto festgelegt!
+ Kein Schlüssel für dieses Konto konfiguriert! Überprüfen Sie die Einstellungen.
+ Verschlüsselungsdienst verwendet inkompatible Version. Bitte überprüfen Sie Ihre Einstellungen!
+ Verbindung zu Verschlüsselungsdienst fehlgeschlagen. Überprüfen Sie die Einstellungen oder tippen Sie auf das Verschlüsselungs-Symbol um es erneut zu versuchen.
+ Die Ende-zu-Ende Verschlüsselung konnte nicht initialisiert werden, bitte überprüfen Sie Ihre Einstellungen.
+ PGP/INLINE-Modus unterstützt keine Anhänge!
+ PGP/INLINE aktivieren
+ PGP/INLINE deaktivieren
+ PGP Nur-Signieren aktivieren
+ PGP Nur-Signieren deaktivieren
+ PGP/INLINE-Modus
+ Die E-Mail wurde im PGP/INLINE-Format gesendet.\nDies sollte nur für die Kompatibilität verwendet werden:
+ Manche Clients unterstützen nur dieses Format
+ Signaturen können während dem Versand kaputtgehen
+ Anhänge werden nicht unterstützt
+ Verstanden!
+ Deaktivieren
+ Aktiviert lassen
+ Verstanden!
+ Deaktivieren
+ Aktiviert lassen
+ PGP Nur-Signieren-Modus
+ In diesem Modus wird Ihr PGP-Schlüssel verwendet, um eine kryptografische Signatur einer unverschlüsselten E-Mail zu erstellen.
+ Dadurch wird die E-Mail nicht verschlüsselt, sondern es wird überprüft, ob sie von Ihrem Schlüssel gesendet wurde.
+ Signaturen können kaputtgehen, wenn sie an Mailinglisten gesendet werden.
+ Signaturen können als \'signature.asc\'-Anhänge in einigen Clients angezeigt werden.
+ Verschlüsselte Nachrichten enthalten immer eine Signatur.
+ Klartext
+ Ende-zu-Ende-Signatur enthielt einen Fehler
+ muss die Nachricht vollständig herunterladen, um die Signatur zu verarbeiten
+ enthält eine nicht unterstützte Ende-zu-Ende-Signatur
+ Nachricht ist verschlüsselt, aber in einem nicht unterstützten Format.
+ Nachricht ist verschlüsselt, aber Entschlüsselung wurde abgebrochen.
+ Ende-zu-Ende signierter Klartext
+ von verifiziertem Signierer
+ Signierter Klartext
+ aber der Ende-zu-Ende-Schlüssel stimmte nicht mit dem Absender überein
+ aber der Ende-zu-Ende-Schlüssel ist abgelaufen
+ aber der Ende-zu-Ende-Schlüssel wurde widerrufen
+ aber der Ende-zu-Ende-Schlüssel wird nicht als sicher angesehen.
+ von unbekanntem Ende-zu-Ende-Schlüssel
+ Verschlüsselt
+ aber es gab einen Entschlüsselungsfehler
+ muss die Nachricht zur Entschlüsselung vollständig herunterladen
+ aber keine Verschlüsselungs-App eingerichtet
+ Verschlüsselt
+ aber nicht Ende-zu-Ende
+ Ende-zu-Ende verschlüsselt
+ von verifiziertem Absender
+ Verschlüsselt
+ von unbekanntem Ende-zu-Ende-Schlüssel
+ aber der Ende-zu-Ende-Schlüssel stimmte nicht mit dem Absender überein
+ aber der Ende-zu-Ende-Schlüssel ist abgelaufen
+ aber der Ende-zu-Ende-Schlüssel wurde widerrufen
+ aber der Ende-zu-Ende-Schlüssel wird nicht als sicher angesehen
+ aber die Ende-zu-Ende Daten sind fehlerhaft
+ aber die Verschlüsselung wird nicht als sicher angesehen
+ OK
+ Schlüssel suchen
+ Signierer anzeigen
+ Absender anzeigen
+ Details
+ Entsperren
+ Dieser Teil wurde nicht verschlüsselt und könnte unsicher sein.
+ Ungeschützter Anhang
+ Ladevorgang…
+ Entschlüsselung abgebrochen.
+ Wiederholen
+ Verschlüsselte Nachricht muss für die Entschlüsselung heruntergeladen werden.
+ Fehler beim Entschlüsseln der E-Mail
+ Sonderzeichen werden derzeit nicht unterstützt!
+ Fehler beim Einlesen der Adresse!
+ Unverschlüsselte Signaturen ausblenden
+ Nur verschlüsselte Signaturen werden angezeigt
+ Alle Signaturen werden angezeigt
+ Verschlüsselung ist im Nur-Signieren-Modus nicht verfügbar!
+ Unsignierter Text
+ Diese E-Mail ist verschlüsselt
+ Diese E-Mail ist mit OpenPGP verschlüsselt.
+Um sie zu lesen, muss eine kompatible OpenPGP-App installiert und konfiguriert werden.
+ Zu den Einstellungen
+ K-9 Nachrichtenliste
+ Nachrichten werden geladen…
+ Verschlüsselung nicht möglich
+ Einige der ausgewählten Empfänger unterstützen diese Funktion nicht!
+ Verschlüsselung aktivieren
+ Verschlüsselung deaktivieren
+ Durch die Verschlüsselung von Nachrichten wird sichergestellt, dass sie für den Empfänger und sonst niemanden lesbar sind.
+ Die Verschlüsselung wird nur angezeigt, wenn sie von allen Empfängern unterstützt wird und diese Ihnen zuvor schon eine E-Mail gesendet haben.
+ Schalten Sie die Verschlüsselung um, indem Sie auf dieses Symbol klicken.
+ Verstanden
+ Zurück
+ Verschlüsselung deaktivieren
+ OpenPGP-Verschlüsselung
+ Autocrypt wechselseitiger Modus
+ Autocrypt wechselseitiger Modus
+ Nachrichten werden normalerweise auf Wunsch oder beim Antworten auf eine verschlüsselte Nachricht verschlüsselt.
+ Wenn sowohl Sender als auch Empfänger den wechselseitigen Modus aktiviert haben, wird die Verschlüsselung automatisch aktiviert.
+ Sie können hier klicken , um mehr zu erfahren.
+ Allgemeine Einstellungen
+ Keine OpenPGP-App installiert
+ Installieren
+ Mail benötigt OpenKeychain für die Ende-zu-Ende Verschlüsselung.
+ Verschlüsselte Nachricht
+ Betreff der Nachricht verschlüsseln
+ Möglicherweise nicht von allen Empfängern unterstützt
+ Interner Fehler: Ungültiges Konto!
+ Fehler beim Verbinden mit %s!
+ Autocrypt-Setup-Nachricht senden
+ Das Ende-zu-Ende-Setup auf sichere Art mit anderen Geräten teilen
+ Autocrypt-Setup-Nachricht
+ Eine Autocrypt-Setup-Nachricht teilt Ihr Ende-zu-Ende-Setup auf sichere Art und Weise mit anderen Geräten
+ Setup-Nachricht senden
+ Diese Nachricht wird an Ihre Adresse gesendet:
+ Erstelle Setup-Nachricht…
+ Sende Nachricht an:
+ Um abzuschließen, öffnen Sie die Nachricht auf einem anderen Gerät und geben Sie den Setup-Code ein.
+ Setup-Code anzeigen
+ Autocrypt-Setup-Nachricht
+ Diese Nachricht enthält alle Informationen, um Ihre Autocrypt-Einstellungen zusammen mit Ihrem geheimen Schlüssel von Ihrem ursprünglichen Gerät sicher zu übertragen.
Um Ihr neues Gerät für Autocrypt einzurichten, folgen Sie bitte den Anweisungen, die auf dem neuen Gerät angezeigt werden.
Sie können diese Nachricht aufheben und sie als Backup für Ihren geheimen Schlüssel nutzen. Wenn Sie das tun möchten, sollten Sie sich das Passwort notieren und sicher verwahren.
- Beim Senden der Nachricht trat ein Fehler auf. Bitte überprüfen Sie die Netzwerkverbindung und die Konfiguration ausgehender Server.
- An
- Aus
- Öffnen
- Schließen
-
- Zugriff auf Kontakte erlauben
- Damit die App Kontakte vorschlagen sowie Namen und Fotos der Kontakte anzeigen kann, benötigt sie Zugriff auf Ihre Kontakte.
- Beim Laden der Daten ist ein Fehler aufgetreten
- Initialisieren …
- Warte auf neue E-Mails
- Wartet, bis die Hintergrundsynchronisation erlaubt ist
- Wartet, bis ein Netzwerk verfügbar ist
- Antippen, um mehr zu erfahren.
- Push-Information
- Bei der Verwendung von Push hält K-9 Mail eine Verbindung zum E-Mail-Server aufrecht. Bei Android muss eine laufende Benachrichtigung angezeigt werden, während die App im Hintergrund aktiv ist. %s
- Allerdings erlaubt Android auch, dass die Benachrichtigung ausgeblendet wird.
- Mehr erfahren
- Benachrichtigung einstellen
- Wenn du keine sofortigen Benachrichtigungen über neue Nachrichten benötigst, solltest du Push deaktivieren und die Abfrage verwenden. Die Abfrage prüft in regelmäßigen Abständen auf neue Mails und benötigt die Benachrichtigung nicht.
- Push deaktivieren
- Antworten an:
- Kopieren nach …
- E-Mails von allen Konten abrufen
- Neue Nachrichten
- Export ist fehlgeschlagen.
- E-Mail-Aussendungsklang
- Einen Ton abspielen, wenn ein E-Mail erfolgreich gesendet wurde
- Beweise deine Identität
- Entsperre, um dein Passwort zu sehen
- Benachrichtigungskategorien
- Benachrichtigungen für neue Nachrichten konfigurieren
- Fehler- und Statusbenachrichtigungen konfigurieren
- Verschieben nach …
- Zertifikats-Fehler
- Logdateien exportieren
- Der Export war erfolgreich. Logdateien können sensible Daten enthalten. Achte darauf, wem du sie sendest.
- Um dein Passwort hier zu sehen, aktiviere die Bildschirmsperre auf deinem Gerät.
- XOauth2 (Gmail, Outlook)
- Alle 5 Minuten
- Chat-Nachrichten ignorieren
- Zeige keine Benachrichtigungen für Nachrichten, die Teil eines E-Mail-Chats sind
- Favoritenanzahl anzeigen
- Autoren
- Versionsnummer
- Information über E-Mail
- Lizenz
- Quellcode
- - E-Mail ist ein quelloffener E-Mail-Client für Android.
- E-Mail ist eine Abänderung von K9-Mail
- Nutzungsbedingungen
- Datenschutz-Bestimmungen
- Benachrichtigungslicht
- Deaktiviert
- Konto-Farbe
- Weiß
- Rot
- Grün
- Blau
- Gelb
- Blaugrün
- Fuchsia
- Kontaktnamen-Farbe
- Vibration
- Deaktiviert
- Standardfarbe des Systems
- LÖSCHEN
- BEARBEITEN
+ Beim Senden der Nachricht trat ein Fehler auf. Bitte überprüfen Sie die Netzwerkverbindung und die Konfiguration ausgehender Server.
+ An
+ Aus
+
+ Zugriff auf Kontakte erlauben
+ Damit die App Kontakte vorschlagen sowie Namen und Fotos der Kontakte anzeigen kann, benötigt sie Zugriff auf Ihre Kontakte.
+ Beim Laden der Daten ist ein Fehler aufgetreten
+ Initialisieren…
+ Warte auf neue E-Mails
+ Wartet, bis die Hintergrundsynchronisation erlaubt ist
+ Wartet, bis ein Netzwerk verfügbar ist
+ Antippen, um mehr zu erfahren.
+ Push-Information
+ Bei der Verwendung von Push hält Mail eine Verbindung zum E-Mail-Server aufrecht. Bei Android muss eine laufende Benachrichtigung angezeigt werden, während die App im Hintergrund aktiv ist. %s
+ Allerdings erlaubt Android auch, dass die Benachrichtigung ausgeblendet wird.
+ Mehr erfahren
+ Benachrichtigung einstellen
+ Wenn Sie keine sofortigen Benachrichtigungen über neue Nachrichten benötigst, sollten Sie Push deaktivieren und die Abfrage verwenden. Die Abfrage prüft in regelmäßigen Abständen auf neue E-Mails und benötigt die Benachrichtigung nicht.
+ Push deaktivieren
\ No newline at end of file
diff --git a/app/ui/legacy/src/main/res/values-el/strings.xml b/app/ui/legacy/src/main/res/values-el/strings.xml
index f95d30b9eb6265607495b55fe57582c32662a2cd..bc0b8d121b8dbaad2a1826bb2289c39d4df24541 100644
--- a/app/ui/legacy/src/main/res/values-el/strings.xml
+++ b/app/ui/legacy/src/main/res/values-el/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
- Πνευματικά δικαιώματα 2008-%s από The K-9 Dog Walkers. Τμηματικά πνευματικά δικαιώματα 2006-%s από το Android Open Source Project.
+ The Mail Dog WalkersΠηγαίος κώδικαςΆδεια χρήσης Apache, έκδοση 2.0.Έργο Ανοιχτού Κώδικα
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.Συντελεστές
- Πληροφορίες έκδοσηςΑνάγνωση μηνυμάτωνΕπιτρέπει στην εφαρμογή να διαβάζει την ηλεκτρονική σας αλληλογραφία.Διαγραφή μηνυμάτωνΕπιτρέπει στην εφαρμογή να διαγράφει την ηλεκτρονική σας αλληλογραφία.About MailΛογαριασμοί
- ΦάκελοιΠροχωρημέναΣύνθεσηΑπάντηση
@@ -49,12 +46,6 @@
Προώθηση ως συνημμένοΕπιλογή ΛογαριασμούΕπιλογή φακέλου
- Έλεγχος %s:%s%s
- Μετάκληση επικεφαλίδων %s:%s%s
- Αποστολή %s%s
- Επεξεργασία %s:%s%s
- \u0020%d/%d
- Συγχρονισμός απενεργοποιημένος%d επιλέχθηκανΕπόμενοΠροηγούμενο
@@ -62,10 +53,7 @@
OKΆκυροΑποστολή
- Αποστολή πάλιΤο θέμα είναι κενό, πατήστε επανάληψη για αποστολή
- Επιλογή
- Απο-επιλογήΑπάντησηΑπάντηση σε όλουςΔιαγραφή
@@ -130,8 +118,6 @@
%.1f MB%.1f kB%d B
- Ο λογαριασμός «%s» συρρικνώθηκε από %s σε %s
- Συμπίεση λογαριασμού «%s»Νέο μήνυμα%d νέο μήνυμα
@@ -199,20 +185,14 @@
Προς:Κοιν:Κρυφή Κοιν:
- Άνοιγμα
- ΑποθήκευσηΑδυναμία αποθήκευσης συνημμένου.Προβολή εικόνωνΔεν υπάρχει πρόγραμμα προβολής για %s.Λήψη πλήρους μηνύματος
- Αδυναμία προβολής μηνύματοςμέσω %1$s
- Όλες οι επικεφαλίδες λήφθηκαν, αλλά δεν υπάρχουν επιπλέον επικεφαλίδες για εμφάνιση.
- Απέτυχε η ανάσυρση πρόσθετων επικεφαλίδων από τη βάση δεδομένων ή από τον εξυπηρετητή ταχυδρομείου.Από τον ίδιο αποστολέαΑπό %s
- Αποσφαλμάτωση / Απαλοιφή του σώματος του μηνύματοςΤο μήνυμα αγνοήθηκε.Το μήνυμα αποθηκεύτηκε στα πρόχειρα.Προβολή αστεριών
@@ -243,13 +223,9 @@
Μαρκάρισμα όλων των μηνυμάτων ως αναγνωσμένωνΔιαγραφή (από ειδοποίηση)Απόκρυψη προγράμματος-πελάτη αλληλογραφίας
- Αφαίρεση του Πράκτορα-χρήστη K-9 από την επικεφαλίδα του μηνύματος
+ Αφαίρεση του Πράκτορα-χρήστη Mail από την επικεφαλίδα του μηνύματοςΑπόκρυψη ζώνης ώραςΧρήση της Συντονισμένης Παγκόσμιας Ώρας έναντι της τοπικής ζώνης ώρας, στις επικεφαλίδες αλληλογραφίας και απάντησης
- Απόκρυψη θέματος στις ειδοποιήσεις
- Ποτέ
- Όταν η συσκευή είναι κλειδωμένη
- ΠάντοτεΕμφάνιση κουμπιού «Διαγραφή»ΠοτέΓια ειδοποίηση ενός μηνύματος
@@ -269,7 +245,12 @@
Ρύθμιση νέου λογαριασμούΔιεύθυνση ηλ. ταχυδρομείουΣυνθηματικό
- Εμφάνιση κωδικού πρόσβασης
+
+
+
+ Για να δείτε τον κωδικό σας εδώ, ενεργοποιήστε το κλείδωμα οθόνης σε αυτή την συσκευή.
+ Επαλήθευση ταυτότητας
+ Ξεκλειδώστε για να δείτε τον κωδικό σαςΧειροκίνητη ρύθμισηΑνάκτηση πληροφοριών λογαριασμού\u2026
@@ -307,12 +288,6 @@
Να μη διαγράφεται στον εξυπηρετητήΝα διαγράφεται άμεσα από τον εξυπηρετητήΕπισήμανση ως αναγνωσμένο στον εξυπηρετητή
- Συμπίεση στο δίκτυο
- Κινητής
- Ασύρματης Wi-Fi
- Άλλο
- Εξωτερική αποθήκευση (SD card)
- Κανονική εσωτερική αποθήκευσηΕξάλειψη διαγραμμένων μηνυμάτωνΑμέσωςΚατά την ενημέρωση
@@ -327,11 +302,8 @@
Προβολή μόνον φακέλων υπό παρακολούθησηΑυτόματη επέκταση φακέλουΜονοπάτι OWA
- ΠροαιρετικόΜονοπάτι πιστοποίησης
- ΠροαιρετικόΨευδώνυμο γραμματοκιβωτίου
- ΠροαιρετικόΡυθμίσεις εξερχόμενης αλληλογραφίαςΕξυπηρέτης SMTPΘύρα
@@ -343,7 +315,6 @@
Το «%1$s = %2$s» δεν είναι έγκυρο με «%3$s = %4$s»Άκυρη ρύθμιση: %sΕπιλογές λογαριασμού
- ΣυμπύκνωσηΣυχνότητα ενημέρωσης φακέλωνΠοτέΚάθε 15 λεπτά
@@ -354,8 +325,6 @@
Κάθε 2 ώρεςΚάθε 12 ώρεςΚάθε 24 ώρες
- Ενεργοποίηση σπρωξίματος για αυτό το λογαριασμό
- Αν το υποστηρίζει ο εξυπηρετητής σας, θα εμφανιστούν αμέσως τα νέα μηνύματα. Αυτή η επιλογή μπορεί, είτε να βελτιώσει δραματικά είτε να βλάψει δραματικά τις επιδόσεις.Ανανέωση της σύνδεσης IDLEΚάθε 2 λεπτάΚάθε 3 λεπτά
@@ -366,7 +335,6 @@
Κάθε 48 λεπτάΚάθε 60 λεπτάΕιδοποίηση κατά την άφιξη μηνύματος
- Ειδοποίηση όσο ελέγχεται το γραμματοκιβώτιοΠλήθος μηνυμάτων που προβάλλονται10 μηνύματα25 μηνύματα
@@ -388,8 +356,6 @@
ΣυνέχειαΠροχωρημέναΡυθμίσεις λογαριασμού
- Εξ ορισμού λογαριασμός
- Αποστολή ταχυδρομείου από αυτό το λογαριασμό εξ ορισμούΕιδοποιήσεις νέων μηνυμάτωνΦάκελοι ειδοποιήσεωνΌλοι
@@ -411,8 +377,6 @@
Σημείωση ως αναγνωσμένου με την προβολήΣήμανση ως αναγνωσμένο κατά τη διαγραφήΝα γίνεται σήμανση του μηνύματος ως αναγνωσμένο όταν διαγράφεται
- Ρυθμίσεις ειδοποιήσεων
- Άνοιγμα ρυθμίσεων ειδοποιήσεων συστήματοςΠροβολή εικόνων πάνταΌχιΑπό τις Επαφές
@@ -454,9 +418,6 @@
Συχνότητα ενημέρωσης φακέλωνΧρώμα λογαριασμούΕπιλογή χρώματος για χρήση στους φακέλους και τη λίστα των λογαριασμών
- Χωρίς χρώμα
- Χρώμα ειδοποίησης LED
- Επιλογή του χρώματος που θα αναβοσβήνει η λυχνία του κινητού για αυτό το λογαριασμόΜέγεθος τοπικού φακέλουΜετάκληση μηνυμάτων μέχρι1 KiB
@@ -543,7 +504,6 @@
Το όνομά σουΕιδοποιήσειςΔόνηση
- Δόνηση όταν φτάνει μήνυμαΤρόπος δόνησηςΕξ ορισμούΤύπος 1
@@ -664,10 +624,6 @@
Προβολή μηνύματοςΔιάφορες προβολές λίσταςΕμφάνιση Ενιαία Εισερχόμενα
- %s%s
- - Μη αναγνωσμένα
- Όλα τα μηνύματα
- Όλα τα μηνύματα σε φακέλους αναζήτησηςΕνιαία ΕισερχόμεναΌλα τα μηνύματα στους ενοποιημένους φακέλουςΕνιαία Εισερχόμενα
@@ -700,8 +656,6 @@
ΘέμαΏρα και ΗμερομηνίαΣώμα μηνύματος
- %d%%
- %1$s: %2$sΣύνθεση μηνύματοςΚείμενο πεδίων εισόδουΕξ ορισμού
@@ -759,12 +713,16 @@
Επιλογή αρχείουΕισαγωγήΟι ρυθμίσεις έχουν εισαχθεί με επιτυχία
+
Παρακαλώ εισάγετε συνθηματικά
+
+
Αποτυχία εισαγωγής ρυθμίσεωνΑποτυχία ανάγνωσης αρχείου ρυθμίσεωνΑποτυχία εισαγωγής ορισμένων ρυθμίσεωνΗ εισαγωγή ολοκληρώθηκε με επιτυχίαΑπαιτείται κωδικός πρόσβασης
+
Δεν έχει εισαχθείΑποτυχία εισαγωγήςΑργότερα
@@ -954,21 +912,11 @@
Θα εμφανίζονται όλες οι υπογραφέςΗ κρυπτογράφηση δεν είναι διαθέσιμη στη λειτουργία υπογραφής μόνο!Ανυπόγραφο κείμενο
- Προειδοποίηση απόρριψης APG
- Το APG δεν διατηρείται πλέον!
- Εξαιτίας αυτού, η υποστήριξη για APG έχει αφαιρεθεί από το K-9 Mail.
- Η ανάπτυξη σταμάτησε στις αρχές του 2014
- Περιέχει μη λυμένα θέματα ασφάλειας
- Μπορείτε να πατήσετε εδώ για να μάθετε περισσότερα.
- Το \'πιασα!
- APGΑυτό το μήνυμα είναι κρυπτογραφημένοΑυτό το μήνυμα έχει κρυπτογραφηθεί με το OpenPGP.\nΓια να το διαβάσετε, πρέπει να εγκαταστήσετε και να ρυθμίσετε τις παραμέτρους μιας συμβατής εφαρμογής OpenPGP.Μετάβαση στις Ρυθμίσεις
- Λίστα Μηνυμάτων του K-9
+ Λίστα Μηνυμάτων του MailΦόρτωση μηνυμάτων...
- Η λήψη της λίστας φακέλων απέτυχε
- Παρουσιάστηκε σφάλμα κατά την ανάκτηση της κατάστασης του παραλήπτη από την υπηρεσία παροχής OpenPGP!Δεν είναι δυνατή η κρυπτογράφησηΟρισμένοι από τους επιλεγμένους παραλήπτες δεν υποστηρίζουν αυτήν τη δυνατότητα!Ενεργοποίηση Κρυπτογράφησης
@@ -988,7 +936,7 @@
Γενικές ρυθμίσειςΔεν υπάρχει εγκατεστημένη εφαρμογή OpenPGPΕγκατάσταση
- Το K-9 Mail χρειάζεται το OpenKeychain για διατερματική κρυπτογράφηση.
+ Το Mail χρειάζεται το OpenKeychain για διατερματική κρυπτογράφηση.Κρυπτογραφημένο μήνυμαΚρυπτογράφηση θεμάτων των μηνυμάτων Ενδέχεται να μην υποστηρίζεται από ορισμένους παραλήπτες
@@ -1014,8 +962,6 @@
Προέκυψε ένα σφάλμα κατά την αποστολή του μηνύματος. Παρακαλώ ελέγξτε τη συνδεσιμότητα του δικτύου σας και τη διαμόρφωση του εξερχόμενου εξυπηρετητή.ΕνεργόΑνενεργό
- Άνοιγμα
- ΚλείσιμοΝα επιτρέπεται η πρόσβαση στις επαφέςΓια να είναι δυνατή η παροχή προτάσεων επαφών και η εμφάνιση των ονομάτων και φωτογραφιών των επαφών, η εφαρμογή χρειάζεται πρόσβαση στις επαφές σας.
@@ -1026,7 +972,7 @@
Σε αδράνεια μέχρι να υπάρξει διαθέσιμο δίκτυοΠατήστε για να μάθετε περισσότεραΠληροφορίες σπρωξίματος
- Όταν χρησιμοποιείται το σπρώξιμο (Push), το K-9 Mail διατηρεί μια σύνδεση με τον εξυπηρετητή ηλεκτρονικού ταχυδρομείου (mail server). Το Android απαιτεί να προβάλλεται συνεχώς μια ειδοποίηση όσο η εφαρμογή λειτουργεί στο παρασκήνιο. %s
+ Όταν χρησιμοποιείται το σπρώξιμο (Push), το Mail διατηρεί μια σύνδεση με τον εξυπηρετητή ηλεκτρονικού ταχυδρομείου (mail server). Το Android απαιτεί να προβάλλεται συνεχώς μια ειδοποίηση όσο η εφαρμογή λειτουργεί στο παρασκήνιο. %sΠαρόλα αυτά, το Android σάς επιτρέπει να κρύψετε την ειδοποίηση.Μάθετε περισσότεραΠροσαρμογή ειδοποίησης
diff --git a/app/ui/legacy/src/main/res/values-en-rGB/strings.xml b/app/ui/legacy/src/main/res/values-en-rGB/strings.xml
index 6a72e4a304ca3cfe3cb4180c25e3d797ac76bdbf..acae234b02f2986f6f3bc4a67ae313260820aa11 100644
--- a/app/ui/legacy/src/main/res/values-en-rGB/strings.xml
+++ b/app/ui/legacy/src/main/res/values-en-rGB/strings.xml
@@ -15,6 +15,8 @@
Colourise contactsColourise names in your contact list
+
+
Cannot copy or move a message that is not synchronised with the server
@@ -22,6 +24,10 @@
The accent colour of this account used in folder and account listAccount colourUnrecognised Certificate
+
+
+
+
Colourise contact picturesColourise missing contact pictures
diff --git a/app/ui/legacy/src/main/res/values-eo/strings.xml b/app/ui/legacy/src/main/res/values-eo/strings.xml
index d38df1d5b38d9b99d162caeb15919af0fb5dacec..c2463a1e8c55f0e8f5b446672b96ef8d08c6ad77 100644
--- a/app/ui/legacy/src/main/res/values-eo/strings.xml
+++ b/app/ui/legacy/src/main/res/values-eo/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- Programistoj de K-9
+ Programistoj de MailFontkodoApache License, versio 2.0Malfermkoda projekto
@@ -245,6 +245,8 @@
Aldoni novan kontonRetpoŝtadresoPasvorto
+
+
Por vidigi vian pasvorton tie ĉi, aktivigu la ŝlos-ekranon ĉe via aparato.Verigi vian identigon
@@ -286,10 +288,6 @@
Ne forigi ĝin en serviloForigi ĝin en serviloMarki ĝin kiel legitan en servilo
- Uzi kompaktigon je reto:
- Movtelefona
- Vifio
- AliaForviŝi forigitajn mesaĝojn el serviloSenprokrasteJe ĉiu elŝutado
@@ -713,12 +711,16 @@
Elekti dosieronEnportiSukcese enportis agordojn
+
Bonvolu entajpi pasvortojn
+
+
Fiaskis enporti agordojnFiaskis legi dosieron kun agordojFiaskis enporti iujn agordojnSukcesis enportiPostulas pasvorton
+
Ne enportitaFiaskis enportiPoste
@@ -909,14 +911,6 @@
Ĉiuj subskriboj montriĝosĈifrado ne eblas en nur-subskriba reĝimo!Nesubskribita teksto
- Averto: APG evitindas
- APG ne estas plu disvolvata!
- Tial subteno por APG estis forigita el K-9 Retpoŝtilo.
- Disvolvado haltis je komenco de 2014
- Enhavas neriparitajn sekurec-erarojn
- Vi povas klaki tie ĉi por sciiĝi pli (en la angla).
- Mi komprenas!
- APGTiu ĉi retletero estas ĉifritaTiu ĉi retletero estas ĉifrita per OpenPGP.\nPor legi ĝin, vi devas instali kaj agordi kongruan OpenPGP-aplikaĵon.Iri al agordoj
diff --git a/app/ui/legacy/src/main/res/values-es/strings.xml b/app/ui/legacy/src/main/res/values-es/strings.xml
index 45302e00ad69240421ef8101002365a984f98cec..4a07643c351bb2fd35c900e9c40064ef5458d850 100644
--- a/app/ui/legacy/src/main/res/values-es/strings.xml
+++ b/app/ui/legacy/src/main/res/values-es/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- Los paseadores del perro K-9
- Copyright 2008-%s The K-9 Dog Walkers. Partes del Copyright 2006-%s Android Open Source Project.
+ Los paseadores del perro MailCódigo fuenteApache License, Version 2.0Proyecto de Código Abierto
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.Autores
- Información de la revisiónLeer correosPermitir a esta aplicación leer los correos.Borrar correosPermitir a esta aplicación borrar los correos.About MailCuentas
- CarpetasAvanzadoRedactarResponder
@@ -49,12 +46,6 @@
Reenviar como adjuntoElija cuentaSeleccionar carpeta
- Comprobando %s:%s%s
- Recuperando cabeceras %s:%s%s
- Enviando %s%s
- Proc %s:%s%s
- \u0020%d/%d
- Sincronización deshabilitada%d seleccionadosSiguienteAnterior
@@ -62,10 +53,7 @@
AceptarCancelarEnviar
- Volver a enviarEl asunto está vacío, haga clic de nuevo para enviarlo tal cual
- Seleccionar
- DeseleccionarResponderResponder a todosBorrar
@@ -100,10 +88,12 @@
Añadir estrellaBorrar estrellaCopiar
+ Cancelar suscripciónMostrar cabecerasSe ha copiado la dirección al portapapeles
- Se han copiado las direcciones al portapeles
+ Se han copiado las direcciones al portapapeles
+ Se han copiado las direcciones al portapapelesTexto copiado en el portapapelesCambiar a tema oscuro
@@ -129,11 +119,10 @@
%.1f MB%.1f kB%d B
- Cuenta \"%s\" reducida desde %s a %s
- Compactando cuenta \"%s\"Correo nuevo%d mensaje nuevo
+ %d mensajes nuevos%d mensajes nuevos%d Sin Leer (%s)
@@ -198,20 +187,14 @@
Para:Cc:Cco:
- Abrir
- GuardarNo se puede guardar el adjunto.Mostrar imágenesImposible encontrar visor de %s.Descargar mensaje completo
- No se puede mostrar el mensajevía %1$s
- Se han descargado todas las cabeceras, pero no hay cabeceras adicionales que mostrar.
- Se ha producido un error al descargar las cabeceras desde la base de datos o el servidor.Más de este remitenteDesde %s
- Depurar / Limpiar el cuerpo del mensajeMensaje descartadoMensaje guardado como borradorMostrar estrellas
@@ -243,13 +226,9 @@
Marcar todos los mensajes como leídosBorrar (de notificaciones)Ocultar cliente de correo
- Eliminar el Agente de Usuario K-9 de las cabeceras del mensaje
+ Eliminar el Agente de Usuario Mail de las cabeceras del mensajeOcultar zona horariaUsar UTC en lugar de la zona horaria local en las cabeceras de mensaje y cabeceras de respuesta
- Ocultar asunto en notificaciones
- Nunca
- Cuando el dispositivo está bloqueado
- SiempreMostrar el botón \'Borrar\'NuncaPara notificaciones de un mensaje
@@ -269,7 +248,15 @@
Configurar nueva cuentaDirección de correoContraseña
- Mostrar la contraseña
+ Para poder utilizar esta cuenta de correo con Mail es necesario que inicies sesión y propones los permisos necesarios de acceso.
+
+ Iniciar sesión
+
+ Iniciar sesión con Google
+
+ Para ver tu contraseña, activa la pantalla de bloqueo en tu dispositivo.
+ Verifica tu identidad
+ Desbloquea para ver tu contraseñaConfiguración manualRecuperando información de cuenta\u2026
@@ -289,6 +276,7 @@
Contraseña, transmitida de manera no seguraContraseña cifradaCertificado de cliente
+ OAuth 2.0Configuración del servidor de entradaUsuarioContraseña
@@ -307,12 +295,7 @@
No borrar del servidorBorrar del servidorMarcar como leído en servidor
- Utilizar compresión en la red:
- Móvil
- Wi-Fi
- Otras
- Almacenamiento externo (tarjeta SD)
- Almacenamiento interno
+ Con compresiónEliminar mensajes borrados en el servidorInmediatamenteDurante comprobación
@@ -327,11 +310,8 @@
Ver sólo carpetas suscritasAutoexpandir carpetaDirección WebDAV (Exchange)
- OpcionalDirección de autentificación
- OpcionalAlias del buzón
- OpcionalCorreo salienteServidor SMTPPuerto
@@ -343,7 +323,6 @@
\"%1$s = %2$s\" no es válido con \"%3$s = %4$s\"Configuración inválida: %sOpciones de cuenta
- CompactarFrecuencia de comprobación de correo nuevoNuncaCada 15 minutos
@@ -354,8 +333,6 @@
Cada 6 horasCada 12 horasCada 24 horas
- Activar push para esta cuenta
- Si su servidor lo permite, los mensajes aparecerán de forma instantánea. Esta opción puede ralentizar el sistema.Refrescar conexión IDLECada 2 minutosCada 3 minutos
@@ -366,7 +343,6 @@
Cada 48 minutosCada 60 minutosAvisarme cuando lleguen nuevos mensajes
- Avisarme cuando se esté comprobando el correoNúmero de mensajes a mostrar10 mensajes25 mensajes
@@ -384,12 +360,14 @@
Usuario o contraseña incorrectos.\n(%s)El servidor presentó un certificado SSL no válido. Algunas veces, esto es debido a un problema de configuración en el servidor. Otras veces es porque alguien está intentando atacarle o atacar su servidor de correo. Si no está seguro de qué ocurre, pulse en Rechazar y contacte con quien gestiona su servidor de correo.\n\n(%s)No se pudo conectar con el servidor.\n(%s)
+ See ha cancelado la autorización
+ La autorización ha fallado con el error: %s
+ El servidor no parece que sea compatible con OAuth 2.0.
+ La aplicación no ha encontrado ningún navegador que se pueda utilizar para iniciar sesión en tu cuenta.Editar detallesContinuarAvanzadoConfiguración de cuenta
- Cuenta predeterminada
- Enviar mensajes desde esta cuenta de manera predeterminadaNotificar mensajes nuevosCarpetas de notificacionesTodas
@@ -411,8 +389,6 @@
Marcar mensaje como leído cuando se abre para verloMarcar como leído al borrarMarcar el mensaje como leído cuando se borra
- Configuración de las notificaciones
- Abrir opciones de notificación del sistema.Mostrar imágenes siempreNoSólo de mis contactos
@@ -454,9 +430,6 @@
Frecuencia de comprobaciónColor de la cuentaSeleccione el color a utilizar en la carpeta y en la cuenta
- Sin color
- Color de aviso
- Seleccione el color del LED para los avisos de esta cuentaTamaño carpeta localObtener mensajes de hasta1 KiB
@@ -543,7 +516,6 @@
NotificacionesVibraciónVibrar
- Vibrar cuando llega correoSecuencia de vibraciónPredeterminadoSecuencia 1
@@ -675,10 +647,6 @@
Vista de mensajeListas de mensajesMostrar bandeja de entrada unificada
- %s%s
- - No leído
- Todos los mensajes
- Mensajes en las carpetas de búsquedaEntrada unificadaTodos los mensajes en la Entrada unificadaUnificar
@@ -711,8 +679,6 @@
AsuntoHora y FechaCuerpo de mensaje
- %d%%
- %1$s: %2$sComposición mensajeCampos entrada textoPredeterminado
@@ -737,8 +703,9 @@
Confirmar borrado¿Quiere borrar este mensaje?
- ¿Realmente desea borrar este mensaje?
- ¿Realmente desea borrar %1$d mensajes?
+ ¿Seguro que quieres borrar este mensaje?
+ ¿Seguro que quieres borrar %1$d mensajes?
+ ¿Seguro que quieres borrar %1$d mensajes?SíNo
@@ -750,8 +717,9 @@
NoConfirmar mover a la carpeta Spam
- ¿Quiere mover este mensaje a la carpeta Spam?
- ¿Quiere realmente mover %1$d mensajes a la carpeta Spam?
+ ¿Seguro que quieres mover este mensaje a la carpeta de Spam?
+ ¿Seguro que quieres mover %1$d mensajes a la carpeta de Spam?
+ ¿Seguro que quieres mover %1$d mensajes a la carpeta de Spam?SíNo
@@ -771,19 +739,26 @@
ImportarLos ajustes se han importado correctamentePor favor, introduzca contraseñas
+
+ Iniciar sesión
+
+ Inicia sesión escribiendo tu contraseñaLa importación de ajustes ha falladoFallo al leer el archivo de ajustesLa importación de algunos ajustes ha falladoImportado correctamenteSe requiere contraseña
+
+ Es necesario iniciar sesiónNo importadoFallo en la importaciónMás tardeImportar ajustesImportando ajustes…
- Para poder usar la cuenta \"%s\" necesita proporcionar la contraseña del servidor.
- Para poder usar la cuenta \"%s\" necesita proporcionar las contraseñas del servidor.
+ Para poder usar la cuenta \"%s\" es necesario proporcionar la contraseña del servidor.
+ Para poder usar la cuenta \"%s\" es necesario proporcionar las contraseñas del servidor.
+ Para poder usar la cuenta \"%s\" es necesario proporcionar las contraseñas del servidor.Contraseña del servidor de correo entranteContraseña del servidor de correo saliente
@@ -833,12 +808,14 @@
Limitar resultados en búsquedas en el servidorEnviar consulta al servidor
- Obteniendo %d resultado
- Obteniendo %d resultados
+ Obteniendo %d elemento
+ Obteniendo %d elementos
+ Obteniendo %d elementos
- Obteniendo resultado %1$d de %2$d
- Obteniendo resultado %1$d de %2$d
+ Obteniendo elemento %1$d de %2$d
+ Obteniendo elemento %1$d de %2$d
+ Obteniendo elemento %1$d de %2$dLa búsqueda en remoto fallóBuscar
@@ -966,21 +943,11 @@
Se mostrarán todas las firmas¡El cifrado no está disponible en el modo de solo firma!Texto sin firmar
- Aviso sobre desuso de APG
- ¡APG ya no se mantiene!
- Debido a esto, el soporte para APG se ha eliminado de K-9 Mail.
- El desarrollo se detuvo a principios de 2014
- Contiene problemas de seguridad sin arreglar
- Puede pulsar aquí para saber más.
- ¡Entendido!
- APGEste correo está cifradoEste correo ha sido cifrado con OpenPGP.\nPara leerlo, necesita instalar y configurar una aplicación compatible con OpenPGP.Ir a los Ajustes
- Lista de mensajes de K-9
+ Lista de mensajes de MailCargando mensajes…
- Ha fallado la recuperación de la lista de carpetas
- ¡Error al recuperar el estado del destinatario del proveedor de OpenPGP!Imposible el cifrado¡Algunos remitentes marcados no soportan esta característica!Activar cifrado
@@ -1000,7 +967,7 @@
Configuración generalSin aplicación OpenPGP instaladaInstalar
- K-9 Mail necesita OpenKeychain para el cifrado de extremo a extremo.
+ Mail necesita OpenKeychain para el cifrado de extremo a extremo.Mensaje cifradoAsunto del mensaje cifradoPuede ser incompatible con algunos destinatarios
@@ -1026,8 +993,6 @@ Puede mantener este mensaje y usarlo como copia de seguridad de su clave secreta
Ha ocurrido un error al enviar el mensaje, por favor verifique su conexión a internet y la configuración del servidor de salida.EncendidoApagado
- Abrir
- CerrarPermitir acceso a contactosPara poder proveer sugerencias de contactos y mostrar sus nombres, la app necesita acceso a éstos.
@@ -1037,7 +1002,7 @@ Puede mantener este mensaje y usarlo como copia de seguridad de su clave secreta
Esperar hasta que la red esté disponibleToca aquí para leer más sobre el tema.Información sobre comprobaciones rápidas («push»)
- Al utilizar la comprobación rápida de mensajes (conocido como «push», o IDLE) K-9 Mail mantiene una conexión permanente e ininterrumpida con el servidor de correo. Por limitaciones de Android esto requiere mostrar una notificación mientras realiza esta acción en un segundo plano. %s
+ Al utilizar la comprobación rápida de mensajes (conocido como «push», o IDLE) Mail mantiene una conexión permanente e ininterrumpida con el servidor de correo. Por limitaciones de Android esto requiere mostrar una notificación mientras realiza esta acción en un segundo plano. %sSin embargo, Android también te permite ocultar la notificación. Leer más sobre el temaConfigurar la apariencia de la notificación
diff --git a/app/ui/legacy/src/main/res/values-et/strings.xml b/app/ui/legacy/src/main/res/values-et/strings.xml
index ca0f565afa9bfc0e918288dfe4e7ce6c8c9384b2..5c5e5e382c944c55b16461e4a58e95458294985f 100644
--- a/app/ui/legacy/src/main/res/values-et/strings.xml
+++ b/app/ui/legacy/src/main/res/values-et/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- K-9 Autorid
- Copyright 2008-%s The K-9 Dog Walkers. Portions Copyright 2006-%s the Android Open Source Project.
+ Mail AutoridLähtekoodApache litsents, versioon 2.0Avatud lähtekoodiga projekt
@@ -29,14 +28,12 @@
The account \"%s\" will be removed from Mail.Autorid
- ParandusteaveLoe e-kirjuLubab sellel rakendusel lugeda sinu e-kirju.Kustuta e-kirjadLubab sellel rakendusel kustutada sinu e-kirju.About MailKontod
- KaustadTäpsemaltKoostaVasta
@@ -45,12 +42,6 @@
Saada edasi manusenaVali kontoVali kaust
- Kontrollib olekut %s:%s%s
- Toob pealkirju %s:%s%s
- Saadab %s%s
- Töötleb %s:%s%s
- \u0020%d/%d
- Sünkroniseerimine mitteaktiivne%d valitudJärgmineEelmine
@@ -58,10 +49,7 @@
OkTühistaSaada
- Saada uuestiTeema on tühi, ikkagi saatmiseks klõpsa uuesti
- Vali
- Tühista valikVastaVasta kõigileKustuta
@@ -97,6 +85,7 @@
Lisa tärnEemalda tärnKopeeri
+ Eemalda tellimusNäita päiseidAadress kopeeritud lõikelauale
@@ -127,8 +116,6 @@
%.1f MB%.1f kB%d B
- Konto\"%s\" kahanenud %s%s
- Tihendatakse kontot \"%s\"Uus e-kiri%d uus kiri
@@ -199,20 +186,14 @@
Kellele:Koopia:Pimekoopia:
- Ava
- SalvestaManuse salvestamine ebaõnnestusNäita pilteEi leia %s kuvamiseks programmi.Laadi kogu kiri alla
- Kirja kuvamine ebaõnnestus%1$s kaudu
- Kõik pealkirjad on alla laaditud, kuid näitamiseks pole ühtegi täiendavat pealkirja.
- Pealkirjade toomine andmebaasist või emaili serverist ebaõnnestus.Veel sellelt saatjaltSaatja %s
- Silu / Puhasta sõnumi sisuKirjast loobutudKiri salvestatud mustandinaNäita tärne
@@ -244,13 +225,9 @@
Märgi kõik sõnumid loetuksKustuta (teadetest)Peida meiliklient
- Eemalda K-9 User-Agent meili päistest
+ Eemalda Mail User-Agent meili päistestPeida ajavööndKasuta UTCd kohaliku ajavööndi asemel meili päistes ja vastuse päises
- Peida teadetes teema
- Mitte kunagi
- Kui seade on lukustatud
- AlatiNäita \"Kustuta\" nuppuMitte kunagiÜhest kirjast teatamiseks
@@ -270,7 +247,15 @@
Seadista uus kontoE-posti aadressSalasõna
- Näita parooli
+ Kui soovid seda e-posti teenust kasutada Mail e-postikliendiga, siis logi teenusesse ja anna rakendusele õigus e-kirju ligeda.
+
+ Logi sisse
+
+ Logi sisse kasutades Google\'i teenuseid
+
+ Kui soovid siin näha salasõna, siis lülita sisse lukustuskuva kasutamine.
+ Tuvasta oma isik
+ Oma salasõna vaatamiseks eemalda lukustusKäsitsi seadistamineKonto info hankimine\u2026
@@ -290,6 +275,7 @@
Salasõna, ebaturvaliselt edastatudKrüpteeritud salasõnaKliendi sertifikaat
+ OAuth 2.0Sissetuleva meiliserveri sättedKasutajanimiSalasõna
@@ -308,12 +294,7 @@
Ära kustuta seda serverisKustuta serverisMärgi serveris loetuks
- Kasuta tihendamist võrgus:
- Mobiilne
- WiFi
- Muu
- Välismälu (SD-kaart)
- Tavaline sisemälu
+ Kasuta pakkimistEemalda kustutatud kirjad serveristKoheseltAndmete kontrollimisel
@@ -328,11 +309,8 @@
Näita üksnes tellitud kaustuKaustade automaatne laiendamineOWA tee
- ValikulineAutentimise tee
- ValikulinePostkasti hüüdnimin
- ValikulineVäljuva meiliserveri sättedSMTP serverPort
@@ -344,7 +322,6 @@
\"%1$s = %2$s\" ei valideeru seadega \"%3$s = %4$s\"Vigased sätted: %sKonto valikud
- Tihenda kontoKausta pollimise sagedusMitte kunagiIga 15 minuti tagant
@@ -355,8 +332,6 @@
Iga 6 tunni tagantIga 12 tunni tagantIga 24 tunni tagant
- Luba sellele kontole tõukemeil
- Kui Sinu server võimaldab seda, ilmuvad uued kirjad koheselt. See valik võib oluliselt parandada või halvendada süsteemi jõudlust.Värskenda ootel ühendustIga 2 minuti tagantIga 3 minuti tagant
@@ -367,7 +342,6 @@
Iga 48 minuti tagantIga 60 minuti tagantTeavita uue e-kirja saabumisest
- Teavita mind uute e-kirjade kontrollimisestKuvatavate kirjade arv10 kirja25 kirja
@@ -382,17 +356,19 @@
kõik kirjadServeriga sünkroniseerimata kirja pole võimalik kopeerida ega teisaldadaSeadistamist polnud võimalik lõpetada
- Vale kasutajanimi või parool.\n(%s)
+ Vale kasutajanimi või salasõna.\n(%s)Server esitles vigast SSL sertifikaati. Mõnikord juhtub see serveri vale konfiguratsiooni tõttu. Mõnikord seetõttu, et keegi proovib rünnata Sind või Sinu meiliserverit. Kui Sa ei ole kindel mis toimub, klõpsa Tühista ja võta ühendust tegelastega, kes haldavad su meiliserverit.
(%s)Ei saa serveriga ühendust.\n(%s)
+ Autentimine on katkenud
+ Autentimine ei õnnestunud ja tekkis järgnev viga: %s
+ See teenusepakkuja ei võimalda OAuth 2.0 kasutamist.
+ Rakendus ei suutnud tuvastada veebibrauserit, mida saaks sinu kontole ligipääsu lubamiseks kasutada.Muuda detaileJätkaTäpsemaltKonto sätted
- Vaikimisi konto
- Saada e-kirjad vaikimisi sellelt kontoltUue sõnumi teatedTeadete kaustKõik
@@ -411,8 +387,6 @@
Pärast avamist märgi loetuksMärgi sõnum loetuks, kui see on vaatamiseks avatudPärast kustutamist märgi loetuks
- Teadete sätted
- Ava süsteemi teadete sättedNäita alati pilteEiKontaktidelt
@@ -454,9 +428,6 @@
Kausta pollimise sagedusKonto värvKonto rõhutamise värvi kasutatakse selle konto kaustades ja nimekirjades
- Ilma värvita
- Märgutule LED-i värvus
- Mis värviga peab Sinu seadme LED tuli selle konto jaoks vilkumaLokaalse kausta suurusToo sõnumeid kuni1 KB
@@ -675,10 +646,6 @@
Helitugevuse nupuga liikumineSõnumite vaadetesNimekirja vaadetes
- %s%s
- - Lugemata
- Kõik sõnumid
- Kõik sõnumid otsitavatest kaustadestKoondsisendkaustKõik sõnumid koondkaustadesKoonda
@@ -711,8 +678,6 @@
TeemaKellaaeg ja kuupäevSõnumi põhiosa
- %d%%
- %1$s: %2$sSõnumi koostamineTeksti sisestamise väljadVaikimisi
@@ -769,12 +734,19 @@
Vali failImpordiSätted edukalt imporditud
- Palun sisesta paroolid
+
+ Palun sisesta salasõnad
+
+ Palun logi sisse
+
+ Palun logi sisse ja sisesta salasõnadSätete importimine ebaõnnestusSätete faili lugemine ebaõnnestusMõnede sätete importimine ebaõnnestusImportimine õnnestusSalasõna on vajalik
+
+ Sisselogimine on nõutavEi imporditudImportimise vigaHiljem
@@ -784,9 +756,9 @@
„%s“ konto kasutamiseks palun sisesta serveri salasõna.„%s“ konto kasutamiseks palun sisesta serveri salasõnad.
- Sissetuleva serveri parool
- Väljuva serveri parool
- Kasuta sama parooli väljuva serveri tarbeks
+ Saabuvaid kirju teenindava serveri salasõna
+ Väljuvaid kirju teenindava serveri salasõna
+ Kasuta sama salasõna ka väljuvate kirjade serveri tarbeksServeri nimi: %sNäita lugemata sõnumite arvu...Konto
@@ -963,14 +935,6 @@
Näita kõiki allkirjuAinult allkirjastamisel ei ole krüptimine kasutusel!Allkirjastamata tekst
- APG pole enam kasutusel
- APG arendust enam ei toimu ja ta pole enam kasutusel!
- Seetõttu on APG tugi K-9 Mail\'ist eemaldatud.
- Arendus lõppes 2014. aasta alguses
- Sisaldab parandamata turvavigu
- Võid lisainfoks klikkida siia
- Arusaadav!
- APGSee e-kiri on krüpteeritudSelle kirja krüptimisel on kasutusel OpenPGP.\nKirjalugemsieks palun paigalda ja seadista sobilik OpenPGP rakendus.Ava sätted
@@ -995,7 +959,7 @@
Üldised sättedOpenPGP rakendus paigaldamataPaigalda
- K-9 Mail eeldab läbiva krüptimise puhul OpenKeychain\'i kasutamist.
+ Mail eeldab läbiva krüptimise puhul OpenKeychain\'i kasutamist.Krüptitud kiriKrüpti ka kirja pealkiriSelline funktsionaalsus ei pruugi kõikide kirjasaajate puhul olla toetatud
@@ -1021,11 +985,9 @@ Palun jäta see kiri alles ning kasuta seda muu hulgas oma krüptovõtme varunda
Kirja saatmisel tekkis viga. Palun kontrolli võrguühenduse olemasolu ning väljuva e-postiserveri seadistusi.SisseVälja
- Ava
- SulgeLuba juurdepääs kontaktidele
- Selleks, et kirja koostamisel pakkuda saajate aadresse ning kuvada nimesid ning fotosid vajab K-9 Mail õigusi lugeda nutiseadme kontaktide andmebaasi.
+ Selleks, et kirja koostamisel pakkuda saajate aadresse ning kuvada nimesid ning fotosid vajab Mail õigusi lugeda nutiseadme kontaktide andmebaasi.Andmete laadimisel tekkis vigaKäivitamine…Ootan uusi e-kirju
@@ -1033,7 +995,7 @@ Palun jäta see kiri alles ning kasuta seda muu hulgas oma krüptovõtme varunda
Ootan taustal kuni võrguühendus taastubLisateabe saamiseks klõpsiTõuketeenuste teave
- Tõuketeenuste kasutamiseks vajab K-9 Mail pidevat ühendust e-posti serveriga. Selleks eeldab Android K-9 Mail\'i taustatl töötamise ajal püsiteavituse kasutamist. %s
+ Tõuketeenuste kasutamiseks vajab Mail pidevat ühendust e-posti serveriga. Selleks eeldab Android K-9 Mail\'i taustatl töötamise ajal püsiteavituse kasutamist. %sSiiski, Androidis saad sa ka teavituse peita.LisateaveSeadista teavitusi
diff --git a/app/ui/legacy/src/main/res/values-eu/strings.xml b/app/ui/legacy/src/main/res/values-eu/strings.xml
index f68bcdb1d19e28d0d5263a053c004bc944379e9a..c6a4ad65b48dfc43757b52350ac0fcef122fb841 100644
--- a/app/ui/legacy/src/main/res/values-eu/strings.xml
+++ b/app/ui/legacy/src/main/res/values-eu/strings.xml
@@ -242,6 +242,8 @@
Konfiguratu kontu berriaPosta helbideaPasahitza
+
+
Eskuzko konfigurazioa
@@ -280,10 +282,6 @@
Ez ezabatu zerbitzarianZerbitzarian ezabatuZerbitzarian irakurritako gisa markatu
- Erabili konpresioa sarean:
- Mugikorra
- Wi-Fi
- BestelakoakEzabatu hemen ere zerbitzaritik ezabatutako mezuakBerehalaAtzitzean
@@ -700,12 +698,16 @@
Hautatu fitxategiaInportatuEzarpenak ongi inportatu dira
+
Sartu pasahitzak
+
+
Ezin izan dira ezarpenak inportatuEzin izan da ezarpenen fitxategia irakurriEzin izan dira ezarpen batzuk inportatuOngi inportatutaPasahitza derrigorrezkoa
+
Inportatu gabeInportazio hutsegiteaGeroago
@@ -895,14 +897,6 @@
Sinadura guztiak erakutsiko diraZifratzea ez dago erabilgarri sinadura bakarrik moduan!Sinatu gabeko testua
- APG Zaharkitze Abisua
- APG mantentzeari utzi zaio!
- Honengatik, APGren onarpena kendua izan da K-9 Mailetik.
- Garapena 2014ren hasieran gelditu zen
- Konpondu gabeko segurtasun arazoak dauzka
- Klikatu hemen gehiago ikasteko.
- Ados!
- APGPosta hau zifratuta dagoPosta hau OpenPGPrekin zifratua izan da.\nIrakurtzeko, OpenPGPrekin bateragarria den aplikazio bat instalatu eta konfiguratu behar duzu.Joan ezarpenetara
diff --git a/app/ui/legacy/src/main/res/values-fa/strings.xml b/app/ui/legacy/src/main/res/values-fa/strings.xml
index 0cfeeead51218463ec2eb6088f3d659d8ccc541e..1655383b8146cf7f4a3b70f86bb99aa540a7b96d 100644
--- a/app/ui/legacy/src/main/res/values-fa/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fa/strings.xml
@@ -8,7 +8,6 @@
Mail UnreadThe K-9 Dog Walkers
- Copyright 2008-%s The K-9 Dog Walkers. Portions Copyright 2006-%s the Android Open Source Project.مجوز آپاچی، نسخهٔ ۲٫۰کتابخانههامجوز
@@ -21,14 +20,12 @@
The account \"%s\" will be removed from Mail.سازندگان
- اطلاعات نسخههاخواندن رایانامههابه این برنامه اجازه میدهد رایانامههای شما را بخواند.پاککردن رایانامههابه این برنامه اجازه میدهد رایانامههای شما را پاک کند.About Mailحسابها
- پوشههاپیشرفتهنوشتنپاسخ
@@ -37,12 +34,6 @@
هدایت بهشکل پیوستانتخاب حسابانتخاب پوشه
- سرکشی به %s:%s%s
- واکشی سرایندها %s:%s%s
- در حال ارسال %s%s
- پردازش %s:%s%s
- \u0020%d/%d
- همگامسازی غیرفعال شد%d انتخاببعدیقبلی
@@ -50,10 +41,7 @@
خبلغوارسال
- دوباره بفرستموضوع خالی است، دوباره بزنید میرود
- انتخاب
- بیانتخابپاسخپاسخ به همهحذف
@@ -118,8 +106,6 @@
%.1f MB%.1f kB%d B
- حجم حساب «%s» از %s به %s کاهش پیدا کرد
- در حال فشردهسازی حساب «%s»رایانامهٔ جدید%d نامهٔ جدید
@@ -191,20 +177,14 @@
به:رونوشت به:مخفیانه به:
- باز
- ذخیرهنمیتوان پیوست را ذخیره کرد.نمایش تصاویرنمایشدهندهای برای %s پیدا نشد.دانلود پیام کامل
- نمیتوان پیام را نمایش داداز طریق %1$s
- همهٔ سرایندها دانلود شده است، اما سرایند دیگری برای نمایش وجود ندارد.
- بازیابی سرایندهای بیشتر از پایگاهداده یا کارساز رایانامه ناموفق بود.بیشتر از این فرستندهاز %s
- عیبیابی / پاکسازی بدنهٔ پیامپیام از بین رفتپیشنویس پیام ذخیره شدنمایش ستارهها
@@ -239,10 +219,6 @@
شناسهٔ K-9 User-Agent را از سرایند رایانامهها بردارمنطقهٔ زمانی را مخفی کندر سرایند رایانامه و سرایند پاسخ بهجای زمان محلی از UTC استفاده کن
- پنهانکردن موضوع در اعلانها
- هرگز
- هنگامی که دستگاه قفل است
- همیشهنمایش دکمهٔ «حذف»هرگزبرای اعلان پیام تکی
@@ -262,7 +238,12 @@
راهاندازی حساب کاربری جدیدنشانی رایانامه گذرواژه
- نمایش گذرواژه
+
+
+
+ برای دیدن گذرواژهتان در این قسمت، قفل صفحه در این دستگاه را فعال کنید.
+ هویتتان را تایید کنید
+ برای دیدن گذرواژه، قفل را باز کنیدتنظیم دستیبازیابی اطلاعات حساب کاربری\u2026
@@ -300,12 +281,6 @@
در کارساز پاک نشوددر کارساز نیز پاک شوددر کارساز نشان بزن که خواندم
- روی این شبکهها از فشردهسازی استفاده کن:
- دادهٔ همراه
- وایفای
- سایر
- حافظهٔ خارجی (کارت SD)
- حافظهٔ داخلی همیشگیپیامهای حذفشده را در کارساز پاک کنفوراًدر زمان سرکشی
@@ -320,11 +295,8 @@
فقط پوشههایی که مشترک آنها هستم را نمایش بدهپوشهٔ آغازینمسیر OWA
- اختیاریمسیر احراز هویت
- اختیارینام مستعار صندوق پستی
- اختیاریتنظیمات کارساز خروجی کارساز SMTPدرگاه
@@ -336,7 +308,6 @@
«%1$s = %2$s» با «%3$s = %4$s» معتبر نیستپیکربندی نادرست: %sگزینههای حساب کاربری
- فشردهسازی حسابتکرار سرکشی به پوشههرگزهر ۱۵ دقیقه
@@ -347,8 +318,6 @@
هر ۶ ساعتهر ۱۲ ساعتهر ۲۴ ساعت
- پیشرانی رایانامه (Push Mail) برای این حساب فعال شود
- اگر کارساز شما آن را پشتیبانی کند پیامهای جدید بلافاصله ظاهر میشوند. این گزینه میتواند کارایی را بهشدت افزایش یا کاهش دهد.تازهکردن اتصال IDLEهر ۲ دقیقههر ۳ دقیقه
@@ -359,7 +328,6 @@
هر ۴۸ دقیقههر ۶۰ دقیقههر زمان رایانامه رسید به من خبر بده
- حین بهروزآوری رایانامهها به من خبر بدهتعداد پیامها برای نمایش۱۰ پیام۲۵ پیام
@@ -381,8 +349,6 @@
ادامهپیشرفتهتنظیمات حساب
- حساب پیشفرض
- بهطور پیشفرض رایانامهها را با این حساب بفرستاعلانهای رایانامهٔ جدیدپوشههای دارای اعلانهمه
@@ -404,8 +370,6 @@
زمانی که یک پیام را باز کردید بهعنوان خوانده علامت بخوردهنگام حذف، نشان بزن که خواندمپیام حذفشده را نشان بزن که خواندهام
- تنظیمات اعلان
- بازکردن تنظیمات اعلان دستگاههمیشه عکسها را نشان بدهخیراز مخاطبان
@@ -447,9 +411,6 @@
تکرار سرکشی به پوشهرنگ حساب کاربریرنگ مشخصهٔ این حساب که در لیست پوشهها و حسابها استفاده میشود
- بیرنگ
- رنگ اعلان LED
- برای این حساب کاربری چراغ LED دستگاهتان به چه رنگی چشمک بزندگنجایش پوشهٔ محلیواکشی پیامها تا این حجم1 KiB
@@ -537,7 +498,6 @@
اعلانهالرزشلرزش
- لرزش بههنگام رسیدن رایانامهالگوهای لرزشپیشفرضالگوی ۱
@@ -668,10 +628,6 @@
جابهجایی با کلیدهای تنظیم صدادر نماهای پیامدر نماهای لیستی
- %s%s
- - نخوانده
- همهٔ پیامها
- همهٔ پیامهای پوشههای قابلجستجوصندوق ورودی یکپارچههمهٔ پیامهای صندوق ورودی یکپارچهیکپارچگی
@@ -704,8 +660,6 @@
موضوعساعت و تاریخمتن پیام
- %d%%
- %1$s: %2$sنوشتن پیامفیلدهای ورود متنپیشفرض
@@ -763,12 +717,16 @@
انتخاب پروندهدرونبرددرونبرد تنظیمات موفقیتآمیز بود
+
لطفاً گذرواژهها را وارد کنید
+
+
درونبرد تنظیمات ناموفق بودخواندن پروندهٔ تنظیمات ناموفق بوددرونبرد برخی تنظیمات ناموفق بوددرونبرد موفقیتآمیز بودگذرواژه لازم است
+
درونبرد نشدهشکست درونبردبعداً
@@ -959,21 +917,11 @@
همهٔ امضاها نشان داده میشوددر حالت فقط-امضا (sign-only) رمزنگاری امکانپذیر نیست!متن امضانشده
- هشدار وَرافتادگی APG
- دیگر کسی APG را توسعه نمیدهد!
- بر این اساس، K-9 Mail به پشتیبانی از APG پایان داده است.
- توسعهٔ آن در اوایل سال ۲۰۱۴ متوقف شد
- مشکلات امنتی حلنشدهای دارد
- برای اطلاعات بیشتر اینجا کلیک کنید.
- فهمیدم!
- APGاین رایانامه رمزنگاری شده استاین رایانامه با OpenPGP رمزنگاری شده است.\nبرای خواندن آن، باید برنامهای سازگار با OpenPGP را نصب و پیکربندی کنید.برو به تنظیماتلیست پیام K-9بارگیری پیامها...
- واکشی لیست پوشه ناموفق بود
- هنگام بازیابی وضعیت گیرنده از فراهمساز OpenPGP خطایی رخ داد!رمزنگاری ممکن نیستبعضی از گیرندگان انتخابی از این ویژگی پشتیبانی نمیکنند!فعالسازی رمزنگاری
@@ -1019,8 +967,6 @@
هنگام ارسال پیام خطایی رخ داد. لطفاً اتصال شبکهتان و پیکربندی کارساز خروجی را بررسی کنید.روشنخاموش
- باز
- بستناجازهٔ دسترسی به مخاطبان را بدهیدبرای پیشنهاد مخاطب و نمایش نام و عکس مخاطب، برنامه نیاز دارد به مخاطبان شما دسترسی داشته باشد.
diff --git a/app/ui/legacy/src/main/res/values-fi/strings.xml b/app/ui/legacy/src/main/res/values-fi/strings.xml
index 044f364c20571d25621ac40d83b227655657e948..18002181e19ece5d15599b95bfbb7e663c9f4041 100644
--- a/app/ui/legacy/src/main/res/values-fi/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fi/strings.xml
@@ -1,987 +1,1019 @@
-
-
-
- Mail
- Mail Accounts
- Mail Unread
-
- The K-9 Dog Walkers
- Lähdekoodi
- Apache-lisenssi, versio 2.0
- Avoimen lähdekoodin projekti
- Verkkosivusto
- Käyttäjäfoorumi
- Fediverse
- Twitter
- Kirjastot
- Lisenssi
- Muutosloki
- Muutoslokia ei voitu ladata.
- Versio %s
- Mitä uutta
- Näytä viimeisimmät muutokset kun sovellus päivitettiin
- Selvitä mitä uutta tämä julkaisu sisältää
-
- Welcome to Mail
- Mail is the default mail client for /e/
-
- -- Sent from /e/ Mail.
-
- The account \"%s\" will be removed from Mail.
-
- Tekijät
- Lue sähköpostit
- Antaa sovellukselle luvan lukea sähköpostisi.
- Poista sähköpostit
- Antaa sovellukselle luvan poistaa sähköposteja.
- About Mail
- Tilit
- Lisäasetukset
- Kirjoita
- Vastaa
- Vastaa kaikille
- Välitä
- Välitä liitteenä
- Valitse tili
- Valitse kansio
- Siirrä…
- Kopioi…
- %d valittu
- Seuraava
- Edellinen
-
- OK
- Peruuta
- Lähetä
- Aihe on tyhjä, napsauta uudelleen lähettääksesi silti
- Vastaa
- Vastaa kaikille
- Poista
- Arkistoi
- Roskaposti
- Välitä
- Välitä liitteenä
- Muokkaa uutena viestinä
- Siirrä
- Siirrä luonnoksiin
- Lähetä…
- Siirrä kansioon…
- Valmis
- Hylkää
- Tallenna luonnoksena
- Tarkista sähköposti
- Lähetä viestit
- Päivitä kansiot
- Etsi kansio
- Lisää tili
- Kirjoita
- Etsi
- Etsi kaikkialta
- Hakutulokset
- Uudet viestit
- Asetukset
- Hallitse kansioita
- Tilin asetukset
- Poista tili
- Merkitse luetuksi
- Jaa
- Valitse lähettäjä
- Lisää tähti
- Poista tähti
- Kopioi
- Näytä otsikot
-
- Osoite kopioitu leikepöydälle
- Osoitteet kopioitu leikepöydälle
-
- Aiheen teksti kopioitu leikepöydälle
- Vaihda tummaan teemaan
- Vaihda vaaleaan teemaan
- Merkitse Lukemattomaksi
- Lukukuittaus
- Lukukuittaus pyydetään
- Lukukuittausta ei pyydetä
- Lisää liite
- Tyhjennä roskakori
- Poista pysyvästi
- About
- Asetukset
-
- (Ei aihetta)
- Ei lähettäjää
- Ladataan viestejä\u2026
- Yhteysvirhe
- Viestiä ei löytynyt
- Viestin lataamisvirhe
- Lataa %d lisää
- %.1f Gt
- %.1f Mt
- %.1f kt
- %d t
- Uusi sähköpostiviesti
-
- %d uusi viesti
- %d uutta viestiä
-
- %d lukematonta (%s)
- + %1$d lisää tilillä %2$s
- Vastaa
- Merkitse luetuksi
- Merkitse kaikki luetuiksi
- Poista
- Poista kaikki
- Arkistoi
- Arkistoi kaikki
- Roskaposti
- Varmennevirhe
- Varmennevirhe tilissä %s
- Tarkista palvelinasetuksesi
- Tunnistautuminen epäonnistui
- Tilin %s tunnistautuminen epäonnistui. Päivitä palvelinasetuksesi.
- Tarkistetaan viestejä: %s:%s
- Tarkistetaan viestejä
- Lähetetään viestejä: %s
- Lähetetään viestejä
- :
- Synkronoi (Push)
- Näytetään kun odotetaan uusia viestejä
- Viestit
- Viesteihin liittyvät ilmoitukset
- Muut
- Virheet ja muut sekalaiset ilmoitukset
- Saapuneet
- Lähtevät
- Luonnokset
- Roskakori
- Lähetetyt
- Joidenkin viestien lähettäminen epäonnistui
- Versio
- Käytä virheenkorjaustietojen lokiinkirjausta
- Kirjoita lokiin laajempi kuvaus ongelmista
- Kirjaa lokiin arkaluontoisia tietoja
- Salasanat saattavat näkyä lokeissa.
- Vie lokit
- Vienti onnistui. Lokit saattavat sisältää arkaluonteista tietoa. Pidä ne tallessa ja ole varovainen sen suhteen, kenelle ne lähetät.
- Vienti epäonnistui.
- Lataa lisää viestejä
- Vastaanottaja: %s
- Aihe
- Viesti
- Allekirjoitus
- -------- Alkuperäinen viesti --------
- Aihe:
- Lähetetty:
- Lähettäjä:
- Vastaanottaja:
- Kopio:
- %s kirjoitti:
- %1$s%2$s kirjoitti:
- Valitse vähintään yksi vastaanottaja.
- Vastaanottajakenttä sisältää virheellistä syötettä!
- Tämän henkilön sähköpostiosoitetta ei löytynyt.
- Joitakin liitteitä ei voida lähettää edelleen, koska niitä ei ole ladattu.
- Viestiä ei voida välittää, koska joitain liitteitä ei ole ladattu.
- Sisällytä lainattu viesti
- Poista lainattu teksti
- Muokkaa lainattua tekstiä
- Poista liite
- Lähettäjä: %s <%s>
- Vast.ottaja:
- Kopio:
- Piilokopio:
- Liitteen tallentaminen epäonnistui.
- Näytä kuvat
- Tiedostotyypille %s ei löydy katseluohjelmaa.
- Lataa koko viesti
- %1$s kautta
- Lisää samalta lähettäjältä
- Lähettäjä %s
- Viesti peruutettu
- Viesti tallennettu luonnoksena
- Näytä tähdet
- Tähdet ilmaisevat merkityt viestit
- Rivien esikatselu
- Näytä yhteyshenkilöiden nimet
- Näytä yhteyshenkilöiden nimet sähköpostiosoitteen sijaan
- Yhteyshenkilö aiheen yläpuolella
- Näytä yhteyshenkilöiden nimet aiherivin yläpuolella
- Näytä yhteystietojen nimet
- Käytä vastaanottajien nimiä osoitekirjasta, kun käytettävissä
- Väritä yhteystiedot
- Väritä nimet yhteystiedoissa
- Kiinteä kirjasinleveys
- Käytä kiinteää kirjasinleveyttä muotoilemattomille viesteille
- Sovita viestit automaattisesti
- Kutista viestit sopimaan näytölle
- Palaa poiston jälkeen listanäkymään
- Palaa viestilistaan viestin poiston jälkeen
- Näytä poiston jälkeen seuraava viesti
- Näytä viestin poiston jälkeen oletusarvoisesti seuraava viesti
- Vahvista toiminnot
- Näytä valintaikkuna valitun toiminnon suorittamisesta
- Poista
- Poista tähdellä merkityt (viestinäkymässä)
- Roskaposti
- Hylkää viesti
- Merkitse kaikki viestit luetuiksi
- Poista (ilmoituksessa)
- Piilota sähköpostisovellus
- Remove Mail User-Agent from mail headers
- Piilota aikavyöhyke
- Käytä UTC-aikaa paikallisen aikavyöhykkeen sijaan sähköpostin otsakkeissa
- Näytä Poista-painike
- Ei koskaan
- Yksittäisen viestin ilmoituksessa
- Aina
- Lukitusnäytön ilmoitukset
- Ei lukitusnäytön ilmoituksia
- Sovelluksen nimi
- Uusien viestien määrä
- Viestien määrä ja lähettäjät
- Sama kuin näytön ollessa lukittuna
- Hiljainen aika
- Poista käytöstä soittoäänen pirinä, pärinä ja vilkkuminen yöllä
- Poista ilmoitukset käytöstä
- Poista ilmoitukset kokonaan käytöstä hiljaisena aikana
- Hiljainen aika alkaa
- Hiljainen aika päättyy
- Lisää uusi tili
- Sähköpostiosoite
- Salasana
-
- Jotta näet salasanasi tässä, ota käyttöön laitteen näytön lukitus.
- Vahvista henkilöllisyytesi
- Avaa lukitus nähdäksesi salasanasi
- Määritä asetukset käsin
-
- Haetaan tilin tietoja\u2026
- Tarkistetaan saapuvan postin palvelimen asetuksia\u2026
- Tarkistetaan lähtevän postin palvelimen asetuksia\u2026
- Varmistetaan\u2026
- Haetaan tilin asetuksia\u2026
- Peruutetaan\u2026
- Melkein valmista!
- Anna tilin nimi (ei pakollinen):
- Anna nimesi (näkyy lähtevissä viesteissä):
- Tilin tyyppi
- Mikä tämän tilin tyyppi on?
- POP3
- IMAP
- Tavallinen salasana
- Salasana, välitetään turvattomasti
- Salattu salasana
- Asiakasvarmenne
- Saapuvan postin palvelimen asetukset
- Käyttäjätunnus
- Salasana
- Asiakasvarmenne
- POP3-palvelin
- IMAP-palvelin
- Exchange-palvelin
- Portti
- Suojauksen tyyppi
- Todennuksen tyyppi
- Ei mitään
- SSL/TLS
- STARTTLS
- \"%1$s = %2$s\" ei ole kelvollinen asetuksen \"%3$s = %4$s\" kanssa
- Kun viesti poistetaan
- Älä poista palvelimelta
- Poista palvelimelta
- Merkitse luetuksi palvelimella
- Käytä pakkausta verkossa:
- Mobiili
- Wi-Fi
- Muu
- Poista viestit
- Heti
- Tarkistettaessa
- Vain manuaalisesti
- Tunnista IMAP-nimiavaruus automaattisesti
- IMAP-polun etuliite
- Luonnokset-kansio
- Lähetetyt-kansio
- Roskakori-kansio
- Arkisto-kansio
- Roskapostikansio
- Näytä vain tilatut kansiot
- Avaa kansio automaattisesti
- OWA-polku
- Autentikointipolku
- Postilaatikon polku
- Lähtevän postin palvelimen asetukset
- SMTP-palvelin
- Portti
- Suojaus
- Edellytä kirjautumista.
- Käyttäjätunnus
- Salasana
- Todennustapa
- \"%1$s = %2$s\" ei ole kelvollinen asetuksen \"%3$s = %4$s\" kanssa
- Virheellinen asetus: %s
- Tilin asetukset
- Viestien tarkistus
- Ei koskaan
- Joka 15 minuutti
- Joka 30 minuutti
- Joka tunti
- Joka 2 tunti
- Joka 3 tunti
- Joka 6 tunti
- Joka 12 tunti
- Joka 24 tunti
- Päivitä IDLE-yhteys
- Joka 2 minuutti
- Joka 3 minuutti
- Joka 6 minuutti
- Joka 12 minuutti
- Joka 24 minuutti
- Joka 36 minuutti
- Joka 48 minuutti
- Joka 60 minuutti
- Ilmoitus postin saapumisesta
- Näytettävien viestien lukumäärä
- 10 kansiota
- 25 kansiota
- 50 kansiota
- 100 viestiä
- 250 viestiä
- 500 viestiä
- 1000 viestiä
- 2500 messages
- 5000 viestiä
- 10000 viestiä
- Kaikki viestit
- Viestiä ei voi kopioida tai siirtää, koska sitä ei ole synkronoitu palvelimelle
- Asennus ei onnistunut
- Käyttäjätunnus tai salasana väärin.\n(%s)
- Palvelin esitti virheellisen SSL-varmenteen. Joskus tämä johtuu palvelimen vääristä asetuksista. Toisinaan syynä on se, että joku koettaa hyökätä sinua tai sähköpostipalvelinta vastaan. Jos et ole varma mistä on kyse, napsauta Hylkää ja ole yhteydessä sähköpostipalvelinta hallinoivaan tahoon.\n\n(%s)
- Yhteys palvelimeen ei onnistu.\n(%s)
- Muokkaa tietoja
- Jatka
- Lisäasetukset
- Yleiset
- Saapuneiden viestien ilmoitukset
- Ilmoitukset kansioista
- Kaikki
- Vain 1. luokan kansiot
- 1. ja 2. luokan kansiot
- Kaikki paitsi 2. luokan kansiot
- Ei mitään
- Synkronointi-ilmoitukset
- Sähköpostiosoite
- Näytä tilapalkissa ilmoitus uusista viesteistä
- Näytä tilapalkissa ilmoitus viestien tarkistamisesta
- Ilmoita lähetetyistä viesteistä
- Näytä ilmoitus lähettämistäni viesteistä
- Vain yhteystiedot
- Näytä ilmoitukset vain niistä viesteistä, jotka tulevat tunnetuilta yhteystiedoilta
- Älä huomioi keskusteluviestejä
- Älä näytä ilmoituksia viesteistä, jotka kuuluvat sähköpostikeskusteluun
- Merkitse viesti luetuksi avattaessa
- Merkitse viesti luetuksi, kun se avataan katseltavaksi
- Merkitse luetuksi poistettaessa
- Merkitse viesti luetuksi, kun se poistetaan
- Ilmoitusten luokat
- Määritä uusien viestien ilmoitukset
- Määritä virhe- ja tilailmoitukset
- Näytä aina kuvat
- Ei koskaan
- Vain yhteystiedoissa olevilta lähettäjiltä
- Keneltä tahansa
- Viestien lähettäminen
- Lainaa alkuperäistä viestiä vastattaessa
- Alkuperäinen viesti on vastauksessa viesteihin vastattaessa.
- Vastaa lainauksen jälkeen
- Kun viestiin vastataan, alkuperäinen viesti on vastauksesi yläpuolella.
- Poista allekirjoitus lainatusta vastauksesta
- Lainatun tekstin allekirjoitus poistetaan viesteihin vastattaessa
- Viestin muoto
- Pelkkä teksti (kuvat ja muotoilu poistetaan)
- HTML (kuvat ja muotoilu säilytetään)
- Automaattinen (pelkkä teksti, ellei vastata HTML-viestiin)
- Näytä aina Kopio/Piilokopio
- Vastaanottokuittaus
- Pyydä aina vastaanottokuittaus
- Lainauksen tyyli vastattaessa
- Etuliite (kuten Gmail, Pine)
- Otsikko (kuten Outlook, Yahoo!, Hotmail)
- Lähetä lähetetyt viestit
- Lähetä viestit Lähetetyt-kansioon lähettämisen jälkeen
- Yleiset asetukset
- Viestien lukeminen
- Viestien hakeminen
- Kansiot
- Lainauksen etuliite
- Päästä päähän -salaus
- Ota käyttöön OpenPGP-tuki
- Valitse OpenPGP-sovellus
- Määritä päästä päähän -avain
- OpenPGP-sovellusta ei ole määritetty
- Yhdistetty kohteeseen %s
- Tehdään asetuksia…
- Talleta kaikki luonnokset salattuina
- Kaikki luonnokset talletetaan salattuina
- Salaa luonnokset vain jos salaus on käytössä
- Kansioiden tarkistus
- Tilin väri
- Valitse väri tilin kansio- ja tililistaan
- Paikallisen kansion koko
- Hae viestit, joiden koko on enintään
- 1 KiB
- 2 KiB
- 4 KiB
- 8 KiB
- 16 KiB
- 32 KiB
- 64 KiB
- 128 KiB
- 256 KiB
- 512 KiB
- 1 MiB
- 2 MiB
- 5 MiB
- 10 MiB
- mikä tahansa (ei rajoitusta)
- Synkronoi viestit ajalta
- milloin vain (ei rajoitusta)
- tänään
- viimeiset 2 päivää
- viimeiset 3 päivää
- viime viikko
- viimeiset 2 viikkoa
- viimeiset 3 viikkoa
- viime kuun
- viimeiset 2 kuukautta
- viimeiset 3 kuukautta
- viimeiset 6 kuukautta
- viime vuosi
- Näytä kansiot
- Kaikki
- Vain 1. luokan kansiot
- 1. ja 2. luokan kansiot
- Kaikki paitsi 2. luokan kansiot
- Tarkistettavat kansiot
- Kaikki
- Vain 1. luokan kansiot
- 1. ja 2. luokan kansiot
- Kaikki paitsi 2. luokan kansiot
- Ei mitään
- Push-kysely
- Kaikki
- Vain 1. luokan kansiot
- 1. ja 2. luokan kansiot
- Kaikki paitsi 2. luokan kansiot
- Ei mitään
- Siirrä/kopioi kohdekansiot
- Kaikki
- Vain 1. luokan kansiot
- 1. ja 2. luokan kansiot
- Kaikki paitsi 2. luokan kansiot
- Synkronoi etäpoistot
- Poista viestit, kun ne poistetaan palvelimelta
- OpenPGP-sovellus puuttuu - poistettiinko se?
- Kansioiden asetukset
- Näytä yläryhmässä
- Näytä yläosassa lähellä kansiolistaa
- Näytettävän kansion luokka
- Ei luokkaa
- 1. luokka
- 2. luokka
- Poll-luokka
- Ei mitään
- 1. luokka
- 2. luokka
- Sama kuin näytettävällä kansiolla
- Push-luokka
- Ei luokkaa
- 1. Luokka
- 2. Luokka
- Sama kuin poll-luokka
- Kansion ilmoitusluokka
- Ei luokkaa
- 1. luokka
- 2. luokka
- Sama kuin push-luokka
- Poista paikalliset viestit
- Saapuvan postin palvelin
- Määritä saapuvan postin palvelin
- Lähtevän postin palvelin
- Määritä lähtevän postin palvelin (SMTP)
- Tilin nimi
- Nimesi
- Ilmoitukset
- Värinä
- Värinä
- Värinän malli
- Oletus
- Malli 1
- Malli 2
- Malli 3
- Malli 4
- Malli 5
- Värinöiden lukumäärä
- Ei käytössä
- Uuden viestin ilmoitusääni
- Ilmoituksen valo
- Ei käytössä
- Tilin väri
- Järjestelmän oletusväri
- Valkoinen
- Punainen
- Vihreä
- Sininen
- Keltainen
- Syaani
- Magenta
- Viestin kirjoituksen asetukset
- Kirjoituksen oletusasetukset
- Aseta oletusarvot: lähettäjä, piilokopio ja allekirjoitus
- Identiteettien hallinta
- Määritä vaihtoehtoiset lähettäjän osoitteet ja allekirjoitukset
- Identiteettien hallinta
- Identiteetin hallinta
- Muokkaa identiteettiä
- Tallenna
- Uusi identiteetti
- Piilokopio kaikista viesteistä osoitteeseen
- Muokkaa
- Siirrä ylös
- Siirrä alas
- Siirrä ylimmäiseksi / aseta oletukseksi
- Poista
- Identiteetin kuvaus
- (Valinnainen)
- Nimesi
- (Valinnainen)
- Sähköpostiosoite
- (Pakollinen)
- Vastausosoite
- (Valinnainen)
- Allekirjoitus
- (Valinnainen)
- Käytä allekirjoitusta
- Allekirjoitus
- Oletusidentiteetti
- Valitse identiteetti
- Lähetä nimellä
- Et voi poistaa ainoaa identiteettiäsi
- Identiteettiä ei voi käyttää ilman sähköpostiosoitetta
- Vanhemmat viestit ensin
- Uusimmat viestit ensin
- Aiheen mukaan aakkosjärjestyksessä
- Aiheen mukaan käänteisessä aakkosjärjestyksessä
- Lähettäjän mukaan aakkosjärjestyksessä
- Lähettäjän mukaan käänteisessä aakkosjärjestyksessä
- Tähdellä merkityt ensin
- Merkitsemättömät ensin
- Lukemattomat ensin
- Luetut ensin
- Liitteelliset viestit ensin
- Liitteettömät viestit ensin
- Lajittele…
- Päivämäärän mukaan
- Saapumisajan mukaan
- Aiheen mukaan
- Lähettäjän mukaan
- Tähden mukaan
- Luettujen/lukemattomien mukaan
- Liitteiden mukaan
- Poista tili
- Tunnistamaton varmenne
- Hyväksy-avain
- Hylkää-avain
- Del (tai D) - Poista\nR - Vastaa\nA - Vastaa Kaikille\nC - Uusi viesti\nF - Välitä\nM - Siirrä\nV - Arkistoi\nY - Kopioi\nZ - Merkitse Lukemattomaksi(luetuksi)\nG - Merkitse tähdellä\nO - Lajittelun tyyppi\nI - Lajittelun järjestys\nQ - Palaa kansioihin\nS - Valitse/Poista valinta\nJ tai P - Edellinen viesti\nK or N - Seuraava viesti
- Del (tai D) - Poista\nC - Uusi viesti\nM - Siirrä\nV - Arkistoi\nY - Kopioi\nZ - Merkitse (Lukemattomaksi)luetuksi\nG - Merkitse tähdellä\nO - Lajittelun tyyppi\nI - Lajittelun järjestys\nQ - Palaa kansioihin\nS - Valitse / poista valinta
- Kansion nimi sisältää
- Näytä kansiot…
- Näytä kaikki
- Näytä vain 1. luokan kansiot
- Näytä 1. ja 2. luokan kansiot
- Näytä kaikki paitsi 2. luokan kansiot
- Allekirjoituksen sijainti
- Ennen lainattua viestiä
- Lainatun viestin jälkeen
- Käytä sovellusteemaa
- Tumma
- Vaalea
- Käytä järjestelmän oletusta
- Näyttö
- Yleiset
- Virheenkorjaus
- Tietosuoja
- Verkko
- Toimintoasetukset
- Tililista
- Viestilistat
- Viestit
- Teema
- Viestinäkymän teema
- Viestimuokkaimen teema
- Kieli
- Asetuksia ei löytynyt
- Kiinteä viestiteema
- Valitse viestinäkymän teema viestiä luettaessa
- Käytä kiinteää viestinäkymän teemaa
- Järjestelmän oletus
- Synkronointi taustalla
- Ei koskaan
- Aina
- Kun Automaattinen synkronointi on valittu
- Valitse kaikki
- Push-tilassa tarkistettavien kansioiden enimmäismäärä
- 5 kansiota
- 10 kansiota
- 25 kansiota
- 50 kansiota
- 100 kansiota
- 250 kansiota
- 500 kansiota
- 1000 kansiota
- Animaatio
- Käytä koreita visuaalisia tehosteita
- Navigointi äänenvoimakkuusnäppäimillä
- Viestinäkymä
- Erilaisia listanäkymiä
- Näytä \"Yhdistetty saapuneet\"
- Näytä tähdellä merkittyjen viestien määrä
- Yhdistetty saapuneet
- Kaikki viestit Yhdistetty saapuneet -kansiossa
- Yhdistä
- Kaikki viestit näytetään Yhdistetty saapuneet -kansiossa
- Etsi kansioista
- Kaikki
- Näytettävissä olevat
- Ei mitään
- Ei mitään
- Automaattinen (%s)
- Kirjasinkoko
- Määritä kirjasimen koko
- Tililista
- Tilin nimi
- Tilin kuvaus
- Kansiolistat
- Kansion nimi
- Kansion tila
- Viestilistat
- Viestin aihe
- Lähettäjä
- Päiväys
- Esikatselu
- Viestinäkymä
- Lähettäjä
- Vastaanottaja
- Kopio
- Piilokopio
- Tarkemmat otsikkotiedot
- Viestin aihe
- Aika ja päivämäärä
- Viestin sisältö
- Viestin kirjoitus
- Tekstinsyöttökentät
- Oletus
- Pienin
- Erittäin pieni
- Pienempi
- Pieni
- Keskikoko
- Suuri
- Suurin
- Toiminnolle ei löytynyt sopivaa ohjelmaa.
- Lähetys epäonnistui: %s
- Vahvista poisto
- Tallennetaanko vai hylätäänkö tämä viesti?
- Tallennetaanko vai hylätäänkö muutokset?
- Vahvista poisto
- Haluatko varmasti hylätä tämän viestin?
- Valitse kopioitava teksti.
- Poistetaanko paikalliset viestit?
- Tämä poistaa kaikki paikalliset viestit kansiosta. Viestejä ei poisteta palvelimelta.
- Poista viestit
- Vahvista poisto
- Haluatko poistaa tämän viestin?
-
- Haluatko varmasti poistaa tämän viestin?
- Haluatko varmasti poistaa %1$d viestiä?
-
- Kyllä
- Ei
- Vahvista kaikki luetuiksi
- Haluatko merkitä kaikki viestit luetuiksi?
- Vahvista roskakorin tyhjentäminen
- Haluatko tyhjentää roskakorikansion?
- Kyllä
- Ei
- Vahvista roskapostikansioon siirtäminen
-
- Haluatko todella siirtää tämän viestin roskapostikansioon?
- Haluatko todella siirtää %1$d viestiä roskapostikansioon?
-
- Kyllä
- Ei
- Ladataan liitettä
- »
- ›
- Varmuuskopio
- Muut
- Vie asetukset
- Vie
- Jaa
- Viedään asetuksia…
- Asetukset vietiin onnistuneesti
- Asetusten vienti epäonnistui
- Tuo asetukset
- Valitse tiedosto
- Tuo
- Asetusten tuonti onnistui
- Anna salasanat
- Asetusten tuonti epäonnistui
- Asetustiedoston lukeminen epäonnistui
- Joidenkin asetusten tuonti epäonnistui
- Tuonti onnistui
- Salasana vaaditaan
- Ei tuotu
- Tuontivirhe
- Myöhemmin
- Tuo asetukset
- Tuodaan asetuksia…
-
- Jotta voit käyttää tiliä \"%s\", sinun on annettava palvelimen salasana.
- Jotta voit käyttää tiliä \"%s\", sinun on annettava palvelimien salasanat.
-
- Saapuvan postin palvelimen salasana
- Lähtevän postin palvelimen salasana
- Käytä samaa salasanaa lähtevälle palvelimelle
- Palvelimen nimi: %s
- Näytä tilin lukemattomien viestien määrä…
- Tili
- Tili, jonka osalta näytetään lukemattomien viestien määrä
- Yhdistetty saapuneet
- Kansion määrä
- Näytä lukemattomien määrä vain yksittäisen kansion osalta
- Kansio
- Kansio, jonka osalta näytetään lukemattomien viestien määrä
- Valmis
- %1$s - %2$s
- Tiliä ei ole valittu
- Kansiota ei ole valittu
- Ei tekstiä
- Avaa katseltavaksi
- Jaa linkki
- Kopioi linkki leikepöydälle
- Linkki
- Kopioi linkin teksti leikepöydälle
- Linkin teksti
- Kuva
- Näytä kuva
- Tallenna kuva
- Lataa kuva
- Kopioi kuvan osoite leikepöydälle
- Kuvan osoite
- Soita numeroon
- Tallenna yhteystietoihin
- Kopioi puhelinnumero leikepöydälle
- Puhelinnumero
- Lähetä sähköpostia
- Tallenna yhteystietoihin
- Kopioi sähköpostiosoite leikepöydälle
- Sähköpostiosoite
- Kaikki
- 10
- 25
- 50
- 100
- 250
- 500
- 1000
- Etähaun tulosten rajoitus
- Lähetetään pyyntöä palvelimelle
-
- Noudetaan %d tulos
- Noudetaan %d tulosta
-
-
- Noudetaan %1$d / %2$d tulos
- Noudetaan %1$d / %2$d tulosta
-
- Etähaku epäonnistui
- Hae
- Etsi viestejä palvelimelta
- Etähaku ei ole käytettävissä ilman verkkoyhteyttä.
- Vaihda väriä kun luettu
- Eri tausta osoittaa viestin olevan luettu
- Ketjunäkymä
- Kutista samaan ketjuun kuuluvat viestit
- Päivitetään tietokantoja
- Päivitetään tietokantoja…
- Päivitetään tilin \"%s\" tietokantaa
- Näytä jaettu näyttö
- Aina
- Ei koskaan
- Kun laite on vaaka-asennossa
- Valitse viesti vasemmalta
- Näytä yhteystietojen kuvat
- Näytä yhteystietojen kuvat viestilistassa
- Merkitse kaikki luetuiksi
- Väritä yhteystietojen kuvat
- Väritä yhteystietojen puuttuvat kuvat
- Näytettävät viestitoiminnot
- Näytä valitut toiminnot viestin Näytä-valikossa
- Ladataan liitettä…
- Lähetetään viestiä
- Tallennetaan luonnosta
- Noudetaan liitettä…
- Autentikointi ei onnistu. Palvelin ei ilmoita SASL EXTERNAL -kelpoisuudesta. Tämä saattaa johtua vanhentuneesta asiakasvarmenteesta, tuntemattomasta varmentajasta tai jostakin muusta asetusongelmasta.
-
- Käytä asiakasvarmennetta
- Ei asiakasvarmennetta
- Poista asiakasvarmenteen valinta
- Asiakasvarmenteen noutaminen epäonnistui aliakselle \"%s\"
- Lisäasetukset
- Asiakasvarmenne \"%1$s\" on vanhentunut tai ei ole vielä voimassa (%2$s)
-
- *Salattu*
- Lisää yhteystiedoista
- Kopio
- Piilokopio
- Vastaanottaja
- Lähettäjä
- Vastausosoite
- <Tuntematon vastaanottaja>
- <Tuntematon lähettäjä>
- Koti
- Työ
- Muu
- Mobiili
- Tälle tilille ei ole määritelty luonnoskansiota!
- Tälle tilille ei ole määritetty avainta! Tarkista asetukset.
- Salauksen tarjoaja käyttää yhteensopimatonta versiota. Tarkista asetuksesi!
- Yhteyttä salaussovellukseen ei voitu muodostaa: tarkista asetukset tai yritä napsauttaa salauskuvaketta uudestaan!
- Päästä päähän -salauksen alustaminen epäonnistui, tarkista asetukset
- PGP/INLINE-tila ei tue liitteitä!
- Käytä PGP/INLINE-tilaa
- Poista PGP/INLINE käytöstä
- Käytä PGP:n pelkkää allekirjoitusta
- Poista käytöstä PGP:n pelkkä allekirjoitus
- PGP/INLINE-tila
- Viesti lähetetään PGP/INLINE-muodossa.\nTätä tulisi käyttää vain yhteensopivuutta varten:
- Jotkin sovellukset tukevat vain tätä muotoa
- Allekirjoitukset saattavat rikkontua siirron aikana
- Liitteet eivät ole tuettuja
- Selvä!
- Poista käytöstä
- Pidä käytössä
- Selvä!
- Poista käytöstä
- Pidä käytössä
- PGP:n pelkkä allekirjoitustila
- Tässä tilassa PGP-avaintasi käytetään salakirjoitetun allekirjoituksen luomiseen, mutta sähköpostiviestiä ei salata.
- Tämä ei salaa sähköpostiviestiä, mutta vahvistaa sen olevan lähetetty käyttäen sinun avainta.
- Allekirjoitukset saattavat rikkoontua postituslistoille lähettäessä.
- Allekirjoitukset saatetaan näyttää \"signature.asc\"-liitteinä joissain sovelluksissa.
- Salatut viestit sisältävät aina allekirjoituksen.
- Raakateksti
- päästä päähän -allekirjoitus sisälsi virheen
- viesti täytyy ladata kokonaan, jotta allekirjoitus voidaan käsitellä
- sisältää ei-tuetun päästä päähän -allekirjoituksen
- Viesti on salattu, mutta sen muoto ei ole tuettu.
- Viesti on salattu, mutta viestin salauksen purkaminen peruttiin.
- Päästä päähän -allekirjoitettu raakateksti
- vahvistetulta allekirjoittajalta
- Allekirjoitettu raakateksti
- mutta päästä päähän -avain ei vastannut lähettäjää
- mutta päästä päähän -avain on vanhentunut
- mutta päästä päähän -avain on kumottu
- mutta päästä päähän -avainta ei mielletä turvalliseksi
- tuntemattomalta päästä päähän -avaimelta
- Salattu
- mutta salausta purkaessa tapahtui virhe
- täytyy ladata viesti kokonaan salauksen purkamista varten
- mutta salaussovellusta ei ole määritetty
- Salattu
- mutta ei päästä päähän
- Päästä päähän salattu
- vahvistetulta lähettäjältä
- Salattu
- tuntemattomalta päästä päähän -avaimelta
- mutta päästä päähän -avain ei vastannut lähettäjää
- mutta päästä päähän -avain on vanhentunut
- mutta päästä päähän -avain on kumottu
- mutta päästä päähän -avainta ei mielletä turvalliseksi
- mutta päästä päähän -tiedot sisältävät virheitä
- mutta salausta ei mielletä turvalliseksi
- OK
- Etsi avainta
- Näytä allekirjoittaja
- Näytä lähettäjä
- Yksityiskohdat
- Avaa lukitus
- Tätä osaa ei salattu, joten se saattaa olla epäluotettava.
- Suojaamaton liite
- Ladataan…
- Salauksen purkaminen peruttiin.
- Yritä uudelleen
- Salattu viesti tulee ladata, jotta sen salaus voidaan avata.
- Virhe sähköpostin salausta avatessa
- Erikoismerkit eivät ole tällä hetkellä tuettuja!
- Virhe osoitetta jäsennettäessä!
- Piilota salaamattomat allekirjoitukset
- Vain salatut allekirjoitukset näytetään
- Kaikki allekirjoitukset näytetään
- Salaus ei ole käytettävissä pelkässä allekirjoitustilassa!
- Allekirjoittamaton teksti
- Varoitus APG:n vanhentamisesta
- APG:tä ei enää ylläpidetä!
- Tämän vuoksi APG on poistettu K-9 Mailista.
- Kehitys loppui alkuvuodesta 2014.
- Sisältää korjaamattomia tietoturvaongelmia
- Lisätietoja saat napsauttamalla tästä.
- Selvä!
- APG
- Tämä sähköpostiviesti on salattu
- Tämä viesti on salattu OpenPGP:llä.\nLukeaksesi viestin sinun tulee asentaa ja määrittää yhteensopiva OpenPGP-sovellus.
- Siirry asetuksiin
- Mail Message List
- Ladataan viestejä…
- Salaus ei ole mahdollista
- Jotkut valitut vastaanottajat eivät tue tätä ominaisuutta!
- Käytä salausta
- Poista salaus käytöstä
- Viestin salaus takaa, ettei niitä voi lukea kukaan muu kuin vastaanottaja.
- Salaus ilmestyy näkyviin vain jos kaikki vastaanottajat tukevat sitä, ja kyseiset tahot ovat lähettäneet sinulle sähköpostia aiemmin.
- Ota salaus käyttöön tai poista käytöstä napauttamalla tätä kuvaketta.
- Selvä
- Takaisin
- Poista salaus käytöstä
- OpenPGP-salaus
- Automattisen salauksen kahdenkeskeinen tila
- Automattisen salauksen kahdenkeskeinen tila
- Viestit salataan normaalisti valinnan mukaan, tai kun vastataan salattuun viestiin.
- Jos sekä lähettäjä että vastaanottaja ottavat käyttöön kahdenkeskeisen tilan, salaus otetaan käyttöön oletusarvoisesti.
- Voit napauttaa tästä saadaksesi lisätietoja.
- Yleiset asetukset
- OpenPGP-sovellusta ei ole asennettu
- Asenna
- Mail requires OpenKeychain for end-to-end encryption.
- Salattu viesti
- Salaa viestien aiheet
- Ei välttämättä tuettu kaikkien vastaanottajien osalta
- Sisäinen virhe: virheellinen tili!
- Virhe yhdistäessä kohteeseen %s!
- Lähetä Autocrypt-asetusviesti
- Jaa päästä päähän -salauksen asetukset turvallisesti muiden laitteiden kanssa
- Autocrypt-asetusviesti
- Autocrypt-asetusviesti jakaa päästä päähän -salauksen asetukset turvallisesti muiden laitteidesi kanssa.
- Lähetä asetusviesti
- Viesti lähetetään osoitteeseesi:
- Luodaan asetusviestiä…
- Lähetetään viesti:
- Viimeistelläksesi avaa viesti toisella laitteellasi ja kirjoita asetuskoodi.
- Näytä asetuskoodi
- Autocrypt-asetusviesti
- Tämä viesti sisältää kaikki tiedot Autocryptin asetusten sekä salausavaimesi siirtämiseksi turvallisesti alkuperäisestä laitteestasi.
+
+
+
+ Mail
+ Mail-tilit
+ Mail-lukematon
+
+ The Mail Dog Walkers
+ Lähdekoodi
+ Apache-lisenssi, versio 2.0
+ Avoimen lähdekoodin projekti
+ Verkkosivusto
+ Käyttäjäfoorumi
+ Fediverse
+ Twitter
+ Kirjastot
+ Lisenssi
+ Muutosloki
+ Muutoslokia ei voitu ladata.
+ Versio %s
+ Mitä uutta
+ Näytä viimeisimmät muutokset kun sovellus päivitettiin
+ Selvitä mitä uutta tämä julkaisu sisältää
+
+ Tervetuloa Mail Mailiin
+
+Mail on monipuolinen ilmainen sähköpostisovellus Androidille.
+
+Sen ominaisuuksiin kuuluu muun muassa:
+
+
+
Push-viestit käyttäen IMAP IDLE -toteutusta
+
Aiempaa parempi suorituskyky
+
Viestien arkistointi
+
Sähköpostin allekirjoitukset
+
Piilokopioiden lähetys itselle
+
Kansiotilaukset
+
Kaikkien kansioiden synkronointi
+
Vastausosoitteen määritys
+
Pikanäppäimet
+
Aiempaa parempi IMAP-tuki
+
Liitteiden tallennus muistikortille
+
Roskakorin tyhjennys
+
Viestien järjestely
+
…ja paljon muuta
+
+
+Huomioithan, että Mail ei tue useimpia ilmaisia Hotmail-tilejä, ja kuten monet muut sovellukset, saattaa kärsiä pienistä ongelmista Microsoft Exchange -ympäristössä.
+
+ ]]>
+
+ -- \nLähetetty Android-laitteestani Mail Maililla. Pahoittelut vähäsanaisuudestani.
+
+ Tili \"%s\" poistetaan Mailista.
+
+ Tekijät
+ Tietoja Mailista
+ Tilit
+ Lisäasetukset
+ Kirjoita
+ Vastaa
+ Vastaa kaikille
+ Välitä
+ Välitä liitteenä
+ Valitse tili
+ Valitse kansio
+ Siirrä…
+ Kopioi…
+ %d valittu
+ Seuraava
+ Edellinen
+
+ OK
+ Peruuta
+ Lähetä
+ Aihe on tyhjä, napsauta uudelleen lähettääksesi silti
+ Vastaa
+ Vastaa kaikille
+ Poista
+ Arkistoi
+ Roskaposti
+ Välitä
+ Välitä liitteenä
+ Muokkaa uutena viestinä
+ Siirrä
+ Siirrä luonnoksiin
+ Lähetä…
+ Siirrä kansioon…
+ Valmis
+ Hylkää
+ Tallenna luonnoksena
+ Tarkista sähköposti
+ Lähetä viestit
+ Päivitä kansiot
+ Etsi kansio
+ Lisää tili
+ Kirjoita
+ Etsi
+ Etsi kaikkialta
+ Hakutulokset
+ Uudet viestit
+ Asetukset
+ Hallitse kansioita
+ Tilin asetukset
+ Poista tili
+ Merkitse luetuksi
+ Jaa
+ Valitse lähettäjä
+ Lisää tähti
+ Poista tähti
+ Kopioi
+ Lopeta tilaus
+ Näytä otsikot
+
+ Osoite kopioitu leikepöydälle
+ Osoitteet kopioitu leikepöydälle
+
+ Aiheen teksti kopioitu leikepöydälle
+ Vaihda tummaan teemaan
+ Vaihda vaaleaan teemaan
+ Merkitse lukemattomaksi
+ Lukukuittaus
+ Lukukuittaus pyydetään
+ Lukukuittausta ei pyydetä
+ Lisää liite
+ Tyhjennä roskakori
+ Poista pysyvästi
+ Tietoja
+ Asetukset
+
+ (Ei aihetta)
+ Ei lähettäjää
+ Ladataan viestejä\u2026
+ Yhteysvirhe
+ Viestiä ei löytynyt
+ Viestin lataamisvirhe
+ Lataa %d lisää
+ %.1f Gt
+ %.1f Mt
+ %.1f kt
+ %d t
+ Uusi sähköpostiviesti
+
+ %d uusi viesti
+ %d uutta viestiä
+
+ %d lukematonta (%s)
+ + %1$d lisää tilillä %2$s
+ Vastaa
+ Merkitse luetuksi
+ Merkitse kaikki luetuiksi
+ Poista
+ Poista kaikki
+ Arkistoi
+ Arkistoi kaikki
+ Roskaposti
+ Varmennevirhe
+ Varmennevirhe tilissä %s
+ Tarkista palvelinasetuksesi
+ Tunnistautuminen epäonnistui
+ Tilin %s tunnistautuminen epäonnistui. Päivitä palvelinasetuksesi.
+ Tarkistetaan viestejä: %s:%s
+ Tarkistetaan viestejä
+ Lähetetään viestejä: %s
+ Lähetetään viestejä
+ :
+ Synkronoi (Push)
+ Näytetään kun odotetaan uusia viestejä
+ Viestit
+ Viesteihin liittyvät ilmoitukset
+ Muut
+ Virheet ja muut sekalaiset ilmoitukset
+ Saapuneet
+ Lähtevät
+ Luonnokset
+ Roskakori
+ Lähetetyt
+ Joidenkin viestien lähettäminen epäonnistui
+ Versio
+ Käytä virheenkorjaustietojen lokiinkirjausta
+ Kirjoita lokiin laajempi kuvaus ongelmista
+ Kirjaa lokiin arkaluontoisia tietoja
+ Salasanat saattavat näkyä lokeissa.
+ Vie lokit
+ Vienti onnistui. Lokit saattavat sisältää arkaluonteista tietoa. Pidä ne tallessa ja ole varovainen sen suhteen, kenelle ne lähetät.
+ Vienti epäonnistui.
+ Lataa lisää viestejä
+ Vastaanottaja: %s
+ Aihe
+ Viesti
+ Allekirjoitus
+ -------- Alkuperäinen viesti --------
+ Aihe:
+ Lähetetty:
+ Lähettäjä:
+ Vastaanottaja:
+ Kopio:
+ %s kirjoitti:
+ %1$s%2$s kirjoitti:
+ Valitse vähintään yksi vastaanottaja.
+ Vastaanottajakenttä sisältää virheellistä syötettä!
+ Tämän henkilön sähköpostiosoitetta ei löytynyt.
+ Joitakin liitteitä ei voida lähettää edelleen, koska niitä ei ole ladattu.
+ Viestiä ei voida välittää, koska joitain liitteitä ei ole ladattu.
+ Sisällytä lainattu viesti
+ Poista lainattu teksti
+ Muokkaa lainattua tekstiä
+ Poista liite
+ Lähettäjä: %s <%s>
+ Vast.ottaja:
+ Kopio:
+ Piilokopio:
+ Liitteen tallentaminen epäonnistui.
+ Näytä kuvat
+ Tiedostotyypille %s ei löydy katseluohjelmaa.
+ Lataa koko viesti
+ %1$s kautta
+ Lisää samalta lähettäjältä
+ Lähettäjä %s
+ Viesti peruutettu
+ Viesti tallennettu luonnoksena
+ Näytä tähdet
+ Tähdet ilmaisevat merkityt viestit
+ Rivien esikatselu
+ Näytä yhteyshenkilöiden nimet
+ Näytä yhteyshenkilöiden nimet sähköpostiosoitteen sijaan
+ Yhteyshenkilö aiheen yläpuolella
+ Näytä yhteyshenkilöiden nimet aiherivin yläpuolella
+ Näytä yhteystietojen nimet
+ Käytä vastaanottajien nimiä osoitekirjasta, kun käytettävissä
+ Väritä yhteystiedot
+ Väritä nimet yhteystiedoissa
+ Yhteystiedon nimen väri
+ Kiinteä kirjasinleveys
+ Käytä kiinteää kirjasinleveyttä muotoilemattomille viesteille
+ Sovita viestit automaattisesti
+ Kutista viestit sopimaan näytölle
+ Palaa poiston jälkeen listanäkymään
+ Palaa viestilistaan viestin poiston jälkeen
+ Näytä poiston jälkeen seuraava viesti
+ Näytä viestin poiston jälkeen oletusarvoisesti seuraava viesti
+ Vahvista toiminnot
+ Näytä valintaikkuna valitun toiminnon suorittamisesta
+ Poista
+ Poista tähdellä merkityt (viestinäkymässä)
+ Roskaposti
+ Hylkää viesti
+ Merkitse kaikki viestit luetuiksi
+ Poista (ilmoituksessa)
+ Piilota sähköpostisovellus
+ Poista Mail-tunnistetiedot viestin otsaketiedoista
+ Piilota aikavyöhyke
+ Käytä UTC-aikaa paikallisen aikavyöhykkeen sijaan sähköpostin otsakkeissa
+ Näytä Poista-painike
+ Ei koskaan
+ Yksittäisen viestin ilmoituksessa
+ Aina
+ Lukitusnäytön ilmoitukset
+ Ei lukitusnäytön ilmoituksia
+ Sovelluksen nimi
+ Uusien viestien määrä
+ Viestien määrä ja lähettäjät
+ Sama kuin näytön ollessa lukittuna
+ Hiljainen aika
+ Poista käytöstä soittoäänen pirinä, pärinä ja vilkkuminen yöllä
+ Poista ilmoitukset käytöstä
+ Poista ilmoitukset kokonaan käytöstä hiljaisena aikana
+ Hiljainen aika alkaa
+ Hiljainen aika päättyy
+ Lisää uusi tili
+ Sähköpostiosoite
+ Salasana
+ Jotta voit käyttää tätä tiliä K-9 Mailin kanssa, sinun tulee kirjautua sisään ja antaa sovellukselle oikeus päästä sähköposteihisi.
+
+ Kirjaudu sisään
+
+ Kirjaudu sisään Googlella
+
+ Jotta näet salasanasi tässä, ota käyttöön laitteen näytön lukitus.
+ Vahvista henkilöllisyytesi
+ Avaa lukitus nähdäksesi salasanasi
+ Määritä asetukset käsin
+
+ Haetaan tilin tietoja\u2026
+ Tarkistetaan saapuvan postin palvelimen asetuksia\u2026
+ Tarkistetaan lähtevän postin palvelimen asetuksia\u2026
+ Varmistetaan\u2026
+ Haetaan tilin asetuksia\u2026
+ Peruutetaan\u2026
+ Melkein valmista!
+ Anna tilin nimi (ei pakollinen):
+ Anna nimesi (näkyy lähtevissä viesteissä):
+ Tilin tyyppi
+ Mikä tämän tilin tyyppi on?
+ POP3
+ IMAP
+ Tavallinen salasana
+ Salasana, välitetään turvattomasti
+ Salattu salasana
+ Asiakasvarmenne
+ OAuth 2.0
+ Saapuvan postin palvelimen asetukset
+ Käyttäjätunnus
+ Salasana
+ Asiakasvarmenne
+ POP3-palvelin
+ IMAP-palvelin
+ Exchange-palvelin
+ Portti
+ Suojauksen tyyppi
+ Todennuksen tyyppi
+ Ei mitään
+ SSL/TLS
+ STARTTLS
+ \"%1$s = %2$s\" ei ole kelvollinen asetuksen \"%3$s = %4$s\" kanssa
+ Kun viesti poistetaan
+ Älä poista palvelimelta
+ Poista palvelimelta
+ Merkitse luetuksi palvelimella
+ Käytä pakkausta
+ Poista viestit
+ Heti
+ Tarkistettaessa
+ Vain manuaalisesti
+ Tunnista IMAP-nimiavaruus automaattisesti
+ IMAP-polun etuliite
+ Luonnokset-kansio
+ Lähetetyt-kansio
+ Roskakori-kansio
+ Arkisto-kansio
+ Roskapostikansio
+ Näytä vain tilatut kansiot
+ Avaa kansio automaattisesti
+ OWA-polku
+ Autentikointipolku
+ Postilaatikon polku
+ Lähtevän postin palvelimen asetukset
+ SMTP-palvelin
+ Portti
+ Suojaus
+ Edellytä kirjautumista.
+ Käyttäjätunnus
+ Salasana
+ Todennustapa
+ \"%1$s = %2$s\" ei ole kelvollinen asetuksen \"%3$s = %4$s\" kanssa
+ Virheellinen asetus: %s
+ Tilin asetukset
+ Viestien tarkistus
+ Ei koskaan
+ Joka 15 minuutti
+ Joka 30 minuutti
+ Joka tunti
+ Joka 2 tunti
+ Joka 3 tunti
+ Joka 6 tunti
+ Joka 12 tunti
+ Joka 24 tunti
+ Päivitä IDLE-yhteys
+ Joka 2 minuutti
+ Joka 3 minuutti
+ Joka 6 minuutti
+ Joka 12 minuutti
+ Joka 24 minuutti
+ Joka 36 minuutti
+ Joka 48 minuutti
+ Joka 60 minuutti
+ Ilmoitus postin saapumisesta
+ Näytettävien viestien lukumäärä
+ 10 kansiota
+ 25 kansiota
+ 50 kansiota
+ 100 viestiä
+ 250 viestiä
+ 500 viestiä
+ 1000 viestiä
+ 2500 viestiä
+ 5000 viestiä
+ 10000 viestiä
+ Kaikki viestit
+ Viestiä ei voi kopioida tai siirtää, koska sitä ei ole synkronoitu palvelimelle
+ Asennus ei onnistunut
+ Käyttäjätunnus tai salasana väärin.\n(%s)
+ Palvelin esitti virheellisen SSL-varmenteen. Joskus tämä johtuu palvelimen vääristä asetuksista. Toisinaan syynä on se, että joku koettaa hyökätä sinua tai sähköpostipalvelinta vastaan. Jos et ole varma mistä on kyse, napsauta Hylkää ja ole yhteydessä sähköpostipalvelinta hallinoivaan tahoon.\n\n(%s)
+ Yhteys palvelimeen ei onnistu.\n(%s)
+ Valtuutus peruttu
+ Valtuutus epäonnistui, virhe: %s
+ OAuth 2.0 ei ole tällä hetkellä tuettu tämän palvelutarjoajan kohdalla.
+ Sovellus ei löytänyt selainta käytettäväksi pääsyn antamiseksi tälle tilille.
+ Muokkaa tietoja
+ Jatka
+ Lisäasetukset
+ Yleiset
+ Saapuneiden viestien ilmoitukset
+ Ilmoitukset kansioista
+ Kaikki
+ Vain 1. luokan kansiot
+ 1. ja 2. luokan kansiot
+ Kaikki paitsi 2. luokan kansiot
+ Ei mitään
+ Synkronointi-ilmoitukset
+ Sähköpostiosoite
+ Näytä tilapalkissa ilmoitus uusista viesteistä
+ Näytä tilapalkissa ilmoitus viestien tarkistamisesta
+ Ilmoita lähetetyistä viesteistä
+ Näytä ilmoitus lähettämistäni viesteistä
+ Vain yhteystiedot
+ Näytä ilmoitukset vain niistä viesteistä, jotka tulevat tunnetuilta yhteystiedoilta
+ Älä huomioi keskusteluviestejä
+ Älä näytä ilmoituksia viesteistä, jotka kuuluvat sähköpostikeskusteluun
+ Merkitse viesti luetuksi avattaessa
+ Merkitse viesti luetuksi, kun se avataan katseltavaksi
+ Merkitse luetuksi poistettaessa
+ Merkitse viesti luetuksi, kun se poistetaan
+ Ilmoitusten luokat
+ Määritä uusien viestien ilmoitukset
+ Määritä virhe- ja tilailmoitukset
+ Näytä aina kuvat
+ Ei koskaan
+ Vain yhteystiedoissa olevilta lähettäjiltä
+ Keneltä tahansa
+ Viestien lähettäminen
+ Lainaa alkuperäistä viestiä vastattaessa
+ Alkuperäinen viesti on vastauksessa viesteihin vastattaessa.
+ Vastaa lainauksen jälkeen
+ Kun viestiin vastataan, alkuperäinen viesti on vastauksesi yläpuolella.
+ Poista allekirjoitus lainatusta vastauksesta
+ Lainatun tekstin allekirjoitus poistetaan viesteihin vastattaessa
+ Viestin muoto
+ Pelkkä teksti (kuvat ja muotoilu poistetaan)
+ HTML (kuvat ja muotoilu säilytetään)
+ Automaattinen (pelkkä teksti, ellei vastata HTML-viestiin)
+ Näytä aina Kopio/Piilokopio
+ Vastaanottokuittaus
+ Pyydä aina vastaanottokuittaus
+ Lainauksen tyyli vastattaessa
+ Etuliite (kuten Gmail, Pine)
+ Otsikko (kuten Outlook, Yahoo!, Hotmail)
+ Lähetä lähetetyt viestit
+ Lähetä viestit Lähetetyt-kansioon lähettämisen jälkeen
+ Yleiset asetukset
+ Viestien lukeminen
+ Viestien hakeminen
+ Kansiot
+ Lainauksen etuliite
+ Päästä päähän -salaus
+ Ota käyttöön OpenPGP-tuki
+ Valitse OpenPGP-sovellus
+ Määritä päästä päähän -avain
+ OpenPGP-sovellusta ei ole määritetty
+ Yhdistetty kohteeseen %s
+ Tehdään asetuksia…
+ Talleta kaikki luonnokset salattuina
+ Kaikki luonnokset talletetaan salattuina
+ Salaa luonnokset vain jos salaus on käytössä
+ Kansioiden tarkistus
+ Tilin väri
+ Valitse väri tilin kansio- ja tililistaan
+ Paikallisen kansion koko
+ Hae viestit, joiden koko on enintään
+ 1 KiB
+ 2 KiB
+ 4 KiB
+ 8 KiB
+ 16 KiB
+ 32 KiB
+ 64 KiB
+ 128 KiB
+ 256 KiB
+ 512 KiB
+ 1 MiB
+ 2 MiB
+ 5 MiB
+ 10 MiB
+ mikä tahansa (ei rajoitusta)
+ Synkronoi viestit ajalta
+ milloin vain (ei rajoitusta)
+ tänään
+ viimeiset 2 päivää
+ viimeiset 3 päivää
+ viime viikko
+ viimeiset 2 viikkoa
+ viimeiset 3 viikkoa
+ viime kuun
+ viimeiset 2 kuukautta
+ viimeiset 3 kuukautta
+ viimeiset 6 kuukautta
+ viime vuosi
+ Näytä kansiot
+ Kaikki
+ Vain 1. luokan kansiot
+ 1. ja 2. luokan kansiot
+ Kaikki paitsi 2. luokan kansiot
+ Tarkistettavat kansiot
+ Kaikki
+ Vain 1. luokan kansiot
+ 1. ja 2. luokan kansiot
+ Kaikki paitsi 2. luokan kansiot
+ Ei mitään
+ Push-kysely
+ Kaikki
+ Vain 1. luokan kansiot
+ 1. ja 2. luokan kansiot
+ Kaikki paitsi 2. luokan kansiot
+ Ei mitään
+ Siirrä/kopioi kohdekansiot
+ Kaikki
+ Vain 1. luokan kansiot
+ 1. ja 2. luokan kansiot
+ Kaikki paitsi 2. luokan kansiot
+ Synkronoi etäpoistot
+ Poista viestit, kun ne poistetaan palvelimelta
+ OpenPGP-sovellus puuttuu - poistettiinko se?
+ Kansioiden asetukset
+ Näytä yläryhmässä
+ Näytä yläosassa lähellä kansiolistaa
+ Näytettävän kansion luokka
+ Ei luokkaa
+ 1. luokka
+ 2. luokka
+ Poll-luokka
+ Ei mitään
+ 1. luokka
+ 2. luokka
+ Sama kuin näytettävällä kansiolla
+ Push-luokka
+ Ei luokkaa
+ 1. luokka
+ 2. luokka
+ Sama kuin poll-luokka
+ Kansion ilmoitusluokka
+ Ei luokkaa
+ 1. luokka
+ 2. luokka
+ Sama kuin push-luokka
+ Poista paikalliset viestit
+ Saapuvan postin palvelin
+ Määritä saapuvan postin palvelin
+ Lähtevän postin palvelin
+ Määritä lähtevän postin palvelin (SMTP)
+ Tilin nimi
+ Nimesi
+ Ilmoitukset
+ Värinä
+ Värinä
+ Värinän malli
+ Oletus
+ Malli 1
+ Malli 2
+ Malli 3
+ Malli 4
+ Malli 5
+ Värinöiden lukumäärä
+ Ei käytössä
+ Uuden viestin ilmoitusääni
+ Ilmoituksen valo
+ Ei käytössä
+ Tilin väri
+ Järjestelmän oletusväri
+ Valkoinen
+ Punainen
+ Vihreä
+ Sininen
+ Keltainen
+ Syaani
+ Magenta
+ Viestin kirjoituksen asetukset
+ Kirjoituksen oletusasetukset
+ Aseta oletusarvot: lähettäjä, piilokopio ja allekirjoitus
+ Identiteettien hallinta
+ Määritä vaihtoehtoiset lähettäjän osoitteet ja allekirjoitukset
+ Identiteettien hallinta
+ Identiteetin hallinta
+ Muokkaa identiteettiä
+ Tallenna
+ Uusi identiteetti
+ Piilokopio kaikista viesteistä osoitteeseen
+ Muokkaa
+ Siirrä ylös
+ Siirrä alas
+ Siirrä ylimmäiseksi / aseta oletukseksi
+ Poista
+ Identiteetin kuvaus
+ (Valinnainen)
+ Nimesi
+ (Valinnainen)
+ Sähköpostiosoite
+ (Pakollinen)
+ Vastausosoite
+ (Valinnainen)
+ Allekirjoitus
+ (Valinnainen)
+ Käytä allekirjoitusta
+ Allekirjoitus
+ Oletusidentiteetti
+ Valitse identiteetti
+ Lähetä nimellä
+ Et voi poistaa ainoaa identiteettiäsi
+ Identiteettiä ei voi käyttää ilman sähköpostiosoitetta
+ Vanhemmat viestit ensin
+ Uusimmat viestit ensin
+ Aiheen mukaan aakkosjärjestyksessä
+ Aiheen mukaan käänteisessä aakkosjärjestyksessä
+ Lähettäjän mukaan aakkosjärjestyksessä
+ Lähettäjän mukaan käänteisessä aakkosjärjestyksessä
+ Tähdellä merkityt ensin
+ Merkitsemättömät ensin
+ Lukemattomat ensin
+ Luetut ensin
+ Liitteelliset viestit ensin
+ Liitteettömät viestit ensin
+ Lajittele…
+ Päivämäärän mukaan
+ Saapumisajan mukaan
+ Aiheen mukaan
+ Lähettäjän mukaan
+ Tähden mukaan
+ Luettujen/lukemattomien mukaan
+ Liitteiden mukaan
+ Poista tili
+ Tunnistamaton varmenne
+ Hyväksy-avain
+ Hylkää-avain
+ Del (tai D) - Poista\nR - Vastaa\nA - Vastaa Kaikille\nC - Uusi viesti\nF - Välitä\nM - Siirrä\nV - Arkistoi\nY - Kopioi\nZ - Merkitse Lukemattomaksi(luetuksi)\nG - Merkitse tähdellä\nO - Lajittelun tyyppi\nI - Lajittelun järjestys\nQ - Palaa kansioihin\nS - Valitse/Poista valinta\nJ tai P - Edellinen viesti\nK or N - Seuraava viesti
+ Del (tai D) - Poista\nC - Uusi viesti\nM - Siirrä\nV - Arkistoi\nY - Kopioi\nZ - Merkitse (Lukemattomaksi)luetuksi\nG - Merkitse tähdellä\nO - Lajittelun tyyppi\nI - Lajittelun järjestys\nQ - Palaa kansioihin\nS - Valitse / poista valinta
+ Kansion nimi sisältää
+ Kansiot
+ Näytä kaikki
+ Näytä vain 1. luokan kansiot
+ Näytä 1. ja 2. luokan kansiot
+ Näytä kaikki paitsi 2. luokan kansiot
+ Allekirjoituksen sijainti
+ Ennen lainattua viestiä
+ Lainatun viestin jälkeen
+ Käytä sovellusteemaa
+ Tumma
+ Vaalea
+ Käytä järjestelmän oletusta
+ Näyttö
+ Yleiset
+ Virheenkorjaus
+ Tietosuoja
+ Verkko
+ Toimintoasetukset
+ Tililista
+ Viestilistat
+ Viestit
+ Teema
+ Viestinäkymän teema
+ Viestimuokkaimen teema
+ Kieli
+ Asetuksia ei löytynyt
+ Kiinteä viestiteema
+ Valitse viestinäkymän teema viestiä luettaessa
+ Käytä kiinteää viestinäkymän teemaa
+ Järjestelmän oletus
+ Synkronointi taustalla
+ Ei koskaan
+ Aina
+ Kun Automaattinen synkronointi on valittu
+ Valitse kaikki
+ Push-tilassa tarkistettavien kansioiden enimmäismäärä
+ 5 kansiota
+ 10 kansiota
+ 25 kansiota
+ 50 kansiota
+ 100 kansiota
+ 250 kansiota
+ 500 kansiota
+ 1000 kansiota
+ Animaatio
+ Käytä koreita visuaalisia tehosteita
+ Navigointi äänenvoimakkuusnäppäimillä
+ Viestinäkymä
+ Erilaisia listanäkymiä
+ Näytä \"Yhdistetty saapuneet\"
+ Näytä tähdellä merkittyjen viestien määrä
+ Yhdistetty saapuneet
+ Kaikki viestit Yhdistetty saapuneet -kansiossa
+ Yhdistä
+ Kaikki viestit näytetään Yhdistetty saapuneet -kansiossa
+ Etsi kansioista
+ Kaikki
+ Näytettävissä olevat
+ Ei mitään
+ Ei mitään
+ Automaattinen (%s)
+ Kirjasinkoko
+ Määritä kirjasimen koko
+ Tililista
+ Tilin nimi
+ Tilin kuvaus
+ Kansiolistat
+ Kansion nimi
+ Kansion tila
+ Viestilistat
+ Viestin aihe
+ Lähettäjä
+ Päiväys
+ Esikatselu
+ Viestinäkymä
+ Lähettäjä
+ Vastaanottaja
+ Kopio
+ Piilokopio
+ Tarkemmat otsikkotiedot
+ Viestin aihe
+ Aika ja päivämäärä
+ Viestin sisältö
+ Viestin kirjoitus
+ Tekstinsyöttökentät
+ Oletus
+ Pienin
+ Erittäin pieni
+ Pienempi
+ Pieni
+ Keskikoko
+ Suuri
+ Suurin
+ Toiminnolle ei löytynyt sopivaa ohjelmaa.
+ Lähetys epäonnistui: %s
+ Tallennetaanko luonnos?
+ Tallennetaanko vai hylätäänkö tämä viesti?
+ Tallennetaanko vai hylätäänkö muutokset?
+ Hylkää viesti?
+ Haluatko varmasti hylätä tämän viestin?
+ Valitse kopioitava teksti.
+ Poistetaanko paikalliset viestit?
+ Tämä poistaa kaikki paikalliset viestit kansiosta. Viestejä ei poisteta palvelimelta.
+ Poista viestit
+ Vahvista poisto
+ Haluatko poistaa tämän viestin?
+
+ Haluatko varmasti poistaa tämän viestin?
+ Haluatko varmasti poistaa %1$d viestiä?
+
+ Kyllä
+ Ei
+ Vahvista kaikki luetuiksi
+ Haluatko merkitä kaikki viestit luetuiksi?
+ Vahvista roskakorin tyhjentäminen
+ Haluatko tyhjentää roskakorikansion?
+ Kyllä
+ Ei
+ Vahvista roskapostikansioon siirtäminen
+
+ Haluatko todella siirtää tämän viestin roskapostikansioon?
+ Haluatko todella siirtää %1$d viestiä roskapostikansioon?
+
+ Kyllä
+ Ei
+ Ladataan liitettä
+ »
+ ›
+ Varmuuskopio
+ Muut
+ Vie asetukset
+ Vie
+ Jaa
+ Viedään asetuksia…
+ Asetukset vietiin onnistuneesti
+ Asetusten vienti epäonnistui
+ Tuo asetukset
+ Valitse tiedosto
+ Tuo
+ Asetusten tuonti onnistui
+
+ Anna salasanat
+
+ Kirjaudu sisään
+
+ Kirjaudu sisään ja anna salasanat
+ Asetusten tuonti epäonnistui
+ Asetustiedoston lukeminen epäonnistui
+ Joidenkin asetusten tuonti epäonnistui
+ Tuonti onnistui
+ Salasana vaaditaan
+
+ Sisäänkirjautuminen vaaditaan
+ Ei tuotu
+ Tuontivirhe
+ Myöhemmin
+ Tuo asetukset
+ Tuodaan asetuksia…
+
+ Jotta voit käyttää tiliä \"%s\", sinun on annettava palvelimen salasana.
+ Jotta voit käyttää tiliä \"%s\", sinun on annettava palvelimien salasanat.
+
+ Saapuvan postin palvelimen salasana
+ Lähtevän postin palvelimen salasana
+ Käytä samaa salasanaa lähtevälle palvelimelle
+ Palvelimen nimi: %s
+ Näytä tilin lukemattomien viestien määrä…
+ Tili
+ Tili, jonka osalta näytetään lukemattomien viestien määrä
+ Yhdistetty saapuneet
+ Kansion määrä
+ Näytä lukemattomien määrä vain yksittäisen kansion osalta
+ Kansio
+ Kansio, jonka osalta näytetään lukemattomien viestien määrä
+ Valmis
+ %1$s - %2$s
+ Tiliä ei ole valittu
+ Kansiota ei ole valittu
+ Ei tekstiä
+ Avaa katseltavaksi
+ Jaa linkki
+ Kopioi linkki leikepöydälle
+ Linkki
+ Kopioi linkin teksti leikepöydälle
+ Linkin teksti
+ Kuva
+ Näytä kuva
+ Tallenna kuva
+ Lataa kuva
+ Kopioi kuvan osoite leikepöydälle
+ Kuvan osoite
+ Soita numeroon
+ Tallenna yhteystietoihin
+ Kopioi puhelinnumero leikepöydälle
+ Puhelinnumero
+ Lähetä sähköpostia
+ Tallenna yhteystietoihin
+ Kopioi sähköpostiosoite leikepöydälle
+ Sähköpostiosoite
+ Kaikki
+ 10
+ 25
+ 50
+ 100
+ 250
+ 500
+ 1000
+ Etähaun tulosten rajoitus
+ Lähetetään pyyntöä palvelimelle
+
+ Noudetaan %d tulos
+ Noudetaan %d tulosta
+
+
+ Noudetaan %1$d / %2$d tulos
+ Noudetaan %1$d / %2$d tulosta
+
+ Etähaku epäonnistui
+ Hae
+ Etsi viestejä palvelimelta
+ Etähaku ei ole käytettävissä ilman verkkoyhteyttä.
+ Vaihda väriä kun luettu
+ Eri tausta osoittaa viestin olevan luettu
+ Ketjunäkymä
+ Kutista samaan ketjuun kuuluvat viestit
+ Päivitetään tietokantoja
+ Päivitetään tietokantoja…
+ Päivitetään tilin \"%s\" tietokantaa
+ Näytä jaettu näyttö
+ Aina
+ Ei koskaan
+ Kun laite on vaaka-asennossa
+ Valitse viesti vasemmalta
+ Näytä yhteystietojen kuvat
+ Näytä yhteystietojen kuvat viestilistassa
+ Merkitse kaikki luetuiksi
+ Väritä yhteystietojen kuvat
+ Väritä yhteystietojen puuttuvat kuvat
+ Näytettävät viestitoiminnot
+ Näytä valitut toiminnot viestin Näytä-valikossa
+ Ladataan liitettä…
+ Lähetetään viestiä
+ Tallennetaan luonnosta
+ Noudetaan liitettä…
+ Autentikointi ei onnistu. Palvelin ei ilmoita SASL EXTERNAL -kelpoisuudesta. Tämä saattaa johtua vanhentuneesta asiakasvarmenteesta, tuntemattomasta varmentajasta tai jostakin muusta asetusongelmasta.
+
+ Käytä asiakasvarmennetta
+ Ei asiakasvarmennetta
+ Poista asiakasvarmenteen valinta
+ Asiakasvarmenteen noutaminen epäonnistui aliakselle \"%s\"
+ Lisäasetukset
+ Asiakasvarmenne \"%1$s\" on vanhentunut tai ei ole vielä voimassa (%2$s)
+
+ *Salattu*
+ Lisää yhteystiedoista
+ Kopio
+ Piilokopio
+ Vastaanottaja
+ Lähettäjä
+ Vastausosoite
+ <Tuntematon vastaanottaja>
+ <Tuntematon lähettäjä>
+ Koti
+ Työ
+ Muu
+ Mobiili
+ Tälle tilille ei ole määritelty luonnoskansiota!
+ Tälle tilille ei ole määritetty avainta! Tarkista asetukset.
+ Salauksen tarjoaja käyttää yhteensopimatonta versiota. Tarkista asetuksesi!
+ Yhteyttä salaussovellukseen ei voitu muodostaa: tarkista asetukset tai yritä napsauttaa salauskuvaketta uudestaan!
+ Päästä päähän -salauksen alustaminen epäonnistui, tarkista asetukset
+ PGP/INLINE-tila ei tue liitteitä!
+ Käytä PGP/INLINE-tilaa
+ Poista PGP/INLINE käytöstä
+ Käytä PGP:n pelkkää allekirjoitusta
+ Poista käytöstä PGP:n pelkkä allekirjoitus
+ PGP/INLINE-tila
+ Viesti lähetetään PGP/INLINE-muodossa.\nTätä tulisi käyttää vain yhteensopivuutta varten:
+ Jotkin sovellukset tukevat vain tätä muotoa
+ Allekirjoitukset saattavat rikkontua siirron aikana
+ Liitteet eivät ole tuettuja
+ Selvä!
+ Poista käytöstä
+ Pidä käytössä
+ Selvä!
+ Poista käytöstä
+ Pidä käytössä
+ PGP:n pelkkä allekirjoitustila
+ Tässä tilassa PGP-avaintasi käytetään salakirjoitetun allekirjoituksen luomiseen, mutta sähköpostiviestiä ei salata.
+ Tämä ei salaa sähköpostiviestiä, mutta vahvistaa sen olevan lähetetty käyttäen sinun avainta.
+ Allekirjoitukset saattavat rikkoontua postituslistoille lähettäessä.
+ Allekirjoitukset saatetaan näyttää \"signature.asc\"-liitteinä joissain sovelluksissa.
+ Salatut viestit sisältävät aina allekirjoituksen.
+ Raakateksti
+ päästä päähän -allekirjoitus sisälsi virheen
+ viesti täytyy ladata kokonaan, jotta allekirjoitus voidaan käsitellä
+ sisältää ei-tuetun päästä päähän -allekirjoituksen
+ Viesti on salattu, mutta sen muoto ei ole tuettu.
+ Viesti on salattu, mutta viestin salauksen purkaminen peruttiin.
+ Päästä päähän -allekirjoitettu raakateksti
+ vahvistetulta allekirjoittajalta
+ Allekirjoitettu raakateksti
+ mutta päästä päähän -avain ei vastannut lähettäjää
+ mutta päästä päähän -avain on vanhentunut
+ mutta päästä päähän -avain on kumottu
+ mutta päästä päähän -avainta ei mielletä turvalliseksi
+ tuntemattomalta päästä päähän -avaimelta
+ Salattu
+ mutta salausta purkaessa tapahtui virhe
+ täytyy ladata viesti kokonaan salauksen purkamista varten
+ mutta salaussovellusta ei ole määritetty
+ Salattu
+ mutta ei päästä päähän
+ Päästä päähän salattu
+ vahvistetulta lähettäjältä
+ Salattu
+ tuntemattomalta päästä päähän -avaimelta
+ mutta päästä päähän -avain ei vastannut lähettäjää
+ mutta päästä päähän -avain on vanhentunut
+ mutta päästä päähän -avain on kumottu
+ mutta päästä päähän -avainta ei mielletä turvalliseksi
+ mutta päästä päähän -tiedot sisältävät virheitä
+ mutta salausta ei mielletä turvalliseksi
+ OK
+ Etsi avainta
+ Näytä allekirjoittaja
+ Näytä lähettäjä
+ Yksityiskohdat
+ Avaa lukitus
+ Tätä osaa ei salattu, joten se saattaa olla epäluotettava.
+ Suojaamaton liite
+ Ladataan…
+ Salauksen purkaminen peruttiin.
+ Yritä uudelleen
+ Salattu viesti tulee ladata, jotta sen salaus voidaan avata.
+ Virhe sähköpostin salausta avatessa
+ Erikoismerkit eivät ole tällä hetkellä tuettuja!
+ Virhe osoitetta jäsennettäessä!
+ Piilota salaamattomat allekirjoitukset
+ Vain salatut allekirjoitukset näytetään
+ Kaikki allekirjoitukset näytetään
+ Salaus ei ole käytettävissä pelkässä allekirjoitustilassa!
+ Allekirjoittamaton teksti
+ Tämä sähköpostiviesti on salattu
+ Tämä viesti on salattu OpenPGP:llä.\nLukeaksesi viestin sinun tulee asentaa ja määrittää yhteensopiva OpenPGP-sovellus.
+ Siirry asetuksiin
+ K-9-viestiluettelo
+ Ladataan viestejä…
+ Salaus ei ole mahdollista
+ Jotkut valitut vastaanottajat eivät tue tätä ominaisuutta!
+ Käytä salausta
+ Poista salaus käytöstä
+ Viestin salaus takaa, ettei niitä voi lukea kukaan muu kuin vastaanottaja.
+ Salaus ilmestyy näkyviin vain jos kaikki vastaanottajat tukevat sitä, ja kyseiset tahot ovat lähettäneet sinulle sähköpostia aiemmin.
+ Ota salaus käyttöön tai poista käytöstä napauttamalla tätä kuvaketta.
+ Selvä
+ Takaisin
+ Poista salaus käytöstä
+ OpenPGP-salaus
+ Automattisen salauksen kahdenkeskeinen tila
+ Automattisen salauksen kahdenkeskeinen tila
+ Viestit salataan normaalisti valinnan mukaan, tai kun vastataan salattuun viestiin.
+ Jos sekä lähettäjä että vastaanottaja ottavat käyttöön kahdenkeskeisen tilan, salaus otetaan käyttöön oletusarvoisesti.
+ Voit napauttaa tästä saadaksesi lisätietoja.
+ Yleiset asetukset
+ OpenPGP-sovellusta ei ole asennettu
+ Asenna
+ K-9 Mail vaatii OpenKeychainin päästä päähän -salausta varten.
+ Salattu viesti
+ Salaa viestien aiheet
+ Ei välttämättä tuettu kaikkien vastaanottajien osalta
+ Sisäinen virhe: virheellinen tili!
+ Virhe yhdistäessä kohteeseen %s!
+ Lähetä Autocrypt-asetusviesti
+ Jaa päästä päähän -salauksen asetukset turvallisesti muiden laitteiden kanssa
+ Autocrypt-asetusviesti
+ Autocrypt-asetusviesti jakaa päästä päähän -salauksen asetukset turvallisesti muiden laitteidesi kanssa.
+ Lähetä asetusviesti
+ Viesti lähetetään osoitteeseesi:
+ Luodaan asetusviestiä…
+ Lähetetään viesti:
+ Viimeistelläksesi avaa viesti toisella laitteellasi ja kirjoita asetuskoodi.
+ Näytä asetuskoodi
+ Autocrypt-asetusviesti
+ Tämä viesti sisältää kaikki tiedot Autocryptin asetusten sekä salausavaimesi siirtämiseksi turvallisesti alkuperäisestä laitteestasi.
Aseta uusi laitteesi käyttämään Autocryptiä noudattamalla ohjeita, jotka uusi laitteesi näyttää.
diff --git a/app/ui/legacy/src/main/res/values-fr/strings.xml b/app/ui/legacy/src/main/res/values-fr/strings.xml
index 0db962c08ebcf5cce3dc62205c001f836660bf56..83643a650de166fbacdba5b2e058062289fbeeb2 100644
--- a/app/ui/legacy/src/main/res/values-fr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fr/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- Tous droits réservés 2008 à %s The K-9 Dog Walkers. Parties protégées par droits d’auteur 2006 à%s le projet à code source ouvert Android.
- Les auteurs de Courriel K-9 Mail
+ Les auteurs de Courriel MailCode sourceLicence Apache, Version 2.0Projet à code source ouvert
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.Auteurs
- Renseignements sur la révisionLire les courrielsPermet à cette application de lire vos courriels.Supprimer les courrielsPermet à cette application de supprimer vos courriels.About MailComptes
- DossiersAvancéRédigerRépondre
@@ -49,12 +46,6 @@
Transférer en tant que fichier jointChoisir un compteChoisir un dossier
- Scrutation. %s:%s%s
- Récup. entêtes %s:%s%s
- Envoi %s%s
- Prép %s:%s%s
- \u0020%d/%d
- Synchronisation désactivée%d sélectionné(s)SuivantPrécédent
@@ -62,10 +53,7 @@
OKAnnulerEnvoyer
- RenvoyerL’objet est vide. Touchez de nouveau pour envoyer quand même
- Sélectionner
- DessélectionnerRépondreRépondre à tousSupprimer
@@ -102,9 +90,11 @@
Ajouter une étoileSupprimer l’étoileCopier
+ Se désabonnerAfficher les en-têtesL’adresse a été copiée dans le presse-papiers
+ Les adresses ont été copiées dans le presse-papiersLes adresses ont été copiées dans le presse-papiersLe texte de l’objet a été copié dans le presse-papiers
@@ -132,11 +122,10 @@ jusqu’à %d de plus%.1f Mo%.1f ko%d o
- La taille du compte « %s » est passée de %s à %s
- Compactage du compte « %s »Nouveau courriel%d nouveau courriel
+ %d nouveaux courriels%d nouveaux courriels%d non lus (%s)
@@ -205,20 +194,14 @@ jusqu’à %d de plusÀ :Cc :Cci :
- Ouvrir
- EnregistrerImpossible d’enregistrer le fichier joint.Afficher les imagesImpossible de trouver un visualiseur pour %s.Télécharger le courriel complet
- Impossible d’afficher le courrielpar %1$s
- Tous les en-têtes ont été téléchargés, mais il n’y en a aucun autre à afficher.
- La récupération d’en-têtes supplémentaires de la base de données ou du serveur de courriel a échouée.Plus de cet expéditeurDe %s
- Déboguer / Effacer le corps du courrielLe courriel a été abandonnéLe courriel a été enregistré comme brouillonAfficher les étoiles
@@ -250,13 +233,9 @@ jusqu’à %d de plus
Marquer tous les courriels comme lusSupprimer (d’une notification)Cacher le client de courriel
- Supprimer l’agent utilisateur K-9 des en-têtes de courriels
+ Supprimer l’agent utilisateur Mail des en-têtes de courrielsCacher le fuseau horaireUtiliser UTC au lieu du fuseau horaire local dans les en-têtes de courriel et de réponse
- Cacher l’objet dans les notifications
- Jamais
- Quand l’appareil est verrouillé
- ToujoursAfficher le bouton SupprimerJamaisPour une notification de courriel unique
@@ -276,7 +255,15 @@ jusqu’à %d de plus
Configurer un nouveau compteAdresse courrielMot de passe
- Afficher le mot de passe
+ Pour utiliser ce compte de courriel avec Courriel Mail, vous devez vous connecter et accorder à l’appli l’accès à vos courriels.
+
+ Connexion
+
+ Connexion avec Google
+
+ Pour visualiser votre mot de passe ici, activez le verrouillage de l’écran sur cet appareil.
+ Confirmer votre identité
+ Déverrouiller l’écran pour visualiser votre mot de passeConfiguration manuelleRécupération des renseignements de compte\u2026
@@ -296,6 +283,7 @@ jusqu’à %d de plus
Mot de passe, envoyé sans sécuritéMot de passe chiffréCertificat client
+ OAuth 2.0Paramètres du serveur entrantNom d’utilisateurMot de passe
@@ -314,12 +302,7 @@ jusqu’à %d de plus
Ne pas supprimer sur le serveurSupprimer sur le serveurMarquer comme lu sur le serveur
- Utiliser la compression sur le réseau :
- mobile
- Wi-Fi
- autre
- Stockage externe (carte SD)
- Stockage interne habituel
+ Utiliser la compressionEffacer les courriels supprimés du serveurImmédiatementLors de la scrutation
@@ -334,11 +317,8 @@ jusqu’à %d de plus
Afficher seulement les dossiers auxquels je suis abonnéOuvrir automatiquement le dossierChemin OWA
- FacultatifChemin d’authentification
- FacultatifAlias de la boîte de courriels
- FacultatifParamètres du serveur sortantServeur SMTPPort
@@ -350,7 +330,6 @@ jusqu’à %d de plus
« %1$s = %2$s » n’est pas valide avec « %3$s = %4$s »Paramétrage invalide : %sOptions du compte
- Compacter le compteFréquence de scrutation des dossiersJamaisToutes les 15 minutes
@@ -361,8 +340,6 @@ jusqu’à %d de plus
Toutes les 6 heuresToutes les 12 heuresToutes les 24 heures
- Activer le courriel poussé pour ce compte
- Si votre serveur le prend an charge, les nouveaux courriels apparaîtront instantanément. Cette option peut considérablement améliorer ou dégrader les performances.Actualiser la connexion IDLEToutes les 2 minutesToutes les 3 minutes
@@ -373,7 +350,6 @@ jusqu’à %d de plus
Toutes les 48 minutesToutes les 60 minutesM’avertir lors de l’arrivée de courriels
- M’avertir lors de la relève des courrielsNombre de courriels à afficher10 courriels25 courriels
@@ -391,12 +367,14 @@ jusqu’à %d de plus
Nom d’utilisateur ou mot de passe erroné.\n(%s)Le serveur a présenté un certificat SSL invalide. Cela est parfois causé par une mauvaise configuration du serveur. Parfois, quelqu’un essaie de vous attaquer ou d’attaquer votre serveur de courriel. Si vous n’êtes pas sûr de ce qu’il se passe, touchez Rejeter et contactez les gestionnaires de votre serveur de courriel.\n\n(%s)Impossible de se connecter au serveur.\n(%s)
+ L’autorisation a été annulée
+ Échec d’autorisation avec l’erreur suivante :%s
+ OAuth 2.0 n’est actuellement pas pris en charge par ce fournisseur.
+ L’appli n’a pas trouvé de navigateur à utiliser pour accorder l’accès à votre compte.Modifier les détailsContinuerAvancéParamètres du compte
- Compte par défaut
- Envoyer par défaut les courriels avec ce compteNotifications de nouveaux courrielsDossiers des notificationsTous
@@ -418,8 +396,6 @@ jusqu’à %d de plus
Marquer un courriel comme lu quand il est ouvert pour être visualiséMarquer comme lu lors de la suppressionMarquer un message comme lu lors de la suppression
- Paramètres de notification
- Ouvrir les paramètres de notification du systèmeToujours afficher les imagesNonPour les contacts uniquement
@@ -461,9 +437,6 @@ jusqu’à %d de plus
Fréquence de scrutation des dossiersCouleur du compteLa note de couleur du compte utilisée dans la liste des dossiers et des comptes
- Pas de couleur
- Couleur de la DEL de notification
- La couleur de clignotement de la DEL de votre appareilTaille du fichier localRécupérer les courriels jusqu’à1 Kio
@@ -551,7 +524,6 @@ jusqu’à %d de plus
NotificationsVibrationVibration
- Vibrer dès réception d’un courrielModèles de vibrationpar défautmodèle 1
@@ -683,10 +655,6 @@ jusqu’à %d de plus
Dans les vues des courrielsDans les vues en listeAfficher la boîte de réception unifiée
- %s%s
- - Non lu
- Tous les courriels
- Tous les courriels dans les dossiers pouvant être recherchésBoîte de réception unifiéeTous les courriels dans les dossiers unifiésUnifier
@@ -719,8 +687,6 @@ jusqu’à %d de plus
ObjetHorodatageCorps du courriel
- %d %%
- %1$s: %2$sRédaction de courrielChamp de saisie de textePar défaut
@@ -746,6 +712,7 @@ jusqu’à %d de plus
Voulez-vous supprimer ce courriel ?Voulez-vous vraiment supprimer ce courriel ?
+ Voulez-vous vraiment supprimer les %1$d courriels ?Voulez-vous vraiment supprimer les %1$d courriels ?Oui
@@ -759,6 +726,7 @@ jusqu’à %d de plus
Confirmer le déplacement vers le dossier des pourrielsVoulez-vous déplacer ce courriel vers le dossiers des pourriels\u00A0?
+ Voulez-vous déplacer les %1$d courriels vers le dossiers des pourriels ?Voulez-vous déplacer les %1$d courriels vers le dossiers des pourriels ?Oui
@@ -778,12 +746,19 @@ jusqu’à %d de plus
Sélectionner un fichierImportationLes paramètres ont été importés avec succès
+
Veuillez saisir vos mots de passe
+
+ Veuillez vous connecter
+
+ Veuillez vous connecter et saisir les mots de passeÉchec d’importation des paramètresÉchec de lecture du fichier de paramètresÉchec d’importation de certains paramètresA été importé avec succèsLe mot de passe est exigé
+
+ La connexion est requiseN’a pas été importéÉchec d’importationPlus tard
@@ -791,7 +766,8 @@ jusqu’à %d de plus
Importation des paramètres…Pour utiliser le compte « %s », vous devez indiquer le mot de passe du serveur.
- Pour utiliser le compte « %s », vous devez indiquer les mots de passe du serveur.
+ Pour utiliser le compte « %s », vous devez indiquer les mots de passe des serveurs.
+ Pour utiliser le compte « %s », vous devez indiquer les mots de passe des serveurs.Mot de passe du serveur entrantMot de passe du serveur sortant
@@ -842,10 +818,12 @@ jusqu’à %d de plus
Envoi de la requête au serveurRécupération d’%d résultat
+ Récupération de %d résultatsRécupération de %d résultatsRécupération d’%1$d résultat de %2$d
+ Récupération de %1$d résultats de %2$dRécupération de %1$d résultats de %2$dÉchec de recherche distante
@@ -974,21 +952,11 @@ jusqu’à %d de plus
Toutes les signatures seront affichéesLe chiffrement n’est pas proposé en mode signature-seulement !Texte non signé
- Avertissement d’obsolescence d’APG
- APG n’est plus maintenu !
- C’est pourquoi APG n’est plus pris en charge par Courriel K-9 Mail.
- Le développement s’est arrêté début 2014.
- Des problèmes de sécurité non résolus persistent
- Vous pouvez toucher ici (page en anglais) pour en apprendre davantage.
- D’accord !
- APGLe courriel est chiffréCe courriel a été chiffré avec OpenPGP.\nPour le lire, vous devez installer et configurer une appli compatible avec OpenPGP.Aller dans Paramètres
- Liste des courriels de K-9
+ Liste des courriels de MailChargement des courriels…
- Échec de récupération de la liste de dossiers
- Erreur d’obtention de l’état du destinataire par le fournisseur OpenPGP !Le chiffrement est impossibleCertains des destinataires sélectionnés ne prennent pas cette fonction en charge !Activer le chiffrement
@@ -1008,7 +976,7 @@ jusqu’à %d de plus
Paramètres générauxAucune appli OpenPGP n’est installéeInstaller
- Courriel K-9 Mail a besoin d’OpenKeychain pour le chiffrement de bout en bout.
+ Courriel Mail a besoin d’OpenKeychain pour le chiffrement de bout en bout.Message chiffréChiffrer l’objet des courrielsPourrait ne pas être pris en charge par certains destinataires
@@ -1029,8 +997,6 @@ jusqu’à %d de plus
Une erreur est survenue lors de l’envoi du message. Veuillez vérifier votre connexion connectivité réseau et la configuration du serveur sortant.ActiverDésactiver
- Ouvrir
- FermerAutoriser l’accès au contactAfin de fournir des suggestions de contact et d’afficher les noms et les photos des contacts, l’appli a besoin d’accéder à vos contacts.
@@ -1041,7 +1007,7 @@ jusqu’à %d de plus
Inactif tant que le réseau n’est pas disponibleTouchez pour en apprendre davantage.Renseignements du pousser
- En utilisant le pousser, Courriel K-9 Mail maintient une connexion vers le serveur de courriel. Android exige l’affichage d’une notification continue quand l’appli est active en arrière-plan. %s
+ En utilisant le pousser, Courriel Mail maintient une connexion vers le serveur de courriel. Android exige l’affichage d’une notification continue quand l’appli est active en arrière-plan. %sCependant, Android vous permet aussi de cacher la notification.En apprendre davantageConfigurer la notification
diff --git a/app/ui/legacy/src/main/res/values-gd/strings.xml b/app/ui/legacy/src/main/res/values-gd/strings.xml
index 42c18450ab76c7c9009412a938e10c285e6d0b7b..a789d8c5844b6d142d0287b6bf8a13aa00011905 100644
--- a/app/ui/legacy/src/main/res/values-gd/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gd/strings.xml
@@ -1,817 +1,751 @@
-
-
-
- Post
- Cunntasan a’ phuist
- Post gun leughadh
-
- Còir-lethbhreac 2008-%s Luchd-coiseachd nan con K-9. Cuid dhen chòir-lethbhreac 2006-%s Android Open Source Project.
-
- Fàilte gu aplacaid a’ phuist
- Seo aplacaid bunaiteach a’ phuist airson /e/
-
- -- Chaidh a chur o phost /e/.
-
- Thèid an cunntas “%s” a thoirt air falbh o aplacaid a’ phuist.
-
- Puist-d a leughadh
- Leig leis an aplacaid seo na puist-d agad a leughadh.
- Puist-d a sguabadh às
- Leig leis an aplacaid seo na puist-d agad a sguabadh às.
- Cunntasan
- Pasganan
- Adhartach
- Sgrìobh
- Freagair
- Freagair na h-uile
- Sìn air adhart
- Tagh pasgan
- Ceasnaich %s:%s%s
- A’ faighinn nam bannan-cinn %s:%s%s
- A’ cur %s%s
- Proc %s:%s%s
- %d/%d
- Chaidh an sioncronachadh a chur à comas
- %d air a thaghadh
- Air adhart
- Air ais
-
- Ceart ma-thà
- Sguir dheth
- Cuir
- Cuir a-rithist
- Cha do shònraich thu cuspair, thoir gnogag às ùr airson a chur co-dhiù
- Tagh
- Dì-thagh
- Freagair
- Freagair na h-uile
- Sguab às
- Tasg-lann
- Spama
- Sìn air adhart
- Gluais
- Cuir…
- Ath-fhaidhlich…
- Deiseil
- Tilg air falbh
- Sàbhail mar dhreachd
- Thoir sùil airson post
- Cuir na teachdaireachdan
- Ath-nuadhaich liosta nam pasgan
- Lorg pasgan
- Cuir cunntas ris
- Sgrìobh
- Lorg
- Toraidhean an luirg
- Roghainnean
- Roghainnean a’ chunntais
- Thoir an cunntas air falbh
- Comharraich gun deach a leughadh
- Co-roinn
- Tagh seòladair
- Cuir rionnag ris
- Thoir an rionnag air falbh
- Lethbhreac
- Seall na bannan-cinn
-
- Chaidh an seòladh a chur air an stòr-bhòrd
- Chaidh na seòlaidhean a chur air an stòr-bhòrd
- Chaidh na seòlaidhean a chur air an stòr-bhòrd
- Chaidh na seòlaidhean a chur air an stòr-bhòrd
-
- Cleachd ùrlar dorcha ’na àite
- Cleachd ùrlar soilleir ’na àite
- Comharraich nach deach a leughadh
- Cuidhteas-leughaidh
- Iarraidh seo cuidhteas-leughaidh
- Chan iarr seo cuidhteas-leughaidh
- Cuir ceanglachan ris
- Falamhaich an sgudal
- Thoir air falbh gu tur
- Mu dhèidhinn
- Roghainnean
-
- (Gun chuspair)
- Gun seòladair
- A’ luchdadh nan teachdaireachdan…
- Mearachd leis a’ cheangal
- Cha deach an teachdaireachd a lorg
- Mearachd a’ luchdadh na teachdaireachd
- Luchdaich suas ri %d a bharrachd
- Bha an cunntas “%s” %s is tha e %s a-nis
- A’ dùmhlachadh a’ chunntais “%s”
- Post ùr
-
- %d teachdaireachd ùr
- %d theachdaireachd ùr
- %d teachdaireachdan ùra
- %d teachdaireachd ùr
-
- %d gun leughadh (%s)
- ⁊ %1$d a bharrachd air %2$s
- Freagair
- Comharraich gun deach a leughadh
- Comharraich gun deach iad uile a leughadh
- Sguab às
- Sguab às na h-uile
- Tasglannaich
- Tasglannaich na h-uile
- Spama
- Mearachd an teisteanais aig %s
- Thoir sùil air roghainnean an fhrithealaiche agad
- Dh’fhàillig an dearbhadh
- Dh’fhàillig dearbhadh a’ chunntais %s. Ùraich roghainnean an fhrithealaiche agad.
- A’ toirt sùil airson post: %s:%s
- A’ toirt sùil airson post
- A’ cur a’ phuist: %s
- A’ cur a’ phuist
- :
- Teachdaireachdan
- An còrr
- A-steach
- A-mach
- Dreachdan
- Sgudal
- Air a chur
- Cha deach cuid dhe na teachdaireachdan a chur
- Cuir an comas logadh airson dì-bhugachadh
- Logaich fiosrachadh diagnosachd a bharrachd
- Logaich fiosrachadh dìomhair
- Dh’fhaoidte gun leig seo ris faclan-faire sna logaichean.
- Luchdaich barrachd theachdaireachdan
- Gu:%s
- Cuspair
- Teacsa na teachdaireachd
- An t-earr-sgrìobhadh
- -------- An teachdaireachd thùsail --------
- Cuspair:
- Air a chur:
- O:
- Gu:
- Cc:
- Sgrìobh %s:
- Sgrìobh %2$s%1$s:
- Feumaidh tu co-dhiù aon neach a chur ris a gheibh e.
- Tha rud ann an raon nan daoine a gheibh e a tha leth-chrìochnaichte!
- Cha b’ urrainn dhuinn seòladh puist-d a lorg dhan neach-aithne seo.
- Chan urrainn dhuinn cuid dhe na ceanglachain a shìneadh air adhart oir cha deach an luchdadh a-nuas.
- Thoir an luaidh air falbh
- Crìoch na luaidhe
- O: %s <%s>
- Gu:
- Cc:
- Bcc:
- Fosgail
- Sàbhail
- Seall na dealbhan
- Chan urrainn dhuinn aplacaid a lorg a sheallas %s.
- Luchdaich a-nuas an teachdaireachd shlàn
- Chan urrainn dhuinn an teachdaireachd a shealltainn
- slighe %1$s
-
- Chaidh gach bann-cinn a luchdadh a-nuas ach chan eil bann-cinn eile ann a ghabhas a shealltainn.
- Cha b’ urrainn dhuinn bannan-cinn a bharrachd fhaighinn on stòr-dàta no o fhrithealaiche a’ phuist.
- Barrachd on t-seòladair seo
- Dì-bhugaich / Falamhaich bodhaig na teachdaireachd
- Chaidh an teachdaireachd a thilgeil air falbh
- Chaidh an teachdaireachd a shàbhaladh mar dhreachd
- Seall na rionnagan
- Tha rionnagan a’ comharradh teachdaireachdan le bratach
- Loidhnichean a ro-shealladh
- Seall ainmean nan seòladairean
- Seall ainmean nan seòladairean seach na seòlaidhean puist-d aca
- An seòladair os cionn a’ chuspair
- Seall ainmean nan seòladairean os cionn a’ chuspair seach foidhe
- Seall ainmean an luchd-aithne
- Cleachd ainmean o luchd-aithne Android ma tha gin ann
- Cuir dathan air an luchd-aithne
- Cuir dathan air ainmean ann an liosta an luchd-aithne agad
- Cruthan-clò le leud socraichte
- Cleachd cruth-clò le leud socraichte nuair a thèid taisbeanadh ann an teacsa lom a shealltainn
- Fèin-fhreagair teachdaireachdan
- Crùb teachdaireachdan ach am freagair iad ris an sgrìn
- Till gun liosta an dèidh sguabaidh às
- Till gun liosta an dèidh teachdaireachd a sguabadh às
- Seall an ath-theachdaireachd an dèidh sguabaidh às
- Seall an ath-theachdaireachd a ghnàth an dèidh teachdaireachd a sguabaidh às
- Dearbh gnìomhan
- Seall còmhradh uair sam bith a nì thu gnìomh sònraichte
- Sguab às
- Sguab às an fheadhainn le rionnag (ann an sealladh nan teachdaireachdan)
- Spama
- Tilg an teachdaireachd air falbh
- Comharraich gun deach gach teachdaireachd a leughadh
- Sguab às (on bhrath)
- Falaich cliant a’ phuist
- Thoir air falbh an raon Mail User-Agent o bhannan-cinn phost
- Falaich an roinn-tìde
- Cleachd UTC an àite na roinn-tìde ionadail ann am bannan-cinn phost is fhreagairtean
- Falaich an cuspair ann am brathan
- Chan ann idir
- Nuair a bhios an t-uidheam glaiste
- An-còmhnaidh
- Seall am putan “Sguab às”
- Chan ann idir
- Airson brath aon teachdaireachd
- An-còmhnaidh
- Brathan air an sgrìn-ghlasaidh
- Na seall brathan air an sgrìn-ghlasaidh
- Ainm na h-aplacaid
- Co mheud teachdaireachd gun leughadh
- Co mheud teachdaireachd is seòladair
- An aon rud nuair nach bi an sgrìn glaiste
- Amannan sàmhach
- Cuir seirm, srann is boillsgeadh à comas rè na h-oidhche
- Cuir na brathan à comas
- Cuir na brathan à comas buileach rè nan amannan sàmhach
- Tòisichidh an t-àm sàmhach
- Thig an t-àm sàmhach gu crìoch
- Suidhich cunntas ùr
- Seòladh puist-d
- Facal-faire
- Seall am facal-faire
- Suidheachadh de làimh
-
- A’ faighinn fiosrachadh a’ chunntais…
- A’ sgrùdadh roghainnean an fhrithealaiche a-steach…
- A’ sgrùdadh roghainnean an fhrithealaiche a-mach…
- ’Ga dhearbhadh…
- A’ faighinn roghainnean a’ chunntais…
- A’ sgur dheth…
- Cha mhòr deiseil!
- Thoir ainm air a’ chunntas seo (roghainneil):
- Cuir a-steach d’ ainm (chithear seo ann an teachdaireachdan a thèid a-mach):
- Seòrsa a’ chunntais
- Dè seòrsa cunntas a tha seo\?
- POP3
- IMAP
- Facal-faire àbhaisteach
- Facal-faire, ’ga chur gun tèarainteachd
- Facal-faire crioptaichte
- Teisteanas a’ chliant
- Roghainnean an fhrithealaiche a-steach
- Ainm-cleachdaiche
- Facal-faire
- Teisteanas a’ chliant
- Frithealaiche POP3
- Frithealaiche IMAP
- Frithealaiche Exchange
- Port
- Tèarainteachd
- Dearbhadh
- Chan eil gin
- SSL/TLS
- STARTTLS
- Chan eil “%1$s = %2$s” dligheach mu choinneamh “%3$s = %4$s”
- Nuair a sguabas mi teachdaireachd às
- Na sguab às an fhrithealaiche e
- Sguab às an fhrithealaiche e
- Comharraich air an fhrithealaiche gun deach a leughadh
- Cleachd dùmhlachadh air lìonra:
- Mobile
- WiFi
- Eile
- Stòras air an taobh a-muigh (cairt SD)
- Stòras inntearnail àbhaisteach
- Ma sguabas mi às teachdaireachd, sguab às an fhrithealaiche e
- Sa bhad
- Aig an ath-cheasnachadh
- A làimh
- Mothaich dha IMAP Namespace gu fèin-obrachail
- Ro-leasachan slighe IMAP
- Pasgan nan dreachdan
- Pasgan a’ phuist chuirte
- Pasgan an sgudail
- Pasgan nan tasg-lann
- Pasgan an spama
- Na seall ach pasganan a tha fo-sgrìobhadh agam aca
- Leudaich am pasgan gu fèin-obrachail
- Slighe OWA
- Roghainneil
- Slighe an dearbhaidh
- Roghainneil
- Alias a’ bhogsa-phuist
- Roghainneil
- Roghainnean an fhrithealaiche a-mach
- Frithealaiche SMTP
- Port
- Tèarainteachd
- Iarr clàradh a-steach.
- Ainm-cleachdaiche
- Facal-faire
- Dearbhadh
- Chan eil “%1$s = %2$s” dligheach mu choinneamh “%3$s = %4$s”
- Suidheachadh mì-dhligheach: %s
- Roghainnean a’ chunntais
- Dùmhlaich an cunntas
- Dè cho tric ’s a dh’iarras sinn am post\?
- Chan ann idir
- Gach cairteal na h-uarach
- Gach leth-uair a thìde
- Gach uair a thìde
- Gach 2 uair a thìde
- Gach 3 uairean a thìde
- Gach 6 uairean a thìde
- Gach 12 uair a thìde
- Gach 24 uair a thìde
- Cuir putadh a’ phuist an comas airson a’ chunntais seo
- Ma bheir am frithealaiche agad taic dha, nochdaidh teachdaireachdan ùra sa bhad. Faodaidh buaidh mhòr (math no dona) a bhith aig an roghainn seo air dèanadas.
- Ath-nuadhaich an ceangal IDLE
- Gach 2 mhionaid
- Gach 3 mionaidean
- Gach 6 mionaidean
- Gach 12 mhionaid
- Gach 24 mionaid
- Gach 36 mionaid
- Gach 48 mionaid
- Gach uair a thìde
- Innis dhomh nuair a ruigeas post
- Innis dhomh nuair a thathar a’ toirt sùil airson post
- Co mheud teachdaireachd a thèid a shealltainn
- 10 teachdaireachdan
- 25 teachdaireachd
- 50 teachdaireachd
- 100 teachdaireachd
- 250 teachdaireachd
- 500 teachdaireachd
- 1,000 teachdaireachd
- 2.500 teachdaireachd
- 5,000 teachdaireachd
- 10,000 teachdaireachd
- na h-uile teachdaireachd
- Chan urrainn dhut lethbhreac a dhèanamh de theachdaireachd, no a ghluasad, mur eil e sioncronaichte leis an fhrithealaiche
- Cha b’ urrainn dhuinn an suidheachadh a choileanadh
- Tha an t-ainm-cleachdaiche no facal-faire ceàrr.
+
+
+
+ Post
+ Cunntasan puist
+ Post gun leughadh
+
+
+ Fàilte gu aplacaid a’ phuist
+ <p> Tha aplacaid a’ phuist ’na aplacaid shaor agus chumhachdach airson post-d air Android. </p><p> Tha na gleusan leasaichte aige a’ gabhail a-staigh: </p> <ul> <li>Put post slighe IMAP IDLE</li> <li>Dèanadas nas fheàrr</li> <li>Ath-fhaidhleadh theachdaireachdan</li> <li>Eàrr-sgrìobhaidhean puist-d</li> <li>Bcc dhut fhèin</li> <li>Fo-sgrìobhaidhean phasganan</li> <li>Sioncronachadh nam pasgan air fad</li> <li>Rèiteachadh seòladh nam freagairt</li> <li>Ath-ghoiridean a’ mheur-chlàir</li> <li>Taic nas fheàrr airson IMAP</li> <li>Sàbhaladh de cheanglachain air SD</li> <li>Falamhachadh an sgudail</li> <li>Seòrsachadh theachdaireachdan</li> <li>…is mòran a bharrachd</li> </ul> <p> Thoir an aire nach cuir aplacaid a’ phuist taic ri cuid mhòr de chunntasan saora Hotmail agus mar iomadh prògram puist-d eile, tachraidh rudan neònach nuair a bhruidhneas e ri Microsoft Exchange. </p><p> Fàilte air bugaichean, com-pàirteachas, iarrtasan airson gleusan ùra is ceistean aig <a href=https://github.com/k9mail/k-9/>https://github.com/k9mail/k-9/</a>. </p>
+
+ -- Chaidh a chur le post /e/.
+
+ Thèid an cunntas “%s” a thoirt air falbh on phost.
+
+ Cunntasan
+ Adhartach
+ Sgrìobh
+ Freagair
+ Freagair na h-uile
+ Sìn air adhart
+ Tagh pasgan
+ %d air a thaghadh
+ Air adhart
+ Ais ais
+
+ Ceart ma-thà
+ Sguir dheth
+ Cuir
+ Tha an cuspair falamh, thoir gnogag a-rithist airson a chur co-dhiù
+ Freagair
+ Freagair na h-uile
+ Sguab às
+ Tasg-lann
+ Spama
+ Sìn air adhart
+ Gluais
+ Cuir…
+ Ath-fhaidhlich…
+ Deiseil
+ Tilg air falbh
+ Sàbhail mar dhreachd
+ Thoir sùil airson post
+ Cuir na teachdaireachdan
+ Ath-nuadhaich liosta nam pasgan
+ Lorg sa phasgan
+ Cuir cunntas ris
+ Sgrìobh
+ Lorg sa phost
+ Toraidhean an luirg
+ Roghainnean
+ Roghainnean a’ chunntais
+ Thoir an cunntas air falbh
+ Comharraich gun deach a leughadh
+ Co-roinn
+ Tagh seòladair
+ Cuir rionnag ris
+ Thoir an rionnag air falbh
+ Dèan lethbhreac
+ Seall na bannan-cinn
+
+ Chaidh an seòladh a chur air an stòr-bhòrd
+ Chaidh na seòlaidhean a chur air an stòr-bhòrd
+ Chaidh na seòlaidhean a chur air an stòr-bhòrd
+ Chaidh na seòlaidhean a chur air an stòr-bhòrd
+
+ Cleachd ùrlar dorcha ’na àite
+ Cleachd ùrlar soilleir ’na àite
+ Comharraich nach deach a leughadh
+ Cuidhteas-leughaidh
+ Thèid cuidhteas-leughaidh iarraidh
+ Cha dèid cuidhteas-leughaidh iarraidh
+ Cuir ceanglachan ris
+ Falamhaich an sgudal
+ Thoir air falbh gu tur
+ Mu dhèidhinn
+ Roghainnean
+
+ (Gun chuspair)
+ Gun seòladair
+ A’ luchdadh nan teachdaireachdan…
+ Mearachd leis a’ cheangal
+ Cha deach an teachdaireachd a lorg
+ Mearachd a’ luchdadh na teachdaireachd
+ Luchdaich suas ri %d a bharrachd
+ Post ùr
+
+ %d teachdaireachd ùr
+ %d theachdaireachd ùr
+ %d teachdaireachdan ùra
+ %d teachdaireachd ùr
+
+ %d gun leughadh (%s)
+ ⁊ %1$d a bharrachd air %2$s
+ Freagair
+ Comharraich gun deach a leughadh
+ Comharraich gun deach iad uile a leughadh
+ Sguab às
+ Sguab às na h-uile
+ Tasglannaich
+ Tasglannaich na h-uile
+ Spama
+ Mearachd an teisteanais aig %s
+ Thoir sùil air roghainnean an fhrithealaiche agad
+ Dh’fhàillig an dearbhadh
+ Dh’fhàillig dearbhadh a’ chunntais %s. Ùraich roghainnean an fhrithealaiche agad.
+ A’ toirt sùil airson post: %s:%s
+ A’ toirt sùil airson post
+ A’ cur a’ phuist: %s
+ A’ cur a’ phuist
+ :
+ Teachdaireachdan
+ An corr
+ A-steach
+ A-mach
+ Dreachdan
+ Sgudal
+ Air a chur
+ Cha deach cuid dhe na teachdaireachdan a chur
+ Cuir an comas logadh airson dì-bhugachadh
+ Logaich fiosrachadh diagnosachd a bharrachd
+ Logaich fiosrachadh dìomhair
+ Dh’fhaoidte gun leig seo ris faclan-faire sna logaichean.
+ Luchdaich barrachd theachdaireachdan
+ Gu:%s
+ Cuspair
+ Teacsa na teachdaireachd
+ Eàrr-sgrìobhadh
+ -------- An teachdaireachd thùsail --------
+ Cuspair:
+ Air a chur:
+ O:
+ Gu:
+ Cc:
+ Sgrìobh %s:
+ Sgrìobh %2$s%1$s:
+ Feumaidh tu co-dhiù aon neach a chur ris a gheibh e.
+ Tha rud ann an raon nam faightearan a tha leth-chrìochnaichte!
+ Cha b’ urrainn dhuinn seòladh puist-d a lorg dhan neach-aithne seo.
+ Chan urrainn dhuinn cuid dhe na ceanglachain a shìneadh air adhart oir cha deach an luchdadh a-nuas.
+ Thoir an às-earrann air falbh
+ Deasaich an às-earrann
+ O: %s <%s>
+ Gu:
+ Cc:
+ Bcc:
+ Seall na dealbhan
+ Chan urrainn dhuinn aplacaid a lorg a sheallas %s.
+ Luchdaich a-nuas an teachdaireachd shlàn
+ slighe %1$s
+ Barrachd on t-seòladair seo
+ Chaidh an teachdaireachd a thilgeil air falbh
+ Chaidh an teachdaireachd a shàbhaladh mar dhreachd
+ Seall na rionnagan
+ Tha rionnagan a’ comharradh teachdaireachdan le bratach
+ Loidhnichean ro-sheallaidh
+ Seall ainmean nan seòladairean
+ Seall ainmean nan seòladairean seach na seòlaidhean puist-d aca
+ An seòladair os cionn a’ chuspair
+ Seall ainmean nan seòladairean os cionn a’ chuspair seach foidhe
+ Seall ainmean an luchd-aithne
+ Cleachd ainmean o aplacaid an luchd-aithne ma tha gin ann
+ Cuir dathan air an luchd-aithne
+ Cuir dathan air ainmean ann an liosta an luchd-aithne agad
+ Cruthan-clò le leud socraichte
+ Cleachd cruth-clò le leud socraichte nuair a thèid teachdaireachd ann an teacsa lom a shealltainn
+ Fèin-fhreagair teachdaireachdan
+ Crùb teachdaireachdan ach am freagair iad ris an sgrìn
+ Till gun liosta an dèidh sguabaidh às
+ Till gun liosta an dèidh teachdaireachd a sguabadh às
+ Seall an ath-theachdaireachd an dèidh sguabaidh às
+ Seall an ath-theachdaireachd a ghnàth an dèidh teachdaireachd a sguabadh às
+ Dearbh gnìomhan
+ Seall còmhradh uair sam bith a nì thu gnìomh sònraichte
+ Sguab às
+ Sguab às an fheadhainn le rionnag (ann an sealladh nan teachdaireachdan)
+ Spama
+ Tilg an teachdaireachd air falbh
+ Comharraich gun deach gach teachdaireachd a leughadh
+ Sguab às (on bhrath)
+ Falaich cliant a’ phuist
+ Thoir air falbh an raon Mail User-Agent o bhannan-cinn phost
+ Falaich an roinn-tìde
+ Cleachd UTC an àite na roinn-tìde ionadail ann am bannan-cinn phost is fhreagairtean
+ Seall am putan “Sguab às”
+ Chan ann idir
+ Airson brath aon teachdaireachd
+ An-còmhnaidh
+ Brathan air an sgrìn-ghlasaidh
+ Na seall brathan air an sgrìn-ghlasaidh
+ Ainm na h-aplacaid
+ Co mheud teachdaireachd is seòladair
+ An aon rud nuair nach bi an sgrìn glaiste
+ Amannan sàmhach
+ Cuir seirm, crith is boillsgeadh à comas rè na h-oidhche
+ Cuir na brathan à comas
+ Cuir na brathan à comas buileach rè nan amannan sàmhach
+ Tòisichidh an t-àm sàmhach
+ Thig an t-àm sàmhach gu crìoch
+ Suidhich cunntas ùr
+ Seòladh puist-d
+ Facal-faire
+
+
+
+ Dearbh cò thusa
+ Suidheachadh a làimh
+
+ A’ faighinn fiosrachadh a’ chunntais…
+ A’ sgrùdadh roghainnean an fhrithealaiche a-steach…
+ A’ sgrùdadh roghainnean an fhrithealaiche a-mach…
+ ’Ga dhearbhadh…
+ A’ faighinn roghainnean a’ chunntais…
+ A’ sgur dheth…
+ Cha mhòr deiseil!
+ Thoir ainm air a’ chunntas seo
+ Cuir a-steach d’ ainm (chithear seo ann an teachdaireachdan a thèid a-mach):
+ Seòrsa a’ chunntais
+ Dè seòrsa cunntas a tha seo\?
+ POP3
+ IMAP
+ Facal-faire àbhaisteach
+ Facal-faire, ’ga chur gun tèarainteachd
+ Facal-faire crioptaichte
+ Teisteanas a’ chliant
+ Roghainnean an fhrithealaiche a-steach
+ Ainm-cleachdaiche
+ Facal-faire
+ Teisteanas a’ chliant
+ Frithealaiche POP3
+ Frithealaiche IMAP
+ Frithealaiche Exchange
+ Port
+ Tèarainteachd
+ Dearbhadh
+ Chan eil gin
+ SSL/TLS
+ STARTTLS
+ Chan eil “%1$s = %2$s” dligheach mu choinneamh “%3$s = %4$s”
+ Nuair a sguabas mi teachdaireachd às
+ Na sguab às an fhrithealaiche e
+ Sguab às an fhrithealaiche e
+ Comharraich air an fhrithealaiche gun deach a leughadh
+ Ma sguabas mi às teachdaireachd, sguab às an fhrithealaiche e
+ Sa bhad
+ Aig an ath-cheasnachadh
+ A làimh
+ Mothaich dha ainm-spàs IMAP gu fèin-obrachail
+ Ro-leasachan slighe IMAP
+ Pasgan nan dreachdan
+ Pasgan a’ phuist chuirte
+ Pasgan an sgudail
+ Pasgan nan tasg-lann
+ Pasgan an spama
+ Na seall ach pasganan le fo-sgrìobhadh
+ Leudaich am pasgan gu fèin-obrachail
+ Slighe OWA
+ Slighe an dearbhaidh
+ Alias a’ bhogsa-phuist
+ Roghainnean an fhrithealaiche a-mach
+ Frithealaiche SMTP
+ Port
+ Tèarainteachd
+ Iarr clàradh a-steach.
+ Ainm-cleachdaiche
+ Facal-faire
+ Dearbhadh
+ Chan eil “%1$s = %2$s” dligheach mu choinneamh “%3$s = %4$s”
+ Suidheachadh mì-dhligheach: %s
+ Roghainnean a’ chunntais
+ Dè cho tric ’s a dh’iarras sinn am post\?
+ Chan ann idir
+ Gach cairteal na h-uarach
+ Gach leth-uair a thìde
+ Gach uair a thìde
+ Gach 2 uair a thìde
+ Gach 3 uairean a thìde
+ Gach 6 uairean a thìde
+ Gach 12 uair a thìde
+ Gach 24 uair a thìde
+ Ath-nuadhaich an ceangal IDLE
+ Gach 2 mhionaid
+ Gach 3 mionaidean
+ Gach 6 mionaidean
+ Gach 12 mhionaid
+ Gach 24 mionaid
+ Gach 36 mionaid
+ Gach 48 mionaid
+ Gach uair a thìde
+ Thoir brath dhomh nuair a ruigeas post
+ Co mheud teachdaireachd a thèid a shealltainn
+ 10 teachdaireachdan
+ 25 teachdaireachd
+ 50 teachdaireachd
+ 100 teachdaireachd
+ 250 teachdaireachd
+ 500 teachdaireachd
+ 1,000 teachdaireachd
+ 2,500 teachdaireachd
+ 5,000 teachdaireachd
+ 10,000 teachdaireachd
+ na h-uile teachdaireachd
+ Chan urrainn dhut lethbhreac a dhèanamh de theachdaireachd, no a ghluasad, mur eil e sioncronaichte leis an fhrithealaiche
+ Cha b’ urrainn dhuinn an suidheachadh a choileanadh
+ Tha an t-ainm-cleachdaiche no facal-faire ceàrr.
\n(%s)Nochd am frithealaiche teisteanas SSL nach eil dligheachd. Tachraidh seo uaireannan ma chaidh am frithealaiche a dhroch-rèiteachadh. Air neo dh’fhaoidte gu bheil cuideigin airson briseadh a-steach ort no air frithealaiche a’ phuist agad. Mur eil thu cinnteach dè tha a’ tachairt, thoir gnogag air “Diùlt” is cuir fios gu na daoine a stiùireas frithealaiche a’ phuist agad.
\n
\n(%s)Chan urrainn dhuinn ceangal ris an fhrithealaiche.
\n(%s)
- Deasaich am fiosrachadh
- Lean air adhart
- Adhartach
- Roghainnean a’ chunntais
- An cunntas bunaiteach
- Cuir post on chunntas seo a ghnàth
- Brathan mu phost ùr
- Pasganan a chuireas brath
- Na h-uile
- Pasganan a’ 1ᵈ clas a-mhàin
- Pasganan a’ 1ᵈ is an 2ⁿᵃ chlas
- Gach aon ach pasganan an 2ⁿᵃ chlas
- Chan eil gin
- Brathan sioncronachaidh
- An seòladh puist-d agad
- Innis dhomh air bàr na staide nuair a ruigeas post
- Innis dhomh air bàr na staide nuair a thathar a’ toirt sùil airson post
- Gabh a-staigh post a tha a’ dol a-mach
- Seall brath nuair a bhios mi air teachdaireachd a chur
- Luchd-aithne a-mhàin
- Na seall brathan ach ma thig teachdaireachd o neach-aithne agam
- Comharraich gun deach a leughadh nuair a dh’fhosglas mi e
- Comharraich gun deach an teachdaireachd a leughadh nuair a dh’fhosglas mi e a choimhead air
- Seall dealbhan an-còmhnaidh
- Chan eil
- On luchd-aithne
- O dhuine sam bith
- A’ cur a’ phuist
- Dèan luaidh air an teachdaireachd ann am freagairt
- Gabh a-staigh an teachdaireachd thùsail ’nad theachdaireachd.
- Freagair an dèidh na luaidhe
- Nochdaidh an teachdaireachd thùsail os cionn do fhreagairt.
- Spìon earr-sgrìobhadh sam bith às ann am freagairt
- Thèid earr-sgrìobhaidhean a thoirt air falbh o theachdaireachd air a bheilear a’ dèanamh luaidh
- Fòrmat na teachdaireachd
- Teacsa lom (thoir air falbh dealbhan is fòrmatadh)
- HTMP (glèidh na dealbhan is am fòrmatadh)
- Gu fèin-obrachail
- Seall an Cc/Bcc an-còmhnaidh
- Cuidhteas-leughaidh
- Iarr cuidhteas-leughaidh an-còmhnaidh
- Stoidhle nan luaidhean ann am freagairtean
- Le ro-leasachan (mar Gmail)
- Bann-cinn (mar Outlook)
- Roghainnean coitcheann
- A’ leughadh post
- A’ faighinn post
- Pasganan
- Ro-leasachan teacsa nan luaidh
- Dè cho tric ’s a dh’iarras sinn am post\?
- Dath a’ chunntais
- Dath cuideam a’ chunntais seo a chithear ann am pasganan is liosta nan cunntas
- Dath LED nam brath
- An dath a bhios air LED an uidheim agad dhan chunntas seo
- Meud a’ phasgain ionadail
- Faigh teachdaireachdan suas ri
- meud sam bith (gun chrìoch)
- Sioncronaich teachdaireachdan
- o àm sam bith (gun chrìoch)
- on diugh
- on dè
- on bhòin-dè
- on t-seachdain seo chaidh
- on chola-deug seo chaidh
- o na 3 seachdainean seo chaidh
- on mhìos seo chaidh
- on 2 mhìos seo chaidh
- o na 3 mìosan seo chaidh
- on na 6 mìosan seo chaidh
- on uraidh
- Na pasganan a thèid a shealltainn
- Na h-uile
- Pasganan a’ 1ᵈ chlas a-mhàin
- Pasganan a’ 1ᵈ is an 2na chlas
- Gach aon ach pasganan an 2ⁿᵃ chlas
- Na pasganan a thèid a cheasnachadh
- Na h-uile
- Pasganan a’ 1ᵈ chlas a-mhàin
- Pasganan a’ 1ᵈ is an 2na chlas
- Gach aon ach pasganan an 2ⁿᵃ chlas
- Chan eil gin
- Na pasganan putaidh
- Na h-uile
- Pasganan a’ 1ᵈ chlas a-mhàin
- Pasganan a’ 1ᵈ is an 2na chlas
- Gach aon ach pasganan an 2ⁿᵃ chlas
- Chan eil gin
- Pasganan dhan a chuirear lethbhreacan/dhan a ghluaisear
- Na h-uile
- Pasganan a’ 1ᵈ chlas a-mhàin
- Pasganan a’ 1ᵈ is an 2na chlas
- Gach aon ach pasganan an 2ⁿᵃ chlas
- Sioncronaich sguabadh às air an fhrithealaiche
- Thoir air falbh teachdaireachdan a chaidh a sguabadh às an fhrithealaiche
- Roghainnean a’ phasgain
- Seall aig a’ bhàrr sa bhuidheann
- Seall faisg air bàrr liosta nam pasganan
- Clas taisbeanadh a’ phasgain
- Gun chlas
- 1ᵈ chlas
- 2ⁿᵃ clas
- Gun chlas
- 1ᵈ chlas
- 2ⁿᵃ clas
- Co-ionnann ri clas an taisbeanaidh
- Clas putadh a’ phasgain
- Gun chlas
- 1ᵈ chlas
- 2ⁿᵃ clas
- Clas brathan a’ phasgain
- Gun chlas
- 1ᵈ chlas
- 2ⁿᵃ clas
- Co-ionnann ri clas a’ phutaidh
- Falamhaich na teachdaireachdan ionadail
- Am frithealaiche a-steach
- Rèitich frithealaiche a’ phuist a-steach
- Am frithealaiche a-mach
- Rèitich frithealaiche a’ phuist a-mach (SMTP)
- Ainm a’ chunntais
- D’ ainm
- Brathan
- Crith
- Cuir air chrith nuair a ruigeas post
- Pàtranan crith
- bun-roghainn
- pàtran 1
- pàtran 2
- pàtran 3
- pàtran 4
- pàtran 5
- Crith a-rithist
- Seirm a’ phuist ùir
- Boillsg an LED
- Boillsg an LED nuair a ruigeas post
- Roghainnean sgrìobhadh theachdaireachdan
- Bun-roghainnean sgrìobhaidh
- Suidhich bun-roghainnean “O”, “Bcc” agus an earr-sgrìobhaidh
- Stiùirich na dearbh-aithnean
- Suidhich seòlaidhean “O” is earr-sgrìobhaidhean eadar-dhealaichte
- Stiùirich na dearbh-aithnean
- Stiùirich an dearbh-aithne
- Deasaich an dearbh-aithne
- Sàbhail
- Dearbh-aithne ùr
- Cuir Bcc de gach teachdaireachd gu
- Deasaich
- Gluais suas
- Gluais sìos
- Gluais suas / dèan a’ bhun-roghainn dheth
- Thoir air falbh
- Tuairisgeul na dearbh-aithne
- (Roghainneil)
- D’ ainm
- (Roghainneil)
- Seòladh puist-d
- (Riatanach)
- An seòladh dhan chuirear freagairtean
- (Roghainneil)
- An t-earr-sgrìobhadh
- (Roghainneil)
- Cleachd earr-sgrìobhadh
- An t-earr-sgrìobhadh
- An dearbh-aithne thòiseachail
- Tagh dearbh-aithne
- Cuir mar
- Chan urrainn dhut an aon dearbh-aithne agad a thoirt air falbh
- Chan urrainn dhut dearbh-aithne a chleachdadh gun seòladh puist-d
- Na teachdaireachdan as sine an toiseach
- Na teachdaireachdan as ùire an toiseach
- Aibidileach a-rèir a’ chuspair
- Aibidileach contrarra a-rèir a’ chuspair
- Aibidileach a-rèir an t-seòladair
- Aibidileach contrarra a-rèir an t-seòladair
- Teachdaireachdan le rionnag an toiseach
- Teachdaireachdan gun rionnag an toiseach
- Teachdaireachdan gun leughadh an toiseach
- Teachdaireachdan a chaidh an leughadh an toiseach
- Teachdaireachdan le ceanglachain an toiseach
- Teachdaireachdan gun cheanglachain an toiseach
- Seòrsaich a-rèir…
- Ceann-là
- Ruigsinn
- Cuspair
- Seòladair
- Rionnag
- Air a leughadh/gun leughadh
- Ceanglachain
- Thoir an cunntas air falbh
- Teisteanas neo-aithnichte
- Gach ris an iuchair
- Diùlt an iuchair
- Seall pasganan…
- Na h-uile pasgan
- Pasganan a’ 1ᵈ chlas
- Pasganan a’ 1ᵈ ⁊ an 2ⁿᵃ chlas
- Falaich pasganan an 2ⁿᵃ chlas
- Ionad an earr-sgrìobhaidh
- Ron luaidh air an teachdaireachd
- An dèidh luaidh air an teachdaireachd
- Cleachd ùrlar na h-aplacaid
- Dorcha
- Soilleir
- Taisbeanadh
- Uile-choitcheann
- Dì-bhugachadh
- Prìobhaideachd
- Lìonra
- Eadar-ghabhail
- Liosta nan cunntasan
- Liosta nan teachdaireachdan
- Teachdaireachdan
- Ùrlar
- Ùrlar sealladh nan teachdaireachdan
- Ùrlar an sgrìobhaidh
- Ùrlair socraichte airson teachdaireachdan
- Tagh ùrlar airson na teachdaireachd is tu a’ coimhead air
- Cleachd ùrlair socraichte airson teachdaireachdan
- Sioncronachadh sa chùlaibh
- Chan ann idir
- An-còmhnaidh
- Ma tha cromag ri “Sioncronachadh fèin-obrachail”
- Tagh na h-uile
- An àireamh as motha a phasganan air an doirear sùil le putadh
- 10 pasganan
- 25 pasgan
- 50 pasgan
- 100 pasgan
- 250 pasgan
- 500 pasgan
- 1,000 pasgan
- Beòthachadh
- Cleachd èifeachdan lèirsinneach leòmach
- Seòladaireachd slighe putanan àirde na fuaime
- Ann an seallaidhean theachdaireachdan
- Ann an seallaidhean liostaichean
- %s%s
- - gun leughadh
- Na h-uile teachdaireachd
- Na h-uile teachdaireachd ann am pasgan so-luirg
- Am bogsa a-steach co-aonaichte
- Na h-uile teachdaireachd ann am pasganan co-aonaichte
- Co-aonaich
- Tha gach teachdaireachd ’ga shealltainn sa bhogsa a-steach cho-aonaichte
- Na pasganan san dèanar lorg
- Na h-uile
- So-thaisbeanadh
- Chan eil gin
- Chan eil gin
- Meud a’ chrutha-chlò
- Rèitich meud a’ chrutha-chlò
- Liosta nan cunntasan
- Ainm a’ chunntais
- Tuairisgeul a’ chunntais
- Liosta nam pasganan
- Ainm a’ phasgain
- Staid a’ phasgain
- Liosta nan teachdaireachdan
- Cuspair
- Seòladair
- Ceann-là
- Ro-shealladh
- Teachdaireachdan
- Seòladair
- Gu
- Cc
- Bcc
- Bannan-cinn a bharrachd
- Cuspair
- Ceann-là ’s àm
- Bodhaig na teachdaireachd
- %d%%
- %1$s: %2$s
- Sgrìobhadh theachdaireachdan
- Raointean ion-chur teacsa
- Bun-roghainn
- As lugha
- Beag bìodach
- Nas lugha
- Beag
- Meadhanach
- Mòr
- Nas motha
- Cha deach aplacaid iomchaidh a lorg dhan ghnìomh seo.
- Dh’fhàillig a chur: %s
- A bheil thu airson dreachd na teachdaireachd a shàbhaladh\?
- A bheil thu airson an teachdaireachd seo a shàbhaladh no a thilgeil air falbh\?
- A bheil thu airson an teachdaireachd seo a thilgeil air falbh\?
- A bheil thu cinnteach gu bheil thu airson an teachdaireachd seo a thilgeil air falbh\?
- Tagh teacsa airson lethbhreac a dhèanamh dheth.
- Dearbh a sguabadh às
- A bheil thu cinnteach gu bheil thu airson an teachdaireachd seo a sguabadh às\?
- Tha
- Chan eil
- Comharraich gun deach gach teachdaireachd a leughadh
- A bheil thu airson comharradh gun deach na teachdaireachdan uile seo a leughadh\?
- Tha
- Chan eil
- Dearbh a ghluasad do phasgan an spama
- Tha
- Chan eil
- A’ luchdadh a-nuas a’ cheanglachain
- »
- ›
- An còrr
- Às-phortaich
- Co-roinn
- Ag às-phortadh nan roghainnean…
- Chaidh na roghainnean às-phortadh
- Cha b’ urrainn dhuinn na roghainnean às-phortadh
- Ion-phortaich na roghainnean
- Ion-phortaich
- Ion-phortaich na roghainnean
- Ag ion-phortadh nan roghainnean…
- Seall na tha gun leughadh ann an…
- Gun teacsa
- Fosgail an ceangal
- Co-roinn an ceangal
- Cuir lethbhreac dhen cheangal air an stòr-bhòrd
- Ceangal
- Dealbh
- Seall an dealbh
- Sàbhail an dealbh
- Luchdaich a-nuas an dealbh
- Cuir lethbhreac de URL an deilbh air an stòr-bhòrd
- URL an deilbh
- Cuir fòn gun àireamh seo
- Sàbhail san luchd-aithne
- Cuir lethbhreac dhen àireamh air an stòr-bhòrd
- Àireamh fòn
- Cuir am post
- Sàbhail san luchd-aithne
- Cuir lethbhreac dhen t-seòladh air an stòr-bhòrd
- Seòladh puist-d
- Na h-uile
- 10
- 25
- 50
- 100
- 250
- 500
- 1,000
- Crìoch lorg an fhrithealaiche
- A’ cur na ceiste dhan fhrithealaiche
-
- A’ faighinn %d toradh
- A’ faighinn %d thoradh
- A’ faighinn %d toraidhean
- A’ faighinn %d toradh
-
-
- A’ faighinn %1$d à %2$d toradh
- A’ faighinn %1$d à %2$d thoradh
- A’ faighinn %1$d à %2$d toraidhean
- A’ faighinn %1$d à %2$d toradh
-
- Dh’fhàillig an lorg cèin
- Lorg
- Cuir an comas lorg air an fhrithealaiche
- Lorg teachdaireachdan air an fhrithealaiche a bharrachd air an fheadhainn air an uidheam agad
- Lorg teachdaireachdan air an fhrithealaiche
- Feumaidh tu ceangal ris an lìonra mus urrainn dhut lorg a dhèanamh air an fhrithealaiche.
- Atharraich an dath nuair a chaidh a leughadh
- Nochdaidh cùlaibh eile a sheallas gun deach an teachdaireachd a leughadh
- Sealladh ann an snàithleanan
- Buidhnich teachdaireachdan ann an còmhraidhean
- Ag àrdachadh nan stòr-dàta
- Ag àrdachadh nan stòr-dàta…
- Ag àrdachadh nan stòr-dàta aig a’ chunntas “%s”
- Seall air sgrìn sgoilte
- An-còmhnaidh
- Chan ann idir
- Nuair a tha e ’ga shealltainn mar dreach-tìre
- Tagh teachdaireachd air an taobh chlì
- Seall dealbhan an luchd-aithne
- Seall dealbhan an luchd-aithne ann an liosta nan teachdaireachd
- Comharraich gun deach iad uile a leughadh
- Cuir dathan air dealbhan an luchd-aithne
- Cuir dathan air dealbhan an luchd-aithne a tha a dhìth
- Gnìomhan theachdaireachdan ri fhaicinn
- Seall na gnìomhan a thagh thu ann an clàr-taice sealladh na teachdaireachd
- A’ luchdadh a’ cheanglachain…
- A’ cur na teachdaireachd
- A’ sàbhaladh an dreachd
- A’ faighinn a’ cheanglachain…
- Cha b’ urrainn dhuinn dearbhadh a dhèanamh. Chan eil am frithealaiche a’ leigeil ris a bheil e comasach air SASL EXTERNAL. Dh’fhaoidte gu bheil duilgheadas le teisteanas a’ chliant (ro aosta, ùghdarras neo-aithnichte) no duilgheadas eile leis an rèiteachadh.
-
- Cleachd teisteanas a’ chliant
- Chan eil teisteanas aig a’ chliant
- Thoir air falbh taghadh teisteanas a’ chliant
- Cha b’ urrainn dhuinn teisteanas a’ chliant fhaighinn dhan alias “%s”
- Roghainnean adhartach
- Dh’fhalbh an ùine air teisteanas a’ chliant “%1$s” no chan eil e dligheachd fhathast (%2$s)
-
- *Crioptaichte*
- Cuir ris on luchd-aithne
- Cc
- Bcc
- Gu
- O
- Taigh
- Obair
- Eile
- Fòn-làimhe
- Cha deach pasgan do dhreachdan a rèiteachadh dhan chunntas seo!
- Chan urrainn dhuinn ceangal ri solaraiche a’ chrioptachaidh, thoir sùil air na roghainnean no thoir gnogag air ìomhaigheag a’ chrioptachaidh airson feuchainn ris a-rithist!
- Chan eil taic ri ceanglachain sa mhodh PGP/INLINE!
- Cuir PGP/INLINE an comas
- Cuir PGP/INLINE à comas
- Cuir PGP Sign-Only an comas
- Cuir PGP Sign-Only à comas
- Am modh PGP/INLINE
- Tha am post-d seo ’ga chur san fhòrmat PGP/INLINE.
+ Deasaich am fiosrachadh
+ Lean air adhart
+ Adhartach
+ Roghainnean a’ chunntais
+ Brathan mu phost ùr
+ Pasganan a chuireas brath
+ Na h-uile
+ Pasganan na 1ᵈ ìre a-mhàin
+ Pasganan na 1ᵈ is na 2ⁿᵃ ìre
+ Gach aon ach pasganan na 2ⁿᵃ ìre
+ Chan eil gin
+ Brathan sioncronachaidh
+ An seòladh puist-d agad
+ Thoir brath dhomh air bàr na staide nuair a ruigeas post
+ Thoir brath dhomh air bàr na staide nuair a thathar a’ toirt sùil airson post
+ Gabh a-staigh post a tha a’ dol a-mach
+ Seall brath nuair a bhios mi air teachdaireachd a chur
+ Luchd-aithne a-mhàin
+ Na seall brath ach ma thig teachdaireachd o neach-aithne agam
+ Comharraich gun deach a leughadh nuair a dh’fhosglas mi i
+ Comharraich gun deach an teachdaireachd a leughadh nuair a dh’fhosglas mi i a choimhead air
+ Seall dealbhan an-còmhnaidh
+ Na seall idir
+ On luchd-aithne
+ O dhuine sam bith
+ Cur a’ phuist
+ Cuir às-earrann dhen teachdaireachd ris an fhreagairt
+ Gabh a-staigh an teachdaireachd thùsail ’nad theachdaireachd.
+ Freagair às dèidh na h-às-earrainn
+ Nochdaidh an teachdaireachd thùsail os cionn do fhreagairt.
+ Spìon eàrr-sgrìobhadh sam bith às ann am freagairt
+ Thèid earr-sgrìobhaidhean a thoirt air falbh o às-earrannan theachdaireachdan
+ Fòrmat na teachdaireachd
+ Teacsa lom (thoir air falbh dealbhan is fòrmatadh)
+ HTMP (glèidh na dealbhan is am fòrmatadh)
+ Fèin-obrachail
+ Seall Cc/Bcc an-còmhnaidh
+ Cuidhteas-leughaidh
+ Iarr cuidhteas-leughaidh an-còmhnaidh
+ Stoidhle nan às-earrannan ann am freagairtean
+ Le ro-leasachan (mar Gmail)
+ Bann-cinn (mar Outlook)
+ Roghainnean coitcheann
+ A’ leughadh post
+ A’ faighinn post
+ Pasganan
+ Ro-leasachan na h-às-earrainn
+ Dè cho tric ’s a dh’iarras sinn am post\?
+ Dath a’ chunntais
+ Dath soillearachadh a’ chunntais seo a chithear ann am pasganan is liosta nan cunntas
+ Meud a’ phasgain ionadail
+ Faigh teachdaireachdan suas ri
+ meud sam bith (gun chrìoch)
+ Sioncronaich teachdaireachdan
+ o àm sam bith (gun chrìoch)
+ on diugh
+ on dè
+ on bhòin-dè
+ on t-seachdain seo chaidh
+ on chola-deug seo chaidh
+ o na 3 seachdainean seo chaidh
+ on mhìos seo chaidh
+ on 2 mhìos seo chaidh
+ o na 3 mìosan seo chaidh
+ on na 6 mìosan seo chaidh
+ on uiridh
+ Na pasganan a thèid a shealltainn
+ Na h-uile
+ Pasganan na 1ᵈ ìre a-mhàin
+ Pasganan na 1ᵈ is na 2ⁿᵃ ìre
+ Gach aon ach pasganan na 2ⁿᵃ ìre
+ Na pasganan a thèid a cheasnachadh
+ Na h-uile
+ Pasganan na 1ᵈ ìre a-mhàin
+ Pasganan na 1ᵈ is na 2ⁿᵃ ìre
+ Gach aon ach pasganan na 2ⁿᵃ ìre
+ Chan eil gin
+ Na pasganan putaidh
+ Na h-uile
+ Pasganan na 1ᵈ ìre a-mhàin
+ Pasganan na 1ᵈ is na 2ⁿᵃ ìre
+ Gach aon ach pasganan na 2ⁿᵃ ìre
+ Chan eil gin
+ Pasganan dhan a chuirear lethbhreacan/dhan a ghluaisear
+ Na h-uile
+ Pasganan na 1ᵈ ìre a-mhàin
+ Pasganan na 1ᵈ is na 2ⁿᵃ ìre
+ Gach aon ach pasganan na 2ⁿᵃ ìre
+ Sioncronaich sguabadh às air an fhrithealaiche
+ Thoir air falbh teachdaireachdan a chaidh a sguabadh às an fhrithealaiche
+ Roghainnean a’ phasgain
+ Seall aig a’ bhàrr sa bhuidheann
+ Seall faisg air bàrr liosta nam pasganan
+ Ìre taisbeanadh a’ phasgain
+ Gun ìre
+ 1ᵈ ìre
+ 2ⁿᵃ ìre
+ Chan eil gin
+ 1ᵈ ìre
+ 2ⁿᵃ ìre
+ Co-ionnann ris an ìre taisbeanaidh
+ Ìre putadh a’ phasgain
+ Gun ìre
+ 1ᵈ ìre
+ 2ⁿᵃ ìre
+ Ìre brathan a’ phasgain
+ Gun ìre
+ 1ᵈ ìre
+ 2ⁿᵃ ìre
+ Co-ionnann ris an ìre putaidh
+ Falamhaich na teachdaireachdan ionadail
+ Am frithealaiche a-steach
+ Rèitich frithealaiche a’ phuist a-steach
+ Am frithealaiche a-mach
+ Rèitich frithealaiche a’ phuist a-mach (SMTP)
+ Ainm a’ chunntais
+ D’ ainm
+ Brathan
+ Crith
+ Bun-roghainn
+ Crith a-rithist
+ Seirm a’ phuist ùir
+ Dath a’ chunntais
+ Roghainnean sgrìobhadh theachdaireachdan
+ Bun-roghainnean sgrìobhaidh
+ Suidhich bun-roghainnean “O”, “Bcc” agus an eàrr-sgrìobhaidh
+ Stiùirich na dearbh-aithnean
+ Suidhich seòlaidhean “O” is eàrr-sgrìobhaidhean eadar-dhealaichte
+ Stiùirich na dearbh-aithnean
+ Stiùirich an dearbh-aithne
+ Deasaich an dearbh-aithne
+ Sàbhail
+ Dearbh-aithne ùr
+ Cuir Bcc de gach teachdaireachd gu
+ Deasaich
+ Gluais suas
+ Gluais sìos
+ Gluais suas / dèan a’ bhun-roghainn dheth
+ Thoir air falbh
+ Tuairisgeul na dearbh-aithne
+ (Roghainneil)
+ D’ ainm
+ (Roghainneil)
+ Seòladh puist-d
+ (Riatanach)
+ An seòladh dhan chuirear freagairtean
+ (Roghainneil)
+ An t-eàrr-sgrìobhadh
+ (Roghainneil)
+ Cleachd eàrr-sgrìobhadh
+ An t-eàrr-sgrìobhadh
+ An dearbh-aithne thòiseachail
+ Tagh dearbh-aithne
+ Cuir mar
+ Chan urrainn dhut an aon dearbh-aithne agad a thoirt air falbh
+ Chan urrainn dhut dearbh-aithne a chleachdadh gun seòladh puist-d
+ Na teachdaireachdan as sine an toiseach
+ Na teachdaireachdan as ùire an toiseach
+ Aibidileach a-rèir a’ chuspair
+ Aibidileach contrarra a-rèir a’ chuspair
+ Aibidileach a-rèir an t-seòladair
+ Aibidileach contrarra a-rèir an t-seòladair
+ Teachdaireachdan le rionnag an toiseach
+ Teachdaireachdan gun rionnag an toiseach
+ Teachdaireachdan gun leughadh an toiseach
+ Teachdaireachdan a chaidh an leughadh an toiseach
+ Teachdaireachdan le ceanglachain an toiseach
+ Teachdaireachdan gun cheanglachain an toiseach
+ Seòrsaich a-rèir…
+ Ceann-là
+ Ruigsinn
+ Cuspair
+ Seòladair
+ Rionnag
+ Air a leughadh/gun leughadh
+ Ceanglachain
+ Thoir an cunntas air falbh
+ Teisteanas nach aithne dhuinn
+ Gabh ris an iuchair
+ Diùlt an iuchair
+ Seall pasganan…
+ Na h-uile pasgan
+ Pasganan na 1ᵈ ìre
+ Pasganan na 1ᵈ ⁊ na 2ⁿᵃ ìre
+ Falaich pasganan na 2ⁿᵃ ìre
+ Ionad an eàrr-sgrìobhaidh
+ Ron às-earrann dhen teachdaireachd
+ Às dèidh às-earrann dhen teachdaireachd
+ Cleachd ùrlar na h-aplacaid
+ Dorcha
+ Soilleir
+ Sealladh
+ Uile-choitcheann
+ Dì-bhugachadh
+ Prìobhaideachd
+ Lìonra
+ Eadar-ghabhail
+ Liosta nan cunntasan
+ Liostaichean theachdaireachdan
+ Teachdaireachdan
+ Ùrlar
+ Ùrlar sealladh nan teachdaireachdan
+ Ùrlar an sgrìobhaidh
+ Ùrlair socraichte airson teachdaireachdan
+ Tagh ùrlar airson na teachdaireachd is tu a’ coimhead air
+ Cleachd ùrlair socraichte airson teachdaireachdan
+ Sioncronachadh sa chùlaibh
+ Chan ann idir
+ An-còmhnaidh
+ Ma tha cromag ri “Sioncronachadh fèin-obrachail”
+ Tagh na h-uile
+ An àireamh as motha a phasganan air an doirear sùil le putadh
+ 10 pasganan
+ 25 pasgan
+ 50 pasgan
+ 100 pasgan
+ 250 pasgan
+ 500 pasgan
+ 1,000 pasgan
+ Beòthachadh
+ Cleachd èifeachdan lèirsinneach leòmach
+ Seòladaireachd le putanan àirde na fuaime
+ Ann an seallaidhean theachdaireachdan
+ Ann an seallaidhean liostaichean
+ An t-oll-bhogsa
+ Na h-uile teachdaireachd ann am pasganan co-aonaichte
+ Co-aonaich
+ Tha gach teachdaireachd ga shealltainn san oll-bhogsa
+ Na pasganan san dèanar lorg
+ Na h-uile
+ So-thaisbeanadh
+ Chan eil gin
+ Chan eil gin
+ Meud a’ chrutha-chlò
+ Rèitich meud a’ chrutha-chlò
+ Liosta nan cunntasan
+ Ainm a’ chunntais
+ Tuairisgeul a’ chunntais
+ Liostaichean phasganan
+ Ainm a’ phasgain
+ Staid a’ phasgain
+ Liostaichean theachdaireachdan
+ Cuspair
+ Seòladair
+ Ceann-là
+ Ro-shealladh
+ Teachdaireachdan
+ Seòladair
+ Gu
+ Cc
+ Bcc
+ Bannan-cinn a bharrachd
+ Cuspair
+ Ceann-là ’s àm
+ Bodhaig na teachdaireachd
+ Sgrìobhadh theachdaireachdan
+ Raointean ion-chur teacsa
+ Bun-roghainn
+ As lugha
+ Beag bìodach
+ Nas lugha
+ Beag
+ Meadhanach
+ Mòr
+ Nas motha
+ Cha deach aplacaid iomchaidh a lorg dhan ghnìomh seo.
+ Dh’fhàillig a chur: %s
+ A bheil thu airson dreachd na teachdaireachd a shàbhaladh\?
+ A bheil thu airson an teachdaireachd seo a shàbhaladh no a thilgeil air falbh\?
+ A bheil thu airson an teachdaireachd seo a thilgeil air falbh\?
+ A bheil thu cinnteach gu bheil thu airson an teachdaireachd seo a thilgeil air falbh\?
+ Tagh teacsa airson lethbhreac a dhèanamh dheth.
+ Dearbh an sguabadh às
+ A bheil thu cinnteach gu bheil thu airson an teachdaireachd seo a sguabadh às\?
+ Tha
+ Chan eil
+ Comharraich gun deach gach teachdaireachd a leughadh
+ A bheil thu airson comharradh gun deach na teachdaireachdan uile seo a leughadh\?
+ Tha
+ Chan eil
+ Dearbh a ghluasad do phasgan an spama
+ Tha
+ Chan eil
+ A’ luchdadh a-nuas a’ cheanglachain
+ »
+ ›
+ An corr
+ Às-phortaich
+ Co-roinn
+ Ag às-phortadh nan roghainnean…
+ Chaidh na roghainnean às-phortadh
+ Cha b’ urrainn dhuinn na roghainnean às-phortadh
+ Ion-phortaich na roghainnean
+ Ion-phortaich
+
+
+
+
+ Ion-phortaich na roghainnean
+ Ag ion-phortadh nan roghainnean…
+ Seall co mheud a tha gun leughadh ann an…
+ Gun teacsa
+ Fosgail an ceangal
+ Co-roinn an ceangal
+ Cuir lethbhreac dhen cheangal air an stòr-bhòrd
+ Ceangal
+ Dealbh
+ Seall an dealbh
+ Sàbhail an dealbh
+ Luchdaich a-nuas an dealbh
+ Cuir lethbhreac de URL an deilbh air an stòr-bhòrd
+ URL an deilbh
+ Cuir fòn gun àireamh seo
+ Sàbhail san luchd-aithne
+ Cuir lethbhreac dhen àireamh air an stòr-bhòrd
+ Àireamh fòn
+ Cuir am post
+ Sàbhail san luchd-aithne
+ Cuir lethbhreac dhen t-seòladh air an stòr-bhòrd
+ Seòladh puist-d
+ Na h-uile
+ 10
+ 25
+ 50
+ 100
+ 250
+ 500
+ 1,000
+ Crìoch lorg an fhrithealaiche
+ A’ cur na ceiste dhan fhrithealaiche
+
+ A’ faighinn %d toradh
+ A’ faighinn %d thoradh
+ A’ faighinn %d toraidhean
+ A’ faighinn %d toradh
+
+
+ A’ faighinn %1$d à %2$d toradh
+ A’ faighinn %1$d à %2$d thoradh
+ A’ faighinn %1$d à %2$d toraidhean
+ A’ faighinn %1$d à %2$d toradh
+
+ Dh’fhàillig an lorg cèin
+ Lorg
+ Lorg teachdaireachdan air an fhrithealaiche
+ Feumaidh tu ceangal ris an lìonra mus urrainn dhut lorg a dhèanamh air an fhrithealaiche.
+ Atharraich an dath nuair a chaidh a leughadh
+ Nochdaidh cùlaibh eile a sheallas gun deach an teachdaireachd a leughadh
+ Sealladh ann an snàithleanan
+ Buidhnich teachdaireachdan ann an còmhraidhean
+ Ag àrdachadh nan stòr-dàta
+ Ag àrdachadh nan stòr-dàta…
+ Ag àrdachadh stòr-dàta a’ chunntais “%s”
+ Seall air sgrìn sgoilte
+ An-còmhnaidh
+ Chan ann idir
+ Nuair a tha e ’ga shealltainn mar dreach-tìre
+ Tagh teachdaireachd air an taobh chlì
+ Seall dealbhan an luchd-aithne
+ Seall dealbhan an luchd-aithne ann an liosta nan teachdaireachd
+ Comharraich gun deach iad uile a leughadh
+ Cuir dathan air dealbhan an luchd-aithne
+ Cuir dathan air dealbhan an luchd-aithne a tha a dhìth
+ Gnìomhan theachdaireachdan ri fhaicinn
+ Seall na gnìomhan a thagh thu ann an clàr-taice sealladh na teachdaireachd
+ A’ luchdadh a’ cheanglachain…
+ A’ cur na teachdaireachd
+ A’ sàbhaladh an dreachd
+ A’ faighinn a’ cheanglachain…
+ Cha b’ urrainn dhuinn dearbhadh a dhèanamh. Chan eil am frithealaiche a’ leigeil ris a bheil e comasach air SASL EXTERNAL. Dh’fhaoidte gu bheil duilgheadas le teisteanas a’ chliant (ro aosta, ùghdarras neo-aithnichte) no duilgheadas eile leis an rèiteachadh.
+
+ Cleachd teisteanas a’ chliant
+ Chan eil teisteanas aig a’ chliant
+ Thoir air falbh taghadh teisteanas a’ chliant
+ Cha b’ urrainn dhuinn teisteanas a’ chliant fhaighinn dhan alias “%s”
+ Roghainnean adhartach
+ Dh’fhalbh an ùine air teisteanas a’ chliant “%1$s” no chan eil e dligheachd fhathast (%2$s)
+
+ *Crioptaichte*
+ Cuir ris on luchd-aithne
+ Cc
+ Bcc
+ Gu
+ O
+ Dachaigh
+ Obair
+ Eile
+ Mobile
+ Cha deach pasgan do dhreachdan a rèiteachadh dhan chunntas seo!
+ Chan urrainn dhuinn ceangal ri solaraiche a’ chrioptachaidh, thoir sùil air na roghainnean no thoir gnogag air ìomhaigheag a’ chrioptachaidh airson feuchainn ris a-rithist!
+ Chan eil taic ri ceanglachain sa mhodh PGP/INLINE!
+ Cuir PGP/INLINE an comas
+ Cuir PGP/INLINE à comas
+ Cuir PGP Sign-Only an comas
+ Cuir PGP Sign-Only à comas
+ Am modh PGP/INLINE
+ Tha am post-d seo ’ga chur san fhòrmat PGP/INLINE.
\nCha bu chòir dhut seo a dhèanamh ach airson co-chòrdalachd:
- Tha cuid a chliantan ann nach cuir taic ach ris an fhòrmat seo
- Dh’fhaoidte gum bris soidhneadh rè an tar-chuir
- Chan eil taic ri ceanglachain
- Tha mi agaibh!
- Cuir à comas
- Cum an comas
- Tha mi agaibh!
- Cuir à comas
- Cum an comas
- Am modh PGP Sign-Only
- Sa mhodh seo, thèid an iuchair PGP agad a chleachdaidh airson soidhneadh crioptografach de phost-d gun chrioptachadh a chruthachadh.
- Cha chrioptaich seo am post-d ach dearbhaidh e gun deach a chuir on iuchair agad.
- Dh’fhaoidte gum bris soidhneadh nuair a chuireas tu rud gu liosta-phuist.
- Dh’fhaoidte gun nochd soidhnidhean mar “signature.asc” ann an cuid a chliantan.
- Bidh soidhneadh am broinn teachdaireachd chrioptaichte an-còmhnaidh.
- Tha an teachdaireachd crioptaichte ach ann am fòrmat ris nach eil taic.
- Tha an teachdaireachd crioptaichte ach cha sguireadh dhen dì-chrioptachadh.
- Ceart ma-thà
- Mion-fhiosrachadh
- Thoir a’ ghlas dheth
- Cha deach a’ phàirt seo a chrioptachadh is dh’fhaoidte gu bheil e neo-thèarainte.
- Ceanglachan gun dìon
- ’Ga luchdadh…
- Sguireadh dhen dì-chrioptachadh.
- Feuch ris a-rithist
- Feumaidh tu an teachdaireachd chrioptaichte a luchdadh a-nuas mus gabh a dhì-chrioptachadh.
- Mearachd a’ dì-chrioptachadh a’ phuist-d
- Chan eil taic ri caractaran sònraichte aig an àm seo!
- Mearachd a’ pàrsadh an t-seòlaidh!
- Chan eil crioptachadh ri fhaighinn sa mhodh anns nach dèanar ach soidhneadh!
- Teacsa gun soidhneadh
- Rabhadh mu chleachdadh APG
- Chan eilear a’ leasachadh APG tuilleadh!
- Ri linn sin, chan eil taic ri APG aig post K-9 tuilleadh.
- Sguir daoine dhen obair-leasachaidh aig toiseach 2014
- Tha duilgheadasan tèarainteachd ann nach deach an càradh
- Briog an-seo airson barrachd fiosrachaidh.
- Tha mi agaibh!
- APG
- Tha am post-d seo crioptaichte
- Chaidh am post-d seo a chrioptachadh le OpenPGP.
+ Tha cuid a chliantan ann nach cuir taic ach ris an fhòrmat seo
+ Dh’fhaoidte gum bris soidhneadh rè an tar-chuir
+ Chan eil taic ri ceanglachain
+ Tha mi agaibh!
+ Cuir à comas
+ Cùm an comas
+ Tha mi agaibh!
+ Cuir à comas
+ Cùm an comas
+ Am modh PGP Sign-Only
+ Sa mhodh seo, thèid an iuchair PGP agad a chleachdadh airson soidhneadh crioptografach de phost-d gun chrioptachadh a chruthachadh.
+ Cha chrioptaich seo am post-d ach dearbhaidh e gun deach a chur on iuchair agad.
+ Dh’fhaoidte gum bris soidhnidhean nuair a chuireas tu rud gu liosta-phuist.
+ Dh’fhaoidte gun nochd soidhnidhean mar “signature.asc” ann an cuid a chliantan.
+ Bidh soidhneadh am broinn teachdaireachd chrioptaichte an-còmhnaidh.
+ Tha an teachdaireachd crioptaichte ach ann am fòrmat ris nach eil taic.
+ Tha an teachdaireachd crioptaichte ach sguireadh dhen dì-chrioptachadh.
+ Ceart ma-thà
+ Mion-fhiosrachadh
+ Thoir a’ ghlas dheth
+ Cha deach a’ phàirt seo a chrioptachadh is dh’fhaoidte gu bheil e neo-thèarainte.
+ Ceanglachan gun dìon
+ ’Ga luchdadh…
+ Sguireadh dhen dì-chrioptachadh.
+ Feuch ris a-rithist
+ Feumaidh tu an teachdaireachd chrioptaichte a luchdadh a-nuas mus gabh a dhì-chrioptachadh.
+ Mearachd a’ dì-chrioptachadh a’ phuist-d
+ Chan eil taic ri caractaran sònraichte aig an àm seo!
+ Mearachd a’ parsadh an t-seòlaidh!
+ Chan eil crioptachadh ri fhaighinn sa mhodh anns nach dèanar ach soidhneadh!
+ Teacsa gun soidhneadh
+ Tha am post-d seo crioptaichte
+ Chaidh am post-d seo a chrioptachadh le OpenPGP.
\nFeumaidh tu aplacaid a tha comasach air OpenPGP a stàladh is a rèiteachadh mus urrainn dhut a leughadh.Liosta theachdaireachdan puistA’ luchdadh nan teachdaireachdan…
- Cha b’ urrainn dhuinn liosta nam pasgan fhaighinnRoghainnean coitcheann
- Fosgail
- DùinTeirmichean na seirbheiseÙghdaran
@@ -875,7 +809,6 @@
Cuir crioptachadh an comasCha chuir gach faightear a thagh thu taic ris a’ ghleus seo!Cha ghabh a chrioptachadh
- Mearachd a’ faighinn staid an fhaighteir on t-solaraiche OpenPGP!Rach gu na roghainneanThèid gach soidhneadh a shealltainnCha dèid ach soidhnidhean le crioptachadh a shealltainn
@@ -1020,7 +953,6 @@
4 KiB2 KiB1 KiB
- Gun dathNa crioptaich dreachdan ach ma tha an crioptachadh an comasThèid a h-uile dreachd a stòradh le crioptachadhSàbhail a h-uile dreachd le crioptachadh
@@ -1033,8 +965,6 @@
Crioptachadh o cheann gu ceannLuchdaich suas teachdaireachdan gu pasgan a’ phuist chuirte às dèidh an curLuchdaich suas na teachdaireachdan a chaidh a chur
- Fosgail roghainnean brathan an t-siostaim
- Roghainnean nam brathanComharraich gun deach a leughadh nuair a sguabas mi às iXOauth2 (Gmail, Outlook)Co-ionnann ri clas a’ cheasnachaidh
@@ -1075,7 +1005,6 @@
Poileasaidh prìobhaideachdSìn air adhart mar cheanglachanMun phost
- Fiosrachadh mun lèirmheasÙghdaranFaigh a-mach dè tha ùr san sgaoileadh seoSeall na h-atharraichean o chionn goirid nuair bhios an aplacaid air a h-ùrachadh
diff --git a/app/ui/legacy/src/main/res/values-gl-rES/strings.xml b/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
index a881224747754d160786b9d2eaf94f0bb23b12f4..b33e3aecf07a25083e84b8661ef97dc9534f09ca 100644
--- a/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
@@ -7,7 +7,6 @@
Mail AccountsMail Unread
- Copyright 2008-%s The K-9 Dog Walkers. Portions Copyright 2006-%s the Android Open Source Project.Welcome to Mail
@@ -16,19 +15,12 @@
The account \"%s\" will be removed from Mail.Contas
- CartafolesAvanzadoRedactarResponderResponder a todosReenviarEscoller cartafol
- Comprobando %s:%s%s
- Obtendo cabeceiras %s:%s%s
- Enviando %s%s
- Procesando %s:%s%s
- \u0020%d/%d
- Sincronización deshabilitada%d seleccionadaSeguinteAnterior
@@ -36,9 +28,6 @@
AceptarCancelarEnviar
- Volver enviar
- Seleccionar
- DeseleccionarResponderResponder a todosEliminar
@@ -87,8 +76,6 @@
Mensaxe non atopadaCargar
%d máis
- Conta \"%s\" reducida de %s a %s
- A compactar a conta \"%s\"Correo novo%d sen ler (%s)+ %1$d máis en %2$s
@@ -140,14 +127,10 @@
Desde: %s <%s>Para:Cc:
- Abrir
- GardarMostrar imaxesNon é posible atopar un visor para %s.Descargar mensaxe completa
- Descargáronse todas as cabeceiras, mais non hai cabeceiras adicionais para mostrar.
- Houbo un erro ao recuperar as cabeceiras adicionais desde a base de datos ou do servidor.Máis deste remitenteMensaxe descartadaMensaxe gardada como borrador
@@ -177,11 +160,7 @@
LixoDesbotar mensaxeEliminar (das notificacións)
- Quitar o User-Agent K-9 das cabeceiras do correo
- Agochar asunto nas notificacións
- Nunca
- Cando o dispositivo está bloqueado
- Sempre
+ Quitar o User-Agent Mail das cabeceiras do correoMostrar botón \'Eliminar\'NuncaPara notificacións dunha mensaxe
@@ -201,7 +180,9 @@
Configurar unha nova contaEnderezo electrónicoContrasinal
- Mostrar contrasinal
+
+
+
Configuración manualRecuperando información da conta\u2026
@@ -238,12 +219,6 @@
Non eliminar do servidorEliminar do servidorMarcar como lida no servidor
- Usar comprensión de rede:
- Móbil
- Wifi
- Outras
- Almacenamento externo (tarxeta SD)
- Almacenamento internoEliminar mensaxes borradas no servidorInmediatamenteDurante a comprobación
@@ -258,11 +233,8 @@
Ver só cartafoles subscritosExpandir automaticamente cartafolCamiño OWA
- OpcionalCamiño de autenticación
- OpcionalAlcume da caixa de correo
- OpcionalAxustes do servidor de saídaServidor SMTPPorto
@@ -273,7 +245,6 @@
AutenticaciónConfiguración non válida: %sOpcións da conta
- Compactar contaFrecuencia de comprobación de correoNuncaCada 15 minutos
@@ -284,8 +255,6 @@
Cada 6 horasCada 12 horasCada 24 horas
- Habilitar o correo push para esta conta
- Se o teu servidor o admite, as mensaxes aparecerán ao instante. Esta opción pode provocar un enlentecemento do sistema.Recargar conexión IDLECada 2 minutosCada 3 minutos
@@ -296,7 +265,6 @@
Cada 48 minutosCada 60 minutosAvisarme cando chegue unha mensaxe
- Avisarme cando o correo está a ser comprobadoNúmero de mensaxes para mostrar10 mensaxes25 mensaxes
@@ -317,8 +285,6 @@
ContinuarAvanzadoAxustes da conta
- Conta predefinida
- Establecer esta conta como predefinida para enviar correoNotificacións de correo novoNotificacións dos cartafolesTodo
@@ -426,7 +392,6 @@
O teu nomeNotificaciónsVibrar
- Vibrar cando chegue unha mensaxePadróns de vibraciónpredefinidopadrón 1
@@ -540,10 +505,6 @@
Navegación coas teclas de volumeNa vista de mensaxesNas listaxes
- %s%s
- - Non lida
- Todas as mensaxes
- Todas as mensaxes nos cartafoles en que se poden facer procurasCaixa de entrada unificadaTodas as mensaxes en cartafoles unificadosUnificar
@@ -575,8 +536,6 @@
AsuntoHora e dataCorpo da mensaxe
- %d%%
- %1$s: %2$sEdición da mensaxeCampos de entrada de textoPredefinido
@@ -620,6 +579,10 @@
Erro ao exportar os axustesImportar axustesImportar
+
+
+
+
Importar axustesA importar os axustes…Mostrar contador de mensaxes non lidas para…
@@ -683,7 +646,5 @@
A cargar…VolverAxustes xerais
- Abrir
- Pechar
diff --git a/app/ui/legacy/src/main/res/values-gl/strings.xml b/app/ui/legacy/src/main/res/values-gl/strings.xml
index 0be23ea916a1fe0bbaf147a0e96489947f6d8a55..b83dbbf07fea4365238f90552ed39ee0e77f711c 100644
--- a/app/ui/legacy/src/main/res/values-gl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gl/strings.xml
@@ -7,7 +7,6 @@
Mail AccountsMail Unread
- Copyright 2008-%s The K-9 Dog Walkers. Porcións Copyright 2006-%s the Android Open Source Project.Welcome to MailMail is the default mail client for /e/
@@ -17,9 +16,8 @@
The account \"%s\" will be removed from Mail.Autores
- Sobre correo K-9
+ Sobre correo MailContas
- CartafolesAvanzadoRedactarResponder
@@ -28,12 +26,6 @@
Reenviar como anexoEscolla ContaSeleccionar carpeta
- Poll %s:%s%s
- Recuperando encabezados %s:%s%s
- Enviando %s%s
- Proc %s:%s%s
- \u0020%d/%d
- Comprobación rematada%d seleccionadaSeguinteAnterior
@@ -41,10 +33,7 @@
AceptarCancelarEnviar
- Volver a enviarO Asunto está baldeiro, pulse de novo e envíe de todos xeitos
- Seleccionar
- DeseleccionarResponderResponder a todosBorrar
@@ -104,8 +93,6 @@
Mensaxes non atopadosFallo ao cargar a mensaxeCargar %d máis
- Conta \"%s\" reducida dende %s a %s
- Compactando conta \"%s\"Correo novo%d nova mensaxe
@@ -177,19 +164,13 @@
Para:Cc:Bcc:
- Abrir
- GardarAmosar imaxeImposible atopar visor para %s.Descargar mensaxe completa
- Non se pode mostrar a mensaxevía %1$s
- Todas as cabeceiras foron descargadas, pero non hai encabezados adicionáis que amosar
- Prodúxose un erro ao descargar os encabezados adicionáis dende a base de datos ou o servidorMáis deste remitenteDe %s
- Depurar/Limpar corpo da mensaxeMensaxes descartadasMensaxe guardada coma borradorVer estrelas
@@ -220,13 +201,9 @@
Marcar todas as mensaxes como lidasBorrar (da notificación)Ocultar cliente de correo
- Quitar K-9 User-Agent dos cabezallos do correo
+ Quitar Mail User-Agent dos cabezallos do correoOcultar zona horariaUtilizar UTC no lugar da hora local nos cabezallos do correo e as respostas
- Agochar asunto nas notificacións
- Nunca
- Cando o dispositivo está bloqueado
- SempreMostar botón \'Eliminar\'NuncaPara notificacións de mensaxes individuais
@@ -246,7 +223,12 @@
Configurar nova contaEnderezo de correoContrasinal
- Mostrar contrasinal
+
+
+
+ Para ver o teu contrasinal aquí, activa o bloqueo de pantalla deste dispositivo.
+ Verificar a túa identidade
+ Desbloquea para ver o teu contrasinalConfiguración ManualRecuperando información da conta\u2026
@@ -284,12 +266,6 @@
Non borrar do servidorBorrar do servidorMarcar coma lido no servidor
- Usar compresión na rede:
- Móbil
- Wi-Fi
- Outras
- Almacenamento externo (SD)
- Almacenamento internoErradicar borradosInmediatamenteDurante comprobación
@@ -304,11 +280,8 @@
Ver só carpetas suscritasAutoexpandir carpetaEnderezo WebDAV (Exchange)
- OpcionalEnderezo de autentificación
- OpcionalEnderezo buzón
- OpcionalCorreo SainteServidor SMTPPorto
@@ -320,7 +293,6 @@
\"%1$s = %2$s\" non é válido con \"%3$s = %4$s\"Configuración inválida: %sOpcións de conta
- CompactarFrecuencia de comprobación de correoNuncaCada 15 minutos
@@ -331,8 +303,6 @@
Cada 6 horasCada 12 horasCada 24 horas
- Activar subscripción para esta conta
- Se se soporta polo teu servidor, as mensaxes aparecerán de xeito instantáneo. Esta opción pode provocar lentitude no sistemaRefrescar conexiónCada 2 minutosCada 3 minutos
@@ -343,7 +313,6 @@
Cada 48 minutosCada 60 minutosAvisarme cando chegue correo
- Avisarme cuando se comprobe o correoNúmero de mensaxes a visualizar10 mensaxes25 mensaxes
@@ -365,8 +334,6 @@
ContinuarAvanzadoConfiguración de conta
- Conta por defecto
- Enviar correo dende esta conta por defectoNotificar correo novoCartafoles de notificaciónsTodas
@@ -518,7 +485,6 @@
O teu nomeNotificaciónsVibrar
- Vibrar cando chega correoSecuencia de vibraciónPor defectoSecuencia 1
@@ -638,10 +604,6 @@
Navegación con teclas de volumeVista de mensaxeListas de mensaxes
- %s%s
- - Sin ler
- Todas as mensaxes
- Mensaxes nas carpetas de búsquedaEntrada unificadaMensajes na Entrada unificadaUnificar
@@ -674,8 +636,6 @@
AsuntoData e HoraCorpo da mensaxe
- %d%%
- %1$s: %2$sEstructura da mensaxeCampos de entrada de textoPredefinido
@@ -733,12 +693,16 @@
Seleccionar ficheiroImportarConfiguración importada con éxito
+
Insira os contrasinais
+
+
Houbo un erro ao importar a configuraciónHoubo un erro ao ler o ficheiro de configuraciónsHoubo un erro ao importar algúns axustesImportado con éxitoRequírese contrasinal
+
Non importadoErro da importaciónMáis tarde
@@ -921,20 +885,10 @@
Amosaranse todas as sinaturasCifrado non dispoñible no modo só-firma!Texto non firmado
- Aviso de APG obsoleto
- AGP xa non está mantido!
- Debido a esto, eliminouse o soporte para APG de K-9 Mail.
- O desenvolvemento detívose a inicios do 2014
- Contén cuestións de seguridade non resoltas
- Pode pulsar aquí para saber máis.
- Vale!
- APGEste correo está cifradoEste correo foi cifrado con OpenPGP.\nPara lelo, precisa instalar e configurar un app compatible con OpenPGP
- Lista de mensaxes K-9
+ Lista de mensaxes MailCargando mensaxes...
- Fallou a obtención da lista de cartafoles
- Fallo ao obter o estado do destinatario do proveedor OpenPGP!Cifrado non posibleAlgún dos destinatarios seleccionados non soportan esta característica!Habilitar cifrado
diff --git a/app/ui/legacy/src/main/res/values-hr/strings.xml b/app/ui/legacy/src/main/res/values-hr/strings.xml
index d3c32206f1b5f2d111eb0d59832c82fff418673d..3b2bba76ef2618bd23dcd77b8d687f36771b08bb 100644
--- a/app/ui/legacy/src/main/res/values-hr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-hr/strings.xml
@@ -8,7 +8,6 @@
Mail UnreadK-9 Šetač Pasa
- Copyright 2008-%s The K-9 Dog Walkers. Portions Copyright 2006-%s the Android Open Source Project.Apache Licenca, Verzija 2.0KnjižniceLicenca
@@ -20,14 +19,12 @@
The account \"%s\" will be removed from Mail.Autori
- Informacije o inačiciČitanje E-PošteDopušta ovoj aplikaciji čitanje vaše E-PošteBrisanje E-PošteDopušta ovoj aplikaciji brisanje vaše E-Pošte.About MailRačuni
- MapeNaprednoSastaviOdgovori
@@ -36,12 +33,6 @@
Proslijedi kao privitakOdaberite računOdaberite Mapu
- Provjera %s:%s%s
- Dobavljanje zaglavlja %s:%s%s
- Šaljem %s%s
- Obrađujem %s:%s%s
- \u0020%d/%d
- Sinhroniziranje je onemogućeno%d odabranoSlijedećaPrethodna
@@ -49,10 +40,7 @@
U REDUOtkažiPošalji
- Pošalji PonovnoSubjekt je prazan, kliknite ponovno da svejedno pošaljete
- Označi
- OdznačiOdgovoriOdgovori svimaObriši
@@ -115,8 +103,6 @@
%.1f MB%.1f kB%d B
- Račun \"%s\" smanjeno sa %s na %s
- Zbijanje računa \"%s\"Nova pošta%d nova poruka
@@ -183,17 +169,12 @@
Za:Cc:Bcc:
- Otvori
- SpremiNije moguće spremiti privitak.Prikaži slikeNe mogu pronaći preglednik za %s.Preuzmi cijelu poruku
- Prikaz poruke nije mogućputem %1$s
- Sva zaglavlja su preuzeta, ali nema dodatnih zaglavlja za prikaz.
- Dohvat dodatnih zaglavlja iz baze podataka ili poslužitelja pošte nije uspio.Više od ovog pošiljateljaOd %sPoruka odbačena
@@ -229,10 +210,6 @@
Remove Mail User-Agent from mail headersSakrij vremensku zonuKoristi UTC umjesto lokalne vremenske zone u zaglavlju mail-a i zaglavlju odgovora
- Sakrij subjekt u obavijesti
- Nikad
- Kad je uređaj zaključan
- UvijekPrikaži gumb \'Obriši\'NikadaZa pojedinačnu obavijest poruke
@@ -252,7 +229,9 @@
Postavi novi računAdresa E-PošteLozinka
- Prikaži lozinku
+
+
+
Ručno postavljanjeDohvaćam informacije o računu\u2026
@@ -290,12 +269,6 @@
Nemoj brisati na poslužiteljuObriši sa poslužiteljaOznači kao pročitano na poslužitelju
- Koristi sabijanje na mreži:
- Mobilna
- Wi-Fi
- Ostalo
- Vanjska pohrana (SD kartica)
- regularna unutarnja pohranaObriši izbrisane poruke sa poslužiteljaOdmahPrilikom provjere
@@ -310,11 +283,8 @@
Prikaži samo pretplaćene mapeAutomatsko proširivanje mapaOWA putanja
- OpcionalnoPutanja identifikacije
- OpcionalnoAlias poštanskog sandučića
- OpcionalnoPostavke odlaznog poslužiteljaSMTP poslužiteljUlaz
@@ -326,7 +296,6 @@
\"%1$s = %2$s\" nije važeća sa \"%3$s = %4$s\"Nevažeće postavljanje: %sOpcije računa
- Sažmi računUčestalost provjera mapaNikadSvakih 15 minuta
@@ -337,8 +306,6 @@
Svakih 6 satiSvakih 12 satiSvaka 24 sata
- Omogući poštu guranja za ovaj račun
- Ako vaš poslužitelj podržava, nove poruke će se prikazivati odmah. Ova opcija može dramatično poboljšati ili pogoršati performanse.Osvježi BESPOSLENU vezuSvake 2 minuteSvake 3 minute
@@ -349,7 +316,6 @@
Svakih 48 minutaSvakih 60 minutaObavijesti me kod primitka pošte
- Obavijesti me prilikom provjere pošteBroj poruka za prikaz10 poruka25 poruka
@@ -371,8 +337,6 @@
NastaviNaprednoPostavke računa
- Zadani račun
- Pošalji poštu s ovog računa po zadanomNova obavijest pošteMapa obavijestiSvi
@@ -392,8 +356,6 @@
Označava poruke pročitanima nakon otvaranja za pregledOznači kao pročitano prilikom brisanjaOznači poruku kao pročitanu kada bude obrisana
- Postavke obavijesti
- Otvori sistemske postavke obavijestiUvijek prikaži slikeNeIz kontakata
@@ -435,9 +397,6 @@
Učestalost provjere mapeBoja računaOznaka boje za ovaj račun koristi se za mape i popis računa
- Bez boje
- Boja LED obavijesti
- LED boja vašeg uređaja koja bi trebala treptati za ovaj računVeličina lokalne mapeDohvaćaj poruke do1 KB
@@ -523,7 +482,6 @@
vaše imeObavijestiVibriranje
- Vibrira nakon primitka e-pošteUzorci vibriranjazadanouzorak 1
@@ -643,10 +601,6 @@
Navigacija tipkama glasnoćeU pregledu porukaU pregledu popisa
- %s%s
- - Nepročitano
- Sve poruke
- Sve poruke uključene u mape pretraživanjaObjedinjena Dolazna PoštaSve poruke u objedinjenim mapamaObjedini
@@ -679,8 +633,6 @@
SubjektVrijeme i datumTijelo poruke
- %d%%
- %1$s: %2$sSastavljanje porukePolja unosa tekstaZadano
@@ -738,12 +690,16 @@
Odaberi datotekuUvozPostavke su uspješno uvezene
+
Molimo unesite lozinke
+
+
Uvoz postavki nije uspioČitanje datoteke postavki nije uspjeloUvoz nekih postavki nije uspioUspješno uvezenoLozinka obavezna
+
Nije uvezenoUvoz nije uspioKasnije
@@ -921,20 +877,11 @@
Samo šifrirani potpisi će biti prikazaniSvi potpisi će biti prikazaniNepotpisani tekst
- APG Upozorenje o uklanjanju
- APG više nije održavan!
- Zbog toga, podrška za APG je uklonjena iz K-9 Mail.
- Razvoj je zaustavljen u ranoj 2014
- Sadrži neispravljene sigurnosne probleme
- Možete kliknuti ovdje da saznate više.
- OK
- APGVaš email je šifriranOva e-poruka je šifrirana korištenjem OpenPGP.\nDa bi ste je pročitali, potrebno je da instalirate i postavite odgovarajuću OpenPGP aplikaciju.Idi u PostavkeK-9 Popis PorukaUčitavam poruke...
- Dobavljanje popisa mape nije uspjeloŠifriranje nije mogućeUključi šifriranjeOnemogući šifriranje
@@ -965,8 +912,6 @@
Došlo je do greške prilikom slanja poruke. Molimo provjerite da li ste povezani na mrežu i postavke izlaznog servera.UključenoIsključeno
- Otvori
- ZatvoriDopusti pristup kontaktima
diff --git a/app/ui/legacy/src/main/res/values-hu/strings.xml b/app/ui/legacy/src/main/res/values-hu/strings.xml
index ebd70e2a6157a02bac1285ccdd3f66a1c3266eb7..0eab6a484a63000d885b54429d5ea0f5a2f5e700 100644
--- a/app/ui/legacy/src/main/res/values-hu/strings.xml
+++ b/app/ui/legacy/src/main/res/values-hu/strings.xml
@@ -8,7 +8,6 @@
Mail UnreadThe K-9 Dog Walkers
- Copyright 2008-%s a The K-9 Dog Walkers. Részleges Copyright 2006-%s az Android Nyílt Forráskódú Projekt.ForráskódApache licenc, 2.0-s verzióNyílt forráskódú Projekt
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.Szerzők
- Felülvizsgálati információkE-mailek olvasásaAz alkalmazás olvashatja az emaileket.E-mailek törléseAz alkalmazás törölheti ez e-maileket.About MailFiókok
- MappákBővítettÍrásVálasz
@@ -49,12 +46,6 @@
Továbbítás mellékletkéntFiók választásaMappa választása
- Lekérés %s:%s%s
- Fejlécek beolvasása %s:%s%s
- %s%sküldése folyamatban van...
- Feldolgozás %s:%s%s
- \u0020%d/%d
- A szinkronizálás letiltásra került.%d kiválasztvaKövetkezőElőző
@@ -62,10 +53,7 @@
OKMégseKüldés
- Küldés újraA tárgy üres. Kattintás újra a küldés erőltetéséhez.
- Kijelölés
- Kijelölés törléseVálaszVálasz mindenkinekTörlés
@@ -129,8 +117,6 @@
%.1f MB%.1f kB%d B
- \"%s\" fiók tömörítésre került: %s=> %s
- \"%s\" fiók tömörítése folyamatban van.Új levél%d új üzenet
@@ -198,20 +184,14 @@
Címzett:Másolat:Titkos másolat:
- Megnyitás
- MentésNem lehet elmenteni a mellékletet.Fényképek megjelenítéseNem sikerült megjelenítőt találni: %s.Teljes üzenet letöltése
- Az üzenet nem jeleníthető meg.ezen keresztül: %1$s
- Az összes fejléc letöltésre került, de nincsenek további megjelenítendő fejlécek.
- A további fejlécek letöltése sikertelen volt az adatbázisból vagy a levélszerverről.Továbbiak ettől a feladótólInnen: %s
- Üzenet törzsének hibakeresése vagy kiürítéseAz üzenet elvetésre került.Az üzenet vázlatként lett elmentve.Csillagok megjelenítése
@@ -245,10 +225,6 @@
A K-9 fazonosító eltávolítása a levél fejlécébőlIdőzóna elrejtéseUTC használata a helyi időzóna helyett a levelek fejlécében és a válasz fejlécében
- Tárgy elrejtése az értesítésekben
- Soha
- Ha a készülék zárolt
- Mindig\"Törlés” gomb megjelenítéseSohaÖnálló üzenet értesítésénél
@@ -268,7 +244,12 @@
Új fiók beállításaEmail címJelszó
- Jelszó megjelenítése
+
+
+
+ Ha itt szeretné megtekinteni jelszavát, engedélyezze a képernyőzárat ezen az eszközön.
+ Személyazonosság ellenőrzése
+ Feloldás a jelszó megtekintéséhezKézi beállításFiókinformációk beolvasása\u2026
@@ -306,12 +287,6 @@
Nincs törlés a kiszolgálórólTörlés a kiszolgálórólOlvasottnak jelölés a kiszolgálón
- Tömörítés használata a hálózaton:
- Mobil
- Wi-Fi
- Egyéb
- Külső tárhely (SD-kártya)
- Szabályos belső tárhelyTörölt üzenetek végleges törlése a kiszolgálórólAzonnalLekérdezéskor
@@ -326,11 +301,8 @@
Csak feliratkozott mappák megjelenítéseMappa automatikus kinyitásaOWA útvonal
- VálaszthatóHitelesítési útvonal
- VálaszthatóPostafiók álnév
- VálaszthatóKimenő kiszolgáló beállításaiSMTP kiszolgálóPort
@@ -342,7 +314,6 @@
„%1$s = %2$s” nem érvényes ezzel: „%3$s = %4$s”Érvénytelen beállítás: %sFiók beállításai
- Fiók tömörítéseMappa lekérdezésének gyakoriságaSoha15 percenként
@@ -353,8 +324,6 @@
6 óránként12 óránként24 óránként
- Levél küldésének engedélyezése ennél a fióknál
- Ha a kiszolgáló támogatja, akkor az új üzenetek azonnal megjelennek. Ez az lehetőség drámaian növelheti vagy csökkenti a teljesítményt.IDLE kapcsolat frissítése2 percenként3 percenként
@@ -365,7 +334,6 @@
48 percenként60 percenkéntÉrtesítsen, amikor levél érkezik
- Értesítés levél ellenőrzéskorMegjelenítendő üzenetek száma10 üzenet25 üzenet
@@ -387,8 +355,6 @@
TovábbBővítettFiók beállításai
- Alapértelmezett fiók
- Levél küldése alapértelmezésben ebből a fiókbólÚj levél értesítésekÉrtesítések mappáiÖsszes
@@ -410,8 +376,6 @@
Üzenet megjelölése olvasottként megtekintésre megnyitáskorMegjelölés olvasottként törléskorÜzenet megjelölése olvasottként törléskor
- Értesítés beállításai
- A rendszer értesítési beállításainak megnyitásaKépek megjelenítése mindigNemPartnerektől
@@ -453,9 +417,6 @@
Mappa lekérdezésének gyakoriságaFiókszínA fiókban és a fióklistában használt fiók kiemelt színe
- Nincs szín
- Értesítési LED színe
- A fiókhoz tartozó villogó eszköz LED színHelyi mappa méreteLegfeljebb ennyi üzenet beolvasása1 KiB
@@ -542,7 +503,6 @@
Saját névÉrtesítésekRezgés
- Rezgés, amikor levél érkezikRezgés mintáialapértelmezett1. minta
@@ -663,10 +623,6 @@
ÜzenetnézetekbenListanézetekbenEgységesített Beérkezett üzenetek mutatása
- %s%s
- – Olvasatlan
- Összes üzenet
- Az összes üzenet kereshető mappákbanEgységes beérkezett üzenetekAz összes üzenet egységes mappákbanEgyesítés
@@ -699,8 +655,6 @@
TárgyIdő és dátumÜzenet törzse
- %d%%
- %1$s: %2$sÜzenet írásaSzöveg beviteli mezőkAlapértelmezett
@@ -758,12 +712,16 @@
Fájl kiválasztásaImportálásA beállítások sikeresen importálva
+
Adja meg a jelszavakat
+
+
Nem sikerült importálni a beállításokatNem sikerült beolvasni a beállítások fájltNem sikerült importálni néhány beállítástSikeresen importálvaJelszó szükséges
+
Nincs importálvaImportálási hibaKésőbb
@@ -956,21 +914,11 @@
Az összes aláírás meg lesz jelenítveA titkosítás nem érhető el csak aláíró módban!Aláíratlan szöveg
- APG elavultsági figyelmeztetés
- Az APG többé nincs karbantartva!
- Emiatt az APG támogatás eltávolításra került a K-9 Mail alkalmazásból.
- A fejlesztés 2014 elején leállt
- Javítatlan biztonsági hibákat tartalmaz
- Kattintson ide, ha többet szeretne megtudni.
- Értem!
- APGEz az e-mail titkosítottAz e-mail titkosítva lett OpenPGP használatával.\nAz elolvasásához egy megfelelő OpenPGP alkalmazást kell telepítenie és beállítania.Ugrás a beállításokhozK-9 üzenetlistaÜzenetek betöltése…
- A mappalista lekérése sikertelen
- Hiba a címzettek állapotának lekérésekor az OpenPGP szolgáltatótól!A titkosítás nem lehetségesNéhány kiválasztott címzett nem támogatja ezt a funkciót!Titkosítás engedélyezése
@@ -1016,8 +964,6 @@ Megtarthatja ezt az üzenetet, és felhasználhatja a titkos kulcs biztonsági m
Hiba történt az üzenet elküldése során. Ellenőrizze a hálózati kapcsolatot és a kimenő kiszolgáló beállítását.BeKi
- Megnyitás
- BezárásHozzáférés engedélyezése a partnerekhezAhhoz, hogy partnerjavaslatokat tudjon tenni és meg tudja jeleníteni a partner nevét és fényképeit, az alkalmazásnak hozzáférésre van szüksége a partnereihez.
diff --git a/app/ui/legacy/src/main/res/values-in/strings.xml b/app/ui/legacy/src/main/res/values-in/strings.xml
index 6754e698deca9a4a3f27c0b25e50e657b25fb5a4..53e4f4d7a193f8f7071a58e835fdbc42d1f2b789 100644
--- a/app/ui/legacy/src/main/res/values-in/strings.xml
+++ b/app/ui/legacy/src/main/res/values-in/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
- Hak Cipta 2008-%s The K-9 Dog Walkers. Hak Cipta Sebagian 2006-%s the Android Open Source Project.
+ The Mail Dog WalkersKode SumberLisensi Apache, Versi 2.0Proyek Sumber Terbuka
@@ -30,14 +29,12 @@
The account \"%s\" will be removed from Mail.Penulis
- Informasi RevisiBaca EmailIzinkan aplikasi ini membaca Email Anda.Hapus EmailIzinkan aplikasi ini menghapus Email Anda.About MailAkun
- FolderLanjutanMenyusunBalas
@@ -46,12 +43,6 @@
Teruskan sebagai LampiranPilih akunPilih Folder
- Menarik %s:%s%s
- Mengunduh tajuk %s:%s%s
- Mengirimkan %s%s
- Memproses %s:%s%s
- \u0020%d/%d
- Sinkronisasi dinonfungsikan%d dipilihSelanjutnyaSebelumnya
@@ -59,10 +50,7 @@
OKBatalKirim
- Kirim LagiSubjek kosong, klik lagi untuk dikirim
- Pilih
- Hapus pilihanBalasBalas semuaHapus
@@ -124,8 +112,6 @@
%.1f MB%.1f kB%d B
- Akun \"%s\" dimampatkan dari %s ke %s
- Memampatkan akun \"%s\"Pesan baru%d pesan baru
@@ -190,20 +176,14 @@
Kepada:Cc:Bcc:
- Buka
- SimpanTidak dapat menyimpan lampiran.Tampilkan gambarTidak bisa menemukan penampil untuk %s.Unduh seluruh pesan
- Tidak dapat menampilkan pesanmelalui %1$s
- Semua tajuk telah diunduh, tetapi tidak ada tajuk lagi untuk ditampilkan.
- Gagal mengambil tajuk tambahan dari basis data atau server surat.Lebih banyak dari pengirim iniDari %s
- Debug / Kosongkan isi pesanPesan dibuangPesan disimpan sebagai drafTampilkan bintang
@@ -237,10 +217,6 @@
Remove Mail User-Agent from mail headersSembunyikan zona waktuGunakan UTC, bukan zona waktu lokal di header email dan header balasan
- Sembunyikan subyek dalam notifikasi
- Tidak pernah
- Ketika perangkat di kunci
- SelaluTampilkan tombol \'Hapus\'Tidak pernahUntuk notifikasi pesan tunggal
@@ -260,7 +236,9 @@
Siapkan akun baruAlamat surelSandi
- Tampilkan sandi
+
+
+
ManualMengambil informasi akun\u2026
@@ -298,12 +276,6 @@
Jangan hapus dari serverHapus dari serverTandai sebagai telah dibaca di server
- Gunakan kompresi pada jaringan:
- Mobile
- Wi-Fi
- Lainnya
- Penyimpanan eksternal (kartu SD)
- Penyimpanan internal umumHapus pesan yang dihapus di serverSegeraKetika menarik
@@ -318,11 +290,8 @@
Hanya tampilkan folder langgananOtomatis lebarkan folderJalur OWA
- OpsionalJalur otentikasi
- OpsionalAlias kotak surat
- OpsionalPengaturan server keluarServer SMTPPort
@@ -334,7 +303,6 @@
\"%1$s = %2$s\" tidak valid dengan \"%3$s = %4$s\"Salah pengaturan: %sOpsi akun
- Akun ringkasFrekuensi penarikan folderTidak pernahSetiap 15 menit
@@ -345,8 +313,6 @@
Setiap 6 jamSetiap 12 jamSetiap 24 jam
- Aktifkan push mail untuk akun ini
- Jika server Anda mendukungnya, pesan baru akan segera muncul. Pilihan ini dapat secara dramatis memperbaiki atau melukai kinerja.Segarkan koneksi IDLESetiap 2 MenitSetiap 3 Menit
@@ -357,7 +323,6 @@
Setiap 48 MenitSetiap 60 MenitBeritahu saya bila surat masuk
- Beritahu saya saat surat sedang diperiksaJumlah pesan yang akan ditampilkan10 pesan25 pesan
@@ -379,8 +344,6 @@
LanjutkanLanjutanPengaturan akun
- Akun bawaan
- Kirim email dari akun ini secara bawaanPemberitahuan surat baruFolder notifikasiSemua
@@ -400,8 +363,6 @@
Tandai pesan sebagai telah dibaca saat dibuka untuk dilihatTandai sebagai terbaca saat dihapusTandai pesan sebagai terbaca saat dihapus
- Pengaturan notifikasi
- Buka pengaturan notifikasi sistemSelalu tampilkan gambarTidakDari kontak
@@ -443,9 +404,6 @@
Frekuensi penarikan folderWarna akunWarna aksen akun ini digunakan dalam folder dan daftar akun
- Tanpa warna
- Warna LED notifikasi
- Warna LED perangkat Anda harus berkedip untuk akun iniUkuran folder lokalAmbil pesan hingga1 KiB
@@ -531,7 +489,6 @@
Nama andaNotifikasiGetar
- Getar saat email tibaPola getarBawaanPola 1
@@ -652,10 +609,6 @@
Dalam tampilan pesanDalam tampilan daftarTampilkan Kotak Masuk Terpadu
- %s%s
- - Belum dibaca
- Semua pesan
- Semua pesan dalam folder yang dapat ditelusuriInbox terpaduSemua pesan dalam folder terpaduSatukan
@@ -688,8 +641,6 @@
SubyekTanggal dan waktuIsi pesan
- %d%%
- %1$s: %2$sSusunan pesanKolom input teksBawaan
@@ -744,12 +695,16 @@
Pilih fileImporPengaturan berhasil diimpor
+
Silahkan masukkan kata sandi
+
+
Gagal mengimpor pengaturanGagal membaca file pengaturanGagal mengimpor beberapa pengaturanBerhasil diimporDiperlukan kata sandi
+
Tidak diimporKegagalan imporNanti
@@ -936,21 +891,11 @@
Semua tanda tangan akan ditampilkanEnkripsi tidak tersedia dalam mode tanda tangan saja!Teks yang tidak ditandai
- Peringatan AGP tak berlaku lagi
- APG tidak lagi dipertahankan!
- Karena itu, dukungan untuk APG telah dihapus dari K-9 Mail.
- Pembangunan dihentikan pada awal 2014
- Berisi masalah keamanan yang tidak diperbaiki
- Anda bisa klik di sini untuk mempelajari lebih lanjut.
- Mengerti!
- APGEmail ini dienkripsiEmail ini telah dienkripsi dengan OpenPGP. \nUntuk membacanya, Anda perlu menginstal dan mengkonfigurasi Aplikasi OpenPGP yang kompatibel.Pergi ke pengaturan
- Daftar pesan K-9
+ Daftar pesan MailMemuat pesan...
- Mengambil daftar folder gagal
- Terjadi kesalahan saat mengambil status penerima dari penyedia OpenPGP!Enkripsi tidak mungkin dilakukanBeberapa penerima yang dipilih tidak mendukung fitur ini!Aktifkan Enkripsi
@@ -996,8 +941,6 @@ Anda dapat menyimpan pesan ini dan menggunakannya sebagai cadangan untuk kunci r
Terjadi kesalahan saat mengirim pesan. Silakan periksa konektivitas jaringan Anda dan konfigurasi server keluar.OnOff
- Buka
- TutupIzinkan akses ke kontakUntuk dapat memberikan saran kontak dan untuk menampilkan nama kontak dan foto, aplikasi perlu akses ke kontak Anda.
diff --git a/app/ui/legacy/src/main/res/values-is/strings.xml b/app/ui/legacy/src/main/res/values-is/strings.xml
index 494eef683fc9b93649d1d354ac66841161634f52..6c1459a5d83d17466fb34580489814b61c9278ea 100644
--- a/app/ui/legacy/src/main/res/values-is/strings.xml
+++ b/app/ui/legacy/src/main/res/values-is/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- K-9 hundatemjararnir
+ Mail hundatemjararnirGrunnkóðiApache notkunarleyfi, útgáfa 2.0Verkefni með opnum grunnkóða
@@ -245,6 +245,8 @@
Setja upp nýjan aðgangNetfangLykilorð
+
+
Til að skoða lykilorðið þitt hér skaltu virkja skjálæsingu á þessu tækiSannreyndu auðkennin þín
@@ -286,10 +288,6 @@
Ekki eyða af póstþjóniEyða af póstþjóniMerkja sem lesið á póstþjóni
- Nota þjöppun á netkerfi:
- Farsímakerfi
- Þráðlausu Wi-Fi
- AnnaðÞurrka út eyddum skilaboðum á póstþjóniStraxVið athuganir
@@ -713,12 +711,16 @@
Velja skráFlytja innTókst að flytja inn stillingar
+
Settu inn lykilorðin
+
+
Mistókst að flytja inn stillingarMistókst að lesa stillingaskráMistókst að flytja inn sumar stillingarInnflutningur tókstLykilorðs er krafist
+
Ekki flutt innInnflutningur mistókstSíðar
@@ -909,14 +911,6 @@
Allar undirritanir verða birtarDulritun ekki tiltæk í einungis-undirrita ham!Óundirritaður texti
- Aðvörun vegna úreldingar APG
- APG er ekki lengur viðhaldið!
- Vegna þessa hefur stuðningur við APG verið fjarlægður úr K-9 póstforritinu.
- Þróun þess hætti snemma á árinu 2014
- Inniheldur óleyst öryggisvandamál
- Þú getur smellt hér til að fræðast betur um þetta.
- Náði því!
- APGÞessi tölvupóstur er dulritaðurÞessi tölvupóstur var dulritaður með OpenPGP.\nTil að lesa hann verður þú að setja upp og stilla samhæft OpenPGP-forrit.Fara í stillingar
@@ -977,7 +971,7 @@ Til að setja Autocrypt upp á nýju tæki, farðu þá eftir leiðbeiningunum s
Í dvala þar til netkerfi er til taksÝttu til að kanna nánar.Upplýsingar um ýtitilkynningar
- Þegar ýtitilkynningar (push-notifications) eru notaðar, heldur K-9 póstforritið virkri tengingu við póstþjóninn. Android krefst þess að þá sé birt í sífellu tilkynning um þetta á meðan forritið er virkt í bakgrunni. %s
+ Þegar ýtitilkynningar (push-notifications) eru notaðar, heldur Mail póstforritið virkri tengingu við póstþjóninn. Android krefst þess að þá sé birt í sífellu tilkynning um þetta á meðan forritið er virkt í bakgrunni. %sHinsvegar leyfir Android einnig að tilkynningin sé falin.Kanna nánarStilla tilkynningu
diff --git a/app/ui/legacy/src/main/res/values-it/strings.xml b/app/ui/legacy/src/main/res/values-it/strings.xml
index 926f3de991738ef900b409a5094a4225b2e56bc2..c4b8d0cb4822bd9878bd56cd07964f3f50108504 100644
--- a/app/ui/legacy/src/main/res/values-it/strings.xml
+++ b/app/ui/legacy/src/main/res/values-it/strings.xml
@@ -1,972 +1,1020 @@
-
-
-
- Mail
- Mail Accounts
- Mail Unread
-
- The K-9 Dog Walkers
- Codice sorgente
- Apache License, Versione 2.0
- Progetto Open Source
- Sito web
- Forum utenti
- Fediverse
- Twitter
- Librerie
- Licenza
- Novità
- Impossibile caricare le novità.
- Versione %s
- Cosa c\'è di nuovo
- Mostra le modifiche recenti quando l\'applicazione è stata aggiornata
- Scopri le novità di questa versione
-
- Welcome to Mail
- Mail is the default mail client for /e/
-
- -- Sent from /e/ Mail.
-
- The account \"%s\" will be removed from Mail.
-
- Autori
- Leggi i messaggi
- Consenti all\'applicazione di leggere i messaggi.
- Elimina messaggi
- Permetti all\'applicazione di eliminare i messaggi.
- About Mail
- Account
- Avanzate
- Componi
- Rispondi
- Rispondi a tutti
- Inoltra
- Inoltra come allegato
- Scegli account
- Scegli cartella
- Sposta in…
- Copia in…
- %d selezionati
- Successivo
- Precedente
-
- OK
- Annulla
- Invia
- L\'oggetto è vuoto, fai clic di nuovo per inviarlo comunque
- Rispondi
- Rispondi a tutti
- Elimina
- Archivia
- Spam
- Inoltra
- Inoltra come allegato
- Modifica come nuovo messaggio
- Sposta
- Sposta in Bozze
- Invio…
- Riarchivia…
- Fatto
- Ignora
- Salva come bozza
- Controlla posta
- Controlla la posta in tutti gli account
- Invia messaggi
- Aggiorna elenco cartelle
- Trova cartella
- Aggiungi account
- Componi
- Cerca
- Cerca ovunque
- Risultati della ricerca
- Impostazioni
- Gestisci cartelle
- Impostazioni account
- Rimuovi account
- Marca come letto
- Condividi
- Scegli il mittente
- Aggiungi stella
- Rimuovi stella
- Copia
- Mostra intestazioni
-
- Indirizzo copiato negli appunti
- IndirizzI copiati negli appunti
-
- Testo dell\'oggetto copiato negli appunti
- Passa al tema scuro
- Passa al tema chiaro
- Marca come da leggere
- Notifica di lettura
- Richiederà la notifica di lettura
- Richiesta notifica di lettura disattivata
- Aggiungi allegato
- Svuota cestino
- Rimuovi messaggi eliminati
- About
- Impostazioni
-
- (Nessun oggetto)
- Nessun mittente
- Caricamento messaggi\u2026
- Errore di connessione
- Messaggio non trovato
- Errore di caricamento dei messaggi
- Scarica altri
+
+
+
+ Mail
+ Account di Mail
+ Mail Nuovi messaggi
+
+ The Mail Dog Walkers
+ Codice sorgente
+ Apache License, Versione 2.0
+ Progetto Open Source
+ Sito web
+ Forum utenti
+ Fediverse
+ Twitter
+ Librerie
+ Licenza
+ Novità
+ Impossibile caricare le novità.
+ Versione %s
+ Cosa c\'è di nuovo
+ Mostra le modifiche recenti quando l\'applicazione è stata aggiornata
+ Scopri le novità di questa versione
+
+ Benvenuto in Mail
+
+Mail è un potente client di posta libero e gratuito per Android.
+
+Le sue funzioni migliorate includono:
+
+
+
Ricezione email push utilizzando IMAP IDLE
+
Migliori prestazioni
+
Ri-archiviazione dei messaggi
+
Firme dei messaggi
+
Auto CCN
+
Sottoscrizioni delle cartelle
+
Sincronizzazione di tutte le cartelle
+
Configurazione dell\'indirizzo di risposta
+
Scorciatoie da tastiera
+
Migliore supporto IMAP
+
Salvataggio degli allegati su SD
+
Svuotamento cestino
+
Ordinamento dei messaggi
+
…e altro
+
+
+Nota che Mail non supporta la maggior parte degli account gratuiti Hotmail e, come molti altri client di posta, ha
+qualche problema quando dialoga con Microsoft Exchange.
+
+ ]]>
+
+ -- \nInviato dal mio dispositivo Android con Mail. Perdonate la brevità.
+
+ L\'account \"%s\" sarà rimosso da Mail.
+
+ Autori
+ Informazioni su Mail
+ Account
+ Avanzate
+ Componi
+ Rispondi
+ Rispondi a tutti
+ Inoltra
+ Inoltra come allegato
+ Scegli account
+ Scegli cartella
+ Sposta in…
+ Copia in…
+ %d selezionati
+ Successivo
+ Precedente
+
+ OK
+ Annulla
+ Invia
+ L\'oggetto è vuoto, fai clic di nuovo per inviarlo comunque
+ Rispondi
+ Rispondi a tutti
+ Elimina
+ Archivia
+ Spam
+ Inoltra
+ Inoltra come allegato
+ Modifica come nuovo messaggio
+ Sposta
+ Sposta in Bozze
+ Invio…
+ Riarchivia…
+ Fatto
+ Ignora
+ Salva come bozza
+ Controlla posta
+ Invia messaggi
+ Aggiorna elenco cartelle
+ Trova cartella
+ Aggiungi account
+ Componi
+ Cerca
+ Cerca ovunque
+ Risultati della ricerca
+ Impostazioni
+ Gestisci cartelle
+ Impostazioni account
+ Rimuovi account
+ Marca come letto
+ Condividi
+ Scegli il mittente
+ Aggiungi stella
+ Rimuovi stella
+ Copia
+ Annulla l\'iscrizione
+ Mostra intestazioni
+
+ Indirizzo copiato negli appunti
+ IndirizzI copiati negli appunti
+ IndirizzI copiati negli appunti
+
+ Testo dell\'oggetto copiato negli appunti
+ Passa al tema scuro
+ Passa al tema chiaro
+ Marca come da leggere
+ Notifica di lettura
+ Richiederà la notifica di lettura
+ Richiesta notifica di lettura disattivata
+ Aggiungi allegato
+ Svuota cestino
+ Rimuovi messaggi eliminati
+ Informazioni
+ Impostazioni
+
+ (Nessun oggetto)
+ Nessun mittente
+ Caricamento messaggi\u2026
+ Errore di connessione
+ Messaggio non trovato
+ Errore di caricamento dei messaggi
+ Scarica altri
%d messaggi
- %.1f GB
- %.1f MB
- %.1f kB
- %d B
- Nuova posta
-
- %d nuovo messaggio
- %d nuovi messaggi
-
- %d non letti (%s)
- + %1$d altri in %2$s
- Rispondi
- Letto
- Marca tutti come letti
- Elimina
- Elimina tutto
- Archivia
- Archivia tutto
- Spam
- Errore di certificato per %s
- Controlla le impostazioni del server
- Autenticazione non riuscita
- Autenticazione non riuscita per %s. Aggiorna le impostazioni del server.
- Controllo posta: %s:%s
- Controllo posta
- Invio posta: %s
- Invio posta
- :
- Sincronizza (Push)
- Visualizzato in attesa di nuovi messaggi
- Messaggi
- Notifiche relative ai messaggi
- Varie
- Notifiche varie come errori ecc.
- Posta in arrivo
- Posta in uscita
- Bozze
- Cestino
- Posta inviata
- Impossibile inviare alcuni messaggi
- Versione
- Attiva la registrazione di debug
- Registra informazioni diagnostiche aggiuntive
- Registra informazioni sensibili
- Può mostrare le password nei file di log.
- Carica altri messaggi
- A:%s
- Oggetto
- Testo del messaggio
- Firma
- -------- Messaggio originale --------
- Oggetto:
- Inviato il:
- Da:
- A:
- Cc:
- %s ha scritto:
- Il %1$s, %2$s ha scritto:
- Devi aggiungere almeno un destinatario.
- Il campo del destinatario è incompleto!
- Nessun indirizzo email trovato per questo contatto.
- Alcuni allegati non possono essere inoltrati poiché non sono stati scaricati.
- Il messaggio non può essere inoltrato poiché alcuni allegato non sono stati scaricati.
- Includi messaggio citato
- Rimuovi testo citato
- Modifica testo citato
- Rimuovi allegato
- Da: %s <%s>
- A:
- Cc:
- Ccn:
- Impossibile salvare l\'allegato.
- Mostra immagini
- Impossibile trovare un visualizzatore per %s.
- Scarica messaggio completo
- tramite %1$s
- Altri da questo mittente
- Da %s
- Messaggio scartato
- Messaggio salvato come bozza
- Mostra stelle
- Le stelle indicano i messaggi evidenziati
- Righe di anteprima
- Mostra nomi dei corrispondenti
- Mostra i nomi dei corrispondenti al posto degli indirizzi email
- Corrispondenti sopra l\'oggetto
- Mostra i nomi dei corrispondenti sopra la riga dell\'oggetto, invece che sotto
- Mostra nomi dei contatti
- Utilizza i nomi dei destinatari dalla rubrica se disponibili
- Colora i contatti
- Colora i nomi nel tuo elenco dei contatti
- Caratteri a larghezza fissa
- Usa carattere a larghezza fissa quando mostri i messaggi di testo
- Adattamento automatico messaggi
- Riduci i messaggi per adattarli allo schermo
- Torna all\'elenco dopo l\'eliminazione
- Ritorna all\'elenco dei messaggi dopo l\'eliminazione di un messaggio
- Mostra messaggio successivo dopo l\'eliminazione
- Mostra messaggio successivo in modo predefinito dopo l\'eliminazione di un messaggio
- Conferma azioni
- Mostra una finestra di dialogo ogni volta che si eseguono le azioni selezionate
- Elimina
- Elimina messaggi con la stella (solo messaggi visualizzati)
- Spam
- Scarta messaggio
- Marca tutti i messaggi come letti
- Elimina (da notifica)
- Nascondi User-Agent
- Remove Mail User-Agent from mail headers
- Nascondi fuso orario
- Usa UTC invece del fuso orario locale nelle intestazioni dei messaggi e nell\'intestazione delle risposte
- Mostra il pulsante \'Elimina\'
- Mai
- Per la notifica di un singolo messaggio
- Sempre
- Notifiche schermata di blocco
- Nessuna notifica sulla schermata di blocco
- Nome applicazione
- Numero nuovi messaggi
- Numero messaggi e mittenti
- Come quando lo schermo è sbloccato
- Modalità silenziosa
- Disabilita suoneria, vibrazione e lampeggi di notte
- Disabilita notifiche
- Disabilita completamente le notifiche quando in Modalità silenziosa
- Attiva alle
- Disattiva alle
- Configura un nuovo account
- Indirizzo email
- Password
-
- Per visualizzare la tua password qui, abilita il blocco dello schermo su questo dispositivo.
- Verifica la tua identità
- Sblocca per visualizzare la tua password
- Configurazione manuale
-
- Recupero delle informazioni dell\'account\u2026
- Verifica delle impostazioni del server della posta in arrivo\u2026
- Verifica delle impostazioni del server della posta in uscita\u2026
- Autenticazione\u2026
- Recupero delle impostazioni dell\'account in corso\u2026
- Annullamento in corso\u2026
- Hai quasi fatto!
- Assegna un nome all\'account (facoltativo):
- Scrivi il tuo nome (visualizzato nei messaggi in uscita):
- Tipo di account
- Che tipo di account è questo?
- POP3
- IMAP
- Password normale
- Password, trasmessa in modo non sicuro
- Password cifrata
- Certificato client
- Impostazioni server della posta in arrivo
- Nome utente
- Password
- Certificato client
- Server POP3
- Server IMAP
- Server Exchange
- Porta
- Protezione
- Autenticazione
- Nessuna
- SSL/TLS
- STARTTLS
- \"%1$s = %2$s\" non è valido con \"%3$s = %4$s\"
- Quando elimino un messaggio
- Non eliminare dal server
- Elimina dal server
- Segna come letto sul server
- Utilizza la compressione sulla rete:
- Cellulare
- Wi-Fi
- Altro
- Cancella i messaggi eliminati sul server
- Immediatamente
- Durante ogni recupero
- Manualmente
- Rilevamento automatico dello spazio dei nomi IMAP
- Prefisso percorso IMAP
- Cartella Bozze
- Cartella Posta inviata
- Cartella Cestino
- Cartella Archivio
- Cartella Spam
- Mostra solo le cartelle sottoscritte
- Auto-espandi cartella
- Percorso OWA
- Percorso di autenticazione
- Alias della casella postale
- Impostazioni server della posta in uscita
- Server SMTP
- Porta
- Protezione
- Richiedi accesso.
- Nome utente
- Password
- Autenticazione
- \"%1$s = %2$s\" non è valido con \"%3$s = %4$s\"
- Configurazione non valida: %s
- Opzioni account
- Frequenza di recupero della cartella
- Mai
- Ogni 15 minuti
- Ogni 30 minuti
- Ogni ora
- Ogni 2 ore
- Ogni 3 ore
- Ogni 6 ore
- Ogni 12 ore
- Ogni 24 ore
- Aggiorna connessione inattiva
- Ogni 2 minuti
- Ogni 3 minuti
- Ogni 6 minuti
- Ogni 12 minuti
- Ogni 24 minuti
- Ogni 36 minuti
- Ogni 48 minuti
- Ogni 60 minuti
- Notifica l\'arrivo di nuovi messaggi
- Numero di messaggi da visualizzare
- 10 messaggi
- 25 messaggi
- 50 messaggi
- 100 messaggi
- 250 messaggi
- 500 messaggi
- 1000 messaggi
- 2500 messaggi
- 5000 messaggi
- 10000 messaggi
- tutti i messaggi
- Impossibile copiare o spostare un messaggio che non sia sincronizzato con il server
- Impossibile completare la configurazione
- Nome utente o password errata.\n(%s)
- Il server ha fornito un certificato SSL non valido. A volte ciò è dovuto a problemi di configurazione del server. In altri casi potrebbe dipendere da un tentativo di attacco a te o al tuo server di posta. Se non sei sicuro delle cause, fai clic su Rifiuta e contatta gli amministratori del tuo server di posta.\n\n(%s)
- Impossibile connettersi al server.\n(%s)
- Modifica i dettagli
- Continua
- Avanzate
- Impostazioni account
- Notifiche nuovi messaggi
- Notifiche delle cartelle
- Tutte
- Soltanto cartelle di 1a classe
- Cartelle di 1a e 2a classe
- Tutte eccetto le cartelle di 2a classe
- Nessuna
- Sincronizza notifiche
- Il tuo indirizzo email
- Notifica nella barra di stato l\'arrivo di un nuovo messaggio
- Notifica nella barra di stato mentre la posta viene controllata
- Includi la posta in uscita
- Mostra una notifica per i messaggi che ho inviato
- Solo contatti
- Mostra notifiche solo per i messaggi da contatti conosciuti
- Ignora i messaggi di chat
- Non visualizzare notifiche per i messaggi appartenenti ad una chat
- Marca come letto all\'apertura
- Marca un messaggio come letto quando viene aperto per la visualizzazione
- Marca come letto all\'eliminazione
- Marca un messaggio come letto quando viene eliminato
- Categorie di notifiche
- Configura le notifiche per i nuovi messaggi
- Configura le notifiche per errori e stato
- Mostra sempre le immagini
- No
- Dai contatti
- Da chiunque
- Invio messaggio
- Cita messaggio originale nella risposta
- Includi il messaggio originale nella risposta.
- Risposta dopo testo citato
- Il messaggio originale sarà visualizzato sopra la tua risposta.
- Rimuovi firma nella risposta
- Le firme saranno rimosse dai messaggi citati
- Formato messaggio
- Testo semplice (saranno rimosse immagini e formattazione)
- HTML (preserva immagini e formattazione)
- Automatico
- Mostra sempre CC/Ccn
- Notifica di lettura
- Richiedi sempre la notifica di lettura
- Aspetto testo citato nelle risposte
- Prefisso (come Gmail)
- Intestazione (come Outlook)
- Carica i messaggi inviati
- Carica i messaggi nella cartella Posta inviata dopo l\'invio
- Impostazioni generali
- Lettura dei messaggi
- Recupero dei messaggi
- Cartelle
- Prefisso testo citato
- Cifratura end-to-end
- Abilita supporto OpenPGP
- Seleziona applicazione OpenPGP
- Configura chiave end-to-end
- Nessuna applicazione OpenPGP configurata
- Connesso a %s
- Configurazione in corso…
- Memorizza tutte le bozze con cifratura
- Tutte le bozze saranno memorizzate con cifratura
- Cifra le bozze solo se la cifratura è abilitata
- Frequenza di recupero della cartella
- Colore dell\'account
- Scegli il colore dell\'account utilizzato nell\'elenco delle cartelle e degli account
- Colore del LED di notifica
- Scegli con quale colore dovrà lampeggiare il LED del dispositivo per questo account
- Dimensione cartelle locali
- Scarica messaggi di dimensione fino a
- 1 KiB
- 2 KiB
- 4 KiB
- 8 KiB
- 16 KiB
- 32 KiB
- 64 KiB
- 128 KiB
- 256 KiB
- 512 KiB
- 1 MiB
- 2 MiB
- 5 MiB
- 10 MiB
- qualsiasi dimensione (nessun limite)
- Sincronizza messaggi da
- qualsiasi momento (senza limite)
- oggi
- gli ultimi 2 giorni
- gli ultimi 3 giorni
- l\'ultima settimana
- le ultime 2 settimane
- le ultime 3 settimane
- l\'ultimo mese
- gli ultimi 2 mesi
- gli ultimi 3 mesi
- gli ultimi 6 mesi
- l\'ultimo anno
- Cartelle da visualizzare
- Tutte
- Solo cartelle di 1a classe
- Cartelle di 1a e 2a classe
- Tutte eccetto le cartelle di 2a classe
- Cartelle da sincronizzare
- Tutte
- Solo cartelle di 1a classe
- Cartelle di 1a e 2a classe
- Tutte eccetto le cartelle di 2a classe
- Nessuna
- Cartelle push
- Tutte
- Solo cartelle di 1a classe
- Cartelle di 1a e 2a classe
- Tutte eccetto le cartelle di 2a classe
- Nessuna
- Cartelle di destinazione per spostamento/copia
- Tutte
- Solo cartelle di 1a classe
- Cartelle di 1a e 2a classe
- Tutte eccetto le cartelle di 2a classe
- Sincronizza eliminazioni sul server
- Rimuovi i messaggi una volta eliminati sul server
- Applicazione OpenPGP mancante - è stata disinstallata?
- Impostazioni cartella
- Mostra nel gruppo superiore
- Mostra nella parte superiore dell\'elenco delle cartelle
- Classe di visualizzazione delle cartelle
- Nessuna classe
- 1a classe
- 2a classe
- Classe di recupero della cartella
- Nessuna
- 1a classe
- 2a classe
- Uguale alla classe di visualizzazione
- Classe delle cartelle push
- Nessuna classe
- 1a classe
- 2a classe
- Uguale alla classe di poll
- Classe di notifica delle cartelle
- Nessuna classe
- 1a classe
- 2a classe
- Uguale alla classe di push
- Cancella messaggi locali
- Server della posta in arrivo
- Configura il server della posta in arrivo
- Server della posta in uscita
- Configura il server della posta in uscita (SMTP)
- Nome account
- Il tuo nome
- Notifiche
- Vibrazione
- Vibra quando arriva un messaggio
- Predefinito
- Ripeti vibrazione
- Suoneria nuovo messaggio
- LED lampeggiante
- LED lampeggiante quando arriva un messaggio
- Opzioni di composizione messaggi
- Opzioni predefinite di composizione
- Imposta Da, Ccn e firma predefiniti
- Gestione identità
- Imposta indirizzo mittente e firma alternativi
- Gestione identità
- Gestione identità
- Modifica identità
- Salva
- Nuova identità
- Invia in Ccn tutti i messaggi a
- Modifica
- Sposta su
- Sposta giù
- Sposta in cima / rendi predefinito
- Rimuovi
- Descrizione identità
- (Opzionale)
- Il tuo nome
- (Opzionale)
- Indirizzo email
- (Richiesto)
- Indirizzo per le risposte
- (Opzionale)
- Firma
- (Opzionale)
- Usa la firma
- Firma
- Identità iniziale
- Scegli identità
- Invia come
- Non puoi rimuovere l\'unica identità
- Non puoi utilizzare un\'identità senza un indirizzo email
- Data messaggio (più vecchi)
- Data messaggio (più recenti)
- Oggetto in ordine alfabetico
- Oggetto in ordine alfabetico inverso
- Mittente in ordine alfabetico
- Mittente in ordine alfabetico inverso
- Prima i messaggi con stella
- Prima i messaggi senza stella
- Prima i messaggi non letti
- Prima i messaggi letti
- Prima i messaggi con allegati
- Prima i messaggi senza allegati
- Ordina per…
- Data
- Arrivo
- Oggetto
- Mittente
- Stella
- Letto/non letto
- Allegati
- Rimuovi account
- Certificato non riconosciuto
- Accetta chiave
- Rifiuta chiave
- Canc (o D) - Elimina\nR - Rispondi\nA - Rispondi a tutti\nC - Componi\nF - Inoltra\nM - Sposta\nV - Archivia\nY - Copia\nZ - Marca come (non) letto\nG - Stella\nO - Tipo di ordinamento\nI - Ordinamento\nQ - Ritorna alle cartelle\nS - Seleziona/deseleziona\nJ o P - Messaggio precedente\nK o N - Messaggio successivo
- Canc (o D) - Elimina\nC - Componi\nM - Sposta\nV - Archivia\nY - Copia\nZ - Marca come (non) letto\nG - Stella\nO - Tipo di ordinamento\nI - Ordinamento\nQ - Ritorna alle cartelle\nS - Seleziona/deseleziona
- Il nome della cartella contiene
- Mostra cartelle…
- Tutte le cartelle
- Cartelle di 1a classe
- Cartelle di 1a e 2a classe
- Nascondi le cartelle di 2a classe
- Posizione della firma
- Prima del testo citato
- Dopo il testo citato
- Usa il tema dell\'applicazione
- Scuro
- Chiaro
- Utilizza predefinito di sistema
- Visualizzazione
- Globali
- Debug
- Riservatezza
- Rete
- Interazione
- Elenco account
- Elenchi dei messaggi
- Messaggi
- Tema
- Tema della vista dei messaggi
- Tema del compositore
- Lingua
- Nessuna impostazionie trovata
- Tema fisso del messaggio
- Seleziona il tema della vista dei messaggi durante la visualizzazione del messaggio
- Usa un tema fisso nella vista dei messaggi
- Predefinita di sistema
- Sincronizzazione in background
- Mai
- Sempre
- Quando \'Sincronizzazione automatica\' è selezionato
- Seleziona tutto
- Limite di cartelle da sincronizzare in push
- 5 cartelle
- 10 cartelle
- 25 cartelle
- 50 cartelle
- 100 cartelle
- 250 cartelle
- 500 cartelle
- 1000 cartelle
- Animazione
- Utilizza effetti visivi vistosi
- Navigazione con tasti volume
- In visualizzazione messaggi
- In visualizzazione elenco
- Mosta posta in arrivo combinata
- Mostra il numero dei messaggi con stella
- Posta in arrivo combinata
- Tutti i messaggi nella Posta in arrivo combinata
- Combina
- Tutti i messaggi sono visualizzati nella Posta in arrivo combinata
- Cartelle da analizzare
- Tutte
- Visibili
- Nessuna
- Nessuna
- Automatica (%s)
- Dimensione carattere
- Configura la dimensione dei caratteri
- Elenco account
- Nome account
- Descrizione account
- Elenchi delle cartelle
- Nome cartella
- Stato cartella
- Elenchi dei messaggi
- Oggetto
- Mittente
- Data
- Anteprima
- Messaggi
- Mittente
- A
- Cc
- Ccn
- Intestazioni aggiuntive
- Oggetto
- Ora e data
- Corpo del messaggio
- Composizione messaggio
- Campi di testo
- Predefinito
- Piccolissimo
- Molto piccolo
- Più piccolo
- Piccolo
- Medio
- Grande
- Più grande
- Nessuna applicazione trovata per questa azione.
- Invio non riuscito: %s
- Vuoi salvare la bozza?
- Vuoi salvare o scartare questo messaggio?
- Vuoi salvare o scartare le modifiche?
- Vuoi eliminare il messaggio?
- Sei sicuro di voler eliminare questo messaggio?
- Seleziona il testo da copiare.
- Vuoi cancellare i messaggi locali?
- Questo rimuoverà tutti i messaggi locali dalla cartella. Nessun messaggio sarà eliminato dal server.
- Cancella messaggi
- Conferma l\'eliminazione
- Vuoi eliminare questo messaggio?
-
- Vuoi davvero eliminare questo messaggio?
- Vuoi davvero eliminare i %1$d messaggi selezionati?
-
- Sì
- No
- Conferma di marcare tutti come letti
- Vuoi marcare tutti i messaggi come letti?
- Conferma lo svuotamento del cestino
- Vuoi svuotare la cartella del cestino?
- Sì
- No
- Conferma spostamento nella cartella spam
-
- Vuoi davvero spostare il messaggio nella cartella spam?
- Vuoi davvero spostare i %1$d messaggi selezionati nella cartella spam?
-
- Sì
- No
- Scaricamento allegato
- »
- ›
- Copia di sicurezza
- Varie
- Esporta impostazioni
- Esporta
- Condividi
- Esportazione impostazioni…
- Impostazioni esportate correttamente
- Esportazione impostazioni non riuscita
- Importa impostazioni
- Seleziona file
- Importa
- Impostazioni importate correttamente
- Digita le password
- Importazione impostazioni non riuscita
- Lettura file delle impostazioni non riuscita
- Importazione di alcune impostazioni non riuscita
- Importate correttamente
- Password richiesta
- Non importate
- Importazione non riuscita
- Più tardi
- Importa impostazioni
- Importazione impostazioni…
-
- Per poter utilizzare l\'account \"%s\" devi fornire le password del server.
- Per poter utilizzare l\'account \"%s\" devi fornire le password del server.
-
- Password del server di ingresso
- Password del server di uscita
- Usa la stessa password per il server di uscita
- Nome server: %s
- Mostra numero messaggi non letti per…
- Account
- L\'account per il quale dovrebbe essere visualizzato il contatore non letti
- Posta in arrivo combinata
- Conteggio cartella
- Visualizza il contatore non letti solo di una singola cartella
- Cartella
- La cartella per la quale dovrebbe essere visualizzato il contatore non letti
- Fatto
- %1$s - %2$s
- Nessun account selezionato
- Nessuna cartella selezionata
- Senza testo
- Apri collegamento
- Condividi collegamento
- Copia collegamento negli appunti
- Collegamento
- Copia testo del collegamento negli appunti
- Testo del collegamento
- Immagine
- Visualizza immagine
- Salva immagine
- Scarica immagine
- Copia URL immagine negli appunti
- URL immagine
- Chiama numero
- Salva nei contatti
- Copia numero negli appunti
- Numero di telefono
- Invia messaggio
- Salva nei contatti
- Copia indirizzo negli appunti
- Indirizzo email
- Tutti
- 10
- 25
- 50
- 100
- 250
- 500
- 1000
- Limite risultati di ricerca sul server
- Invio della richiesta al server
-
- Recupero di %d risultato
- Recupero di %d risultati
-
-
- Recupero di %1$d di %2$d risultati
- Recupero di %1$d di %2$d risultati
-
- Ricerca remota non riuscita
- Cerca
- Abilita la ricerca sul server
- Cerca messaggi sul server oltre a quelli sul dispositivo
- Cerca messaggi sul server
- Le ricerche sul server richiedono una connessione alla rete.
- Cambia colore dopo la lettura
- Uno sfondo diverso mostrerà che il messaggio è stato letto
- Vista di conversazione
- Raggruppa i messaggi per conversazione
- Aggiornamento dei database
- Aggiornamento dei database…
- Aggiornamento del database dell\'account \"%s\"
- Mostra schermo diviso
- Sempre
- Mai
- Con orientazione orizzontale
- Seleziona un messaggio a sinistra
- Mostra immagini dei contatti
- Mostra le immagini dei contatti nell\'elenco dei messaggi
- Marca tutti come letti
- Colora le immagini dei contatti
- Colora le immagini mancanti dei contatti
- Azioni del messaggio visibile
- Mostra le azioni selezionate nel menu di visualizzazione messaggio
- Caricamento allegati…
- Invio messaggio
- Salvataggio bozza
- Recupero allegati…
- Impossibile autenticare. Il server non offre la funzionalità SASL EXTERNAL. Ciò potrebbe essere causato da un problema con il certificato client (scaduto, autorità di certificazione sconosciuta) o da un altro problema di configurazione.
-
- Usa certificato client
- Nessun certificato client
- Rimuovi selezione del certificato client
- Recupero del certificato client per l\'alias \"%s\" non riuscito
- Opzioni avanzate
- Il certificato client \"%1$s\" è scaduto o non è ancora valido (%2$s)
-
- *Cifrato*
- Aggiungi da Contatti
- Cc
- Ccn
- A
- Da
- Rispondi a
- <Destinatario sconosciuto>
- <Mittente sconosciuto>
- Casa
- Lavoro
- Altro
- Cellulare
- Non è stata configurata una cartella Bozze per questo account!
- Nessuna chiave configurata per questo account! Controlla le impostazioni.
- Il fornitore della cifratura utilizza una versione incompatibile. Controlla le tue impostazioni!
- Impossibile connettersi al fornitore della crittografia, controlla le impostazioni oppure fai clic sull\'icona di cifratura per riprovare!
- Inizializzazione della cifratura end-to-end non riuscita, controlla le tue impostazioni
- La modalità PGP/INLINE non supporta gli allegati!
- Abilita PGP/INLINE
- Disabilita PGP/INLINE
- Abilita PGP solo firma
- Disabilita PGP solo firma
- Modalità PGP/INLINE
- Questo messaggio è inviato nel formato PGP/INLINE.\nQuesto formato dovrebbe essere utilizzato solo per compatibilità:
- Alcuni client supportano solo questo formato
- Le firme potrebbero danneggiarsi durante il transito
- Gli allegati non sono supportati
- Ricevuto!
- Disabilita
- Lascia abilitato
- Ricevuto!
- Disabilita
- Lascia abilitato
- Modailità PGP solo firma
- In questa modalità, la tua chiave PGP sarà utilizzata per creare una firma di cifratura di un messaggio di posta non cifrato.
- Ciò non cifra il messaggio di posta, ma verifica che sia stato inviato dalla tua chiave.
- Le firme potrebbero danneggiarsi in caso di invio a liste di distribuzione.
- Le firme possono essere visualizzate come allegati \'signature.asc\' in alcuni client.
- I messaggi cifrati includono sempre una firma.
- Testo semplice
- la firma end-to-end conteneva un errore
- devi scaricare completamente il messaggio per verificare la firma
- contiene una firma end-to-end non supportata
- Il messaggio è cifrato, ma in un formato non supportato.
- Il messaggio è cifrato, ma la decifratura è stata annullata.
- Testo semplice firmato End-to-End
- da firmatario verificato
- Testo semplice firmato
- ma la chiave end-to-end non corrisponde al mittente
- ma la chiave end-to-end è scaduta
- ma la chiave end-to-end è revocata
- ma la chiave end-to-end non è considerata sicura
- da una chiave end-to-end sconosciuta
- Cifrato
- ma si è verificato un errore di decifratura
- devi scaricare completamente per la decifratura
- ma nessuna applicazione di cifratura è configurata
- Cifrato
- ma non end-to-end
- Cifrato end-to-end
- da mittente verificato
- Cifrato
- da una chiave end-to-end sconosciuta
- ma la chiave end-to-end non corrisponde al mittente
- ma la chiave end-to-end è scaduta
- ma la chiave end-to-end è revocata
- ma la chiave end-to-end non è considerata sicura
- ma i dati end-to-end presentano errori
- ma la cifratura non è considerata sicura
- OK
- Chiave di ricerca
- Visualizza firmatario
- Visualizza mittente
- Dettagli
- Sblocca
- Questa parte non è stata cifrata, e potrebbe non essere sicura.
- Allegato non protetto
- Caricamento in corso…
- La decifratura è stata annullata.
- Riprova
- Il messaggio cifrato deve essere scaricato per la decifratura.
- Errore durante la decifratura del messaggio
- I caratteri speciali al momento non sono supportati!
- Errore durante l\'analisi dell\'indirizzo!
- Nascondi le firme non cifrate
- Saranno visualizzate solo le firme cifrate
- Tutte le firme saranno visualizzate
- Cifratura non disponibile in modalità solo firma!
- Testo non firmato
- Avviso di disapprovazione di APG
- APG non è più mantenuto!
- Per questo motivo, il supporto di APG è stato rimosso da K-9 Mail.
- Sviluppo interrotto all\'inizio del 2014
- Contiene problemi di sicurezza non corretti
- Puoi fare clic qui per ulteriori informazioni.
- Ricevuto!
- APG
- Questo messaggio è cifrato
- Questo messaggio è stato cifrato con OpenPGP.\nPer leggerlo, devi installare e configurare un\'applicazione compatibile con OpenPGP.
- Vai alle impostazioni
- Mail Message List
- Caricamento messaggi…
- Cifratura non possibile
- Alcuni dei destinatari selezionati non supportano questa funzionalità!
- Abilita cifratura
- Disabilita cifratura
- La cifratura dei messaggi assicura che possano essere letti dal destinatario e da nessun altro.
- La cifratura sarà mostrata solo se supportata da tutti i destinatari, e devono averti inviato un\'email in precendenza.
- Commuta la cifratura facendo clic su questa icona.
- Ricevuto
- Indietro
- Disabilita cifratura
- Cifratura OpenPGP
- Modalità di cifratura reciproca automatica
- Modalità di cifratura reciproca automatica
- I messaggi saranno normalmente cifrati per scelta o quando si risponde a un messaggio cifrato.
- Se il mittente e i destinatari abilitano la modalità reciproca, la cifratura sarà abilitata in modo predefinito.
- Puoi fare clic qui per ulteriori informazioni.
- Impostazioni generali
- Nessuna applicazione OpenPGP installata
- Installa
- Mail requires OpenKeychain for end-to-end encryption.
- Messaggio cifrato
- Cifra l\'oggetto dei messaggi
- Potrebbe non essere supportato da alcuni destinatari
- Errore interno: account non valido!
- Errore durante la connessione a %s!
- Invio messaggio di configurazione di Autocrypt
- Condividi in modo sicuro la configurazione end-to-end con altri dispositivi
- Messaggio di configurazione di Autocrypt
- Un messaggio di configurazione di Autocrypt condivide in modo sicuro la tua configurazione end-to-end con altri dispositivi.
- Invio messaggio di configurazione
- Il messaggio sarà inviato al tuo indirizzo:
- Generazione del messaggio di configurazione…
- Invio del messaggio a:
- Per terminare, apri il messaggio sull\'altro dispositivo e digita il codice di configurazione.
- Mostra codice di configurazione
- Messaggio di configurazione di Autocrypt
- Questo messaggio contiene tutte le informazioni per trasferire in modo sicuro le tue impostazioni di Autocrypt insieme alla tua chiave segreta dal tuo dispositivo originale.
+ %.1f GB
+ %.1f MB
+ %.1f kB
+ %d B
+ Nuova posta
+
+ %d nuovo messaggio
+ %d nuovi messaggi
+ %d nuovi messaggi
+
+ %d non letti (%s)
+ + %1$d altri in %2$s
+ Rispondi
+ Letto
+ Marca tutti come letti
+ Elimina
+ Elimina tutto
+ Archivia
+ Archivia tutto
+ Spam
+ Errore di certificato per %s
+ Controlla le impostazioni del server
+ Autenticazione non riuscita
+ Autenticazione non riuscita per %s. Aggiorna le impostazioni del server.
+ Controllo posta: %s:%s
+ Controllo posta
+ Invio posta: %s
+ Invio posta
+ :
+ Sincronizza (Push)
+ Visualizzato in attesa di nuovi messaggi
+ Messaggi
+ Notifiche relative ai messaggi
+ Varie
+ Notifiche varie come errori ecc.
+ Posta in arrivo
+ Posta in uscita
+ Bozze
+ Cestino
+ Posta inviata
+ Impossibile inviare alcuni messaggi
+ Versione
+ Attiva la registrazione di debug
+ Registra informazioni diagnostiche aggiuntive
+ Registra informazioni sensibili
+ Può mostrare le password nei file di log.
+ Carica altri messaggi
+ A:%s
+ Oggetto
+ Testo del messaggio
+ Firma
+ -------- Messaggio originale --------
+ Oggetto:
+ Inviato il:
+ Da:
+ A:
+ Cc:
+ %s ha scritto:
+ Il %1$s, %2$s ha scritto:
+ Devi aggiungere almeno un destinatario.
+ Il campo del destinatario è incompleto!
+ Nessun indirizzo email trovato per questo contatto.
+ Alcuni allegati non possono essere inoltrati poiché non sono stati scaricati.
+ Il messaggio non può essere inoltrato poiché alcuni allegato non sono stati scaricati.
+ Includi messaggio citato
+ Rimuovi testo citato
+ Modifica testo citato
+ Rimuovi allegato
+ Da: %s <%s>
+ A:
+ Cc:
+ Ccn:
+ Impossibile salvare l\'allegato.
+ Mostra immagini
+ Impossibile trovare un visualizzatore per %s.
+ Scarica messaggio completo
+ tramite %1$s
+ Altri da questo mittente
+ Da %s
+ Messaggio scartato
+ Messaggio salvato come bozza
+ Mostra stelle
+ Le stelle indicano i messaggi evidenziati
+ Righe di anteprima
+ Mostra nomi dei corrispondenti
+ Mostra i nomi dei corrispondenti al posto degli indirizzi email
+ Corrispondenti sopra l\'oggetto
+ Mostra i nomi dei corrispondenti sopra la riga dell\'oggetto, invece che sotto
+ Mostra nomi dei contatti
+ Utilizza i nomi dei destinatari dalla rubrica se disponibili
+ Colora i contatti
+ Colora i nomi nel tuo elenco dei contatti
+ Colore del nome di contatto
+ Caratteri a larghezza fissa
+ Usa carattere a larghezza fissa quando mostri i messaggi di testo
+ Adattamento automatico messaggi
+ Riduci i messaggi per adattarli allo schermo
+ Torna all\'elenco dopo l\'eliminazione
+ Ritorna all\'elenco dei messaggi dopo l\'eliminazione di un messaggio
+ Mostra messaggio successivo dopo l\'eliminazione
+ Mostra messaggio successivo in modo predefinito dopo l\'eliminazione di un messaggio
+ Conferma azioni
+ Mostra una finestra di dialogo ogni volta che si eseguono le azioni selezionate
+ Elimina
+ Elimina messaggi con la stella (solo messaggi visualizzati)
+ Spam
+ Scarta messaggio
+ Marca tutti i messaggi come letti
+ Elimina (da notifica)
+ Nascondi User-Agent
+ Rimuove lo User-Agent di Mail dalle intestazioni dei messaggi
+ Nascondi fuso orario
+ Usa UTC invece del fuso orario locale nelle intestazioni dei messaggi e nell\'intestazione delle risposte
+ Mostra il pulsante \'Elimina\'
+ Mai
+ Per la notifica di un singolo messaggio
+ Sempre
+ Notifiche schermata di blocco
+ Nessuna notifica sulla schermata di blocco
+ Nome applicazione
+ Numero nuovi messaggi
+ Numero messaggi e mittenti
+ Come quando lo schermo è sbloccato
+ Modalità silenziosa
+ Disabilita suoneria, vibrazione e lampeggi di notte
+ Disabilita notifiche
+ Disabilita completamente le notifiche quando in Modalità silenziosa
+ Attiva alle
+ Disattiva alle
+ Configura un nuovo account
+ Indirizzo email
+ Password
+ Per usare un account email con Mail, tu devi registrarti e permettere l\'accesso dell\' app alla tua posta.
+
+ Registrati
+
+ Registrati con Google
+
+ Per visualizzare la tua password qui, abilita il blocco dello schermo su questo dispositivo.
+ Verifica la tua identità
+ Sblocca per visualizzare la tua password
+ Configurazione manuale
+
+ Recupero delle informazioni dell\'account\u2026
+ Verifica delle impostazioni del server della posta in arrivo\u2026
+ Verifica delle impostazioni del server della posta in uscita\u2026
+ Autenticazione\u2026
+ Recupero delle impostazioni dell\'account in corso\u2026
+ Annullamento in corso\u2026
+ Hai quasi fatto!
+ Assegna un nome all\'account (facoltativo):
+ Scrivi il tuo nome (visualizzato nei messaggi in uscita):
+ Tipo di account
+ Che tipo di account è questo?
+ POP3
+ IMAP
+ Password normale
+ Password, trasmessa in modo non sicuro
+ Password cifrata
+ Certificato client
+ OAuth 2.0
+ Impostazioni server della posta in arrivo
+ Nome utente
+ Password
+ Certificato client
+ Server POP3
+ Server IMAP
+ Server Exchange
+ Porta
+ Protezione
+ Autenticazione
+ Nessuna
+ SSL/TLS
+ STARTTLS
+ \"%1$s = %2$s\" non è valido con \"%3$s = %4$s\"
+ Quando elimino un messaggio
+ Non eliminare dal server
+ Elimina dal server
+ Marca come letto sul server
+ Usa la compressione
+ Cancella i messaggi eliminati sul server
+ Immediatamente
+ Durante ogni recupero
+ Manualmente
+ Rileva automaticamente lo spazio dei nomi IMAP
+ Prefisso percorso IMAP
+ Cartella Bozze
+ Cartella Posta inviata
+ Cartella Cestino
+ Cartella Archivio
+ Cartella Spam
+ Mostra solo le cartelle sottoscritte
+ Auto-espandi cartella
+ Percorso OWA
+ Percorso di autenticazione
+ Alias della casella postale
+ Impostazioni server della posta in uscita
+ Server SMTP
+ Porta
+ Protezione
+ Richiedi accesso.
+ Nome utente
+ Password
+ Autenticazione
+ \"%1$s = %2$s\" non è valido con \"%3$s = %4$s\"
+ Configurazione non valida: %s
+ Opzioni account
+ Frequenza verifica cartella
+ Mai
+ Ogni 15 minuti
+ Ogni 30 minuti
+ Ogni ora
+ Ogni 2 ore
+ Ogni 3 ore
+ Ogni 6 ore
+ Ogni 12 ore
+ Ogni 24 ore
+ Aggiorna connessione inattiva
+ Ogni 2 minuti
+ Ogni 3 minuti
+ Ogni 6 minuti
+ Ogni 12 minuti
+ Ogni 24 minuti
+ Ogni 36 minuti
+ Ogni 48 minuti
+ Ogni 60 minuti
+ Notifica l\'arrivo di nuovi messaggi
+ Numero di messaggi da visualizzare
+ 10 messaggi
+ 25 messaggi
+ 50 messaggi
+ 100 messaggi
+ 250 messaggi
+ 500 messaggi
+ 1000 messaggi
+ 2500 messaggi
+ 5000 messaggi
+ 10000 messaggi
+ tutti i messaggi
+ Impossibile copiare o spostare un messaggio che non sia sincronizzato con il server
+ Impossibile completare la configurazione
+ Nome utente o password errata.\n(%s)
+ Il server ha fornito un certificato SSL non valido. A volte ciò è dovuto a problemi di configurazione del server. In altri casi potrebbe dipendere da un tentativo di attacco a te o al tuo server di posta. Se non sei sicuro delle cause, fai clic su Rifiuta e contatta gli amministratori del tuo server di posta.\n\n(%s)
+ Impossibile connettersi al server.\n(%s)
+ Autorizzazione cancellata
+ Autorizzazione fallita con il seguente errore %s
+ OAuth 2.0 non è attualmente supportato da questo provider di posta.
+ L\' applicazione non può trovare il browser da usare per richiedere l\' accesso al tuo account.
+ Modifica i dettagli
+ Continua
+ Avanzate
+ Impostazioni account
+ Notifiche nuovi messaggi
+ Notifiche delle cartelle
+ Tutte
+ Soltanto cartelle di 1a classe
+ Cartelle di 1a e 2a classe
+ Tutte eccetto le cartelle di 2a classe
+ Nessuna
+ Sincronizza notifiche
+ Il tuo indirizzo email
+ Notifica nella barra di stato l\'arrivo di un nuovo messaggio
+ Notifica nella barra di stato mentre la posta viene controllata
+ Includi la posta in uscita
+ Mostra una notifica per i messaggi che ho inviato
+ Solo contatti
+ Mostra notifiche solo per i messaggi da contatti conosciuti
+ Ignora i messaggi di chat
+ Non visualizzare notifiche per i messaggi appartenenti ad una chat
+ Marca come letto all\'apertura
+ Marca un messaggio come letto quando viene aperto per la visualizzazione
+ Marca come letto all\'eliminazione
+ Marca un messaggio come letto quando viene eliminato
+ Categorie di notifiche
+ Configura le notifiche per i nuovi messaggi
+ Configura le notifiche per errori e stato
+ Mostra sempre le immagini
+ No
+ Dai contatti
+ Da chiunque
+ Invio messaggio
+ Cita messaggio originale nella risposta
+ Includi il messaggio originale nella risposta.
+ Risposta dopo testo citato
+ Il messaggio originale sarà visualizzato sopra la tua risposta.
+ Rimuovi firma nella risposta
+ Le firme saranno rimosse dai messaggi citati
+ Formato messaggio
+ Testo semplice (saranno rimosse immagini e formattazione)
+ HTML (preserva immagini e formattazione)
+ Automatico
+ Mostra sempre CC/Ccn
+ Notifica di lettura
+ Richiedi sempre la notifica di lettura
+ Aspetto testo citato nelle risposte
+ Prefisso (come Gmail)
+ Intestazione (come Outlook)
+ Carica i messaggi inviati
+ Carica i messaggi nella cartella Posta inviata dopo l\'invio
+ Impostazioni generali
+ Lettura dei messaggi
+ Recupero dei messaggi
+ Cartelle
+ Prefisso testo citato
+ Cifratura end-to-end
+ Abilita supporto OpenPGP
+ Seleziona applicazione OpenPGP
+ Configura chiave end-to-end
+ Nessuna applicazione OpenPGP configurata
+ Connesso a %s
+ Configurazione in corso…
+ Memorizza tutte le bozze con cifratura
+ Tutte le bozze saranno memorizzate con cifratura
+ Cifra le bozze solo se la cifratura è abilitata
+ Frequenza di recupero della cartella
+ Colore dell\'account
+ Scegli il colore dell\'account utilizzato nell\'elenco delle cartelle e degli account
+ Dimensione cartelle locali
+ Scarica messaggi di dimensione fino a
+ 1 KiB
+ 2 KiB
+ 4 KiB
+ 8 KiB
+ 16 KiB
+ 32 KiB
+ 64 KiB
+ 128 KiB
+ 256 KiB
+ 512 KiB
+ 1 MiB
+ 2 MiB
+ 5 MiB
+ 10 MiB
+ qualsiasi dimensione (nessun limite)
+ Sincronizza messaggi da
+ qualsiasi momento (senza limite)
+ oggi
+ gli ultimi 2 giorni
+ gli ultimi 3 giorni
+ l\'ultima settimana
+ le ultime 2 settimane
+ le ultime 3 settimane
+ l\'ultimo mese
+ gli ultimi 2 mesi
+ gli ultimi 3 mesi
+ gli ultimi 6 mesi
+ l\'ultimo anno
+ Cartelle da visualizzare
+ Tutte
+ Solo cartelle di 1a classe
+ Cartelle di 1a e 2a classe
+ Tutte eccetto le cartelle di 2a classe
+ Cartelle da sincronizzare
+ Tutte
+ Solo cartelle di 1a classe
+ Cartelle di 1a e 2a classe
+ Tutte eccetto le cartelle di 2a classe
+ Nessuna
+ Cartelle push
+ Tutte
+ Solo cartelle di 1a classe
+ Cartelle di 1a e 2a classe
+ Tutte eccetto le cartelle di 2a classe
+ Nessuna
+ Cartelle di destinazione per spostamento/copia
+ Tutte
+ Solo cartelle di 1a classe
+ Cartelle di 1a e 2a classe
+ Tutte eccetto le cartelle di 2a classe
+ Sincronizza eliminazioni sul server
+ Rimuovi i messaggi una volta eliminati sul server
+ Applicazione OpenPGP mancante - è stata disinstallata?
+ Impostazioni cartella
+ Mostra nel gruppo superiore
+ Mostra nella parte superiore dell\'elenco delle cartelle
+ Classe di visualizzazione delle cartelle
+ Nessuna classe
+ 1a classe
+ 2a classe
+ Classe di recupero della cartella
+ Nessuna
+ 1a classe
+ 2a classe
+ Uguale alla classe di visualizzazione
+ Classe delle cartelle push
+ Nessuna classe
+ 1a classe
+ 2a classe
+ Uguale alla classe di poll
+ Classe di notifica delle cartelle
+ Nessuna classe
+ 1a classe
+ 2a classe
+ Uguale alla classe di push
+ Cancella messaggi locali
+ Server della posta in arrivo
+ Configura il server della posta in arrivo
+ Server della posta in uscita
+ Configura il server della posta in uscita (SMTP)
+ Nome account
+ Il tuo nome
+ Notifiche
+ Vibrazione
+ Vibrazione
+ Tipo di vibrazione
+ Predefinito
+ Tipo 1
+ Tipo 2
+ Tipo 3
+ Tipo 4
+ Tipo 5
+ Ripeti vibrazione
+ Disabilitato
+ Suoneria nuovo messaggio
+ Luce di notifica
+ Disabilitato
+ Colore dell\'account
+ Colori di sistema
+ Bianco
+ Rosso
+ Verde
+ Blù
+ Giallo
+ Ciano
+ Magenta
+ Opzioni di composizione messaggi
+ Opzioni predefinite di composizione
+ Imposta Da, Ccn e firma predefiniti
+ Gestione identità
+ Imposta indirizzo mittente e firma alternativi
+ Gestione identità
+ Gestione identità
+ Modifica identità
+ Salva
+ Nuova identità
+ Invia in Ccn tutti i messaggi a
+ Modifica
+ Sposta su
+ Sposta giù
+ Sposta in cima / rendi predefinito
+ Rimuovi
+ Descrizione identità
+ (Opzionale)
+ Il tuo nome
+ (Opzionale)
+ Indirizzo email
+ (Richiesto)
+ Indirizzo per le risposte
+ (Opzionale)
+ Firma
+ (Opzionale)
+ Usa la firma
+ Firma
+ Identità iniziale
+ Scegli identità
+ Invia come
+ Non puoi rimuovere l\'unica identità
+ Non puoi utilizzare un\'identità senza un indirizzo email
+ Data messaggio (più vecchi)
+ Data messaggio (più recenti)
+ Oggetto in ordine alfabetico
+ Oggetto in ordine alfabetico inverso
+ Mittente in ordine alfabetico
+ Mittente in ordine alfabetico inverso
+ Prima i messaggi con stella
+ Prima i messaggi senza stella
+ Prima i messaggi non letti
+ Prima i messaggi letti
+ Prima i messaggi con allegati
+ Prima i messaggi senza allegati
+ Ordina per…
+ Data
+ Arrivo
+ Oggetto
+ Mittente
+ Stella
+ Letto/non letto
+ Allegati
+ Rimuovi account
+ Certificato non riconosciuto
+ Accetta chiave
+ Rifiuta chiave
+ Canc (o D) - Elimina\nR - Rispondi\nA - Rispondi a tutti\nC - Componi\nF - Inoltra\nM - Sposta\nV - Archivia\nY - Copia\nZ - Marca come (non) letto\nG - Stella\nO - Tipo di ordinamento\nI - Ordinamento\nQ - Ritorna alle cartelle\nS - Seleziona/deseleziona\nJ o P - Messaggio precedente\nK o N - Messaggio successivo
+ Canc (o D) - Elimina\nC - Componi\nM - Sposta\nV - Archivia\nY - Copia\nZ - Marca come (non) letto\nG - Stella\nO - Tipo di ordinamento\nI - Ordinamento\nQ - Ritorna alle cartelle\nS - Seleziona/deseleziona
+ Il nome della cartella contiene
+ Mostra cartelle…
+ Tutte le cartelle
+ Cartelle di 1a classe
+ Cartelle di 1a e 2a classe
+ Nascondi le cartelle di 2a classe
+ Posizione della firma
+ Prima del testo citato
+ Dopo il testo citato
+ Usa il tema dell\'applicazione
+ Scuro
+ Chiaro
+ Utilizza predefinito di sistema
+ Visualizzazione
+ Globali
+ Debug
+ Riservatezza
+ Rete
+ Interazione
+ Elenco account
+ Elenchi dei messaggi
+ Messaggi
+ Tema
+ Tema della vista dei messaggi
+ Tema del compositore
+ Lingua
+ Nessuna impostazionie trovata
+ Tema fisso del messaggio
+ Seleziona il tema della vista dei messaggi durante la visualizzazione del messaggio
+ Usa un tema fisso nella vista dei messaggi
+ Predefinita di sistema
+ Sincronizzazione in background
+ Mai
+ Sempre
+ Quando \'Sincronizzazione automatica\' è selezionato
+ Seleziona tutto
+ Limite di cartelle da sincronizzare in push
+ 5 cartelle
+ 10 cartelle
+ 25 cartelle
+ 50 cartelle
+ 100 cartelle
+ 250 cartelle
+ 500 cartelle
+ 1000 cartelle
+ Animazione
+ Utilizza effetti visivi vistosi
+ Navigazione con tasti volume
+ In visualizzazione messaggi
+ In visualizzazione elenco
+ Mosta posta in arrivo combinata
+ Mostra il numero dei messaggi con stella
+ Posta in arrivo combinata
+ Tutti i messaggi nella Posta in arrivo combinata
+ Combina
+ Tutti i messaggi sono visualizzati nella Posta in arrivo combinata
+ Cartelle da analizzare
+ Tutte
+ Visibili
+ Nessuna
+ Nessuna
+ Automatica (%s)
+ Dimensione carattere
+ Configura la dimensione dei caratteri
+ Elenco account
+ Nome account
+ Descrizione account
+ Elenchi delle cartelle
+ Nome cartella
+ Stato cartella
+ Elenchi dei messaggi
+ Oggetto
+ Mittente
+ Data
+ Anteprima
+ Messaggi
+ Mittente
+ A
+ Cc
+ Ccn
+ Intestazioni aggiuntive
+ Oggetto
+ Ora e data
+ Corpo del messaggio
+ Composizione messaggio
+ Campi di testo
+ Predefinito
+ Piccolissimo
+ Molto piccolo
+ Più piccolo
+ Piccolo
+ Medio
+ Grande
+ Più grande
+ Nessuna applicazione trovata per questa azione.
+ Invio non riuscito: %s
+ Vuoi salvare la bozza?
+ Vuoi salvare o scartare questo messaggio?
+ Vuoi salvare o scartare le modifiche?
+ Vuoi eliminare il messaggio?
+ Sei sicuro di voler eliminare questo messaggio?
+ Seleziona il testo da copiare.
+ Vuoi cancellare i messaggi locali?
+ Questo rimuoverà tutti i messaggi locali dalla cartella. Nessun messaggio sarà eliminato dal server.
+ Cancella messaggi
+ Conferma l\'eliminazione
+ Vuoi eliminare questo messaggio?
+
+ Vuoi davvero eliminare questo messaggio?
+ Vuoi davvero eliminare i %1$d messaggi selezionati?
+ Vuoi davvero eliminare i %1$d messaggi selezionati?
+
+ Sì
+ No
+ Conferma di marcare tutti come letti
+ Vuoi marcare tutti i messaggi come letti?
+ Conferma lo svuotamento del cestino
+ Vuoi svuotare la cartella del cestino?
+ Sì
+ No
+ Conferma spostamento nella cartella spam
+
+ Vuoi davvero spostare il messaggio nella cartella spam?
+ Vuoi davvero spostare i %1$d messaggi selezionati nella cartella spam?
+ Vuoi davvero spostare i %1$d messaggi selezionati nella cartella spam?
+
+ Sì
+ No
+ Scaricamento allegato
+ »
+ ›
+ Copia di sicurezza
+ Varie
+ Esporta impostazioni
+ Esporta
+ Condividi
+ Esportazione impostazioni…
+ Impostazioni esportate correttamente
+ Esportazione impostazioni non riuscita
+ Importa impostazioni
+ Seleziona file
+ Importa
+ Impostazioni importate correttamente
+
+ Digita le password
+
+
+ Importazione impostazioni non riuscita
+ Lettura file delle impostazioni non riuscita
+ Importazione di alcune impostazioni non riuscita
+ Importate correttamente
+ Password richiesta
+
+ Non importate
+ Importazione non riuscita
+ Più tardi
+ Importa impostazioni
+ Importazione impostazioni…
+
+ Per poter utilizzare l\'account \"%s\" devi fornire le password del server.
+ Per poter utilizzare l\'account \"%s\" devi fornire le password del server.
+ Per poter utilizzare l\'account \"%s\" devi fornire le password del server.
+
+ Password del server di ingresso
+ Password del server di uscita
+ Usa la stessa password per il server di uscita
+ Nome server: %s
+ Mostra numero messaggi non letti per…
+ Account
+ L\'account per il quale dovrebbe essere visualizzato il contatore non letti
+ Posta in arrivo combinata
+ Conteggio cartella
+ Visualizza il contatore non letti solo di una singola cartella
+ Cartella
+ La cartella per la quale dovrebbe essere visualizzato il contatore non letti
+ Fatto
+ %1$s - %2$s
+ Nessun account selezionato
+ Nessuna cartella selezionata
+ Senza testo
+ Apri collegamento
+ Condividi collegamento
+ Copia collegamento negli appunti
+ Collegamento
+ Copia testo del collegamento negli appunti
+ Testo del collegamento
+ Immagine
+ Visualizza immagine
+ Salva immagine
+ Scarica immagine
+ Copia URL immagine negli appunti
+ URL immagine
+ Chiama numero
+ Salva nei contatti
+ Copia numero negli appunti
+ Numero di telefono
+ Invia messaggio
+ Salva nei contatti
+ Copia indirizzo negli appunti
+ Indirizzo email
+ Tutti
+ 10
+ 25
+ 50
+ 100
+ 250
+ 500
+ 1000
+ Limite risultati di ricerca sul server
+ Invio della richiesta al server
+
+ Recupero di %d risultato
+ Recupero di %d risultati
+ Recupero di %d risultati
+
+
+ Recupero di %1$d di %2$d risultati
+ Recupero di %1$d di %2$d risultati
+ Recupero di %1$d di %2$d risultati
+
+ Ricerca remota non riuscita
+ Cerca
+ Cerca messaggi sul server
+ Le ricerche sul server richiedono una connessione alla rete.
+ Cambia colore dopo la lettura
+ Uno sfondo diverso mostrerà che il messaggio è stato letto
+ Vista di conversazione
+ Raggruppa i messaggi per conversazione
+ Aggiornamento dei database
+ Aggiornamento dei database…
+ Aggiornamento del database dell\'account \"%s\"
+ Mostra schermo diviso
+ Sempre
+ Mai
+ Con orientazione orizzontale
+ Seleziona un messaggio a sinistra
+ Mostra immagini dei contatti
+ Mostra le immagini dei contatti nell\'elenco dei messaggi
+ Marca tutti come letti
+ Colora le immagini dei contatti
+ Colora le immagini mancanti dei contatti
+ Azioni del messaggio visibile
+ Mostra le azioni selezionate nel menu di visualizzazione messaggio
+ Caricamento allegati…
+ Invio messaggio
+ Salvataggio bozza
+ Recupero allegati…
+ Impossibile autenticare. Il server non offre la funzionalità SASL EXTERNAL. Ciò potrebbe essere causato da un problema con il certificato client (scaduto, autorità di certificazione sconosciuta) o da un altro problema di configurazione.
+
+ Usa certificato client
+ Nessun certificato client
+ Rimuovi selezione del certificato client
+ Recupero del certificato client per l\'alias \"%s\" non riuscito
+ Opzioni avanzate
+ Il certificato client \"%1$s\" è scaduto o non è ancora valido (%2$s)
+
+ *Cifrato*
+ Aggiungi da Contatti
+ Cc
+ Ccn
+ A
+ Da
+ Rispondi a
+ <Destinatario sconosciuto>
+ <Mittente sconosciuto>
+ Casa
+ Lavoro
+ Altro
+ Cellulare
+ Non è stata configurata una cartella Bozze per questo account!
+ Nessuna chiave configurata per questo account! Controlla le impostazioni.
+ Il fornitore della cifratura utilizza una versione incompatibile. Controlla le tue impostazioni!
+ Impossibile connettersi al fornitore della crittografia, controlla le impostazioni oppure fai clic sull\'icona di cifratura per riprovare!
+ Inizializzazione della cifratura end-to-end non riuscita, controlla le tue impostazioni
+ La modalità PGP/INLINE non supporta gli allegati!
+ Abilita PGP/INLINE
+ Disabilita PGP/INLINE
+ Abilita PGP solo firma
+ Disabilita PGP solo firma
+ Modalità PGP/INLINE
+ Questo messaggio è inviato nel formato PGP/INLINE.\nQuesto formato dovrebbe essere utilizzato solo per compatibilità:
+ Alcuni client supportano solo questo formato
+ Le firme potrebbero danneggiarsi durante il transito
+ Gli allegati non sono supportati
+ Ricevuto!
+ Disabilita
+ Lascia abilitato
+ Ricevuto!
+ Disabilita
+ Lascia abilitato
+ Modailità PGP solo firma
+ In questa modalità, la tua chiave PGP sarà utilizzata per creare una firma di cifratura di un messaggio di posta non cifrato.
+ Ciò non cifra il messaggio di posta, ma verifica che sia stato inviato dalla tua chiave.
+ Le firme potrebbero danneggiarsi in caso di invio a liste di distribuzione.
+ Le firme possono essere visualizzate come allegati \'signature.asc\' in alcuni client.
+ I messaggi cifrati includono sempre una firma.
+ Testo semplice
+ la firma end-to-end conteneva un errore
+ devi scaricare completamente il messaggio per verificare la firma
+ contiene una firma end-to-end non supportata
+ Il messaggio è cifrato, ma in un formato non supportato.
+ Il messaggio è cifrato, ma la decifratura è stata annullata.
+ Testo semplice firmato End-to-End
+ da firmatario verificato
+ Testo semplice firmato
+ ma la chiave end-to-end non corrisponde al mittente
+ ma la chiave end-to-end è scaduta
+ ma la chiave end-to-end è revocata
+ ma la chiave end-to-end non è considerata sicura
+ da una chiave end-to-end sconosciuta
+ Cifrato
+ ma si è verificato un errore di decifratura
+ devi scaricare completamente per la decifratura
+ ma nessuna applicazione di cifratura è configurata
+ Cifrato
+ ma non end-to-end
+ Cifrato end-to-end
+ da mittente verificato
+ Cifrato
+ da una chiave end-to-end sconosciuta
+ ma la chiave end-to-end non corrisponde al mittente
+ ma la chiave end-to-end è scaduta
+ ma la chiave end-to-end è revocata
+ ma la chiave end-to-end non è considerata sicura
+ ma i dati end-to-end presentano errori
+ ma la cifratura non è considerata sicura
+ OK
+ Chiave di ricerca
+ Visualizza firmatario
+ Visualizza mittente
+ Dettagli
+ Sblocca
+ Questa parte non è stata cifrata, e potrebbe non essere sicura.
+ Allegato non protetto
+ Caricamento in corso…
+ La decifratura è stata annullata.
+ Riprova
+ Il messaggio cifrato deve essere scaricato per la decifratura.
+ Errore durante la decifratura del messaggio
+ I caratteri speciali al momento non sono supportati!
+ Errore durante l\'analisi dell\'indirizzo!
+ Nascondi le firme non cifrate
+ Saranno visualizzate solo le firme cifrate
+ Tutte le firme saranno visualizzate
+ Cifratura non disponibile in modalità solo firma!
+ Testo non firmato
+ Questo messaggio è cifrato
+ Questo messaggio è stato cifrato con OpenPGP.\nPer leggerlo, devi installare e configurare un\'applicazione compatibile con OpenPGP.
+ Vai alle impostazioni
+ Elenco messaggi di Mail
+ Caricamento messaggi…
+ Cifratura non possibile
+ Alcuni dei destinatari selezionati non supportano questa funzionalità!
+ Abilita cifratura
+ Disabilita cifratura
+ La cifratura dei messaggi assicura che possano essere letti dal destinatario e da nessun altro.
+ La cifratura sarà mostrata solo se supportata da tutti i destinatari, e devono averti inviato un\'email in precendenza.
+ Commuta la cifratura facendo clic su questa icona.
+ Ricevuto
+ Indietro
+ Disabilita cifratura
+ Cifratura OpenPGP
+ Modalità di cifratura reciproca automatica
+ Modalità di cifratura reciproca automatica
+ I messaggi saranno normalmente cifrati per scelta o quando si risponde a un messaggio cifrato.
+ Se il mittente e i destinatari abilitano la modalità reciproca, la cifratura sarà abilitata in modo predefinito.
+ Puoi fare clic qui per ulteriori informazioni.
+ Impostazioni generali
+ Nessuna applicazione OpenPGP installata
+ Installa
+ Mail richiede OpenKeychain la cifratura end-to-end.
+ Messaggio cifrato
+ Cifra l\'oggetto dei messaggi
+ Potrebbe non essere supportato da alcuni destinatari
+ Errore interno: account non valido!
+ Errore durante la connessione a %s!
+ Invio messaggio di configurazione di Autocrypt
+ Condividi in modo sicuro la configurazione end-to-end con altri dispositivi
+ Messaggio di configurazione di Autocrypt
+ Un messaggio di configurazione di Autocrypt condivide in modo sicuro la tua configurazione end-to-end con altri dispositivi.
+ Invio messaggio di configurazione
+ Il messaggio sarà inviato al tuo indirizzo:
+ Generazione del messaggio di configurazione…
+ Invio del messaggio a:
+ Per terminare, apri il messaggio sull\'altro dispositivo e digita il codice di configurazione.
+ Mostra codice di configurazione
+ Messaggio di configurazione di Autocrypt
+ Questo messaggio contiene tutte le informazioni per trasferire in modo sicuro le tue impostazioni di Autocrypt insieme alla tua chiave segreta dal tuo dispositivo originale.
Per configurare il tuo nuovo dispositivo per Autocrypt, segui le istruzioni che dovrebbero essere presentate dal nuovo dispositivo.
@@ -985,7 +1033,7 @@ Puoi conservare questo messaggio e utilizza come una copia di sicurezza della tu
Inattivo fino a quando la rete non sarà disponibileTocca per saperne di più.Push Info
- Quando si utilizza Push, K-9 Mail mantiene una connessione al server di posta. Android richiede la visualizzazione di una notifica in corso mentre l\'applicazione è attiva in background. %s
+ Quando si utilizza Push, Mail mantiene una connessione al server di posta. Android richiede la visualizzazione di una notifica in corso mentre l\'applicazione è attiva in background. %sTuttavia, Android ti consente anche di nascondere la notifica.Per saperne di piùConfigura notifica
diff --git a/app/ui/legacy/src/main/res/values-iw/strings.xml b/app/ui/legacy/src/main/res/values-iw/strings.xml
index 0a212ff4f9f3267c0b153cdaa132df46441a814a..0f80fa014e61a2cbdaca84f6b3bf855073abc950 100644
--- a/app/ui/legacy/src/main/res/values-iw/strings.xml
+++ b/app/ui/legacy/src/main/res/values-iw/strings.xml
@@ -223,6 +223,8 @@
הגדר חשבון חדשכתובת דוא\"לסיסמא
+
+
אמת את זהותךהוראות הגדרה
@@ -256,10 +258,6 @@
אל תמחוק בשרתמחק מהשרתסמן כנקרא בשרת
- השתמש בדחיסה ברשת:
- סלולרי
- Wi-Fi
- אחרלמחוק הודעות שנמחקומידWhen polling
@@ -572,6 +570,10 @@
יבא הגדרותבחר קובץיבא
+
+
+
+
מאוחר יותריבא הגדרותמייבא הגדרות…
diff --git a/app/ui/legacy/src/main/res/values-ja/strings.xml b/app/ui/legacy/src/main/res/values-ja/strings.xml
index 7c797e3544ee582222787c061292588a234b661f..972dd9c1c12af300f931154cd5270f7fc0297477 100644
--- a/app/ui/legacy/src/main/res/values-ja/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ja/strings.xml
@@ -7,21 +7,21 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
+ The Mail Dog WalkersソースコードApache License, Version 2.0オープンソースプロジェクト
- Webサイト
+ ウェブサイトユーザーフォーラムFediverseTwitterライブラリーライセンス変更履歴
- 変更履歴を読み込みできませんでした。
+ 変更履歴を読み込めませんでした。バージョン %s新着
- アプリの更新後に更新内容を表示する
+ アプリの更新後に更新内容を表示このリリースは何が新しいのかを知るWelcome to Mail
@@ -91,6 +91,7 @@
スターを付けるスターをはずすコピー
+ 購読解除ヘッダを表示アドレスをクリップボードにコピーしました
@@ -249,6 +250,11 @@
電子メールセットアップメールアドレスパスワード
+ このメールアカウントを Mail で使用するには、サインインしてアプリにメールへのアクセスを許可する必要があります。
+
+ サインイン
+
+ Google でサインインここでパスワードを表示するには、このデバイスの画面ロックを有効にしてください。あなたの本人確認
@@ -272,6 +278,7 @@
パスワード (危険な方法で送信される)暗号化されたパスワードクライアント証明書
+ OAuth 2.0受信メールサーバ設定アカウント名パスワード
@@ -290,10 +297,7 @@
サーバでは削除しないサーバでも削除するサーバでは既読にする
- ネットワーク経由で圧縮:
- モバイル
- WiFi
- その他
+ 圧縮を使用するサーバから削除したメッセージを消す削除または移動後即時同期のタイミングで
@@ -357,7 +361,11 @@
設定エラーサーバー名またはパスワードが不正です\n(%s)サーバが無効なSSL証明書を発行しました。これはサーバの設定ミスが原因で起こることがあります。あるいは誰かがあなたやあなたのメールサーバに攻撃を試みることによって起こることもあります。何が起こっているのかがよくわからないのであれば、「拒否」をクリックし、メールサーバを管理している人たちに連絡をとってください。\n\n(%s)
- サーバーに接続できません\n(%s)
+ サーバに接続できません\n(%s)
+ 認証がキャンセルされました
+ エラーが発生したため認証に失敗しました: %s
+ 現在、このプロバイダーでは OAuth 2.0 がサポートされていません。
+ あなたのアカウントへのアクセスを許可するためのブラウザーが見つかりませんでした。詳細再編集続行プッシュ接続の拡張設定
@@ -414,10 +422,10 @@
メール受信フォルダ引用記号
- エンドトゥエンド暗号化
+ エンドツーエンド暗号化OpenPGPサポートを有効にするOpenPGPアプリを選択
- エンドトゥエンドの鍵を設定
+ エンドツーエンドの鍵を設定OpenPGPアプリが設定されていません%s に接続しました設定しています…
@@ -489,7 +497,7 @@
クラスなし1st クラス2nd クラス
- フォルダ受信クラス
+ フォルダ同期クラスなし1st クラス2nd クラス
@@ -734,12 +742,19 @@
ファイルを選択インポート設定を正常にインポートしました
+
パスワードを入力してください
+
+ サインインしてください
+
+ サインインしてパスワードを入力してください設定のインポートに失敗しました設定ファイルの読み込みに失敗しました一部の設定のインポートに失敗しましたインポートに成功しましたパスワードが必要です
+
+ サインインが必要ですインポートされていませんインポート失敗後で
@@ -828,14 +843,14 @@
メッセージ送信下書き保存添付ファイル取得中…
- 認証できません。サーバーはSASL EXTERNALが利用できることを示していません。これはクライアント証明書の問題(期限切れ、未知の認証局)又は何らかの設定の為です。
+ 認証できません。サーバはSASL EXTERNALが利用できることを示していません。これはクライアント証明書の問題(期限切れ、未知の認証局)又は何らかの設定の為です。
- クライアント証明書を使う
- クライアント証明書なし
- クライアント証明書を削除します
- 別名 \"%s\" の為のクライアント証明書の取得に失敗しました
+ クライアント証明書を使う
+ クライアント証明書なし
+ クライアント証明書を削除します
+ 別名 \"%s\" のためのクライアント証明書の取得に失敗しました拡張設定
- クライアント証明書 \"%1$s\" が、期限切れまたは不正です (%2$s)
+ クライアント証明書 \"%1$s\" が、期限切れまたは不正です (%2$s)*暗号化*連絡先から追加
@@ -854,7 +869,7 @@
このアカウントの鍵が設定されていません! 設定を確認してください。暗号化プロバイダが互換性のないバージョンを使用しています。 設定を確認してください!暗号化プロバイダに接続できません。設定を確認するか、暗号化アイコンをクリックして再実行してください!
- エンドトゥエンド暗号化の初期化に失敗しました。設定を確認してください
+ エンドツーエンド暗号化の初期化に失敗しました。設定を確認してくださいPGP/INLINE モードは添付ファイルをサポートしていません!PGP/INLINE を有効にするPGP/INLINE を無効にする
@@ -878,34 +893,34 @@
一部のクライアントで、署名は \'signature.asc\' 添付ファイルとして表示されることがあります。暗号化されたメッセージには常に署名が含まれます。平文
- エンドトゥエンド署名にエラーが含まれていました
+ エンドツーエンド署名にエラーが含まれていました署名を処理するために、完全にダウンロードする必要があります
- サポートされていないエンドトゥエンド署名が含まれています
+ サポートされていないエンドツーエンド署名が含まれていますメッセージは暗号化されていますが、サポートされていない形式です。メッセージは暗号化されていますが、復号化をキャンセルされました。
- エンドトゥエンド署名された平文
+ エンドツーエンド署名された平文検証済の署名者から署名された平文
- エンドトゥエンドの鍵が送信者と一致しません
- エンドトゥエンドの鍵が有効期限切れです
- エンドトゥエンドの鍵が失効されました
- エンドトゥエンドの鍵が安全ではないようです
- 不明なエンドトゥエンドの鍵から
+ エンドツーエンドの鍵が送信者と一致しません
+ エンドツーエンドの鍵が有効期限切れです
+ エンドツーエンドの鍵が失効しました
+ エンドツーエンドの鍵が安全ではないようです
+ 未知のエンドツーエンドの鍵から暗号化復号エラーがありました復号化するために、完全にダウンロードする必要があります暗号化アプリが設定されていません暗号化
- エンドトゥエンドではありません
- エンドトゥエンドではありません
+ エンドツーエンドではありません
+ エンドツーエンド暗号化検証済の署名者から暗号化
- 不明なエンドトゥエンドの鍵から
- エンドトゥエンドの鍵が送信者と一致しません
- エンドトゥエンドの鍵が有効期限切れです
- エンドトゥエンドの鍵が失効されました
- エンドトゥエンドの鍵が安全ではないようです
- エンドトゥエンドのデータがエラーです
+ 未知のエンドツーエンドの鍵から
+ エンドツーエンドの鍵が送信者と一致しません
+ エンドツーエンドの鍵が有効期限切れです
+ エンドツーエンドの鍵が失効しました
+ エンドツーエンドの鍵が安全ではないようです
+ エンドツーエンドのデータにエラーがあります暗号が安全ではないようですOK検索キー
@@ -927,14 +942,6 @@
すべての署名が表示されます署名専用モードでは暗号化を使用できません!未署名のテキスト
- APG廃止の警告
- APGはもはやメンテナンスされていません!
- このため、APG のサポートはK-9 Mail から削除されました。
- 開発は2014年初頭に停止しました
- 未修正のセキュリティ問題が含まれています
- 詳細については ここをクリック してください。
- 了解!
- APGこのメールは暗号化されていますこのメールは OpenPGP で暗号化されました。\n読むためには、OpenPGP と互換のアプリをインストールして設定する必要があります。設定に移動
@@ -951,8 +958,8 @@
戻る暗号化を無効にするOpenPGP 暗号化
- 自動暗号化相互モード
- 自動暗号化相互モード
+ Autocrypt Mutual モード
+ Autocrypt Mutual モード通常、メッセージは選択して、または暗号化されたメッセージに返信するときに暗号化されます。送信者と受信者の両方が相互モードを有効にすると、デフォルトで暗号化が有効になります。詳細については ここをクリック してください。
@@ -966,9 +973,9 @@
内部エラー: アカウントが無効です!%s に接続中にエラーが発生しました!Autocryptセットアップメッセージを送信
- 他のデバイスとエンドツーエンドに安全な共有をセットアップ
+ エンドツーエンドのセットアップを他のデバイスと安全に共有Autocryptセットアップメッセージ
- 他のデバイスとエンドツーエンドに安全な共有を行うAutocryptセットアップメッセージ
+ エンドツーエンドのセットアップを他のデバイスと安全に共有する Autocrypt セットアップメッセージセットアップメッセージを送信メッセージをあなたのアドレスに送信します:セットアップメッセージの生成中…
diff --git a/app/ui/legacy/src/main/res/values-ko/strings.xml b/app/ui/legacy/src/main/res/values-ko/strings.xml
index ad18e199996f27603b3f87c5ea0b9d491600e29e..35e1c1313fad0db1056c50586d6fd7582b40d32b 100644
--- a/app/ui/legacy/src/main/res/values-ko/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ko/strings.xml
@@ -199,6 +199,8 @@
계정 추가이메일 주소비밀번호
+
+
수동 설정
@@ -236,10 +238,6 @@
서버에는 메일을 삭제하지 않음서버에도 메일 삭제서버에 읽은 메일로 표시
- 네트워크 상에서 압축 사용:
- 모바일 네트워크
- Wi-Fi
- 기타삭제된 메시지를 제거즉시수신 시
@@ -604,6 +602,10 @@
설정 내보내기 실패설정 가져오기가져오기
+
+
+
+
설정 가져오기설정 가져오는 중…읽지 않은 메일 수 보이기
@@ -736,8 +738,6 @@
암호화된지 않은 서명을 숨기기모든 서명이 보일 것입니다서명되지 않은 글
- 알았습니다!
- APG설정으로 움직이기암호화가 불가능합니다선택된 몇몇 받는 사람들은 이 기능을 지원하지 않습니다!
diff --git a/app/ui/legacy/src/main/res/values-lt/strings.xml b/app/ui/legacy/src/main/res/values-lt/strings.xml
index 0ffc1c76bd8a4cdbfd8425025d865dd7f168a58c..ceabf2a813381c1ce0001379fe80b9b53701a17a 100644
--- a/app/ui/legacy/src/main/res/values-lt/strings.xml
+++ b/app/ui/legacy/src/main/res/values-lt/strings.xml
@@ -7,19 +7,42 @@
Mail AccountsMail Unread
+ Mail šunų vedžiotojai
+ Šaltinio kodas
+ Apache licencija, versija 2.0
+ Atviro kodo projektas
+ Svetainė
+ Naudotojų forumas
+ Fediverse
+ Twitter
+ Bibliotekos
+ Licencija
+ Pakeitimų sąrašas
+ Nepavyko įkelti pakeitimų žurnalo.
+ Versija %s
+ Kas naujo
+ Rodyti naujausius pakeitimus, kai programa buvo atnaujinta
+ Sužinokite, kas naujo šioje versijojeWelcome to Mail
+ - \nIšsiųsta iš mano Android įrenginio naudojant Mail. Atleiskite už trumpumą.The account \"%s\" will be removed from Mail.
+ Autoriai
+ Apie MailPaskyrosIšplėstinisSukurtiAtsakytiAtsakyti visiemsPersiųsti
- Pasirinkite aplanką
+ Persiųsti kaip priedą
+ Pasirinkti Paskyrą
+ Pasirinkti aplanką
+ Perkelti į...
+ Kopijuoti į...%d pasirinktaKitasAnkstesnis
@@ -27,15 +50,19 @@
GeraiAtsisakytiSiųsti
+ Tema yra tuščia, spustelėkite dar kartą, kad vis tiek išsiųstumėteAtsakytiAtsakyti visiemsŠalintiArchyvuotiBrukalasPersiųsti
+ Persiųsti kaip priedą
+ Redaguoti kaip naują žinutęPerkelti
+ Perkelti į JuodraščiusSiųsti…
- Refile…
+ Pertvarkyti...AtliktaAtmestiIšsaugoti kaip juodraštį
@@ -46,24 +73,35 @@
Pridėti paskyrąSukurtiIeškoti
+ Ieškoti visurPaieškos rezultatai
+ Naujos žinutėsNustatymai
+ Tvarkyti aplankusPaskyros nustatymaiPašalinti paskyrąPažymėti kaip skaitytą
- Dalintis
+ BendrintiPasirinkti siuntėjąPridėti žvaigždutęPašalinti žvaigždutęKopijuoti
+ Atsisakyti prenumeratosRodyti antraštes
+
+ Adresas nukopijuotas į iškarpinę
+ Adresai nukopijuoti į iškarpinę
+ Adresai nukopijuoti į iškarpinę
+ Adresai nukopijuoti į iškarpinę
+
+ Temos tekstas nukopijuotas į iškarpinęPersijungti į tamsią temąPersijungti į šviesią temąPažymėti kaip neskaitytąSkaitymo patvirtinimasPrašys perskaitymo patvirtinimoNeprašys perskaitymo patvirtinimo
- Priedo priedą
+ Pridėti priedąIšvalyti šiukšliadėžęIšvalytiAbout
@@ -71,36 +109,63 @@
(Nėra temos)Nėra siuntėjo
- Įkeliami laiškai…
+ Įkeliami laiškai\u2026Prisijungimo klaidaLaiškas nerastas
- Įkelti dar iki %d laiškų
+ Laiško įkėlimo klaida
+ Įkelti dar
+ iki %d laiškų
+ %.1f GB
+ %.1f MB
+ %.1f kB
+ %d BNaujas laiškas
+
+ %d naujas laiškas
+ %d nauji laiškai
+ %d nauji laiškai
+ %d nauji laiškai
+ %d Neskaitytų (%s)
+ + %1$d daugiau ant %2$sAtsakytiPažymėti kaip skaitytą
+ Pažymėti viską kaip skaitytąŠalinti
- Archyvas
+ Šalinti Visus
+ Archyvuoti
+ Archyvuoti viskąBrukalas
+ Sertifikato klaidaSertifikato klaida %sPatikrinkite serverio nustatymus
+ Autentifikavimas nepavyko
+ Nepavyko nustatyti autentiškumo %s. Atnaujinkite serverio nustatymus.Tikrinamas paštas: %s:%sTikrinamas paštasSiunčiamas paštas: %sSiunčiamas paštas:
+ Sinchronizuoti (Push)
+ Rodoma laukiant naujų laiškųLaiškai
- Kita
+ Pranešimai susiję su laiškais
+ Įvairūs
+ Įvairūs pranešimai, pavyzdžiui, apie klaidas ir pan.Gauti laiškaiSiunčiamiejiJuodraščiaiŠiukšliadėžėIšsiųstiKai kurių laiškų išsiųsti nepavyko
+ VersijaĮjungti derinimo žurnaląĮrašyti papildomą diagnostinę informacijąĮrašyti jautrią informacijąŽurnaluose gali būti rodomi slaptažodžiai
+ Eksportuoti žurnalus
+ Eksportas sėkmingas. Žurnaluose gali būti konfidencialios informacijos. Būkite atsargūs, kam juos siunčiate.
+ Eksportas nepavyko.Įkelti daugiau laiškųKam:%sTema
@@ -114,18 +179,27 @@
Cc:%s rašė:
+ %1$s, %2$s parašė:Turite pridėti bent vieną gavėją.
+ Gavėjo lauke pateikti neišsamūs įvesties duomenys!Nepavyko rasti šio kontakto el. pašto adreso.Kai kurių priedų negalima persiųsti, nes jie nebuvo parsiųsti.
+ Laiškas negali būti persiųstas, nes kai kurie priedai nebuvo atsisiųsti.
+ Įtraukti cituojamą laiškąPašalinti cituojamą tekstąRedaguoti cituojamą tekstą
+ Pašalinti priedąNuo: %s <%s>Kam:Cc:
+ Bcc:
+ Nepavyko išsaugoti priedo.Rodyti paveikslėliusNepavyko rasto %s peržiūros programos.Parsiųsti visą laišką
+ per %1$sDaugiau iš šio siuntėjo
+ Nuo %sLaiškas pašalintasLaiškas išsaugotas kaip juodraštisRodyti žvaigždutes
@@ -137,8 +211,9 @@
Korespondentų vardus rodyti virš temos eilutės, ne poRodyti kontaktų vardusKai įmanoma naudoti gavėjų vardus iš adresų knygos
- Spalvoti kontaktus
- Adresatų sąraše spalvoti vardus
+ Spalvinti kontaktus
+ Adresatų sąraše spalvinti vardus
+ Kontakto vardo spalvaNekintančio pločio šriftaiAtvaizduojant gryno teksto laiškus naudoti lygiaplotį šriftąAutomatiškai pritaikyti laiškus
@@ -152,27 +227,46 @@
ŠalintiPašalinti pažymėtus žvaigždute (pranešimų rodinyje)Brukalas
+ Išmesti laišką
+ Pažymėti visus laiškus kaip perskaitytusPašalinti
+ Slėpti pašto klientą
+ Pašalinti Mail User-Agent iš pašto antraščių
+ Slėpti laiko juostą
+ Naudoti UTC laiko juostą laiško antraštėse ir atsakymo antraštėje vietoj vietinės laiko juostosRodyti mygtuką „Šalinti“NiekadaVieno laiško pranešimuiVisada
+ Užrakto ekrano pranešimai
+ Nėra užrakto ekrano pranešimų
+ Programos pavadinimas
+ Naujų laiškų skaičius
+ Laiškų skaičius ir siuntėjai
+ Tas pats, kaip ir atrakinus ekranąTylos laikasIšjungti skambėjimą, pyptelėjimą ir mirgėjimą, nakties metu
+ Išjungti pranešimus
+ Visiškas pranešimų išjungimas tylos laikuTylos laikas prasidedaTylos laikas baigiasiNustatyti naują paskyrąEl. pašto adresasSlaptažodis
+
+
+ Norėdami čia peržiūrėti slaptažodį, įjunkite ekrano užraktą šiame prietaise.
+ Patvirtinkite savo tapatybę
+ Atrakinkite norint peržiūrėti savo slaptažodįRankinis nustatymas
- Gaunama paskyros informacija…
- Tikrinami gaunamųjų serverio nustatymai…
- Tikrinami siunčiamųjų serverio nustatymai…
- Patvirtinama tapatybė…
- Gaunami paskyros nustatymai…
- Atšaukiama…
+ Gaunama paskyros informacija\u2026
+ Tikrinami gaunamųjų serverio nustatymai\u2026
+ Tikrinami siunčiamųjų serverio nustatymai\u2026
+ Patvirtinama tapatybė\u2026
+ Gaunami paskyros nustatymai\u2026
+ Atšaukiama\u2026Beveik baigėte!Pavadinkite šią paskyrą (nebūtina):Įrašykite savo vardą (rodoma siunčiamuose pranešimuose):
@@ -180,9 +274,15 @@
Kokios rūšies ši paskyra?POP3IMAP
+ Paprastas slaptažodis
+ Nesaugiai perduotas slaptažodis
+ Šifruotas slaptažodis
+ Kliento sertifikatas
+ OAuth 2.0Gaunamųjų laiškų serverisNaudotojo vardasSlaptažodis
+ Kliento sertifikatasPOP3 serverisIMAP serverisMainų serveris
@@ -190,14 +290,14 @@
SaugumasTapatumo nustatymasJoks
+ SSL/TLS
+ STARTTLS
+ \"%1$s = %2$s\" netinka su \"%3$s = %4$s\"Kai pašalinu laiškąNešalinti serveryjePašalinti iš serverioPažymėti kaip skaitytą serveryje
- Tinkle naudoti suspaudimą:
- Mobilus
- Wi-Fi
- Kita
+ Naudoti suspaudimąPašalinti ištrintus laiškus serveryjeTuojau patKai užklausiama
@@ -222,6 +322,7 @@
Naudotojo vardasSlaptažodisTapatumo nustatymas
+ \"%1$s = %2$s\" netinka su \"%3$s = %4$s\"Netinkamai nustatyta: %sPaskyros parinktysAplanko tikrinimo dažnis
@@ -252,14 +353,25 @@
250 laiškų500 laiškų1000 laiškų
+ 2500 laiškų
+ 5000 laiškų
+ 10000 laiškųvisi laiškaiNeįmanoma kopijuoti ar perkelti laiškų kurie nesinchronizuoti su serveriuNepavyko baigti nustatymo
+ Naudotojo vardas arba slaptažodis neteisingi.\n(%s)
+ Serveris pateikė negaliojantį SSL sertifikatą. Kartais taip nutinka dėl netinkamos serverio konfigūracijos. Kartais dėl to, kad kas nors bando atakuoti jus arba jūsų pašto serverį. Jei nesate tikri, kas nutiko, spustelėkite Atmesti ir susisiekite su žmonėmis, kurie valdo jūsų pašto serverį.\n\n(%s)
+ Nepavyko prisijungti prie serverio.\n(%s)
+ Leidimas atšauktas
+ Autorizavimas nepavyko su šia klaida: %s
+ Šiuo metu šis teikėjas nepalaiko OAuth 2.0.
+ Programai nepavyko rasti naršyklės, kurią naudoti suteikiant prieigą prie paskyros.Keisti detalesTęstiIšplėstinisAplanko nustatymaiPranešimai apie naujus laiškus
+ Pranešimų aplankaiVisiTik 1 klasės aplankai1 ir 2 klasės aplankai
@@ -271,8 +383,17 @@
Tikrinant paštą pranešti apie tai būsenos juostojeĮtraukti siunčiamus laiškusRodyti pranešimą mano išsiųstiems laiškams
+ Tik kontaktai
+ Rodyti pranešimus tik žinomų kontaktų laiškams
+ Ignoruoti pokalbių žinutes
+ Nerodyti pranešimų apie el. pašto pokalbiui priklausančias žinutesAtvėrus pažymėti kaip skaitytąKai laiškas atveriamas peržiūrai pažymėti jį kaip skaitytą
+ Pažymėti kaip perskaitytą kai ištrinama
+ Pažymėti laišką kaip perskaitytą, kai jis ištrinamas
+ Pranešimų kategorijos
+ Konfiguruoti naujų laiškų pranešimus
+ Konfiguruoti klaidų ir būsenos pranešimusVisada rodyti paveikslėliusNeIš kontaktų
@@ -294,16 +415,42 @@
Atsakymo citavimo stiliusPriedėlis (pvz. Gmail)Antraštė (kaip Outlook)
+ Įkelti išsiųstus laiškus
+ Įkelti laiškus į išsiųstų laiškų aplanką po išsiuntimoBendri nustatymaiSkaitomas paštasGaunamas paštasAplankaiRedaguojamo teksto priedėlis
+ Visapusiškas šifravimas
+ Įjungti OpenPGP palaikymą
+ Pasirinkti OpenPGP programą
+ Konfiguruoti visapusišką raktą
+ Nesukonfigūruota OpenPGP programa
+ Prisijungta prie %s
+ Konfiguruojama...
+ Laikyti visus juodraščius užšifruotus
+ Visi juodraščiai bus saugomi užšifruoti
+ Šifruoti juodraščius tik tada, jei įjungtas šifravimasAplanko tikrinimo dažnisPaskyros spalvaŠios paskyros spalva kuri bus naudojama aplankuose ir paskyrų sąrašeVietinio aplanko dydisGauti laiškus iki
+ 1 KiB
+ 2 KiB
+ 4 KiB
+ 8 KiB
+ 16 KiB
+ 32 KiB
+ 64 KiB
+ 128 KiB
+ 256 KiB
+ 512 KiB
+ 1 MiB
+ 2 MiB
+ 5 MiB
+ 10 MiBbet kokio dydžio (be limito)Sinchronizuoti laiškus nuovisus (be limito)
@@ -342,21 +489,29 @@
Visus išskyrus 2 klasės aplankusSinchronizuoti serverio pašalinimusPašalinti laiškus kai jie pašalinami serveryje
+ Trūksta OpenPGP programos - ar ji buvo pašalinta?Aplanko nustatymaiRodyti viršutinėje grupėjeRodyti aplankų sąrašo viršujeRodomos aplankų klasės
+ Be klasės1 klasė2 klasė
+ Aplanko poll klasėJokios1 klasė2 klasėTos pačios kaip ir rodymo klasėsAplankų gavimo klasė
+ Be klasės1 klasė2 klasė
+ Ta pati kaip ir poll klasė
+ Aplanko pranešimų klasė
+ Be klasės1 klasė2 klasė
+ Ta pati kaip ir push klasėIšvalyti vietinius laiškusLaiškų gavimo serverisKonfigūruoti gaunamųjų laiškų serverį
@@ -365,11 +520,29 @@
Paskyros pavadinimasJūsų vardasPranešimai
+ VibracijaVibruoti
+ Vibracijos raštasNumatytas
+ Raštas 1
+ Raštas 2
+ Raštas 3
+ Raštas 4
+ Raštas 5Kartoti vibraciją
+ IšjungtaNaujo laiško skambėjimo tonas
+ Pranešimo šviesa
+ IšjungtaPaskyros spalva
+ Sistemos numatyta spalva
+ Balta
+ Raudona
+ Žalia
+ Mėlyna
+ Geltona
+ Žydra
+ PurpurinėLaiško kūrimo parinktysKūrimo numatytiejiNustatykite numatytąjį „Nuo“, Bcc ir parašą
@@ -392,7 +565,7 @@
(Nebūtina)El. pašto adresas(Būtina)
- „Reply-to“ adresas
+ Atsakymo adresas(Nebūtina)Parašas(Nebūtina)
@@ -427,6 +600,9 @@
Neatpažintas sertifikatasPriimti raktąAtmesti raktą
+ Del (arba D) - Ištrinti\nR - Atsakyti\nA - Atsakyti visiems\nC - Sudaryti\nF - Persiųsti\nM - Perkelti\nV - Archyvuoti\nY - Kopijuoti\nZ - Pažymėti (Ne)perskaitytą\nG - Žvaigždutė\nO - Rūšiavimo tipas\nI - Rūšiavimo tvarka\nQ - Grįžti į aplankus\nS - Pasirinkti/atšaukti pasirinkimą\nJ arba P - Ankstesnis laiškas\nK arba N - Kitas laiškas
+ Del (arba D) - Ištrinti\nC - Sudaryti\nM - Perkelti\nV - Archyvuoti\nY - Kopijuoti\nZ - Pažymėti (Ne)perskaitytą\nG - Žvaigždutė\nO - Rūšiavimo tipas\nI - Rušiavimo tvarka\nQ - Grįžti į aplankus\nS - Pasirinkti/atšaukti pasirinkimą
+ Aplanko pavadinime yraRodyti aplankus…Visus aplankus1 klasės aplankus
@@ -438,6 +614,7 @@
Naudoti programos temąTamsiŠviesi
+ Naudoti numatytuosius sistemos nustatymusRodymasBendraDerinimas
@@ -451,6 +628,7 @@
Laiško peržiūros temaKūrimo temaKalba
+ Nerasta jokių nustatymųNumatyta laiško temaPeržiūrint laišką, pasirinkti laiško peržiūros temąNaudoti numatytą laiškų peržiūros temą
@@ -461,6 +639,7 @@
Kai pažymėtas „automatinis sinchronizavimas“Pasirinkti viskąKiek daugiausia aplankų tikrinti siunčiant
+ 5 aplankai10 aplankų25 aplankai50 aplankų
@@ -473,6 +652,8 @@
Navigacija garso klavišaisPranešimų rodinyjeSąrašo rodiniuose
+ Rodyti suvestinius gautuosius
+ Rodyti žvaigždučių skaičiųSuvestiniai gautiejiVisi laiškai suvestiniuose aplankuoseSuvesti
@@ -482,6 +663,7 @@
RodomasJokiosJokios
+ Automatinis (%s)Šrifto dydisKonfigūruoti šrifto dydįPaskyrų sąrašas
@@ -514,16 +696,31 @@
VidutinisDidelisDidesnis
- Šiai programai nerasta tinkamo veiksmo.
+ Šiam veiksmui nerasta tinkama programa.
+ Išsiųsti nepavyko: %sIšsaugoti laišką kaip juodraštį?Išsaugoti ar nepaisyti šio laiško?
+ Išsaugoti ar atmesti pakeitimus?Nepaisyti laiško?Ar tikrai norite nepaisyti šio pranešimo?Pasirinkti tesktą kopijavimui.
+ Išvalyti vietinius laiškus?
+ Tai iš aplanko pašalins visus vietinius laiškus. Iš serverio laiškai nebus ištrinti.
+ Išvalyti laiškusPatvirtinti šalinimąAr tikrai norite pašalinti laišką?
+
+ Ar tikrai norite ištrinti šį laišką?
+ Ar tikrai norite ištrinti %1$d laiškus?
+ Ar tikrai norite ištrinti %1$d laiškų?
+ Ar tikrai norite ištrinti %1$d laiškus?
+ TaipNe
+ Patvirtinti pažymėjimą visų kaip perskaitytų
+ Ar norite visus laiškus pažymėti kaip perskaitytus?
+ Patvirtinti šiukšliadėžes ištuštinimą
+ Ar norite ištuštinti šiukšlių aplanką?TaipNePatvirtinti perkėlimą į brukalo aplanką
@@ -535,24 +732,65 @@
TaipNe
- Parsiunčiama priedas
+ Priedas atsiunčiamas»›
- Kita
+ Atsarginė kopija
+ Įvairūs
+ Eksportuoti nustatymusEksportuoti
- Dalintis
+ BendrintiEksportuojami nustatymai…
+ Nustatymai sėkmingai eksportuotiEksportuoti nustatymų nepavykoImportuoti nustatymus
+ Pasirinkti failąImportuoti
+ Nustatymai sėkmingai importuoti
+
+ Prašome įvesti slaptažodžius
+
+
+ Nepavyko importuoti nustatymų
+ Nepavyko nuskaityti nustatymų failo
+ Nepavyko importuoti kai kurių nustatymų
+ Sėkmingai importuota
+ Būtinas slaptažodis
+
+ Neimportuota
+ Importo sutrikimas
+ VėliauImportuoti nustatymusImportuojami nustatymai…
+
+ Kad galėtumėte naudotis paskyra \"%s\", turite nurodyti serverio slaptažodį.
+ Kad galėtumėte naudotis paskyra \"%s\", turite nurodyti serverio slaptažodžius.
+ Kad galėtumėte naudotis paskyra \"%s\", turite nurodyti serverio slaptažodžius.
+ Kad galėtumėte naudotis paskyra \"%s\", turite nurodyti serverio slaptažodžius.
+
+ Gaunamo serverio slaptažodis
+ Siunčiamo serverio slaptažodis
+ Naudoti tą patį slaptažodį siunčiamajame serveryje
+ Serverio pavadinimas: %sRodyti neskaitytų skaičių…
+ Paskyra
+ Paskyra, kurios neperskaitytų laiškų skaičius turėtų būti rodomas
+ Suvestiniai gautieji
+ Aplankų skaičius
+ Rodyti tik vieno aplanko neperskaitytų laiškų skaičių
+ Aplankas
+ Aplankas, kuriame turėtų būti rodomas neperskaitytų laiškų skaičius
+ Baigta
+ %1$s - %2$s
+ Nepasirinkta jokia paskyra
+ Nepasirinktas joks aplankasJokio tekstoAtverti nuorodą
- Dalintis nuoroda
+ Bendrinti nuorodąKopijuoti nuorodą į iškarpinęNuoroda
+ Kopijuoti nuorodos tekstą į iškarpinę
+ Nuorodos tekstasPaveikslėlisPeržiūrėti paveikslėlįIšsaugoti paveikslėlį
@@ -577,10 +815,24 @@
1000Serverio paieškos limitasUžklausiamas serveris
+
+ Gauname %d rezultatą
+ Gauname %d rezultatus
+ Gauname %d rezultatus
+ Gauname %d rezultatus
+
+
+ Gauname %1$d iš %2$d rezultatų
+ Gauname %1$d iš %2$d rezultatų
+ Gauname %1$d iš %2$d rezultatų
+ Gauname %1$d iš %2$d rezultatų
+ Nuotolinė paieška nepavykoIeškotiIeškoti laiškų serveryjePaieškai serveryje reikia interneto ryšio.
+ Pakeisti spalvą, kai perskaityta
+ Kitoks fonas rodys, kad pranešimas buvo perskaitytas.Teminis rodinysGrupuoti laiškus pagal pokalbįAtnaujinamos duomenų bazės
@@ -602,16 +854,169 @@
Siunčiamas laiškasIšsaugomas juodraštisGaunamas priedas…
+ Nepavyksta patvirtinti autentiškumo. Serveris nereklamuoja SASL EXTERNAL galimybės. Taip gali būti dėl kliento sertifikato problemos (pasibaigęs sertifikato galiojimo laikas, nežinoma sertifikato institucija) arba kitos konfigūracijos problemos.
+ Naudoti kliento sertifikatą
+ Nėra kliento sertifikato
+ Pašalinti kliento sertifikato pasirinkimą
+ Nepavyko gauti kliento sertifikato slapyvardžiui \"%s\"
+ Išplėstinės parinktys
+ Kliento sertifikato \"%1$s\" galiojimo laikas baigėsi arba jis dar negalioja (%2$s)
+ *Užšifruotas*
+ Pridėti iš kontaktųCcSlapta kopijaKam
+ Nuo
+ Atsakymas į
+ <Nežinomas gavėjas>
+ <Nežinomas siuntėjas>
+ Pradžia
+ DarbasKitaMobilus
+ Šioje paskyroje nėra sukonfigūruoto Juodraščių aplanko!
+ Šiai paskyrai nėra sukonfigūruoto rakto! Patikrinkite savo nustatymus.
+ Kriptografijos paslaugų teikėjas naudoja nesuderinamą versiją. Patikrinkite savo nustatymus!
+ Nepavyko prisijungti prie šifravimo paslaugų teikėjo, patikrinkite nustatymus arba spustelėkite šifravimo piktogramą ir bandykite dar kartą!
+ Nepavyko inicijuoti visapusiško šifravimo, patikrinkite nustatymus
+ PGP/INLINE režimas nepalaiko priedų!
+ Įjungti PGP/INLINE
+ Išjungti PGP/INLINE
+ Įjungti PGP Tik-Pasirašymui
+ Išjungti PGP Tik-Pasirašymui
+ PGP/INLINE režimas
+ Laiškas siunčiamas PGP/INLINE formatu.\nTai turėtų būti naudojama tik dėl suderinamumo:
+ Kai kurie klientai palaiko tik šį formatą
+ Siuntimo metu parašai gali sulūžti
+ Priedai nepalaikomi
+ Supratau!
+ Išjungti
+ Laikyti Įjungtą
+ Supratau!
+ Išjungti
+ Laikyti Įjungtą
+ PGP Tik-Pasirašymo Režimas
+ Šiame režime jūsų PGP raktas bus naudojamas nešifruoto el. laiško kriptografiniam parašui sukurti.
+ Tai el. laiško neužšifruoja, bet patvirtina, kad jis išsiųstas iš jūsų rakto.
+ Siunčiant į pašto adresų sąrašus parašai gali sulūžti.
+ Kai kuriuose klientuose parašai gali būti rodomi kaip \'signature.asc\' priedai.
+ Užšifruotuose laiškuose visada yra parašas.
+ Paprastas tekstas
+ visapusiškame paraše buvo klaida
+ būtina pilnai atsisiųsti laišką, kad būtų galima apdoroti parašą
+ turi nepalaikomą visapusišką parašą
+ Laiškas užšifruotas, bet nepalaikomu formatu.
+ Laiškas užšifruotas, bet iššifravimas atšauktas.
+ Visapusiškai pasirašytas paprastasis tekstas
+ nuo patvirtinto pasirašytojo
+ Pasirašytas paprastasis tekstas
+ bet visapusiškas raktas neatitiko siuntėjo
+ bet visapusiškas raktas yra nebegaliojantis
+ bet visapusiškas raktas yra atšauktas
+ bet visapusiškas raktas nelaikomas saugiu
+ nuo nežinomo visapusiško rakto
+ Užšifruotas
+ tačiau įvyko iššifravimo klaida
+ iššifravimui būtina pilnai atsisiųsti laišką
+ bet nėra sukonfigūruota šifravimo programa
+ Užšifruotas
+ bet ne visapusiškai
+ Visapusiškai užšifruotas
+ nuo patvirtinto siuntėjo
+ Užšifruotas
+ nuo nežinomo visapusiško rakto
+ bet visapusiškas raktas neatitiko siuntėjo
+ bet visapusiškas raktas yra nebegaliojantis
+ bet visapusiškas raktas yra atšauktas
+ bet visapusiškas raktas nelaikomas saugiu
+ bet visapusiški duomenys turi klaidų
+ bet šifravimas nelaikomas saugiuGerai
+ Ieškoti rakto
+ Peržiūrėti pasirašytoją
+ Peržiūrėti siuntėją
+ Išsamiau
+ Atrakinti
+ Ši dalis nebuvo užšifruota ir gali būti nesaugi.
+ Neapsaugotas priedasĮkeliama…
+ Iššifravimas buvo atšauktas.
+ Pakartoti
+ Užšifruotas laiškas turi būti atsisiųstas iššifravimui.
+ Klaida iššifruojant el. laišką
+ Specialieji simboliai šiuo metu nepalaikomi!
+ Klaida analizuojant adresą!
+ Slėpti nešifruotus parašus
+ Bus rodomi tik užšifruoti parašai
+ Visi parašai bus rodomi
+ Šifravimas nepasiekiamas tik-pasirašymo režimu!
+ Nepasirašytas tekstas
+ Šis el. laiškas yra užšifruotas
+ Šis el. laiškas užšifruotas naudojant OpenPGP.\nKad jį perskaitytumėte, turite įdiegti ir sukonfigūruoti suderinamą OpenPGP programėlę.
+ Eiti į Nustatymus
+ Mail laiškų sąrašas
+ Įkeliami laiškai...
+ Šifravimas neįmanomas
+ Kai kurie iš pasirinktų gavėjų nepalaiko šios funkcijos!
+ Įjungti šifravimą
+ Išjungti šifravimą
+ Šifruojant laiškus užtikrinama, kad juos galės perskaityti tik gavėjas ir niekas kitas.
+ Šifravimas bus rodomas tik tuo atveju, jei jį palaiko visi gavėjai ir jie turi būti anksčiau jums siuntę el. laišką.
+ Perjunkite šifravimą spustelėdami šią piktogramą.
+ SupratauAtgal
+ Išjungti šifravimą
+ OpenPGP šifravimas
+ Automatinio šifravimo abipusis režimas
+ Automatinio šifravimo abipusis režimas
+ Paprastai laiškai šifruojami pasirinktinai arba atsakant į užšifruotą laišką.
+ Jei ir siuntėjas, ir gavėjai įjungia abipusį režimą, šifravimas bus įjungtas pagal numatytuosius nustatymus.
+ Jūs galite spausti čia kad sužinoti daugiau.Bendri nustatymai
+ Neįdiegta OpenPGP programa
+ Įdiegti
+ Mail reikia OpenKeychain, visapusiškam šifravimui.
+ Šifruotas laiškas
+ Užšifruoti laiškų temas
+ Gali būti nepalaikoma kai kurių gavėjų
+ Vidinė klaida: Netinkama paskyra!
+ Klaida jungiantis prie %s!
+ Siųsti automatinio šifravimo sąrankos laišką
+ Saugiai bendrinkite visapusę sąranką su kitais įrenginiais
+ Automatinio šifravimo sąrankos laiškas
+ Automatinio šifravimo sąrankos laiške saugiai bendrinama visapuse sąranka su kitais prietaisais.
+ Siųsti sąrankos laišką
+ Šis laiškas bus išsiųstas jūsų adresu:
+ Generuojamas sąrankos laiškas...
+ Siunčiamas laiškas į:
+ Kad baigti, atidarykite laišką kitame prietaise ir įveskite sąrankos kodą.
+ Rodyti sąrankos kodą
+ Automatinio šifravimo sąrankos laiškas
+ Šioje žinutėje pateikiama visa informacija, skirta saugiai perkelti automatinio šifravimo nustatymus ir slaptąjį raktą iš pradinio įrenginio.
+
+Norėdami nustatyti automatinį šifravimą naujajame prietaise, vadovaukitės instrukcijomis, kurios turėtų būti pateiktos naujajame prietaise.
+
+Šią žinutę galite pasilikti ir naudoti kaip atsarginę slaptojo rakto kopiją. Jei norite tai padaryti, turėtumėte užsirašyti slaptažodį ir saugiai jį laikyti.
+
+ Siunčiant laišką įvyko klaida. Patikrinkite tinklo ryšį ir siunčiamo serverio konfigūraciją.
+ Įj.
+ Išj.
+ Leisti prieigą prie kontaktų
+ Kad galėtų teikti kontaktų pasiūlymus ir rodyti kontaktų vardus bei nuotraukas, programėlei reikia prieigos prie jūsų kontaktų.
+ Įkeliant duomenis įvyko klaida
+ Inicializuojama...
+ Laukiama naujų laiškų
+ Miegama iki kol fono sinchronizacija bus leidžiama
+ Miegama, kol bus prieinamas tinklas
+ Bakstelėkite, jei norite sužinoti daugiau.
+ Push Info
+ Naudojant Push, Mail palaiko ryšį su pašto serveriu. Android reikalauja rodyti nuolatinį pranešimą, kol programa veikia fone. %s
+ Tačiau Android taip pat leidžia paslėpti pranešimą.
+ Sužinoti daugiau
+ Konfiguruoti pranešimą
+ Jei jums nereikia momentinių pranešimų apie naujus laiškus, turėtumėte išjungti Push ir naudoti Polling. Polling reguliariai tikrina, ar yra naujų laiškų, ir pranešimo nereikia.
+ Išjungti Push
diff --git a/app/ui/legacy/src/main/res/values-lv/strings.xml b/app/ui/legacy/src/main/res/values-lv/strings.xml
index 781f59cc8c31b04cbe9566ba94fa42d61eff898f..341e351a0e9bb48c1b1bcc0f4d2e268e86c8528d 100644
--- a/app/ui/legacy/src/main/res/values-lv/strings.xml
+++ b/app/ui/legacy/src/main/res/values-lv/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- K-9 Suņa Staigātāji
+ Mail Suņa StaigātājiAvota kodsApache licence, Versija 2.0Atvērtā koda projekts
@@ -251,6 +251,8 @@ pat %d vairāk
Uzlikt jaunu kontuE-pasta adreseParole
+
+
Lai apskatītos savu paroli, ieslēdziet ekrāna bloķēšanu savā ierīcē.Apstiprināt savu identitāti
@@ -292,10 +294,6 @@ pat %d vairāk
Nedzēst to no serveraIzdzēst to no serveraAtzīmēt kā izlasītu uz servera
- Izmantot saspiešanu tīklā:
- Mobilais
- Wi-Fi
- CitsIzdzēst vēstules arī no serveraUzreizPārbaudes laikā
@@ -721,12 +719,16 @@ pat %d vairāk
Izvēlēties datniImportētIestatījumi importēti veiksmīgi
+
Lūdzu, ievadiet paroles
+
+
Neizdevās importēt iestatījumusNeizdevās nolasīt iestatījumu datniNeizdevās importēt dažus iestatījumusImportēts veiksmīgiNepieciešama parole
+
Nav importētsImportēšana neizdevāsVēlāk
@@ -920,14 +922,6 @@ pat %d vairāk
Tiks parādīti visi parakstiNevar iešifrēt \"tikai parakstīt\" režīmā!Neparakstīts teksts
- APG koda brīdinājums
- APG vairs netiek atbalstīts!
- Tāpēc APG atbalsts ir izņemts no K-9 Pasta.
- Izstrāde pārtraukta 2014. gada sākumā
- Satur neatrisinātas drošības problēmas
- Šeit uzspiežot, varat uzzināt vairāk.
- Sapratu!
- APGVēstule ir šifrētaŠī vēstule ir šifrēta ar OpenPGP. Lai to apskatītos, jāinstalē un jāiestata saderīga OpenPGP lietotneDoties uz iestatījumiem
diff --git a/app/ui/legacy/src/main/res/values-ml/strings.xml b/app/ui/legacy/src/main/res/values-ml/strings.xml
index 6f1200564caaac7c4b3da67dd710f42960c63ea5..a41668ec398c7f67527da4d5ad2389b93cd36eb3 100644
--- a/app/ui/legacy/src/main/res/values-ml/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ml/strings.xml
@@ -8,7 +8,6 @@
കെ-9 വായിക്കാത്തവThe K-9 Dog Walkers
- Copyright 2008-%s The K-9 Dog Walkers. Portions Copyright 2006-%s the Android Open Source Project.സോഴ്സ് കോഡ്അപ്പാച്ചെ ലൈസൻസ്, പതിപ്പ് 2.0ഓപ്പൺ സോഴ്സ് പ്രോജക്റ്റ്
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.രചയിതാക്കൾ
- പുനരവലോകന വിവരങ്ങൾഇ-മെയിലുകൾ വായിക്കുകനിങ്ങളുടെ ഇമെയിലുകൾ വായിക്കാൻ ഈ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.ഇമെയിലുകൾ ഇല്ലാതാക്കുകനിങ്ങളുടെ ഇമെയിലുകൾ ഇല്ലാതാക്കാൻ ഈ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.About Mailഅക്കൗണ്ടുകൾ
- ഫോൾഡറുകൾവിപുലമായത്രചിക്കുകമറുപടി പറയുക
@@ -51,12 +48,6 @@
ഫോൾഡർ തിരഞ്ഞെടുക്കുകഇതിലേക്ക് നീക്കുക...ഇതിലേക്ക് പകർത്തുക...
- പോൾ%s:%s%s
- തലക്കെട്ടുകൾ ലഭ്യമാക്കുന്നു %s:%s%s
- അയയ്ക്കുന്നു %s%s
- Proc %s:%s%s
- \u0020%d/%d
- സമന്വയിപ്പിക്കൽ പ്രവർത്തനരഹിതമാക്കി%d തിരഞ്ഞെടുത്തുഅടുത്തത്മുമ്പത്തെ
@@ -64,10 +55,7 @@
ശരിറദ്ദാക്കുകഅയയ്ക്കുക
- വീണ്ടും അയയ്ക്കുകവിഷയം ശൂന്യമാണ്, ഏതുവിധേനയും അയയ്ക്കാൻ വീണ്ടും ക്ലിക്കുചെയ്യുക
- തിരഞ്ഞെടുക്കുക
- തിരഞ്ഞെടുത്തത് മാറ്റുകമറുപടി പറയുകഎല്ലാത്തിനും മറുപടി പറയുകഇല്ലാതാക്കുക
@@ -203,20 +191,14 @@
സ്വീകർത്താവ്:Cc:Bcc:
- തുറക്കുക
- സേവ്അറ്റാച്ചുമെന്റ് സേവ് ചെയ്യാനായില്ല.ചിത്രങ്ങൾ കാണിക്കുക%s എന്നതിനുള്ള വീക്ഷണസഹായസാമഗ്രി കണ്ടെത്താനായില്ല.പൂർണ്ണ സന്ദേശം ഡൗൺലോഡു ചെയ്യുക
- സന്ദേശം പ്രദർശിപ്പിക്കാൻ കഴിയുന്നില്ല%1$s വഴി
- എല്ലാ തലക്കെട്ടുകളും ഡൗൺലോഡുചെയ്തു, പക്ഷേ പ്രദർശിപ്പിക്കുന്നതിന് അധിക തലക്കെട്ടുകളൊന്നുമില്ല.
- ഡാറ്റാബേസ് അല്ലെങ്കിൽ മെയിൽ സെർവറിൽ നിന്ന് അധിക തലക്കെട്ടുകൾ വീണ്ടെടുക്കുന്നത് പരാജയപ്പെട്ടു.ഈ അയച്ചയാളിൽ നിന്ന് കൂടുതൽ%s ൽ നിന്ന്
- ഡീബഗ് / സന്ദേശ ബോഡി ശൂന്യമാക്കുകസന്ദേശം നിരസിച്ചുസന്ദേശം ഡ്രാഫ്റ്റായി സംരക്ഷിച്ചുനക്ഷത്രങ്ങൾ കാണിക്കുക
@@ -251,10 +233,6 @@
മെയിൽ തലക്കെട്ടുകളിൽ നിന്ന് കെ-9 യൂസർ-ഏജന്റിനെ നീക്കം ചെയ്യുകസമയ മേഖല മറയ്ക്കൂമെയിൽ തലക്കെട്ടുകളിലും മറുപടി ശീർഷകത്തിലും പ്രാദേശിക സമയമേഖലയ്ക്ക് പകരം യുടിസി ഉപയോഗിക്കുക,
- അറിയിപ്പുകളിൽ വിഷയം മറയ്ക്കുക
- ഒരിക്കലും
- ഉപകരണം ലോക്കുചെയ്യുമ്പോൾ
- എല്ലായ്പ്പോഴും\'ഇല്ലാതാക്കുക\' ബട്ടൺ കാണിക്കുകഒരിക്കലുംഒരൊറ്റ സന്ദേശ അറിയിപ്പിനായി
@@ -274,7 +252,8 @@
ഒരു പുതിയ അക്കൗണ്ട് സജ്ജമാക്കുകഇമെയിൽ വിലാസംരഹസ്യവാചകം
- രഹസ്യവാചകം പ്രദർശിപ്പിക്കുക
+
+
നിങ്ങളുടെ പാസ്വേഡ് ഇവിടെ കാണുന്നതിന്, ഈ ഉപകരണത്തിൽ സ്ക്രീൻ ലോക്ക് പ്രവർത്തനക്ഷമമാക്കുക.നിങ്ങളുടെ ഐഡന്റിറ്റി പരിശോധിച്ചുറപ്പിക്കുക
@@ -316,12 +295,6 @@
സെർവറിൽ ഇല്ലാതാക്കരുത്സെർവറിൽ നിന്ന് ഇല്ലാതാക്കൂസെർവറിൽ വായിച്ചതായി അടയാളപ്പെടുത്തുക
- നെറ്റ്വർക്കിൽ ചുരുക്കൽ ഉപയോഗിക്കുക:
- മൊബൈൽ
- വൈഫൈ
- മറ്റുള്ളവ
- ബാഹ്യ സംഭരണം (SD കാർഡ്)
- പതിവ് ആന്തരിക സംഭരണംസെർവറിൽ ഇല്ലാതാക്കിയ സന്ദേശങ്ങൾ മായ്ക്കുകഉടനെപോളിംഗ് നടത്തുമ്പോൾ
@@ -336,11 +309,8 @@
സബ്സ്ക്രൈബുചെയ്ത ഫോൾഡറുകൾ മാത്രം കാണിക്കുകഫോൾഡർ യാന്ത്രികമായി വികസിപ്പിക്കുകOWA പാത
- ഐച്ഛികംപ്രാമാണീകരണ പാത
- ഐച്ഛികംമെയിൽബോക്സ് അപരനാമം
- ഐച്ഛികംഔട്ട്ഗോയിംഗ് സെർവർ ക്രമീകരണങ്ങൾSMTP സെർവർപോർട്ട്
@@ -352,7 +322,6 @@
\"%1$s = %2$s\" \"%3$s = %4$s\" ഉപയോഗിച്ച് സാധുവല്ലഅസാധുവായ സജ്ജീകരണം: %sഅക്കൗണ്ട് ഓപ്ഷനുകൾ
- കോംപാക്റ്റ് അക്കൗണ്ട്ഫോൾഡർ വോട്ടെടുപ്പ് ആവൃത്തിഒരിക്കലുംഓരോ 15 മിനിറ്റിലും
@@ -363,8 +332,6 @@
ഓരോ 6 മണിക്കൂറിലുംഓരോ 12 മണിക്കൂറിലുംഓരോ 24 മണിക്കൂറിലും
- ഈ അക്കൗണ്ടിനായി പുഷ് മെയിൽ പ്രവർത്തനക്ഷമമാക്കുക
- നിങ്ങളുടെ സെർവർ ഇതിനെ പിന്തുണയ്ക്കുന്നുവെങ്കിൽ, പുതിയ സന്ദേശങ്ങൾ തൽക്ഷണം ദൃശ്യമാകും. ഈ ഓപ്ഷന് പ്രകടനം നാടകീയമായി മെച്ചപ്പെടുത്താനോ കുറയ്ക്കാനോ കഴിയും.IDLE കണക്ഷൻ പുതുക്കുകഓരോ 2 മിനിറ്റിലുംഓരോ 3 മിനിറ്റിലും
@@ -375,7 +342,6 @@
ഓരോ 48 മിനിറ്റിലുംഓരോ 60 മിനിറ്റിലുംമെയിൽ വരുമ്പോൾ എന്നെ അറിയിക്കുക
- മെയിൽ പരിശോധിക്കുമ്പോൾ എന്നെ അറിയിക്കുകപ്രദർശിപ്പിക്കേണ്ട സന്ദേശങ്ങളുടെ എണ്ണം10 സന്ദേശങ്ങൾ25 സന്ദേശങ്ങൾ
@@ -397,8 +363,6 @@
തുടരുകവിപുലമായത്അക്കൗണ്ട് സജ്ജീകരണങ്ങൾ
- സ്ഥിരസ്ഥിതി അക്കൗണ്ട്
- സ്ഥിരസ്ഥിതിയായി ഈ അക്കൗണ്ടിൽ നിന്ന് മെയിൽ അയയ്ക്കുകപുതിയ മെയിൽ അറിയിപ്പുകൾഅറിയിപ്പുകൾ ഫോൾഡറുകൾഎല്ലാം
@@ -420,8 +384,6 @@
ഒരു സന്ദേശം കാണാനായി തുറക്കുമ്പോൾ വായിച്ചതായി അടയാളപ്പെടുത്തുകഇല്ലാതാക്കുമ്പോൾ വായിച്ചതായി അടയാളപ്പെടുത്തുകഒരു സന്ദേശം ഇല്ലാതാക്കുമ്പോൾ വായിച്ചതായി അടയാളപ്പെടുത്തുക
- അറിയിപ്പ് ക്രമീകരണങ്ങൾ
- സിസ്റ്റം അറിയിപ്പ് ക്രമീകരണങ്ങൾ തുറക്കുകഅറിയിപ്പ് വിഭാഗങ്ങൾപുതിയ സന്ദേശങ്ങൾക്കായി അറിയിപ്പുകൾ ക്രമീകരിക്കുകപിശകക്, സ്റ്റാറ്റസ് അറിയിപ്പുകളും ക്രമീകരിക്കുക
@@ -466,9 +428,6 @@
ഫോൾഡർ വോട്ടെടുപ്പ് ആവൃത്തിഅക്കൗണ്ട് നിറംഫോൾഡറിലും അക്കൗണ്ട് ലിസ്റ്റിലും ഈ അക്കൗണ്ടിന്റെ ഊന്നിപ്പറഞ്ഞ നിറം ഉപയോഗിക്കുന്നു
- നിറമില്ല
- അറിയിപ്പ് LED നിറം
- നിങ്ങളുടെ ഉപകരണത്തിന്റെ LED നിറം ഈ അക്കൗണ്ടിനായി മിന്നിമറയണംപ്രാദേശിക ഫോൾഡർ വലുപ്പംവരെ സന്ദേശങ്ങൾ നേടുക1 KiB
@@ -555,7 +514,6 @@
നിങ്ങളുടെ പേര്അറിയിപ്പുകൾവിറയ്ക്കുക
- മെയിൽ വരുമ്പോൾ വിറയ്ക്കുകവിറയ്ക്കൽ പാറ്റേണുകൾസ്ഥിരസ്ഥിതിപാറ്റേൺ 1
@@ -676,10 +634,6 @@
സന്ദേശ കാഴ്ചകളിൽലിസ്റ്റ് കാഴ്ചകളിൽഏകീകൃത ഇൻബോക്സ് കാണിക്കുക
- %s%s
- - വായിക്കാത്തത്
- എല്ലാ സന്ദേശങ്ങളും
- തിരയാൻ കഴിയുന്ന ഫോൾഡറുകളിലെ എല്ലാ സന്ദേശങ്ങളുംഏകീകൃത ഇൻബോക്സ്ഏകീകൃത ഫോൾഡറുകളിലെ എല്ലാ സന്ദേശങ്ങളുംഏകീകരിക്കുക
@@ -712,8 +666,6 @@
വിഷയംസമയവും തീയതിയുംസന്ദേശ ബോഡി
- %d%%
- %1$s: %2$sസന്ദേശ ഘടനടെക്സ്റ്റ് ഇൻപുട്ട് ഫീൽഡുകൾസ്ഥിരസ്ഥിതി
@@ -770,12 +722,16 @@
ഫയൽ തിരഞ്ഞെടുക്കുകഇംപോർട്ട്ക്രമീകരണങ്ങൾ വിജയകരമായി ഇംപോർട്ട് ചെയ്തു
+
ദയവായി രഹസ്യവാക്കുകൾ നൽകുക
+
+
ക്രമീകരണങ്ങൾ ഇംപോർട്ടുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടുക്രമീകരണ ഫയൽ വായിക്കുന്നതിൽ പരാജയപ്പെട്ടുചില ക്രമീകരണങ്ങൾ ഇംപോർട്ട് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടുവിജയകരമായി ഇംപോർട്ട് ചെയ്തുരഹസ്യവാചകം ആവശ്യമാണ്
+
ഇംപോർട്ട് ചെയ്തിട്ടില്ലഇംപോർട്ട് തകരാർപിന്നീട്
@@ -965,21 +921,11 @@
എല്ലാ ഒപ്പുകളും പ്രദർശിപ്പിക്കുംചിഹ്നം മാത്രമുള്ള മോഡിൽ എൻക്രിപ്ഷൻ ലഭ്യമല്ല!ഒപ്പിടാത്ത വാചകം
- APG ഒഴിവാക്കൽ മുന്നറിയിപ്പ്
- APG ഇപ്പോൾ പരിപാലിക്കപ്പെടുന്നില്ല!
- ഇക്കാരണത്താൽ, കെ-9 മെയിലിൽ നിന്ന് APGയ്ക്കുള്ള പിന്തുണ നീക്കംചെയ്തു.
- 2014 ന്റെ തുടക്കത്തിൽ വികസനം നിലച്ചു
- പരിഹരിക്കാത്ത സുരക്ഷാ പ്രശ്നങ്ങൾ അടങ്ങിയിരിക്കുന്നു
- കൂടുതലറിയാൻ നിങ്ങൾക്ക് ഇവിടെ ക്ലിക്കു ചെയ്യാം.
- മനസ്സിലായി!
- APGഈ ഇമെയിൽ എൻക്രിപ്റ്റ് ചെയ്തതാണ്ഈ ഇമെയിൽ ഓപ്പൺപിജിപി ഉപയോഗിച്ച് എൻക്രിപ്റ്റുചെയ്തതാണ്.\nഇത് വായിക്കാൻ, നിങ്ങൾ ഒരു അനുയോജ്യമായ ഓപ്പൺപിജിപി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്ത് ക്രമീകരിക്കേണ്ടതുണ്ട്. ക്രമീകരണങ്ങളിലേക്ക് പോകുകകെ-9 സന്ദേശ പട്ടികസന്ദേശങ്ങൾ ലോഡുചെയ്യുന്നു...
- ഫോൾഡർ ലിസ്റ്റ് നേടുന്നത് പരാജയപ്പെട്ടു
- OpenPGP ദാതാവിൽ നിന്ന് സ്വീകർത്താവിന്റെ നില വീണ്ടെടുക്കുന്നതിൽ പിശക്!എൻക്രിപ്ഷൻ സാധ്യമല്ലതിരഞ്ഞെടുത്ത ചില സ്വീകർത്താക്കൾ ഈ സവിശേഷതയെ പിന്തുണയ്ക്കുന്നില്ല!എൻക്രിപ്ഷൻ പ്രാപ്തമാക്കുക
@@ -1025,8 +971,6 @@
സന്ദേശം അയയ്ക്കുമ്പോൾ ഒരു പിശക് സംഭവിച്ചു. നിങ്ങളുടെ നെറ്റ്വർക്ക് കണക്റ്റിവിറ്റിയും ഔട്ട്ഗോയിംഗ് സെർവർ സജ്ജീകരണങ്ങളും പരിശോധിക്കുക.ഓൺഓഫ്
- തുറക്കുക
- അടയ്ക്കുകകോൺടാക്റ്റുകളിലേക്ക് പ്രവേശനം അനുവദിക്കുകകോൺടാക്റ്റ് നിർദ്ദേശങ്ങൾ നൽകുന്നതിനും കോൺടാക്റ്റ് നാമങ്ങളും ഫോട്ടോകളും പ്രദർശിപ്പിക്കുന്നതിനും, അപ്ലിക്കേഷന് നിങ്ങളുടെ കോൺടാക്റ്റുകളിലേക്ക് പ്രവേശനം ആവശ്യമാണ്.
diff --git a/app/ui/legacy/src/main/res/values-nb/strings.xml b/app/ui/legacy/src/main/res/values-nb/strings.xml
index b19da5ec93d5d2a55d9fa0bcbf89b9ee92a310e4..2d6a1512a2abb7b8ce437e4f79f345ce7f85b6c0 100644
--- a/app/ui/legacy/src/main/res/values-nb/strings.xml
+++ b/app/ui/legacy/src/main/res/values-nb/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
+ The Mail Dog WalkerskildekodeApache License, Version 2.0Åpen kildekode prosjekt
@@ -234,6 +234,8 @@ til %d flere
Sett opp en ny kontoE-postadressePassord
+
+
Manuelt oppsett
@@ -272,10 +274,6 @@ til %d flere
Ikke slett på tjenerSlett fra tjenerMerk som lest på tjener
- Bruk kompresjon på nettverk:
- Mobil
- Wi-Fi
- AnnetFjern slettede meldinger på tjenerUmiddelbartI forbindelse med sjekking av e-post
@@ -689,12 +687,16 @@ til %d flere
Velg filImporterInnstillinger importert med hell
+
Vennligst skriv inn passord
+
+
Import av innstillinger mislyktesKlarte ikke avlese innstillingsfilKlarte ikke importere noen innstillingerImportert med hellPassord er påkrevd
+
Importer innstillingerImporterer innstillinger …Tjenernavn: %s
@@ -870,14 +872,6 @@ til %d flere
Alle signaturer vil bli vistKryptering utilgjengelig i Bare-signer -modus!Usignert tekst
- APG-foreldelsesadvarsel
- APG vedlikeholdes ikke lenger
- På grunn av dette, har støtten for APG blitt fjernet fra K-9.
- Utviklingen stoppet tidlig i 2014
- Inneholder sikkerhetsfeil som ikke er rettet
- Du kan klikke her for å lære mer.
- Skjønner!
- APGDenne e-posten er kryptertDenne e-posten har blitt kryptert med OpenPGP.\nFor å lese den, må du installere og sette opp et kompatibelt OpenPGP-program.Gå til \"Innstillinger\"
diff --git a/app/ui/legacy/src/main/res/values-nl/strings.xml b/app/ui/legacy/src/main/res/values-nl/strings.xml
index 01f2f1e970d62c8beb64384806877bdd7e57e622..d21112983f7c40098394d78865908e6fac188c39 100644
--- a/app/ui/legacy/src/main/res/values-nl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-nl/strings.xml
@@ -1,1011 +1,959 @@
-
-
-
- Mail
- Mail Accounts
- Ongelezen email
-
- Het K-9 ontwikkelingsteam
- Auteursrecht 2008-%s The K-9 Dog Walkers. Voor gedeeltes geldt auteursrecht 2006-%s het Android Open Source Project.
- Broncode
- Apache Licentie, versie 2.0
- Opensource project
- Website
- Gebruikersforum
- Fediverse
- Twitter
- Bibliotheken
- Licentie
- Wijzigingslog
- Het laden van het wijzigingslog is mislukt.
- Versie %s
- Wat is er nieuw
- Laat veranderingen zien als de app recent is bijgewerkt
- Ontdek wat er nieuw is in deze uitgave
-
- Welkom bij Mail
- Mail is the default mail client for /e/
-
- -- Verzonden met /e/ Mail.
-
- De account \"%s\" zal verwijderd worden van Mail.
-
- Auteurs
- Revisie informatie
- E-mails lezen
- Sta deze appicatie toe om je e-mails te lezen.
- Verwijder e-mails
- Sta deze applicatie toe om je e-mails te verwijderen.
- Over Mail
- Accounts
- Mappen
- Geavanceerd
- Nieuw bericht
- Antwoorden
- Allen antwoorden
- Doorsturen
- Doorsturen als bijlage
- Kies account
- Kies een map
- Peiling %s:%s%s
- Koppen ophalen %s:%s%s
- Verzenden %s%s
- Verw. %s:%s%s
- \u0020%d/%d
- Synchroniseren uitgeschakeld
- %d geselecteerd
- Volgende
- Vorige
-
- OK
- Annuleren
- Verzenden
- Opnieuw verzenden
- Er is geen onderwerp ingevuld. Tik nogmaals om toch te versturen
- Selecteren
- Deselecteren
- Antwoorden
- Iedereen antwoorden
- Verwijderen
- Archief
- Spam
- Doorsturen
- Doorsturen als bijlage
- Bewerken als nieuw bericht
- Verplaatsen
- Verplaatsen naar concepten
- Verzenden …
- Opnieuw opslaan …
- Klaar
- Vergeten
- Opslaan als concept
- Controleer e-mail
- Verstuur berichten
- Maplijst verversen
- Map vinden
- Account toevoegen
- Nieuw bericht
- Zoeken
- Overal zoeken
- Zoekresultaten
- Instellingen
- Beheer mappen
- Account instellingen
- Verwijder account
- Markeer als gelezen
- Delen
- Kies afzender
- Voeg ster toe
- Verwijder ster
- Kopieer
- Laat koppen zien
-
- Adres naar klembord gekopieerd
- Adressen naar klembord gekopieerd
-
- De onderwerpsregel is naar het klembord gekopieerd
- Schakel naar donker thema
- Schakel naar licht thema
- Markeer als ongelezen
- Lees rapport
- Lees rapport vragen
- Geen lees rapport vragen
- Voeg bijlage toe
- Prullenbak legen
- Wissen
- Over
- Instellingen
-
- (Geen onderwerp)
- Geen afzender
- Berichten laden\u2026
- Netwerkfout
- Bericht niet gevonden
- Bericht laden is mislukt
- Volgende %d berichten
- %.1f GB
- %.1f MB
- %.1f kB
- %d B
- Account \"%s\" verkleind van %s naar %s
- Account \"%s\" comprimeren
- Nieuw bericht
-
- %d nieuw bericht
- %d nieuwe berichten
-
- %d Ongelezen (%s)
- + %1$d meer bij %2$s
- Antwoorden
- Gelezen
- Alles als gelezen markeren
- Verwijderen
- Alles verwijderen
- Archiveren
- Alles Archiveren
- Spam
- Certificaat fout voor %s
- Controleer de server instellingen
- Authenticatie mislukt
- Authenticatie mislukt voor %s. Werk de server instellingen bij.
- Controleer berichten: %s:%s
- Controleren berichten
- Versturen van berichten: %s
- Berichten versturen
- :
- Synchroniseren (Duw)
- Wordt weergeven in afwachting op nieuwe berichten
- Beeld berichten
- Meldingen gerelateerd aan berichten
- Diversen
- Overige meldingen zoals fouten en dergelijke.
- Postvak-in
- Postvak-uit
- Concepten
- Prullenbak
- Verzonden
- Fout bij verzenden van berichten
- Versie
- Debug log toestaan
- Log extra diagnostische informatie
- Log gevoelige informatie
- Kan wachtwoorden laten zien in logs.
- Laad meer berichten
- Aan:%s
- Onderwerp
- Bericht tekst
- Handtekening
- -------- Origineel bericht --------
- Onderwerp:
- Verzonden:
- Van:
- Aan:
- CC:
- %s schreef:
- %2$s schreef op %1$s:
- Voeg minimaal 1 ontvanger toe.
- De ontvanger is niet (volledig) ingevuld!
- Geen e-mailadres gevonden.
- Sommige bijlagen kunnen niet worden doorgestuurd omdat ze niet zijn gedownload.
- Dit bericht kan niet worden doorgestuurd omdat bijlagen niet zijn gedownload.
- Geciteerd bericht bijvoegen
- Citaattekst verwijderen
- Citaattekst bewerken
- Bijlage verwijderen
- Van: %s <%s>
- Aan:
- CC:
- BCC:
- Open
- Opslaan
- Kan bijlage niet opslaan.
- Afbeeldingen weergeven
- Niet in staat viewer te vinden voor %s.
- Download compleet bericht
- Niet mogelijk om bericht te tonen
- via %1$s
-
- Alle koppen zijn gedownload, maar er zijn geen extra koppen om te tonen.
- Het ophalen van extra koppen uit de gegevensbank of e-mailserver is mislukt.
- Meer van deze afzender
- Van %s
- Debug / Bericht veld wissen
- Bericht verwijderd
- Bericht opgeslagen als concept
- Bericht sterren
- Sterren geven gemarkeerde berichten aan
- Preview regels
- Toon naam bij bericht
- Geef bij voorkeur naam van afzender/geadresseerde weer i.p.v. e-mailadres
- Correspondent boven onderwerp
- Correspondent namen boven de onderwerp-regel, niet eronder
- Toon naam uit contactenlijst
- Gebruik naam uit het adresboek
- Kleuren contacten
- Kleur de namen in uw contact lijst
- Vaste breedte lettertypen
- Gebruik een lettertype met vaste breedte bij het weergeven van plain-tekstberichten
- Berichten auto-passen
- Berichten passend maken op het scherm
- Terug naar lijst na verwijderen
- Terug naar berichtenlijst na bericht verwijderen
- Volgend bericht laten zien na verwijderen
- Standaard volgend bericht laten zien na verwijderen
- Bevestig acties
- Toon een dialoogvenster altijd wanneer u de geselecteerde acties uitvoert
- Verwijder
- Gemarkeerd verwijderen (in bericht weergave)
- Spam
- Bericht afbreken
- Markeer alles als gelezen
- Verwijder (van notificaties)
- Mail client verbergen
- Remove Mail User-Agent from mail headers
- Tijdzone verbergen
- Gebruik UTC in plaats van de lokale tijdzone in de e-mailkoppen en bij het antwoorden van e-mail
- Verberg onderwerp in notificaties
- Nooit
- Als toestel vergrendeld is
- Altijd
- Toon \'Verwijder\' knop
- Nooit
- Notificatie voor los bericht
- Altijd
- Start scherm notificatie
- Geen start scherm notificatie
- App naam
- Aantal nieuwe berichten
- Berichten teller (ook verzonden)
- Hetzelfde. Ook na schermontgrendeling
- Stilteperiode
- Schakel beltoon, trillen en leds uit gedurende de nacht
- Notificaties uitzetten
- Notificaties volledig uitzetten tijdens stilteperiode
- Stilteperiode start
- Stilteperiode einde
- Een nieuw account instellen
- E-mailadres
- Wachtwoord
- Toon wachtwoord
- Handmatige setup
-
- Ophalen account informatie\u2026
- Controleren van inkomende serverinstellingen\u2026
- Controleren van uitgaande serverinstellingen\u2026
- Authenticatie\u2026
- Accountinstellingen worden opgehaald\u2026
- Annuleren\u2026
- Bijna klaar!
- Geef dit account een naam (optioneel):
- Typ je naam (zichtbaar bij uitgaande berichten):
- Account type
- Welk type account is dit?
- POP3
- IMAP
- Normaal wachtwoord
- Wachtwoord, onveilig verzonden
- Versleuteld wachtwoord
- Client certificaat
- Inkomende server instellingen
- Gebruikersnaam
- Wachtwoord
- Client certificaat
- POP3 server
- IMAP server
- WebDAV (Exchange) server
- Poort
- Beveiligingstype
- Authenticatietype
- Geen
- SSL/TLS
- STARTTLS
- \"%1$s = %2$s\" is niet geldig met \"%3$s = %4$s\"
- Wanneer ik een bericht verwijder
- Verwijder niet van server
- Verwijder van server
- Markeer als gelezen op server
- Gebruik compressie op het netwerk:
- Mobiel
- Wi-Fi
- Anders
- Externe opslag (SD-kaart)
- Reguliere interne opslag
- Wissen berichten
- Onmiddellijk na verwijderen of verplaatsen
- Tijdens iedere peiling
- Alleen handmatig
- IMAP namespace automatisch detecteren
- IMAP pad voorvoegsel
- Conceptmap
- Verzendmap
- Prullenbakmap
- Archiefmap
- Spammap
- Alleen geabonneerde mappen weergeven
- Map automatisch uitvouwen
- WebDAV (Exchange) path
- Optioneel
- Authenticatie pad
- Optioneel
- Mailbox alias
- Optioneel
- Uitgaande server instellingen
- SMTP server
- Poort
- Beveiligingstype
- Inloggen vereist.
- Gebruikersnaam
- Wachtwoord
- Authenticatietype
- \"%1$s = %2$s\" is niet geldig met \"%3$s = %4$s\"
- Ongeldige setup: %s
- Account opties
- Account comprimeren
- Map peiling-frequentie
- Nooit
- Elke 15 minuten
- Elke 30 minuten
- Elk uur
- Elke 2 uur
- Elke 3 uur
- Elke 6 uur
- Elke 12 uur
- Elke 24 uur
- Duw mail voor dit account toestaan
- Als uw server dit ondersteunt, zullen nieuwe berichten direct verschijnen. Deze optie kan drastisch verbeteringen of verslechteringen van prestaties teweeg brengen.
- Vernieuw inactieve verbinding
- Elke 2 minuten
- Elke 3 minuten
- Elke 6 minuten
- Elke 12 minuten
- Elke 24 minuten
- Elke 36 minuten
- Elke 48 minuten
- Elke 60 minuten
- Waarschuw me wanneer nieuwe e-mail binnenkomt
- Waarschuw me wanneer er op nieuwe e-mail wordt gecontroleerd
- Aantal berichten om te laten zien
- 10 berichten
- 25 berichten
- 50 berichten
- 100 berichten
- 250 berichten
- 500 berichten
- 1000 berichten
- 2500 berichten
- 5000 berichten
- 10000 berichten
- alle berichten
- Kan bericht niet kopiëren of verplaatsen omdat deze niet gesynchroniseerd is met de server
- Setup kon niet afronden
- Gebruikersnaam of wachtwoord ongeldig.\n(%s)
- De server presenteerde een ongeldig SSL certificaat. Dit kan komen door een verkeerd geconfigureerde server. Dit kan ook komen doordat iemand U of Uw mailserver aan probeert te vallen. Als U niet zeker weet wat er aan de hand is klik dan op Reject en neem contact op met de beheerder van Uw mailserver.\n\n(%s)
- Kan geen verbinding met server maken.\n(%s)
- Details aanpassen
- Doorgaan
- Geavanceerd
- Account instellingen
- Standaard account
- Standaard e-mail verzenden vanaf dit account
- Nieuwe e-mail notificatie
- Notificatie mappen
- Alles
- Alleen 1e klasse mappen
- 1e en 2e klasse mappen
- Alle behalve 2e klasse mappen
- Geen
- Sync notificatie
- Je e-mailadres
- Notificatie in statusbalk wanneer nieuwe e-mail binnenkomt
- Notificatie in statusbalk wanneer wordt gecontroleerd op nieuwe e-mail
- Notificatie voor e-mail welke ik verstuur
- Notificatie ook voor e-mail verzonden vanaf een identiteit
- Alleen contacten
- Notificaties alleen weergeven voor bekende contacten
- Markeer als gelezen als geopend
- Markeer als gelezen wanneer ingezien
- Als gelezen markeren bij wissen
- Markeer een bericht als gelezen wanneer die wordt gewist
- Meldingsinstellingen
- Open instellingen voor systeemmeldingen
- Laat afbeeldingen automatisch zien
- Nooit
- Alleen van contacten
- Altijd
- Berichten versturen
- Citeer bericht bij antwoorden
- Neem orginele bericht mee in het antwoord.
- Antwoorden na citaat
- Wanneer u antwoord op berichten, zal het originele bericht boven uw antwoord staan.
- Verwijder handtekening bij reactie
- Handtekeningen worden verwijderd bij geciteerde berichten
- Berichtopmaak
- Platte Tekst (plaatjes en opmaak worden verwijderd)
- HTML (plaatjes en opmaak blijven behouden)
- Automatisch
- Altijd Cc/Bcc laten zien
- Leesbevestiging
- Altijd een leesbevestiging vragen
- Quotestijl bij antwoorden
- Prefix (zoals Gmail, Pine)
- Kop (zoals Outlook)
- Upload verzonden berichten
- Upload berichten naar \'Verzonden\' map na verzenden
- Algemene instellingen
- Berichten lezen
- Berichten ophalen
- Mappen
- Citaat voorvoegsel
- Eind-tot-eind versleuteling
- OpenPGP ondersteuning inschakelen
- OpenPGP app selecteren
- Eind-tot-eind sleutel instellen
- Geen OpenPGP app ingesteld
- Verbonden met %s
- Configureren…
- Sla alle concepten versleuteld op
- Alle concepten zullen versleuteld worden opgeslagen
- Versleutel concepten alleen als versleuteling is ingeschakeld
- Map peiling-frequentie
- Account kleur
- De kleur van dit account gebruikt bij mappen en accountlijst
- Geen kleur
- Notificatie LED kleur
- Kies de LED kleur van de telefoon, welke moet knipperen voor deze account
- Lokale map grootte
- Automatisch berichten downloaden tot
- 1 KiB
- 2 KiB
- 4 KiB
- 8 KiB
- 16 KiB
- 32 KiB
- 64 KiB
- 128 KiB
- 256 KiB
- 512 KiB
- 1 MiB
- 2 MiB
- 5 MiB
- 10 MiB
- elke grootte (geen limiet)
- Sync berichten van
- alles (geen limiet)
- vandaag
- laatste 2 dagen
- laatste 3 dagen
- afgelopen week
- afgelopen 2 weken
- afgelopen 3 weken
- afgelopen maand
- afgelopen 2 maanden
- afgelopen 3 maanden
- afgelopen 6 maanden
- afgelopen jaar
- Mappen om weer te geven
- Alle
- Alleen 1e klasse mappen
- 1e en 2e klasse mappen
- Alle behalve 2e klasse mappen
- Peiling mappen
- Alle
- Alleen 1e klasse mappen
- 1e en 2e klasse mappen
- Alle behalve 2e klasse mappen
- Geen
- Duw mappen
- Alle
- Alleen 1e klasse mappen
- 1e en 2e klasse mappen
- Alle behalve 2e klasse mappen
- Geen
- Verplaats/kopieer doel mappen
- Alle
- Alleen 1e klasse mappen
- 1e en 2e klasse mappen
- Alle behalve 2e klasse mappen
- Sync verwijderingen op server
- Verwijder berichten wanneer verwijderd van server
- OpenPGP app niet aanwezig - is de app verwijderd?
- Map instellingen
- Laat zien in kopgroep
- Laat zien bij de top van de maplijst
- Map weergave klasse
- Geen klasse
- 1e klasse
- 2e klasse
- Map peiling-klasse
- Geen
- 1e klasse
- 2e klasse
- Zelfde als weergave klasse
- Duw klasse map
- Geen klasse
- 1e klasse
- 2e klasse
- Zelfde als peiling-klasse
- Map notificatie klasse
- Geen klasse
- 1e klasse
- 2e klasse
- Zelfde als duw klasse
- Lokale berichten wissen
- Inkomende server
- Instellen van de inkomende mail server
- Uitgaande server
- Configureren van de uitgaande (SMTP) server
- Account naam
- Jouw naam
- Notificaties
- Trillen
- Trillen wanneer nieuwe e-mail binnenkomt
- Trillingspatroon
- Standaard
- Patroon 1
- Patroon 2
- Patroon 3
- Patroon 4
- Patroon 5
- Aantal trillingen
- Nieuwe e-mail beltoon
- Knipper LED
- Knipper LED wanneer e-mail binnenkomt
- Bericht samenstelling opties
- Samenstelling standaarden
- Stel standaard in voor: Van, Bcc en handtekening
- Beheer identiteiten
- Instellen alternatieve \'Van\' adressen en handtekeningen
- Beheer identiteiten
- Beheer identiteit
- Identiteit aanpassen
- Opslaan
- Nieuwe identiteit
- Bcc alle berichten naar
- Aanpassen
- Verplaats ophoog
- Verplaats omlaag
- Verplaats naar top / maak standaard
- Verwijder
- Identiteit beschrijving
- (Optioneel)
- Jouw naam
- (Optioneel)
- E-mailadres
- (Vereist)
- Antwoord adres
- (Optioneel)
- Handtekening
- (Optioneel)
- Gebruik handtekening
- Handtekening
- Initiële identiteit
- Kies identiteit
- Verzenden als
- Je kunt je eigen identiteit niet verwijderen
- Je kunt een identiteit niet gebruiken zonder e-mailadres
- Oudste berichten eerst
- Nieuwste berichten eerst
- Onderwerp alfabetische volgorde
- Onderwerp omgekeerd alfabetische volgorde
- Verzender alfabetisch
- Verzender omgekeerd alfabetisch
- Berichten met ster eerst
- Berichten zonder ster eerst
- Ongelezen berichten eerst
- Gelezen berichten eerst
- Berichten met bijlagen eerst
- Berichten zonder bijlagen eerst
- Sorteren volgens …
- Datum
- Aankomst
- Onderwerp
- Verzender
- Ster
- Gelezen/ongelezen
- Bijlagen
- Account verwijderen
- Onbekend Certificaat
- Accepteer Sleutel
- Verwerp Sleutel
- Del (of D) - Verwijderen\nR - Antwoord\nA - Iedereen antwoorden\nC - Opstellen\nF - Doorsturen\nM - Verplaatsen\nV - Archiveren\nY - Kopiëren\nZ - Markeer (on)gelezen\nG - Ster\nO - Sorteer type\nI - Sorteer volgorde\nQ - Terug naar Mappen\nS - Selecteer/deselecteer\nJ of P - Vorig bericht\nK of N - Volgende bericht
- Del (of D) - Verwijderen\nC - Opstellen\nM - Verplaatsen\nV - Archiveren\nY - Kopiëren\nZ - Markeer (on)gelezen\nG - Ster\nO - Sorteer type\nI - Sorteer volgorde\nQ - Terug naar Mappen\nS - Selecteer/deselecteer
- Mapnaam bevat
- Toon mappen…
- Laat alle mappen zien
- 1e klasse mappen
- 1e & 2e klasse mappen
- 2e klasse mappen verbergen
- Positie handtekening
- Voor geciteerd bericht
- Na geciteerd bericht
- Gebruik app thema
- Donker
- Licht
- Gebruik systeemstandaard
- Weergave
- Algemeen
- Debuggen
- Privacy
- Netwerk
- Interactie
- Accountlijst
- Berichtlijsten
- Berichten
- Thema
- Thema om berichten te zien
- Thema om berichten te schrijven
- Taal
- Geen instellingen gevonden
- Vast berichten thema
- Kies het thema tijdens het bekijken van het bericht
- Gebruik een vast thema om het bericht te bekijken
- Volgens systeem
- Achtergrond sync
- Nooit
- Altijd
- Als \'Auto-sync\' geselecteerd is
- Selecteer alle
- Max mappen om te controleren met duw
- 5 mappen
- 10 mappen
- 25 mappen
- 50 mappen
- 100 mappen
- 250 mappen
- 500 mappen
- 1000 mappen
- Animatie
- Gebruik opzichtige visuele effecten
- Volume op/neer navigatie
- Bericht beeld
- Variabele lijst weergave
- Gecombineerde postvak-in weergeven
- %s%s
- - Ongelezen
- Alle berichten
- Alle berichten in doorzoekbare mappen
- Gecombineerde postvak-in
- Alle berichten in gecombineerde mappen
- Combineer
- Alle berichten worden in de gecombineerde postvak-in weergegeven
- Mappen om te doorzoeken
- Alle
- Zichtbare
- Geen
- Geen
- Automatisch (%s)
- Lettergrootte
- Instellen lettergrootte
- Account lijst
- Account naam
- Account beschrijving
- Maplijsten
- Mapnaam
- Mapstatus
- Bericht lijsten
- Bericht onderwerp
- Bericht afzender
- Bericht datum
- Voorbeeldweergave
- Beeld berichten
- Bericht afzender
- Bericht ontvanger (Aan)
- Bericht ontvanger (CC)
- Bericht ontvanger (BCC)
- Extra koppen
- Bericht onderwerp
- Bericht tijd en datum
- Bericht inhoud
- %d%%
- %1$s: %2$s
- Bericht opstellen
- Tekst invoervelden
- Standaard
- Kleinste
- Heel klein
- Kleiner
- Klein
- Gemiddeld
- Groot
- Groter
- Geen geschikte applicatie gevonden voor deze actie.
- Verzenden mislukt: %s
- Concept opslaan?
- Opslaan of negeren van dit bericht?
- Wijzigingen opslaan of verwerpen?
- Bericht afbreken?
- Weet je zeker dat je dit bericht wilt verwijderen?
- Selecteer tekst om te kopiëren.
- Lokale berichten wissen?
- Dit zal alle lokale berichten uit deze map wissen. Er worden geen berichten van de server gewist.
- Berichten wissen
- Bevestig verwijderen
- Wil je dit bericht verwijderen?
-
- Wil je dit bericht echt verwijderen?
- Wil je echt %1$d berichten verwijderen?
-
- Ja
- Nee
- Markeer alles als gelezen
- Wil je alle berichten markeren als gelezen?
- Bevestig prullenbak legen
- Wil je de prullenbak leeg maken?
- Ja
- Nee
- Bevestig verplaatsing naar spam map
-
- Wil je dit bericht echt verplaatsen naar de spam map\?
- Wil je deze %1$d berichten verplaatsen naar de spam map\?
-
- Ja
- Nee
- Bijlage wordt opgehaald
- »
- ›
- Back-up
- Diversen
- Exporteer instellingen
- Export
- Delen
- Instellingen exporteren …
- Instellingen succesvol geëxporteerd
- Instellingen exporteren mislukt
- Instellingen importeren
- Bestand selecteren
- Importeren
- Instellingen succesvol geïmporteerd
- Voer de wachtwoorden in
- Instellingen importeren mislukt
- Lezen van instellingenbestand mislukt
- Importeren van sommige instellingen mislukt
- Succesvol geïmporteerd
- Wachtwoord vereist
- Niet geïmporteerd
- Importeren mislukt
- Later
- Import instellingen
- Instellingen importeren …
-
- Om het account \"%s\" te gebruiken moet je het wachtwoord van de server opgeven.
- Om het account \"%s\" te gebruiken moet je de wachtwoorden van de server opgeven.
-
- Binnenkomendeserver-wachtwoord
- Uitgaandeserver-wachtwoord
- Gebruik het zelfde wachtwoord voor de uitgaandeserver
- Servernaam: %s
- Ongelezen aantal weergeven voor …
- Account
- Het account waarvan het aantal ongelezen berichten wordt getoond
- Gecombineerde postvak-in
- Map aantal
- Toon het aantal ongelezen berichten van een enkele map
- Map
- De map waarbij ongelezen berichten wordt getoond
- Klaar
- %1$s - %2$s
- Geen account gekozen
- Geen folder gekozen
- Geen tekst
- Open link
- Deel link
- Kopieer link naar klembord
- Link
- Verwijzingstekst naar klembord kopiëren
- Verwijzingstekst
- Afbeelding
- Afbeelding laten zien
- Afbeelding opslaan
- Afbeelding downloaden
- Kopieer afbeelding-URL naar klembord
- Afbeelding-URL
- Bel nummer
- Opslaan in contacten
- Kopieer nummer naar klembord
- Telefoonnummer
- Stuur e-mail
- Opslaan in contacten
- Kopieer adres naar klembord
- E-mailadres
- Alle
- 10
- 25
- 50
- 100
- 250
- 500
- 1000
- Server zoek limiet
- Zoekopdracht naar server sturen
-
- %d resultaat ophalen
- %d berichten ophalen
-
-
- %1$d van %2$d resultaten ophalen
- %1$d van %2$d resultaten ophalen
-
- Zoekopdracht mislukt
- Zoeken
- Server zoeken toestaan
- Zoek berichten op de server en op je eigen toestel
- Zoek berichten op server
- Er is een netwerk verbinding nodig voor server zoeken.
- Kleur wijzigen na lezen
- Een andere achtergrond laat zien nadat de e-mail is gelezen
- Gesprek overzicht
- Groepeer berichten per gesprek
- Gegevensbanken bijwerken
- Gegevensbanken aan het bijwerken …
- Gegevensbanken van account \"%s\" bijwerken
- Gespleten-scherm laten zien
- Altijd
- Nooit
- In landschap oriëntatie
- Selecteer een bericht aan de linker kant
- Laat contactafbeeldingen zien
- Laat contactafbeeldingen in de berichtenlijst zien
- Markeer alles als gelezen
- Contactafbeeldingen kleuren
- Voorzie afwezige contactafbeeldingen van een kleur
- Zichtbare berichtacties
- Toon de geselecteerde acties in het Berichten-menu
- Bijlage aan het laden …
- Bericht wordt verstuurd
- Concept wordt opgeslagen
- Bijlage aan het ophalen …
- Kan niet authenticeren. De server ondersteunt geen SASL EXTERNAL. Dit kan komen door een probleem met het client certificaat (verlopen of onbekende CA) of een ander configuratieprobleem.
-
- Gebruik client certificaat
- Geen client certificaat
- Verwijder client certificaat selectie
- Client certificaat niet ontvangen voor alias \"%s\"
- Geavanceerde opties
- Client certificaat \"%1$s\" is verlopen of niet geldig (%2$s)
-
- *Versleuteld*
- Toevoegen vanuit contacten
- CC
- BCC
- Aan
- Van
- <Onbekende ontvanger>
- <Onbekende verzender>
- Privé
- Werk
- Anders
- Mobiel
- Geen concepten map ingesteld voor dit account!
- Geen sleutel ingesteld voor dit account! Controleer je instellingen.
- Crypto provider gebruikt incompatibele versie. Controleer je instellingen!
- Kan geen verbinding maken met de crypto provider. Controleer de instellingen of klik op het crypto icoontje om nogmaals te proberen!
- Initialisatie van eind-tot-eind versleuteling is mislukt, controleer de instellingen
- PGP/MIME instelling ondersteund geen bijlagen!
- PGP/INLINE toestaan
- PGP/INLINE uitzetten
- PGP handtekening toestaan
- PGP handtekening stoppen
- PGP/INLINE instellingen
- De e-mail is verzonden in PGP/INLINE formaat.\nDit word alleen gebruikt voor compatibiliteit:
- Sommige clients ondersteunen alleen dit formaat
- Handtekening kan onderweg breken
- Bijlagen zijn niet ondersteund
- Begrepen!
- Uitzetten
- Keep toestaan
- Begrepen!
- Uitzetten
- Houd ingeschakeld
- alleen PGP handtekening modes
- In deze mode wordt je PGP sleutel gebruikt voor een cryptografische handtekening of een ongecodeerde email.
- Dit versleutelt niet de e-mail, maar controleert dat je eigen sleutel gebruikt is.
- Handtekeningen kunnen breken wanneer ze naar mailinglijsten worden gestuurd.
- Handtekening kan worden weergegeven als \'signature.asc\' bijlage bij sommige programma\'s.
- Versleutelde berichten bevatten altijd een handtekening.
- Platte tekst
- eind-tot-eind handtekening bevat een fout
- moet bericht volledig downloaden om handtekening te verwerken
- bevat niet ondersteunde eind-tot-eind handtekening
- Bericht is versleuteld, maar in een niet ondersteund formaat.
- Bericht is versleuteld, maar ontsleuteling was gestopt.
- Eind-tot-eind getekende platte tekst
- van geverifieerde ondertekenaar
- Platte tekst getekend
- maar eind-tot-eind sleutel komt niet overeen met afzender
- maar eind-tot-eind sleutel is verlopen
- maar eind-tot-eind sleutel is ingetrokken
- maar eind-tot-eind sleutel is niet veilig
- van een onbekende eind-tot-eind sleutel
- Versleuteld
- maar er is een ontsleutel fout opgetreden
- moet bericht volledig downloaden voor ontsleuteling
- maar er is geen crypto app geconfigureerd
- Versleuteld
- maar niet eind-tot-eind
- Eind-tot-eind versleuteld
- van geverifieerde afzender
- Versleuteld
- van een onbekende eind-tot-eind sleutel
- maar eind-tot-eind sleutel komt niet overeen met afzender
- maar eind-tot-eind sleutel is verlopen
- maar eind-tot-eind sleutel is ingetrokken
- maar eind-tot-eind sleutel is niet veilig
- maar eind-tot-eind gegevens bevatten fouten
- maar versleuteling is niet veilig
- OK
- Zoek Sleutel
- Bekijk ondertekenaar
- Bekijk afzender
- Details
- Deblokkeer
- Dit onderdeel is niet versleuteld en is misschien niet veilig.
- Onbeveiligde Bijlage
- Laden …
- Decryptie is geannuleerd.
- Opnieuw
- Versleuteld bericht moet gedownload worden om te decrypten.
- Fout tijdens ontsleutelen e-mail
- Speciale leestekens worden nog niet ondersteund!
- Fout bij parsen van adres!
- Verberg niet-versleutelde handtekeningen
- Alleen versleutelde handtekeningen worden weergegeven
- Alle handtekeningen worden weergegeven
- Versleuteling niet mogelijk in sign-only mode!
- niet getekende tekst
- APG Deprecation Waarschuwing
- APG is niet langer beheerd!
- Hierdoor is de ondersteuning voor APG verwijderd binnen K-9 mail.
- De ontwikkeling is gestopt in 2014
- Bevat onveilige items
- Klik hier voor meer informatie.
- Begrepen!
- APG
- Deze e-mail is versleuteld
- Deze e-mail is OpenPGP versleuteld.\nInstalleer en stel een OpenPGP App in om de e-mail te lezen.
- Ga naar Instellingen
- Mail Message List
- Berichten aan het laden …
- Ophalen maplijst niet gelukt
- Fout bij ophalen status van de OpenPGP provider!
- Versleuteling niet mogelijk
- Sommige van de ontvangers ondersteunen deze functie niet!
- Versleuteling inschakelen
- Versleuteling uitschakelen
- Versleuteling maakt dat berichten alleen door de ontvanger gelezen kunnen worden, en niemand anders.
- Versleuteling is alleen beschikbaar als alle ontvangers dit ondersteunen, en zij moeten U eerder reeds een e-mail gestuurd hebben.
- Schakel versleuteling aan of uit met dit icoon.
- Ik snap het
- Terug
- Versleuteling uitschakelen
- OpenPGP-versleuteling
- Autocrypt wederzijdse modus
- Autocrypt wederzijdse modus
- Berichten worden op verzoek versleuteld, of bij beantwoorden van een versleuteld bericht.
- Als verzender en ontvanger de wederzijdse modus inschakelen dan wordt standaard versleuteling ingesteld.
- Klik hier voor meer informatie.
- Algemene instellingen
- Geen OpenPGP app geïnstalleerd
- Installeren
- Mail vereist OpenKeychain voor end-to-end encryptie.
- Versleuteld bericht
- Onderwerp van bericht versleutelen
- Mogelijk niet ondersteund door alle ontvangers
- Interne fout: Ongeldig account!
- Fout bij het verbinden met %s!
- Verstuur Autocrypt Setup bericht
- Veilig delen van eind-tot-eind instellingen met andere apparaten
- Autocrypt Setup bericht
- Een Autocrypt Setup bericht deelt uw eind-tot-eind instellingen op een beveiligde manier met andere apparaten.
- Verstuur Setup bericht
- Het bericht wordt verstuurd naar uw adres:
- Instelbericht aan het genereren…
- Bericht sturen naar:
- Om te voltooien, open het bericht op uw andere apparaat en voer de Setup Code in.
- Setup Code weergeven
- Autocrypt Setup bericht
- Dit bericht bevat alle informatie om uw Autocrypt instellingen met geheime sleutel beveiligd over te brengen vanaf uw oorspronkelijke apparaat.
+
+
+
+ Mail
+ Mail Accounts
+ Mail Ongelezen
+
+ Het Mail ontwikkelingsteam
+ Broncode
+ Apache Licentie, versie 2.0
+ Openbroncode-project
+ Website
+ Gebruikersforum
+ Fediverse
+ Twitter
+ Bibliotheken
+ Licentie
+ Wijzigingslog
+ Het laden van het wijzigingslog is mislukt.
+ Versie %s
+ Wat is er nieuw
+ Laat veranderingen zien als de app recent is bijgewerkt
+ Ontdek wat er nieuw is in deze versie
+
+ Welkom bij Mail
+
+ -- \nVerstuurd vanaf mijn Android apparaat met Mail.
+
+ Het account \"%s\" wordt verwijderd uit Mail.
+
+ Auteurs
+ Over Mail
+ Accounts
+ Geavanceerd
+ Nieuw bericht
+ Antwoorden
+ Allen antwoorden
+ Doorsturen
+ Doorsturen als bijlage
+ Kies account
+ Kies een map
+ %d geselecteerd
+ Volgende
+ Vorige
+
+ OK
+ Annuleren
+ Verzenden
+ Er is geen onderwerp ingevuld. Tik nogmaals om toch te versturen.
+ Antwoorden
+ Iedereen antwoorden
+ Verwijderen
+ Archief
+ Spam
+ Doorsturen
+ Doorsturen als bijlage
+ Bewerken als nieuw bericht
+ Verplaatsen
+ Verplaatsen naar concepten
+ Verzenden …
+ Opnieuw opslaan …
+ Klaar
+ Vergeten
+ Opslaan als concept
+ Controleer e-mail
+ Berichten versturen
+ Maplijst verversen
+ Map vinden
+ Account toevoegen
+ Nieuw bericht
+ Zoeken
+ Overal zoeken
+ Zoekresultaten
+ Instellingen
+ Mappen beheren
+ Accountinstellingen
+ Verwijder account
+ Markeer als gelezen
+ Delen
+ Kies afzender
+ Ster toevoegen
+ Ster verwijderen
+ Kopieer
+ Uitschrijven
+ Laat koppen zien
+
+ Adres naar klembord gekopieerd
+ Adressen naar klembord gekopieerd
+
+ De onderwerpsregel is naar het klembord gekopieerd
+ Schakel naar donker thema
+ Schakel naar licht thema
+ Markeer als ongelezen
+ Lees rapport
+ Lees rapport vragen
+ Geen lees rapport vragen
+ Voeg bijlage toe
+ Prullenbak legen
+ Wissen
+ Over
+ Instellingen
+
+ (Geen onderwerp)
+ Geen afzender
+ Berichten aan het laden\u2026
+ Netwerkfout
+ Bericht niet gevonden
+ Bericht laden is mislukt
+ Volgende %d berichten
+ %.1f GB
+ %.1f MB
+ %.1f kB
+ %d B
+ Nieuw bericht
+
+ %d nieuwe berichten
+ %d nieuwe berichten
+
+ %d Ongelezen (%s)
+ + %1$d meer bij %2$s
+ Antwoorden
+ Gelezen
+ Alles als gelezen markeren
+ Verwijderen
+ Alles verwijderen
+ Archiveren
+ Alles Archiveren
+ Spam
+ Certificaat fout voor %s
+ Controleer de server instellingen
+ Authenticatie mislukt
+ Authenticatie mislukt voor %s. Werk de server instellingen bij.
+ Controleer berichten: %s:%s
+ Controleren berichten
+ Versturen van berichten: %s
+ Berichten versturen
+ :
+ Synchroniseren (Push)
+ Wordt weergeven in afwachting op nieuwe berichten
+ Beeld berichten
+ Meldingen gerelateerd aan berichten
+ Diversen
+ Overige meldingen zoals fouten en dergelijke.
+ Postvak-in
+ Postvak-uit
+ Concepten
+ Prullenbak
+ Verzonden
+ Fout bij verzenden van berichten
+ Versie
+ Debug log toestaan
+ Log extra diagnostische informatie
+ Log gevoelige informatie
+ Kan wachtwoorden laten zien in logs.
+ Laad meer berichten
+ Aan:%s
+ Onderwerp
+ Bericht tekst
+ Handtekening
+ -------- Origineel bericht --------
+ Onderwerp:
+ Verzonden:
+ Van:
+ Aan:
+ CC:
+ %s schreef:
+ %2$s schreef op %1$s:
+ Voeg minimaal 1 ontvanger toe.
+ De ontvanger is niet (volledig) ingevuld!
+ Geen e-mailadres gevonden.
+ Sommige bijlagen kunnen niet worden doorgestuurd omdat ze niet zijn gedownload.
+ Dit bericht kan niet worden doorgestuurd omdat bijlagen niet zijn gedownload.
+ Geciteerd bericht bijvoegen
+ Citaattekst verwijderen
+ Citaattekst bewerken
+ Bijlage verwijderen
+ Van: %s <%s>
+ Aan:
+ CC:
+ BCC:
+ Kan bijlage niet opslaan.
+ Laat afbeeldingen zien
+ Niet in staat viewer te vinden voor %s.
+ Download compleet bericht
+ via %1$s
+ Meer van deze afzender
+ Van %s
+ Bericht verwijderd
+ Bericht opgeslagen als concept
+ Sterren weergeven
+ Sterren geven gemarkeerde berichten aan
+ Preview regels
+ Toon naam bij bericht
+ Geef bij voorkeur naam van afzender/geadresseerde weer i.p.v. e-mailadres
+ Correspondent boven onderwerp
+ Correspondent namen boven de onderwerp-regel, niet eronder
+ Toon naam uit contactenlijst
+ Gebruik naam uit het adresboek
+ Kleuren contacten
+ Kleur de namen in jouw contact lijst
+ Vaste breedte lettertypen
+ Gebruik een lettertype met vaste breedte bij het weergeven van plain-tekstberichten
+ Berichten auto-passen
+ Berichten passend maken op het scherm
+ Terug naar lijst na verwijderen
+ Terug naar berichtenlijst na bericht verwijderen
+ Volgend bericht laten zien na verwijderen
+ Standaard volgend bericht laten zien na verwijderen
+ Bevestig acties
+ Toon een dialoogvenster altijd wanneer je de geselecteerde acties uitvoert
+ Verwijder
+ Berichten met een ster verwijderen (in berichtweergave)
+ Spam
+ Bericht afbreken
+ Markeer alles als gelezen
+ Verwijder (van notificaties)
+ Mail client verbergen
+ Mail gebruikersagent van e-mailkoppen verwijderen
+ Tijdzone verbergen
+ Gebruik UTC in plaats van de lokale tijdzone in de e-mailkoppen en bij het antwoorden van e-mail
+ Toon \'Verwijder\' knop
+ Nooit
+ Notificatie voor los bericht
+ Altijd
+ Start scherm notificatie
+ Geen start scherm notificatie
+ Applicatienaam
+ Aantal nieuwe berichten
+ Berichten teller (ook verzonden)
+ Hetzelfde. Ook na schermontgrendeling
+ Stilteperiode
+ Schakel beltoon, trillen en leds uit gedurende de nacht
+ Notificaties uitzetten
+ Notificaties volledig uitzetten tijdens stilteperiode
+ Stilteperiode start
+ Stilteperiode einde
+ Een nieuw account instellen
+ E-mailadres
+ Wachtwoord
+ Om met Mail dit e-mailaccount te gebruiken moet je inloggen om K-9 toegang te geven tot je e-mails.
+
+ Inloggen
+
+ Inloggen met Google
+
+ Handmatige setup
+
+ Ophalen account informatie\u2026
+ Controleren van inkomende serverinstellingen\u2026
+ Controleren van uitgaande serverinstellingen\u2026
+ Authenticatie\u2026
+ Accountinstellingen worden opgehaald\u2026
+ Annuleren\u2026
+ Bijna klaar!
+ Geef dit account een naam (optioneel):
+ Typ je naam (zichtbaar bij uitgaande berichten):
+ Accounttype
+ Welk type account is dit?
+ POP3
+ IMAP
+ Normaal wachtwoord
+ Wachtwoord, onveilig verzonden
+ Versleuteld wachtwoord
+ Client certificaat
+ OAuth 2.0
+ Inkomende server instellingen
+ Gebruikersnaam
+ Wachtwoord
+ Client certificaat
+ POP3-server
+ IMAP-server
+ WebDAV (Exchange) server
+ Poort
+ Beveiligingstype
+ Authenticatietype
+ Geen
+ SSL/TLS
+ STARTTLS
+ \"%1$s = %2$s\" is niet geldig met \"%3$s = %4$s\"
+ Wanneer ik een bericht verwijder
+ Verwijder niet van server
+ Verwijder van server
+ Markeer als gelezen op server
+ Compressie gebruiken
+ Wissen berichten
+ Onmiddellijk na verwijderen of verplaatsen
+ Tijdens iedere peiling
+ Alleen handmatig
+ IMAP-namespace automatisch detecteren
+ IMAP-pad voorvoegsel
+ Conceptmap
+ Verzendmap
+ Prullenbakmap
+ Archiefmap
+ Spammap
+ Alleen geabonneerde mappen weergeven
+ Map automatisch uitvouwen
+ WebDAV (Exchange) path
+ Authenticatie pad
+ Mailbox alias
+ Uitgaande server instellingen
+ SMTP-server
+ Poort
+ Beveiligingstype
+ Inloggen vereist.
+ Gebruikersnaam
+ Wachtwoord
+ Authenticatietype
+ \"%1$s = %2$s\" is niet geldig met \"%3$s = %4$s\"
+ Ongeldige setup: %s
+ Accountopties
+ Map-peiling-frequentie
+ Nooit
+ Elke 15 minuten
+ Elke 30 minuten
+ Elk uur
+ Elke 2 uur
+ Elke 3 uur
+ Elke 6 uur
+ Elke 12 uur
+ Elke 24 uur
+ Inactieve verbinding vernieuwen
+ Elke 2 minuten
+ Elke 3 minuten
+ Elke 6 minuten
+ Elke 12 minuten
+ Elke 24 minuten
+ Elke 36 minuten
+ Elke 48 minuten
+ Elke 60 minuten
+ Waarschuw me wanneer nieuwe e-mail binnenkomt
+ Aantal berichten om te laten zien
+ 10 berichten
+ 25 berichten
+ 50 berichten
+ 100 berichten
+ 250 berichten
+ 500 berichten
+ 1000 berichten
+ 2500 berichten
+ 5000 berichten
+ 10000 berichten
+ alle berichten
+ Kan bericht niet kopiëren of verplaatsen omdat deze niet gesynchroniseerd is met de server
+ Setup kon niet afronden
+ Gebruikersnaam of wachtwoord ongeldig.\n(%s)
+ De server presenteerde een ongeldig SSL certificaat. Dit kan komen door een verkeerd geconfigureerde server. Dit kan ook komen doordat iemand jouw mailserver aan probeert te vallen. Als je niet zeker weet wat er aan de hand is klik dan op Reject en neem contact op met de beheerder van de mailserver.\n\n(%s)
+ Kan geen verbinding met server maken.\n(%s)
+ Autorisatie is geannuleerd
+ Autorisatie is mislukt met de volgende foutmelding: %s
+ OAuth 2.0 wordt momenteel niet ondersteund door deze dienstverlener.
+ Deze app kan geen browser vinden. Een browser is nodig om toegang te geven tot je account.
+ Details aanpassen
+ Doorgaan
+ Geavanceerd
+ Algemene instellingen
+ Nieuwe e-mail notificatie
+ Notificatie mappen
+ Alles
+ Alleen 1e klasse mappen
+ 1e en 2e klasse mappen
+ Alle behalve 2e klasse mappen
+ Geen
+ Synchronisatiemeldingen
+ Je e-mailadres
+ Notificatie in statusbalk wanneer nieuwe e-mail binnenkomt
+ Notificatie in statusbalk wanneer wordt gecontroleerd op nieuwe e-mail
+ Notificatie voor e-mail welke ik verstuur
+ Notificatie ook voor e-mail verzonden vanaf een identiteit
+ Alleen contacten
+ Notificaties alleen weergeven voor bekende contacten
+ Markeer als gelezen als geopend
+ Markeer als gelezen wanneer ingezien
+ Als gelezen markeren bij wissen
+ Markeer een bericht als gelezen wanneer die wordt gewist
+ Laat afbeeldingen automatisch zien
+ Nooit
+ Alleen van contacten
+ Altijd
+ Berichten versturen
+ Citeer bericht bij antwoorden
+ Neem orginele bericht mee in het antwoord.
+ Antwoorden na citaat
+ Wanneer je antwoord op een bericht, zal het originele bericht boven jouw antwoord staan.
+ Verwijder handtekening bij reactie
+ Handtekeningen worden verwijderd bij geciteerde berichten
+ Berichtopmaak
+ Platte Tekst (plaatjes en opmaak worden verwijderd)
+ HTML (plaatjes en opmaak blijven behouden)
+ Automatisch
+ Altijd Cc/Bcc laten zien
+ Leesbevestiging
+ Altijd een leesbevestiging vragen
+ Quotestijl bij antwoorden
+ Prefix (zoals Gmail, Pine)
+ Kop (zoals Outlook)
+ Upload verzonden berichten
+ Upload berichten naar \'Verzonden\' map na verzenden
+ Algemene instellingen
+ Berichten lezen
+ Berichten ophalen
+ Mappen
+ Citaat voorvoegsel
+ Eind-tot-eind versleuteling
+ OpenPGP ondersteuning inschakelen
+ OpenPGP app selecteren
+ Eind-tot-eind sleutel instellen
+ Geen OpenPGP-app ingesteld
+ Verbonden met %s
+ Aan het configureren …
+ Sla alle concepten versleuteld op.
+ Alle concepten zullen versleuteld worden opgeslagen
+ Versleutel concepten alleen als versleuteling is ingeschakeld
+ Map-peiling-frequentie
+ Accountkleur
+ De kleur van dit account gebruikt bij mappen en accountlijst
+ Grootte van lokale map
+ Automatisch berichten downloaden tot
+ 1 KiB
+ 2 KiB
+ 4 KiB
+ 8 KiB
+ 16 KiB
+ 32 KiB
+ 64 KiB
+ 128 KiB
+ 256 KiB
+ 512 KiB
+ 1 MiB
+ 2 MiB
+ 5 MiB
+ 10 MiB
+ elke grootte (geen limiet)
+ Sync berichten van
+ alles (geen limiet)
+ vandaag
+ laatste 2 dagen
+ laatste 3 dagen
+ afgelopen week
+ afgelopen 2 weken
+ afgelopen 3 weken
+ afgelopen maand
+ afgelopen 2 maanden
+ afgelopen 3 maanden
+ afgelopen 6 maanden
+ afgelopen jaar
+ Mappen om weer te geven
+ Alle
+ Alleen 1e klasse mappen
+ 1e en 2e klasse mappen
+ Alle behalve 2e klasse mappen
+ Peiling mappen
+ Alle
+ Alleen 1e klasse mappen
+ 1e en 2e klasse mappen
+ Alle behalve 2e klasse mappen
+ Geen
+ Push-mappen
+ Alle
+ Alleen 1e klasse mappen
+ 1e en 2e klasse mappen
+ Alle behalve 2e klasse mappen
+ Geen
+ Verplaats/kopieer doel mappen
+ Alle
+ Alleen 1e klasse mappen
+ 1e en 2e klasse mappen
+ Alle behalve 2e klasse mappen
+ Sync verwijderingen op server
+ Verwijder berichten wanneer verwijderd van server
+ OpenPGP app niet aanwezig - is de app verwijderd?
+ Map instellingen
+ Laat zien in kopgroep
+ Laat zien bij de top van de maplijst
+ Map weergave klasse
+ Geen klasse
+ 1e klasse
+ 2e klasse
+ Map peiling-klasse
+ Geen
+ 1e klasse
+ 2e klasse
+ Zelfde als weergave klasse
+ Push klasse map
+ Geen klasse
+ 1e klasse
+ 2e klasse
+ Zelfde als peiling-klasse
+ Map notificatie klasse
+ Geen klasse
+ 1e klasse
+ 2e klasse
+ Zelfde als push klasse
+ Lokale berichten wissen
+ Inkomende server
+ Instellen van de inkomende mail server
+ Uitgaande server
+ Configureren van de uitgaande (SMTP) server
+ Account naam
+ Jouw naam
+ Notificaties
+ Trillen
+ Tril-patroon
+ Standaard
+ Patroon 1
+ Patroon 2
+ Patroon 3
+ Patroon 4
+ Patroon 5
+ Aantal trillingen
+ Nieuwe e-mail beltoon
+ Bericht samenstelling opties
+ Samenstelling standaarden
+ Stel standaard in voor: Van, Bcc en handtekening
+ Beheer identiteiten
+ Instellen alternatieve \'Van\' adressen en handtekeningen
+ Beheer identiteiten
+ Beheer identiteit
+ Identiteit aanpassen
+ Bewaar
+ Nieuwe identiteit
+ Bcc alle berichten naar
+ Aanpassen
+ Verplaats ophoog
+ Verplaats omlaag
+ Verplaats naar top / maak standaard
+ Verwijder
+ Identiteit beschrijving
+ (Optioneel)
+ Jouw naam
+ (Optioneel)
+ E-mailadres
+ (Vereist)
+ Antwoordadres
+ (Optioneel)
+ Handtekening
+ (Optioneel)
+ Gebruik handtekening
+ Handtekening
+ Initiële identiteit
+ Kies identiteit
+ Verzenden als
+ Je kunt je eigen identiteit niet verwijderen
+ Je kunt een identiteit niet gebruiken zonder e-mailadres
+ Oudste berichten eerst
+ Nieuwste berichten eerst
+ Onderwerp alfabetische volgorde
+ Onderwerp omgekeerd alfabetische volgorde
+ Verzender alfabetisch
+ Verzender omgekeerd alfabetisch
+ Berichten met ster eerst
+ Berichten zonder ster eerst
+ Ongelezen berichten eerst
+ Gelezen berichten eerst
+ Berichten met bijlagen eerst
+ Berichten zonder bijlagen eerst
+ Sorteren volgens …
+ Datum
+ Aankomst
+ Onderwerp
+ Verzender
+ Ster
+ Gelezen/ongelezen
+ Bijlagen
+ Account verwijderen
+ Onbekend certificaat
+ Accepteer Sleutel
+ Verwerp Sleutel
+ Del (of D) - Verwijderen\nR - Antwoord\nA - Iedereen antwoorden\nC - Opstellen\nF - Doorsturen\nM - Verplaatsen\nV - Archiveren\nY - Kopiëren\nZ - Markeer (on)gelezen\nG - Ster\nO - Sorteer type\nI - Sorteer volgorde\nQ - Terug naar Mappen\nS - Selecteer/deselecteer\nJ of P - Vorig bericht\nK of N - Volgende bericht
+ Del (of D) - Verwijderen\nC - Opstellen\nM - Verplaatsen\nV - Archiveren\nY - Kopiëren\nZ - Markeer (on)gelezen\nG - Ster\nO - Sorteer type\nI - Sorteer volgorde\nQ - Terug naar Mappen\nS - Selecteer/deselecteer
+ Mapnaam bevat
+ Mappen
+ Laat alle mappen zien
+ 1e klasse mappen
+ 1e & 2e klasse mappen
+ 2e klasse mappen verbergen
+ Positie handtekening
+ Voor geciteerd bericht
+ Na geciteerd bericht
+ Gebruik app thema
+ Donker
+ Licht
+ Gebruik systeemstandaard
+ Algemene instellingen
+ Algemeen
+ Debuggen
+ Privacy
+ Netwerk
+ Interactie
+ Accountlijst
+ Berichtlijsten
+ Berichten
+ Thema
+ Thema om berichten te zien
+ Thema om berichten te schrijven
+ Taal
+ Geen instellingen gevonden
+ Vast berichten thema
+ Kies het thema tijdens het bekijken van het bericht
+ Gebruik een vast thema om het bericht te bekijken
+ Systeemstandaard
+ Achtergrond sync
+ Nooit
+ Altijd
+ Als \'Auto-sync\' geselecteerd is
+ Selecteer alle
+ Max mappen om te controleren met push
+ 5 mappen
+ 10 mappen
+ 25 mappen
+ 50 mappen
+ 100 mappen
+ 250 mappen
+ 500 mappen
+ 1000 mappen
+ Animatie
+ Gebruik opzichtige visuele effecten
+ Volume op/neer navigatie
+ Bericht beeld
+ Variabele lijst weergave
+ Gecombineerde postvak-in weergeven
+ Gecombineerde postvak-in
+ Alle berichten in gecombineerde mappen
+ Combineer
+ Alle berichten worden in de gecombineerde postvak-in weergegeven
+ Mappen om te doorzoeken
+ Alle
+ Zichtbare
+ Geen
+ Geen
+ Automatisch (%s)
+ Lettergrootte
+ Instellen lettergrootte
+ Account lijst
+ Accountnaam
+ Account beschrijving
+ Maplijsten
+ Mapnaam
+ Mapstatus
+ Bericht lijsten
+ Bericht onderwerp
+ Bericht afzender
+ Bericht datum
+ Preview
+ Beeld berichten
+ Bericht afzender
+ Bericht ontvanger (Aan)
+ Bericht ontvanger (CC)
+ Bericht ontvanger (BCC)
+ Extra koppen
+ Bericht onderwerp
+ Bericht tijd en datum
+ Bericht inhoud
+ Bericht opstellen
+ Tekst invoervelden
+ Standaard
+ Kleinste
+ Heel klein
+ Kleiner
+ Klein
+ Gemiddeld
+ Groot
+ Groter
+ Geen geschikte applicatie gevonden voor deze actie.
+ Verzenden mislukt: %s
+ Concept opslaan?
+ Opslaan of negeren van dit bericht?
+ Wijzigingen opslaan of verwerpen?
+ Bericht afbreken?
+ Weet je zeker dat je dit bericht wilt verwijderen?
+ Selecteer tekst om te kopiëren.
+ Lokale berichten wissen?
+ Dit zal alle lokale berichten uit deze map wissen. Er worden geen berichten van de server gewist.
+ Berichten wissen
+ Bevestig verwijderen
+ Wil je dit bericht verwijderen?
+
+ Wil je dit bericht echt verwijderen?
+ Wil je echt %1$d berichten verwijderen?
+
+ Ja
+ Nee
+ Markeer alles als gelezen
+ Wil je alle berichten markeren als gelezen?
+ Bevestig prullenbak legen
+ Wil je de prullenbak leeg maken?
+ Ja
+ Nee
+ Bevestig verplaatsing naar spam map
+
+ Wil je dit bericht echt verplaatsen naar de spam map?
+ Wil je dit bericht echt verplaatsen naar de spam map?
+
+ Ja
+ Nee
+ Bijlage wordt opgehaald
+ »
+ ›
+ Back-up
+ Diversen
+ Exporteer instellingen
+ Export
+ Delen
+ Instellingen exporteren …
+ Instellingen succesvol geëxporteerd
+ Instellingen exporteren mislukt
+ Instellingen importeren
+ Bestand selecteren
+ Importeren
+ Instellingen succesvol geïmporteerd
+
+ Voer de wachtwoorden in
+
+ Log in
+
+ Log in en voer het wachtwoord in
+ Instellingen importeren mislukt
+ Lezen van instellingenbestand mislukt
+ Importeren van sommige instellingen mislukt
+ Succesvol geïmporteerd
+ Wachtwoord vereist
+
+ Inloggen vereist
+ Niet geïmporteerd
+ Importeren mislukt
+ Later
+ Import instellingen
+ Instellingen importeren …
+
+ Om het account \"%s\" te gebruiken moet je het wachtwoord van de server opgeven.
+ Om het account \"%s\" te gebruiken moet je de wachtwoorden van de server opgeven.
+
+ Binnenkomendeserver-wachtwoord
+ Uitgaandeserver-wachtwoord
+ Gebruik het zelfde wachtwoord voor de uitgaandeserver
+ Servernaam: %s
+ Ongelezen aantal weergeven voor …
+ Account
+ Het account waarvan het aantal ongelezen berichten wordt getoond
+ Gecombineerde postvak-in
+ Mapaantal
+ Toon het aantal ongelezen berichten van een enkele map
+ Map
+ De map waarbij ongelezen berichten wordt getoond
+ Klaar
+ %1$s - %2$s
+ Geen account gekozen
+ Geen folder gekozen
+ Geen tekst
+ Verwijzing openen
+ Verwijzing delen
+ Verwijzing naar klembord kopiëren
+ Verwijzing
+ Verwijzingstekst naar klembord kopiëren
+ Verwijzingstekst
+ Afbeelding
+ Afbeelding laten zien
+ Afbeelding opslaan
+ Afbeelding downloaden
+ Kopieer afbeelding-URL naar klembord
+ Afbeelding-URL
+ Nummer bellen
+ Opslaan in contacten
+ Kopieer nummer naar klembord
+ Telefoonnummer
+ Stuur e-mail
+ Opslaan in contacten
+ Kopieer adres naar klembord
+ E-mailadres
+ Alle
+ 10
+ 25
+ 50
+ 100
+ 250
+ 500
+ 1000
+ Server-zoeklimiet
+ Zoekopdracht naar server sturen
+
+ %d resultaat ophalen
+ %d berichten ophalen
+
+
+ %1$d van %2$d berichten ophalen
+ %1$d van %2$d berichten ophalen
+
+ Zoekopdracht mislukt
+ Zoeken
+ Berichten op server zoeken
+ Er is een netwerk verbinding nodig voor server zoeken.
+ Kleur wijzigen na lezen
+ Een andere achtergrond laat zien nadat de e-mail is gelezen
+ Gesprek overzicht
+ Groepeer berichten per gesprek
+ Gegevensbanken bijwerken
+ Gegevensbanken aan het bijwerken …
+ Gegevensbanken van account \"%s\" bijwerken
+ Gespleten-scherm laten zien
+ Altijd
+ Nooit
+ In landschap oriëntatie
+ Selecteer een bericht aan de linker kant
+ Laat contactafbeeldingen zien
+ Laat contactafbeeldingen in de berichtenlijst zien
+ Markeer alles als gelezen
+ Contactafbeeldingen kleuren
+ Voorzie afwezige contactafbeeldingen van een kleur
+ Zichtbare berichtacties
+ Toon de geselecteerde acties in het Berichten-menu
+ Bijlage aan het laden …
+ Bericht wordt verstuurd
+ Concept wordt opgeslagen
+ Bijlage aan het ophalen …
+ Kan niet authenticeren. De server ondersteunt geen SASL EXTERNAL. Dit kan komen door een probleem met het client certificaat (verlopen of onbekende CA) of een ander configuratieprobleem.
+
+ Gebruik client certificaat
+ Geen client certificaat
+ Verwijder client certificaat selectie
+ Client certificaat niet ontvangen voor alias \"%s\"
+ Geavanceerde opties
+ Client certificaat \"%1$s\" is verlopen of niet geldig (%2$s)
+
+ *Versleuteld*
+ Toevoegen vanuit contacten
+ CC
+ BCC
+ Aan
+ Van
+ <Onbekende ontvanger>
+ <Onbekende verzender>
+ Privé
+ Werk
+ Anders
+ Mobiel
+ Geen concepten map ingesteld voor dit account!
+ Geen sleutel ingesteld voor dit account! Controleer je instellingen.
+ Crypto provider gebruikt incompatibele versie. Controleer je instellingen!
+ Kan geen verbinding maken met de crypto provider. Controleer de instellingen of klik op het crypto icoontje om nogmaals te proberen.
+ Initialisatie van eind-tot-eind versleuteling is mislukt, controleer de instellingen
+ PGP/MIME instelling ondersteund geen bijlagen!
+ PGP/INLINE toestaan
+ PGP/INLINE uitzetten
+ PGP handtekening toestaan
+ PGP handtekening stoppen
+ PGP/INLINE instellingen
+ De e-mail is verzonden in PGP/INLINE formaat.\nDit word alleen gebruikt voor compatibiliteit:
+ Sommige clients ondersteunen alleen dit formaat
+ Handtekening kan onderweg breken
+ Bijlagen zijn niet ondersteund
+ Begrepen!
+ Uitzetten
+ Keep toestaan
+ Begrepen!
+ Uitzetten
+ Houd ingeschakeld
+ alleen PGP handtekening modes
+ In deze mode wordt je PGP sleutel gebruikt voor een cryptografische handtekening of een ongecodeerde email.
+ Dit versleutelt niet de e-mail, maar controleert dat je eigen sleutel gebruikt is.
+ Handtekening kan bij versturing naar verzendlijst breken
+ Handtekening kan worden weergegeven als \'signature.asc\' bijlage bij sommige programma\'s
+ Versleutelde berichten bevatten altijd een handtekening.
+ Platte tekst
+ eind-tot-eind handtekening bevat een fout
+ moet bericht volledig downloaden om handtekening te verwerken
+ bevat niet ondersteunde eind-tot-eind handtekening
+ Bericht is versleuteld, maar in een niet ondersteund formaat.
+ Bericht is versleuteld, maar ontsleuteling was gestopt.
+ Eind-tot-eind getekende platte tekst
+ van geverifieerde ondertekenaar
+ Platte tekst getekend
+ maar eind-tot-eind sleutel komt niet overeen met afzender
+ maar eind-tot-eind sleutel is verlopen
+ maar eind-tot-eind sleutel is ingetrokken
+ maar eind-tot-eind sleutel is niet veilig
+ van een onbekende eind-tot-eind sleutel
+ Versleuteld
+ maar er is een ontsleutel fout opgetreden
+ moet bericht volledig downloaden voor ontsleuteling
+ maar er is geen crypto app geconfigureerd
+ Versleuteld
+ maar niet eind-tot-eind
+ Eind-tot-eind versleuteld
+ van geverifieerde afzender
+ Versleuteld
+ van een onbekende eind-tot-eind sleutel
+ maar eind-tot-eind sleutel komt niet overeen met afzender
+ maar eind-tot-eind sleutel is verlopen
+ maar eind-tot-eind sleutel is ingetrokken
+ maar eind-tot-eind sleutel is niet veilig
+ maar eind-tot-eind gegevens bevatten fouten
+ maar versleuteling is niet veilig
+ OK
+ Zoek Sleutel
+ Bekijk ondertekenaar
+ Bekijk afzender
+ Details
+ Deblokkeer
+ Dit onderdeel is niet versleuteld en is misschien niet veilig.
+ Onbeveiligde Bijlage
+ Aan het laden …
+ Ontsleuteling is gestopt.
+ Opnieuw
+ Versleuteld bericht moet gedownload zijn voor ontsleuteling.
+ Fout tijdens ontsleutelen e-mail
+ Speciale leestekens worden nog niet ondersteund!
+ Fout bij parsen van adres!
+ Verberg niet-versleutelde handtekeningen
+ Alleen versleutelde handtekeningen worden weergegeven
+ Alle handtekeningen worden weergegeven
+ Versleuteling niet mogelijk in sign-only mode!
+ niet getekende tekst
+ Deze e-mail is versleuteld
+ Deze e-mail is OpenPGP versleuteld.\nInstalleer en stel een OpenPGP App in om de e-mail te lezen.
+ Ga naar Instellingen
+ K-9 Bericht Lijst
+ Berichten aan het laden …
+ Versleuteling niet mogelijk
+ Sommige van de ontvangers ondersteunen deze functie niet!
+ Versleuteling inschakelen
+ Versleuteling uitschakelen
+ Versleuteling maakt dat berichten alleen door de ontvanger gelezen kunnen worden, en niemand anders.
+ Versleuteling is alleen beschikbaar als alle ontvangers dit ondersteunen, en zij moeten je reeds eerder een e-mail gestuurd hebben.
+ Schakel versleuteling aan of uit met dit icoon.
+ Ik snap het
+ Terug
+ Versleuteling uitschakelen
+ OpenPGP-versleuteling
+ Autocrypt wederzijdse modus
+ Autocrypt wederzijdse modus
+ Berichten worden op verzoek versleuteld, of bij beantwoorden van een versleuteld bericht.
+ Als verzender en ontvanger de wederzijdse modus inschakelen dan wordt standaard versleuteling ingesteld.
+ Klik hier voor meer informatie.
+ Algemene instellingen
+ Geen OpenPGP app geïnstalleerd
+ Installeren
+ K-9 Mail vereist OpenKeychain voor eind-tot-eind versleuteling.
+ Versleuteld bericht
+ Onderwerp van bericht versleutelen
+ Mogelijk niet ondersteund door alle ontvangers
+ Interne fout: Ongeldig account!
+ Fout bij het verbinden met %s!
+ Verstuur Autocrypt Setup bericht
+ Veilig delen van eind-tot-eind instellingen met andere apparaten
+ Autocrypt Setup bericht
+ Een Autocrypt Setup bericht deelt uw eind-tot-eind instellingen op een beveiligde manier met andere apparaten.
+ Verstuur Setup bericht
+ Het bericht wordt verstuurd naar jouw adres:
+ Instelbericht aan het genereren …
+ Bericht sturen naar:
+ Om te voltooien, open het bericht op je andere apparaat en voer de Setup Code in.
+ Setup Code weergeven
+ Autocrypt Setup bericht
+ Dit bericht bevat alle informatie om jouw Autocrypt instellingen met geheime sleutel beveiligd over te brengen vanaf jouw oorspronkelijke apparaat.
Volg de instructies op uw nieuwe apparaat om daarop Autocrypt in te stellen.
@@ -1014,8 +962,6 @@ U kunt dit bericht bewaren als backup voor uw geheime sleutel. Als u dit wilt do
Er is een fout opgetreden tijdens het verzenden van het bericht. Controleer uw netwerk verbinding en uitgaande server configuratie.AanUit
- Open
- SluitenSta toegang tot contacten toeOm contact suggesties te kunnen bieden en om namen en/of foto\'s van contacten weer te geven, heeft de app toegang nodig tot je contactenlijst.
@@ -1053,7 +999,6 @@ U kunt dit bericht bewaren als backup voor uw geheime sleutel. Als u dit wilt do
Notificatie categorieënConfigureer notificaties voor nieuwe berichtenConfigureer fout en status notificaties
- Email voor alle accounts ophalenCertificaat foutExporteren voltooid. Log bestanden kunnen gevoelige informatie bevatten. Let op naar wie je ze verstuurd.Elke 5 minuten
diff --git a/app/ui/legacy/src/main/res/values-pl/strings.xml b/app/ui/legacy/src/main/res/values-pl/strings.xml
index 880904abb09a79bc01910729d6f59159dc39fd84..5440ace02b7eea3fbf26416889b6b10ef3b7a797 100644
--- a/app/ui/legacy/src/main/res/values-pl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pl/strings.xml
@@ -65,12 +65,12 @@
Przekaż jako załącznikEdytuj jako nową wiadomośćPrzenieś
- Przenieś do Wersji roboczych
+ Przenieś do wersji roboczychWyślij…Operacje…GotoweUsuń
- Zapisz jako szkic
+ Zapisz jako wersję robocząSprawdź pocztęWyślij wiadomościOdśwież listę
@@ -91,6 +91,7 @@
Dodaj gwiazdkęUsuń gwiazdkęSkopiuj
+ Anuluj subskrypcjęPokaż nagłówkiAdres skopiowany do schowka
@@ -157,7 +158,7 @@
Dodatkowe powiadomienia jak błędy itp.OdebraneWychodzące
- Szablony
+ Wersje roboczeKoszWysłaneNiektóre wiadomości nie zostały wysłane
@@ -184,7 +185,7 @@
Dnia %1$s, %2$s napisał/a:Musisz dodać co najmniej jednego odbiorcę.Pole Odbiorca jest niekompletne!
- Żaden adres email nie został znaleziony.
+ Żaden adres e-mail nie został znaleziony.Niektóre załączniki nie mogą być przesłane dalej ponieważ nie zostały wcześniej pobrane.Wiadomość nie może zostać przesłana, gdyż niektóre załączniki nie zostały ściągnięte.Załącz cytowaną wiadomość
@@ -203,12 +204,12 @@
Więcej od tego nadawcyOd %s Wiadomość usunięta
- Wiadomość zapisana jako szkic
+ Wiadomość zapisana jako wersja roboczaOznaczaj gwiazdkamiGwiazdki wskazują oznakowane wiadomościPodglądaj liniePokazuj nazwiska korespondentów
- Pokazuj nazwiska korespondentów zamiast ich adresów email
+ Pokazuj nazwiska korespondentów zamiast ich adresów e-mailKorespondent powyżej tematuWyświetlaj nazwę korespondenta powyżej, zamiast poniżej tematuPokazuj nazwę kontaktu
@@ -243,18 +244,23 @@
Powiadomienia na ekranie blokadyBez powiadomień na ekranie blokadyNazwa aplikacji
- wiadomość nowych wiadomości.
+ Liczba nowych wiadomościNadawcy i liczba wiadomościTak samo, jak przy odblokowanym ekranieOkres ciszyWyłącz dzwonienie, wibracje i mruganie w nocyWyłącz powiadomienia
- Całkowicie wyłącz powiadomienia podczas Okresu Ciszy
+ Całkowicie wyłącz powiadomienia podczas okresu ciszyPoczątek okresu ciszyKoniec okresu ciszyDodaj konto
- Adres email
+ Adres e-mailHasło
+ Aby używać tego konta e-mail z K-9 Mail, musisz się zalogować i przyznać aplikacji dostęp do swoich e-maili.
+
+ Zaloguj się
+
+ Zaloguj się przez GoogleAby zobaczyć tutaj swoje hasło, ustaw blokadę ekranu na tym urządzeniu.Zweryfikuj swoją tożsamość
@@ -278,6 +284,7 @@
Hasło, przesyłane bez zabezpieczeńZaszyfrowane hasłoCertyfikat klienta
+ OAuth 2.0Serwer poczty przychodzącejIdentyfikatorHasło
@@ -296,17 +303,14 @@
Nie usuwaj z serweraUsuń ją z serweraOznacz jako przeczytane na serwerze
- Używaj kompresji przy połączeniu:
- 2G/3G
- Wi-Fi
- Inne
+ Użyj kompresjiKasowanie usuniętych wiadomości na serwerzeOd razuPodczas każdego pobraniaTylko ręcznieWykrywaj automatycznie IMAP namespacePrefiks ścieżki IMAP
- Szkice
+ Folder wersji roboczychWysłaneKoszArchiwum
@@ -364,7 +368,11 @@
Nieprawidłowa nazwa użytkownika lub hasło.\n(%s)Serwer przedstawił nieprawidłowy certyfikat SSL. Czasami przyczyną jest błędna konfiguracja. Czasami dzieje się tak, ponieważ ktoś próbuje wykonać atak na Ciebie lub Twój serwer pocztowy. Jeśli nie masz pewności, co się dzieje, kliknij Odrzuć i skontaktuj się osobami zarządzającymi Twoim serwerem pocztowym.\n\n(%s)Nie można uzyskać połączenia z serwerem.\n(%s)
- Popraw ustawienia
+ Autoryzacja anulowana
+ Autoryzacja nie powiodła się z następującym błędem: %s
+ Ten dostawca nie obsługuje obecnie protokołu OAuth 2.0.
+ Aplikacja nie może znaleźć przeglądarki, której można użyć do przyznania dostępu do Twojego konta.
+ Edytuj szczegółyKontynuujZaawansowaneUstawienia ogólne
@@ -427,9 +435,9 @@
Brak skonfigurowanej aplikacji OpenPGPPołączono z %sKonfigurowanie…
- Szyfruj wszystkie składowane szkice
- Wszystkie szkice będą szyfrowane
- Szyfruj szkice tylko jeśli szyfrowanie jest wspierane
+ Szyfruj wszystkie przechowywane wersje robocze
+ Wszystkie wersje robocze będą szyfrowane
+ Szyfruj wersje robocze tylko wtedy, gdy szyfrowanie jest włączoneSprawdzanie kontaKolor kontaWybierz kolor, który będzie użyty przy wyświetlaniu folderów i listy kont
@@ -540,7 +548,7 @@
NiebieskiŻółtyCyjan
- Różowy
+ MagentaOpcje tworzenia wiadomościTworzenie wiadomościUstaw podpis oraz domyślne wartości Do, UDW
@@ -548,7 +556,7 @@
Ustaw alternatywne adresy nadawcy oraz podpisyZarządzaj tożsamościamiZarządzaj tożsamością
- Edytcja tożsamość
+ Edytuj tożsamośćZapiszNowa tożsamośćAdres UDW dla wysyłanych wiadomości (opcja)
@@ -561,9 +569,9 @@
(Opcjonalny)Imię i nazwisko(Opcjonalne)
- Adres email
+ Adres e-mail(Wymagany)
- Adres Odpowiedz-Do
+ Adres zwrotny(Opcjonalny)Podpis(Opcjonalna)
@@ -573,7 +581,7 @@
Wybierz tożsamośćWyślij jakoNie możesz usunąć jedynej tożsamości
- Nie możesz użyć tożsamości bez podanego adresu email
+ Nie możesz użyć tożsamości bez podanego adresu e-mailNajstarsze wiadomości na początkuNajnowsze wiadomości na początkuAlfabetycznie, po temacie (A->Z)
@@ -600,7 +608,7 @@
OdrzućDel (lub D) - Usuń\nR - Odpowiedz\nA - Odpowiedz wszystkim\nC - Stwórz\nF - Przekaż\nM - Przenieś\nV - Archiwizuj\nY - Kopiuj\nZ - Zaznacz (Nie)przeczytane\nG - Gwiazdka\nO - Typ sortowania\nl - Kolejność sortowania\nQ - Wróć do folderów\nS - Wybierz/odznacz\nJ lub P - Poprzednia wiadomość\nK lub N - Następna wiadomośćDel (lub D) - Usuń\nR - Odpowiedz\nA - Odpowiedz wszystkim\nC - Stwórz\nF - Przekaż\nM - Przenieś\nV - Archiwizuj\nY - Kopiuj\nZ - Zaznacz (Nie)przeczytane\nG - Gwiazdka\nO - Typ sortowania\nl - Kolejność sortowania\nQ - Wróć do folderów\nS - Wybierz/odznacz
- Nazwa katalogu zawiera
+ Nazwa folderu zawieraWyświetlanie folderówWszystkieTylko klasy 1
@@ -696,7 +704,7 @@
WielkaNie znaleiono odpowiedniej aplikacji.Wysyłanie nieudane: %s
- Zapisać szkic?
+ Zapisać wersję roboczą wiadomości?Zapisać czy porzucić wiadomość?Zapisać czy odrzucić zmiany?Usunąć wiadomość?
@@ -745,12 +753,19 @@
Wybierz plikImportujUstawienia zostały zaimportowane
+
Proszę podać hasła
+
+ Zaloguj się
+
+ Zaloguj się i wprowadź hasłaImport ustawień się nie powiódłOdczyt pliku ustawień się nie powiódłImport niektórych ustawień się nie powiódłZaimportowano z powodzeniemHasło wymagane
+
+ Wymagane jest zalogowanie sięNie zaimportowaneImport zakończony niepowodzeniemPóźniej
@@ -798,7 +813,7 @@
Wyślij wiadomośćZapisz do kontaktówKopiuj adres email do schowka
- Adres email
+ Adres e-mailWszystkie1025
@@ -846,7 +861,7 @@
Pokaż wybrane operacje w menu widoku wiadomościŁadowanie załącznika…Wysyłanie wiadomości
- Zapisywanie szkicu
+ Zapisywanie wersji roboczejPobieranie załącznika…Nie można uwierzytelnić. Serwer nie ogłasza zdolności SASL EXTERNAL. Przyczyną może być problem z certyfikatem klienta (wygasł, nieznane CA) lub jakiś inny problem z konfiguracją.
@@ -858,7 +873,7 @@
Certyfikat klienta \"%1$s\" wygasł lub nie jest jeszcze ważny (%2$s)*Zaszyfrowane*
- Dodaj z Kontaktów
+ Dodaj z kontaktówDWUDWDo
@@ -870,7 +885,7 @@
PracaInneMobilny
- Folder Szkice nie skonfigurowany dla tego konta.
+ Folderu wersji roboczych nie skonfigurowano dla tego konta!Nie skonfigurowanego klucza dla tego konta! Sprawdź ustawienia.Aplikacja szyfrująca w niezgodnej wersji. Sprawdź ustawienia.Nie można połączyć się z dostawcą kryptografii, sprawdź ustawienia lub kliknij ikonę kryptografii, aby spróbować ponownie!
@@ -881,13 +896,13 @@
Włącz PGP Sign-OnlyWyłącz PGP Sign-OnlyTryb PGP/INLINE
- Ten email został wysłany w formacie PGP/INLINE.\nPowinno to być użyte wyłącznie dla kompatybilności:
+ Ten e-mail został wysłany w formacie PGP/INLINE.\nPowinno to być użyte wyłącznie dla kompatybilności:Niektóre klienty obsługują tylko ten formatPodpisy mogą ulec uszkodzeniu podczas przesyłaniaZałączniki nie są obsługiwane.Mam to!Wyłącz
- Utrzymuj Włączone
+ Utrzymuj włączoneRozumiem!WyłączPozostaw włączone
@@ -947,14 +962,6 @@
Wszystkie podpisy będą widoczne.Szyfrowanie nie jest dostępne w trybie sign-only!Niepodpisany tekst
- Ostrzeżenie o przestarzałości APG
- APG nie jest już rozwijany!
- Z tego powodu wsparcie dla APG zostało usunięte z K-9 Mail.
- Rozwój zakończył się na początku 2014 roku.
- Zawiera nienaprawione błędy bezpieczeństwa.
- Możesz kliknąć tu by dowiedzieć się więcej.
- Rozumiem!
- APGTa wiadomość e-mail jest zaszyfrowanaTa wiadomość e-mail została zaszyfrowana z użyciem OpenPGP.\n Aby ją odczytać, należy zainstalować i skonfigurować zgodną aplikację OpenPGP.Przejdź do ustawień
@@ -1010,7 +1017,7 @@ Tą wiadomość można zachować i użyć w formie kopii zapasowej twojego klucz
Aby włączyć sugestie kontaktów i wyświetlać nazwy oraz zdjęcia aplikacja wymaga dostępu do kontaktów.Podczas ładowania danych wystąpił błądInicjowanie…
- Oczekiwanie na nowe emaile
+ Oczekiwanie na nowe e-maileUśpione, dopóki synchronizacja w tle nie będzie dozwolonaUśpione, dopóki sieć nie będzie dostępnaDotknij, aby dowiedzieć się więcej.
diff --git a/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml b/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
index e7496fbead18122965e4745706c8a12d9ab9b12f..350ee258577ead1ea0e1dfd5281d96d8ce82c18f 100644
--- a/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- Criadores do K-9
+ Criadores do MailCódigo-fonteApache License, Versão 2.0Projeto de código aberto
@@ -91,9 +91,11 @@
Adicionar estrelaRemover estrelaCopiar
+ Cancelar inscriçãoExibir cabeçalhosEndereço copiado para área de transferência
+ Endereços copiados para a área de transferênciaEndereços copiados para a área de transferênciaTexto do assunto copiado para a área de transferência
@@ -123,6 +125,7 @@
Nova mensagem%d nova mensagem
+ %d novas mensagens%d novas mensagens%d não lidas (%s)
@@ -251,6 +254,11 @@
Configurar uma nova contaEndereço de e-mailSenha
+ Para usar essa conta de e-mail com o Mail, você precisa autenticar-se e fornecer acesso do app aos seus e-mails.
+
+ Autenticar-se
+
+ Autenticar-se com o GooglePara ver a sua senha aqui, habilite o bloqueio de tela neste dispositivo.Verifique sua identidade
@@ -274,6 +282,7 @@
Senha, transmitida de forma inseguraSenha criptografadaCertificado do cliente
+ OAuth 2.0Configurações do servidor de entradaUsuárioSenha
@@ -292,10 +301,7 @@
Não excluí-la do servidorExcluí-la do servidorMarcá-la como lida no servidor
- Usar compactação em redes:
- Rede móvel
- Wi-Fi
- Outras
+ Usar compressãoApagar no servidor as mensagens excluídas localmenteImediatamenteDurante a verificação
@@ -360,6 +366,10 @@
Usuário ou senha incorretos.\n(%s)O servidor apresentou um certificado SSL inválido. Isto pode ocorrer por problemas de configuração do servidor, ou porque alguém está tentando atacar você ou o seu servidor de e-mails. Se você não tem certeza do que está acontecendo clique em Rejeitar e entre em contato com o administrador do seu servidor de e-mails.\n\n(%s)Não é possível conectar-se ao servidor.\n(%s)
+ Autorização cancelada
+ Ocorreu o seguinte erro com a autorização: %s
+ Oauth 2.0 não é atualmente suportado atualmente neste provedor.
+ O app não conseguiu encontrar um navegador para usar para garantir acesso à sua conta.Editar detalhesContinuarAvançado
@@ -705,6 +715,7 @@
Deseja excluir esta mensagem?Deseja realmente excluir esta mensagem?
+ Deseja realmente excluir %1$d mensagens?Deseja realmente excluir %1$d mensagens?Sim
@@ -718,6 +729,7 @@
Mover para a pasta de spamDeseja mesmo mover esta mensagem para a pasta de spam?
+ Deseja realmente mover %1$d mensagens para a pasta de spam?Deseja realmente mover %1$d mensagens para a pasta de spam?Sim
@@ -737,12 +749,19 @@
Selecionar arquivoImportarAs configurações foram importadas com sucesso
+
Por favor, digite as senhas
+
+ Por favor, faça sua autenticação
+
+ Por favor, faça sua autenticação digite a senhaNão foi possível importar as configuraçõesNão foi possível ler o arquivo de configuraçõesNão foi possível importar algumas configuraçõesImportado com sucessoÉ necessária a senha
+
+ É necessário fazer a sua autenticaçãoNão importadoFalha na importaçãoMais tarde
@@ -750,6 +769,7 @@
Importando configurações…Para usar a conta \"%s\", você precisa fornecer a senha do servidor.
+ Para usar a conta \"%s\", você precisa fornecer as senhas do servidor.Para usar a conta \"%s\", você precisa fornecer as senhas do servidor.Senha do servidor de entrada
@@ -801,10 +821,12 @@
Enviando a consulta ao servidorBuscando %d resultado
+ Buscando %d resultadosBuscando %d resultadosBuscando %1$d de %2$d resultado
+ Buscando %1$d de %2$d resultadosBuscando %1$d de %2$d resultadosNão foi possível efetuar a pesquisa remota
@@ -933,14 +955,6 @@
Todas as assinaturas serão exibidasA criptografia não está disponível no modo Somente Assinatura!Texto não assinado
- Aviso de obsolescência do APG
- O APG não é mais mantido!
- Devido a isso, o suporte ao APG foi removido do K-9 Mail.
- O desenvolvimento parou no início de 2014
- Contém problemas de segurança não corrigidos
- Você pode clicar aqui para saber mais.
- Entendi!
- APGEsta mensagem está criptografadaEsta mensagem foi criptografada com o OpenPGP.\nPara lê-la, você precisa instalar e configurar um aplicativo OpenPGP compatível.Ir para as configurações
@@ -1001,7 +1015,7 @@ Você pode manter esta mensagem e usá-la como um backup para a sua chave secret
Suspenso até que a rede esteja disponívelToque para aprender mais.Informação push
- Ao usar Push, o K-9 Mail mantém uma conexão com o servidor de e-mail. O Android demanda que seja exibida uma notificação enquanto o app estiver ativo em segundo plano. %s
+ Ao usar Push, o Mail mantém uma conexão com o servidor de e-mail. O Android demanda que seja exibida uma notificação enquanto o app estiver ativo em segundo plano. %sEntretanto, o Android também permite que você oculte a notificação.Saiba maisConfigurar a notificação
diff --git a/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml b/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
index e0d95540f8122a6f863de142ac12b60d26a6640d..dc5bfbf4e8686ea561aa2c1e358f0f625a526fc7 100644
--- a/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
@@ -78,6 +78,7 @@
Mostrar cabeçalhosEndereço copiado para a área de transferência
+ Endereços copiados para a área de transferênciaEndereços copiados para a área de transferênciaMudar para o tema escuro
@@ -106,6 +107,7 @@
Novo email%d nova mensagem
+ %d novas mensagens%d novas mensagens%d não lidos (%s)
@@ -226,6 +228,8 @@
Configurar uma nova contaEndereço de emailPalavra-passe
+
+
Configuração manual
@@ -264,10 +268,6 @@
Não eliminar no servidorEliminar no servidorMarcar como lido no servidor
- Usar compressão na rede:
- Móvel
- Wi-Fi
- OutrasApagar mensagens eliminadas no servidorImediatamenteNa verificação
@@ -650,6 +650,7 @@
Pretende eliminar esta mensagem?Pretende mesmo eliminar esta mensagem?
+ Pretende mesmo eliminar %1$d mensagens?Pretende mesmo eliminar %1$d mensagens?Sim
@@ -663,6 +664,7 @@
Confirmar o deslocamento para a pasta de spamPretende mesmo mover esta mensagem para a pasta de spam?
+ Pretende mesmo mover %1$d mensagens para a pasta de spam?Pretende mesmo mover %1$d mensagens para a pasta de spam?Sim
@@ -682,12 +684,16 @@
Selecionar ficheiroImportarConfigurações importadas com sucesso
+
Por favor inserir palavras-passe
+
+
Falha na importação de configuraçõesFalha na leitura do ficheiro de configuraçõesFalha na importação de algumas configuraçõesA importação foi bem sucedidaNecessária palavra-passe
+
Não importadoFalha na importaçãoPosteriormente
@@ -695,6 +701,7 @@
A importar configurações…Para poder utilizar a conta \"%s\" tem de fornecer as senhas do servidor.
+ Para poder utilizar a conta \"%s\" tem de fornecer as senhas do servidor.Para poder utilizar a conta \"%s\" tem de fornecer as senhas do servidor.Palavra-passe do servidor de entrada
@@ -746,10 +753,12 @@
A enviar consulta ao servidorA obter %d resultado
+ A obter %d resultadosA obter %d resultadosA obter %1$d de %2$d resultado
+ A obter %1$d de %2$d resultadosA obter %1$d de %2$d resultadosA pesquisa remota falhou
@@ -877,14 +886,6 @@
Serão mostradas todas as assinaturasEncriptação indisponível no modo de apenas assinatura!Texto não assinado
- Aviso de descontinuação do APG
- O APG já não é mantido!
- Devido a isto, o suporte ao APG foi removido do K-9 Mail.
- O desenvolvimento terminou em princípios de 2014
- Contém falhas de segurança não corrigidas
- Pode clicar aqui para saber mais.
- Entendido!
- APGEste email está encriptadoEste email foi encriptado com OpenPGP.\nPara o ler, necessita de instalar e configurar uma aplicação OpenPGP compatível.Ir para as configurações
diff --git a/app/ui/legacy/src/main/res/values-ro/strings.xml b/app/ui/legacy/src/main/res/values-ro/strings.xml
index 724e62e58280698a2350064009b22aaae9378191..251dfe8d9a51e52d9e091f24c4e6235e642bf10f 100644
--- a/app/ui/legacy/src/main/res/values-ro/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ro/strings.xml
@@ -254,6 +254,8 @@ cel mult încă %dConfigurarează un cont nouAdresa e-mailParolă
+
+
Pentru a vă vedea parola aici, activați blocarea ecranului pe acest dispozitiv.Verifică-ți identitatea
@@ -295,10 +297,6 @@ cel mult încă %dNu şterge de pe serverŞterge de pe serverMarchează ca citit pe server
- Foloseşte compresie pe reţea:
- Mobil
- Wi-Fi
- AltcevaElimină mesajele şterse de pe serverImediatCând interoghează
@@ -744,12 +742,16 @@ Uneori datorită faptului că cineva încearcă să te atace pe tine sau serveru
Selectează fișierImportăSetările au fost importate cu succes
+
Te rog introduce parolele
+
+
Importarea setărilor a eșuatCitirea fișierului cu setări a eșuatImportarea unor setări a eșuatImportat cu succesParola necesară
+
Nu a fost importatEșuare la importMai târziu
@@ -943,14 +945,6 @@ Uneori datorită faptului că cineva încearcă să te atace pe tine sau serveru
Toate semnăturile vor fi afișateCriptare indisponibilă în modul „Doar semnare”!Text nesemnat
- Avertisment APG
- APG nu mai este întreținut!
- Din această cauză suportul pentru APG a fost eliminat din K-9 Mail.
- Dezvoltarea s-a oprit la începutul lui 2014
- Conține probleme de securitate nerezolvate
- Puteți apăsa aici pentru mai multe informații.
- Am înțeles!
- APGAcest mesaj este criptatAcest mesaj a fost criptat cu OpenPGP.\nPentru a-l citi trebuie să instalați și să configurați o aplicație OpenPGP compatibilă.Mergi la Setări
diff --git a/app/ui/legacy/src/main/res/values-ru/strings.xml b/app/ui/legacy/src/main/res/values-ru/strings.xml
index b382414aad24b0b92d3ae11f12b57cd39620cf89..82710d572911169663d24c0ddf35bfa9107cac18 100644
--- a/app/ui/legacy/src/main/res/values-ru/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ru/strings.xml
@@ -1,1096 +1,1042 @@
-
-
-
- Почта
- Учетные записи почты
- Непрочитанная почта
-
- The K-9 Dog Walkers
- Авторское право 2008-%s The K-9 Dog Walkers. Части авторского права 2006-%s the Android Open Source Project.
- Исходный код
- Лицензия Apache версии 2.0
- Проект с открытым исходным кодом
- Сайт
- Форум
- Федеративная сеть
- Твиттер
- Библиотеки
- Лицензия
- Список изменений
- Не удалось загрузить список изменений.
- Версия %s
-
- Добро пожаловать в Почту
- Mail is the default mail client for /e/
-
- -- Отправлено из /e/ Mail.
-
- Учётная запись \"%s\" будет удалена из Почты.
-
- Авторы
- Ревизия
- Чтение почты
- Разрешить программе чтение почты.
- Удаление почты
- Разрешить программе удаление почты.
- О приложении Почта
- Ящики
- Папки
- Расширенный
- Новое
- Ответ
- Ответить всем
- Пересылка
- Переслать вложением
- Выберите ящик
- Выбор папки
- Проверка %s:%s%s
- Просмотр %s:%s%s
- Отправка %s%s
- Обработка %s:%s%s
- \u0020%d/%d
- Синхронизация отключена
- %d выбрано
- Далее
- Назад
-
- ОК
- Отмена
- Отправить
- Редактировать
- Тема не заполнена, нажмите ещё раз для отправки без темы
- Выбрать
- Снять выбор
- Ответить
- Ответить всем
- Удалить
- В архив
- В спам
- Переслать
- Переслать вложением
- Редактировать как новое сообщение
- Переместить
- Переместить в черновики
- Отправить…
- Смена папки…
- Готово
- Отменить
- Сохранить как черновик
- Обновить
- Отправить почту
- Обновить список папок
- Поиск папки
- Добавить
- Создать
- Поиск сообщения
- Результаты поиска
- Настройки
- Выбрать папки
- Настройки ящика
- Удалить ящик
- Прочитано
- Передать
- Выбрать отправителя
- Важное
- Обычное
- Копировать
- Все заголовки
-
- Адрес скопирован в буфер
- Адреса скопированы в буфер
- Адреса скопированы в буфер
- Адреса скопированы в буфер
-
- Тёмная тема
- Светлая тема
- Не прочитано
- Уведомление
- Запросить уведомление о прочтении
- Без уведомления о прочтении
- Вложение
- Очистить корзину
- Стереть
- О действии
- Настройки
-
- (без темы)
- нет отправителя
- Проверка новых сообщений…
- Сбой подключения
- Сообщение не найдено
- Сбой загрузки сообщения
- Получить до %d следующих
- %.1f ГБ
- %.1f МБ
- %.1f кБ
- %d Б
- Ящик \"%s\" изменился с %s до %s
- Сжатие ящика \"%s\"
- Новая почта
-
- %d новое сообщение
- %d новых сообщения
- %d новых сообщений
- %d новых сообщений
-
- %d новых (%s)
- + ещё %1$d в %2$s
- Ответить
- Прочитано
- Прочитаны все
- Удалить
- Удалить все
- Архив
- В архив все
- Спам
- Сбой сертификата %s
- Проверьте настройки сервера
- Сбой аутентификации
- Сбой аутентификации для %s. Измените настройки сервера.
- Проверка %s:%s
- Проверка почты
- Отправка %s
- Отправка почты
- :
- Сообщение
- Уведомления о сообщениях
- Разное
- Различные уведомления, например, об ошибках и т.д.
- Входящие
- Исходящие
- Черновики
- Корзина
- Отправленные
- Сбой отправки почты
- Версия
- Журнал отладки
- Запись диагностических сообщений
- Личные данные
- Разрешить запись паролей в журнал.
- Получить новые сообщения
- кому:%s
- Тема
- Сообщение
- подпись
- ------ Исходное сообщение ------
- Тема:
- Отправлено:
- От:
- Кому:
- Копия:
- %s пишет:
- %1$s, %2$s пишет:
- Укажите по крайней мере одного адресата.
- Поле адресата заполнено неполностью!
- Контакт не содержит сведений о email адресе.
- Некоторые вложения не были загружены и не могут быть отправлены.
- Сообщение не может быть переслано, так как не загружены вложения.
- Включить цитируемое сообщение
- Удалить цитату
- Правка цитаты
- Удалить вложение
- от: %s <%s>
- кому:
- копия:
- Скрытая:
- Открыть
- Сохранить
- Не получается сохранить вложение.
- Изображения
- Отсутствует просмотрщик%s.
- Загрузить полностью
- Невозможно отобразить сообщение
- от %1$s
-
- Все заголовки были загружены, но нет дополнительных заголовков для показа.
- Сбой получения дополнительных заголовков из базы данных или почтового сервера.
- Вся переписка
- От %s
- Отладка / Очистка тела сообщения
- Сообщение отменено
- Черновик сохранён
- Показать важное
- Для управления важностью сообщений
- Строки предпросмотра
- Имя вместо адреса
- Показать имя отправителя вместо его адреса
- Отправитель над темой
- Показать имя отправителя над темой сообщения
- Имя из контактов
- Подбор имён отправителей из контактов
- Цвет
- Выделять цветом имена из контактов
- Моноширинный шрифт
- Показать сообщения в формате \"Только текст\" моноширинным шрифтом
- Вписать по ширине
- Подогнать текст сообщения по ширине экрана
- В список после удаления
- Перейти в список сообщений после удаления сообщения
- В следующее после удаления
- Открыть следующее сообщение после удаления сообщения
- Подтверждать действия
- Запросить подтверждение действий
- Удалить
- Удалить важное (в просмотре)
- Переместить в спам
- Отменить сообщение
- Прочитаны все
- Удалить (в уведомлении)
- Скрыть почтовый клиент
- Удалить Почтового User-Agent\'а из почтовых заголовков
- Скрыть временную зону
- Использовать UTC вместо местного времени в заголовках сообщений
- Скрыть тему в уведомлении
- Никогда
- При блокировке
- Всегда
- Кнопка \"Удалить\"
- Никогда
- Для одиночного
- Всегда
- Встроить в экран блокировки
- Убрать с экрана блокировки
- Имя приложения
- Счётчик непрочитанных
- Счётчик сообщений и отправители
- Как без блокировки
- Период тишины
- Отключить мелодию, вибрацию и индикатор на ночь
- Отключить уведомления
- Полностью, в период тишины
- Начало
- Конец
- Новый ящик
- адрес email
- Пароль
- Показать пароль
- Вручную
-
- Получение сведений\u2026
- Проверка сервера входящей почты\u2026
- Проверка сервера исходящей почты\u2026
- Аутентификация\u2026
- Получение настроек\u2026
- Отмена\u2026
- Всё почти готово!
- Имя ящика (опция):
- Ваше имя (видно адресату):
- Тип ящика
- Какой это тип учётной записи\?
- POP3
- IMAP
- Обычный пароль
- Небезопасный пароль
- Безопасный пароль
- Сертификат клиента
- Сервер входящей почты
- Логин
- Пароль
- Сертификат клиента
- Сервер POP3
- Сервер IMAP
- Сервер Exchange
- Порт
- Безопасность
- Аутентификация
- Нет
- SSL/TLS
- STARTTLS
- \"%1$s = %2$s\" недействителен для \"%3$s = %4$s\"
- Удалённое, на сервере
- Оставить
- Удалить
- Прочитано
- Сжатие в сетях:
- Мобильные
- Wi-Fi
- Прочие
- Карта памяти
- Внутренняя память
- Стирать удалённые с сервера
- Немедленно
- При проверке
- Вручную
- Автообласть имён IMAP
- Префикс пути IMAP
- Папка черновиков
- Папка отправленных
- Папка удалённых
- Папка архивных
- Папка спама
- Только подписанные папки
- Автопереход в папку
- Путь OWA
- опция
- Путь аутентификации
- опция
- Псевдоним ящика
- опция
- Сервер исходящей почты
- Сервер SMTP
- Порт
- Безопасность
- Требуется авторизация.
- Логин
- Пароль
- Аутентификация
- \"%1$s = %2$s\" недействителен для \"%3$s = %4$s\"
- Неверная настройка: %s
- Настройки ящика
- Сжать ящик
- Интервал проверки
- Вручную
- 15 минут
- 30 минут
- 1 час
- 2 часа
- 3 часа
- 6 часов
- 12 часов
- 1 сутки
- Push-уведомления
- Мгновенное извещение о новых сообщениях, при поддержке сервером. Этот параметр может значительно улучшить или ухудшить производительность.
- Интервал обновления IDLE
- 2 минуты
- 3 минуты
- 6 минут
- 12 минут
- 24 минуты
- 36 минут
- 48 минут
- 1 час
- Уведомлять о новой почте
- Уведомлять о проверке почты
- Загружать сообщений
- 10
- 25
- 50
- 100
- 250
- 500
- 1000
- 2500 сообщений
- 5000 сообщений
- 10000 сообщений
- Все
- Нельзя скопировать или переместить сообщение, не синхронизированное с сервером
- Настройка не завершена
- Неверные логин или пароль.\n(%s)
- Сервер предоставляет неверный сертификат SSL. Иногда это обусловлено неправильной настройкой. Или кто-то пытается атаковать Ваш почтовый сервер или Ваш компьютер. Если Вы не уверены в причинах, нажмите Отклонить и свяжитесь с персоналом, обслуживающим почтовый сервер.\n\n(%s)
- Не удаётся подключиться к серверу.\n(%s)
- Правка
- Продолжить
- Дополнительно
- Настройки ящика
- Ящик по умолчанию
- По умолчанию отправлять почту с этого ящика
- Уведомить о почте
- Уведомления папок
- Все
- 1 класс
- 1 и 2 классы
- Кроме 2 класса
- Нет
- Уведомить о проверке
- Ваш адрес email
- Показать уведомление о новой почте
- Показать уведомление о проверке почты
- Также об исходящей
- Показать уведомление при отправке почты
- Для контактов
- Уведомлять только о сообщениях от контактов
- Открытое прочитано
- Отметить сообщение прочитанным после просмотра
- Отметить как прочитанное при удалении
- Отметить как прочитанное при удалении
- Настройки уведомлений
- Открыть настройки уведомлений
- Показать изображения
- Никогда
- От контактов
- Всегда
- Отправка
- Цитировать при ответе
- Включать исходное сообщение в ответ.
- Цитата над ответом
- Разместить исходное сообщение над текстом ответа.
- Без подписей в цитате
- Удалять подписи из цитируемого текста
- Формат сообщений
- Только текст
- HTML
- Автоматически
- Включать Копия/Скрытая
- Уведомление о прочтении
- Всегда запрашивать уведомление о прочтении
- Стиль цитирования
- Префикс (Gmail)
- Заголовок (Outlook)
- Выгрузить отправленные
- Помещать сообщения в папку \"Отправленные\" после отправки
- Основные
- Чтение
- Получение
- Папки
- Префикс цитаты
- Сквозное шифрование
- Поддержка OpenPGP
- Приложение OpenPGP
- Настройка ключа шифрования
- OpenPGP не настроен
- соединено с %s
- Конфигурируется…
- Хранить черновики зашифрованными
- Все черновики будут зашифрованы
- Зашифровать черновики, если включено шифрование
- Интервал проверки
- Цвет
- Метка в списке ящиков и папок
- Без цвета
- Цвет LED-индикатора
- Цвет LED-индикатора для оповещений этого ящика
- Отображать сообщений
- Загружать фрагмент
- 1 КиБ
- 2 КиБ
- 4 КиБ
- 8 КиБ
- 16 КиБ
- 32 КиБ
- 64 КиБ
- 128 КиБ
- 256 КиБ
- 512 КиБ
- 1 МиБ
- 2 МиБ
- 5 МиБ
- 10 МиБ
- Полностью
- Глубина синхронизации
- Всё время
- Сегодня
- 2 дня
- 3 дня
- 1 неделя
- 2 недели
- 3 недели
- 1 месяц
- 2 месяца
- 3 месяца
- 6 месяцев
- 1 год
- Видимость папок
- Все
- 1 класс
- 1 и 2 классы
- Кроме 2 класса
- Проверка папок
- Все
- 1 класс
- 1 и 2 классы
- Кроме 2 класса
- Нет
- Push
- Все
- 1 класс
- 1 и 2 классы
- Кроме 2 класса
- Нет
- Приёмник копирования
- Все
- 1 класс
- 1 и 2 классы
- Кроме 2 класса
- Синхронное удаление
- Удалять сообщения при удалении на сервере
- Отсутствует OpenPGP-приложение – было удалено?
- Настройки папки
- Начало списка
- Отображать ближе к началу списка папок
- Видимость
- Нет класса
- 1 класс
- 2 класс
- Проверка папок
- Нет
- 1 класс
- 2 класс
- Как видимости
- Push
- Нет класса
- 1 класс
- 2 класс
- Уведомление
- Нет класса
- 1 класс
- 2 класс
- Как push
- Стереть локальные
- Сервер входящих
- Настройки сервера входящей почты
- Сервер исходящих
- Настройки сервера исходящей почты
- Имя ящика
- Ваше имя
- Уведомления
- Вибрация
- Вибрация при новой почте
- Шаблон вибрации
- Обычный
- Шаблон 1
- Шаблон 2
- Шаблон 3
- Шаблон 4
- Шаблон 5
- Повтор
- Мелодия
- Мигать LED-индикатором
- Мигать LED-индикатором при новой почте
- Новое сообщение
- Новое сообщение
- Отправитель, скрытая копия и подпись по умолчанию
- Управление ролями
- Альтернативные значения отправителя и подписи
- Управление ролями
- Управление
- Правка роли
- Сохранить
- Новая роль
- Скрытая копия всех сообщений
- Правка
- Выше
- Ниже
- Вверх
- Удалить
- Название роли
- опция
- Ваше имя
- опция
- Адрес email
- обязательно
- Адрес для ответа
- опция
- Подпись
- опция
- Использовать подпись
- Подпись
- Основная роль
- Выберите роль
- Отправитель
- Нельзя удалить основную роль
- Нельзя создать роль без адреса email
- Старые – новые
- Новые – старые
- Тема А – Я
- Тема Я – А
- Отправитель А – Я
- Отправитель Я – А
- Важные – обычные
- Обычные – важные
- Не прочитано – прочитано
- Прочитано – не прочитано
- С вложениями – без вложений
- Без вложений – с вложениями
- Сортировка…
- Дата
- Получено
- Тема
- Отправитель
- Важность
- Прочитано
- Вложение
- Удаление ящика
- Неверный сертификат сервера
- Принять
- Отклонить
- Del (или D) – удалить\nR – ответить\nA – ответить всем\nC – создать\nF – переслать\nM – переместить\nV – в архив\nY – копировать\nZ – (не) прочитано\nG – важное\nO – сортировка\nI – порядок\nQ – папки\nS – выбрать\nJ или P – назад\nK или N – далее
- Del (или D) – удалить\nC – создать\nM – переместить\nV – в архив\nY – копировать\nZ – (не) прочитано\nG – важное\nO – сортировка\nI – порядок\nQ – папки\nS – выбрать
- Поиск папки
- Видимость папок…
- Все папки
- 1 класс
- 1 и 2 класс
- Кроме 2 класса
- Размещение подписи
- перед цитатой
- после цитаты
- Как в приложении
- Тёмная
- Светлая
- Использовать умолчания системы
- Вид
- Общие
- Отладка
- Личное
- Сеть
- Интерфейс
- Список ящиков
- Список сообщений
- Сообщения
- Тема приложения
- Тема просмотра
- Тема редактора
- Язык
- Настройки не найдены
- Фиксированный просмотр
- Разрешить выбор темы при чтении сообщения
- Отключить выбор темы при чтении сообщения
- По умолчанию
- Фоновая синхронизация
- Никогда
- Всегда
- Если вкл. Автосинхронизация
- Выбрать все
- Лимит push-проверки
- 5 папок
- 10 папок
- 25 папок
- 50 папок
- 100 папок
- 250 папок
- 500 папок
- 1000 папок
- Анимация
- Анимация интерфейса
- Прокрутка кнопками Vol+/-
- В сообщении
- В списке
- Показывать общий ящик для входящих
- %s%s
- – не прочитано
- Вся почта
- в видимых папках
- Входящие
- из всех ящиков
- Общие \"Входящие\"
- Показать почту в общих \"Входящих\"
- Папки для поиска
- Все
- Видимые
- Нет
- Нет
- Автоматически (%s)
- Шрифт
- Настройка размера шрифтов
- Список ящиков
- Имя ящика
- Описание ящика
- Список папок
- Имя папки
- Состояние папки
- Список сообщений
- Тема
- Отправитель
- Дата
- Просмотр
- Сообщение
- Отправитель
- Кому
- Копия
- Скрытая
- Все заголовки
- Тема
- Дата и время
- Тело сообщения
- %d%%
- %1$s: %2$s
- Редактор сообщения
- Формы ввода
- По умолчанию
- Крохотный
- Маленький
- Небольшой
- Средний
- Большой
- Огромный
- Гигантский
- Подходящее приложение не найдено.
- Сбой отправки: %s
- Сохранить черновик сообщения\?
- Сохранить черновик или отменить сообщение?
- Сохранить или отменить изменения?
- Отбросить сообщение\?
- Отменить сообщение?
- Выберите текст для копирования.
- Стереть локальные сообщения?
- Это удалит все локальные сообщения из папки.Сообщения не будут удалены с сервера.
- Стереть сообщения
- Удаление
- Удалить сообщение?
-
- Вы действительно хотите удалить это сообщение\?
- Вы действительно хотите удалить эти <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\" id=\"message_count\"> сообщения\?
- Вы действительно хотите удалить эти <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\" id=\"message_count\"> сообщений\?
- Вы действительно хотите удалить эти <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\" id=\"message_count\"> сообщений\?
-
- ОК
- Отмена
- Подтверждение прочтения
- Отметить все сообщения прочитанными?
- Подтвердите очистку корзины
- Очистить корзину?
- Да
- Нет
- Переместить в спам
-
- Вы действительно хотите переместить сообщение в Спам\?
- Вы действительно хотите переместить %1$d сообщения в Спам\?
- Вы действительно хотите переместить %1$d сообщений в Спам\?
- Вы действительно хотите переместить %1$d сообщений в Спам\?
-
- ОК
- Отмена
- Загрузка вложения
- »
- ›
- Резервная копия
- Разное
- Экспортировать настройки
- Экспорт
- Передать
- Экспорт настроек…
- Настройки успешно экспортированы
- Сбой экспорта настроек
- Импорт
- Выберите файл
- Импорт
- Настройки успешно импортированы
- Пожалуйста, введите пароли
- Не удалось импортировать настройки
- Не удалось прочесть файл настроек
- Не удалось импортировать некоторые настройки
- Успешный импорт
- Требуется пароль
- Не импортировано
- Импорт не удался
- Позже
- Импорт
- Импорт настроек…
-
- Чтобы использовать ящик \"%s\" необходимо ввести пароль.
- Чтобы использовать ящик \"%s\" необходимо ввести пароли.
- Чтобы использовать ящик \"%s\" необходимо ввести пароли.
- Чтобы использовать ящик \"%s\" необходимо ввести пароли.
-
- Входящий пароль сервера
- Исходящий пароль сервера
- Использовать тот же пароль для исходящего сервера
- Названия серверов: %s
- Счётчик непрочитанных…
- Ящик
- Ящик для отображения счётчика непрочитанных
- Общие \"Входящие\"
- Счётчик папок
- Показывать счётчик непрочитанных только для одной папки
- Папка
- Папка для отображения счётчика непрочитанных
- Готово
- %1$s – %2$s
- Ящик не выбран
- Папка не выбрана
- Текст отсутствует
- Открыть
- Отправить
- В буфер
- Ссылка
- Копировать текст ссылки в буфер обмена
- Текст ссылки
- Изображение
- Открыть
- Сохранить
- Загрузить
- URL в буфер
- URL изображения
- Вызвать
- В контакты
- В буфер
- Номер
- Написать
- В контакты
- В буфер
- Адрес email
- Все
- 10
- 25
- 50
- 100
- 250
- 500
- 1000
- Лимит поиска на сервере
- Отправка запроса на сервер
-
- Получение %d результата
- Получение %d результатов
- Получение %d результатов
- Получение %d результатов
-
-
- Получение %1$d из %2$d результатов
- Получение %1$d из %2$d результатов
- Получение %1$d из %2$d результатов
- Получение %1$d из %2$d результатов
-
- Сбой поиска на сервере
- Поиск
- Поиск на сервере
- Искать сообщения локально и на сервере
- Поиск на сервере
- Для поиска на сервере необходимо подключение к сети.
- Менять цвет при прочтении
- Другой фон для прочитанных сообщений
- Цепочки сообщений
- Объединить сообщения, показав счётчик
- Обновление данных
- Обновление данных…
- Обновление ящика \"%s\"
- Разделить экран
- Всегда
- Нет
- Ландшафт
- Выберите сообщение
- Фото контактов
- Показать фото контактов в списке сообщений
- Прочитаны все
- Цветные места для фото
- Инициалы контактов на цветном фоне при отсутствии фото
- Действия для сообщения
- Видимость действий для открытого сообщения
- Загрузка вложений…
- Отправка сообщения
- Сохранение черновика
- Получение вложений…
- Аутентификация не удалась. Сервер не сообщает о совместимости с SASL EXTERNAL. Причиной может служить неверный сертификат клиента (просрочен, неизвестен издатель) или проблемы с настройками.
-
- Использовать сертификат клиента
- Не использовать сертификат клиента
- Отключить выбор сертификата клиента
- Не найден сертификат клиента для алиаса \"%s\"
- Дополнительно
- Срок действия сертификата клиента \"%1$s\" истёк или ещё не наступил (%2$s)
-
- *Зашифровано*
- Добавить из Контактов
- Копия
- Скрытая
- Кому
- От
- Неизвестный получатель
- Неизвестный отправитель
- Дом
- Работа
- Прочее
- Мобильный
- Для данного ящика не настроена папка Черновики!
- Для данного ящика не настроены ключи! Проверьте настройки.
- Криптопровайдер использует несовместимую версию. Проверьте настройки!
- Нет доступа к криптопровайдеру. Проверьте настройки или нажмите значок шифрования для повтора!
- Ошибка инициализации сквозного шифрования. Проверьте настройки
- Режим PGP/INLINE не поддерживает вложения!
- Включить PGP/INLINE
- Отключить PGP/INLINE
- Включить PGP \"Только подпись\"
- Отключить PGP \"Только подпись\"
- Режим PGP/INLINE
- Сообщение отправлено в формате PGP/INLINE.
-\nИспользуйте только для совместимости:
- Некоторые клиенты поддерживают только этот формат
- Подписи могут повредиться при передаче
- Вложения не поддерживаются
- Понятно!
- Отключить
- Не отключать
- Понятно!
- Отключить
- Не отключать
- Режим PGP \"Только подпись\"
- В этом режиме, ключ PGP будет использоваться для создания криптографической подписи к незашифрованному сообщению.
- Адресат сможет убедиться, что сообщение подлинное и подписано Вашим ключом.
- Подписи могут повредиться при отправке нескольким адресатам.
- Подписи могут отображаться как вложенные файлы \'signature.asc\' для некоторых клиентов.
- Всегда добавлять подпись к шифрованным сообщениям.
- Текст
- неверная подпись сквозного шифрования
- требуется полная загрузка сообщения для проверки подписи
- содержит неподдерживаемую подпись сквозного шифрования
- Сообщение зашифровано в неподдерживаемом формате.
- Сообщение зашифровано, дешифрование отменено.
- текст, подписанный ключом сквозного шифрования
- от проверенного подписанта
- Подписанный текст
- но ключ сквозного шифрования не соответствует отправителю
- но ключ сквозного шифрования просрочен
- но ключ сквозного шифрования отозван
- но ключ сквозного шифрования небезопасен
- но ключ сквозного шифрования неизвестен
- Зашифровано
- но был сбой дешифрования
- требуется полная загрузка сообщения для дешифрования
- но отсутствует настроенное криптоприложение
- Зашифровано
- но без сквозного шифрования
- со сквозным шифрованием
- от проверенного отправителя
- Зашифровано
- но ключ сквозного шифрования неизвестен
- но ключ сквозного шифрования не соответствует отправителю
- но ключ сквозного шифрования просрочен
- но ключ сквозного шифрования отозван
- но ключ сквозного шифрования небезопасен
- но произошёл сбой в данных сквозного шифрования
- но шифрование не считается безопасным
- OK
- Поиск криптоключа
- Кем подписано
- Кем отправлено
- Подробности
- Разблокировать
- Эта часть не была зашифрована и небезопасна.
- Незащищённое вложение
- Загрузка…
- Дешифрование отменено.
- Повтор
- Зашифрованное сообщение необходимо загрузить для дешифрования.
- Сбой дешифрования почты
- Спецсимволы пока не поддерживаются!
- Сбой разбора адреса!
- Скрыть подписи без шифрования
- Отображаются зашифрованные подписи
- Отображаются все подписи
- Шифрование недоступно в режиме \"Только подпись\"!
- Неподписанный текст
- Прекращение поддержки APG
- Разработка APG прекращена!
- В связи с этим, поддержка APG удалена из приложения Почты.
- Разработка прекращена в начале 2014 года
- Имеются неисправленные уязвимости
- Нажмите здесь для подробностей.
- Понятно!
- APG
- Сообщение зашифровано
- Сообщение зашифровано OpenPGP.
-\nЧтобы прочесть его, необходимо установить и настроить подходящее OpenPGP-приложение.
- В Настройки
- Mail Message List
- Загрузка сообщений…
- Сбой при получении списка папок
- Сбой получения статуса адресата у провайдера OpenPGP!
- Шифрование невозможно
- Не все выбранные адресаты поддерживают эту возможность!
- Включить шифрование
- Отключить шифрование
- Шифрование сообщений гарантирует, что никто, кроме получателя, не сможет их прочесть.
- Шифрование отображается, если поддерживается всеми адресатами и они писали Вам ранее.
- Включите шифрование, нажав этот значок.
- Понятно
- Назад
- Отключить шифрование
- Шифрование OpenPGP
- Взаимное автошифрование
- Взаимное автошифрование
- Сообщения шифруются по желанию или при ответе на шифрованное сообщение.
- Если отправитель и получатели включили режим взаимного автошифрования, включать шифрование по умолчанию.
- Нажмите здесь для подробностей.
- Основные
- OpenPGP не установлен
- Установить
- Почта требует OpenKeychain для end-to-end шифрования.
- Зашифрованное сообщение
- Шифровать темы сообщений
- Может поддерживаться не всеми адресатами
- Внутренний сбой: неверный ящик!
- Сбой подключения к %s!
- Отправка настроек автошифрования
- Передать настройки шифрования на другие устройства
- Настройки автошифрования
- Сообщение с настройками взаимного автошифрования позволяет безопасно передать эти настройки на другие устройства.
- Отправить настройки
- Сообщение будет отправлено на ваш адрес:
- Создание сообщения с настройками…
- Отправка сообщения для:
- Для завершения, откройте сообщение на новом устройстве и введите код установки.
- Показать код установки
- Настройки взаимного автошифрования
- Это сообщение содержит все сведения, необходимые для переноса настроек взаимного автошифрования вместе с секретным ключом с исходного устройства.
+
+
+
+ Почта Mail
+ Аккаунты Mail
+ Не прочитано
+
+ The Mail Dog Walkers
+ Исходный код
+ Лицензия Apache версии 2.0
+ Проект с открытым исходным кодом
+ Сайт
+ Форум
+ Федеративная сеть
+ Твиттер
+ Библиотеки
+ Лицензия
+ Список изменений
+ Не удалось загрузить список изменений.
+ Версия %s
+ Что нового
+ Показывать последние изменения, когда приложение было обновлено
+ Узнайте, что нового в этом выпуске
+
+ Добро пожаловать!
+
+Mail — почтовый клиент для Android.
+
+Возможности:
+
+
+
push-уведомления,
+
быстрая работа,
+
перемещение сообщений,
+
несколько подписей,
+
скрытая копия себе,
+
подписка на папки,
+
синхронизация всех папок,
+
адрес для ответа,
+
горячие клавиши,
+
улучшенная поддержка IMAP,
+
сохранение вложений,
+
очистка корзины,
+
сортировка сообщений,
+
…и многое другое!
+
+
+Отметим, что Mail не полностью совместим с MS Exchange и не поддерживает бесплатные ящики Hotmail.
+
+ ]]>
+
+ -- \nПростите за краткость, создано в Mail.
+
+ Ящик \"%s\" будет удалён из приложения Mail.
+
+ Авторы
+ О почте Mail
+ Ящики
+ Дополнительно
+ Новое
+ Ответ
+ Ответить всем
+ Пересылка
+ Переслать вложением
+ Выберите ящик
+ Выбор папки
+ Переместить в…
+ Копировать в…
+ %d выбрано
+ Далее
+ Назад
+
+ ОК
+ Отмена
+ Отправить
+ Тема не заполнена, нажмите ещё раз для отправки без темы
+ Ответить
+ Ответить всем
+ Удалить
+ В архив
+ В спам
+ Переслать
+ Переслать вложением
+ Редактировать как новое сообщение
+ Переместить
+ Переместить в черновики
+ Отправить…
+ Смена папки…
+ Готово
+ Отменить
+ Сохранить как черновик
+ Проверить почту
+ Отправить почту
+ Обновить список папок
+ Поиск папки
+ Добавить
+ Создать
+ Поиск сообщения
+ Искать везде
+ Результаты поиска
+ Новые сообщения
+ Настройки
+ Выбрать папки
+ Настройки ящика
+ Удалить ящик
+ Прочитано
+ Передать
+ Выбрать отправителя
+ Важное
+ Обычное
+ Копировать
+ Все заголовки
+
+ Адрес скопирован в буфер
+ Адреса скопированы в буфер
+ Адреса скопированы в буфер
+ Адреса скопированы в буфер
+
+ Текст темы скопирован в буфер обмена
+ Тёмная тема
+ Светлая тема
+ Не прочитано
+ Уведомление
+ Запросить уведомление о прочтении
+ Без уведомления о прочтении
+ Добавить вложение
+ Очистить корзину
+ Стереть
+ О программе
+ Настройки
+
+ (Без темы)
+ Нет отправителя
+ Загрузка сообщений\u2026
+ Сбой подключения
+ Сообщение не найдено
+ Сбой загрузки сообщения
+ Получить до %d следующих
+ %.1f ГБ
+ %.1f МБ
+ %.1f кБ
+ %d Б
+ Новая почта
+
+ %d новое сообщение
+ %d новых сообщения
+ %d новых сообщений
+ %d новых сообщений
+
+ %d новых (%s)
+ + ещё %1$d в %2$s
+ Ответить
+ Прочитано
+ Прочитаны все
+ Удалить
+ Удалить все
+ Архив
+ В архив все
+ Спам
+ Ошибка сертификата
+ Сбой сертификата %s
+ Проверьте настройки сервера
+ Сбой аутентификации
+ Сбой аутентификации для %s. Измените настройки сервера
+ Проверка %s:%s
+ Проверка почты
+ Отправка %s
+ Отправка почты
+ :
+ Синхронизация (Push)
+ Отображается во время ожидания новых сообщений
+ Сообщение
+ Уведомления о сообщениях
+ Разное
+ Прочие уведомления
+ Входящие
+ Исходящие
+ Черновики
+ Корзина
+ Отправленные
+ Сбой отправки почты
+ Версия
+ Журнал отладки
+ Запись диагностических сообщений
+ Личные данные
+ Разрешить запись паролей в журнал
+ Экспорт журнала
+ Экспорт выполнен. Журнал может содержать личные данные. Будьте осторожнее с тем, кому вы их посылаете.
+ Сбой экспорта.
+ Получить новые сообщения
+ Кому:%s
+ Тема
+ Сообщение
+ Подпись
+ ------ Исходное сообщение ------
+ Тема:
+ Отправлено:
+ От:
+ Кому:
+ Копия:
+ %s пишет:
+ %1$s, %2$s пишет:
+ Вы должны добавить хотя бы одного адресата.
+ Поле адресата заполнено неполностью!
+ Контакт не содержит сведений о email.
+ Некоторые вложения не были загружены и не могут быть отправлены.
+ Сообщение не может быть переслано, так как не загружены вложения.
+ Включить цитируемое сообщение
+ Удалить цитату
+ Правка цитаты
+ Удалить вложение
+ От: %s <%s>
+ Кому:
+ Копия:
+ Скрытая:
+ Не получается сохранить вложение.
+ Изображения
+ Отсутствует просмотрщик %s.
+ Загрузить полностью
+ от %1$s
+ Вся переписка
+ От %s
+ Сообщение отменено
+ Черновик сохранён
+ Показать важное
+ Для управления важностью сообщений
+ Строки предпросмотра
+ Имя вместо адреса
+ Показать имя отправителя вместо его адреса
+ Отправитель над темой
+ Показать имя отправителя над темой сообщения
+ Имя из контактов
+ Подбор имён отправителей из контактов
+ Цвет
+ Выделять цветом имена из контактов
+ Цвет контакта
+ Моноширинный шрифт
+ Показать сообщения в формате \"Только текст\" моноширинным шрифтом
+ Вписать по ширине
+ Подогнать текст сообщения по ширине экрана
+ В список после удаления
+ Перейти в список сообщений после удаления сообщения
+ В следующее после удаления
+ Открыть следующее сообщение после удаления сообщения
+ Подтверждать действия
+ Запросить подтверждение действий
+ Удалить
+ Удалить важное (в просмотре)
+ Переместить в спам
+ Отменить сообщение
+ Прочитаны все
+ Удалить (в уведомлении)
+ Скрыть почтовый клиент
+ Убрать Mail User-Agent из заголовков сообщений
+ Скрыть временную зону
+ Использовать UTC вместо местного времени в заголовках сообщений
+ Кнопка \"Удалить\"
+ Никогда
+ Для одиночного
+ Всегда
+ Встроить в экран блокировки
+ Убрать с экрана блокировки
+ Имя приложения
+ Счетчик новых
+ Счётчик сообщений и отправители
+ Как без блокировки
+ Период тишины
+ Отключить мелодию, вибрацию и индикатор на ночь
+ Отключить уведомления
+ Полностью, в период тишины
+ Начало
+ Конец
+ Новый ящик
+ Адрес email
+ Пароль
+
+
+
+ Чтобы увидеть свой пароль, включите блокировку экрана на этом устройстве.
+ Проверка вашей роли
+ Разблокировать для просмотра своего пароля
+ Вручную
+
+ Получение сведений\u2026
+ Проверка сервера входящей почты\u2026
+ Проверка сервера исходящей почты\u2026
+ Аутентификация\u2026
+ Получение настроек\u2026
+ Отмена\u2026
+ Всё почти готово!
+ Имя ящика (необязательно):
+ Ваше имя (видно адресату в сообщениях):
+ Тип ящика
+ Доступные протоколы
+ POP3
+ IMAP
+ Обычный пароль
+ Небезопасный пароль
+ Безопасный пароль
+ Сертификат клиента
+ Сервер входящей почты
+ Логин
+ Пароль
+ Сертификат клиента
+ Сервер POP3
+ Сервер IMAP
+ Сервер Exchange
+ Порт
+ Безопасность
+ Аутентификация
+ Нет
+ SSL/TLS
+ STARTTLS
+ \"%1$s = %2$s\" недействителен для \"%3$s = %4$s\"
+ Удалённое, на сервере
+ Оставить
+ Удалить
+ Прочитано
+ Стирать удалённые с сервера
+ Немедленно
+ При проверке
+ Вручную
+ Автообласть имён IMAP
+ Префикс пути IMAP
+ Папка черновиков
+ Папка отправленных
+ Папка удалённых
+ Папка архивных
+ Папка спама
+ Только подписанные папки
+ Автопереход в папку
+ Путь OWA
+ Путь аутентификации
+ Псевдоним ящика
+ Сервер исходящей почты
+ Сервер SMTP
+ Порт
+ Безопасность
+ Авторизация
+ Логин
+ Пароль
+ Аутентификация
+ \"%1$s = %2$s\" недействителен для \"%3$s = %4$s\"
+ Неверная настройка: %s
+ Настройки ящика
+ Интервал проверки
+ Вручную
+ 15 минут
+ 30 минут
+ 1 час
+ 2 часа
+ 3 часа
+ 6 часов
+ 12 часов
+ 1 сутки
+ Интервал обновления IDLE
+ 2 минуты
+ 3 минуты
+ 6 минут
+ 12 минут
+ 24 минуты
+ 36 минут
+ 48 минут
+ 1 час
+ Уведомлять о новой почте
+ Загружать сообщений
+ 10
+ 25
+ 50
+ 100
+ 250
+ 500
+ 1000
+ 2500 сообщений
+ 5000 сообщений
+ 10000 сообщений
+ Все
+ Нельзя скопировать или переместить сообщение, не синхронизированное с сервером
+ Настройка не завершена
+ Неверные логин или пароль.\n(%s)
+ Сервер предоставляет неверный сертификат SSL. Иногда это обусловлено неправильной настройкой. Или кто-то пытается атаковать Ваш почтовый сервер или Ваш компьютер. Если Вы не уверены в причинах, нажмите Отклонить и свяжитесь с персоналом, обслуживающим почтовый сервер.\n\n(%s)
+ Не удаётся подключиться к серверу.\n(%s)
+ Правка
+ Продолжить
+ Дополнительно
+ Настройки ящика
+ Уведомить о почте
+ Уведомления папок
+ Все
+ 1 класс
+ 1 и 2 классы
+ Кроме 2 класса
+ Нет
+ Уведомить о проверке
+ Ваш адрес email
+ Показать уведомление о новой почте
+ Показать уведомление о проверке почты
+ Также об исходящей
+ Показать уведомление при отправке почты
+ Для контактов
+ Уведомлять только о сообщениях от контактов
+ Игнорировать сообщения чата
+ Не показывать уведомления для сообщений, принадлежащих email чату
+ Открытое прочитано
+ Отметить сообщение прочитанным после просмотра
+ Отметить как прочитанное при удалении
+ Отметить как прочитанное при удалении
+ Категории уведомлений
+ Настройка уведомлений о новых сообщениях
+ Настройка уведомлений об ошибках и статусе
+ Показать изображения
+ Никогда
+ От контактов
+ Всегда
+ Отправка
+ Цитировать при ответе
+ Включать исходное сообщение в ответ
+ Цитата над ответом
+ Разместить исходное сообщение над текстом ответа
+ Без подписей в цитате
+ Удалять подписи из цитируемого текста
+ Формат сообщений
+ Только текст
+ HTML
+ Автоматически
+ Включать Копия/Скрытая
+ Уведомление о прочтении
+ Всегда запрашивать уведомление о прочтении
+ Стиль цитирования
+ Префикс (Gmail)
+ Заголовок (Outlook)
+ Выгрузить отправленные
+ Помещать сообщения в папку \"Отправленные\" после отправки
+ Основные
+ Чтение
+ Получение
+ Папки
+ Префикс цитаты
+ Сквозное шифрование
+ Поддержка OpenPGP
+ Приложение OpenPGP
+ Настройка ключа шифрования
+ OpenPGP не настроен
+ Соединено с %s
+ Конфигурируется…
+ Хранить черновики зашифрованными
+ Все черновики будут зашифрованы
+ Зашифровать черновики, если включено шифрование
+ Интервал проверки
+ Цвет
+ Метка в списке ящиков и папок
+ Отображать сообщений
+ Загружать фрагмент
+ 1 КиБ
+ 2 КиБ
+ 4 КиБ
+ 8 КиБ
+ 16 КиБ
+ 32 КиБ
+ 64 КиБ
+ 128 КиБ
+ 256 КиБ
+ 512 КиБ
+ 1 МиБ
+ 2 МиБ
+ 5 МиБ
+ 10 МиБ
+ Полностью
+ Глубина синхронизации
+ Всё время
+ Сегодня
+ 2 дня
+ 3 дня
+ 1 неделя
+ 2 недели
+ 3 недели
+ 1 месяц
+ 2 месяца
+ 3 месяца
+ 6 месяцев
+ 1 год
+ Видимость папок
+ Все
+ 1-й класс
+ 1-й и 2-й классы
+ Кроме 2-го класса
+ Проверка папок
+ Все
+ 1-й класс
+ 1-й и 2-й классы
+ Кроме 2-го класса
+ Нет
+ Push
+ Все
+ 1-й класс
+ 1-й и 2-й классы
+ Кроме 2-го класса
+ Нет
+ Приёмник копирования
+ Все
+ 1-й класс
+ 1-й и 2-й классы
+ Кроме 2-го класса
+ Синхронное удаление
+ Удалять сообщения при удалении на сервере
+ Отсутствует OpenPGP-приложение – было удалено?
+ Настройки папки
+ Начало списка
+ Отображать ближе к началу списка папок
+ Видимость
+ Нет класса
+ 1-й класс
+ 2-й класс
+ Проверка папок
+ Нет
+ 1-й класс
+ 2-й класс
+ Как видимости
+ Push
+ Нет класса
+ 1-й класс
+ 2-й класс
+ То же, что и класс опроса
+ Уведомление
+ Нет класса
+ 1-й класс
+ 2-й класс
+ Как push
+ Стереть локальные
+ Сервер входящих
+ Настройки сервера входящей почты
+ Сервер исходящих
+ Настройки сервера исходящей почты
+ Имя ящика
+ Ваше имя
+ Уведомления
+ Вибрация
+ Вибрация
+ Cхема вибрации
+ По умолчанию
+ Шаблон 1
+ Шаблон 2
+ Шаблон 3
+ Шаблон 4
+ Шаблон 5
+ Повтор
+ Выключено
+ Мелодия
+ Свет уведомлений
+ Выключено
+ Цвет
+ Системный цвет
+ Белый
+ Красный
+ Зеленый
+ Синий
+ Желтый
+ Голубой
+ Пурпурный
+ Новое сообщение
+ Новое сообщение
+ Отправитель, скрытая копия и подпись по умолчанию
+ Управление ролями
+ Альтернативные значения отправителя и подписи
+ Управление ролями
+ Управление
+ Правка роли
+ Сохранить
+ Новая роль
+ Скрытая копия всех сообщений
+ Правка
+ Выше
+ Ниже
+ Вверх
+ Удалить
+ Название роли
+ опция
+ Ваше имя
+ опция
+ Адрес email
+ обязательно
+ Адрес для ответа
+ опция
+ Подпись
+ опция
+ Использовать подпись
+ Подпись
+ Основная роль
+ Выберите роль
+ Отправитель
+ Нельзя удалить основную роль
+ Нельзя создать роль без адреса email
+ Старые – новые
+ Новые – старые
+ Тема А – Я
+ Тема Я – А
+ Отправитель А – Я
+ Отправитель Я – А
+ Важные – обычные
+ Обычные – важные
+ Не прочитано – прочитано
+ Прочитано – не прочитано
+ С вложениями – без вложений
+ Без вложений – с вложениями
+ Сортировка
+ Дата
+ Получено
+ Тема
+ Отправитель
+ Важность
+ Прочитано
+ Вложение
+ Удаление ящика
+ Неверный сертификат сервера
+ Принять
+ Отклонить
+ Del (или D) – удалить\nR – ответить\nA – ответить всем\nC – создать\nF – переслать\nM – переместить\nV – в архив\nY – копировать\nZ – (не) прочитано\nG – важное\nO – сортировка\nI – порядок\nQ – папки\nS – выбрать\nJ или P – назад\nK или N – далее
+ Del (или D) – удалить\nC – создать\nM – переместить\nV – в архив\nY – копировать\nZ – (не) прочитано\nG – важное\nO – сортировка\nI – порядок\nQ – папки\nS – выбрать
+ Поиск папки
+ Видимость папок
+ Все папки
+ 1-й класс
+ 1-й и 2-й класс
+ Кроме 2-го класса
+ Размещение подписи
+ Перед цитатой
+ После цитаты
+ Как в приложении
+ Тёмная
+ Светлая
+ Использовать умолчания системы
+ Вид
+ Общие
+ Отладка
+ Личное
+ Сеть
+ Интерфейс
+ Список ящиков
+ Список сообщений
+ Сообщения
+ Тема приложения
+ Тема просмотра
+ Тема редактора
+ Язык
+ Настройки не найдены
+ Фиксированный просмотр
+ Разрешить выбор темы при чтении сообщения
+ Отключить выбор темы при чтении сообщения
+ По умолчанию
+ Фоновая синхронизация
+ Никогда
+ Всегда
+ Если вкл. Автосинхронизация
+ Выбрать все
+ Лимит push-проверки
+ 5 папок
+ 10 папок
+ 25 папок
+ 50 папок
+ 100 папок
+ 250 папок
+ 500 папок
+ 1000 папок
+ Анимация
+ Анимация интерфейса
+ Прокрутка кнопками Vol+/-
+ В сообщении
+ В списке
+ Показывать общий ящик для входящих
+ Cчетчик важных
+ Входящие
+ из всех ящиков
+ Общие \"Входящие\"
+ Показать почту в общих \"Входящих\"
+ Папки для поиска
+ Все
+ Видимые
+ Нет
+ Нет
+ Автоматически (%s)
+ Шрифт
+ Настройка размера шрифтов
+ Список ящиков
+ Имя ящика
+ Описание ящика
+ Список папок
+ Имя папки
+ Состояние папки
+ Список сообщений
+ Тема
+ Отправитель
+ Дата
+ Просмотр
+ Сообщение
+ Отправитель
+ Кому
+ Копия
+ Скрытая
+ Все заголовки
+ Тема
+ Дата и время
+ Тело сообщения
+ Редактор сообщения
+ Формы ввода
+ По умолчанию
+ Крохотный
+ Маленький
+ Небольшой
+ Средний
+ Большой
+ Огромный
+ Гигантский
+ Подходящее приложение не найдено
+ Сбой отправки: %s
+ Выберите действие
+ Сохранить черновик или отменить сообщение?
+ Сохранить или отменить изменения?
+ Удаление
+ Отменить сообщение?
+ Выберите текст длительным нажатием
+ Стереть локальные сообщения?
+ Это удалит все локальные сообщения из папки.Сообщения не будут удалены с сервера.
+ Стереть сообщения
+ Подтвердите удаление
+ Вы хотите удалить это сообщение?
+
+ Удалить сообщение?
+ Удалить %1$d сообщения?
+ Удалить %1$d сообщений?
+ Вы действительно хотите удалить %1$d сообщений?
+
+ Да
+ Нет
+ Подтверждение прочтения
+ Отметить все сообщения прочитанными?
+ Подтвердите очистку корзины
+ Вы хотите очистить корзину?
+ Да
+ Нет
+ Переместить в спам
+
+ Переместить сообщение в Спам?
+ Переместить %1$d сообщения в Спам?
+ Переместить %1$d сообщений в Спам?
+ Переместить %1$d сообщений в Спам?
+
+ Да
+ Нет
+ Загрузка вложения
+ »
+ ›
+ Резервная копия
+ Разное
+ Экспортировать настройки
+ Экспорт
+ Передать
+ Экспорт настроек…
+ Настройки успешно экспортированы
+ Сбой экспорта настроек
+ Импорт
+ Выберите файл
+ Импорт
+ Настройки успешно импортированы
+
+ Пожалуйста, введите пароли
+
+
+ Не удалось импортировать настройки
+ Не удалось прочесть файл настроек
+ Не удалось импортировать некоторые настройки
+ Успешный импорт
+ Требуется пароль
+
+ Не импортировано
+ Импорт не удался
+ Позже
+ Импорт
+ Импорт настроек…
+
+ Чтобы использовать ящик \"%s\" необходимо ввести пароль.
+ Чтобы использовать ящик \"%s\" необходимо ввести пароли.
+ Чтобы использовать ящик \"%s\" необходимо ввести пароли.
+ Чтобы использовать ящик \"%s\" необходимо ввести пароли.
+
+ Входящий пароль сервера
+ Исходящий пароль сервера
+ Использовать тот же пароль для исходящего сервера
+ Названия серверов: %s
+ Счётчик непрочитанных
+ Ящик
+ Ящик для отображения счётчика непрочитанных
+ Общие \"Входящие\"
+ Счётчик папок
+ Показывать счётчик непрочитанных только для одной папки
+ Папка
+ Папка для отображения счётчика непрочитанных
+ Готово
+ %1$s – %2$s
+ Ящик не выбран
+ Папка не выбрана
+ Текст отсутствует
+ Открыть
+ Отправить
+ В буфер
+ Ссылка
+ Копировать текст ссылки в буфер обмена
+ Текст ссылки
+ Изображение
+ Открыть
+ Сохранить
+ Загрузить
+ URL в буфер
+ URL изображения
+ Вызвать
+ В контакты
+ В буфер
+ Номер
+ Написать
+ В контакты
+ В буфер
+ Адрес email
+ Все
+ 10
+ 25
+ 50
+ 100
+ 250
+ 500
+ 1000
+ Лимит поиска на сервере
+ Отправка запроса на сервер
+
+ Получение %d результата
+ Получение %d результатов
+ Получение %d результатов
+ Получение %d результатов
+
+
+ Получение %1$d из %2$d результатов
+ Получение %1$d из %2$d результатов
+ Получение %1$d из %2$d результатов
+ Получение %1$d из %2$d результатов
+
+ Сбой поиска на сервере
+ Поиск
+ Поиск на сервере
+ Для поиска на сервере необходимо подключение к сети
+ Менять цвет при прочтении
+ Другой фон для прочитанных сообщений
+ Цепочки сообщений
+ Объединить сообщения, показав счётчик
+ Обновление данных
+ Обновление данных…
+ Обновление ящика \"%s\"
+ Разделить экран
+ Всегда
+ Нет
+ Ландшафт
+ Выберите сообщение
+ Фото контактов
+ Показать фото контактов в списке сообщений
+ Прочитаны все
+ Цветные места для фото
+ Инициалы контактов на цветном фоне при отсутствии фото
+ Действия для сообщения
+ Видимость действий для открытого сообщения
+ Загрузка вложений…
+ Отправка сообщения
+ Сохранение черновика
+ Получение вложений…
+ Аутентификация не удалась. Сервер не сообщает о совместимости с SASL EXTERNAL. Причиной может служить неверный сертификат клиента (просрочен, неизвестен издатель) или проблемы с настройками
+
+ Использовать сертификат клиента
+ Не использовать сертификат клиента
+ Отключить выбор сертификата клиента
+ Не найден сертификат клиента для алиаса \"%s\"
+ Дополнительно
+ Срок действия сертификата клиента \"%1$s\" истёк или ещё не наступил (%2$s)
+
+ *Зашифровано*
+ Добавить из Контактов
+ Копия
+ Скрытая
+ Кому
+ От
+ Ответить на
+ Неизвестный получатель
+ Неизвестный отправитель
+ Дом
+ Работа
+ Прочее
+ Мобильный
+ Для данного ящика не настроена папка Черновики!
+ Для данного ящика не настроены ключи! Проверьте настройки
+ Криптопровайдер использует несовместимую версию. Проверьте настройки!
+ Нет доступа к криптопровайдеру. Проверьте настройки или нажмите значок шифрования для повтора!
+ Ошибка инициализации сквозного шифрования. Проверьте настройки
+ Режим PGP/INLINE не поддерживает вложения!
+ Включить PGP/INLINE
+ Отключить PGP/INLINE
+ Включить PGP \"Только подпись\"
+ Отключить PGP \"Только подпись\"
+ Режим PGP/INLINE
+ Сообщение отправлено в формате PGP/INLINE.\nИспользуйте только для совместимости
+ Некоторые клиенты поддерживают только этот формат
+ Подписи могут повредиться при передаче
+ Вложения не поддерживаются
+ Понятно!
+ Отключить
+ Не отключать
+ Понятно!
+ Отключить
+ Не отключать
+ Режим PGP \"Только подпись\"
+ В этом режиме, ключ PGP будет использоваться для создания криптографической подписи к незашифрованному сообщению
+ Адресат сможет убедиться, что сообщение подлинное и подписано Вашим ключом
+ Подписи могут повредиться при отправке нескольким адресатам
+ Подписи могут отображаться как вложенные файлы \'signature.asc\'
+ Всегда добавлять подпись к шифрованным сообщениям
+ Текст
+ неверная подпись сквозного шифрования
+ требуется полная загрузка сообщения для проверки подписи
+ содержит неподдерживаемую подпись сквозного шифрования
+ Сообщение зашифровано в неподдерживаемом формате
+ Сообщение зашифровано, дешифрование отменено
+ текст, подписанный ключом сквозного шифрования
+ от проверенного подписанта
+ Подписанный текст
+ но ключ сквозного шифрования не соответствует отправителю
+ но ключ сквозного шифрования просрочен
+ но ключ сквозного шифрования отозван
+ но ключ сквозного шифрования небезопасен
+ но ключ сквозного шифрования неизвестен
+ Зашифровано
+ но был сбой дешифрования
+ требуется полная загрузка сообщения для дешифрования
+ но отсутствует настроенное криптоприложение
+ Зашифровано
+ но без сквозного шифрования
+ со сквозным шифрованием
+ от проверенного отправителя
+ Зашифровано
+ но ключ сквозного шифрования неизвестен
+ но ключ сквозного шифрования не соответствует отправителю
+ но ключ сквозного шифрования просрочен
+ но ключ сквозного шифрования отозван
+ но ключ сквозного шифрования небезопасен
+ но произошёл сбой в данных сквозного шифрования
+ но шифрование не считается безопасным
+ OK
+ Поиск криптоключа
+ Кем подписано
+ Кем отправлено
+ Подробности
+ Разблокировать
+ Эта часть не была зашифрована и небезопасна
+ Незащищённое вложение
+ Загрузка…
+ Дешифрование отменено.
+ Повтор
+ Зашифрованное сообщение необходимо загрузить для дешифрования
+ Сбой дешифрования почты
+ Спецсимволы пока не поддерживаются!
+ Сбой разбора адреса!
+ Скрыть подписи без шифрования
+ Отображаются зашифрованные подписи
+ Отображаются все подписи
+ Шифрование недоступно в режиме \"Только подпись\"
+ Неподписанный текст
+ Сообщение зашифровано
+ Сообщение зашифровано OpenPGP.\nЧтобы прочесть его, необходимо установить и настроить подходящее OpenPGP-приложение.
+ В Настройки
+ Сообщения Mail
+ Загрузка сообщений…
+ Шифрование невозможно
+ Не все выбранные адресаты поддерживают эту возможность!
+ Включить шифрование
+ Отключить шифрование
+ Шифрование сообщений гарантирует, что никто, кроме получателя, не сможет их прочесть.
+ Шифрование отображается, если поддерживается всеми адресатами и они писали Вам ранее.
+ Включите шифрование, нажав этот значок.
+ Понятно
+ Назад
+ Отключить шифрование
+ Шифрование OpenPGP
+ Взаимное автошифрование
+ Взаимное автошифрование
+ Сообщения шифруются по желанию или при ответе на шифрованное сообщение
+ Если отправитель и получатели включили режим взаимного автошифрования, включать шифрование по умолчанию.
+ Нажмите здесь для подробностей.
+ Основные
+ OpenPGP не установлен
+ Установить
+ Для сквозного шифрования в Mail нужен OpenKeychain.
+ Зашифрованное сообщение
+ Шифровать темы сообщений
+ Может поддерживаться не всеми адресатами
+ Внутренний сбой: неверный ящик!
+ Сбой подключения к %s!
+ Отправка настроек автошифрования
+ Передать настройки шифрования на другие устройства
+ Настройки автошифрования
+ Сообщение с настройками взаимного автошифрования позволяет безопасно передать эти настройки на другие устройства.
+ Отправить настройки
+ Сообщение будет отправлено на ваш адрес:
+ Создание сообщения с настройками…
+ Отправка сообщения для:
+ Для завершения, откройте сообщение на новом устройстве и введите код установки.
+ Показать код установки
+ Настройки взаимного автошифрования
+ Это сообщение содержит все сведения, необходимые для переноса настроек взаимного автошифрования вместе с секретным ключом с исходного устройства.
Для настройки взаимного автошифрования на новом устройстве, следуйте инструкциям, отображаемым на новом устройстве.
Это сообщение можно сохранить и использовать в качестве резервной копии своего секретного ключа. Запишите и надёжно сохраните пароль.
- Сбой при отправке сообщения. Проверьте соединение с сетью и настройки сервера исходящей почты.
- Вкл
- Отключить
- Открыть
- Закрыть
-
- Разрешить доступ к контактам
- Чтобы подсказывать имена и показывать имена контактов и фото, приложению нужен доступ к контактам.
- При загрузке данных возникла ошибка
- Узнайте, что нового в этом выпуске
- Показывать последние изменения после того, как приложение было обновлено
- Условия предоставления услуг
- Политика конфиденциальности
- Почта является форком K9 Mail
- -Почта - это почтовый клиент для Android с открытым исходным кодом.
- Исходный код
- Лицензия
- Версия сборки
- Информация о почте
- Авторы
- Отключить push-уведомления
- Если вам не нужны мгновенные уведомления о новых сообщениях, вам следует отключить push-уведомления и использовать интервальный опрос. Интервальный опрос проверяет наличие новой почты через регулярные промежутки времени и не нуждается в уведомлениях.
- Настройка уведомления
- Узнать больше
- Однако Android также позволяет скрыть уведомление.
- При использовании push-уведомлений, K-9 Mail поддерживает соединение с почтовым сервером. Android требует отображения постоянного уведомления, пока приложение активно в фоновом режиме. %s
- Информация о push-увеломлениях
- Нажмите, чтобы узнать больше.
- Спящий режим до тех пор, пока не будет доступна сеть
- Спящий режим до тех пор, пока не будет разрешена фоновая синхронизация
- Ожидание новых email сообщений
- Инициализация…
- Такой же, как и класс опроса
- Проверка XOauth2 (для Gmail, Outlook)
- Проигрывать звук когда email успешно отправлен
- Звук отправленного email
- Показывается при ожидании новых сообщений
- Синхронизировать (Нажать)
- Предметный текст скопирован в буфер обмена
- Поиск везде
- Что нового
- Ответить
- Показывать количество помеченных
- Настроить уведомления для новых сообщений
- Настроить уведомления об ошибках и статусе
- Категории уведомленийk
- Не показывать уведомления для сообщений, принадлежащих email-чату
- Игнорировать сообщения чата
- Разблокируйте, чтобы просмотреть пароль
- Подтвердите свою личность
- Чтобы просмотреть здесь пароль, включите блокировку экрана на этом устройстве.
- Проверка почты на всех аккаунтах
- Переместить в…
- Копировать в…
- Новые сообщения
- Ошибка сертификата
- Ошибка экспорта.
- Экспорт журналов
- Экспорт прошел успешно. Журналы могут содержать конфиденциальную информацию. Будьте осторожны с тем, кому вы их отправляете.
- Каждый 5 минут
- Световой индикатор уведомления
- Отключено
- Цвет учётной записи
- Белый
- Красный
- Зелёный
- Синий
- Желтый
- Голубой
- Пурпурный
- Цвет имени контакта
- Вибрация
- Отключено
- Цвет по умолчанию в системе
+ Сбой при отправке сообщения. Проверьте соединение с сетью и настройки сервера исходящей почты.
+ Вкл
+ Откл
+
+ Разрешить доступ к контактам
+ Чтобы подсказывать имена и показывать фото, приложению нужен доступ к контактам
+ При загрузке данных возникла ошибка
+ Инициализация…
+ Ожидание новых писем
+ Спящий режим до тех пор, пока не будет разрешена фоновая синхронизация
+ Спящий режим до появления сети
+ Нажмите, чтобы узнать больше.
+ Всплывающая информация
+ При использовании Push Mail поддерживает соединение с почтовым сервером. Android требует отображения постоянного уведомления, пока приложение активно в фоновом режиме. %s
+ Однако Android также позволяет скрыть уведомление.
+ Узнать больше
+ Настроить уведомление
+ Если вам не нужны мгновенные уведомления о новых сообщениях, вам следует отключить Push и использовать Опрос. Опрос проверяет наличие новой почты через регулярные промежутки времени и не нуждается в уведомлениях.
+ Отключить Push
\ No newline at end of file
diff --git a/app/ui/legacy/src/main/res/values-sk/strings.xml b/app/ui/legacy/src/main/res/values-sk/strings.xml
index 39b63a0b3fd863e1a07a62f1efb998e90ee4d8a6..0f3b0db39df7993af9f86f0a380cf6ff6c990c87 100644
--- a/app/ui/legacy/src/main/res/values-sk/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sk/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
+ The Mail Dog WalkersZdrojový kódLicencované pod licenciou Apache 2.0.Projekt s otvoreným zdrojovým kódom
@@ -239,6 +239,8 @@
Nastaviť nový účetE-mailová adresaHeslo
+
+
Ručné nastavenie
@@ -277,10 +279,6 @@
Ponechať na serveriVymazať na serveriOznačiť ako prečítanú na serveri
- Použiť kompresiu na sieti:
- Mobilná
- Wi-Fi
- InáOdstrániť vymazané správy zo serveraIhneďPri synchronizovaní
@@ -700,12 +698,16 @@
Vybrať súborImportovaťNastavenia úspešne importované
+
Zadajte heslá
+
+
Import nastavení zlyhalNepodarilo sa prečítať súbor s nastaveniamiImport časti nastavení zlyhalImport prebehol úspešneVyžadované heslo
+
NenaimportovanéZlyhanie pri importeNeskôr
@@ -845,14 +847,6 @@
Chyba pri spracovaní adresy!V režime \"len podpis\" nie je možné šifrovať.Nepodpísaný text
- Varovanie o konci podpory APG
- Aplikácia APG už nie je udržiavaná!
- Podpora APG bola z K-9 Mail odstránená.
- Vývoj je zastavený od roku 2014
- Obsahuje bezpečnostné problémy
- Viac informácií nájdete v správe na github.io.
- Rozumiem!
- APGTáto správa je zašifrovanáTáto správa bola zašifrovaná pomocou OpenPGP\nJe potrebné nakonfigurovať kompatibilnú OpenPGP aplikáciu.Mail Message List
diff --git a/app/ui/legacy/src/main/res/values-sl/strings.xml b/app/ui/legacy/src/main/res/values-sl/strings.xml
index 7aa85bc60da85c9edcfc0bfe51605d6356826fa5..50c6c134756d384dcb975dfcbcb29b1dafce65d1 100644
--- a/app/ui/legacy/src/main/res/values-sl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sl/strings.xml
@@ -7,9 +7,9 @@
Mail AccountsMail Unread
- Sprehajalci psov v skupini The K-9 Dog Walkers
+ Sprehajalci psov v skupini The Mail Dog WalkersIzvorna koda
- Licenca Apache, različica 2.0
+ Dovoljenje Apache, različica 2.0Odprtokodni projektSpletna stranUporabniški forum
@@ -20,9 +20,9 @@
Dnevnik spremembDnevnika sprememb ni bilo mogoče naložiti.Različica %s
- Kaj je novega
- Prikaže zadnje spremembe, ko je bila aplikacija posodobljena
- Poglej, kaj je novega v tej izdaji
+ Kaj je novega?
+ Pokaže zadnje spremembe po posodobitvi programa
+ Preglejte, kaj je novega v tej izdajiWelcome to MailMail is the default mail client for /e/
@@ -91,6 +91,7 @@
Označi z zvezdicoOdstrani zvezdicoKopiraj
+ Prekliči naročninoPrikaži glave sporočilNaslov je kopiran v odložišče
@@ -98,7 +99,7 @@
Naslovi so kopirani v odložiščeNaslovi so kopirani v odložišče
- Tekst zadeve kopiran v odložišče
+ Besedilo zadeve je kopirano v odložiščePreklop na temno barvno shemoPreklop na svetlo barvno shemoOznači kot neprebrano
@@ -150,7 +151,7 @@ dodatnih %d sporočil
Pošiljanje pošte: %sPošiljanje pošte:
- Sinhronizacija (potisk)
+ Usklajevanje (potisno)Prikazano med čakanjem na nova sporočilaSporočilaObvestila v zvezi s sporočili
@@ -256,6 +257,11 @@ dodatnih %d sporočil
Nastavitev novega računaNaslov elektronske pošteGeslo
+ Če želiš uporabljati ta e-poštni račun s Mail, se moraš prijaviti in aplikaciji odobriti dostop do svojih e-poštnih sporočil.
+
+ Prijava
+
+ Prijavi se z GoogleČe si želiš ogledati svoje geslo tukaj, omogoči zaklepanje zaslona na tej napravi.Preverjanje identitete
@@ -279,6 +285,7 @@ dodatnih %d sporočil
Geslo, posredovano na ne-varen načinŠifrirano gesloDigitalno potrdilo
+ OAuth 2.0Nastavitve dohodnega strežnikaUporabniško imeGeslo
@@ -297,10 +304,7 @@ dodatnih %d sporočil
Naj se ne izbriše s strežnikaNaj se izbriše s strežnikaNaj bo na strežniku označeno kot prebrano
- Uporabi omrežno stiskanje:
- Mobilno
- Wi-Fi
- Drugo
+ Uporabi stiskanjePočisti izbrisana sporočila na strežnikuPočisti takojPočisti med izpraševanjem
@@ -365,6 +369,10 @@ dodatnih %d sporočil
Napačno uporabniško ime ali geslo\n(%s)Strežnik je objavil neveljavno potrdilo SSL. To je lahko posledica napačne nastavitve strežnika ali pa poskusa napada na poštni strežnik. Če niste prepričani v nastavitve, zavrnite sprejem potrdila in stopite v stik s skrbnikom strežnika.\n\nNapaka:\n(%s)S strežnikom ni mogoče vzpostaviti povezave.\n(%s)
+ Avtorizacija preklicana
+ Avtorizacija ni uspela zaradi te napake: %s
+ OAuth 2.0 trenutno ni podprt s tem ponudnikom.
+ Aplikacija ni mogla najti brskalnika za odobritev dostopa do tvojega računa.Uredi podrobnostiNadaljujNapredne možnosti
@@ -521,7 +529,7 @@ dodatnih %d sporočil
ObvestilaTresenjeTresenje naprave ob prejemu novega sporočila
- Vzorec tresenja
+ Vzorec tresenja napraveprivzetoVzorec 1Vzorec 2
@@ -538,7 +546,7 @@ dodatnih %d sporočil
belardečazelena
- plava
+ modrarumenasvetlo modraškrlatna
@@ -652,7 +660,7 @@ dodatnih %d sporočil
V pogledu sporočilV pogledu seznamovPokaži skupno mapo prejetih sporočil
- Pokaži število označenih z zvezdico
+ Pokaži število označb z zvezdicoSkupna mapa prejetih sporočilVsa sporočila v skupnih mapahZdruži
@@ -699,7 +707,7 @@ dodatnih %d sporočil
Pošiljanje je spodletelo: %sAli želite shraniti osnutek sporočila?Ali želite sporočilo shraniti ali zavreči?
- Želiš shraniti ali zavreči spremembe?
+ Ali želite shraniti ali zavreči spremembe?Ali želite zavreči sporočilo?Ali ste prepričani, da želite zavreči to sporočilo?Izberite besedilo za kopiranje.
@@ -746,12 +754,16 @@ dodatnih %d sporočil
Izberi datotekoUvoziNastavitve so bile uspešno uvožene
+
Prosim, vnesite geslo
+
+
Napaka pri uvozu nastavitevNapaka pri branju datoteke nastavitevNapaka pri uvozu nekaterih nastavitevUspešno uvoženoGeslo je potrebno
+
Ni uvoženoNapaka pri uvozuKasneje
@@ -948,14 +960,6 @@ dodatnih %d sporočil
Izpisani bodo vsi podpisiŠifriranje v načinu ločenega podpisa ni na voljo!Nepodpisano besedilo
- Opozorilo o zastarelosti programa APG
- Program APG že nekaj časa ni bil posodobljen!
- Zato je zanj iz varnostnih vidikov znotraj programa K-9 Mail podpora odstranjena.
- Razvoj se je končal zgodaj leta 2014
- Vsebuje nerazrešene varnostne napake
- Več o tem si je mogoče prebrati na spletišču GitHub.
- Razumem!
- APGSporočilo je šifriranoSporočilo je šifrirano s programom OpenPGP.\nZa ogled vsebine je treba namestiti ustrezen podprt program OpenPGP.Pojdi v Nastavitve
@@ -1018,7 +1022,7 @@ Sporočilo lahko shranite in ga uporabite kot varno kopijo šifrirnega ključa.
Informacije o potiskuKo uporabljate potisk, K-9 Mail vzdržuje povezavo s poštnim strežnikom. Android zahteva prikaz obvestila, ko je aplikacija aktivna v ozadju. %sVendar pa vam Android tudi omogoča, da obvestilo skrijete.
- Nauči se več
+ Več o temNastavitev obvestilaČe ne potrebuješ takojšnjih obvestil o novih sporočilih, moraš onemogočiti potiskanje in uporabiti navadno izpraševanje. Izpraševanje išče novo pošto v rednih časovnih presledkih in ne potrebuje obvestila.Onemogoči potiskanje
diff --git a/app/ui/legacy/src/main/res/values-sq/strings.xml b/app/ui/legacy/src/main/res/values-sq/strings.xml
index c3e69620d3ab2f136ea2b26cad869de71719e8d4..e8061dbffade8579b1046e7ba41e1491f84213fa 100644
--- a/app/ui/legacy/src/main/res/values-sq/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sq/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
+ The Mail Dog WalkersKod burimApache License, Version 2.0Projekt Me Burim të Hapët
@@ -91,6 +91,7 @@
Shtoji yllHiqi yllKopjoje
+ ShpajtomëniShfaqi kryetAdresa u kopjua në të papastër
@@ -252,6 +253,11 @@
Rregulloni një llogari të reAdresë emailFjalëkalim
+ Që të përdorni këtë llogari email me Mail, lypset të bëni hyrjen dhe t’i akordoni aplikacionit hyrje te email-et tuaj.
+
+ Hyni
+
+ Hyni me GoogleQë të shihni fjalëkalimin tuaj këtu, aktivizoni kyçje ekrani në këtë pajisje.Verifikoni identitetin tuaj
@@ -275,6 +281,7 @@
Fjalëkalim, i transmetuar në mënyrë të pasigurtFjalëkalim i fshehtëzuarDëshmi klienti
+ OAuth 2.0Rregullime shërbyesi për marrjeEmër përdoruesiFjalëkalim
@@ -293,10 +300,7 @@
Mos e fshi te shërbyesiFshije te shërbyesiShënoje si të lexuar te shërbyesi
- Përdor ngjeshje në rrjet:
- Celular
- Wi-Fi
- Tjetër
+ Përdor ngjeshjeHiqi dhe në shërbyes mesazhet e fshiraMenjëherëKur vilet
@@ -361,6 +365,10 @@
Emër përdoruesi ose fjalëkalim i pasaktë.\n(%s)Shërbyesi paraqiti një dëshmi SSL të pavlefshme. Ndonjëherë kjo ndodh për shkak të ndonjë keqformësimi të shërbyesit. Ndonjëherë vjen ngaqë dikush po rreket të sulmojë shërbyesin tuaj të postës. Nëse s\’jeni i sigurt se ç\’po ndodh, klikoni mbi Hidhe Tej dhe lidhuni me personat që administrojnë shërbyesin tuaj të postës.\n\n(%s)S\’lidhet dot te shërbyesi.\n(%s)
+ Autorizimi u anulua
+ Autorizimi dështoi me gabimin vijues: %s
+ OAuth 2.0 aktualisht nuk mbulohet me këtë furnizues shërbimi.
+ Aplikacioni s’gjeti dot një shfletues për ta përdorur për t’i akorduar hyrje te llogaria juaj.Përpunoni hollësitëVazhdoTë mëtejshme
@@ -738,12 +746,19 @@
Përzgjidhni kartelëImportojeRregullimet u importuan me sukses
+
Ju lutemi, jepni fjalëkalime
+
+ Ju lutemi, bëni hyrjen
+
+ Ju lutemi, bëni hyrjen dhe jepni fjalëkalimeS’u arrit të importohen rregullimeS’u arrit të lexohet kartelë rregullimeshS’u arrit të importohen disa rregullimeU importuan me suksesLypset fjalëkalim
+
+ Hyrje e domosdoshmeI paimportuarDështim importimiMë vonë
@@ -934,14 +949,6 @@
Do të shfaqen krejt nënshkrimetS\’mund të kihet fshehtëzim nën mënyrën vetëm-nënshkrim!Tekst i Panënshkruar
- Sinjalizim APG-je të Nxjerrë Nga Përdorimi
- Projekti APG nuk mirëmbahet më!
- Për këtë shkak, është hequr mbulimi i APG-së nga K-9 Mail.
- Zhvillimi ndali në fillimet e 2014-s
- Përmban probleme sigurie të pandrequra
- Mund të klikoni këtu për të mësuar më tepër.
- E mora vesh!
- APGKy email është i fshehtëzuarKy email është fshehtëzuar me OpenPGP.\nQë ta lexoni, lypset të instaloni dhe formësoni një aplikacion të përputhshëm me OpenPGP-në.Kaloni te Rregullimet
@@ -1002,7 +1009,7 @@ Mund ta mbani këtë mesazh dhe ta përdorni si një kopjeruatje të kyçit tuaj
Po dremitet, deri sa të ketë rrjetQë të mësoni më tepër, prekeniHollësi Push-i
- Kur përdoret Push, K-9 Mail mban një lidhje me shërbyesin e postës. Android-i lyp shfaqjen e një njoftimi të vazhdueshëm, teksa aplikacioni është aktiv në prapaskenë. %s
+ Kur përdoret Push, Mail mban një lidhje me shërbyesin e postës. Android-i lyp shfaqjen e një njoftimi të vazhdueshëm, teksa aplikacioni është aktiv në prapaskenë. %sMegjithatë, Android-i ju lejon gjithashtu ta fshihni njoftimin.Mësoni më tepërFormësoni njoftim
diff --git a/app/ui/legacy/src/main/res/values-sr/strings.xml b/app/ui/legacy/src/main/res/values-sr/strings.xml
index cc5c9c229d4d440665117cd1b87e223068d01aab..7a9b0616d64e69e0ec5ea44a6d39f57af2928813 100644
--- a/app/ui/legacy/src/main/res/values-sr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sr/strings.xml
@@ -238,6 +238,8 @@
Постави нови налогАдреса е-поштеЛозинка
+
+
Ручна постава
@@ -276,10 +278,6 @@
не бриши са сервераобриши са сервераозначи као прочитану на серверу
- Користи компресију на мрежи:
- мобилној
- бежичној
- осталимУклони обрисане поруке са сервераодмахприликом провере
@@ -697,12 +695,16 @@
Изаберите фајлУвозПоставке успешно увезене
+
Унесите лозинке
+
+
Не могу да увезем поставкеНе могу да читам фајл поставкиНе могу да увезем неке поставкеУспешно увезеноНеопходна је лозинка
+
Није увезено Увоз неуспешанКасније
@@ -895,14 +897,6 @@
Биће приказани сви потписиШифровање није доступно у режиму „само-потпис“!Непотписан текст
- Упозорење о застарењу АПГ-а
- АПГ се више не одржава!
- Због овога, подршка за АПГ је уклоњена из К-9 поште.
- Развој је заустављен почетком 2014
- Садржи непоправљене безбедносне пропусте
- Кликните овде да сазнате више.
- Важи!
- АПГОва е-порука је шифрованаОва е-порука је шифрована помоћу у ОпенПГП-а.\nДа бисте је прочитали, треба да инсталирате и подесите компатибилну ОпенПГП апликацију.Иди на Поставке
diff --git a/app/ui/legacy/src/main/res/values-sv/strings.xml b/app/ui/legacy/src/main/res/values-sv/strings.xml
index 8ddb11fe22e25a4a383464f2e45b8c686a24ba84..791304ba898799dd76654eb65cc66fb589e0c688 100644
--- a/app/ui/legacy/src/main/res/values-sv/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sv/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
- Copyright 2008-%s The K-9 Dog Walkers. Vissa delar har Copyright 2006-%s Android Open Source Project.
+ The Mail Dog WalkersKällkodApache License, Version 2.0Projekt med öppen källkod
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.Upphovsmän
- RevisionsinformationLäs e-postTillåter att denna applikation läser dina e-postmeddelanden.Ta bort e-postTillåter att denna applikation tar bort dina e-postmeddelanden.About MailKonton
- MapparAvanceratSkrivaSvara
@@ -51,12 +48,6 @@
Välj mappFlytta till…Kopiera till…
- Kontrollerar %s:%s%s
- Hämtar rubriker %s:%s%s
- Skickar %s%s
- Behandlar %s:%s%s
- \u0020%d/%d
- Synkronisering inaktiverad%d valdaNästaFöregående
@@ -64,10 +55,7 @@
OKAvbrytSkicka
- Skicka igenÄmnet är tomt, klicka igen för att skicka ändå
- Välj
- Välj bortSvaraSvara allaTa bort
@@ -103,6 +91,7 @@
Lägg till stjärnaTa bort stjärnaKopiera
+ AvregistreraVisa meddelanderubrikerAdress kopierad till urklipp
@@ -204,17 +193,12 @@
Till:Kopia:Blindkopia:
- Öppna
- SparaDet går inte att spara bilaga.Visa bilderKan inte hitta visare för %s.Hämta hela meddelandet
- Det går inte att visa meddelandevia %1$s
- Hela meddelanderubriken har hämtats, men det det finns inga ytterligare meddelanderubriker att visa.
- Det gick inte att hämta ytterligare meddelanderubriker från databas eller e-postserver.Mer från denna avsändareFrån %sMeddelandet kasserades
@@ -248,7 +232,7 @@
Markera alla meddelanden som lästaTa bort (från avisering)Dölj e-postklient
- Ta bort K-9-användaragent från meddelanderubriker
+ Ta bort Mail-användaragent från meddelanderubrikerDölj tidszonAnvänd UTC istället för lokal tidszon i postrubriker och svarsrubrikVisa knappen \"Ta bort\"
@@ -270,6 +254,11 @@
Konfigurera ett nytt kontoE-postadressLösenord
+ För att använda detta e-postkonto med Mail måste du logga in och ge appen åtkomst till dina e-postmeddelanden.
+
+ Logga in
+
+ Logga in med GoogleFör att se ditt lösenord här, aktivera skärmlåset på denna enhet.Verifiera din identitet
@@ -293,6 +282,7 @@
Lösenord, skickas oskyddatKrypterat lösenordKlientcertifikat
+ OAuth 2.0Inställningar för inkommande serverAnvändarnanmLösenord
@@ -311,10 +301,7 @@
Ta inte bort från servernTa bort från servernMarkera som läst på servern
- Använd komprimering på nätverket:
- Mobilt nätverk
- Wi-Fi
- Annat
+ Använd kompressionTa bort borttagna meddelanden från servernDirektVid e-postkontroll
@@ -329,11 +316,8 @@
Visa endast prenumererade mapparExpandera automatiskt mappSökväg för Outlook Web App
- valfrittSökväg för autentisering
- valfrittAlias för brevlåda
- valfrittInställningar för utgående serverSMTP-serverPort
@@ -345,7 +329,6 @@
\"%1$s = %2$s\" är inte giltigt med \"%3$s = %4$s\"Ogiltig konfiguration: %sKontoinställningar
- Komprimera kontoFrekvens för mappkontrollAldrigVar 15:e minut
@@ -356,8 +339,6 @@
Var 6:e timmeVar 12:e timmeVar 24:e timme
- Aktivera push-e-post för detta konto
- Om det stöds av din server så kommer nya meddelanden att levereras omedelbart. Denna inställning kan dramatiskt förbättra eller försämra prestanda.Förnya IDLE-anslutningVarannan minutVar 3:e minut
@@ -368,7 +349,6 @@
Var 48:e minutVar 60:e minutMeddela mig vid ankomst av e-post
- Meddela mig vid kontroll av e-postAntal meddelanden att visa10 meddelanden25 meddelanden
@@ -386,12 +366,14 @@
Felaktigt användarnamn eller lösenord.\n(%s)Servern presenterade ett ogiltigt SSL-certifikat. Ibland beror det på en serverfelkonfiguration. Ibland beror det på att någon försöker attackera dig eller din e-postserver. Om du inte är säker på vad som händer klickar du på Avvisa och kontaktar de personer som hanterar din e-postserver.\n\n(%s)Kan inte ansluta till server.\n(%s)
+ Auktoriseringen avbröts
+ Auktoriseringen misslyckades med följande fel: %s
+ OAuth 2.0 stöds för närvarande inte av denna leverantör.
+ Appen kunde inte hitta en webbläsare att använda för att ge åtkomst till ditt konto.Redigera detaljerFortsättAvanceratKontoinställningar
- Standardkonto
- Skicka e-post från detta konto som standardAviseringar vid ny e-postAviseringsmapparAlla
@@ -457,9 +439,6 @@
Frekvens för mappkontrollKontofärgFärg för detta konto i mapp- och kontolista
- Ingen färg
- Färg på LED vid avisering
- Den färg som enhetens LED ska visa för detta kontoMappstorlek lokaltHämta meddelanden upp till1 KiB
@@ -711,8 +690,6 @@
ÄmneTid och datumInnehåll
- %d%%
- %1$s: %2$sMeddelandekompositionFält för textinmatningStandard
@@ -770,12 +747,19 @@
Välj filImporteraInställningarna importerade
+
Ange lösenord
+
+ Logga in
+
+ Logga in och ange lösenordDet gick inte att importera inställningarDet gick inte att läsa inställningsfilenDet gick inte att importera vissa inställningarImporteradesLösenord krävs
+
+ Inloggning krävsInte importeradImportfelSenare
@@ -966,21 +950,11 @@
Alla signaturer visasKryptering otillgänglig i signera-endast läge!Osignerad text
- APG utfasnings varning
- APG upprätthålls inte längre!
- På grund av detta, har stöd för APG tagits bort från K-9 Mail.
- Utveckling slutade i början av 2014
- Innehåller orättade säkerhetsproblem
- Du kan klicka här för att lära dig mera.
- Uppfattat!
- APGDetta e-postmeddelande är krypteratDetta e-postmeddelande har krypterats med OpenPGP.\nFör att läsa det, måste du installera och konfigurera en kompatibel OpenPGP-app.Gå till inställningarK-9 MeddelandelistaLäser in meddelanden…
- Det gick inte att hämta mapplista
- Fel vid hämtning av mottagarens status från OpenPGP-leverantör!Kryptering inte möjligtNågra av de valda mottagarna stöder inte denna funktion!Aktivera kryptering
@@ -1000,7 +974,7 @@
Allmänna inställningarIngen OpenPGP-app installeradInstallera
- K-9 Mail kräver OpenKeychain för ände-till-ände-kryptering.
+ Mail kräver OpenKeychain för ände-till-ände-kryptering.Krypterat meddelandeKryptera meddelandeämnenKanske inte stöds av vissa mottagare
@@ -1026,8 +1000,6 @@ Du kan behålla detta meddelande och använda det som en säkerhetskopia för di
Ett fel uppstod under tiden meddelandet skickades. Kontrollera din nätverksanslutning och utgående serverkonfiguration.PåAv
- Öppna
- StängTillåt åtkomst till kontakterFör att kunna ge kontaktförslag och visa kontaktnamn och foton behöver appen tillgång till dina kontakter.
@@ -1038,7 +1010,7 @@ Du kan behålla detta meddelande och använda det som en säkerhetskopia för di
Sover tills nätverket är tillgängligtTryck för att lära dig mer.Push-info
- Vid användning av Push upprätthåller K-9 Mail en anslutning till e-postservern. Android kräver att ett pågående meddelande visas medan appen är aktiv i bakgrunden. %s
+ Vid användning av Push upprätthåller Mail en anslutning till e-postservern. Android kräver att ett pågående meddelande visas medan appen är aktiv i bakgrunden. %sMen Android låter dig också dölja aviseringen.Lär dig merAnpassa avisering
diff --git a/app/ui/legacy/src/main/res/values-tr/strings.xml b/app/ui/legacy/src/main/res/values-tr/strings.xml
index 10ccea35a6cde671219ff9fab76f22619d11d4de..b206b8a7e42d8c7090b50a8be4f1b36a0682fc9c 100644
--- a/app/ui/legacy/src/main/res/values-tr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-tr/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- K-9 Köpek Gezdiricileri
+ Mail Köpek GezdiricileriKaynak koduApache Lisansı Uyarlama 2.0Açık Kaynak Projesi
@@ -231,6 +231,7 @@
Kilit Ekranı BildirimleriKilit ekranı bildirimi yokUygulama adı
+ Yeni mesaj sayısıMesaj sayısı ve gönderenlerEkran kilidi açıkken olduğu gibiSessiz Zaman
@@ -242,6 +243,10 @@
Bir yeni hesap kurunE-posta adresiParola
+
+ Giriş yap
+
+ Google ile giriş yapŞifrenizi görmek için ekran kilidini etkinleştirin.Kimliğinizi doğrulayın
@@ -283,10 +288,6 @@
Sunucudan silmeSunucudan silSunucuda okundu olarak işaretle
- Ağda sıkıştırma kullan:
- Cep Telefonu
- Wi-Fi
- DiğerSunucudan silinen mesajları temizleHemenOylama zamanı
@@ -351,6 +352,7 @@
Kullanıcı adı veya parola doğru değil.\n(%s)Sunucu geçersiz bir SSL sertifikası sundu. Bu bazen sunucunun düzgün yapılandırılmamasından kaynaklanır. Bazen de birisi size saldırıyor olabilir. Eğer ne olduğunu bilmiyorsanız, Reddet\'e tıklayın ve posta sunucunuzu yöneten kişilerle iletişime geçin.\n\n(%s)Sunucuya bağlanılamadı.\n(%s)
+ Yetkilendirme iptal edildiAyrıntıları düzenleDevamGelişmiş
@@ -509,6 +511,11 @@
Titreşimi tekrarlaYeni posta zil sesiHesap rengi
+ Beyaz
+ Kırmızı
+ Yeşil
+ Mavi
+ SarıMesaj oluşturma seçenekleriBileşen öntanımlılarıVarsayılan Kimden, Bcc ve imzayı ayarlayın
@@ -709,12 +716,17 @@
Dosya seçİçe AktarAyarlar başarıyla içe aktarıldı
+
Lütfen parolaları girin
+
+ Lütfen giriş yapın
+
Ayarlar içe aktarılamadıAyarlar dosyası okunamadıBazı ayarlar içe aktarılamadıBaşarıyla içe aktarıldıParola gerekli
+
İçe aktarılmadıİçe aktarma hatasıDaha sonra
@@ -904,14 +916,6 @@
Tüm imzalar görüntülenecekSadece imza kipinde şifreleme kullanılamaz!İmzasız Metin
- APG Reddetme Uyarısı
- APG artık sürdürülmüyor!
- Bu nedenle K-9 Mail\'den APG desteği kaldırıldı.
- 2014\'den önce geliştirilmesi durduruldu
- Düzeltilmemiş güvenlik sorunları içerir
- Daha fazlasını öğrenmek için buraya tıkla .
- Anladım!
- APGBu eposta şifreliBu eposta OpenPGP ile şifrelendi. On okumak için uyumlu bir OpenPGP uygulaması kurup yapılandırmanız gerekir.Ayarlara Git
diff --git a/app/ui/legacy/src/main/res/values-uk/strings.xml b/app/ui/legacy/src/main/res/values-uk/strings.xml
index 413e48451fdd21bcaaab26d246a265dcd54af6a9..71b9931db410de5bf2205b1ce27fba6c25969e42 100644
--- a/app/ui/legacy/src/main/res/values-uk/strings.xml
+++ b/app/ui/legacy/src/main/res/values-uk/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- The K-9 Dog Walkers
+ The Mail Dog WalkersПрограмний кодЛіцензія Apache версії 2.0Проект з відкритим кодом
@@ -256,6 +256,8 @@
Налаштувати новий обліковий записАдреса електронної поштиПароль
+
+
Щоб переглянути пароль, налаштуйте цьому пристрою блокування екрану.Звірте свою особу
@@ -297,10 +299,7 @@
Не видаляти на серверіВидаляти на серверіПозначити як прочитане на сервері
- Використовувати стискання даних у мережі:
- Мобільний інтернет
- WI-FI
- Інше
+ СтисненняСтирати видалені повідомлення на серверіНегайноПри опитуванні сервера
@@ -746,12 +745,16 @@
Вибрати файлІмпортНалаштування успішно імпортовано
+
Введіть паролі
+
+
Не вдалося імпортувати налаштуванняНе вдалося прочитати файл з налаштуваннямиНе вдалося імпортувати деякі налаштуванняУспішно імпортованоПотрібен пароль
+
Не імпортованоЗбій імпортуванняПізніше
@@ -948,14 +951,6 @@
Усі підписи будуть відображеніШифрування недоступне у режимі PGP Sign-OnlyНепідписаний Текст
- Попередження про застарілість APG
- APG більше не підтримується!
- Через це підтримку APG було видалено з K-9 Mail.
- Розробка зупинена на початку 2014
- Містить невиправлені проблеми з безпекою
- Ви можете перейти сюди, щоб дізнатися більше.
- Зрозуміло!
- APGЦей електронний лист зашифрованоЦей електронний лист було зашифровано за допомогою OpenPGP.\nЩоб його прочитати, необхідно встановити та налаштувати сумісний OpenPGP-додаток.Перейти до Налаштувань
@@ -1016,7 +1011,7 @@
Очікування наявності мережіНатисніть, щоб дізнатися більше.Інформація про сповіщення
- Під час використання пуш-сповіщень, K-9 Mail використовує з\'єднання з поштовим сервером. Android\'у необхідно відображати сповіщення безперервно доки програма працює на фоні. %s
+ Під час використання пуш-сповіщень, Mail використовує з\'єднання з поштовим сервером. Android\'у необхідно відображати сповіщення безперервно доки програма працює на фоні. %sТим не менш, Android також дозволяє приховати сповіщення.Дізнатися більшеНалаштувати сповіщення
diff --git a/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml b/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
index 4fb9d2ffa37ccfd3d6bc717308b63b1ec33046ec..6963a772ea35f106af6c74e5c514147e2e582da3 100644
--- a/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
+++ b/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
@@ -7,7 +7,7 @@
Mail AccountsMail Unread
- K-9 遛狗人
+ Mail 遛狗人源代码Apache 许可证,2.0 版开源项目
@@ -91,6 +91,7 @@
添加星标移除星标复制
+ 取消订阅显示邮件头地址已复制到剪贴板
@@ -249,6 +250,11 @@
设置新账户电子邮件地址密码
+ 要通过 Mail 使用这个电子邮件帐户,你需要登录并授予应用访问你电子邮件的权限。
+
+ 登录
+
+ Google 登录要查看您的密码,请在此设备上启用屏幕锁定。验证你的身份
@@ -272,6 +278,7 @@
非安全传输的密码加密的密码客户端证书
+ OAuth 2.0收件服务器设置用户名密码
@@ -290,10 +297,7 @@
不要从服务器上删除立刻从服务器上删除在服务器上将邮件标记为已读
- 在以下网络上使用压缩以便节约流量:
- 手机移动网络
- Wi-Fi
- 其他
+ 使用压缩抹掉删除的邮件立刻接收邮件时
@@ -358,6 +362,10 @@
用户名或密码不正确。\n(%s)服务器提供了一个无效的SSL证书。有时候这是由于不正确的服务器配置导致的;有时候这也可能是由于某人正在试图攻击您或者您的邮件服务器。如果您不能确定到底是什么原因,请单击”拒绝“,并联系您的邮件服务器管理人员。\n\n(%s)无法连接到服务器。\n(%s)
+ 授权被取消
+ 授权失败,错误信息如下:%s
+ 此提供商目前不支持 OAuth 2.0.
+ 应用找不到一个浏览器来授予访问你账户的权限。编辑详细信息继续高级
@@ -733,12 +741,19 @@
选择文件导入配置导入成功
+
请输入密码
+
+ 请登录
+
+ 请登录并输入密码配置导入失败读取配置文件失败导入部分配置失败导入成功需要输入密码
+
+ 需要登录没有导入导入失败之后
@@ -926,14 +941,6 @@
显示所有签名加密不可用在签名模式下未签名文本
- APG 弃用警告
- APG 已停止维护!
- 因此,K-9 Mail 移除了对 APG 的支持。
- 开发在 2014 年初停止了
- 包含未修复的安全漏洞
- 你可以 点击这里 查看更多信息。
- 知道了
- APG邮件已被加密这份邮件使用 OpenPGP 进行了加密。\n请安装并配置一个兼容 OpenPGP 的应用来查看邮件。前往设置
@@ -994,7 +1001,7 @@
等待网络可用轻按了解更多推送信息
- 使用 Push 时,K-9 Mail 会保持与邮件服务器的连接。 当应用程序在后台处于活动状态时,Android 需要显示持续的通知。%s
+ 使用 Push 时,Mail 会保持与邮件服务器的连接。 当应用程序在后台处于活动状态时,Android 需要显示持续的通知。%s不过,Android 也允许您隐藏通知。了解更多配置通知
diff --git a/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml b/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
index 032768e676e2c07e61cfc8f9bcbac7ec024b4090..195686cb9e234b32a5844406103255c081bcf4c8 100644
--- a/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
+++ b/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
@@ -7,8 +7,7 @@
Mail AccountsMail Unread
- K-9 使用者
- 版權所有 2008-%s The K-9 Dog Walkers. Portions Copyright 2006-%s the Android Open Source Project.
+ Mail 使用者原始碼Apache 授權條款, 版本 2.0開放原始碼專案
@@ -33,14 +32,12 @@
The account \"%s\" will be removed from Mail.作者
- 修訂資訊顯示郵件允許該程式讀取郵件。刪除郵件允許該程式刪除郵件。About Mail帳號
- 信件匣進階新郵件回覆
@@ -49,12 +46,6 @@
以附件形式轉寄選取帳號選取信件匣
- 正在接收%s:%s%s
- 正在接收郵件訊息%s:%s%s
- 正在寄送%s%s
- 正在處理%s:%s%s
- \u0020%d/%d
- 同步已停用%d 已選擇下一步上一步
@@ -62,10 +53,7 @@
確定取消寄送
- 再次寄送主題為空,確認寄送請再按一次。
- 選擇
- 取消選擇回覆全部回覆刪除
@@ -90,6 +78,7 @@
搜尋在所有地方搜尋搜尋結果
+ 新郵件設定整理信件匣帳號設定
@@ -100,6 +89,7 @@
加上星號移除星號複製
+ 取消訂閱顯示標頭位址已複製到剪貼簿
@@ -128,8 +118,6 @@
%.1fMB%.1fkB%dB
- 帳號「%s」已從%s縮小到%s
- 正在壓縮帳號「%s」您有新郵件%d 條新訊息
@@ -144,6 +132,7 @@
封存全部封存垃圾郵件
+ 證書錯誤%s驗證錯誤檢查你的伺服器設定身份驗證失敗
@@ -170,6 +159,9 @@
記錄額外的除錯訊息記錄敏感訊息允許在日誌中顯示密碼。
+ 匯出日誌
+ 匯出成功。日誌可能包含敏感訊息。請小心選擇您要傳送的人。
+ 匯出失敗載入更多郵件收件人:%s主題
@@ -196,20 +188,14 @@
收件人:副本:密件副本:
- 開啟
- 儲存無法保存附件。顯示圖片無法開啟%s。找不到可以開啟該附件的程式。下載完整郵件
- 無法顯示郵件透過 %1$s
- 全部郵件的訊息都已經下載,沒有其他的郵件訊息可以顯示。
- 從資料庫或郵件伺服器存取其他的郵件訊息失敗。來自這個寄件人的更多郵件寄送自 %s
- 調試/刪除訊息本文郵件已捨棄郵件已儲存為草稿顯示星號
@@ -225,6 +211,7 @@
使用顏色標示聯絡人列表定寬字體使用定寬字體顯示純文字郵件
+ 聯絡人名稱顏色自動調整訊息調整訊息內容符合螢幕大小刪除後自動返回到列表
@@ -240,13 +227,9 @@
全部標記為已讀刪除 (從通知欄)隱藏郵件用戶端
- 從郵件標頭中移除 K-9 的 User-Agent
+ 從郵件標頭中移除 Mail 的 User-Agent隱藏時區在郵件標頭及回覆標頭中使用 UTC 時間而不是本地時間
- 進行通知時隱藏主題
- 絕不
- 當裝置上鎖時
- 總是顯示「刪除」按鈕絕不針對單一訊息通知
@@ -266,7 +249,15 @@
設定新帳號電子郵件地址密碼
- 顯示密碼
+ 如要透過 Mail 使用這個電子郵件帳戶,您需要登入並授權應用程式取用您的電子郵件。
+
+ 登入
+
+ 使用 Google 帳戶登入
+
+ 要在這查看你的密碼請為這個裝置啟用螢幕上鎖。
+ 驗證你的身份
+ 解所來查看你的密碼手動設定正在獲取帳號訊息…
@@ -287,6 +278,7 @@
已加密的密碼用戶端憑證郵件伺服器設定
+ OAuth 2.0使用者名稱密碼用戶端憑證
@@ -304,12 +296,7 @@
不要從伺服器上刪除立刻從伺服器上刪除在伺服器上將郵件標記為已讀
- 在使用指定網路類型時使用壓縮以便節約流量:
- 行動網路
- Wi-Fi
- 其他
- 外部儲存 (記憶卡)
- 手機內部儲存
+ 使用壓縮抹除刪除的郵件立刻接收郵件時
@@ -324,11 +311,8 @@
只顯示訂閱的資料夾自動展開資料夾WebDAV (Exchange)路徑
- 可選擇身份驗證路徑
- 可選擇郵箱路徑
- 可選擇外寄伺服器設定SMTP伺服器埠號
@@ -340,7 +324,6 @@
「%1$s = %2$s」對於「%3$s = %4$s」無效無效的設定:%s帳號選項
- 壓縮信件匣檢查頻率不要15 分鐘一次
@@ -351,8 +334,6 @@
6 小時一次12 小時一次24 小時一次
- 在這個帳戶上啟用推送郵件
- 如果你的郵件伺服器支援此功能,那麼新郵件將會立刻顯示在手機上。這個選項可能大幅的提升性能,也可能嚴重的影響性能。重整間隔連接2 分鐘一次3 分鐘一次
@@ -363,7 +344,6 @@
48 分鐘一次60 分鐘一次有新郵件時通知我
- 檢查新郵件時通知我郵件顯示數量10 封25 封
@@ -381,12 +361,14 @@
使用者名稱或密碼不正確。\n(%s)伺服器提供了一个無效的 SSL 憑證。這是由於不正確的伺服器配置而導致的,也可能是由於有人試圖攻擊您或者您的郵件伺服器。如果您不能確定到底是什麼原因,請點擊「拒絕」,並聯絡您的郵件伺服器管理人員。\n\n(%s)無法連線到伺服器。\n(%s)
+ 授權被取消
+ 授權失敗,錯誤訊息為:%s
+ 此供應商目前不支援 OAuth 2.0。
+ 應用程式找不到瀏覽器以授予取用您賬戶的權限。編輯詳細訊息繼續進階帳號設定
- 預設帳號
- 預設使用這個帳號寄送郵件新郵件通知通知信件匣全部
@@ -408,8 +390,6 @@
開啟檢視郵件內容時自動標記為已讀刪除後標記為已讀刪除郵件時將其標記為已讀
- 通知設定
- 開啟系統通知設定顯示圖片不要來自您的聯絡人的郵件
@@ -451,9 +431,6 @@
上層信件匣檢查頻率帳號顏色選擇帳號在信件匣和帳戶列表中顯示時所使用的顏色
- 沒有顏色
- 通知LED燈顏色
- 選擇手機提醒該帳號訊息時LED燈閃爍的顏色本地信件匣大小收取郵件的最大尺寸為1 KiB
@@ -539,8 +516,8 @@
帳號名稱您的名字通知
+ 震動震動
- 有新郵件時震動震動模式預設模式一
@@ -549,8 +526,19 @@
模式四模式五重複震動
+ 已停用新郵件鈴聲
+ 通知燈
+ 已停用帳號顏色
+ 系統預設顏色
+ 白
+ 紅
+ 綠
+ 藍
+ 黃
+ 青
+ 洋紅撰寫新郵件選項撰寫新郵件預設值設定預設的寄件人、密件副本以及簽名
@@ -661,10 +649,6 @@
訊息檢視不同的訊息檢視列表顯示全域收件匣
- %s%s
- - 未讀
- 所有郵件
- 可搜尋信件匣中的所有郵件全域收件匣將所有郵件集中到全域收件匣全域
@@ -697,8 +681,6 @@
主題日期郵件本文內容
- %d%%
- %1$s: %2$s訊息組成結構文字輸入欄位預設
@@ -754,12 +736,19 @@
選擇檔案匯入設定匯入成功
+
請輸入密碼
+
+ 請登入
+
+ 請登入並輸入密碼設定匯入失敗讀取設定檔案失敗匯入部分設定失敗匯入成功需要輸入密碼
+
+ 需要登入沒有匯入匯入失敗之後
@@ -947,21 +936,11 @@
顯示所有簽名在簽名模式下無法使用加密未簽名文字
- APG 棄用警告
- APG 已停止維護!
- 因此,K-9 Mail 移除了對 APG 的支援。
- 開發於 2014 年初停止
- 包括未修復的安全漏洞
- 你可以點擊這裡了解更多。
- 知道了
- APG郵件已被加密這封郵件使用 OpenPGP 進行了加密。\n請安裝並配置一個相容 OpenPGP 的應用程式来查看郵件。前往設定K-9 訊息列表正在載入郵件…
- 收取信件匣列表失敗
- 檢索支持 OpenPGP 的收件人狀態出錯!無法加密有些已選定的收件人不支援此功能!啟用加密
@@ -981,7 +960,7 @@
一般設定未安裝 OpenPGP 應用程式安裝
- K-9 Mail 需要 OpenKeychain 提供端到端加密。
+ Mail 需要 OpenKeychain 提供端到端加密。已加密的訊息。加密訊息主題有些收件人可能不支援加密
@@ -1007,8 +986,6 @@
寄送訊息時出現錯誤。請檢查您的網路連線與外寄伺服器設定。開關
- 開啟
- 關閉允許訪問聯絡人為了能夠提供聯絡人建議,並顯示聯絡人姓名和照片,應用程式需要訪問你的聯絡人。
@@ -1019,7 +996,7 @@
等待網路可用按一下了解更多推播資訊
- 使用推播時,K-9 Mail 會保持與郵件伺服器的連線。當應用程式在背景執行時,Android 需要開啟顯示持續的通知的權限。%s
+ 使用推播時,Mail 會保持與郵件伺服器的連線。當應用程式在背景執行時,Android 需要開啟顯示持續的通知的權限。%s不過,Android 也允許您隱藏通知。了解更多配置通知
diff --git a/app/ui/legacy/src/main/res/values/constants.xml b/app/ui/legacy/src/main/res/values/constants.xml
index 0a1dd9dcba3b0ce2f68e6b09305639ad2210538c..ff3e2618886cb2a6254d9a25ee24b38ac919ba5f 100644
--- a/app/ui/legacy/src/main/res/values/constants.xml
+++ b/app/ui/legacy/src/main/res/values/constants.xml
@@ -2,7 +2,7 @@
https://k9mail.app/https://forum.k9mail.app/
- K-9 Mail for Android
+ Mail for Androidhttps://github.com/k9mail/k-9/graphs/contributorshttps://github.com/k9mail/k-9https://www.apache.org/licenses/LICENSE-2.0
diff --git a/app/ui/legacy/src/main/res/values/strings.xml b/app/ui/legacy/src/main/res/values/strings.xml
index e501e20114fbd939d9638bb2d4f6b255529e0ccf..bfc5ec444f74c9f7d5105ca2c0a60348f98baf54 100644
--- a/app/ui/legacy/src/main/res/values/strings.xml
+++ b/app/ui/legacy/src/main/res/values/strings.xml
@@ -12,7 +12,7 @@
- The K-9 Dog Walkers
+ The Mail Dog WalkersSource codeApache License, Version 2.0Open Source Project
@@ -113,6 +113,7 @@
Add starRemove starCopy
+ UnsubscribeShow headersAddress copied to clipboard
@@ -307,6 +308,12 @@
Email addressPassword
+ To use this email account with Mail, you need to sign in and grant the app access to your emails.
+
+ Sign in
+
+ Sign in with Google
+
To view your password here, enable screen lock on this device.Verify your identity
@@ -334,7 +341,7 @@
Password, transmitted insecurelyEncrypted passwordClient certificate
- XOauth2 (Gmail, Outlook)
+ OAuth 2.0Incoming server settingsUsername
@@ -356,10 +363,7 @@
Delete from serverMark as read on server
- Use compression on network:
- Mobile
- Wi-Fi
- Other
+ Use compressionErase deleted messages on serverImmediately
@@ -440,6 +444,10 @@
Username or password incorrect.\n(%s)The server presented an invalid SSL certificate. Sometimes, this is because of a server misconfiguration. Sometimes it is because someone is trying to attack you or your mail server. If you\'re not sure what\'s up, click Reject and contact the folks who manage your mail server.\n\n(%s)Cannot connect to server.\n(%s)
+ Authorization canceled
+ Authorization failed with the following error: %s
+ OAuth 2.0 is currently not supported with this provider.
+ The app couldn\'t find a browser to use for granting access to your account.Edit detailsContinue
@@ -904,12 +912,23 @@
Select fileImportSettings successfully imported
+
+
Please enter passwords
+
+
+ Please sign in
+
+
+ Please sign in and enter passwords
+
Failed to import settingsFailed to read settings fileFailed to import some settingsImported successfullyPassword required
+
+ Sign-in requiredNot importedImport failureLater
@@ -1132,14 +1151,6 @@
All signatures will be displayedEncryption unavailable in sign-only mode!Unsigned Text
- APG Deprecation Warning
- APG is no longer maintained!
- Because of this, support for APG has been removed from Mail.
- Development stopped in early 2014
- Contains unfixed security issues
- You can click here to learn more.
- Got it!
- APGThis email is encryptedThis email has been encrypted with OpenPGP.\nTo read it, you need to install and configure a compatible OpenPGP App.Go to Settings
@@ -1215,7 +1226,7 @@ You can keep this message and use it as a backup for your secret key. If you wan
Tap to learn more.Push Info
- When using Push, K-9 Mail maintains a connection to the mail server. Android requires displaying an ongoing notification while the app is active in the background. %s
+ When using Push, Mail maintains a connection to the mail server. Android requires displaying an ongoing notification while the app is active in the background. %sHowever, Android also allows you to hide the notification.Learn moreConfigure notification
diff --git a/app/ui/legacy/src/main/res/xml/general_settings.xml b/app/ui/legacy/src/main/res/xml/general_settings.xml
index 864418d9e9aaa5d3f03a739677cad1e0af4d5554..b072dc7c412d1ec6304225b5aa0a65360cc5e1cd 100644
--- a/app/ui/legacy/src/main/res/xml/general_settings.xml
+++ b/app/ui/legacy/src/main/res/xml/general_settings.xml
@@ -410,12 +410,14 @@
diff --git a/app/ui/legacy/src/test/AndroidManifest.xml b/app/ui/legacy/src/test/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9b1c30102c6373bc02d7319769632b9ec0b3733f
--- /dev/null
+++ b/app/ui/legacy/src/test/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/AttachmentPresenterTest.kt b/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/AttachmentPresenterTest.kt
index d0b965eb1bf34477a4480007f3b78283473d4d12..6a1f04a0c5e705197770e9f6c42d1a11d55a410b 100644
--- a/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/AttachmentPresenterTest.kt
+++ b/app/ui/legacy/src/test/java/com/fsck/k9/activity/compose/AttachmentPresenterTest.kt
@@ -62,7 +62,7 @@ class AttachmentPresenterTest : K9RobolectricTest() {
)
val messageViewInfo = MessageViewInfo(
message, false, message, SUBJECT, false, TEXT, listOf(attachmentViewInfo), null, attachmentResolver,
- EXTRA_TEXT, ArrayList()
+ EXTRA_TEXT, ArrayList(), null
)
mockLoaderManager({ attachmentPresenter.attachments.get(0) as Attachment })
@@ -90,7 +90,7 @@ class AttachmentPresenterTest : K9RobolectricTest() {
)
val messageViewInfo = MessageViewInfo(
message, false, message, SUBJECT, false, TEXT, listOf(attachmentViewInfo), null, attachmentResolver,
- EXTRA_TEXT, ArrayList()
+ EXTRA_TEXT, ArrayList(), null
)
val result = attachmentPresenter.loadAllAvailableAttachments(messageViewInfo)
@@ -111,7 +111,7 @@ class AttachmentPresenterTest : K9RobolectricTest() {
val attachmentViewInfo = AttachmentViewInfo(MIME_TYPE, ATTACHMENT_NAME, size, URI, true, localBodyPart, true)
val messageViewInfo = MessageViewInfo(
message, false, message, SUBJECT, false, TEXT, listOf(attachmentViewInfo), null, attachmentResolver,
- EXTRA_TEXT, ArrayList()
+ EXTRA_TEXT, ArrayList(), null
)
mockLoaderManager({ attachmentPresenter.inlineAttachments.get(contentId) as Attachment })
@@ -139,7 +139,7 @@ class AttachmentPresenterTest : K9RobolectricTest() {
val attachmentViewInfo = AttachmentViewInfo(MIME_TYPE, ATTACHMENT_NAME, size, URI, true, localBodyPart, false)
val messageViewInfo = MessageViewInfo(
message, false, message, SUBJECT, false, TEXT, listOf(attachmentViewInfo), null, attachmentResolver,
- EXTRA_TEXT, ArrayList()
+ EXTRA_TEXT, ArrayList(), null
)
val result = attachmentPresenter.loadAllAvailableAttachments(messageViewInfo)
diff --git a/backend/demo/src/main/resources/inbox/intro.eml b/backend/demo/src/main/resources/inbox/intro.eml
index 8ff767f59ae832f2ce8de5237340d77bc0b21165..7ce770d31b2168fb8b0bdbb26ecbd838fa3dd771 100644
--- a/backend/demo/src/main/resources/inbox/intro.eml
+++ b/backend/demo/src/main/resources/inbox/intro.eml
@@ -2,8 +2,8 @@ MIME-Version: 1.0
From: "cketti"
Date: Thu, 23 Sep 2021 23:42:00 +0200
Message-ID:
-Subject: Welcome to K-9 Mail
+Subject: Welcome to Mail
To: User
Content-Type: text/plain; charset=UTF-8
-Congratulations, you have managed to set up K-9 Mail's demo account. Have fun exploring the app.
+Congratulations, you have managed to set up Mail's demo account. Have fun exploring the app.
diff --git a/backend/webdav/build.gradle b/backend/webdav/build.gradle
index cca7b0a4ce1a8261078527700283c8b7e204513f..b4801f303257aabffa6f5e868059a320fc98e62f 100644
--- a/backend/webdav/build.gradle
+++ b/backend/webdav/build.gradle
@@ -24,6 +24,9 @@ android {
minSdkVersion buildConfig.minSdk
}
+ // for using Apache HTTP Client
+ useLibrary 'org.apache.http.legacy'
+
buildTypes {
debug {
testCoverageEnabled rootProject.testCoverage
diff --git a/build.gradle b/build.gradle
index 073834c7b961047a1eb23b4cc4185bb1b14cb331..d4832439c848ee65fceb19cfa4f89360d0adcc35 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,6 +13,7 @@ buildscript {
versions = [
'kotlin': '1.6.10',
'kotlinCoroutines': '1.6.0',
+ 'jetbrainsAnnotations': '23.0.0',
'androidxAppCompat': '1.4.1',
'androidxActivity': '1.4.0',
'androidxRecyclerView': '1.2.1',
diff --git a/debug.keystore b/debug.keystore
new file mode 100644
index 0000000000000000000000000000000000000000..ddce9b12d43bced3500477f7724d6782a6273276
Binary files /dev/null and b/debug.keystore differ
diff --git a/fastlane/metadata/android/en-US/changelogs/31000.txt b/fastlane/metadata/android/en-US/changelogs/31000.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e249e92da8c4512f16fe596b3d2422ab9ac00abb
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/31000.txt
@@ -0,0 +1,7 @@
+- Added support for using OAuth 2.0 with Google accounts
+- Added support for using OAuth 2.0 with Yahoo and AOL accounts
+- Added "Unsubscribe" action that is displayed in the menu when viewing a message that contains an Unsubscribe header
+- Fixed a bug where unrelated notifications where cleared after synchronizing a folder containing no unread messages
+- Fixed rare problem with decoding format=flowed messages
+- Various other small bug fixes and improvements
+- Updated translations
diff --git a/mail/common/build.gradle b/mail/common/build.gradle
index fe604e059266e54a36913863bcd8d707d9ddda28..bf282669e6a83905b67a82f2e5f80cf4043e1378 100644
--- a/mail/common/build.gradle
+++ b/mail/common/build.gradle
@@ -1,67 +1,31 @@
-apply plugin: 'com.android.library'
-apply plugin: 'org.jetbrains.kotlin.android'
+apply plugin: 'java-library'
+apply plugin: 'kotlin'
if (rootProject.testCoverage) {
apply plugin: 'jacoco'
}
+java {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+}
+
dependencies {
+ api "org.jetbrains:annotations:${versions.jetbrainsAnnotations}"
+
implementation "org.apache.james:apache-mime4j-core:${versions.mime4j}"
implementation "org.apache.james:apache-mime4j-dom:${versions.mime4j}"
implementation "com.squareup.okio:okio:${versions.okio}"
implementation "commons-io:commons-io:${versions.commonsIo}"
- implementation "androidx.annotation:annotation:${versions.androidxAnnotation}"
- implementation "com.jakewharton.timber:timber:${versions.timber}"
- implementation "io.insert-koin:koin-android:${versions.koin}"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinCoroutines}"
+ implementation "com.squareup.moshi:moshi:${versions.moshi}"
- implementation "com.squareup.retrofit2:retrofit:${versions.retrofit}"
- implementation "com.squareup.retrofit2:converter-gson:${versions.retrofit}"
+ // We're only using this for its DefaultHostnameVerifier
+ implementation "org.apache.httpcomponents.client5:httpclient5:5.1.3"
testImplementation project(":mail:testing")
- testImplementation "org.robolectric:robolectric:${versions.robolectric}"
testImplementation "junit:junit:${versions.junit}"
testImplementation "com.google.truth:truth:${versions.truth}"
testImplementation "org.mockito:mockito-inline:${versions.mockito}"
testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}"
testImplementation "com.ibm.icu:icu4j-charset:70.1"
}
-
-android {
- compileSdkVersion buildConfig.compileSdk
- buildToolsVersion buildConfig.buildTools
-
- defaultConfig {
- minSdkVersion buildConfig.minSdk
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildTypes {
- debug {
- testCoverageEnabled rootProject.testCoverage
- buildConfigField "String", "GOOGLE_CLIENT_ID", "\"654688539095-g7f4lov1ljpd6sjal6pgr57j19i58t2p.apps.googleusercontent.com\""
- buildConfigField "String", "GOOGLE_CLIENT_ID_PACKAGE_NAME", "\"foundation.e.mail\""
-
- }
-
- release {
- buildConfigField "String", "GOOGLE_CLIENT_ID", "\"654688539095-g7f4lov1ljpd6sjal6pgr57j19i58t2p.apps.googleusercontent.com\""
- buildConfigField "String", "GOOGLE_CLIENT_ID_PACKAGE_NAME", "\"foundation.e.mail\""
- }
- }
-
- lintOptions {
- abortOnError false
- lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
- }
-
- compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
- }
-
- kotlinOptions {
- jvmTarget = kotlinJvmVersion
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/logging/Logger.kt b/mail/common/src/main/java/com/fsck/k9/logging/Logger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..854e935b4d14ba3d2260dfa2e89bf2c8632c341b
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/logging/Logger.kt
@@ -0,0 +1,26 @@
+package com.fsck.k9.logging
+
+/**
+ * Logging abstraction based on Timber.
+ */
+interface Logger {
+ fun v(message: String?, vararg args: Any?)
+ fun v(t: Throwable?, message: String?, vararg args: Any?)
+ fun v(t: Throwable?)
+
+ fun d(message: String?, vararg args: Any?)
+ fun d(t: Throwable?, message: String?, vararg args: Any?)
+ fun d(t: Throwable?)
+
+ fun i(message: String?, vararg args: Any?)
+ fun i(t: Throwable?, message: String?, vararg args: Any?)
+ fun i(t: Throwable?)
+
+ fun w(message: String?, vararg args: Any?)
+ fun w(t: Throwable?, message: String?, vararg args: Any?)
+ fun w(t: Throwable?)
+
+ fun e(message: String?, vararg args: Any?)
+ fun e(t: Throwable?, message: String?, vararg args: Any?)
+ fun e(t: Throwable?)
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/logging/NoOpLogger.kt b/mail/common/src/main/java/com/fsck/k9/logging/NoOpLogger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bc0a10c9f2226f98d4b1a418df7dff9c210e5f11
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/logging/NoOpLogger.kt
@@ -0,0 +1,36 @@
+package com.fsck.k9.logging
+
+/**
+ * A [Logger] implementation that does nothing.
+ */
+class NoOpLogger : Logger {
+ override fun v(message: String?, vararg args: Any?) = Unit
+
+ override fun v(t: Throwable?, message: String?, vararg args: Any?) = Unit
+
+ override fun v(t: Throwable?) = Unit
+
+ override fun d(message: String?, vararg args: Any?) = Unit
+
+ override fun d(t: Throwable?, message: String?, vararg args: Any?) = Unit
+
+ override fun d(t: Throwable?) = Unit
+
+ override fun i(message: String?, vararg args: Any?) = Unit
+
+ override fun i(t: Throwable?, message: String?, vararg args: Any?) = Unit
+
+ override fun i(t: Throwable?) = Unit
+
+ override fun w(message: String?, vararg args: Any?) = Unit
+
+ override fun w(t: Throwable?, message: String?, vararg args: Any?) = Unit
+
+ override fun w(t: Throwable?) = Unit
+
+ override fun e(message: String?, vararg args: Any?) = Unit
+
+ override fun e(t: Throwable?, message: String?, vararg args: Any?) = Unit
+
+ override fun e(t: Throwable?) = Unit
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/logging/Timber.kt b/mail/common/src/main/java/com/fsck/k9/logging/Timber.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e0912ec943e4bda2e4ba2040313a468c5dcfabc2
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/logging/Timber.kt
@@ -0,0 +1,83 @@
+package com.fsck.k9.logging
+
+/**
+ * Our fake `Timber` object.
+ */
+object Timber {
+ var logger: Logger = NoOpLogger()
+
+ @JvmStatic
+ fun v(message: String?, vararg args: Any?) {
+ logger.v(message, *args)
+ }
+
+ @JvmStatic
+ fun v(t: Throwable?, message: String?, vararg args: Any?) {
+ logger.v(t, message, *args)
+ }
+
+ @JvmStatic
+ fun v(t: Throwable?) {
+ logger.v(t)
+ }
+
+ @JvmStatic
+ fun d(message: String?, vararg args: Any?) {
+ logger.d(message, *args)
+ }
+
+ @JvmStatic
+ fun d(t: Throwable?, message: String?, vararg args: Any?) {
+ logger.d(t, message, *args)
+ }
+
+ @JvmStatic
+ fun d(t: Throwable?) {
+ logger.d(t)
+ }
+
+ @JvmStatic
+ fun i(message: String?, vararg args: Any?) {
+ logger.i(message, *args)
+ }
+
+ @JvmStatic
+ fun i(t: Throwable?, message: String?, vararg args: Any?) {
+ logger.i(t, message, *args)
+ }
+
+ @JvmStatic
+ fun i(t: Throwable?) {
+ logger.i(t)
+ }
+
+ @JvmStatic
+ fun w(message: String?, vararg args: Any?) {
+ logger.w(message, *args)
+ }
+
+ @JvmStatic
+ fun w(t: Throwable?, message: String?, vararg args: Any?) {
+ logger.w(t, message, *args)
+ }
+
+ @JvmStatic
+ fun w(t: Throwable?) {
+ logger.w(t)
+ }
+
+ @JvmStatic
+ fun e(message: String?, vararg args: Any?) {
+ logger.e(message)
+ }
+
+ @JvmStatic
+ fun e(t: Throwable?, message: String?, vararg args: Any?) {
+ logger.e(t, message, *args)
+ }
+
+ @JvmStatic
+ fun e(t: Throwable?) {
+ logger.e(t)
+ }
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/Address.java b/mail/common/src/main/java/com/fsck/k9/mail/Address.java
index d90aa220a3f2ee6f7e4850fe79ea3e30fe8454cb..fd272638fa22fea133a8fa0ad91291bee67afa24 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/Address.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/Address.java
@@ -1,25 +1,24 @@
package com.fsck.k9.mail;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
+import com.fsck.k9.logging.Timber;
+import com.fsck.k9.mail.helper.Rfc822Token;
+import com.fsck.k9.mail.helper.Rfc822Tokenizer;
+import com.fsck.k9.mail.helper.TextUtils;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.codec.DecodeMonitor;
import org.apache.james.mime4j.codec.EncoderUtil;
import org.apache.james.mime4j.dom.address.Mailbox;
import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.field.address.DefaultAddressParser;
-import timber.log.Timber;
-
-import android.text.TextUtils;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.VisibleForTesting;
public class Address implements Serializable {
private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$");
@@ -29,7 +28,7 @@ public class Address implements Serializable {
*/
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[0];
- @NonNull
+ @NotNull
private String mAddress;
private String mPersonal;
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/Authentication.java b/mail/common/src/main/java/com/fsck/k9/mail/Authentication.java
index 08ab4a881596604d1e5518561be54b67ae80a671..68a17cbb349f4331107b449666dbebe224c6b406 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/Authentication.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/Authentication.java
@@ -1,10 +1,12 @@
package com.fsck.k9.mail;
-import java.io.UnsupportedEncodingException;
+
import java.security.MessageDigest;
import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.filter.Hex;
+import okio.ByteString;
+
public class Authentication {
private static final String US_ASCII = "US-ASCII";
@@ -84,11 +86,8 @@ public class Authentication {
}
}
- public static String computeXoauth(String username, String authToken) throws UnsupportedEncodingException {
+ public static String computeXoauth(String username, String authToken) {
String formattedAuthenticationString = String.format(XOAUTH_FORMAT, username, authToken);
- byte[] base64encodedAuthenticationString =
- Base64.encodeBase64(formattedAuthenticationString.getBytes());
-
- return new String(base64encodedAuthenticationString, US_ASCII);
+ return ByteString.encodeUtf8(formattedAuthenticationString).base64();
}
}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/BoundaryGenerator.java b/mail/common/src/main/java/com/fsck/k9/mail/BoundaryGenerator.java
index 9b2452e610045b4061d9cd0081380d32aab103b5..87c46327328353914cdaa64d938e5ded784d3e0b 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/BoundaryGenerator.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/BoundaryGenerator.java
@@ -3,7 +3,7 @@ package com.fsck.k9.mail;
import java.util.Random;
-import androidx.annotation.VisibleForTesting;
+import org.jetbrains.annotations.VisibleForTesting;
public class BoundaryGenerator {
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/CertificateValidationException.java b/mail/common/src/main/java/com/fsck/k9/mail/CertificateValidationException.java
index 18bd457f18aff5b283314f26351eef5809cb7ed3..43628424df71b2695a12f86a3d36efbfa4255123 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/CertificateValidationException.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/CertificateValidationException.java
@@ -7,8 +7,6 @@ import java.security.cert.X509Certificate;
import javax.net.ssl.SSLHandshakeException;
-import android.security.KeyChainException;
-
public class CertificateValidationException extends MessagingException {
public static final long serialVersionUID = -1;
private final Reason mReason;
@@ -90,7 +88,7 @@ public class CertificateValidationException extends MessagingException {
while (throwable != null
&& !(throwable instanceof CertPathValidatorException)
&& !(throwable instanceof CertificateException)
- && !(throwable instanceof KeyChainException)
+ && !("android.security.KeyChainException".equals(throwable.getClass().getCanonicalName()))
&& !(throwable instanceof SSLHandshakeException)) {
throwable = throwable.getCause();
}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/Message.java b/mail/common/src/main/java/com/fsck/k9/mail/Message.java
index c7aa729fdb3f278d00b64e17429804b354eeccad..5ccc21e5c78ba7c6c7bcad315fe3173882730181 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/Message.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/Message.java
@@ -8,11 +8,10 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Set;
-import androidx.annotation.NonNull;
-
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.filter.CountingOutputStream;
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
-import timber.log.Timber;
+import org.jetbrains.annotations.NotNull;
public abstract class Message implements Part, Body {
@@ -98,7 +97,7 @@ public abstract class Message implements Part, Body {
@Override
public abstract void setHeader(String name, String value);
- @NonNull
+ @NotNull
@Override
public abstract String[] getHeader(String name);
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/NetworkType.java b/mail/common/src/main/java/com/fsck/k9/mail/NetworkType.java
deleted file mode 100644
index 769858213ccba15fe7bf5a2182573a5d7e44193a..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/NetworkType.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.fsck.k9.mail;
-
-import android.net.ConnectivityManager;
-
-/**
- * Enum for some of
- * https://developer.android.com/reference/android/net/ConnectivityManager.html#TYPE_MOBILE etc.
- */
-public enum NetworkType {
-
- WIFI,
- MOBILE,
- OTHER;
-
- public static NetworkType fromConnectivityManagerType(int type){
- switch (type) {
- case ConnectivityManager.TYPE_MOBILE:
- return MOBILE;
- case ConnectivityManager.TYPE_WIFI:
- return WIFI;
- default:
- return OTHER;
- }
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/Part.java b/mail/common/src/main/java/com/fsck/k9/mail/Part.java
index c3fb55e7c3142f8f4212425a2cd622438f150a53..655c3bb5c96009c89f3fe86a7e504b148ffd7d9f 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/Part.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/Part.java
@@ -4,7 +4,7 @@ package com.fsck.k9.mail;
import java.io.IOException;
import java.io.OutputStream;
-import androidx.annotation.NonNull;
+import org.jetbrains.annotations.NotNull;
public interface Part {
@@ -27,7 +27,7 @@ public interface Part {
/**
* Returns an array of headers of the given name. The array may be empty.
*/
- @NonNull
+ @NotNull
String[] getHeader(String name);
boolean isMimeType(String mimeType);
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/Transport.java b/mail/common/src/main/java/com/fsck/k9/mail/Transport.java
index 6b60e524377fdcfccfafbcb4d07ed802fcf91f70..6fdf523ecf4fff623e72c00c57c1080b054af436 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/Transport.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/Transport.java
@@ -2,12 +2,6 @@
package com.fsck.k9.mail;
public abstract class Transport {
-
- protected static final int SOCKET_CONNECT_TIMEOUT = 10000;
-
- // RFC 1047
- protected static final int SOCKET_READ_TIMEOUT = 300000;
-
public abstract void open() throws MessagingException;
public abstract void sendMessage(Message message) throws MessagingException;
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Token.java b/mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Token.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf0721f910b2097cbd2e241e31aa3080aa639f0c
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Token.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fsck.k9.mail.helper;
+
+
+import org.jetbrains.annotations.Nullable;
+
+
+/**
+ * This class stores an RFC 822-like name, address, and comment,
+ * and provides methods to convert them to quoted strings.
+ */
+public class Rfc822Token {
+ @Nullable
+ private String mName, mAddress, mComment;
+
+ /**
+ * Creates a new Rfc822Token with the specified name, address,
+ * and comment.
+ */
+ public Rfc822Token(@Nullable String name, @Nullable String address, @Nullable String comment) {
+ mName = name;
+ mAddress = address;
+ mComment = comment;
+ }
+
+ /**
+ * Returns the name part.
+ */
+ @Nullable
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the address part.
+ */
+ @Nullable
+ public String getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * Returns the comment part.
+ */
+ @Nullable
+ public String getComment() {
+ return mComment;
+ }
+
+ /**
+ * Changes the name to the specified name.
+ */
+ public void setName(@Nullable String name) {
+ mName = name;
+ }
+
+ /**
+ * Changes the address to the specified address.
+ */
+ public void setAddress(@Nullable String address) {
+ mAddress = address;
+ }
+
+ /**
+ * Changes the comment to the specified comment.
+ */
+ public void setComment(@Nullable String comment) {
+ mComment = comment;
+ }
+
+ /**
+ * Returns the name (with quoting added if necessary),
+ * the comment (in parentheses), and the address (in angle brackets).
+ * This should be suitable for inclusion in an RFC 822 address list.
+ */
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ if (mName != null && mName.length() != 0) {
+ sb.append(quoteNameIfNecessary(mName));
+ sb.append(' ');
+ }
+
+ if (mComment != null && mComment.length() != 0) {
+ sb.append('(');
+ sb.append(quoteComment(mComment));
+ sb.append(") ");
+ }
+
+ if (mAddress != null && mAddress.length() != 0) {
+ sb.append('<');
+ sb.append(mAddress);
+ sb.append('>');
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns the name, conservatively quoting it if there are any
+ * characters that are likely to cause trouble outside of a
+ * quoted string, or returning it literally if it seems safe.
+ */
+ public static String quoteNameIfNecessary(String name) {
+ int len = name.length();
+
+ for (int i = 0; i < len; i++) {
+ char c = name.charAt(i);
+
+ if (! ((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == ' ') ||
+ (c >= '0' && c <= '9'))) {
+ return '"' + quoteName(name) + '"';
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Returns the name, with internal backslashes and quotation marks
+ * preceded by backslashes. The outer quote marks themselves are not
+ * added by this method.
+ */
+ public static String quoteName(String name) {
+ StringBuilder sb = new StringBuilder();
+
+ int len = name.length();
+ for (int i = 0; i < len; i++) {
+ char c = name.charAt(i);
+
+ if (c == '\\' || c == '"') {
+ sb.append('\\');
+ }
+
+ sb.append(c);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns the comment, with internal backslashes and parentheses
+ * preceded by backslashes. The outer parentheses themselves are
+ * not added by this method.
+ */
+ public static String quoteComment(String comment) {
+ int len = comment.length();
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < len; i++) {
+ char c = comment.charAt(i);
+
+ if (c == '(' || c == ')' || c == '\\') {
+ sb.append('\\');
+ }
+
+ sb.append(c);
+ }
+
+ return sb.toString();
+ }
+
+ public int hashCode() {
+ int result = 17;
+ if (mName != null) result = 31 * result + mName.hashCode();
+ if (mAddress != null) result = 31 * result + mAddress.hashCode();
+ if (mComment != null) result = 31 * result + mComment.hashCode();
+ return result;
+ }
+
+ private static boolean stringEquals(String a, String b) {
+ if (a == null) {
+ return (b == null);
+ } else {
+ return (a.equals(b));
+ }
+ }
+
+ public boolean equals(@Nullable Object o) {
+ if (!(o instanceof Rfc822Token)) {
+ return false;
+ }
+ Rfc822Token other = (Rfc822Token) o;
+ return (stringEquals(mName, other.mName) &&
+ stringEquals(mAddress, other.mAddress) &&
+ stringEquals(mComment, other.mComment));
+ }
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Tokenizer.java b/mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Tokenizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..faf640ab01bc1c83401859229aaeb962d683859b
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Tokenizer.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fsck.k9.mail.helper;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * This class works as a Tokenizer for MultiAutoCompleteTextView for
+ * address list fields, and also provides a method for converting
+ * a string of addresses (such as might be typed into such a field)
+ * into a series of Rfc822Tokens.
+ */
+public class Rfc822Tokenizer {
+
+ /**
+ * This constructor will try to take a string like
+ * "Foo Bar (something) <foo\@google.com>,
+ * blah\@google.com (something)"
+ * and convert it into one or more Rfc822Tokens, output into the supplied
+ * collection.
+ *
+ * It does *not* decode MIME encoded-words; charset conversion
+ * must already have taken place if necessary.
+ * It will try to be tolerant of broken syntax instead of
+ * returning an error.
+ *
+ */
+ public static void tokenize(CharSequence text, Collection out) {
+ StringBuilder name = new StringBuilder();
+ StringBuilder address = new StringBuilder();
+ StringBuilder comment = new StringBuilder();
+
+ int i = 0;
+ int cursor = text.length();
+
+ while (i < cursor) {
+ char c = text.charAt(i);
+
+ if (c == ',' || c == ';') {
+ i++;
+
+ while (i < cursor && text.charAt(i) == ' ') {
+ i++;
+ }
+
+ crunch(name);
+
+ if (address.length() > 0) {
+ out.add(new Rfc822Token(name.toString(),
+ address.toString(),
+ comment.toString()));
+ } else if (name.length() > 0) {
+ out.add(new Rfc822Token(null,
+ name.toString(),
+ comment.toString()));
+ }
+
+ name.setLength(0);
+ address.setLength(0);
+ comment.setLength(0);
+ } else if (c == '"') {
+ i++;
+
+ while (i < cursor) {
+ c = text.charAt(i);
+
+ if (c == '"') {
+ i++;
+ break;
+ } else if (c == '\\') {
+ if (i + 1 < cursor) {
+ name.append(text.charAt(i + 1));
+ }
+ i += 2;
+ } else {
+ name.append(c);
+ i++;
+ }
+ }
+ } else if (c == '(') {
+ int level = 1;
+ i++;
+
+ while (i < cursor && level > 0) {
+ c = text.charAt(i);
+
+ if (c == ')') {
+ if (level > 1) {
+ comment.append(c);
+ }
+
+ level--;
+ i++;
+ } else if (c == '(') {
+ comment.append(c);
+ level++;
+ i++;
+ } else if (c == '\\') {
+ if (i + 1 < cursor) {
+ comment.append(text.charAt(i + 1));
+ }
+ i += 2;
+ } else {
+ comment.append(c);
+ i++;
+ }
+ }
+ } else if (c == '<') {
+ i++;
+
+ while (i < cursor) {
+ c = text.charAt(i);
+
+ if (c == '>') {
+ i++;
+ break;
+ } else {
+ address.append(c);
+ i++;
+ }
+ }
+ } else if (c == ' ') {
+ name.append('\0');
+ i++;
+ } else {
+ name.append(c);
+ i++;
+ }
+ }
+
+ crunch(name);
+
+ if (address.length() > 0) {
+ out.add(new Rfc822Token(name.toString(),
+ address.toString(),
+ comment.toString()));
+ } else if (name.length() > 0) {
+ out.add(new Rfc822Token(null,
+ name.toString(),
+ comment.toString()));
+ }
+ }
+
+ /**
+ * This method will try to take a string like
+ * "Foo Bar (something) <foo\@google.com>,
+ * blah\@google.com (something)"
+ * and convert it into one or more Rfc822Tokens.
+ * It does *not* decode MIME encoded-words; charset conversion
+ * must already have taken place if necessary.
+ * It will try to be tolerant of broken syntax instead of
+ * returning an error.
+ */
+ public static Rfc822Token[] tokenize(CharSequence text) {
+ ArrayList out = new ArrayList();
+ tokenize(text, out);
+ return out.toArray(new Rfc822Token[out.size()]);
+ }
+
+ private static void crunch(StringBuilder sb) {
+ int i = 0;
+ int len = sb.length();
+
+ while (i < len) {
+ char c = sb.charAt(i);
+
+ if (c == '\0') {
+ if (i == 0 || i == len - 1 ||
+ sb.charAt(i - 1) == ' ' ||
+ sb.charAt(i - 1) == '\0' ||
+ sb.charAt(i + 1) == ' ' ||
+ sb.charAt(i + 1) == '\0') {
+ sb.deleteCharAt(i);
+ len--;
+ } else {
+ i++;
+ }
+ } else {
+ i++;
+ }
+ }
+
+ for (i = 0; i < len; i++) {
+ if (sb.charAt(i) == '\0') {
+ sb.setCharAt(i, ' ');
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int findTokenStart(CharSequence text, int cursor) {
+ /*
+ * It's hard to search backward, so search forward until
+ * we reach the cursor.
+ */
+
+ int best = 0;
+ int i = 0;
+
+ while (i < cursor) {
+ i = findTokenEnd(text, i);
+
+ if (i < cursor) {
+ i++; // Skip terminating punctuation
+
+ while (i < cursor && text.charAt(i) == ' ') {
+ i++;
+ }
+
+ if (i < cursor) {
+ best = i;
+ }
+ }
+ }
+
+ return best;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int findTokenEnd(CharSequence text, int cursor) {
+ int len = text.length();
+ int i = cursor;
+
+ while (i < len) {
+ char c = text.charAt(i);
+
+ if (c == ',' || c == ';') {
+ return i;
+ } else if (c == '"') {
+ i++;
+
+ while (i < len) {
+ c = text.charAt(i);
+
+ if (c == '"') {
+ i++;
+ break;
+ } else if (c == '\\' && i + 1 < len) {
+ i += 2;
+ } else {
+ i++;
+ }
+ }
+ } else if (c == '(') {
+ int level = 1;
+ i++;
+
+ while (i < len && level > 0) {
+ c = text.charAt(i);
+
+ if (c == ')') {
+ level--;
+ i++;
+ } else if (c == '(') {
+ level++;
+ i++;
+ } else if (c == '\\' && i + 1 < len) {
+ i += 2;
+ } else {
+ i++;
+ }
+ }
+ } else if (c == '<') {
+ i++;
+
+ while (i < len) {
+ c = text.charAt(i);
+
+ if (c == '>') {
+ i++;
+ break;
+ } else {
+ i++;
+ }
+ }
+ } else {
+ i++;
+ }
+ }
+
+ return i;
+ }
+
+ /**
+ * Terminates the specified address with a comma and space.
+ * This assumes that the specified text already has valid syntax.
+ * The Adapter subclass's convertToString() method must make that
+ * guarantee.
+ */
+ public CharSequence terminateToken(CharSequence text) {
+ return text + ", ";
+ }
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/helper/TextUtils.kt b/mail/common/src/main/java/com/fsck/k9/mail/helper/TextUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..44fdf305e3528521eb39f471c0237fb66f90de23
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/mail/helper/TextUtils.kt
@@ -0,0 +1,11 @@
+package com.fsck.k9.mail.helper
+
+object TextUtils {
+ @JvmStatic
+ fun isEmpty(text: String?) = text.isNullOrEmpty()
+
+ @JvmStatic
+ fun join(separator: String, items: Array): String {
+ return items.joinToString(separator)
+ }
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/BinaryTempFileBody.java b/mail/common/src/main/java/com/fsck/k9/mail/internet/BinaryTempFileBody.java
index 2c664bbfb428f493113f73104f0e1c81eec72a36..1422b3e95aceac11fbdf8e234f8c7880d64260e5 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/internet/BinaryTempFileBody.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/internet/BinaryTempFileBody.java
@@ -9,12 +9,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.filter.Base64OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
import org.apache.james.mime4j.util.MimeUtil;
-import timber.log.Timber;
/**
@@ -146,4 +146,4 @@ public class BinaryTempFileBody implements RawDataBody, SizeAware {
super.close();
}
}
-}
\ No newline at end of file
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/CharsetSupport.java b/mail/common/src/main/java/com/fsck/k9/mail/internet/CharsetSupport.java
index f6f0f262aa6d9f89cf1670cfbcf2923e9dc9c005..038e2636ae1cfe6c3f886fa167b89cdb9595d8fe 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/internet/CharsetSupport.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/internet/CharsetSupport.java
@@ -1,11 +1,10 @@
package com.fsck.k9.mail.internet;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.Part;
import org.apache.commons.io.IOUtils;
-import timber.log.Timber;
import java.io.IOException;
import java.io.InputStream;
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/DecoderUtil.kt b/mail/common/src/main/java/com/fsck/k9/mail/internet/DecoderUtil.kt
index ee8053f1e2b34940d92671c8662ff49a9e68b5b4..eb5cec8ecfdb341d667637d75a98b053f773df3d 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/internet/DecoderUtil.kt
+++ b/mail/common/src/main/java/com/fsck/k9/mail/internet/DecoderUtil.kt
@@ -1,5 +1,6 @@
package com.fsck.k9.mail.internet
+import com.fsck.k9.logging.Timber
import com.fsck.k9.mail.Message
import com.fsck.k9.mail.MessagingException
import java.io.ByteArrayInputStream
@@ -11,7 +12,6 @@ import okio.buffer
import okio.source
import org.apache.james.mime4j.codec.QuotedPrintableInputStream
import org.apache.james.mime4j.util.CharsetUtil
-import timber.log.Timber
/**
* Decoder for encoded words (RFC 2047).
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/FlowedMessageUtils.java b/mail/common/src/main/java/com/fsck/k9/mail/internet/FlowedMessageUtils.java
deleted file mode 100644
index 25a10847b4db0dadc63edd1612fd79435e333a50..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/internet/FlowedMessageUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.fsck.k9.mail.internet;
-
-
-/**
- * Adapted from the Apache James project, see
- * https://james.apache.org/mailet/base/apidocs/org/apache/mailet/base/FlowedMessageUtils.html
- *
- *
Manages texts encoded as text/plain; format=flowed.
In order to decode, the input text must belong to a mail with headers similar to:
- * Content-Type: text/plain; charset="CHARSET"; [delsp="yes|no"; ]format="flowed"
- * (the quotes around CHARSET are not mandatory).
- * Furthermore the header Content-Transfer-Encoding MUST NOT BE Quoted-Printable
- * (see RFC3676 paragraph 4.2).(In fact this happens often for non 7bit messages).
- *
* Invalidating a token and then failure with a new token should be treated as a permanent failure.
*/
- void invalidateToken(String email);
+ void invalidateToken();
}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/OAuth2TokensStore.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/OAuth2TokensStore.kt
deleted file mode 100644
index 7d4f8855b990a6781751fc753d16053a4b944a16..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/OAuth2TokensStore.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.fsck.k9.mail.oauth
-
-import android.content.Context
-import android.content.SharedPreferences
-import java.util.HashMap
-
-/**
- * Store access and refresh token.
- * Access token are in RAM. Refresh token are saved in sharedPreferences.
- */
-class OAuth2TokensStore(context: Context) {
-
- private val sharedPreferences: SharedPreferences =
- context.getSharedPreferences(REFRESH_TOKEN_SP, Context.MODE_PRIVATE)
-
- private val accessTokens: HashMap = HashMap()
-
- fun saveAccessToken(email: String, token: String) {
- accessTokens[email] = token
- }
-
- fun getAccessToken(email: String): String? {
- return accessTokens[email]
- }
-
- fun invalidateAccessToken(email: String) {
- accessTokens.remove(email)
- }
-
- fun saveRefreshToken(email: String, token: String) {
- sharedPreferences.edit().putString(email, token).apply()
- }
-
- fun getRefreshToken(email: String): String? {
- return sharedPreferences.getString(email, null)
- }
-
- fun invalidateRefreshToken(email: String) {
- sharedPreferences.edit().remove(email).apply()
- }
-
- companion object {
- private const val REFRESH_TOKEN_SP = "refresh_token"
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/XOAuth2ChallengeParser.java b/mail/common/src/main/java/com/fsck/k9/mail/oauth/XOAuth2ChallengeParser.java
index 812220f331ab7911c2ab291a493d904edce399c3..192c1639cf1a1f89c5d042e148dc354ba3cf99cf 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/XOAuth2ChallengeParser.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/oauth/XOAuth2ChallengeParser.java
@@ -1,11 +1,14 @@
package com.fsck.k9.mail.oauth;
+import java.io.IOException;
+
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.K9MailLib;
import com.fsck.k9.mail.filter.Base64;
-import org.json.JSONException;
-import org.json.JSONObject;
-import timber.log.Timber;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.JsonDataException;
+import com.squareup.moshi.Moshi;
/**
@@ -23,13 +26,15 @@ public class XOAuth2ChallengeParser {
}
try {
- JSONObject json = new JSONObject(decodedResponse);
- String status = json.getString("status");
- if (!BAD_RESPONSE.equals(status)) {
+ Moshi moshi = new Moshi.Builder().build();
+ JsonAdapter adapter = moshi.adapter(XOAuth2Response.class);
+ XOAuth2Response responseObject = adapter.fromJson(decodedResponse);
+ if (responseObject != null && responseObject.status != null &&
+ !BAD_RESPONSE.equals(responseObject.status)) {
return false;
}
- } catch (JSONException jsonException) {
- Timber.e("Error decoding JSON response from: %s. Response was: %s", host, decodedResponse);
+ } catch (IOException | JsonDataException e) {
+ Timber.e(e, "Error decoding JSON response from: %s. Response was: %s", host, decodedResponse);
}
return true;
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/XOAuth2Response.java b/mail/common/src/main/java/com/fsck/k9/mail/oauth/XOAuth2Response.java
new file mode 100644
index 0000000000000000000000000000000000000000..40d9b04896bb13be2b3e83c26d0ea36d53d38c86
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/mail/oauth/XOAuth2Response.java
@@ -0,0 +1,6 @@
+package com.fsck.k9.mail.oauth;
+
+
+class XOAuth2Response {
+ public String status;
+}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/AuthorizationServer.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/AuthorizationServer.kt
deleted file mode 100644
index 03f45f9e6ba61242965877415ab97fd1223dcd36..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/AuthorizationServer.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.fsck.k9.mail.oauth.authorizationserver
-
-import com.fsck.k9.mail.AuthenticationFailedException
-
-/**
- * Oauth2 rely on authorization server:
- * An authorization code grant flow procedure in a webview (it is the authorization server which provide pages).
- * The exchange code serve to get access code and refresh token from authentication server.
- * The refresh token serve to renew the access token.
- * This interface regroup method needed for user authorization code grant flow procedure and for refreshing token.
- * @see http://www.bubblecode.net/en/2016/01/22/understanding-oauth2/
- */
-interface AuthorizationServer {
-
- /**
- * Get the url for authentication code grant flow procedure.
- */
- fun getAuthorizationUrl(email: String): String
-
- /**
- * At the end of the user authentication flow procedure, we got a code.
- * This request permit to get the tokens from this code.
- * @param code the code obtain at the end of user authentication flow procedure.
- */
- @Throws(AuthenticationFailedException::class)
- fun exchangeCode(email: String, code: String): OAuth2Tokens?
-
- /**
- * Get new access token with refresh token
- * @param refreshToken refresh token got before
- */
- @Throws(AuthenticationFailedException::class)
- fun refreshToken(email: String, refreshToken: String): String?
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/OAuth2Tokens.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/OAuth2Tokens.kt
deleted file mode 100644
index b30bd9402e99718b1d52ed36fa967289cd8dd9df..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/OAuth2Tokens.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.fsck.k9.mail.oauth.authorizationserver
-
-/**
- * OAuth2 tokens: access and refresh.
- * Access token: serve to access data on the resource server.
- * Refresh token: serve to renew the access token.
- * @see http://www.bubblecode.net/en/2016/01/22/understanding-oauth2/
- */
-data class OAuth2Tokens(val accessToken: String, val refreshToken: String)
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2CodeGrantFlowManager.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2CodeGrantFlowManager.kt
deleted file mode 100644
index fb7aa9b66306fddac1733c78b482b60ae656253b..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2CodeGrantFlowManager.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.fsck.k9.mail.oauth.authorizationserver.codegrantflow
-
-import com.fsck.k9.mail.AuthenticationFailedException
-import com.fsck.k9.mail.oauth.OAuth2Provider
-import com.fsck.k9.mail.oauth.OAuth2TokensStore
-
-/**
- * Manage the user authentication code grant flow procedure.
- */
-class OAuth2CodeGrantFlowManager(private val tokensStore: OAuth2TokensStore) {
-
- var promptRequestHandler: OAuth2PromptRequestHandler? = null
-
- @Throws(AuthenticationFailedException::class)
- fun showAuthDialog(email: String) {
- OAuth2Provider.getProvider(email)?.let { provider ->
- promptRequestHandler?.handleRedirectUrl(
- provider.webViewClient(email, this),
- provider.authorizationServer.getAuthorizationUrl(email)
- )
- }
- }
-
- @Throws(AuthenticationFailedException::class)
- fun exchangeCode(email: String, code: String) {
- OAuth2Provider.getProvider(email)?.let {
- it.authorizationServer.exchangeCode(email, code)?.let { tokens ->
- tokensStore.saveAccessToken(email, tokens.accessToken)
- tokensStore.saveRefreshToken(email, tokens.refreshToken)
- }
- }
- }
-
- fun invalidateRefreshToken(email: String) {
- tokensStore.invalidateRefreshToken(email)
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2NeedUserPromptException.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2NeedUserPromptException.kt
deleted file mode 100644
index cf44a5a475cfebd93dd19943541ef380ae693c79..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2NeedUserPromptException.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.fsck.k9.mail.oauth.authorizationserver.codegrantflow
-
-import com.fsck.k9.mail.MessagingException
-
-class OAuth2NeedUserPromptException : MessagingException("Need user's prompt for xoauth2")
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2PromptRequestHandler.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2PromptRequestHandler.kt
deleted file mode 100644
index cf5fc90c49b93cc2c5ab7e5afeb5277d7237a073..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2PromptRequestHandler.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.fsck.k9.mail.oauth.authorizationserver.codegrantflow
-
-import android.webkit.WebViewClient
-
-interface OAuth2PromptRequestHandler {
- fun handleRedirectUrl(webViewClient: WebViewClient, url: String)
-
- fun onObtainCodeSuccessful()
-
- fun onObtainAccessTokenSuccessful()
-
- fun onError(errorMessage: String)
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2WebViewClient.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2WebViewClient.kt
deleted file mode 100644
index 785fc79b81a53d6c7cddd63085d8e87bcc3427d9..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/authorizationserver/codegrantflow/OAuth2WebViewClient.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.fsck.k9.mail.oauth.authorizationserver.codegrantflow
-
-import android.annotation.TargetApi
-import android.net.Uri
-import android.os.Build
-import android.webkit.WebResourceRequest
-import android.webkit.WebView
-import android.webkit.WebViewClient
-import com.fsck.k9.mail.AuthenticationFailedException
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import timber.log.Timber
-
-/**
- * Bass class for standard authorization code flow like Google's or Microsoft's
- */
-abstract class OAuth2WebViewClient(
- private val email: String,
- private val codeGrantFlowManager: OAuth2CodeGrantFlowManager
-) : WebViewClient() {
- protected abstract fun arrivedAtRedirectUri(uri: Uri?): Boolean
-
- override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
- return handleUrl(url)
- }
-
- @TargetApi(Build.VERSION_CODES.N)
- override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
- return handleUrl(request.url.toString())
- }
-
- private fun handleUrl(url: String): Boolean {
- Timber.d(url)
- val uri = Uri.parse(url)
- if (arrivedAtRedirectUri(uri)) {
- val error = uri.getQueryParameter("error")
- if (error != null) {
- Timber.e("got oauth error: $error")
- codeGrantFlowManager.promptRequestHandler?.onError(error)
- return true
- }
-
- uri.getQueryParameter("code")?.let { oauthCode ->
- codeGrantFlowManager.promptRequestHandler?.onObtainCodeSuccessful()
- GlobalScope.launch {
- withContext(Dispatchers.IO) {
- try {
- codeGrantFlowManager.exchangeCode(email, oauthCode)
- withContext(Dispatchers.Main) {
- codeGrantFlowManager.promptRequestHandler?.onObtainAccessTokenSuccessful()
- }
- } catch (e: AuthenticationFailedException) {
- withContext(Dispatchers.Main) {
- codeGrantFlowManager.promptRequestHandler?.onError("Error when exchanging code")
- }
- }
- }
- }
- } ?: run {
- codeGrantFlowManager.promptRequestHandler?.onError("No auth code")
- }
- return true
- }
- return false
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/gmail/GmailAuthorizationServer.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/gmail/GmailAuthorizationServer.kt
deleted file mode 100644
index b89e8e072d4776a1dbb7ac16186afd6b580eb02f..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/gmail/GmailAuthorizationServer.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.fsck.k9.mail.oauth.gmail
-
-import com.fsck.k9.mail.AuthenticationFailedException
-import com.fsck.k9.mail.AuthenticationFailedException.Companion.OAUTH2_ERROR_INVALID_REFRESH_TOKEN
-import com.fsck.k9.mail.AuthenticationFailedException.Companion.OAUTH2_ERROR_UNKNOWN
-import com.fsck.k9.mail.common.BuildConfig
-import com.fsck.k9.mail.oauth.authorizationserver.AuthorizationServer
-import com.fsck.k9.mail.oauth.authorizationserver.OAuth2Tokens
-import com.google.gson.Gson
-import com.google.gson.annotations.SerializedName
-import java.io.IOException
-import retrofit2.Call
-import retrofit2.Response
-import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
-import retrofit2.http.Field
-import retrofit2.http.FormUrlEncoded
-import retrofit2.http.POST
-import timber.log.Timber
-
-class GmailAuthorizationServer : AuthorizationServer {
-
- private val service: GoogleRestApi
-
- init {
- val retrofit = Retrofit.Builder()
- .baseUrl(GOOGLE_API_BASE_URL)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- service = retrofit.create(GoogleRestApi::class.java)
- }
-
- override fun exchangeCode(email: String, code: String): OAuth2Tokens? {
- val call: Call? =
- service.exchangeCode(code, BuildConfig.GOOGLE_CLIENT_ID, "authorization_code", REDIRECT_URI)
- val exchangeResponse: ExchangeResponse?
- val response: Response?
- try {
- response = call?.execute()
- exchangeResponse = response?.body()
- } catch (e: Exception) {
- throw AuthenticationFailedException(e.message!!)
- }
-
- return when {
- exchangeResponse == null -> null
- exchangeResponse.accessToken.isNullOrEmpty() -> null
- else -> OAuth2Tokens(exchangeResponse.accessToken!!, exchangeResponse.refreshToken!!)
- }
- }
-
- override fun refreshToken(email: String, refreshToken: String): String? {
- val call: Call? =
- service.refreshToken(BuildConfig.GOOGLE_CLIENT_ID, refreshToken, "refresh_token")
- val response: Response?
- val refreshResponse: RefreshResponse?
- try {
- response = call?.execute()
- refreshResponse = response?.body()
- } catch (e: IOException) {
- throw AuthenticationFailedException(e.message!!)
- }
-
- refreshResponse?.let {
- return it.accessToken
- } ?: run {
- response?.let {
- try {
- val errorBody = response.errorBody()?.string()
- val oAuth2Error =
- Gson().fromJson(errorBody, OAuth2Error::class.java)
- when (oAuth2Error.error) {
- "invalid_grant" -> throw AuthenticationFailedException(OAUTH2_ERROR_INVALID_REFRESH_TOKEN)
- else -> throw AuthenticationFailedException(OAUTH2_ERROR_UNKNOWN)
- }
- } catch (e: IOException) {
- throw AuthenticationFailedException(OAUTH2_ERROR_UNKNOWN)
- }
- } ?: run {
- throw AuthenticationFailedException(OAUTH2_ERROR_UNKNOWN)
- }
- }
- }
-
- override fun getAuthorizationUrl(email: String): String {
- if (BuildConfig.GOOGLE_CLIENT_ID == "null") {
- throw IllegalStateException("GOOGLE_CLIENT_ID is empty")
- }
- Timber.d("AUTHORIZATION_URL" + AUTHORIZATION_URL)
- return "$AUTHORIZATION_URL&login_hint=$email"
- }
-
- private class OAuth2Error {
- var error: String? = null
-
- @SerializedName("error_description")
- var errorDescription: String? = null
- }
-
- private class ExchangeResponse {
- @SerializedName("access_token")
- var accessToken: String? = null
-
- @SerializedName("id_token")
- var idToken: String? = null
-
- @SerializedName("refresh_token")
- var refreshToken: String? = null
-
- @SerializedName("expires_in")
- var expiresIn = 0
-
- @SerializedName("token_type")
- var tokenType: String? = null
- }
-
- private class RefreshResponse {
- @SerializedName("access_token")
- var accessToken: String? = null
-
- @SerializedName("expires_in")
- var expiresIn = 0
-
- @SerializedName("token_type")
- var tokenType: String? = null
- }
-
- private interface GoogleRestApi {
- @FormUrlEncoded
- @POST("oauth2/v4/token")
- fun refreshToken(
- @Field("client_id") clientId: String,
- @Field("refresh_token") refreshToken: String,
- @Field("grant_type") grantType: String
- ): Call?
-
- @FormUrlEncoded
- @POST("oauth2/v4/token")
- fun exchangeCode(
- @Field("code") code: String,
- @Field("client_id") clientId: String,
- @Field("grant_type") grantType: String,
- @Field("redirect_uri") redirectUri: String
- ): Call?
- }
-
- companion object {
- private const val GOOGLE_API_BASE_URL = "https://www.googleapis.com/"
- private const val REDIRECT_URI = BuildConfig.GOOGLE_CLIENT_ID_PACKAGE_NAME + ":/oauth2redirect"
- private const val AUTHORIZATION_URL = "https://accounts.google.com/o/oauth2/v2/auth?" +
- "scope=https://mail.google.com/&" +
- "response_type=code&" +
- "redirect_uri=" + REDIRECT_URI + "&" +
- "client_id=" + BuildConfig.GOOGLE_CLIENT_ID
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/gmail/GmailWebViewClient.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/gmail/GmailWebViewClient.kt
deleted file mode 100644
index 8656b3fa372636298f3467c3b2ecb44c6502361c..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/gmail/GmailWebViewClient.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.fsck.k9.mail.oauth.gmail
-
-import android.net.Uri
-import com.fsck.k9.mail.common.BuildConfig
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2CodeGrantFlowManager
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2WebViewClient
-
-class GmailWebViewClient(email: String, codeGrantFlowManager: OAuth2CodeGrantFlowManager) :
- OAuth2WebViewClient(email, codeGrantFlowManager) {
- override fun arrivedAtRedirectUri(uri: Uri?): Boolean {
- return BuildConfig.GOOGLE_CLIENT_ID_PACKAGE_NAME == uri!!.scheme
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/outlook/OutlookAuthorizationServer.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/outlook/OutlookAuthorizationServer.kt
deleted file mode 100644
index 2e57a1e9da56efb9286acb1143323cc4bea5573f..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/outlook/OutlookAuthorizationServer.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.fsck.k9.mail.oauth.outlook
-
-import com.fsck.k9.mail.AuthenticationFailedException
-import com.fsck.k9.mail.oauth.authorizationserver.AuthorizationServer
-import com.fsck.k9.mail.oauth.authorizationserver.OAuth2Tokens
-import com.google.gson.annotations.SerializedName
-import java.io.IOException
-import retrofit2.Call
-import retrofit2.Response
-import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
-import retrofit2.http.Field
-import retrofit2.http.FormUrlEncoded
-import retrofit2.http.POST
-
-class OutlookAuthorizationServer : AuthorizationServer {
-
- private val service: OutlookService
-
- init {
- val retrofit = Retrofit.Builder()
- .baseUrl(OUTLOOK_BASE_URL)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- service = retrofit.create(OutlookService::class.java)
- }
-
- override fun getAuthorizationUrl(email: String): String {
- return "$AUTHORIZATION_URL&login_hint=$email"
- }
-
- override fun exchangeCode(email: String, code: String): OAuth2Tokens? {
- val call =
- service.exchangeCode(code, CLIENT_ID, "authorization_code", REDIRECT_URI)
- val exchangeResponse: ExchangeResponse?
- val response: Response?
- try {
- response = call?.execute()
- exchangeResponse = response?.body()
- } catch (e: Exception) {
- throw AuthenticationFailedException(e.message!!)
- }
- return when {
- exchangeResponse == null -> null
- exchangeResponse.accessToken.isNullOrEmpty() -> null
- else -> OAuth2Tokens(exchangeResponse.accessToken!!, exchangeResponse.refreshToken!!)
- }
- }
-
- override fun refreshToken(email: String, refreshToken: String): String? {
- val call =
- service.refreshToken(CLIENT_ID, refreshToken, "refresh_token", "https://login.live.com/oauth20_desktop.srf")
- val response: RefreshResponse?
- response = try {
- call?.execute()?.body()
- } catch (e: IOException) {
- throw AuthenticationFailedException(e.message!!)
- }
- if (response == null) {
- throw AuthenticationFailedException("Error when getting refresh token")
- }
- return response.accessToken
- }
-
- private interface OutlookService {
- @FormUrlEncoded
- @POST("oauth20_token.srf")
- fun refreshToken(
- @Field("client_id") clientId: String,
- @Field("refresh_token") refreshToken: String,
- @Field("grant_type") grantType: String,
- @Field("redirect_uri") redirectUri: String
- ): Call?
-
- @FormUrlEncoded
- @POST("oauth20_token.srf")
- fun exchangeCode(
- @Field("code") code: String,
- @Field("client_id") clientId: String,
- @Field("grant_type") grantType: String,
- @Field("redirect_uri") redirectUri: String
- ): Call?
- }
-
- private class ExchangeResponse {
- @SerializedName("access_token")
- var accessToken: String? = null
-
- @SerializedName("id_token")
- var idToken: String? = null
-
- @SerializedName("refresh_token")
- var refreshToken: String? = null
-
- @SerializedName("expires_in")
- var expiresIn = 0
-
- @SerializedName("token_type")
- var tokenType: String? = null
- }
-
- private class RefreshResponse {
- @SerializedName("access_token")
- var accessToken: String? = null
-
- @SerializedName("expires_in")
- var expiresIn = 0
-
- @SerializedName("token_type")
- var tokenType: String? = null
- }
-
- companion object {
- private const val OUTLOOK_BASE_URL = "https://login.live.com/"
- const val CLIENT_ID = "309d8028-bdcb-4c7a-8d3e-990247a02474"
- private const val REDIRECT_URI = "msal$CLIENT_ID://auth"
- private const val AUTHORIZATION_URL = "https://login.live.com/oauth20_authorize.srf?" +
- "client_id=" + CLIENT_ID + "&" +
- "scope=wl.imap wl.offline_access&" +
- "response_type=code&" +
- "redirect_uri=" + REDIRECT_URI
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/oauth/outlook/OutlookWebViewClient.kt b/mail/common/src/main/java/com/fsck/k9/mail/oauth/outlook/OutlookWebViewClient.kt
deleted file mode 100644
index 908258264e57fbb6f5940f1121b3c533c9e14857..0000000000000000000000000000000000000000
--- a/mail/common/src/main/java/com/fsck/k9/mail/oauth/outlook/OutlookWebViewClient.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.fsck.k9.mail.oauth.outlook
-
-import android.net.Uri
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2CodeGrantFlowManager
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2WebViewClient
-
-class OutlookWebViewClient(email: String, codeGrantFlowManager: OAuth2CodeGrantFlowManager) :
- OAuth2WebViewClient(email, codeGrantFlowManager) {
- override fun arrivedAtRedirectUri(uri: Uri?): Boolean {
- return "msal" + OutlookAuthorizationServer.CLIENT_ID == uri?.scheme
- }
-}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/ssl/LocalKeyStore.kt b/mail/common/src/main/java/com/fsck/k9/mail/ssl/LocalKeyStore.kt
index a2a13e433c9606ac868b95376c06c178962de5ed..712fc09d5633643d8d29ac0f3c5e8adec8d8e0ee 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/ssl/LocalKeyStore.kt
+++ b/mail/common/src/main/java/com/fsck/k9/mail/ssl/LocalKeyStore.kt
@@ -1,5 +1,6 @@
package com.fsck.k9.mail.ssl
+import com.fsck.k9.logging.Timber
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
@@ -11,7 +12,6 @@ import java.security.NoSuchAlgorithmException
import java.security.cert.Certificate
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
-import timber.log.Timber
private const val KEY_STORE_FILE_VERSION = 1
private val PASSWORD = charArrayOf()
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/ssl/TrustManagerFactory.java b/mail/common/src/main/java/com/fsck/k9/mail/ssl/TrustManagerFactory.java
index 26f650bf9e2bc7b5f05a33d0ee1ad45d51d20181..36f2bd963e16a5e6f1c502ff76f92e7dc96dc18e 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/ssl/TrustManagerFactory.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/ssl/TrustManagerFactory.java
@@ -10,12 +10,12 @@ import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.CertificateChainException;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
-import org.apache.http.conn.ssl.StrictHostnameVerifier;
-import timber.log.Timber;
+import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
public class TrustManagerFactory {
public static TrustManagerFactory createInstance(LocalKeyStore localKeyStore) {
@@ -68,6 +68,8 @@ public class TrustManagerFactory {
}
private class SecureX509TrustManager implements X509TrustManager {
+ private final DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
+
private final String mHost;
private final int mPort;
@@ -90,7 +92,7 @@ public class TrustManagerFactory {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
- new StrictHostnameVerifier().verify(mHost, certificate);
+ hostnameVerifier.verify(mHost, certificate);
return;
} catch (CertificateException e) {
// cert. chain can't be validated
diff --git a/mail/common/src/main/java/com/fsck/k9/sasl/OAuthBearer.kt b/mail/common/src/main/java/com/fsck/k9/sasl/OAuthBearer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7ad04e03c46ac572f5863d23c80b9e411b7dad38
--- /dev/null
+++ b/mail/common/src/main/java/com/fsck/k9/sasl/OAuthBearer.kt
@@ -0,0 +1,14 @@
+@file:JvmName("OAuthBearer")
+package com.fsck.k9.sasl
+
+import okio.ByteString.Companion.encodeUtf8
+
+/**
+ * Builds an initial client response for the SASL `OAUTHBEARER` mechanism.
+ *
+ * See [RFC 7628](https://datatracker.ietf.org/doc/html/rfc7628).
+ */
+fun buildOAuthBearerInitialClientResponse(username: String, token: String): String {
+ val saslName = username.replace(",", "=2C").replace("=", "=3D")
+ return "n,a=$saslName,\u0001auth=Bearer $token\u0001\u0001".encodeUtf8().base64()
+}
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/AddressTest.java b/mail/common/src/test/java/com/fsck/k9/mail/AddressTest.java
index 190bfc226db120d9e8b47efc6be049d3b047f6ca..5ec0585661931bc7f9b05e014f9b5c1f162879e9 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/AddressTest.java
+++ b/mail/common/src/test/java/com/fsck/k9/mail/AddressTest.java
@@ -2,7 +2,6 @@ package com.fsck.k9.mail;
import org.junit.Test;
-import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -10,7 +9,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-@RunWith(K9LibRobolectricTestRunner.class)
public class AddressTest {
/**
* test the possibility to parse "From:" fields with no email.
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/Address_quoteAtoms.java b/mail/common/src/test/java/com/fsck/k9/mail/Address_quoteAtoms.java
index 8c8698425e1f450b0fd8209618037c68327dc1c7..6de516565f5730521984078b78b1f5d100f498b6 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/Address_quoteAtoms.java
+++ b/mail/common/src/test/java/com/fsck/k9/mail/Address_quoteAtoms.java
@@ -2,12 +2,10 @@ package com.fsck.k9.mail;
import org.junit.Test;
-import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
-@RunWith(K9LibRobolectricTestRunner.class)
public class Address_quoteAtoms {
@Test
public void testNoQuote() {
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/MessageTest.kt b/mail/common/src/test/java/com/fsck/k9/mail/MessageTest.kt
index cf885b32bb468b94745debb7cb9ebd3c82e42c92..65125df5ae723a8a2a3301c52c554e344762fc65 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/MessageTest.kt
+++ b/mail/common/src/test/java/com/fsck/k9/mail/MessageTest.kt
@@ -10,24 +10,30 @@ import com.fsck.k9.mail.internet.MimeMultipart
import com.fsck.k9.mail.internet.TextBody
import com.google.common.truth.Truth.assertThat
import java.io.ByteArrayOutputStream
+import java.io.File
+import java.nio.file.Files
import java.util.Date
import java.util.TimeZone
import okio.Buffer
import org.apache.james.mime4j.util.MimeUtil
+import org.junit.After
import org.junit.Before
import org.junit.Test
-import org.junit.runner.RunWith
-import org.robolectric.RuntimeEnvironment
-@RunWith(K9LibRobolectricTestRunner::class)
class MessageTest {
- private val context = RuntimeEnvironment.application
+ private lateinit var tempDirectory: File
private var mimeBoundary: Int = 0
@Before
fun setUp() {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"))
- BinaryTempFileBody.setTempDirectory(context.cacheDir)
+ tempDirectory = Files.createTempDirectory("MessageTest").toFile()
+ BinaryTempFileBody.setTempDirectory(tempDirectory)
+ }
+
+ @After
+ fun tearDown() {
+ tempDirectory.deleteRecursively()
}
@Test
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/MimeTypeTest.kt b/mail/common/src/test/java/com/fsck/k9/mail/MimeTypeTest.kt
index 889495f1d403b8112ceb3560ea7ecbcf448852f0..c6074a92370e9fbcdafd9e1682b811ef32554efc 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/MimeTypeTest.kt
+++ b/mail/common/src/test/java/com/fsck/k9/mail/MimeTypeTest.kt
@@ -2,7 +2,6 @@ package com.fsck.k9.mail
import com.fsck.k9.mail.MimeType.Companion.toMimeType
import com.fsck.k9.mail.MimeType.Companion.toMimeTypeOrNull
-import com.fsck.k9.mail.internet.getMimeTypes
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
import org.junit.Test
@@ -22,10 +21,8 @@ class MimeTypeTest {
@Test
fun checkListOfMimeTypes() {
- for (mimeTypeString in getMimeTypes()) {
- // If there's an invalid MIME type this will throw
- mimeTypeString.toMimeType()
- }
+ // TODO: Try to parse all IANA-registered media types
+ // https://www.iana.org/assignments/media-types/media-types.xhtml
}
@Test
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/AddressHeaderBuilderTest.kt b/mail/common/src/test/java/com/fsck/k9/mail/internet/AddressHeaderBuilderTest.kt
index 3c6f6b816754aeb84eb36e6507450a94f0a1e24e..672b8eddf8dbd6013d49fb41413a223c1f40765a 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/internet/AddressHeaderBuilderTest.kt
+++ b/mail/common/src/test/java/com/fsck/k9/mail/internet/AddressHeaderBuilderTest.kt
@@ -1,13 +1,10 @@
package com.fsck.k9.mail.internet
import com.fsck.k9.mail.Address
-import com.fsck.k9.mail.K9LibRobolectricTestRunner
import com.fsck.k9.mail.crlf
import org.junit.Assert.assertEquals
import org.junit.Test
-import org.junit.runner.RunWith
-@RunWith(K9LibRobolectricTestRunner::class)
class AddressHeaderBuilderTest {
@Test
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/CharsetSupportTest.java b/mail/common/src/test/java/com/fsck/k9/mail/internet/CharsetSupportTest.java
index c9205be7817899db1039e7ceb5f7ba0444fe3f5f..d1918be580481c7cc1ef9ef5657bd73d7263ee4e 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/internet/CharsetSupportTest.java
+++ b/mail/common/src/test/java/com/fsck/k9/mail/internet/CharsetSupportTest.java
@@ -1,15 +1,11 @@
package com.fsck.k9.mail.internet;
-import com.fsck.k9.mail.K9LibRobolectricTestRunner;
-
import org.junit.Test;
-import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
-@RunWith(K9LibRobolectricTestRunner.class)
public class CharsetSupportTest {
@Test
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/FlowedMessageUtilsTest.kt b/mail/common/src/test/java/com/fsck/k9/mail/internet/FlowedMessageUtilsTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f9ef77ede7e7d01cf6b7bccbb8766ec7b2e89f9c
--- /dev/null
+++ b/mail/common/src/test/java/com/fsck/k9/mail/internet/FlowedMessageUtilsTest.kt
@@ -0,0 +1,216 @@
+package com.fsck.k9.mail.internet
+
+import com.fsck.k9.mail.crlf
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class FlowedMessageUtilsTest {
+ @Test
+ fun `deflow() with simple text`() {
+ val input = "Text that should be \r\n" +
+ "displayed on one line"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo("Text that should be displayed on one line")
+ }
+
+ @Test
+ fun `deflow() with only some lines ending in space`() {
+ val input = "Text that \r\n" +
+ "should be \r\n" +
+ "displayed on \r\n" +
+ "one line.\r\n" +
+ "Text that should retain\r\n" +
+ "its line break."
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo(
+ """
+ Text that should be displayed on one line.
+ Text that should retain
+ its line break.
+ """.trimIndent().crlf()
+ )
+ }
+
+ @Test
+ fun `deflow() with nothing to do`() {
+ val input = "Line one\r\nLine two\r\n"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo(input)
+ }
+
+ @Test
+ fun `deflow() with quoted text`() {
+ val input = "On [date], [user] wrote:\r\n" +
+ "> Text that should be displayed \r\n" +
+ "> on one line\r\n" +
+ "\r\n" +
+ "Some more text that should be \r\n" +
+ "displayed on one line.\r\n"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo(
+ """
+ |On [date], [user] wrote:
+ |> Text that should be displayed on one line
+ |
+ |Some more text that should be displayed on one line.
+ |
+ """.trimMargin().crlf()
+ )
+ }
+
+ @Test
+ fun `deflow() with quoted text ending in space`() {
+ val input = "> Quoted text \r\n" +
+ "Some other text"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo("> Quoted text \r\nSome other text")
+ }
+
+ @Test
+ fun `deflow() with quoted text ending in space before quoted text of different quoting depth`() {
+ val input = ">> Depth 2 \r\n" +
+ "> Depth 1 \r\n" +
+ "> is here\r\n" +
+ "Some other text"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo(
+ """
+ >> Depth 2${" "}
+ > Depth 1 is here
+ Some other text
+ """.trimIndent().crlf()
+ )
+ }
+
+ @Test
+ fun `deflow() with quoted text ending in space followed by empty line`() {
+ val input = "> Quoted \r\n" +
+ "\r\n" +
+ "Text"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo(input)
+ }
+
+ @Test
+ fun `deflow() with delSp=true`() {
+ val input = "Text that is wrapped mid wo \r\nrd"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = true)
+
+ assertThat(result).isEqualTo("Text that is wrapped mid word")
+ }
+
+ @Test
+ fun `deflow() with quoted text and space-stuffing and delSp=true`() {
+ val input = "> Quoted te \r\n" +
+ "> xt"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = true)
+
+ assertThat(result).isEqualTo("> Quoted text")
+ }
+
+ @Test
+ fun `deflow() with space-stuffed second line`() {
+ val input = "Text that should be \r\n" +
+ " displayed on one line"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo("Text that should be displayed on one line")
+ }
+
+ @Test
+ fun `deflow() with only space-stuffing`() {
+ val input = "Line 1\r\n" +
+ " Line 2\r\n" +
+ " Line 3\r\n"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo("Line 1\r\nLine 2\r\nLine 3\r\n")
+ }
+
+ @Test
+ fun `deflow() with quoted space-stuffed second line`() {
+ val input = "> Text that should be \r\n" +
+ "> displayed on one line"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo("> Text that should be displayed on one line")
+ }
+
+ @Test
+ fun `deflow() with text containing signature`() {
+ val input = "Text that should be \r\n" +
+ "displayed on one line.\r\n" +
+ "\r\n" +
+ "-- \r\n" +
+ "Signature \r\n" +
+ "text"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo(
+ """
+ Text that should be displayed on one line.
+
+ --${" "}
+ Signature text
+ """.trimIndent().crlf()
+ )
+ }
+
+ @Test
+ fun `deflow() with quoted text containing signature`() {
+ val input = "> Text that should be \r\n" +
+ "> displayed on one line.\r\n" +
+ "> \r\n" +
+ "> -- \r\n" +
+ "> Signature \r\n" +
+ "> text"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = false)
+
+ assertThat(result).isEqualTo(
+ """
+ > Text that should be displayed on one line.
+ >${" "}
+ > --${" "}
+ > Signature text
+ """.trimIndent().crlf()
+ )
+ }
+
+ @Test
+ fun `deflow() with flowed line followed by signature separator`() {
+ val input = "Fake flowed line \r\n" +
+ "-- \r\n" +
+ "Signature"
+
+ val result = FlowedMessageUtils.deflow(input, delSp = true)
+
+ assertThat(result).isEqualTo(
+ """
+ Fake flowed line
+ --${" "}
+ Signature
+ """.trimIndent().crlf()
+ )
+ }
+}
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/MessageIdGeneratorTest.kt b/mail/common/src/test/java/com/fsck/k9/mail/internet/MessageIdGeneratorTest.kt
index 477afdfbbb3e9e5c9c8a6e3740f70e31f6a88c74..6ce7a465405f2940426f16a44257bed084a2c076 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/internet/MessageIdGeneratorTest.kt
+++ b/mail/common/src/test/java/com/fsck/k9/mail/internet/MessageIdGeneratorTest.kt
@@ -1,12 +1,9 @@
package com.fsck.k9.mail.internet
import com.fsck.k9.mail.Address
-import com.fsck.k9.mail.K9LibRobolectricTestRunner
import org.junit.Assert.assertEquals
import org.junit.Test
-import org.junit.runner.RunWith
-@RunWith(K9LibRobolectricTestRunner::class)
class MessageIdGeneratorTest {
private val messageIdGenerator = MessageIdGenerator(
object : UuidGenerator {
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeMessageParseTest.java b/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeMessageParseTest.java
index 390fad798a2cbd0549fbc30f6de39ac0185b1cec..3046a91914cc6878ca3240d8001c77e58400a5fc 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeMessageParseTest.java
+++ b/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeMessageParseTest.java
@@ -9,23 +9,18 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import org.apache.commons.io.IOUtils;
-import org.junit.Before;
-import org.junit.Test;
-
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart;
-import com.fsck.k9.mail.K9LibRobolectricTestRunner;
import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.Multipart;
-
-import org.junit.runner.RunWith;
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Test;
import static org.junit.Assert.assertEquals;
-@RunWith(K9LibRobolectricTestRunner.class)
public class MimeMessageParseTest {
@Before
public void setup() {
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeUtilityHelper.kt b/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeUtilityHelper.kt
deleted file mode 100644
index 7fa7e2dc9c680d47f4014e6ca3963468beaa7fae..0000000000000000000000000000000000000000
--- a/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeUtilityHelper.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.fsck.k9.mail.internet
-
-fun getMimeTypes(): List {
- return MimeUtility.MIME_TYPE_BY_EXTENSION_MAP.map { it[1] }
-}
diff --git a/mail/protocols/imap/build.gradle b/mail/protocols/imap/build.gradle
index 54e9e80df05ff6f0ccce94500d350497d35e7b8e..f86c05f47f2d58df13961582f2c30b73b2a218f4 100644
--- a/mail/protocols/imap/build.gradle
+++ b/mail/protocols/imap/build.gradle
@@ -1,21 +1,23 @@
-apply plugin: 'com.android.library'
-apply plugin: 'org.jetbrains.kotlin.android'
+apply plugin: 'java-library'
+apply plugin: 'kotlin'
if (rootProject.testCoverage) {
apply plugin: 'jacoco'
}
+java {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+}
+
dependencies {
api project(":mail:common")
implementation "com.jcraft:jzlib:1.0.7"
implementation "com.beetstra.jutf7:jutf7:1.0.0"
implementation "commons-io:commons-io:${versions.commonsIo}"
- implementation "com.jakewharton.timber:timber:${versions.timber}"
- implementation "androidx.annotation:annotation:${versions.androidxAnnotation}"
testImplementation project(":mail:testing")
- testImplementation "org.robolectric:robolectric:${versions.robolectric}"
testImplementation "junit:junit:${versions.junit}"
testImplementation "com.google.truth:truth:${versions.truth}"
testImplementation "org.mockito:mockito-core:${versions.mockito}"
@@ -23,32 +25,3 @@ dependencies {
testImplementation "com.squareup.okio:okio:${versions.okio}"
testImplementation "org.apache.james:apache-mime4j-core:${versions.mime4j}"
}
-
-android {
- compileSdkVersion buildConfig.compileSdk
- buildToolsVersion buildConfig.buildTools
-
- defaultConfig {
- minSdkVersion buildConfig.minSdk
- }
-
- buildTypes {
- debug {
- testCoverageEnabled rootProject.testCoverage
- }
- }
-
- lintOptions {
- abortOnError false
- lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
- }
-
- compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
- }
-
- kotlinOptions {
- jvmTarget = kotlinJvmVersion
- }
-}
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java
index 0342751e5d404a94e6666944c8e073aa534f1d02..3d4b591e00d38a2f0c83fa896ae056b53bc6b5ee 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java
@@ -6,6 +6,7 @@ class Capabilities {
public static final String CONDSTORE = "CONDSTORE";
public static final String SASL_IR = "SASL-IR";
public static final String AUTH_XOAUTH2 = "AUTH=XOAUTH2";
+ public static final String AUTH_OAUTHBEARER = "AUTH=OAUTHBEARER";
public static final String AUTH_CRAM_MD5 = "AUTH=CRAM-MD5";
public static final String AUTH_PLAIN = "AUTH=PLAIN";
public static final String AUTH_EXTERNAL = "AUTH=EXTERNAL";
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Commands.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Commands.java
index c5137cae272005a2aba0c1c88383b929ed05901b..b883d8825e11277f0cc06cb325f9ddf542c50948 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Commands.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/Commands.java
@@ -8,6 +8,7 @@ class Commands {
public static final String COMPRESS_DEFLATE = "COMPRESS DEFLATE";
public static final String STARTTLS = "STARTTLS";
public static final String AUTHENTICATE_XOAUTH2 = "AUTHENTICATE XOAUTH2";
+ public static final String AUTHENTICATE_OAUTHBEARER = "AUTHENTICATE OAUTHBEARER";
public static final String AUTHENTICATE_CRAM_MD5 = "AUTHENTICATE CRAM-MD5";
public static final String AUTHENTICATE_PLAIN = "AUTHENTICATE PLAIN";
public static final String AUTHENTICATE_EXTERNAL = "AUTHENTICATE EXTERNAL";
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapResponseParser.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapResponseParser.java
index 7357f01cdb3470e4ee20eea75f5b97b87b9fc1f9..64c2058006aea890e46c29a738d5c6ac0c14ca4c 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapResponseParser.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapResponseParser.java
@@ -6,10 +6,10 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.K9MailLib;
import com.fsck.k9.mail.filter.FixedLengthInputStream;
import com.fsck.k9.mail.filter.PeekableInputStream;
-import timber.log.Timber;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_IMAP;
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapSettings.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapSettings.java
index 4689a961af103cc45b42ec3ea1f3038a81846b83..d5c5e1efbae2c0b52f442e1cec00c881c18effbe 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapSettings.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapSettings.java
@@ -2,7 +2,7 @@ package com.fsck.k9.mail.store.imap;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
-import com.fsck.k9.mail.NetworkType;
+
/**
* Settings source for IMAP. Implemented in order to remove coupling between {@link ImapStore} and {@link ImapConnection}.
@@ -22,7 +22,7 @@ interface ImapSettings {
String getClientCertificateAlias();
- boolean useCompression(NetworkType type);
+ boolean useCompression();
String getPathPrefix();
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.kt b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.kt
index 1cba6012dca2a9802f1986bce43dcf6682e4620e..bcb484c6a216e090cb796b7d1c88e74dc25beef6 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.kt
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.kt
@@ -1,6 +1,5 @@
package com.fsck.k9.mail.store.imap
-import android.net.ConnectivityManager
import com.fsck.k9.mail.MessagingException
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.oauth.OAuth2TokenProvider
@@ -22,10 +21,9 @@ interface ImapStore {
serverSettings: ServerSettings,
config: ImapStoreConfig,
trustedSocketFactory: TrustedSocketFactory,
- connectivityManager: ConnectivityManager,
oauthTokenProvider: OAuth2TokenProvider?
): ImapStore {
- return RealImapStore(serverSettings, config, trustedSocketFactory, connectivityManager, oauthTokenProvider)
+ return RealImapStore(serverSettings, config, trustedSocketFactory, oauthTokenProvider)
}
}
}
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreConfig.kt b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreConfig.kt
index fef151222208cd7be364042c72bc80ea18a55eac..0bfbd58eeb10dafa511f642c7e50ff813282f2ee 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreConfig.kt
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreConfig.kt
@@ -1,9 +1,7 @@
package com.fsck.k9.mail.store.imap
-import com.fsck.k9.mail.NetworkType
-
interface ImapStoreConfig {
val logLabel: String
fun isSubscribedFoldersOnly(): Boolean
- fun useCompression(type: NetworkType): Boolean
+ fun useCompression(): Boolean
}
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapUtility.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapUtility.java
index c619a6d9ea5a19da4fea163a89e6807a433dff2d..1c241e73cd9837f63cad0a93fe083dc1a35a2f74 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapUtility.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapUtility.java
@@ -22,8 +22,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.Flag;
-import timber.log.Timber;
/**
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ListResponse.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ListResponse.java
index 4ae0d9dea79ec9e83748976e7ca5d2243edecee8..57e38f73e2bee8877cd9c441703d3ee9a2722fb5 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ListResponse.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ListResponse.java
@@ -5,7 +5,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import androidx.annotation.Nullable;
+import org.jetbrains.annotations.Nullable;
import static com.fsck.k9.mail.store.imap.ImapResponseParser.equalsIgnoreCase;
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapConnection.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapConnection.java
index 6c5123c7e48cb72be6a47be5c43d33a5d3fc06fe..64cbd4cda3edd4b3a7e8b7ff8e561b0858f663de 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapConnection.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapConnection.java
@@ -27,29 +27,26 @@ import java.util.regex.Pattern;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.Authentication;
import com.fsck.k9.mail.AuthenticationFailedException;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.K9MailLib;
import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.NetworkType;
import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.filter.PeekableInputStream;
import com.fsck.k9.mail.oauth.OAuth2TokenProvider;
import com.fsck.k9.mail.oauth.XOAuth2ChallengeParser;
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
import com.fsck.k9.mail.store.imap.IdGrouper.GroupedIds;
+import com.fsck.k9.sasl.OAuthBearer;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
import javax.net.ssl.SSLException;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import timber.log.Timber;
import static com.fsck.k9.mail.ConnectionSecurity.STARTTLS_REQUIRED;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_IMAP;
@@ -77,7 +74,6 @@ class RealImapConnection implements ImapConnection {
private static final int LENGTH_LIMIT_WITH_CONDSTORE = 8172;
- private final ConnectivityManager connectivityManager;
private final OAuth2TokenProvider oauthTokenProvider;
private final TrustedSocketFactory socketFactory;
private final int socketConnectTimeout;
@@ -93,14 +89,13 @@ class RealImapConnection implements ImapConnection {
private ImapSettings settings;
private Exception stacktraceForClose;
private boolean open = false;
- private boolean retryXoauth2WithNewToken = true;
+ private boolean retryOAuthWithNewToken = true;
public RealImapConnection(ImapSettings settings, TrustedSocketFactory socketFactory,
- ConnectivityManager connectivityManager, OAuth2TokenProvider oauthTokenProvider, int connectionGeneration) {
+ OAuth2TokenProvider oauthTokenProvider, int connectionGeneration) {
this.settings = settings;
this.socketFactory = socketFactory;
- this.connectivityManager = connectivityManager;
this.oauthTokenProvider = oauthTokenProvider;
this.socketConnectTimeout = SOCKET_CONNECT_TIMEOUT;
this.socketReadTimeout = SOCKET_READ_TIMEOUT;
@@ -108,12 +103,10 @@ class RealImapConnection implements ImapConnection {
}
public RealImapConnection(ImapSettings settings, TrustedSocketFactory socketFactory,
- ConnectivityManager connectivityManager, OAuth2TokenProvider oauthTokenProvider,
- int socketConnectTimeout, int socketReadTimeout,
+ OAuth2TokenProvider oauthTokenProvider, int socketConnectTimeout, int socketReadTimeout,
int connectionGeneration) {
this.settings = settings;
this.socketFactory = socketFactory;
- this.connectivityManager = connectivityManager;
this.oauthTokenProvider = oauthTokenProvider;
this.socketConnectTimeout = socketConnectTimeout;
this.socketReadTimeout = socketReadTimeout;
@@ -365,10 +358,14 @@ class RealImapConnection implements ImapConnection {
case XOAUTH2:
if (oauthTokenProvider == null) {
throw new MessagingException("No OAuthToken Provider available.");
- } else if (hasCapability(Capabilities.AUTH_XOAUTH2) && hasCapability(Capabilities.SASL_IR)) {
- return authXoauth2withSASLIR();
+ } else if (!hasCapability(Capabilities.SASL_IR)) {
+ throw new MessagingException("SASL-IR capability is missing.");
+ } else if (hasCapability(Capabilities.AUTH_OAUTHBEARER)) {
+ return authWithOAuthToken(OAuthMethod.OAUTHBEARER);
+ } else if (hasCapability(Capabilities.AUTH_XOAUTH2)) {
+ return authWithOAuthToken(OAuthMethod.XOAUTH2);
} else {
- throw new MessagingException("Server doesn't support SASL XOAUTH2.");
+ throw new MessagingException("Server doesn't support SASL OAUTHBEARER or XOAUTH2.");
}
case CRAM_MD5: {
if (hasCapability(Capabilities.AUTH_CRAM_MD5)) {
@@ -401,68 +398,71 @@ class RealImapConnection implements ImapConnection {
}
}
- private List authXoauth2withSASLIR() throws IOException, MessagingException {
- retryXoauth2WithNewToken = true;
+ private List authWithOAuthToken(OAuthMethod method) throws IOException, MessagingException {
+ retryOAuthWithNewToken = true;
try {
- return attemptXOAuth2();
+ return attemptOAuth(method);
} catch (NegativeImapResponseException e) {
//TODO: Check response code so we don't needlessly invalidate the token.
- oauthTokenProvider.invalidateToken(settings.getUsername());
+ oauthTokenProvider.invalidateToken();
- if (!retryXoauth2WithNewToken) {
- throw handlePermanentXoauth2Failure(e);
+ if (!retryOAuthWithNewToken) {
+ throw handlePermanentOAuthFailure(e);
} else {
- return handleTemporaryXoauth2Failure(e);
+ return handleTemporaryOAuthFailure(method, e);
}
}
}
- private AuthenticationFailedException handlePermanentXoauth2Failure(NegativeImapResponseException e) {
- Timber.v(e, "Permanent failure during XOAUTH2");
+ private AuthenticationFailedException handlePermanentOAuthFailure(NegativeImapResponseException e) {
+ Timber.v(e, "Permanent failure during authentication using OAuth token");
return new AuthenticationFailedException(e.getMessage(), e, e.getAlertText());
}
- private List handleTemporaryXoauth2Failure(NegativeImapResponseException e) throws IOException, MessagingException {
- //We got a response indicating a retry might suceed after token refresh
+ private List handleTemporaryOAuthFailure(OAuthMethod method, NegativeImapResponseException e)
+ throws IOException, MessagingException {
+ //We got a response indicating a retry might succeed after token refresh
//We could avoid this if we had a reasonable chance of knowing
//if a token was invalid before use (e.g. due to expiry). But we don't
//This is the intended behaviour per AccountManager
Timber.v(e, "Temporary failure - retrying with new token");
try {
- return attemptXOAuth2();
+ return attemptOAuth(method);
} catch (NegativeImapResponseException e2) {
//Okay, we failed on a new token.
//Invalidate the token anyway but assume it's permanent.
Timber.v(e, "Authentication exception for new token, permanent error assumed");
- oauthTokenProvider.invalidateToken(settings.getUsername());
- throw handlePermanentXoauth2Failure(e2);
+ oauthTokenProvider.invalidateToken();
+ throw handlePermanentOAuthFailure(e2);
}
}
- private List attemptXOAuth2() throws MessagingException, IOException {
+ private List attemptOAuth(OAuthMethod method) throws MessagingException, IOException {
String token = oauthTokenProvider.getToken(settings.getUsername(), OAuth2TokenProvider.OAUTH2_TIMEOUT);
- String authString = Authentication.computeXoauth(settings.getUsername(), token);
- String tag = sendSaslIrCommand(Commands.AUTHENTICATE_XOAUTH2, authString, true);
+ String authString = method.buildInitialClientResponse(settings.getUsername(), token);
+ String tag = sendSaslIrCommand(method.getCommand(), authString, true);
- return responseParser.readStatusResponse(tag, Commands.AUTHENTICATE_XOAUTH2, getLogId(),
+ return responseParser.readStatusResponse(tag, method.getCommand(), getLogId(),
new UntaggedHandler() {
@Override
public void handleAsyncUntaggedResponse(ImapResponse response) throws IOException {
- handleXOAuthUntaggedResponse(response);
+ handleOAuthUntaggedResponse(response);
}
});
}
- private void handleXOAuthUntaggedResponse(ImapResponse response) throws IOException {
- if (response.isString(0)) {
- retryXoauth2WithNewToken = XOAuth2ChallengeParser.shouldRetry(response.getString(0), settings.getHost());
+ private void handleOAuthUntaggedResponse(ImapResponse response) throws IOException {
+ if (!response.isContinuationRequested()) {
+ return;
}
- if (response.isContinuationRequested()) {
- outputStream.write("\r\n".getBytes());
- outputStream.flush();
+ if (response.isString(0)) {
+ retryOAuthWithNewToken = XOAuth2ChallengeParser.shouldRetry(response.getString(0), settings.getHost());
}
+
+ outputStream.write("\r\n".getBytes());
+ outputStream.flush();
}
private List authCramMD5() throws MessagingException, IOException {
@@ -577,32 +577,11 @@ class RealImapConnection implements ImapConnection {
}
private void enableCompressionIfRequested() throws IOException, MessagingException {
- if (hasCapability(Capabilities.COMPRESS_DEFLATE) && shouldEnableCompression()) {
+ if (hasCapability(Capabilities.COMPRESS_DEFLATE) && settings.useCompression()) {
enableCompression();
}
}
- private boolean shouldEnableCompression() {
- boolean useCompression = true;
-
- NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
- if (networkInfo != null) {
- int type = networkInfo.getType();
- if (K9MailLib.isDebug()) {
- Timber.d("On network type %s", type);
- }
-
- NetworkType networkType = NetworkType.fromConnectivityManagerType(type);
- useCompression = settings.useCompression(networkType);
- }
-
- if (K9MailLib.isDebug()) {
- Timber.d("useCompression: %b", useCompression);
- }
-
- return useCompression;
- }
-
private void enableCompression() throws IOException, MessagingException {
try {
executeSimpleCommand(Commands.COMPRESS_DEFLATE);
@@ -918,4 +897,33 @@ class RealImapConnection implements ImapConnection {
public int getConnectionGeneration() {
return connectionGeneration;
}
+
+
+ private enum OAuthMethod {
+ XOAUTH2 {
+ @Override
+ String getCommand() {
+ return Commands.AUTHENTICATE_XOAUTH2;
+ }
+
+ @Override
+ String buildInitialClientResponse(String username, String token) {
+ return Authentication.computeXoauth(username, token);
+ }
+ },
+ OAUTHBEARER {
+ @Override
+ String getCommand() {
+ return Commands.AUTHENTICATE_OAUTHBEARER;
+ }
+
+ @Override
+ String buildInitialClientResponse(String username, String token) {
+ return OAuthBearer.buildOAuthBearerInitialClientResponse(username, token);
+ }
+ };
+
+ abstract String getCommand();
+ abstract String buildInitialClientResponse(String username, String token);
+ }
}
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
index f037aadd8410cea0c5e4fb07fc8a249b0280e8ef..9508716b77de824036be59a6ee0f8838701c3304 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
@@ -1,5 +1,6 @@
package com.fsck.k9.mail.store.imap
+import com.fsck.k9.logging.Timber
import com.fsck.k9.mail.Body
import com.fsck.k9.mail.BodyFactory
import com.fsck.k9.mail.FetchProfile
@@ -24,7 +25,6 @@ import java.util.LinkedHashSet
import java.util.Locale
import kotlin.math.max
import kotlin.math.min
-import timber.log.Timber
internal class RealImapFolder(
private val internalImapStore: InternalImapStore,
@@ -538,7 +538,7 @@ internal class RealImapFolder(
fetchFields.add("RFC822.SIZE")
fetchFields.add(
"BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc " +
- "reply-to message-id references in-reply-to " +
+ "reply-to message-id references in-reply-to list-unsubscribe " +
K9MailLib.IDENTITY_HEADER + " " + K9MailLib.CHAT_HEADER + ")]"
)
}
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolderIdler.kt b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolderIdler.kt
index c1c6d711a2d31e635613ff9a0fb4c2d6209a4490..12b55d660ee99f318e434f6c8f5b47b315d7f5ff 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolderIdler.kt
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolderIdler.kt
@@ -1,9 +1,9 @@
package com.fsck.k9.mail.store.imap
+import com.fsck.k9.logging.Timber
import com.fsck.k9.mail.MessagingException
import com.fsck.k9.mail.power.WakeLock
import java.io.IOException
-import timber.log.Timber
private const val SOCKET_EXTRA_TIMEOUT_MS = 2 * 60 * 1000L
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.java
index 8b8e24fa0d7661bde04b6df370ecbb1fe61a93bc..98b4fb112fe1a208fee68a401f1e553b8d959b4a 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.java
@@ -13,20 +13,17 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import android.net.ConnectivityManager;
-import androidx.annotation.Nullable;
-
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.FolderType;
import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.NetworkType;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.oauth.OAuth2TokenProvider;
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
import org.jetbrains.annotations.NotNull;
-import timber.log.Timber;
+import org.jetbrains.annotations.Nullable;
/**
@@ -38,7 +35,6 @@ class RealImapStore implements ImapStore, ImapConnectionManager, InternalImapSto
private final ImapStoreConfig config;
private final TrustedSocketFactory trustedSocketFactory;
private Set permanentFlagsIndex = EnumSet.noneOf(Flag.class);
- private ConnectivityManager connectivityManager;
private OAuth2TokenProvider oauthTokenProvider;
private String host;
@@ -57,8 +53,7 @@ class RealImapStore implements ImapStore, ImapConnectionManager, InternalImapSto
public RealImapStore(ServerSettings serverSettings, ImapStoreConfig config,
- TrustedSocketFactory trustedSocketFactory, ConnectivityManager connectivityManager,
- OAuth2TokenProvider oauthTokenProvider) {
+ TrustedSocketFactory trustedSocketFactory, OAuth2TokenProvider oauthTokenProvider) {
this.config = config;
this.trustedSocketFactory = trustedSocketFactory;
@@ -66,7 +61,6 @@ class RealImapStore implements ImapStore, ImapConnectionManager, InternalImapSto
port = serverSettings.port;
connectionSecurity = serverSettings.connectionSecurity;
- this.connectivityManager = connectivityManager;
this.oauthTokenProvider = oauthTokenProvider;
authType = serverSettings.authenticationType;
@@ -327,7 +321,6 @@ class RealImapStore implements ImapStore, ImapConnectionManager, InternalImapSto
return new RealImapConnection(
new StoreImapSettings(),
trustedSocketFactory,
- connectivityManager,
oauthTokenProvider,
connectionGeneration);
}
@@ -382,8 +375,8 @@ class RealImapStore implements ImapStore, ImapConnectionManager, InternalImapSto
}
@Override
- public boolean useCompression(final NetworkType type) {
- return config.useCompression(type);
+ public boolean useCompression() {
+ return config.useCompression();
}
@Override
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.java b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.java
deleted file mode 100644
index a87fb5d16e90f171660834f761cc1d189447459b..0000000000000000000000000000000000000000
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.java
+++ /dev/null
@@ -1,1077 +0,0 @@
-package com.fsck.k9.mail.store.imap;
-
-
-import java.io.IOException;
-import java.net.UnknownHostException;
-import java.util.List;
-
-import android.app.Activity;
-import android.net.ConnectivityManager;
-
-import com.fsck.k9.mail.AuthType;
-import com.fsck.k9.mail.AuthenticationFailedException;
-import com.fsck.k9.mail.CertificateValidationException;
-import com.fsck.k9.mail.CertificateValidationException.Reason;
-import com.fsck.k9.mail.ConnectionSecurity;
-import com.fsck.k9.mail.K9LibRobolectricTestRunner;
-import com.fsck.k9.mail.K9MailLib;
-import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.XOAuth2ChallengeParserTest;
-import com.fsck.k9.mail.helpers.TestTrustedSocketFactory;
-import com.fsck.k9.mail.oauth.OAuth2TokenProvider;
-import com.fsck.k9.mail.ssl.TrustedSocketFactory;
-import com.fsck.k9.mail.store.imap.mockserver.MockImapServer;
-import okio.ByteString;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowLog;
-
-import static org.hamcrest.core.StringContains.containsString;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-
-
-@RunWith(K9LibRobolectricTestRunner.class)
-public class RealImapConnectionTest {
- private static final boolean DEBUGGING = false;
-
- private static final String USERNAME = "user";
- private static final String PASSWORD = "123456";
- private static final int SOCKET_CONNECT_TIMEOUT = 10000;
- private static final int SOCKET_READ_TIMEOUT = 10000;
- private static final String XOAUTH_TOKEN = "token";
- private static final String XOAUTH_ANOTHER_TOKEN = "token2";
- private static final String XOAUTH_STRING = ByteString.encodeUtf8(
- "user=" + USERNAME + "\001auth=Bearer " + XOAUTH_TOKEN + "\001\001").base64();
- private static final String XOAUTH_STRING_RETRY = ByteString.encodeUtf8(
- "user=" + USERNAME + "\001auth=Bearer " + XOAUTH_ANOTHER_TOKEN + "\001\001").base64();
-
-
- private TrustedSocketFactory socketFactory;
- private ConnectivityManager connectivityManager;
- private OAuth2TokenProvider oAuth2TokenProvider;
- private SimpleImapSettings settings;
-
-
- @Before
- public void setUp() throws Exception {
- connectivityManager = mock(ConnectivityManager.class);
- oAuth2TokenProvider = createOAuth2TokenProvider();
- socketFactory = TestTrustedSocketFactory.newInstance();
-
- settings = new SimpleImapSettings();
- settings.setUsername(USERNAME);
- settings.setPassword(PASSWORD);
-
- if (DEBUGGING) {
- ShadowLog.stream = System.out;
- K9MailLib.setDebug(true);
- K9MailLib.setDebugSensitive(true);
- }
- }
-
- @Test
- public void open_withNoCapabilitiesInInitialResponse_shouldIssuePreAuthCapabilitiesCommand() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- server.output("* OK example.org server");
- server.expect("1 CAPABILITY");
- server.output("* CAPABILITY IMAP4 IMAP4REV1 AUTH=PLAIN");
- server.output("1 OK CAPABILITY Completed");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("2 OK Success");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withCapabilitiesInInitialResponse_shouldNotIssuePreAuthCapabilitiesCommand() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- server.output("* OK [CAPABILITY IMAP4 IMAP4REV1 AUTH=PLAIN]");
- server.expect("1 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("1 OK Success");
- postAuthenticationDialogRequestingCapabilities(server, 2);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authPlain() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("2 OK Success");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_afterCloseWasCalled_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server);
- server.expect("2 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\"");
- server.output("2 OK LOGIN completed");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
- imapConnection.open();
- imapConnection.close();
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (IllegalStateException e) {
- assertEquals("open() called after close(). Check wrapped exception to see where close() was called.",
- e.getMessage());
- }
- }
-
- @Test
- public void open_authPlainWithLoginDisabled_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "LOGINDISABLED");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (MessagingException e) {
- assertEquals("Server doesn't support unencrypted passwords using AUTH=PLAIN and LOGIN is disabled.",
- e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authPlainWithAuthenticationFailure_shouldFallbackToLogin() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("2 NO Login Failure");
- server.expect("3 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\"");
- server.output("3 OK LOGIN completed");
- postAuthenticationDialogRequestingCapabilities(server, 4);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authPlainAndLoginFallbackWithAuthenticationFailure_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("2 NO Login Failure");
- server.expect("3 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\"");
- server.output("3 NO Go away");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (AuthenticationFailedException e) {
- //FIXME: improve exception message
- assertThat(e.getMessage(), containsString("Go away"));
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authPlainFailureAndDisconnect_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("2 NO [UNAVAILABLE] Maximum number of connections from user+IP exceeded");
- server.closeConnection();
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (NegativeImapResponseException e) {
- assertThat(e.getMessage(), containsString("Maximum number of connections from user+IP exceeded"));
- }
-
- assertFalse(imapConnection.isConnected());
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authPlainWithByeResponseAndConnectionClose_shouldThrowAuthenticationFailedException()
- throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("* BYE Go away");
- server.output("2 NO Login Failure");
- server.closeConnection();
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (AuthenticationFailedException e) {
- //FIXME: improve exception message
- assertThat(e.getMessage(), containsString("Login Failure"));
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authPlainWithoutAuthPlainCapability_shouldUseLoginMethod() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server);
- server.expect("2 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\"");
- server.output("2 OK LOGIN completed");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authCramMd5() throws Exception {
- settings.setAuthType(AuthType.CRAM_MD5);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=CRAM-MD5");
- server.expect("2 AUTHENTICATE CRAM-MD5");
- server.output("+ " + ByteString.encodeUtf8("<0000.000000000@example.org>").base64());
- server.expect("dXNlciA2ZjdiOTcyYjk5YTI4NDk4OTRhN2YyMmE3MGRhZDg0OQ==");
- server.output("2 OK Success");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authCramMd5WithAuthenticationFailure_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.CRAM_MD5);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=CRAM-MD5");
- server.expect("2 AUTHENTICATE CRAM-MD5");
- server.output("+ " + ByteString.encodeUtf8("<0000.000000000@example.org>").base64());
- server.expect("dXNlciA2ZjdiOTcyYjk5YTI4NDk4OTRhN2YyMmE3MGRhZDg0OQ==");
- server.output("2 NO Who are you?");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (AuthenticationFailedException e) {
- //FIXME: improve exception message
- assertThat(e.getMessage(), containsString("Who are you?"));
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authCramMd5WithoutAuthCramMd5Capability_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.CRAM_MD5);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (MessagingException e) {
- assertEquals("Server doesn't support encrypted passwords using CRAM-MD5.", e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authXoauthWithSaslIr() throws Exception {
- settings.setAuthType(AuthType.XOAUTH2);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2");
- server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING);
- server.output("2 OK Success");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authXoauthWithSaslIrThrowsExeptionOn401Response() throws Exception {
- settings.setAuthType(AuthType.XOAUTH2);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2");
- server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING);
- server.output("+ " + XOAuth2ChallengeParserTest.STATUS_401_RESPONSE);
- server.expect("");
- server.output("2 NO SASL authentication failed");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail();
- } catch (AuthenticationFailedException e) {
- assertEquals("Command: AUTHENTICATE XOAUTH2; response: #2# [NO, SASL authentication failed]",
- e.getMessage());
- }
- }
-
- @Test
- public void open_authXoauthWithSaslIrInvalidatesAndRetriesNewTokenOn400Response() throws Exception {
- settings.setAuthType(AuthType.XOAUTH2);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2");
- server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING);
- server.output("+ " + XOAuth2ChallengeParserTest.STATUS_400_RESPONSE);
- server.expect("");
- server.output("2 NO SASL authentication failed");
- server.expect("3 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING_RETRY);
- server.output("3 OK Success");
- postAuthenticationDialogRequestingCapabilities(server, 4);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authXoauthWithSaslIrInvalidatesAndRetriesNewTokenOnInvalidJsonResponse() throws Exception {
- settings.setAuthType(AuthType.XOAUTH2);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2");
- server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING);
- server.output("+ " + XOAuth2ChallengeParserTest.INVALID_RESPONSE);
- server.expect("");
- server.output("2 NO SASL authentication failed");
- server.expect("3 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING_RETRY);
- server.output("3 OK Success");
- requestCapabilities(server, 4);
- simplePostAuthenticationDialog(server, 5);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authXoauthWithSaslIrInvalidatesAndRetriesNewTokenOnMissingStatusJsonResponse() throws Exception {
- settings.setAuthType(AuthType.XOAUTH2);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2");
- server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING);
- server.output("+ " + XOAuth2ChallengeParserTest.MISSING_STATUS_RESPONSE);
- server.expect("");
- server.output("2 NO SASL authentication failed");
- server.expect("3 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING_RETRY);
- server.output("3 OK Success");
- requestCapabilities(server, 4);
- simplePostAuthenticationDialog(server, 5);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authXoauthWithSaslIrWithOldTokenThrowsExceptionIfRetryFails() throws Exception {
- settings.setAuthType(AuthType.XOAUTH2);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2");
- server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING);
- server.output("+ r3j3krj3irj3oir3ojo");
- server.expect("");
- server.output("2 NO SASL authentication failed");
- server.expect("3 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING_RETRY);
- server.output("+ 433ba3a3a");
- server.expect("");
- server.output("3 NO SASL authentication failed");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail();
- } catch (AuthenticationFailedException e) {
- assertEquals("Command: AUTHENTICATE XOAUTH2; response: #3# [NO, SASL authentication failed]",
- e.getMessage());
- }
- }
-
- @Test
- public void open_authXoauthWithSaslIrParsesCapabilities() throws Exception {
- settings.setAuthType(AuthType.XOAUTH2);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2");
- server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING);
- server.output("2 OK [CAPABILITY IMAP4REV1 IDLE XM-GM-EXT-1]");
- simplePostAuthenticationDialog(server, 3);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- assertTrue(imapConnection.hasCapability("XM-GM-EXT-1"));
- }
-
- @Test
- public void open_authExternal() throws Exception {
- settings.setAuthType(AuthType.EXTERNAL);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=EXTERNAL");
- server.expect("2 AUTHENTICATE EXTERNAL " + ByteString.encodeUtf8(USERNAME).base64());
- server.output("2 OK Success");
- postAuthenticationDialogRequestingCapabilities(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authExternalWithAuthenticationFailure_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.EXTERNAL);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=EXTERNAL");
- server.expect("2 AUTHENTICATE EXTERNAL " + ByteString.encodeUtf8(USERNAME).base64());
- server.output("2 NO Bad certificate");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (CertificateValidationException e) {
- //FIXME: improve exception message
- assertThat(e.getMessage(), containsString("Bad certificate"));
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_authExternalWithoutAuthExternalCapability_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.EXTERNAL);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (CertificateValidationException e) {
- assertEquals(Reason.MissingCapability, e.getReason());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withNoPostAuthCapabilityResponse_shouldIssueCapabilityCommand() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("2 OK Success");
- server.expect("3 CAPABILITY");
- server.output("* CAPABILITY IDLE");
- server.output("3 OK CAPABILITY Completed");
- simplePostAuthenticationDialog(server, 4);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- assertTrue(imapConnection.isIdleCapable());
- }
-
- @Test
- public void open_withUntaggedPostAuthCapabilityResponse_shouldNotIssueCapabilityCommand() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("* CAPABILITY IMAP4rev1 UNSELECT IDLE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS " +
- "ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT LIST-EXTENDED LIST-STATUS LITERAL- SPECIAL-USE " +
- "APPENDLIMIT=35651584");
- server.output("2 OK");
- simplePostAuthenticationDialog(server, 3);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- assertTrue(imapConnection.isIdleCapable());
- }
-
- @Test
- public void open_withPostAuthCapabilityResponse_shouldNotIssueCapabilityCommand() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "AUTH=PLAIN");
- server.expect("2 AUTHENTICATE PLAIN");
- server.output("+");
- server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64());
- server.output("2 OK [CAPABILITY IDLE]");
- simplePostAuthenticationDialog(server, 3);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- assertTrue(imapConnection.isIdleCapable());
- }
-
- @Test
- public void open_withNamespaceCapability_shouldIssueNamespaceCommand() throws Exception {
- MockImapServer server = new MockImapServer();
- simplePreAuthAndLoginDialog(server, "NAMESPACE");
- server.expect("3 NAMESPACE");
- server.output("* NAMESPACE ((\"\" \"/\")) NIL NIL");
- server.output("3 OK command completed");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withConnectionError_shouldThrow() throws Exception {
- settings.setHost("127.1.2.3");
- settings.setPort(143);
- ImapConnection imapConnection = createImapConnection(
- settings, socketFactory, connectivityManager, oAuth2TokenProvider);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (MessagingException e) {
- assertEquals("Cannot connect to host", e.getMessage());
- assertTrue(e.getCause() instanceof IOException);
- }
- }
-
- @Test
- public void open_withInvalidHostname_shouldThrow() throws Exception {
- settings.setHost("host name");
- settings.setPort(143);
- ImapConnection imapConnection = createImapConnection(
- settings, socketFactory, connectivityManager, oAuth2TokenProvider);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (UnknownHostException ignored) {
- }
-
- assertFalse(imapConnection.isConnected());
- }
-
- @Test
- public void open_withStartTlsCapability_shouldIssueStartTlsCommand() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "STARTTLS LOGINDISABLED");
- server.expect("2 STARTTLS");
- server.output("2 OK [CAPABILITY IMAP4REV1 NAMESPACE]");
- server.startTls();
- server.expect("3 CAPABILITY");
- server.output("* CAPABILITY IMAP4 IMAP4REV1");
- server.output("3 OK");
- server.expect("4 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\"");
- server.output("4 OK [CAPABILITY NAMESPACE] LOGIN completed");
- server.expect("5 NAMESPACE");
- server.output("* NAMESPACE ((\"\" \"/\")) NIL NIL");
- server.output("5 OK command completed");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withStartTlsButWithoutStartTlsCapability_shouldThrow() throws Exception {
- settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (CertificateValidationException e) {
- //FIXME: CertificateValidationException seems wrong
- assertEquals("STARTTLS connection security not available", e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withUntaggedCapabilityAfterStartTls_shouldNotThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "STARTTLS LOGINDISABLED");
- server.expect("2 STARTTLS");
- server.output("2 OK Begin TLS negotiation now");
- server.startTls();
- server.output("* CAPABILITY IMAP4REV1 IMAP4");
- server.expect("3 CAPABILITY");
- server.output("* CAPABILITY IMAP4 IMAP4REV1");
- server.output("3 OK");
- server.expect("4 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\"");
- server.output("4 OK [CAPABILITY IMAP4REV1] LOGIN completed");
- simplePostAuthenticationDialog(server, 5);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withNegativeResponseToStartTlsCommand_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- settings.setConnectionSecurity(ConnectionSecurity.STARTTLS_REQUIRED);
- MockImapServer server = new MockImapServer();
- preAuthenticationDialog(server, "STARTTLS");
- server.expect("2 STARTTLS");
- server.output("2 NO");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Expected exception");
- } catch (NegativeImapResponseException e) {
- assertEquals(e.getMessage(), "Command: STARTTLS; response: #2# [NO]");
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withCompressDeflateCapability_shouldEnableCompression() throws Exception {
- settings.setUseCompression(true);
- MockImapServer server = new MockImapServer();
- simplePreAuthAndLoginDialog(server, "COMPRESS=DEFLATE");
- server.expect("3 COMPRESS DEFLATE");
- server.output("3 OK");
- server.enableCompression();
- simplePostAuthenticationDialog(server, 4);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withNegativeResponseToCompressionCommand_shouldContinue() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- settings.setUseCompression(true);
- MockImapServer server = new MockImapServer();
- simplePreAuthAndLoginDialog(server, "COMPRESS=DEFLATE");
- server.expect("3 COMPRESS DEFLATE");
- server.output("3 NO");
- simplePostAuthenticationDialog(server, 4);
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withIoExceptionDuringCompressionCommand_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- settings.setUseCompression(true);
- MockImapServer server = new MockImapServer();
- simplePreAuthAndLoginDialog(server, "COMPRESS=DEFLATE");
- server.expect("3 COMPRESS DEFLATE");
- server.closeConnection();
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Exception expected");
- } catch (IOException ignored) {
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withIoExceptionDuringListCommand_shouldThrow() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- simplePreAuthAndLoginDialog(server, "");
- server.expect("3 LIST \"\" \"\"");
- server.output("* Now what?");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.open();
- fail("Exception expected");
- } catch (IOException ignored) {
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withNegativeResponseToListCommand() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- simplePreAuthAndLoginDialog(server, "");
- server.expect("3 LIST \"\" \"\"");
- server.output("3 NO");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- imapConnection.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void isConnected_withoutPreviousOpen_shouldReturnFalse() throws Exception {
- ImapConnection imapConnection = createImapConnection(
- settings, socketFactory, connectivityManager, oAuth2TokenProvider);
-
- boolean result = imapConnection.isConnected();
-
- assertFalse(result);
- }
-
- @Test
- public void isConnected_afterOpen_shouldReturnTrue() throws Exception {
- MockImapServer server = new MockImapServer();
- ImapConnection imapConnection = simpleOpen(server);
-
- boolean result = imapConnection.isConnected();
-
- assertTrue(result);
- server.verifyConnectionStillOpen();
-
- server.shutdown();
- }
-
- @Test
- public void isConnected_afterOpenAndClose_shouldReturnFalse() throws Exception {
- MockImapServer server = new MockImapServer();
- ImapConnection imapConnection = simpleOpen(server);
- imapConnection.close();
-
- boolean result = imapConnection.isConnected();
-
- assertFalse(result);
- server.verifyConnectionClosed();
-
- server.shutdown();
- }
-
- @Test
- public void close_withoutOpen_shouldNotThrow() throws Exception {
- ImapConnection imapConnection = createImapConnection(
- settings, socketFactory, connectivityManager, oAuth2TokenProvider);
-
- imapConnection.close();
- }
-
- @Test
- public void close_afterOpen_shouldCloseConnection() throws Exception {
- MockImapServer server = new MockImapServer();
- ImapConnection imapConnection = simpleOpen(server);
-
- imapConnection.close();
-
- server.verifyConnectionClosed();
-
- server.shutdown();
- }
-
- @Test
- public void isIdleCapable_withoutIdleCapability() throws Exception {
- MockImapServer server = new MockImapServer();
- ImapConnection imapConnection = simpleOpen(server);
-
- boolean result = imapConnection.isIdleCapable();
-
- assertFalse(result);
-
- server.shutdown();
- }
-
- @Test
- public void isIdleCapable_withIdleCapability() throws Exception {
- MockImapServer server = new MockImapServer();
- ImapConnection imapConnection = simpleOpenWithCapabilities(server, "IDLE");
-
- boolean result = imapConnection.isIdleCapable();
-
- assertTrue(result);
-
- server.shutdown();
- }
-
- @Test
- public void sendContinuation() throws Exception {
- settings.setAuthType(AuthType.PLAIN);
- MockImapServer server = new MockImapServer();
- simpleOpenDialog(server, "IDLE");
- server.expect("4 IDLE");
- server.output("+ idling");
- server.expect("DONE");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
- imapConnection.open();
- imapConnection.sendCommand("IDLE", false);
- imapConnection.readResponse();
-
- imapConnection.sendContinuation("DONE");
-
- server.waitForInteractionToComplete();
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void executeSingleCommand_withOkResponse_shouldReturnResult() throws Exception {
- MockImapServer server = new MockImapServer();
- simpleOpenDialog(server, "");
- server.expect("4 CREATE Folder");
- server.output("4 OK Folder created");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- List result = imapConnection.executeSimpleCommand("CREATE Folder");
-
- assertEquals(result.size(), 1);
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void executeSingleCommand_withNoResponse_shouldThrowNegativeImapResponseException() throws Exception {
- MockImapServer server = new MockImapServer();
- simpleOpenDialog(server, "");
- server.expect("4 CREATE Folder");
- server.output("4 NO Folder exists");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- try {
- imapConnection.executeSimpleCommand("CREATE Folder");
-
- fail("Expected exception");
- } catch (NegativeImapResponseException e) {
- assertEquals("Folder exists", e.getLastResponse().getString(1));
- }
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void hasCapability_withNotYetOpenedConnection_shouldConnectAndFetchCapabilities() throws Exception {
- MockImapServer server = new MockImapServer();
- simpleOpenDialog(server, "X-SOMETHING");
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
-
- boolean capabilityPresent = imapConnection.hasCapability("X-SOMETHING");
-
- assertTrue(capabilityPresent);
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- private ImapConnection createImapConnection(ImapSettings settings, TrustedSocketFactory socketFactory,
- ConnectivityManager connectivityManager, OAuth2TokenProvider oAuth2TokenProvider) {
- return new RealImapConnection(settings, socketFactory, connectivityManager, oAuth2TokenProvider,
- SOCKET_CONNECT_TIMEOUT, SOCKET_READ_TIMEOUT, 1);
- }
-
- private ImapConnection startServerAndCreateImapConnection(MockImapServer server) throws IOException {
- server.start();
- settings.setHost(server.getHost());
- settings.setPort(server.getPort());
- return createImapConnection(settings, socketFactory, connectivityManager, oAuth2TokenProvider);
- }
-
- private ImapConnection simpleOpen(MockImapServer server) throws Exception {
- return simpleOpenWithCapabilities(server, "");
- }
-
- private ImapConnection simpleOpenWithCapabilities(MockImapServer server, String postAuthCapabilities)
- throws Exception {
- simpleOpenDialog(server, postAuthCapabilities);
-
- ImapConnection imapConnection = startServerAndCreateImapConnection(server);
- imapConnection.open();
-
- return imapConnection;
- }
-
- private void preAuthenticationDialog(MockImapServer server) {
- preAuthenticationDialog(server, "");
- }
-
- private void preAuthenticationDialog(MockImapServer server, String capabilities) {
- server.output("* OK IMAP4rev1 Service Ready");
- server.expect("1 CAPABILITY");
- server.output("* CAPABILITY IMAP4 IMAP4REV1 " + capabilities);
- server.output("1 OK CAPABILITY");
- }
-
- private void postAuthenticationDialogRequestingCapabilities(MockImapServer server) {
- postAuthenticationDialogRequestingCapabilities(server, 3);
- }
-
- private void postAuthenticationDialogRequestingCapabilities(MockImapServer server, int tag) {
- requestCapabilities(server, tag);
- simplePostAuthenticationDialog(server, tag + 1);
- }
-
- private void requestCapabilities(MockImapServer server, int tag) {
- server.expect(tag + " CAPABILITY");
- server.output("* CAPABILITY IMAP4 IMAP4REV1 ");
- server.output(tag + " OK CAPABILITY");
- }
-
- private void simplePostAuthenticationDialog(MockImapServer server, int tag) {
- server.expect(tag + " LIST \"\" \"\"");
- server.output("* LIST () \"/\" foo/bar");
- server.output(tag + " OK");
- }
-
- private void simpleOpenDialog(MockImapServer server, String postAuthCapabilities) {
- simplePreAuthAndLoginDialog(server, postAuthCapabilities);
- simplePostAuthenticationDialog(server, 3);
- }
-
- private void simplePreAuthAndLoginDialog(MockImapServer server, String postAuthCapabilities) {
- settings.setAuthType(AuthType.PLAIN);
-
- preAuthenticationDialog(server);
-
- server.expect("2 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\"");
- server.output("2 OK [CAPABILITY " + postAuthCapabilities + "] LOGIN completed");
- }
-
- private OAuth2TokenProvider createOAuth2TokenProvider() {
- return new OAuth2TokenProvider() {
- private int invalidationCount = 0;
-
- @Override
- public String getToken(String username, long timeoutMillis) throws AuthenticationFailedException {
- assertEquals(USERNAME, username);
- assertEquals(OAUTH2_TIMEOUT, timeoutMillis);
-
- switch (invalidationCount) {
- case 0: {
- return XOAUTH_TOKEN;
- }
- case 1: {
- return XOAUTH_ANOTHER_TOKEN;
- }
- default: {
- throw new AuthenticationFailedException("Ran out of auth tokens. invalidateToken() called too often?");
- }
- }
- }
-
- @Override
- public void invalidateToken(String username) {
- assertEquals(USERNAME, username);
- invalidationCount++;
- }
- };
- }
-}
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.kt b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ef4fc9e1e47699ccded3bba58d52e62cc31977e0
--- /dev/null
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapConnectionTest.kt
@@ -0,0 +1,1100 @@
+package com.fsck.k9.mail.store.imap
+
+import com.fsck.k9.logging.Timber
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.AuthenticationFailedException
+import com.fsck.k9.mail.CertificateValidationException
+import com.fsck.k9.mail.ConnectionSecurity
+import com.fsck.k9.mail.K9MailLib
+import com.fsck.k9.mail.MessagingException
+import com.fsck.k9.mail.SystemOutLogger
+import com.fsck.k9.mail.XOAuth2ChallengeParserTest
+import com.fsck.k9.mail.helpers.TestTrustedSocketFactory
+import com.fsck.k9.mail.oauth.OAuth2TokenProvider
+import com.fsck.k9.mail.ssl.TrustedSocketFactory
+import com.fsck.k9.mail.store.imap.mockserver.MockImapServer
+import com.google.common.truth.Truth.assertThat
+import java.io.IOException
+import java.net.UnknownHostException
+import okio.ByteString.Companion.encodeUtf8
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+
+private const val DEBUGGING = false
+
+private const val USERNAME = "user"
+private const val PASSWORD = "123456"
+
+private const val SOCKET_CONNECT_TIMEOUT = 10000
+private const val SOCKET_READ_TIMEOUT = 10000
+
+private const val XOAUTH_TOKEN = "token"
+private const val XOAUTH_TOKEN_2 = "token2"
+private val XOAUTH_STRING = "user=$USERNAME\u0001auth=Bearer $XOAUTH_TOKEN\u0001\u0001".base64()
+private val XOAUTH_STRING_RETRY = "user=$USERNAME\u0001auth=Bearer $XOAUTH_TOKEN_2\u0001\u0001".base64()
+private val OAUTHBEARER_STRING = "n,a=$USERNAME,\u0001auth=Bearer $XOAUTH_TOKEN\u0001\u0001".base64()
+
+class RealImapConnectionTest {
+ private var socketFactory = TestTrustedSocketFactory.newInstance()
+ private var oAuth2TokenProvider = TestTokenProvider()
+ private var settings = SimpleImapSettings().apply {
+ username = USERNAME
+ password = PASSWORD
+ }
+
+ @Before
+ fun setUp() {
+ if (DEBUGGING) {
+ Timber.logger = SystemOutLogger()
+ K9MailLib.setDebug(true)
+ K9MailLib.setDebugSensitive(true)
+ }
+ }
+
+ @Test
+ fun `open() with no capabilities in initial response should issue pre-auth capabilities command`() {
+ val server = MockImapServer().apply {
+ output("* OK example.org server")
+ expect("1 CAPABILITY")
+ output("* CAPABILITY IMAP4 IMAP4REV1 AUTH=PLAIN")
+ output("1 OK CAPABILITY Completed")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("2 OK Success")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with capabilities in initial response should not issue pre-auth capabilities command`() {
+ val server = MockImapServer().apply {
+ output("* OK [CAPABILITY IMAP4 IMAP4REV1 AUTH=PLAIN]")
+ expect("1 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("1 OK Success")
+ postAuthenticationDialogRequestingCapabilities(tag = 2)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH PLAIN`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("2 OK Success")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() after close() was called should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog()
+ expect("2 LOGIN \"$USERNAME\" \"$PASSWORD\"")
+ output("2 OK LOGIN completed")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+ imapConnection.open()
+ imapConnection.close()
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: IllegalStateException) {
+ assertThat(e).hasMessageThat()
+ .isEqualTo("open() called after close(). Check wrapped exception to see where close() was called.")
+ }
+ }
+
+ @Test
+ fun `open() AUTH PLAIN with login disabled should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "LOGINDISABLED")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: MessagingException) {
+ assertThat(e).hasMessageThat()
+ .isEqualTo("Server doesn't support unencrypted passwords using AUTH=PLAIN and LOGIN is disabled.")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH PLAIN with authentication failure should fall back to LOGIN`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("2 NO Login Failure")
+ expect("3 LOGIN \"$USERNAME\" \"$PASSWORD\"")
+ output("3 OK LOGIN completed")
+ postAuthenticationDialogRequestingCapabilities(tag = 4)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH PLAIN and LOGIN fallback with authentication failure should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("2 NO Login Failure")
+ expect("3 LOGIN \"$USERNAME\" \"$PASSWORD\"")
+ output("3 NO Go away")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: AuthenticationFailedException) {
+ // FIXME: improve exception message
+ assertThat(e).hasMessageThat().contains("Go away")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH PLAIN failure and disconnect should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("2 NO [UNAVAILABLE] Maximum number of connections from user+IP exceeded")
+ closeConnection()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: NegativeImapResponseException) {
+ assertThat(e).hasMessageThat().contains("Maximum number of connections from user+IP exceeded")
+ }
+
+ assertThat(imapConnection.isConnected).isFalse()
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH PLAIN with BYE response and connection close should throw AuthenticationFailedException`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("* BYE Go away")
+ output("2 NO Login Failure")
+ closeConnection()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: AuthenticationFailedException) {
+ // FIXME: improve exception message
+ assertThat(e).hasMessageThat().contains("Login Failure")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH PLAIN without AUTH PLAIN capability should use LOGIN command`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog()
+ expect("2 LOGIN \"$USERNAME\" \"$PASSWORD\"")
+ output("2 OK LOGIN completed")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH CRAM-MD5`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=CRAM-MD5")
+ expect("2 AUTHENTICATE CRAM-MD5")
+ output("+ ${"<0000.000000000@example.org>".base64()}")
+ expect("dXNlciA2ZjdiOTcyYjk5YTI4NDk4OTRhN2YyMmE3MGRhZDg0OQ==")
+ output("2 OK Success")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.CRAM_MD5)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH CRAM-MD5 with authentication failure should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=CRAM-MD5")
+ expect("2 AUTHENTICATE CRAM-MD5")
+ output("+ ${"<0000.000000000@example.org>".base64()}")
+ expect("dXNlciA2ZjdiOTcyYjk5YTI4NDk4OTRhN2YyMmE3MGRhZDg0OQ==")
+ output("2 NO Who are you?")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.CRAM_MD5)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: AuthenticationFailedException) {
+ // FIXME: improve exception message
+ assertThat(e).hasMessageThat().contains("Who are you?")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH CRAM-MD5 without AUTH CRAM-MD5 capability should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.CRAM_MD5)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: MessagingException) {
+ assertThat(e).hasMessageThat().isEqualTo("Server doesn't support encrypted passwords using CRAM-MD5.")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH OAUTHBEARER`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=OAUTHBEARER")
+ expect("2 AUTHENTICATE OAUTHBEARER $OAUTHBEARER_STRING")
+ output("2 OK Success")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH OAUTHBEARER when AUTH=XOAUTH2 and AUTH=OAUTHBEARER capabilities are present`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH2 AUTH=OAUTHBEARER")
+ expect("2 AUTHENTICATE OAUTHBEARER $OAUTHBEARER_STRING")
+ output("2 OK Success")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 with SASL-IR`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("2 OK Success")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 with untagged CAPABILITY response after authentication`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("* CAPABILITY IMAP4rev1 X-GM-EXT-1")
+ output("2 OK Success")
+ simplePostAuthenticationDialog(tag = 3)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ assertThat(imapConnection.hasCapability("X-GM-EXT-1")).isTrue()
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 throws exception on 401 response`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("+ ${XOAuth2ChallengeParserTest.STATUS_401_RESPONSE}")
+ expect("")
+ output("2 NO SASL authentication failed")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ try {
+ imapConnection.open()
+ fail()
+ } catch (e: AuthenticationFailedException) {
+ assertThat(e).hasMessageThat()
+ .isEqualTo("Command: AUTHENTICATE XOAUTH2; response: #2# [NO, SASL authentication failed]")
+ }
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 invalidates and retries new token on 400 response`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("+ ${XOAuth2ChallengeParserTest.STATUS_400_RESPONSE}")
+ expect("")
+ output("2 NO SASL authentication failed")
+ expect("3 AUTHENTICATE XOAUTH2 $XOAUTH_STRING_RETRY")
+ output("3 OK Success")
+ postAuthenticationDialogRequestingCapabilities(tag = 4)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 invalidates and retries new token on invalid JSON response`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("+ ${XOAuth2ChallengeParserTest.INVALID_RESPONSE}")
+ expect("")
+ output("2 NO SASL authentication failed")
+ expect("3 AUTHENTICATE XOAUTH2 $XOAUTH_STRING_RETRY")
+ output("3 OK Success")
+ requestCapabilities(tag = 4)
+ simplePostAuthenticationDialog(tag = 5)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 invalidates and retries new token on missing status JSON response`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("+ ${XOAuth2ChallengeParserTest.MISSING_STATUS_RESPONSE}")
+ expect("")
+ output("2 NO SASL authentication failed")
+ expect("3 AUTHENTICATE XOAUTH2 $XOAUTH_STRING_RETRY")
+ output("3 OK Success")
+ requestCapabilities(tag = 4)
+ simplePostAuthenticationDialog(tag = 5)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 with old token throws exception if retry fails`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("+ r3j3krj3irj3oir3ojo")
+ expect("")
+ output("2 NO SASL authentication failed")
+ expect("3 AUTHENTICATE XOAUTH2 $XOAUTH_STRING_RETRY")
+ output("+ 433ba3a3a")
+ expect("")
+ output("3 NO SASL authentication failed")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ try {
+ imapConnection.open()
+ fail()
+ } catch (e: AuthenticationFailedException) {
+ assertThat(e).hasMessageThat()
+ .isEqualTo("Command: AUTHENTICATE XOAUTH2; response: #3# [NO, SASL authentication failed]")
+ }
+ }
+
+ @Test
+ fun `open() AUTH XOAUTH2 parses capabilities`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2")
+ expect("2 AUTHENTICATE XOAUTH2 $XOAUTH_STRING")
+ output("2 OK [CAPABILITY IMAP4REV1 IDLE XM-GM-EXT-1]")
+ simplePostAuthenticationDialog(tag = 3)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.XOAUTH2)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ assertThat(imapConnection.hasCapability("XM-GM-EXT-1")).isTrue()
+ }
+
+ @Test
+ fun `open() AUTH EXTERNAL`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=EXTERNAL")
+ expect("2 AUTHENTICATE EXTERNAL ${USERNAME.base64()}")
+ output("2 OK Success")
+ postAuthenticationDialogRequestingCapabilities()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.EXTERNAL)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH EXTERNAL with authentication failure should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=EXTERNAL")
+ expect("2 AUTHENTICATE EXTERNAL ${USERNAME.base64()}")
+ output("2 NO Bad certificate")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.EXTERNAL)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: CertificateValidationException) {
+ // FIXME: improve exception message
+ assertThat(e).hasMessageThat().contains("Bad certificate")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() AUTH EXTERNAL without AUTH EXTERNAL capability should throw`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.EXTERNAL)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: CertificateValidationException) {
+ assertThat(e.reason).isEqualTo(CertificateValidationException.Reason.MissingCapability)
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with no post-auth CAPABILITY response should issue CAPABILITY command`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("2 OK Success")
+ expect("3 CAPABILITY")
+ output("* CAPABILITY IDLE")
+ output("3 OK CAPABILITY Completed")
+ simplePostAuthenticationDialog(tag = 4)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ assertThat(imapConnection.isIdleCapable).isTrue()
+ }
+
+ @Test
+ fun `open() with untagged post-auth CAPABILITY response`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output(
+ "* CAPABILITY IMAP4rev1 UNSELECT IDLE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS " +
+ "ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT LIST-EXTENDED LIST-STATUS LITERAL- SPECIAL-USE " +
+ "APPENDLIMIT=35651584"
+ )
+ output("2 OK")
+ simplePostAuthenticationDialog(tag = 3)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ assertThat(imapConnection.isIdleCapable).isTrue()
+ }
+
+ @Test
+ fun `open() with post-auth CAPABILITY response`() {
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "AUTH=PLAIN")
+ expect("2 AUTHENTICATE PLAIN")
+ output("+")
+ expect("\u0000$USERNAME\u0000$PASSWORD".base64())
+ output("2 OK [CAPABILITY IDLE]")
+ simplePostAuthenticationDialog(tag = 3)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ assertThat(imapConnection.isIdleCapable).isTrue()
+ }
+
+ @Test
+ fun `open() with NAMESPACE capability should issue NAMESPACE command`() {
+ val server = MockImapServer().apply {
+ simplePreAuthAndLoginDialog(postAuthCapabilities = "NAMESPACE")
+ expect("3 NAMESPACE")
+ output("* NAMESPACE ((\"\" \"/\")) NIL NIL")
+ output("3 OK command completed")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with connection error should throw`() {
+ settings.host = "127.1.2.3"
+ settings.port = 143
+ val imapConnection = createImapConnection(settings, socketFactory, oAuth2TokenProvider)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: MessagingException) {
+ assertThat(e).hasMessageThat().isEqualTo("Cannot connect to host")
+ assertThat(e).hasCauseThat().isInstanceOf(IOException::class.java)
+ }
+ }
+
+ @Test
+ fun `open() with invalid hostname should throw`() {
+ settings.host = "host name"
+ settings.port = 143
+ val imapConnection = createImapConnection(settings, socketFactory, oAuth2TokenProvider)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (ignored: UnknownHostException) {
+ }
+
+ assertThat(imapConnection.isConnected).isFalse()
+ }
+
+ @Test
+ fun `open() with STARTTLS capability should issue STARTTLS command`() {
+ settings.connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "STARTTLS LOGINDISABLED")
+ expect("2 STARTTLS")
+ output("2 OK [CAPABILITY IMAP4REV1 NAMESPACE]")
+ startTls()
+ expect("3 CAPABILITY")
+ output("* CAPABILITY IMAP4 IMAP4REV1")
+ output("3 OK")
+ expect("4 LOGIN \"$USERNAME\" \"$PASSWORD\"")
+ output("4 OK [CAPABILITY NAMESPACE] LOGIN completed")
+ expect("5 NAMESPACE")
+ output("* NAMESPACE ((\"\" \"/\")) NIL NIL")
+ output("5 OK command completed")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with STARTTLS but without STARTTLS capability should throw`() {
+ settings.connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED
+ val server = MockImapServer().apply {
+ preAuthenticationDialog()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: CertificateValidationException) {
+ // FIXME: CertificateValidationException seems wrong
+ assertThat(e).hasMessageThat().isEqualTo("STARTTLS connection security not available")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with untagged CAPABILITY after STARTTLS should not throw`() {
+ settings.connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "STARTTLS LOGINDISABLED")
+ expect("2 STARTTLS")
+ output("2 OK Begin TLS negotiation now")
+ startTls()
+ output("* CAPABILITY IMAP4REV1 IMAP4")
+ expect("3 CAPABILITY")
+ output("* CAPABILITY IMAP4 IMAP4REV1")
+ output("3 OK")
+ expect("4 LOGIN \"$USERNAME\" \"$PASSWORD\"")
+ output("4 OK [CAPABILITY IMAP4REV1] LOGIN completed")
+ simplePostAuthenticationDialog(tag = 5)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with negative response to STARTTLS command should throw`() {
+ settings.connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED
+ val server = MockImapServer().apply {
+ preAuthenticationDialog(capabilities = "STARTTLS")
+ expect("2 STARTTLS")
+ output("2 NO")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ try {
+ imapConnection.open()
+ fail("Expected exception")
+ } catch (e: NegativeImapResponseException) {
+ assertThat(e).hasMessageThat().isEqualTo("Command: STARTTLS; response: #2# [NO]")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with COMPRESS=DEFLATE capability should enable compression`() {
+ settings.setUseCompression(true)
+ val server = MockImapServer().apply {
+ simplePreAuthAndLoginDialog(postAuthCapabilities = "COMPRESS=DEFLATE")
+ expect("3 COMPRESS DEFLATE")
+ output("3 OK")
+ enableCompression()
+ simplePostAuthenticationDialog(tag = 4)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with negative response to COMPRESS command should continue`() {
+ settings.setUseCompression(true)
+ val server = MockImapServer().apply {
+ simplePreAuthAndLoginDialog(postAuthCapabilities = "COMPRESS=DEFLATE")
+ expect("3 COMPRESS DEFLATE")
+ output("3 NO")
+ simplePostAuthenticationDialog(tag = 4)
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with IOException during COMPRESS command should throw`() {
+ settings.setUseCompression(true)
+ val server = MockImapServer().apply {
+ simplePreAuthAndLoginDialog(postAuthCapabilities = "COMPRESS=DEFLATE")
+ expect("3 COMPRESS DEFLATE")
+ closeConnection()
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ try {
+ imapConnection.open()
+ fail("Exception expected")
+ } catch (ignored: IOException) {
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with IOException during LIST command should throw`() {
+ val server = MockImapServer().apply {
+ simplePreAuthAndLoginDialog()
+ expect("3 LIST \"\" \"\"")
+ output("* Now what?")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ try {
+ imapConnection.open()
+ fail("Exception expected")
+ } catch (ignored: IOException) {
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with negative response to LIST command`() {
+ val server = MockImapServer().apply {
+ simplePreAuthAndLoginDialog()
+ expect("3 LIST \"\" \"\"")
+ output("3 NO")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ imapConnection.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `isConnected without previous open() should return false`() {
+ val imapConnection = createImapConnection(settings, socketFactory, oAuth2TokenProvider)
+
+ val result = imapConnection.isConnected
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun `isConnected after open() should return true`() {
+ val server = MockImapServer()
+ val imapConnection = simpleOpen(server)
+
+ val result = imapConnection.isConnected
+
+ assertThat(result).isTrue()
+ server.verifyConnectionStillOpen()
+ server.shutdown()
+ }
+
+ @Test
+ fun isConnected_afterOpenAndClose_shouldReturnFalse() {
+ val server = MockImapServer()
+ val imapConnection = simpleOpen(server)
+ imapConnection.close()
+
+ val result = imapConnection.isConnected
+
+ assertThat(result).isFalse()
+ server.verifyConnectionClosed()
+ server.shutdown()
+ }
+
+ @Test
+ fun `close() without open() should not throw`() {
+ val imapConnection = createImapConnection(settings, socketFactory, oAuth2TokenProvider)
+
+ imapConnection.close()
+ }
+
+ @Test
+ fun `close() after open() should close connection`() {
+ val server = MockImapServer()
+ val imapConnection = simpleOpen(server)
+
+ imapConnection.close()
+
+ server.verifyConnectionClosed()
+ server.shutdown()
+ }
+
+ @Test
+ fun `isIdleCapable without IDLE capability should return false`() {
+ val server = MockImapServer()
+ val imapConnection = simpleOpen(server)
+
+ val result = imapConnection.isIdleCapable
+
+ assertThat(result).isFalse()
+ server.shutdown()
+ }
+
+ @Test
+ fun `isIdleCapable with IDLE capability should return true`() {
+ val server = MockImapServer()
+ val imapConnection = simpleOpenWithCapabilities(server, postAuthCapabilities = "IDLE")
+
+ val result = imapConnection.isIdleCapable
+
+ assertThat(result).isTrue()
+ server.shutdown()
+ }
+
+ @Test
+ fun `sendContinuation() should send line without tag`() {
+ val server = MockImapServer().apply {
+ simpleOpenDialog(postAuthCapabilities = "IDLE")
+ expect("4 IDLE")
+ output("+ idling")
+ expect("DONE")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server, authType = AuthType.PLAIN)
+
+ imapConnection.open()
+ imapConnection.sendCommand("IDLE", false)
+ imapConnection.readResponse()
+ imapConnection.sendContinuation("DONE")
+
+ server.waitForInteractionToComplete()
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `executeSimpleCommand() with OK response should return result`() {
+ val server = MockImapServer().apply {
+ simpleOpenDialog()
+ expect("4 CREATE Folder")
+ output("4 OK Folder created")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ val result = imapConnection.executeSimpleCommand("CREATE Folder")
+
+ assertThat(result).hasSize(1)
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `executeSimpleCommand() with NO response should throw NegativeImapResponseException`() {
+ val server = MockImapServer().apply {
+ simpleOpenDialog()
+ expect("4 CREATE Folder")
+ output("4 NO Folder exists")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ try {
+ imapConnection.executeSimpleCommand("CREATE Folder")
+ fail("Expected exception")
+ } catch (e: NegativeImapResponseException) {
+ assertThat(e.lastResponse).containsExactly("NO", "Folder exists")
+ }
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `hasCapability() with not yet opened connection should connect and fetch capabilities`() {
+ val server = MockImapServer().apply {
+ simpleOpenDialog(postAuthCapabilities = "X-SOMETHING")
+ }
+ val imapConnection = startServerAndCreateImapConnection(server)
+
+ val capabilityPresent = imapConnection.hasCapability("X-SOMETHING")
+
+ assertThat(capabilityPresent).isTrue()
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ private fun createImapConnection(
+ settings: ImapSettings,
+ socketFactory: TrustedSocketFactory,
+ oAuth2TokenProvider: OAuth2TokenProvider
+ ): ImapConnection {
+ val connectionGeneration = 1
+ return RealImapConnection(
+ settings,
+ socketFactory,
+ oAuth2TokenProvider,
+ SOCKET_CONNECT_TIMEOUT,
+ SOCKET_READ_TIMEOUT,
+ connectionGeneration
+ )
+ }
+
+ private fun startServerAndCreateImapConnection(
+ server: MockImapServer,
+ authType: AuthType = AuthType.PLAIN
+ ): ImapConnection {
+ server.start()
+ settings.host = server.host
+ settings.port = server.port
+ settings.authType = authType
+
+ return createImapConnection(settings, socketFactory, oAuth2TokenProvider)
+ }
+
+ private fun simpleOpen(server: MockImapServer): ImapConnection {
+ return simpleOpenWithCapabilities(server, postAuthCapabilities = "")
+ }
+
+ private fun simpleOpenWithCapabilities(server: MockImapServer, postAuthCapabilities: String): ImapConnection {
+ server.simpleOpenDialog(postAuthCapabilities)
+
+ val imapConnection = startServerAndCreateImapConnection(server)
+ imapConnection.open()
+
+ return imapConnection
+ }
+
+ private fun MockImapServer.preAuthenticationDialog(capabilities: String = "") {
+ output("* OK IMAP4rev1 Service Ready")
+ expect("1 CAPABILITY")
+ output("* CAPABILITY IMAP4 IMAP4REV1 $capabilities")
+ output("1 OK CAPABILITY")
+ }
+
+ private fun MockImapServer.postAuthenticationDialogRequestingCapabilities(tag: Int = 3) {
+ requestCapabilities(tag)
+ simplePostAuthenticationDialog(tag + 1)
+ }
+
+ private fun MockImapServer.requestCapabilities(tag: Int) {
+ expect("$tag CAPABILITY")
+ output("* CAPABILITY IMAP4 IMAP4REV1 ")
+ output("$tag OK CAPABILITY")
+ }
+
+ private fun MockImapServer.simplePostAuthenticationDialog(tag: Int) {
+ expect("$tag LIST \"\" \"\"")
+ output("* LIST () \"/\" foo/bar")
+ output("$tag OK")
+ }
+
+ private fun MockImapServer.simpleOpenDialog(postAuthCapabilities: String = "") {
+ simplePreAuthAndLoginDialog(postAuthCapabilities)
+ simplePostAuthenticationDialog(3)
+ }
+
+ private fun MockImapServer.simplePreAuthAndLoginDialog(postAuthCapabilities: String = "") {
+ settings.authType = AuthType.PLAIN
+ preAuthenticationDialog()
+ expect("2 LOGIN \"$USERNAME\" \"$PASSWORD\"")
+ output("2 OK [CAPABILITY $postAuthCapabilities] LOGIN completed")
+ }
+}
+
+class TestTokenProvider : OAuth2TokenProvider {
+ private var invalidationCount = 0
+
+ override fun getToken(timeoutMillis: Long): String {
+ assertThat(timeoutMillis).isEqualTo(OAuth2TokenProvider.OAUTH2_TIMEOUT.toLong())
+
+ return when (invalidationCount) {
+ 0 -> XOAUTH_TOKEN
+ 1 -> XOAUTH_TOKEN_2
+ else -> {
+ throw AuthenticationFailedException(
+ "Ran out of auth tokens. invalidateToken() called too often?"
+ )
+ }
+ }
+ }
+
+ override fun invalidateToken() {
+ invalidationCount++
+ }
+}
+
+private fun String.base64() = this.encodeUtf8().base64()
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt
index 9e5866a50b01b91a9ae307f74ac5ac86a4355a9d..1fea9ae2ca0725c7ecf0a4c97c88b5daf62d548b 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt
@@ -4,7 +4,6 @@ import com.fsck.k9.mail.Body
import com.fsck.k9.mail.DefaultBodyFactory
import com.fsck.k9.mail.FetchProfile
import com.fsck.k9.mail.Flag
-import com.fsck.k9.mail.K9LibRobolectricTestRunner
import com.fsck.k9.mail.MessageRetrievalListener
import com.fsck.k9.mail.MessagingException
import com.fsck.k9.mail.Part
@@ -12,11 +11,14 @@ import com.fsck.k9.mail.internet.BinaryTempFileBody
import com.fsck.k9.mail.internet.MimeHeader
import com.fsck.k9.mail.store.imap.ImapResponseHelper.createImapResponse
import com.google.common.truth.Truth.assertThat
+import java.io.File
import java.io.IOException
+import java.nio.file.Files
import java.util.Date
import java.util.TimeZone
import okio.Buffer
import org.apache.james.mime4j.util.MimeUtil
+import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -25,7 +27,6 @@ import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
-import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anySet
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
@@ -40,9 +41,7 @@ import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-import org.robolectric.RuntimeEnvironment
-@RunWith(K9LibRobolectricTestRunner::class)
class RealImapFolderTest {
private val internalImapStore = object : InternalImapStore {
override val logLabel = "Account"
@@ -52,9 +51,17 @@ class RealImapFolderTest {
private val imapConnection = mock()
private val testConnectionManager = TestConnectionManager(imapConnection)
+ private lateinit var tempDirectory: File
+
@Before
fun setUp() {
- BinaryTempFileBody.setTempDirectory(RuntimeEnvironment.application.cacheDir)
+ tempDirectory = Files.createTempDirectory("RealImapFolderTest").toFile()
+ BinaryTempFileBody.setTempDirectory(tempDirectory)
+ }
+
+ @After
+ fun tearDown() {
+ tempDirectory.deleteRecursively()
}
@Test
@@ -690,7 +697,7 @@ class RealImapFolderTest {
verify(imapConnection).sendCommand(
"UID FETCH 1 (UID INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS " +
- "(date subject from content-type to cc reply-to message-id references in-reply-to " +
+ "(date subject from content-type to cc reply-to message-id references in-reply-to list-unsubscribe " +
"X-K9mail-Identity Chat-Version)])",
false
)
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.java b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.java
index adc69fc5ac60cd7b10ceab5b3195ad5f6f3bda56..ef95cf5810c6c47a80b0bfcb0ae821195f175e1a 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.java
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.java
@@ -12,8 +12,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import android.net.ConnectivityManager;
-
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.FolderType;
@@ -47,10 +45,9 @@ public class RealImapStoreTest {
public void setUp() throws Exception {
ServerSettings serverSettings = createServerSettings();
TrustedSocketFactory trustedSocketFactory = mock(TrustedSocketFactory.class);
- ConnectivityManager connectivityManager = mock(ConnectivityManager.class);
OAuth2TokenProvider oauth2TokenProvider = mock(OAuth2TokenProvider.class);
- imapStore = new TestImapStore(serverSettings, config, trustedSocketFactory, connectivityManager,
+ imapStore = new TestImapStore(serverSettings, config, trustedSocketFactory,
oauth2TokenProvider);
}
@@ -463,9 +460,8 @@ public class RealImapStoreTest {
private String testCombinedPrefix;
public TestImapStore(ServerSettings serverSettings, ImapStoreConfig config,
- TrustedSocketFactory trustedSocketFactory, ConnectivityManager connectivityManager,
- OAuth2TokenProvider oauth2TokenProvider) {
- super(serverSettings, config, trustedSocketFactory, connectivityManager, oauth2TokenProvider);
+ TrustedSocketFactory trustedSocketFactory, OAuth2TokenProvider oauth2TokenProvider) {
+ super(serverSettings, config, trustedSocketFactory, oauth2TokenProvider);
}
@Override
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/SimpleImapSettings.java b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/SimpleImapSettings.java
index e14d09b9d83744409becde679765d8973565ae8e..391ae35d8a696d9efcac15479a224102625ac1ad 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/SimpleImapSettings.java
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/SimpleImapSettings.java
@@ -3,7 +3,6 @@ package com.fsck.k9.mail.store.imap;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
-import com.fsck.k9.mail.NetworkType;
class SimpleImapSettings implements ImapSettings {
@@ -55,7 +54,7 @@ class SimpleImapSettings implements ImapSettings {
}
@Override
- public boolean useCompression(NetworkType type) {
+ public boolean useCompression() {
return useCompression;
}
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java
index c9cf9e5bba1d1b8cee6ecbd2bcaa0a7846b97626..140fa61df75134e2a3ca1ac08bdddbbdaa960e08 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java
@@ -21,8 +21,6 @@ import java.util.concurrent.TimeUnit;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
-import android.annotation.SuppressLint;
-
import com.fsck.k9.mail.helpers.KeyStoreProvider;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
@@ -36,7 +34,6 @@ import okio.Okio;
import org.apache.commons.io.IOUtils;
-@SuppressLint("NewApi")
public class MockImapServer {
private static final byte[] CRLF = { '\r', '\n' };
diff --git a/mail/protocols/pop3/build.gradle b/mail/protocols/pop3/build.gradle
index f7afb0a6ae961fb17955fef64391ce6e7ac4a798..8f750863e6688bb488002885c4327848b3acf70f 100644
--- a/mail/protocols/pop3/build.gradle
+++ b/mail/protocols/pop3/build.gradle
@@ -1,17 +1,18 @@
-apply plugin: 'com.android.library'
+apply plugin: 'java-library'
if (rootProject.testCoverage) {
apply plugin: 'jacoco'
}
+java {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+}
+
dependencies {
api project(":mail:common")
- implementation "com.jakewharton.timber:timber:${versions.timber}"
- implementation "androidx.annotation:annotation:${versions.androidxAnnotation}"
-
testImplementation project(":mail:testing")
- testImplementation "org.robolectric:robolectric:${versions.robolectric}"
testImplementation "junit:junit:${versions.junit}"
testImplementation "com.google.truth:truth:${versions.truth}"
testImplementation "org.mockito:mockito-core:${versions.mockito}"
@@ -19,28 +20,3 @@ dependencies {
testImplementation "com.jcraft:jzlib:1.0.7"
testImplementation "commons-io:commons-io:${versions.commonsIo}"
}
-
-android {
- compileSdkVersion buildConfig.compileSdk
- buildToolsVersion buildConfig.buildTools
-
- defaultConfig {
- minSdkVersion buildConfig.minSdk
- }
-
- buildTypes {
- debug {
- testCoverageEnabled rootProject.testCoverage
- }
- }
-
- lintOptions {
- abortOnError false
- lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
- }
-
- compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
- }
-}
diff --git a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java
index 2a0d004e263cc6a3f4aa26d46414afe3aef3b81c..aaff29d73d0cce5eb4ec85cead0b0dc141ec9a1d 100644
--- a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java
+++ b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java
@@ -17,6 +17,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.Authentication;
import com.fsck.k9.mail.AuthenticationFailedException;
@@ -28,7 +29,6 @@ import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.filter.Hex;
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
import javax.net.ssl.SSLException;
-import timber.log.Timber;
import static com.fsck.k9.mail.CertificateValidationException.Reason.MissingCapability;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_POP3;
diff --git a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Folder.java b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Folder.java
index a5c526e93a846bba4cd5d004b9a68fb3aaeb602e..3271626f906d52139efa21709d667b142004b947 100644
--- a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Folder.java
+++ b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Folder.java
@@ -10,14 +10,12 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import android.annotation.SuppressLint;
-
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.K9MailLib;
import com.fsck.k9.mail.MessageRetrievalListener;
import com.fsck.k9.mail.MessagingException;
-import timber.log.Timber;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_POP3;
import static com.fsck.k9.mail.store.pop3.Pop3Commands.*;
@@ -32,7 +30,6 @@ public class Pop3Folder {
private Pop3Store pop3Store;
private Map uidToMsgMap = new HashMap<>();
- @SuppressLint("UseSparseArrays")
private Map msgNumToMsgMap = new HashMap<>();
private Map uidToMsgNumMap = new HashMap<>();
private String name;
diff --git a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Store.java b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Store.java
index cba76ff046ed2870c0430f1047d81f5dd9967629..4542caaedd44f0122249d6d49e8f4fe5e1db0c68 100644
--- a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Store.java
+++ b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Store.java
@@ -4,13 +4,12 @@ package com.fsck.k9.mail.store.pop3;
import java.util.HashMap;
import java.util.Map;
-import androidx.annotation.NonNull;
-
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
+import org.jetbrains.annotations.NotNull;
public class Pop3Store {
@@ -36,7 +35,7 @@ public class Pop3Store {
authType = serverSettings.authenticationType;
}
- @NonNull
+ @NotNull
public Pop3Folder getFolder(String name) {
Pop3Folder folder = mFolders.get(name);
if (folder == null) {
diff --git a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java
index 0742016b0885735d344a2af3452eb1f0224bf3e2..a5142829db503e851086ef680bc7f3c778a25a77 100644
--- a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java
+++ b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java
@@ -21,8 +21,6 @@ import java.util.concurrent.TimeUnit;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
-import android.annotation.SuppressLint;
-
import com.fsck.k9.mail.helpers.KeyStoreProvider;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
@@ -36,7 +34,6 @@ import okio.Okio;
import org.apache.commons.io.IOUtils;
-@SuppressLint("NewApi")
public class MockPop3Server {
private static final byte[] CRLF = { '\r', '\n' };
diff --git a/mail/protocols/smtp/build.gradle b/mail/protocols/smtp/build.gradle
index d1210c3a8db2c5bf8ee05b9cf3cfb6c82454ff2b..fde2b46a9da4d940da00f0f41307795421238233 100644
--- a/mail/protocols/smtp/build.gradle
+++ b/mail/protocols/smtp/build.gradle
@@ -1,46 +1,25 @@
-apply plugin: 'com.android.library'
+apply plugin: 'java-library'
+apply plugin: 'kotlin'
if (rootProject.testCoverage) {
apply plugin: 'jacoco'
}
+java {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+}
+
dependencies {
api project(":mail:common")
implementation "commons-io:commons-io:${versions.commonsIo}"
- implementation "com.jakewharton.timber:timber:${versions.timber}"
- implementation "androidx.annotation:annotation:${versions.androidxAnnotation}"
+ implementation "com.squareup.okio:okio:${versions.okio}"
testImplementation project(":mail:testing")
- testImplementation "org.robolectric:robolectric:${versions.robolectric}"
testImplementation "junit:junit:${versions.junit}"
testImplementation "com.google.truth:truth:${versions.truth}"
- testImplementation "org.mockito:mockito-core:${versions.mockito}"
+ testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}"
testImplementation "com.squareup.okio:okio:${versions.okio}"
testImplementation "com.jcraft:jzlib:1.0.7"
}
-
-android {
- compileSdkVersion buildConfig.compileSdk
- buildToolsVersion buildConfig.buildTools
-
- defaultConfig {
- minSdkVersion buildConfig.minSdk
- }
-
- buildTypes {
- debug {
- testCoverageEnabled rootProject.testCoverage
- }
- }
-
- lintOptions {
- abortOnError false
- lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
- }
-
- compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
- }
-}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/EnhancedNegativeSmtpReplyException.java b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/EnhancedNegativeSmtpReplyException.java
deleted file mode 100644
index 7be1e11f739864398525c19de5226e2d8e4c1c5f..0000000000000000000000000000000000000000
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/EnhancedNegativeSmtpReplyException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.fsck.k9.mail.transport.smtp;
-
-
-class EnhancedNegativeSmtpReplyException extends NegativeSmtpReplyException {
- private final StatusCodeClass statusCodeClass;
- private final StatusCodeSubject statusCodeSubject;
- private final StatusCodeDetail statusCodeDetail;
-
-
- EnhancedNegativeSmtpReplyException(int replyCode, StatusCodeClass statusCodeClass,
- StatusCodeSubject statusCodeSubject, StatusCodeDetail statusCodeDetail,
- String replyText) {
- super(replyCode, replyText);
- this.statusCodeClass = statusCodeClass;
- this.statusCodeSubject = statusCodeSubject;
- this.statusCodeDetail = statusCodeDetail;
- }
-}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/EnhancedStatusCode.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/EnhancedStatusCode.kt
new file mode 100644
index 0000000000000000000000000000000000000000..34718d335da58030514bcc43a86f67c2296e7dfb
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/EnhancedStatusCode.kt
@@ -0,0 +1,7 @@
+package com.fsck.k9.mail.transport.smtp
+
+data class EnhancedStatusCode(
+ val statusClass: StatusCodeClass,
+ val subject: Int,
+ val detail: Int
+)
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/NegativeSmtpReplyException.java b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/NegativeSmtpReplyException.java
deleted file mode 100644
index 4a822030fbbdfd4f7de757b3eaa768d9e26aaa8c..0000000000000000000000000000000000000000
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/NegativeSmtpReplyException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.fsck.k9.mail.transport.smtp;
-
-
-import android.text.TextUtils;
-
-import com.fsck.k9.mail.MessagingException;
-
-
-/**
- * Exception that is thrown when the server sends a negative reply (reply codes 4xx or 5xx).
- */
-class NegativeSmtpReplyException extends MessagingException {
- private static final long serialVersionUID = 8696043577357897135L;
-
-
- private final int replyCode;
- private final String replyText;
-
-
- public NegativeSmtpReplyException(int replyCode, String replyText) {
- super(buildErrorMessage(replyCode, replyText), isPermanentSmtpError(replyCode));
- this.replyCode = replyCode;
- this.replyText = replyText;
- }
-
- private static String buildErrorMessage(int replyCode, String replyText) {
- return TextUtils.isEmpty(replyText) ? "Negative SMTP reply: " + replyCode : replyText;
- }
-
- private static boolean isPermanentSmtpError(int replyCode) {
- return replyCode >= 500 && replyCode <= 599;
- }
-
- public int getReplyCode() {
- return replyCode;
- }
-
- public String getReplyText() {
- return replyText;
- }
-}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/NegativeSmtpReplyException.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/NegativeSmtpReplyException.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8d430ab7e61ba3fe661abfcb63bb6d0c6984028e
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/NegativeSmtpReplyException.kt
@@ -0,0 +1,23 @@
+package com.fsck.k9.mail.transport.smtp
+
+import com.fsck.k9.mail.MessagingException
+
+/**
+ * Exception that is thrown when the server sends a negative reply (reply codes 4xx or 5xx).
+ */
+open class NegativeSmtpReplyException(
+ val replyCode: Int,
+ val replyText: String,
+ val enhancedStatusCode: EnhancedStatusCode? = null
+) : MessagingException(
+ buildErrorMessage(replyCode, replyText),
+ isPermanentSmtpError(replyCode)
+)
+
+private fun buildErrorMessage(replyCode: Int, replyText: String): String {
+ return replyText.ifEmpty { "Negative SMTP reply: $replyCode" }
+}
+
+private fun isPermanentSmtpError(replyCode: Int): Boolean {
+ return replyCode in 500..599
+}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpHelloResponse.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpHelloResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..559acdbaae7527d2b793782f4593aa0a5e7cf5de
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpHelloResponse.kt
@@ -0,0 +1,8 @@
+package com.fsck.k9.mail.transport.smtp
+
+internal sealed interface SmtpHelloResponse {
+ val response: SmtpResponse
+
+ data class Error(override val response: SmtpResponse) : SmtpHelloResponse
+ data class Hello(override val response: SmtpResponse, val keywords: Map>) : SmtpHelloResponse
+}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpLogger.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpLogger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b442c49da933020ab0be87600de3e9f6dffe76bd
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpLogger.kt
@@ -0,0 +1,9 @@
+package com.fsck.k9.mail.transport.smtp
+
+interface SmtpLogger {
+ val isRawProtocolLoggingEnabled: Boolean
+
+ fun log(message: String, vararg args: Any?) = log(throwable = null, message, *args)
+
+ fun log(throwable: Throwable?, message: String, vararg args: Any?)
+}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponse.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4fd8bfaeae54477e4562f240c1d9af5bf7f6034e
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponse.kt
@@ -0,0 +1,59 @@
+package com.fsck.k9.mail.transport.smtp
+
+internal data class SmtpResponse(
+ val replyCode: Int,
+ val enhancedStatusCode: EnhancedStatusCode?,
+ val texts: List
+) {
+ val isNegativeResponse = replyCode >= 400
+
+ val joinedText: String
+ get() = texts.joinToString(separator = " ")
+
+ fun toLogString(omitText: Boolean, linePrefix: String): String {
+ return buildString {
+ if (omitText) {
+ append(linePrefix)
+ append(replyCode)
+ appendIfNotNull(enhancedStatusCode, prefix = ' ')
+ if (texts.isNotEmpty()) {
+ append(" [omitted]")
+ }
+ } else {
+ if (texts.size > 1) {
+ for (i in 0 until texts.lastIndex) {
+ append(linePrefix)
+ append(replyCode)
+ if (enhancedStatusCode == null) {
+ append('-')
+ } else {
+ appendIfNotNull(enhancedStatusCode, prefix = '-')
+ append(' ')
+ }
+ append(texts[i])
+ appendLine()
+ }
+ }
+
+ append(linePrefix)
+ append(replyCode)
+ appendIfNotNull(enhancedStatusCode, prefix = ' ')
+ if (texts.isNotEmpty()) {
+ append(' ')
+ append(texts.last())
+ }
+ }
+ }
+ }
+
+ private fun StringBuilder.appendIfNotNull(enhancedStatusCode: EnhancedStatusCode?, prefix: Char) {
+ if (enhancedStatusCode != null) {
+ append(prefix)
+ append(enhancedStatusCode.statusClass.codeClass)
+ append('.')
+ append(enhancedStatusCode.subject)
+ append('.')
+ append(enhancedStatusCode.detail)
+ }
+ }
+}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParser.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParser.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a7ceefaa10115066087d50416c7d7bf81af14a30
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParser.kt
@@ -0,0 +1,416 @@
+package com.fsck.k9.mail.transport.smtp
+
+import com.fsck.k9.mail.filter.PeekableInputStream
+import okio.Buffer
+import okio.BufferedSource
+
+private const val CR = '\r'
+private const val LF = '\n'
+private const val SPACE = ' '
+private const val DASH = '-'
+private const val HTAB = '\t'
+private const val DOT = '.'
+private const val END_OF_STREAM = -1
+
+/**
+ * Parser for SMTP response lines.
+ *
+ * Supports enhanced status codes as defined in RFC 2034.
+ *
+ * Unfortunately at least one popular implementation doesn't always send an enhanced status code even though its EHLO
+ * response contains the ENHANCEDSTATUSCODES keyword. Begrudgingly, we allow this and other minor standard violations.
+ * However, we output a log message when such a case is encountered.
+ */
+internal class SmtpResponseParser(
+ private val logger: SmtpLogger,
+ private val input: PeekableInputStream
+) {
+ private val logBuffer = Buffer()
+
+ fun readGreeting(): SmtpResponse {
+ // We're not interested in the domain or address literal in the greeting. So we use the standard parser.
+ return readResponse(enhancedStatusCodes = false)
+ }
+
+ fun readHelloResponse(): SmtpHelloResponse {
+ val replyCode = readReplyCode()
+
+ if (replyCode != 250) {
+ val response = readResponseAfterReplyCode(replyCode, enhancedStatusCodes = false)
+ return SmtpHelloResponse.Error(response)
+ }
+
+ val texts = mutableListOf()
+
+ // Read first line containing 'domain' and maybe 'ehlo-greet' (we don't check the syntax and allow any text)
+ when (val char = peekChar()) {
+ SPACE -> {
+ expect(SPACE)
+
+ val text = readUntilEndOfLine().readUtf8()
+
+ expect(CR)
+ expect(LF)
+
+ return SmtpHelloResponse.Hello(
+ response = SmtpResponse(replyCode, enhancedStatusCode = null, texts = listOf(text)),
+ keywords = emptyMap()
+ )
+ }
+ DASH -> {
+ expect(DASH)
+
+ val text = readUntilEndOfLine().readUtf8()
+ texts.add(text)
+
+ expect(CR)
+ expect(LF)
+ }
+ else -> unexpectedCharacterError(char)
+ }
+
+ val keywords = mutableMapOf>()
+
+ // Read EHLO keywords and parameters
+ while (true) {
+ val currentReplyCode = readReplyCode()
+ if (currentReplyCode != replyCode) {
+ parserError("Multi-line response with reply codes not matching: $replyCode != $currentReplyCode")
+ }
+
+ when (val char = peekChar()) {
+ SPACE -> {
+ expect(SPACE)
+
+ val bufferedSource = readUntilEndOfLine()
+ val ehloLine = bufferedSource.readEhloLine()
+ texts.add(ehloLine)
+
+ parseEhloLine(ehloLine, keywords)
+
+ expect(CR)
+ expect(LF)
+
+ return SmtpHelloResponse.Hello(
+ response = SmtpResponse(replyCode, enhancedStatusCode = null, texts),
+ keywords = keywords
+ )
+ }
+ DASH -> {
+ expect(DASH)
+
+ val bufferedSource = readUntilEndOfLine()
+ val ehloLine = bufferedSource.readEhloLine()
+ texts.add(ehloLine)
+
+ parseEhloLine(ehloLine, keywords)
+
+ expect(CR)
+ expect(LF)
+ }
+ else -> unexpectedCharacterError(char)
+ }
+ }
+ }
+
+ private fun parseEhloLine(ehloLine: String, keywords: MutableMap>) {
+ val parts = ehloLine.split(" ")
+
+ try {
+ val keyword = checkAndNormalizeEhloKeyword(parts[0])
+ val parameters = checkEhloParameters(parts)
+
+ if (keywords.containsKey(keyword)) {
+ parserError("Same EHLO keyword present in more than one response line", logging = false)
+ }
+
+ keywords[keyword] = parameters
+ } catch (e: SmtpResponseParserException) {
+ logger.log(e, "Ignoring EHLO keyword line: $ehloLine")
+ }
+ }
+
+ private fun checkAndNormalizeEhloKeyword(text: String): String {
+ val keyword = text.uppercase()
+ if (!keyword[0].isCapitalAlphaDigit() || keyword.any { !it.isCapitalAlphaDigit() && it != DASH }) {
+ parserError("EHLO keyword contains invalid character", logging = false)
+ }
+
+ return keyword
+ }
+
+ private fun checkEhloParameters(parts: List): List {
+ for (i in 1..parts.lastIndex) {
+ val parameter = parts[i]
+ if (parameter.isEmpty()) {
+ parserError("EHLO parameter must not be empty", logging = false)
+ } else if (parameter.any { it.code !in 33..126 }) {
+ parserError("EHLO parameter contains invalid character", logging = false)
+ }
+ }
+
+ return parts.drop(1)
+ }
+
+ fun readResponse(enhancedStatusCodes: Boolean): SmtpResponse {
+ logBuffer.clear()
+
+ val replyCode = readReplyCode()
+ return readResponseAfterReplyCode(replyCode, enhancedStatusCodes)
+ }
+
+ private fun readResponseAfterReplyCode(replyCode: Int, enhancedStatusCodes: Boolean): SmtpResponse {
+ val texts = mutableListOf()
+ var enhancedStatusCode: EnhancedStatusCode? = null
+ var isFirstLine = true
+
+ fun BufferedSource.maybeReadAndCompareEnhancedStatusCode(replyCode: Int): EnhancedStatusCode? {
+ val currentStatusCode = maybeReadEnhancedStatusCode(replyCode)
+ if (!isFirstLine && enhancedStatusCode != currentStatusCode) {
+ parserError(
+ "Multi-line response with enhanced status codes not matching: " +
+ "$enhancedStatusCode != $currentStatusCode"
+ )
+ }
+ isFirstLine = false
+
+ return currentStatusCode
+ }
+
+ while (true) {
+ when (val char = peekChar()) {
+ CR -> {
+ expect(CR)
+ expect(LF)
+
+ return SmtpResponse(replyCode, enhancedStatusCode, texts)
+ }
+ SPACE -> {
+ expect(SPACE)
+
+ val bufferedSource = readUntilEndOfLine()
+
+ if (enhancedStatusCodes) {
+ enhancedStatusCode = bufferedSource.maybeReadAndCompareEnhancedStatusCode(replyCode)
+ }
+
+ val textString = bufferedSource.readTextString()
+ if (textString.isNotEmpty()) {
+ texts.add(textString)
+ }
+
+ expect(CR)
+ expect(LF)
+
+ return SmtpResponse(replyCode, enhancedStatusCode, texts)
+ }
+ DASH -> {
+ expect(DASH)
+
+ val bufferedSource = readUntilEndOfLine()
+
+ if (enhancedStatusCodes) {
+ enhancedStatusCode = bufferedSource.maybeReadAndCompareEnhancedStatusCode(replyCode)
+ }
+
+ val textString = bufferedSource.readTextString()
+ texts.add(textString)
+
+ expect(CR)
+ expect(LF)
+
+ val currentReplyCode = readReplyCode()
+ if (currentReplyCode != replyCode) {
+ parserError(
+ "Multi-line response with reply codes not matching: $replyCode != $currentReplyCode"
+ )
+ }
+ }
+ else -> unexpectedCharacterError(char)
+ }
+ }
+ }
+
+ private fun readReplyCode(): Int {
+ return readReplyCode1() * 100 + readReplyCode2() * 10 + readReplyCode3()
+ }
+
+ private fun readReplyCode1(): Int {
+ val replyCode1 = readDigit()
+ if (replyCode1 !in 2..5) parserError("Unsupported 1st reply code digit: $replyCode1")
+
+ return replyCode1
+ }
+
+ private fun readReplyCode2(): Int {
+ val replyCode2 = readDigit()
+ if (replyCode2 !in 0..5) {
+ logger.log("2nd digit of reply code outside of specified range (0..5): %d", replyCode2)
+ }
+
+ return replyCode2
+ }
+
+ private fun readReplyCode3(): Int {
+ return readDigit()
+ }
+
+ private fun readDigit(): Int {
+ val char = readChar()
+ if (char !in '0'..'9') unexpectedCharacterError(char)
+
+ return char - '0'
+ }
+
+ private fun expect(expectedChar: Char) {
+ val char = readChar()
+ if (char != expectedChar) unexpectedCharacterError(char)
+ }
+
+ private fun readByte(): Int {
+ return input.read()
+ .also {
+ throwIfEndOfStreamReached(it)
+ logBuffer.writeByte(it)
+ }
+ }
+
+ private fun readChar(): Char {
+ return readByte().toChar()
+ }
+
+ private fun peekChar(): Char {
+ return input.peek()
+ .also { throwIfEndOfStreamReached(it) }
+ .toChar()
+ }
+
+ private fun throwIfEndOfStreamReached(data: Int) {
+ if (data == END_OF_STREAM) parserError("Unexpected end of stream")
+ }
+
+ private fun readUntilEndOfLine(): BufferedSource {
+ val buffer = Buffer()
+
+ while (peekChar() != CR) {
+ val byte = readByte()
+ buffer.writeByte(byte)
+ }
+
+ return buffer
+ }
+
+ private fun BufferedSource.readEhloLine(): String {
+ val text = readUtf8()
+ if (text.isEmpty()) {
+ parserError("EHLO line must not be empty")
+ }
+
+ return text
+ }
+
+ private fun BufferedSource.readTextString(): String {
+ val text = readUtf8()
+ if (text.isEmpty()) {
+ logger.log("'textstring' expected, but CR found instead")
+ } else if (text.any { it != HTAB && it.code !in 32..126 }) {
+ logger.log("Text contains characters not allowed in 'textstring'")
+ }
+
+ return text
+ }
+
+ private fun BufferedSource.maybeReadEnhancedStatusCode(replyCode: Int): EnhancedStatusCode? {
+ val replyCode1 = replyCode / 100
+ if (replyCode1 != 2 && replyCode1 != 4 && replyCode1 != 5) return null
+
+ return try {
+ val peekBufferedSource = peek()
+ val statusCode = peekBufferedSource.readEnhancedStatusCode(replyCode1)
+
+ val statusCodeLength = buffer.size - peekBufferedSource.buffer.size
+ skip(statusCodeLength)
+
+ statusCode
+ } catch (e: SmtpResponseParserException) {
+ logger.log(e, "Error parsing enhanced status code")
+ null
+ }
+ }
+
+ private fun BufferedSource.readEnhancedStatusCode(replyCode1: Int): EnhancedStatusCode {
+ val statusClass = readStatusCodeClass(replyCode1)
+ expect(DOT)
+ val subject = readOneToThreeDigitNumber()
+ expect(DOT)
+ val detail = readOneToThreeDigitNumber()
+
+ expect(SPACE)
+
+ return EnhancedStatusCode(statusClass, subject, detail)
+ }
+
+ private fun BufferedSource.readStatusCodeClass(replyCode1: Int): StatusCodeClass {
+ val char = readChar()
+ val statusClass = when (char) {
+ '2' -> StatusCodeClass.SUCCESS
+ '4' -> StatusCodeClass.PERSISTENT_TRANSIENT_FAILURE
+ '5' -> StatusCodeClass.PERMANENT_FAILURE
+ else -> unexpectedCharacterError(char, logging = false)
+ }
+
+ if (char != replyCode1.digitToChar()) {
+ parserError("Reply code doesn't match status code class: $replyCode1 != $char", logging = false)
+ }
+
+ return statusClass
+ }
+
+ private fun BufferedSource.readOneToThreeDigitNumber(): Int {
+ var number = readDigit()
+ repeat(2) {
+ if (peek().readChar() in '0'..'9') {
+ number *= 10
+ number += readDigit()
+ }
+ }
+
+ return number
+ }
+
+ private fun BufferedSource.readDigit(): Int {
+ val char = readChar()
+ if (char !in '0'..'9') unexpectedCharacterError(char, logging = false)
+
+ return char - '0'
+ }
+
+ private fun BufferedSource.readChar(): Char {
+ if (exhausted()) parserError("Unexpected end of stream", logging = false)
+
+ return readByte().toInt().toChar()
+ }
+
+ private fun BufferedSource.expect(expectedChar: Char) {
+ val char = readChar()
+ if (char != expectedChar) unexpectedCharacterError(char, logging = false)
+ }
+
+ private fun unexpectedCharacterError(char: Char, logging: Boolean = true): Nothing {
+ if (char.code in 33..126) {
+ parserError("Unexpected character: $char (${char.code})", logging)
+ } else {
+ parserError("Unexpected character: (${char.code})", logging)
+ }
+ }
+
+ private fun parserError(message: String, logging: Boolean = true): Nothing {
+ if (logging && logger.isRawProtocolLoggingEnabled) {
+ logger.log("SMTP response data on parser error:\n%s", logBuffer.readUtf8().replace("\r\n", "\n"))
+ }
+
+ throw SmtpResponseParserException(message)
+ }
+
+ private fun Char.isCapitalAlphaDigit(): Boolean = this in '0'..'9' || this in 'A'..'Z'
+}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserException.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserException.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e5ff7572d954ff9f8568f6f3181d66d26d15ebc8
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserException.kt
@@ -0,0 +1,3 @@
+package com.fsck.k9.mail.transport.smtp
+
+class SmtpResponseParserException(message: String) : RuntimeException(message)
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.java b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.java
deleted file mode 100644
index 0019198347824835c5dc611b0debfcd9614ef582..0000000000000000000000000000000000000000
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.java
+++ /dev/null
@@ -1,791 +0,0 @@
-
-package com.fsck.k9.mail.transport.smtp;
-
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.security.GeneralSecurityException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
-
-import android.text.TextUtils;
-
-import com.fsck.k9.mail.Address;
-import com.fsck.k9.mail.AuthType;
-import com.fsck.k9.mail.Authentication;
-import com.fsck.k9.mail.AuthenticationFailedException;
-import com.fsck.k9.mail.CertificateValidationException;
-import com.fsck.k9.mail.ConnectionSecurity;
-import com.fsck.k9.mail.K9MailLib;
-import com.fsck.k9.mail.Message;
-import com.fsck.k9.mail.Message.RecipientType;
-import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.ServerSettings;
-import com.fsck.k9.mail.Transport;
-import com.fsck.k9.mail.filter.Base64;
-import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
-import com.fsck.k9.mail.filter.LineWrapOutputStream;
-import com.fsck.k9.mail.filter.PeekableInputStream;
-import com.fsck.k9.mail.filter.SmtpDataStuffing;
-import com.fsck.k9.mail.oauth.OAuth2TokenProvider;
-import com.fsck.k9.mail.oauth.XOAuth2ChallengeParser;
-import com.fsck.k9.mail.ssl.TrustedSocketFactory;
-import javax.net.ssl.SSLException;
-import org.apache.commons.io.IOUtils;
-import timber.log.Timber;
-
-import static com.fsck.k9.mail.CertificateValidationException.Reason.MissingCapability;
-import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_SMTP;
-
-public class SmtpTransport extends Transport {
- private static final int SMTP_CONTINUE_REQUEST = 334;
- private static final int SMTP_AUTHENTICATION_FAILURE_ERROR_CODE = 535;
-
-
- private final TrustedSocketFactory trustedSocketFactory;
- private final OAuth2TokenProvider oauthTokenProvider;
-
- private final String host;
- private final int port;
- private final String username;
- private final String password;
- private final String clientCertificateAlias;
- private final AuthType authType;
- private final ConnectionSecurity connectionSecurity;
-
-
- private Socket socket;
- private PeekableInputStream inputStream;
- private OutputStream outputStream;
- private boolean is8bitEncodingAllowed;
- private boolean isEnhancedStatusCodesProvided;
- private int largestAcceptableMessage;
- private boolean retryXoauthWithNewToken;
- private boolean isPipeliningSupported;
-
-
- public SmtpTransport(ServerSettings serverSettings,
- TrustedSocketFactory trustedSocketFactory, OAuth2TokenProvider oauthTokenProvider) {
- if (!serverSettings.type.equals("smtp")) {
- throw new IllegalArgumentException("Expected SMTP StoreConfig!");
- }
-
- host = serverSettings.host;
- port = serverSettings.port;
-
- connectionSecurity = serverSettings.connectionSecurity;
-
- authType = serverSettings.authenticationType;
- username = serverSettings.username;
- password = serverSettings.password;
- clientCertificateAlias = serverSettings.clientCertificateAlias;
-
- this.trustedSocketFactory = trustedSocketFactory;
- this.oauthTokenProvider = oauthTokenProvider;
- }
-
- @Override
- public void open() throws MessagingException {
- try {
- boolean secureConnection = false;
- InetAddress[] addresses = InetAddress.getAllByName(host);
- for (int i = 0; i < addresses.length; i++) {
- try {
- SocketAddress socketAddress = new InetSocketAddress(addresses[i], port);
- if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
- socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias);
- socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
- secureConnection = true;
- } else {
- socket = new Socket();
- socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
- }
- } catch (SocketException e) {
- if (i < (addresses.length - 1)) {
- // there are still other addresses for that host to try
- continue;
- }
- throw new MessagingException("Cannot connect to host", e);
- }
- break; // connection success
- }
-
- // RFC 1047
- socket.setSoTimeout(SOCKET_READ_TIMEOUT);
-
- inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024));
- outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024);
-
- // Eat the banner
- executeCommand(null);
-
- String hostnameToReportInHelo = buildHostnameToReport();
-
- Map extensions = sendHello(hostnameToReportInHelo);
-
- is8bitEncodingAllowed = extensions.containsKey("8BITMIME");
- isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES");
- isPipeliningSupported = extensions.containsKey("PIPELINING");
-
- if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) {
- if (extensions.containsKey("STARTTLS")) {
- executeCommand("STARTTLS");
-
- socket = trustedSocketFactory.createSocket(
- socket,
- host,
- port,
- clientCertificateAlias);
-
- inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(),
- 1024));
- outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024);
- /*
- * Now resend the EHLO. Required by RFC2487 Sec. 5.2, and more specifically,
- * Exim.
- */
- extensions = sendHello(hostnameToReportInHelo);
- 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");
- }
- }
-
- boolean authLoginSupported = false;
- boolean authPlainSupported = false;
- boolean authCramMD5Supported = false;
- boolean authExternalSupported = false;
- boolean authXoauth2Supported = false;
- if (extensions.containsKey("AUTH")) {
- List saslMech = Arrays.asList(extensions.get("AUTH").split(" "));
- authLoginSupported = saslMech.contains("LOGIN");
- authPlainSupported = saslMech.contains("PLAIN");
- authCramMD5Supported = saslMech.contains("CRAM-MD5");
- authExternalSupported = saslMech.contains("EXTERNAL");
- authXoauth2Supported = saslMech.contains("XOAUTH2");
- }
- parseOptionalSizeValue(extensions);
-
- if (!TextUtils.isEmpty(username)
- && (!TextUtils.isEmpty(password) ||
- AuthType.EXTERNAL == authType ||
- AuthType.XOAUTH2 == authType)) {
-
- switch (authType) {
-
- /*
- * 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();
- } else if (authLoginSupported) {
- saslAuthLogin();
- } else {
- throw new MessagingException(
- "Authentication methods SASL PLAIN and LOGIN are unavailable.");
- }
- break;
-
- case CRAM_MD5:
- if (authCramMD5Supported) {
- saslAuthCramMD5();
- } else {
- throw new MessagingException("Authentication method CRAM-MD5 is unavailable.");
- }
- break;
- case XOAUTH2:
- if (authXoauth2Supported && oauthTokenProvider != null) {
- saslXoauth2();
- } else {
- throw new MessagingException("Authentication method XOAUTH2 is unavailable.");
- }
- break;
- case EXTERNAL:
- if (authExternalSupported) {
- saslAuthExternal();
- } else {
- /*
- * Some SMTP servers are known to provide no error
- * indication when a client certificate fails to
- * validate, other than to not offer the AUTH EXTERNAL
- * capability.
- *
- * So, we treat it is an error to not offer AUTH
- * EXTERNAL when using client certificates. That way, the
- * user can be notified of a problem during account setup.
- */
- throw new CertificateValidationException(MissingCapability);
- }
- 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();
- } else if (authLoginSupported) {
- saslAuthLogin();
- } else if (authCramMD5Supported) {
- saslAuthCramMD5();
- } else {
- throw new MessagingException("No supported authentication methods available.");
- }
- } else {
- if (authCramMD5Supported) {
- saslAuthCramMD5();
- } 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.");
- }
- }
- break;
-
- default:
- throw new MessagingException(
- "Unhandled authentication method found in the server settings (bug).");
- }
- }
- } catch (MessagingException e) {
- close();
- throw e;
- } catch (SSLException e) {
- close();
- throw new CertificateValidationException(e.getMessage(), e);
- } catch (GeneralSecurityException gse) {
- close();
- throw new MessagingException(
- "Unable to open connection to SMTP server due to security error.", gse);
- } catch (IOException ioe) {
- close();
- throw new MessagingException("Unable to open connection to SMTP server.", ioe);
- }
- }
-
- private String buildHostnameToReport() {
- InetAddress localAddress = socket.getLocalAddress();
-
- // we use local ip statically for privacy reasons, see https://github.com/k9mail/k-9/pull/3798
- if (localAddress instanceof Inet6Address) {
- return "[IPv6:::1]";
- } else {
- return "[127.0.0.1]";
- }
- }
-
- private void parseOptionalSizeValue(Map extensions) {
- if (extensions.containsKey("SIZE")) {
- String optionalsizeValue = extensions.get("SIZE");
- if (optionalsizeValue != null && !"".equals(optionalsizeValue)) {
- try {
- largestAcceptableMessage = Integer.parseInt(optionalsizeValue);
- } catch (NumberFormatException e) {
- if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP) {
- Timber.d(e, "Tried to parse %s and get an int", optionalsizeValue);
- }
- }
- }
- }
- }
-
- /**
- * Send the client "identity" using the EHLO or HELO command.
- *
- *
- * We first try the EHLO command. If the server sends a negative response, it probably doesn't
- * support the EHLO command. So we try the older HELO command that all servers need to support.
- * And if that fails, too, we pretend everything is fine and continue unimpressed.
- *
- *
- * @param host
- * The EHLO/HELO parameter as defined by the RFC.
- *
- * @return A (possibly empty) {@code Map} 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 Map sendHello(String host) throws IOException, MessagingException {
- Map extensions = new HashMap<>();
- try {
- List results = executeCommand("EHLO %s", host).results;
- // 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 (K9MailLib.isDebug()) {
- Timber.v("Server doesn't support the EHLO command. Trying HELO...");
- }
-
- try {
- executeCommand("HELO %s", host);
- } catch (NegativeSmtpReplyException e2) {
- Timber.w("Server doesn't support the HELO command. Continuing anyway.");
- }
- }
- return extensions;
- }
-
- @Override
- public void sendMessage(Message message) throws MessagingException {
- Set addresses = new LinkedHashSet<>();
- for (Address address : message.getRecipients(RecipientType.TO)) {
- addresses.add(address.getAddress());
- }
- for (Address address : message.getRecipients(RecipientType.CC)) {
- addresses.add(address.getAddress());
- }
- for (Address address : message.getRecipients(RecipientType.BCC)) {
- addresses.add(address.getAddress());
- }
- message.removeHeader("Bcc");
-
- if (addresses.isEmpty()) {
- return;
- }
-
- close();
- open();
-
- // If the message has attachments and our server has told us about a limit on
- // the size of messages, count the message's size before sending it
- if (largestAcceptableMessage > 0 && message.hasAttachments()) {
- if (message.calculateSize() > largestAcceptableMessage) {
- throw new MessagingException("Message too large for server", true);
- }
- }
-
- boolean entireMessageSent = false;
-
- try {
- String mailFrom = constructSmtpMailFromCommand(message.getFrom(), is8bitEncodingAllowed);
-
- if (isPipeliningSupported) {
- Queue pipelinedCommands = new LinkedList<>();
- pipelinedCommands.add(mailFrom);
-
- for (String address : addresses) {
- pipelinedCommands.add(String.format("RCPT TO:<%s>", address));
- }
-
- executePipelinedCommands(pipelinedCommands);
- readPipelinedResponse(pipelinedCommands);
- } else {
- executeCommand(mailFrom);
-
- for (String address : addresses) {
- executeCommand("RCPT TO:<%s>", address);
- }
- }
-
- executeCommand("DATA");
-
- EOLConvertingOutputStream msgOut = new EOLConvertingOutputStream(
- new LineWrapOutputStream(new SmtpDataStuffing(outputStream), 1000));
-
- message.writeTo(msgOut);
- msgOut.endWithCrLfAndFlush();
-
- entireMessageSent = true; // After the "\r\n." is attempted, we may have sent the message
- executeCommand(".");
- } catch (NegativeSmtpReplyException e) {
- throw e;
- } catch (Exception e) {
- throw new MessagingException("Unable to send message", entireMessageSent, e);
- } finally {
- close();
- }
-
- }
-
- private static String constructSmtpMailFromCommand(Address[] from, boolean is8bitEncodingAllowed) {
- String fromAddress = from[0].getAddress();
- if (is8bitEncodingAllowed) {
- return String.format("MAIL FROM:<%s> BODY=8BITMIME", fromAddress);
- } else {
- Timber.d("Server does not support 8bit transfer encoding");
- return String.format("MAIL FROM:<%s>", fromAddress);
- }
- }
-
- @Override
- public void close() {
- try {
- executeCommand("QUIT");
- } catch (Exception e) {
- // don't care
- }
- IOUtils.closeQuietly(inputStream);
- IOUtils.closeQuietly(outputStream);
- IOUtils.closeQuietly(socket);
- inputStream = null;
- outputStream = null;
- socket = null;
- }
-
- private String readLine() throws IOException {
- StringBuilder sb = new StringBuilder();
- int d;
- while ((d = inputStream.read()) != -1) {
- char c = (char) d;
- if (c == '\n') {
- break;
- } else if (c != '\r') {
- sb.append(c);
- }
- }
- String ret = sb.toString();
- if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP)
- Timber.d("SMTP <<< %s", ret);
-
- return ret;
- }
-
- private void writeLine(String s, boolean sensitive) throws IOException {
- if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP) {
- final String commandToLog;
- if (sensitive && !K9MailLib.isDebugSensitive()) {
- commandToLog = "SMTP >>> *sensitive*";
- } else {
- commandToLog = "SMTP >>> " + s;
- }
- Timber.d(commandToLog);
- }
-
- byte[] data = s.concat("\r\n").getBytes();
-
- /*
- * Important: Send command + CRLF using just one write() call. Using
- * multiple calls will likely result in multiple TCP packets and some
- * SMTP servers misbehave if CR and LF arrive in separate pakets.
- * See issue 799.
- */
- outputStream.write(data);
- outputStream.flush();
- }
-
- private static class CommandResponse {
-
- private final int replyCode;
- private final List results;
-
- CommandResponse(int replyCode, List results) {
- this.replyCode = replyCode;
- this.results = results;
- }
- }
-
- private CommandResponse executeSensitiveCommand(String format, Object... args)
- throws IOException, MessagingException {
- return executeCommand(true, format, args);
- }
-
- private CommandResponse executeCommand(String format, Object... args) throws IOException, MessagingException {
- return executeCommand(false, format, args);
- }
-
- private CommandResponse executeCommand(boolean sensitive, String format, Object... args)
- throws IOException, MessagingException {
- List results = new ArrayList<>();
- if (format != null) {
- String command = String.format(Locale.ROOT, format, args);
- writeLine(command, sensitive);
- }
-
- String line = readCommandResponseLine(results);
-
- int length = line.length();
- if (length < 1) {
- throw new MessagingException("SMTP response is 0 length");
- }
-
- int replyCode = -1;
- if (length >= 3) {
- try {
- replyCode = Integer.parseInt(line.substring(0, 3));
- } catch (NumberFormatException e) { /* ignore */ }
- }
-
- char replyCodeCategory = line.charAt(0);
- boolean isReplyCodeErrorCategory = (replyCodeCategory == '4') || (replyCodeCategory == '5');
- if (isReplyCodeErrorCategory) {
- if (isEnhancedStatusCodesProvided) {
- throw buildEnhancedNegativeSmtpReplyException(replyCode, results);
- } else {
- String replyText = TextUtils.join(" ", results);
- throw new NegativeSmtpReplyException(replyCode, replyText);
- }
- }
-
- return new CommandResponse(replyCode, results);
- }
-
- private MessagingException buildEnhancedNegativeSmtpReplyException(int replyCode, List results) {
- StatusCodeClass statusCodeClass = null;
- StatusCodeSubject statusCodeSubject = null;
- StatusCodeDetail statusCodeDetail = null;
-
- String message = "";
- for (String resultLine : results) {
- message += resultLine.split(" ", 2)[1] + " ";
- }
- if (results.size() > 0) {
- String[] statusCodeParts = results.get(0).split(" ", 2)[0].split("\\.");
-
- statusCodeClass = StatusCodeClass.parse(statusCodeParts[0]);
- statusCodeSubject = StatusCodeSubject.parse(statusCodeParts[1]);
- statusCodeDetail = StatusCodeDetail.parse(statusCodeSubject, statusCodeParts[2]);
- }
-
- return new EnhancedNegativeSmtpReplyException(replyCode, statusCodeClass, statusCodeSubject, statusCodeDetail,
- message.trim());
- }
-
-
- /*
- * Read lines as long as the length is 4 or larger, e.g. "220-banner text here".
- * Shorter lines are either errors of contain only a reply code.
- */
- private String readCommandResponseLine(List results) throws IOException {
- String line = readLine();
- while (line.length() >= 4) {
- if (line.length() > 4) {
- // Everything after the first four characters goes into the results array.
- results.add(line.substring(4));
- }
-
- if (line.charAt(3) != '-') {
- // If the fourth character isn't "-" this is the last line of the response.
- break;
- }
- line = readLine();
- }
- return line;
- }
-
- private void executePipelinedCommands(Queue pipelinedCommands) throws IOException {
- for (String command : pipelinedCommands) {
- writeLine(command, false);
- }
- }
-
- private void readPipelinedResponse(Queue pipelinedCommands) throws IOException, MessagingException {
- String responseLine;
- List results = new ArrayList<>();
- MessagingException firstException = null;
- for (int i = 0, size = pipelinedCommands.size(); i < size; i++) {
- results.clear();
- responseLine = readCommandResponseLine(results);
- try {
- responseLineToCommandResponse(responseLine, results);
-
- } catch (MessagingException exception) {
- if (firstException == null) {
- firstException = exception;
- }
- }
- }
-
- if (firstException != null) {
- throw firstException;
- }
- }
-
- private CommandResponse responseLineToCommandResponse(String line, List results) throws MessagingException {
- int length = line.length();
- if (length < 1) {
- throw new MessagingException("SMTP response to line is 0 length");
- }
-
- int replyCode = -1;
- if (length >= 3) {
- try {
- replyCode = Integer.parseInt(line.substring(0, 3));
- } catch (NumberFormatException e) { /* ignore */ }
- }
-
- char replyCodeCategory = line.charAt(0);
- boolean isReplyCodeErrorCategory = (replyCodeCategory == '4') || (replyCodeCategory == '5');
- if (isReplyCodeErrorCategory) {
- if (isEnhancedStatusCodesProvided) {
- throw buildEnhancedNegativeSmtpReplyException(replyCode, results);
- } else {
- String replyText = TextUtils.join(" ", results);
- throw new NegativeSmtpReplyException(replyCode, replyText);
- }
- }
-
- return new CommandResponse(replyCode, results);
- }
-
-
- private void saslAuthLogin() throws MessagingException, IOException {
- try {
- executeCommand("AUTH LOGIN");
- executeSensitiveCommand(Base64.encode(username));
- executeSensitiveCommand(Base64.encode(password));
- } catch (NegativeSmtpReplyException exception) {
- if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
- throw new AuthenticationFailedException("AUTH LOGIN failed (" + exception.getMessage() + ")");
- } else {
- throw exception;
- }
- }
- }
-
- private void saslAuthPlain() throws MessagingException, IOException {
- String data = Base64.encode("\000" + username + "\000" + password);
- try {
- executeSensitiveCommand("AUTH PLAIN %s", data);
- } catch (NegativeSmtpReplyException exception) {
- if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
- throw new AuthenticationFailedException("AUTH PLAIN failed ("
- + exception.getMessage() + ")");
- } else {
- throw exception;
- }
- }
- }
-
- private void saslAuthCramMD5() throws MessagingException, IOException {
-
- List respList = executeCommand("AUTH CRAM-MD5").results;
- if (respList.size() != 1) {
- throw new MessagingException("Unable to negotiate CRAM-MD5");
- }
-
- String b64Nonce = respList.get(0);
- String b64CRAMString = Authentication.computeCramMd5(username, password, b64Nonce);
-
- try {
- executeSensitiveCommand(b64CRAMString);
- } catch (NegativeSmtpReplyException exception) {
- if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
- throw new AuthenticationFailedException(exception.getMessage(), exception);
- } else {
- throw exception;
- }
- }
- }
-
- private void saslXoauth2() throws MessagingException, IOException {
- retryXoauthWithNewToken = true;
- try {
- attemptXoauth2(username);
- } catch (NegativeSmtpReplyException negativeResponse) {
- if (negativeResponse.getReplyCode() != SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
- throw negativeResponse;
- }
-
- oauthTokenProvider.invalidateToken(username);
-
- if (!retryXoauthWithNewToken) {
- handlePermanentFailure(negativeResponse);
- } else {
- handleTemporaryFailure(username, negativeResponse);
- }
- }
- }
-
- private void handlePermanentFailure(NegativeSmtpReplyException negativeResponse) throws AuthenticationFailedException {
- throw new AuthenticationFailedException(negativeResponse.getMessage(), negativeResponse);
- }
-
- private void handleTemporaryFailure(String username, NegativeSmtpReplyException negativeResponseFromOldToken)
- throws IOException, MessagingException {
- // Token was invalid
-
- //We could avoid this double check if we had a reasonable chance of knowing
- //if a token was invalid before use (e.g. due to expiry). But we don't
- //This is the intended behaviour per AccountManager
-
- Timber.v(negativeResponseFromOldToken, "Authentication exception, re-trying with new token");
- try {
- attemptXoauth2(username);
- } catch (NegativeSmtpReplyException negativeResponseFromNewToken) {
- if (negativeResponseFromNewToken.getReplyCode() != SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
- throw negativeResponseFromNewToken;
- }
-
- //Okay, we failed on a new token.
- //Invalidate the token anyway but assume it's permanent.
- Timber.v(negativeResponseFromNewToken, "Authentication exception for new token, permanent error assumed");
-
- oauthTokenProvider.invalidateToken(username);
-
- handlePermanentFailure(negativeResponseFromNewToken);
- }
- }
-
- private void attemptXoauth2(String username) throws MessagingException, IOException {
- String token = oauthTokenProvider.getToken(username, OAuth2TokenProvider.OAUTH2_TIMEOUT);
- String authString = Authentication.computeXoauth(username, token);
- CommandResponse response = executeSensitiveCommand("AUTH XOAUTH2 %s", authString);
-
- if (response.replyCode == SMTP_CONTINUE_REQUEST) {
- String replyText = TextUtils.join("", response.results);
- retryXoauthWithNewToken = XOAuth2ChallengeParser.shouldRetry(replyText, host);
-
- //Per Google spec, respond to challenge with empty response
- executeCommand("");
- }
- }
-
- private void saslAuthExternal() throws MessagingException, IOException {
- executeCommand("AUTH EXTERNAL %s", Base64.encode(username));
- }
-
- public void checkSettings() throws MessagingException {
- close();
- try {
- open();
- } finally {
- close();
- }
- }
-}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
new file mode 100644
index 0000000000000000000000000000000000000000..46ef38ee644b4ed345d10479433c43df258e9738
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
@@ -0,0 +1,655 @@
+package com.fsck.k9.mail.transport.smtp
+
+import com.fsck.k9.logging.Timber
+import com.fsck.k9.mail.Address
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.Authentication
+import com.fsck.k9.mail.AuthenticationFailedException
+import com.fsck.k9.mail.CertificateValidationException
+import com.fsck.k9.mail.ConnectionSecurity
+import com.fsck.k9.mail.K9MailLib
+import com.fsck.k9.mail.Message
+import com.fsck.k9.mail.Message.RecipientType
+import com.fsck.k9.mail.MessagingException
+import com.fsck.k9.mail.NetworkTimeouts.SOCKET_CONNECT_TIMEOUT
+import com.fsck.k9.mail.NetworkTimeouts.SOCKET_READ_TIMEOUT
+import com.fsck.k9.mail.ServerSettings
+import com.fsck.k9.mail.Transport
+import com.fsck.k9.mail.filter.Base64
+import com.fsck.k9.mail.filter.EOLConvertingOutputStream
+import com.fsck.k9.mail.filter.LineWrapOutputStream
+import com.fsck.k9.mail.filter.PeekableInputStream
+import com.fsck.k9.mail.filter.SmtpDataStuffing
+import com.fsck.k9.mail.oauth.OAuth2TokenProvider
+import com.fsck.k9.mail.oauth.XOAuth2ChallengeParser
+import com.fsck.k9.mail.ssl.TrustedSocketFactory
+import com.fsck.k9.mail.transport.smtp.SmtpHelloResponse.Hello
+import com.fsck.k9.sasl.buildOAuthBearerInitialClientResponse
+import java.io.BufferedInputStream
+import java.io.BufferedOutputStream
+import java.io.IOException
+import java.io.OutputStream
+import java.net.Inet6Address
+import java.net.InetAddress
+import java.net.InetSocketAddress
+import java.net.Socket
+import java.net.SocketException
+import java.security.GeneralSecurityException
+import java.util.Locale
+import javax.net.ssl.SSLException
+import org.apache.commons.io.IOUtils
+
+private const val SMTP_CONTINUE_REQUEST = 334
+private const val SMTP_AUTHENTICATION_FAILURE_ERROR_CODE = 535
+
+class SmtpTransport(
+ serverSettings: ServerSettings,
+ private val trustedSocketFactory: TrustedSocketFactory,
+ private val oauthTokenProvider: OAuth2TokenProvider?
+) : Transport() {
+ private val host = serverSettings.host
+ private val port = serverSettings.port
+ private val username = serverSettings.username
+ private val password = serverSettings.password
+ private val clientCertificateAlias = serverSettings.clientCertificateAlias
+ private val authType = serverSettings.authenticationType
+ private val connectionSecurity = serverSettings.connectionSecurity
+
+ private var socket: Socket? = null
+ private var inputStream: PeekableInputStream? = null
+ private var outputStream: OutputStream? = null
+ private var responseParser: SmtpResponseParser? = null
+ private var is8bitEncodingAllowed = false
+ private var isEnhancedStatusCodesProvided = false
+ private var largestAcceptableMessage = 0
+ private var retryOAuthWithNewToken = false
+ private var isPipeliningSupported = false
+
+ private val logger: SmtpLogger = object : SmtpLogger {
+ override val isRawProtocolLoggingEnabled: Boolean
+ get() = K9MailLib.isDebug()
+
+ override fun log(throwable: Throwable?, message: String, vararg args: Any?) {
+ Timber.v(throwable, message, *args)
+ }
+ }
+
+ init {
+ require(serverSettings.type == "smtp") { "Expected SMTP ServerSettings!" }
+ }
+
+ @Throws(MessagingException::class)
+ override fun open() {
+ try {
+ var secureConnection = false
+ val addresses = InetAddress.getAllByName(host)
+ for ((index, address) in addresses.withIndex()) {
+ try {
+ val socketAddress = InetSocketAddress(address, port)
+ if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
+ socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias).also {
+ it.connect(socketAddress, SOCKET_CONNECT_TIMEOUT)
+ }
+ secureConnection = true
+ } else {
+ socket = Socket().also {
+ it.connect(socketAddress, SOCKET_CONNECT_TIMEOUT)
+ }
+ }
+ } catch (e: SocketException) {
+ if (index < addresses.lastIndex) {
+ // there are still other addresses for that host to try
+ continue
+ }
+
+ throw MessagingException("Cannot connect to host", e)
+ }
+
+ // connection success
+ break
+ }
+
+ val socket = this.socket ?: error("socket == null")
+
+ // RFC 1047
+ socket.soTimeout = SOCKET_READ_TIMEOUT
+
+ inputStream = PeekableInputStream(BufferedInputStream(socket.getInputStream(), 1024))
+ responseParser = SmtpResponseParser(logger, inputStream!!)
+ outputStream = BufferedOutputStream(socket.getOutputStream(), 1024)
+
+ readGreeting()
+
+ val helloName = buildHostnameToReport()
+ var extensions = sendHello(helloName)
+
+ is8bitEncodingAllowed = extensions.containsKey("8BITMIME")
+ isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES")
+ isPipeliningSupported = extensions.containsKey("PIPELINING")
+
+ if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) {
+ if (extensions.containsKey("STARTTLS")) {
+ executeCommand("STARTTLS")
+
+ val tlsSocket = trustedSocketFactory.createSocket(
+ socket,
+ host,
+ port,
+ clientCertificateAlias
+ )
+ this.socket = tlsSocket
+ inputStream = PeekableInputStream(BufferedInputStream(tlsSocket.getInputStream(), 1024))
+ responseParser = SmtpResponseParser(logger, inputStream!!)
+ outputStream = BufferedOutputStream(tlsSocket.getOutputStream(), 1024)
+
+ // Now resend the EHLO. Required by RFC2487 Sec. 5.2, and more specifically, Exim.
+ extensions = sendHello(helloName)
+ 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 CertificateValidationException("STARTTLS connection security not available")
+ }
+ }
+
+ var authLoginSupported = false
+ var authPlainSupported = false
+ var authCramMD5Supported = false
+ var authExternalSupported = false
+ var authXoauth2Supported = false
+ var authOAuthBearerSupported = false
+ val saslMechanisms = extensions["AUTH"]
+ if (saslMechanisms != null) {
+ authLoginSupported = saslMechanisms.contains("LOGIN")
+ authPlainSupported = saslMechanisms.contains("PLAIN")
+ authCramMD5Supported = saslMechanisms.contains("CRAM-MD5")
+ authExternalSupported = saslMechanisms.contains("EXTERNAL")
+ authXoauth2Supported = saslMechanisms.contains("XOAUTH2")
+ authOAuthBearerSupported = saslMechanisms.contains("OAUTHBEARER")
+ }
+ parseOptionalSizeValue(extensions["SIZE"])
+
+ if (
+ username.isNotEmpty() &&
+ (!password.isNullOrEmpty() || AuthType.EXTERNAL == authType || AuthType.XOAUTH2 == authType)
+ ) {
+ when (authType) {
+ AuthType.LOGIN, AuthType.PLAIN -> {
+ // try saslAuthPlain first, because it supports UTF-8 explicitly
+ if (authPlainSupported) {
+ saslAuthPlain()
+ } else if (authLoginSupported) {
+ saslAuthLogin()
+ } else {
+ throw MessagingException("Authentication methods SASL PLAIN and LOGIN are unavailable.")
+ }
+ }
+ AuthType.CRAM_MD5 -> {
+ if (authCramMD5Supported) {
+ saslAuthCramMD5()
+ } else {
+ throw MessagingException("Authentication method CRAM-MD5 is unavailable.")
+ }
+ }
+ AuthType.XOAUTH2 -> {
+ if (oauthTokenProvider == null) {
+ throw MessagingException("No OAuth2TokenProvider available.")
+ } else if (authOAuthBearerSupported) {
+ saslOAuth(OAuthMethod.OAUTHBEARER)
+ } else if (authXoauth2Supported) {
+ saslOAuth(OAuthMethod.XOAUTH2)
+ } else {
+ throw MessagingException("Server doesn't support SASL OAUTHBEARER or XOAUTH2.")
+ }
+ }
+ AuthType.EXTERNAL -> {
+ if (authExternalSupported) {
+ saslAuthExternal()
+ } else {
+ // Some SMTP servers are known to provide no error indication when a client certificate
+ // fails to validate, other than to not offer the AUTH EXTERNAL capability.
+ // So, we treat it is an error to not offer AUTH EXTERNAL when using client certificates.
+ // That way, the user can be notified of a problem during account setup.
+ throw CertificateValidationException(
+ CertificateValidationException.Reason.MissingCapability
+ )
+ }
+ }
+ AuthType.AUTOMATIC -> {
+ if (secureConnection) {
+ // try saslAuthPlain first, because it supports UTF-8 explicitly
+ if (authPlainSupported) {
+ saslAuthPlain()
+ } else if (authLoginSupported) {
+ saslAuthLogin()
+ } else if (authCramMD5Supported) {
+ saslAuthCramMD5()
+ } else {
+ throw MessagingException("No supported authentication methods available.")
+ }
+ } else {
+ if (authCramMD5Supported) {
+ saslAuthCramMD5()
+ } 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 MessagingException(
+ "Update your outgoing server authentication setting. " +
+ "AUTOMATIC authentication is unavailable."
+ )
+ }
+ }
+ }
+ else -> {
+ throw MessagingException("Unhandled authentication method found in server settings (bug).")
+ }
+ }
+ }
+ } catch (e: MessagingException) {
+ close()
+ throw e
+ } catch (e: SSLException) {
+ close()
+ throw CertificateValidationException(e.message, e)
+ } catch (e: GeneralSecurityException) {
+ close()
+ throw MessagingException("Unable to open connection to SMTP server due to security error.", e)
+ } catch (e: IOException) {
+ close()
+ throw MessagingException("Unable to open connection to SMTP server.", e)
+ }
+ }
+
+ private fun readGreeting() {
+ val smtpResponse = responseParser!!.readGreeting()
+ logResponse(smtpResponse)
+ }
+
+ private fun logResponse(smtpResponse: SmtpResponse, omitText: Boolean = false) {
+ if (K9MailLib.isDebug()) {
+ Timber.v("%s", smtpResponse.toLogString(omitText, linePrefix = "SMTP <<< "))
+ }
+ }
+
+ private fun buildHostnameToReport(): String {
+ val localAddress = socket!!.localAddress
+
+ // We use local IP statically for privacy reasons, see https://github.com/k9mail/k-9/pull/3798
+ return if (localAddress is Inet6Address) {
+ "[IPv6:::1]"
+ } else {
+ "[127.0.0.1]"
+ }
+ }
+
+ private fun parseOptionalSizeValue(sizeParameters: List?) {
+ if (sizeParameters != null && sizeParameters.isNotEmpty()) {
+ val sizeParameter = sizeParameters.first()
+ val size = sizeParameter.toIntOrNull()
+ if (size != null) {
+ largestAcceptableMessage = size
+ } else {
+ if (K9MailLib.isDebug() && K9MailLib.DEBUG_PROTOCOL_SMTP) {
+ Timber.d("SIZE parameter is not a valid integer: %s", sizeParameter)
+ }
+ }
+ }
+ }
+
+ /**
+ * Send the client "identity" using the `EHLO` or `HELO` command.
+ *
+ * We first try the EHLO command. If the server sends a negative response, it probably doesn't support the
+ * `EHLO` command. So we try the older `HELO` command that all servers have to support. And if that fails, too,
+ * we pretend everything is fine and continue unimpressed.
+ *
+ * @param host The EHLO/HELO parameter as defined by the RFC.
+ *
+ * @return A (possibly empty) `Map>` of extensions (upper case) and their parameters
+ * (possibly empty) as returned by the EHLO command.
+ */
+ private fun sendHello(host: String): Map> {
+ writeLine("EHLO $host")
+
+ val helloResponse = responseParser!!.readHelloResponse()
+ logResponse(helloResponse.response)
+
+ return if (helloResponse is Hello) {
+ helloResponse.keywords
+ } else {
+ if (K9MailLib.isDebug()) {
+ Timber.v("Server doesn't support the EHLO command. Trying HELO...")
+ }
+
+ try {
+ executeCommand("HELO %s", host)
+ } catch (e: NegativeSmtpReplyException) {
+ Timber.w("Server doesn't support the HELO command. Continuing anyway.")
+ }
+
+ emptyMap()
+ }
+ }
+
+ @Throws(MessagingException::class)
+ override fun sendMessage(message: Message) {
+ val addresses = buildSet {
+ for (address in message.getRecipients(RecipientType.TO)) {
+ add(address.address)
+ }
+
+ for (address in message.getRecipients(RecipientType.CC)) {
+ add(address.address)
+ }
+
+ for (address in message.getRecipients(RecipientType.BCC)) {
+ add(address.address)
+ }
+ }
+
+ if (addresses.isEmpty()) {
+ return
+ }
+
+ message.removeHeader("Bcc")
+
+ close()
+ open()
+
+ // If the message has attachments and our server has told us about a limit on the size of messages, count
+ // the message's size before sending it.
+ if (largestAcceptableMessage > 0 && message.hasAttachments()) {
+ if (message.calculateSize() > largestAcceptableMessage) {
+ throw MessagingException("Message too large for server", true)
+ }
+ }
+
+ var entireMessageSent = false
+ try {
+ val mailFrom = constructSmtpMailFromCommand(message.from, is8bitEncodingAllowed)
+ if (isPipeliningSupported) {
+ val pipelinedCommands = buildList {
+ add(mailFrom)
+
+ for (address in addresses) {
+ add(String.format("RCPT TO:<%s>", address))
+ }
+ }
+
+ executePipelinedCommands(pipelinedCommands)
+ readPipelinedResponse(pipelinedCommands)
+ } else {
+ executeCommand(mailFrom)
+
+ for (address in addresses) {
+ executeCommand("RCPT TO:<%s>", address)
+ }
+ }
+
+ executeCommand("DATA")
+
+ val msgOut = EOLConvertingOutputStream(
+ LineWrapOutputStream(
+ SmtpDataStuffing(outputStream), 1000
+ )
+ )
+
+ message.writeTo(msgOut)
+ msgOut.endWithCrLfAndFlush()
+
+ // After the "\r\n." is attempted, we may have sent the message
+ entireMessageSent = true
+ executeCommand(".")
+ } catch (e: NegativeSmtpReplyException) {
+ throw e
+ } catch (e: Exception) {
+ throw MessagingException("Unable to send message", entireMessageSent, e)
+ } finally {
+ close()
+ }
+ }
+
+ private fun constructSmtpMailFromCommand(from: Array, is8bitEncodingAllowed: Boolean): String {
+ val fromAddress = from.first().address
+ return if (is8bitEncodingAllowed) {
+ String.format("MAIL FROM:<%s> BODY=8BITMIME", fromAddress)
+ } else {
+ Timber.d("Server does not support 8-bit transfer encoding")
+ String.format("MAIL FROM:<%s>", fromAddress)
+ }
+ }
+
+ override fun close() {
+ try {
+ executeCommand("QUIT")
+ } catch (ignored: Exception) {
+ }
+
+ IOUtils.closeQuietly(inputStream)
+ IOUtils.closeQuietly(outputStream)
+ IOUtils.closeQuietly(socket)
+
+ inputStream = null
+ responseParser = null
+ outputStream = null
+ socket = null
+ }
+
+ private fun writeLine(command: String, sensitive: Boolean = false) {
+ if (K9MailLib.isDebug() && K9MailLib.DEBUG_PROTOCOL_SMTP) {
+ val commandToLog = if (sensitive && !K9MailLib.isDebugSensitive()) {
+ "SMTP >>> *sensitive*"
+ } else {
+ "SMTP >>> $command"
+ }
+ Timber.d(commandToLog)
+ }
+
+ // Important: Send command + CRLF using just one write() call. Using multiple calls might result in multiple
+ // TCP packets being sent and some SMTP servers misbehave if CR and LF arrive in separate packets.
+ // See https://code.google.com/archive/p/k9mail/issues/799
+ val data = (command + "\r\n").toByteArray()
+ outputStream!!.apply {
+ write(data)
+ flush()
+ }
+ }
+
+ private fun executeSensitiveCommand(format: String, vararg args: Any): SmtpResponse {
+ return executeCommand(sensitive = true, format, *args)
+ }
+
+ private fun executeCommand(format: String, vararg args: Any): SmtpResponse {
+ return executeCommand(sensitive = false, format, *args)
+ }
+
+ private fun executeCommand(sensitive: Boolean, format: String, vararg args: Any): SmtpResponse {
+ val command = String.format(Locale.ROOT, format, *args)
+ writeLine(command, sensitive)
+
+ val response = responseParser!!.readResponse(isEnhancedStatusCodesProvided)
+ logResponse(response, sensitive)
+
+ if (response.isNegativeResponse) {
+ throw buildNegativeSmtpReplyException(response)
+ }
+
+ return response
+ }
+
+ private fun buildNegativeSmtpReplyException(response: SmtpResponse): NegativeSmtpReplyException {
+ return NegativeSmtpReplyException(
+ replyCode = response.replyCode,
+ replyText = response.joinedText,
+ enhancedStatusCode = response.enhancedStatusCode
+ )
+ }
+
+ private fun executePipelinedCommands(pipelinedCommands: List) {
+ for (command in pipelinedCommands) {
+ writeLine(command, false)
+ }
+ }
+
+ private fun readPipelinedResponse(pipelinedCommands: List) {
+ val responseParser = responseParser!!
+ var firstException: MessagingException? = null
+
+ repeat(pipelinedCommands.size) {
+ val response = responseParser.readResponse(isEnhancedStatusCodesProvided)
+ logResponse(response, omitText = false)
+
+ if (response.isNegativeResponse && firstException == null) {
+ firstException = buildNegativeSmtpReplyException(response)
+ }
+ }
+
+ firstException?.let {
+ throw it
+ }
+ }
+
+ private fun saslAuthLogin() {
+ try {
+ executeCommand("AUTH LOGIN")
+ executeSensitiveCommand(Base64.encode(username))
+ executeSensitiveCommand(Base64.encode(password))
+ } catch (exception: NegativeSmtpReplyException) {
+ if (exception.replyCode == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
+ throw AuthenticationFailedException("AUTH LOGIN failed (${exception.message})")
+ } else {
+ throw exception
+ }
+ }
+ }
+
+ private fun saslAuthPlain() {
+ val data = Base64.encode("\u0000" + username + "\u0000" + password)
+ try {
+ executeSensitiveCommand("AUTH PLAIN %s", data)
+ } catch (exception: NegativeSmtpReplyException) {
+ if (exception.replyCode == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
+ throw AuthenticationFailedException("AUTH PLAIN failed (${exception.message})")
+ } else {
+ throw exception
+ }
+ }
+ }
+
+ private fun saslAuthCramMD5() {
+ val respList = executeCommand("AUTH CRAM-MD5").texts
+ if (respList.size != 1) {
+ throw MessagingException("Unable to negotiate CRAM-MD5")
+ }
+
+ val b64Nonce = respList[0]
+ val b64CRAMString = Authentication.computeCramMd5(username, password, b64Nonce)
+ try {
+ executeSensitiveCommand(b64CRAMString)
+ } catch (exception: NegativeSmtpReplyException) {
+ if (exception.replyCode == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
+ throw AuthenticationFailedException(exception.message!!, exception)
+ } else {
+ throw exception
+ }
+ }
+ }
+
+ private fun saslOAuth(method: OAuthMethod) {
+ retryOAuthWithNewToken = true
+ try {
+ attempOAuth(method, username)
+ } catch (negativeResponse: NegativeSmtpReplyException) {
+ if (negativeResponse.replyCode != SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
+ throw negativeResponse
+ }
+
+ oauthTokenProvider!!.invalidateToken()
+
+ if (!retryOAuthWithNewToken) {
+ handlePermanentFailure(negativeResponse)
+ } else {
+ handleTemporaryFailure(method, username, negativeResponse)
+ }
+ }
+ }
+
+ private fun handlePermanentFailure(negativeResponse: NegativeSmtpReplyException): Nothing {
+ throw AuthenticationFailedException(negativeResponse.message!!, negativeResponse)
+ }
+
+ private fun handleTemporaryFailure(
+ method: OAuthMethod,
+ username: String,
+ negativeResponseFromOldToken: NegativeSmtpReplyException
+ ) {
+ // Token was invalid. We could avoid this double check if we had a reasonable chance of knowing if a token was
+ // invalid before use (e.g. due to expiry). But we don't. This is the intended behaviour per AccountManager.
+ Timber.v(negativeResponseFromOldToken, "Authentication exception, re-trying with new token")
+
+ try {
+ attempOAuth(method, username)
+ } catch (negativeResponseFromNewToken: NegativeSmtpReplyException) {
+ if (negativeResponseFromNewToken.replyCode != SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
+ throw negativeResponseFromNewToken
+ }
+
+ // Okay, we failed on a new token. Invalidate the token anyway but assume it's permanent.
+ Timber.v(negativeResponseFromNewToken, "Authentication exception for new token, permanent error assumed")
+
+ oauthTokenProvider!!.invalidateToken()
+ handlePermanentFailure(negativeResponseFromNewToken)
+ }
+ }
+
+ private fun attempOAuth(method: OAuthMethod, username: String) {
+ val token = oauthTokenProvider!!.getToken(username, OAuth2TokenProvider.OAUTH2_TIMEOUT.toLong())
+ val authString = method.buildInitialClientResponse(username, token)
+
+ val response = executeSensitiveCommand("%s %s", method.command, authString)
+ if (response.replyCode == SMTP_CONTINUE_REQUEST) {
+ val replyText = response.joinedText
+ retryOAuthWithNewToken = XOAuth2ChallengeParser.shouldRetry(replyText, host)
+
+ // Per Google spec, respond to challenge with empty response
+ executeCommand("")
+ }
+ }
+
+ private fun saslAuthExternal() {
+ executeCommand("AUTH EXTERNAL %s", Base64.encode(username))
+ }
+
+ @Throws(MessagingException::class)
+ fun checkSettings() {
+ close()
+
+ try {
+ open()
+ } finally {
+ close()
+ }
+ }
+}
+
+private enum class OAuthMethod {
+ XOAUTH2 {
+ override val command = "AUTH XOAUTH2"
+
+ override fun buildInitialClientResponse(username: String, token: String): String {
+ return Authentication.computeXoauth(username, token)
+ }
+ },
+ OAUTHBEARER {
+ override val command = "AUTH OAUTHBEARER"
+
+ override fun buildInitialClientResponse(username: String, token: String): String {
+ return buildOAuthBearerInitialClientResponse(username, token)
+ }
+ };
+
+ abstract val command: String
+ abstract fun buildInitialClientResponse(username: String, token: String): String
+}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeClass.java b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeClass.java
deleted file mode 100644
index f56f28942357b5de704acb011de421a78b879ae9..0000000000000000000000000000000000000000
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeClass.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.fsck.k9.mail.transport.smtp;
-
-
-enum StatusCodeClass {
- SUCCESS(2),
- PERSISTENT_TRANSIENT_FAILURE(4),
- PERMANENT_FAILURE(5);
-
-
- private final int codeClass;
-
-
- static StatusCodeClass parse(String statusCodeClassString) {
- int value = Integer.parseInt(statusCodeClassString);
- for (StatusCodeClass classEnum : StatusCodeClass.values()) {
- if (classEnum.codeClass == value) {
- return classEnum;
- }
- }
- return null;
- }
-
- StatusCodeClass(int codeClass) {
- this.codeClass = codeClass;
- }
-}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeClass.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeClass.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2fe00975b7d3ecab6b8826829de969aff8180418
--- /dev/null
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeClass.kt
@@ -0,0 +1,7 @@
+package com.fsck.k9.mail.transport.smtp
+
+enum class StatusCodeClass(val codeClass: Int) {
+ SUCCESS(2),
+ PERSISTENT_TRANSIENT_FAILURE(4),
+ PERMANENT_FAILURE(5);
+}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeDetail.java b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeDetail.java
deleted file mode 100644
index 2ea73fe3c6150e7418604175c7d75b1bcb76d9fe..0000000000000000000000000000000000000000
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeDetail.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.fsck.k9.mail.transport.smtp;
-
-
-enum StatusCodeDetail {
- UNDEFINED(StatusCodeSubject.UNDEFINED, 0),
- OTHER_ADDRESS_STATUS(StatusCodeSubject.ADDRESSING, 0),
- BAD_DESTINATION_MAILBOX_ADDRESS(StatusCodeSubject.ADDRESSING, 1),
- BAD_DESTINATION_SYSTEM_ADDRESS(StatusCodeSubject.ADDRESSING, 2),
- BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX(StatusCodeSubject.ADDRESSING, 3),
- DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS(StatusCodeSubject.ADDRESSING, 4),
- DESTINATION_ADDRESS_VALID(StatusCodeSubject.ADDRESSING, 5),
- DESTINATION_MAILBOX_MOVED(StatusCodeSubject.ADDRESSING, 6),
- BAD_SENDER_MAILBOX_SYNTAX(StatusCodeSubject.ADDRESSING, 7),
- BAD_SENDER_SYSTEM_ADDRESS(StatusCodeSubject.ADDRESSING, 8),
-
- OTHER_MAILBOX_STATUS(StatusCodeSubject.MAILBOX, 0),
- MAILBOX_DISABLED(StatusCodeSubject.MAILBOX, 1),
- MAILBOX_FULL(StatusCodeSubject.MAILBOX, 2),
- MESSAGE_LENGTH_EXCEEDED(StatusCodeSubject.MAILBOX, 3),
- MAILING_LIST_EXPANSION_PROBLEM(StatusCodeSubject.MAILBOX, 4),
-
- OTHER_MAIL_SYSTEM_STATUS(StatusCodeSubject.MAIL_SYSTEM, 0),
- MAIL_SYSTEM_FULL(StatusCodeSubject.MAIL_SYSTEM, 1),
- SYSTEM_NOT_ACCEPTING_MESSAGES(StatusCodeSubject.MAIL_SYSTEM, 2),
- SYSTEM_INCAPABLE_OF_FEATURE(StatusCodeSubject.MAIL_SYSTEM, 3),
- MESSAGE_TOO_BIG(StatusCodeSubject.MAIL_SYSTEM, 4),
- SYSTEM_INCORRECTLY_CONFIGURED(StatusCodeSubject.MAIL_SYSTEM, 5),
-
- OTHER_NETWORK_ROUTING(StatusCodeSubject.NETWORK_ROUTING, 0),
- NO_ANSWER_FROM_HOST(StatusCodeSubject.NETWORK_ROUTING, 1),
- BAD_CONNECTION(StatusCodeSubject.NETWORK_ROUTING, 2),
- DIRECTORY_SERVER_FAILURE(StatusCodeSubject.NETWORK_ROUTING, 3),
- UNABLE_TO_ROUTE(StatusCodeSubject.NETWORK_ROUTING, 4),
- MAIL_SYSTEM_CONGESTION(StatusCodeSubject.NETWORK_ROUTING, 5),
- ROUTING_LOOP_DETECTED(StatusCodeSubject.NETWORK_ROUTING, 6),
- DELIVERY_TIME_EXPIRED(StatusCodeSubject.NETWORK_ROUTING, 7),
-
- OTHER_MAIL_DELIVERY_PROTOCOL(StatusCodeSubject.MAIL_DELIVERY_PROTOCOL, 0),
- INVALID_COMMAND(StatusCodeSubject.MAIL_DELIVERY_PROTOCOL, 1),
- SYNTAX_ERROR(StatusCodeSubject.MAIL_DELIVERY_PROTOCOL, 2),
- TOO_MANY_RECIPIENTS(StatusCodeSubject.MAIL_DELIVERY_PROTOCOL, 3),
- INVALID_COMMAND_ARGUMENTS(StatusCodeSubject.MAIL_DELIVERY_PROTOCOL, 4),
- WRONG_PROTOCOL_VERSION(StatusCodeSubject.MAIL_DELIVERY_PROTOCOL, 5),
-
- OTHER_MESSAGE_CONTENT_OR_MEDIA(StatusCodeSubject.MESSAGE_CONTENT_OR_MEDIA, 0),
- MEDIA_NOT_SUPPORTED(StatusCodeSubject.MESSAGE_CONTENT_OR_MEDIA, 1),
- CONVERSION_REQUIRED_AND_PROHIBITED(StatusCodeSubject.MESSAGE_CONTENT_OR_MEDIA, 2),
- CONVERSION_REQUIRED_BUT_UNSUPPORTED(StatusCodeSubject.MESSAGE_CONTENT_OR_MEDIA, 3),
- CONVERSION_WITH_LOSS_PERFORMED(StatusCodeSubject.MESSAGE_CONTENT_OR_MEDIA, 4),
- CONVERSION_FAILED(StatusCodeSubject.MESSAGE_CONTENT_OR_MEDIA, 5),
-
- OTHER_SECURITY_OR_POLICY_STATUS(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 0),
- DELIVERY_NOT_AUTHORIZED(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 1),
- MAILING_LIST_EXPANSION_PROHIBITED(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 2),
- SECURITY_CONVERSION_REQUIRED(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 3),
- SECURITY_FEATURES_UNSUPPORTED(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 4),
- CRYPTOGRAPHIC_FAILURE(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 5),
- CRYPTOGRAPHIC_ALGORITHM_UNSUPPORTED(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 6),
- MESSAGE_INTEGRITY_FAILURE(StatusCodeSubject.SECURITY_OR_POLICY_STATUS, 7);
-
-
- private final StatusCodeSubject subject;
- private final int detail;
-
-
- public static StatusCodeDetail parse(StatusCodeSubject statusCodeSubject, String statusCodeDetailString) {
- int value = Integer.parseInt(statusCodeDetailString);
- for (StatusCodeDetail detailEnum : StatusCodeDetail.values()) {
- if (detailEnum.subject == statusCodeSubject && detailEnum.detail == value) {
- return detailEnum;
- }
- }
- return null;
- }
-
- StatusCodeDetail(StatusCodeSubject subject, int detail) {
- this.subject = subject;
- this.detail = detail;
- }
-}
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeSubject.java b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeSubject.java
deleted file mode 100644
index ff004f3068fc9240b7fd781621db76bc8d076c4f..0000000000000000000000000000000000000000
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/StatusCodeSubject.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.fsck.k9.mail.transport.smtp;
-
-
-enum StatusCodeSubject {
- UNDEFINED(0),
- ADDRESSING(1),
- MAILBOX(2),
- MAIL_SYSTEM(3),
- NETWORK_ROUTING(4),
- MAIL_DELIVERY_PROTOCOL(5),
- MESSAGE_CONTENT_OR_MEDIA(6),
- SECURITY_OR_POLICY_STATUS(7);
-
-
- private final int codeSubject;
-
-
- static StatusCodeSubject parse(String statusCodeSubjectString) {
- int value = Integer.parseInt(statusCodeSubjectString);
- for (StatusCodeSubject classEnum : StatusCodeSubject.values()) {
- if (classEnum.codeSubject == value) {
- return classEnum;
- }
- }
- return null;
- }
-
- StatusCodeSubject(int codeSubject) {
- this.codeSubject = codeSubject;
- }
-}
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java
index 0a88d21110f01745f7e384859efdbab9901a2400..35ece7ab7ccc4f2c561a796e3c86782166d84ea5 100644
--- a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java
+++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java
@@ -21,8 +21,6 @@ import java.util.concurrent.TimeUnit;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
-import android.annotation.SuppressLint;
-
import com.fsck.k9.mail.helpers.KeyStoreProvider;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
@@ -36,7 +34,6 @@ import okio.Okio;
import org.apache.commons.io.IOUtils;
-@SuppressLint("NewApi")
public class MockSmtpServer {
private static final byte[] CRLF = { '\r', '\n' };
@@ -71,6 +68,11 @@ public class MockSmtpServer {
interactions.add(new ExpectedCommand(command));
}
+ public void startTls() {
+ checkServerNotRunning();
+ interactions.add(new UpgradeToTls());
+ }
+
public void closeConnection() {
checkServerNotRunning();
interactions.add(new CloseConnection());
@@ -215,6 +217,9 @@ public class MockSmtpServer {
}
}
+ private static class UpgradeToTls implements SmtpInteraction {
+ }
+
private static class CloseConnection implements SmtpInteraction {
}
@@ -306,6 +311,8 @@ public class MockSmtpServer {
readExpectedCommand((ExpectedCommand) interaction);
} else if (interaction instanceof CannedResponse) {
writeCannedResponse((CannedResponse) interaction);
+ } else if (interaction instanceof UpgradeToTls) {
+ upgradeToTls(socket);
} else if (interaction instanceof CloseConnection) {
clientSocket.close();
}
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserTest.kt b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9b813c0ae5ca806c1890c977c9cb4b658ef08ff7
--- /dev/null
+++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseParserTest.kt
@@ -0,0 +1,678 @@
+package com.fsck.k9.mail.transport.smtp
+
+import com.fsck.k9.mail.crlf
+import com.fsck.k9.mail.filter.PeekableInputStream
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.fail
+import org.junit.Test
+
+class SmtpResponseParserTest {
+ private val logger = TestSmtpLogger()
+
+ @Test
+ fun `read greeting`() {
+ val input = "220 smtp.domain.example ESMTP ready".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readGreeting()
+
+ assertThat(response.replyCode).isEqualTo(220)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("smtp.domain.example ESMTP ready")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `read multi-line greeting`() {
+ val input = """
+ 220-Greetings, stranger
+ 220 smtp.domain.example ESMTP ready
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readGreeting()
+
+ assertThat(response.replyCode).isEqualTo(220)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("Greetings, stranger", "smtp.domain.example ESMTP ready")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `read EHLO response`() {
+ val input = """
+ 250-smtp.domain.example greets 127.0.0.1
+ 250-PIPELINING
+ 250-ENHANCEDSTATUSCODES
+ 250-8BITMIME
+ 250-SIZE 104857600
+ 250-DELIVERBY
+ 250-AUTH PLAIN LOGIN CRAM-MD5 DIGEST-MD5
+ 250 help
+ """.trimIndent()
+ val inputStream = input.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, inputStream)
+
+ val response = parser.readHelloResponse()
+
+ assertType(response) { hello ->
+ assertThat(hello.response.toLogString(omitText = false, linePrefix = "")).isEqualTo(input)
+ assertThat(hello.keywords.keys).containsExactly(
+ "PIPELINING",
+ "ENHANCEDSTATUSCODES",
+ "8BITMIME",
+ "SIZE",
+ "DELIVERBY",
+ "AUTH",
+ "HELP"
+ )
+ assertThat(hello.keywords["PIPELINING"]).isEmpty()
+ assertThat(hello.keywords["SIZE"]).containsExactly("104857600")
+ assertThat(hello.keywords["AUTH"]).containsExactly("PLAIN", "LOGIN", "CRAM-MD5", "DIGEST-MD5")
+ }
+ assertInputExhausted(inputStream)
+ }
+
+ @Test
+ fun `read EHLO response with only one line`() {
+ val input = "250 smtp.domain.example".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readHelloResponse()
+
+ assertType(response) { hello ->
+ assertThat(hello.response.replyCode).isEqualTo(250)
+ assertThat(hello.response.texts).containsExactly("smtp.domain.example")
+ assertThat(hello.keywords).isEmpty()
+ }
+ }
+
+ @Test
+ fun `read EHLO error response`() {
+ val input = "421 Service not available".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readHelloResponse()
+
+ assertType(response) { error ->
+ assertThat(error.response.replyCode).isEqualTo(421)
+ assertThat(error.response.texts).containsExactly("Service not available")
+ }
+ }
+
+ @Test
+ fun `read EHLO response with only reply code`() {
+ val input = "250".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Unexpected character: (13)") {
+ parser.readHelloResponse()
+ }
+
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(
+ throwable = null,
+ message = """
+ SMTP response data on parser error:
+ 250
+ """.trimIndent()
+ )
+ )
+ }
+
+ @Test
+ fun `read EHLO response with reply code not matching`() {
+ val input = """
+ 250-smtp.domain.example
+ 220
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Multi-line response with reply codes not matching: 250 != 220") {
+ parser.readHelloResponse()
+ }
+
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(
+ throwable = null,
+ message = """
+ SMTP response data on parser error:
+ 250-smtp.domain.example
+ 220
+ """.trimIndent()
+ )
+ )
+ }
+
+ @Test
+ fun `read EHLO response with invalid keywords`() {
+ val input = """
+ 250-smtp.domain.example
+ 250-SIZE 52428800
+ 250-8BITMIME
+ 250-PIPELINING
+ 250-PIPE_CONNECT
+ 250-AUTH=PLAIN
+ 250 HELP
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readHelloResponse()
+
+ assertType(response) { hello ->
+ assertThat(hello.keywords.keys).containsExactly(
+ "SIZE",
+ "8BITMIME",
+ "PIPELINING",
+ "HELP"
+ )
+ }
+
+ assertThat(logger.logEntries.map { it.message }).containsExactly(
+ "Ignoring EHLO keyword line: PIPE_CONNECT",
+ "Ignoring EHLO keyword line: AUTH=PLAIN"
+ )
+ assertThat(logger.logEntries.map { it.throwable?.message }).containsExactly(
+ "EHLO keyword contains invalid character",
+ "EHLO keyword contains invalid character"
+ )
+ }
+
+ @Test
+ fun `read EHLO response with empty parameter`() {
+ val input = """
+ 250-smtp.domain.example
+ 250 KEYWORD${" "}
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readHelloResponse()
+
+ assertType(response) { hello ->
+ assertThat(hello.keywords.keys).isEmpty()
+ }
+
+ assertThat(logger.logEntries).hasSize(1)
+ assertThat(logger.logEntries.first().throwable).hasMessageThat().isEqualTo("EHLO parameter must not be empty")
+ assertThat(logger.logEntries.first().message).isEqualTo("Ignoring EHLO keyword line: KEYWORD ")
+ }
+
+ @Test
+ fun `read EHLO response with invalid parameter`() {
+ val input = """
+ 250-smtp.domain.example
+ 250-8BITMIME
+ 250 KEYWORD para${"\t"}meter
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readHelloResponse()
+
+ assertType(response) { hello ->
+ assertThat(hello.keywords.keys).containsExactly("8BITMIME")
+ }
+
+ assertThat(logger.logEntries).hasSize(1)
+ assertThat(logger.logEntries.first().throwable)
+ .hasMessageThat().isEqualTo("EHLO parameter contains invalid character")
+ assertThat(logger.logEntries.first().message)
+ .isEqualTo("Ignoring EHLO keyword line: KEYWORD para${"\t"}meter")
+ }
+
+ @Test
+ fun `positive response`() {
+ val input = "200 OK".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.isNegativeResponse).isFalse()
+ }
+
+ @Test
+ fun `negative response`() {
+ val input = "500 Oops".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.isNegativeResponse).isTrue()
+ }
+
+ @Test
+ fun `reply code only`() {
+ val input = "502".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(502)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).isEmpty()
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `reply code and text`() {
+ val input = "250 OK".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(250)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("OK")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `reply code and text with enhanced status code`() {
+ val input = "250 2.1.0 Originator ok".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(250)
+ assertThat(response.enhancedStatusCode).isEqualTo(
+ EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 1, detail = 0)
+ )
+ assertThat(response.texts).containsExactly("Originator ok")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `enhancedStatusCodes enabled and 3xx reply code`() {
+ val input = "354 Ok Send data ending with .".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(354)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("Ok Send data ending with .")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `multi-line response with text`() {
+ val input = """
+ 500-Line one
+ 500 Line two
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(500)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("Line one", "Line two")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `multi-line response with empty textstring`() {
+ val input = """
+ 500-
+ 500 Line two
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(500)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("", "Line two")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `multi-line response without text on last line`() {
+ val input = """
+ 500-Line one
+ 500-Line two
+ 500
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(500)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("Line one", "Line two")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `multi-line response with enhanced status code`() {
+ val input = """
+ 250-2.1.0 Sender
+ 250 2.1.0 OK
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(250)
+ assertThat(response.enhancedStatusCode).isEqualTo(
+ EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 1, detail = 0)
+ )
+ assertThat(response.texts).containsExactly("Sender ", "OK")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `read multiple responses`() {
+ val input = """
+ 250 Sender OK
+ 250 Recipient OK
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val responseOne = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(responseOne.replyCode).isEqualTo(250)
+ assertThat(responseOne.enhancedStatusCode).isNull()
+ assertThat(responseOne.texts).containsExactly("Sender OK")
+
+ val responseTwo = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(responseTwo.replyCode).isEqualTo(250)
+ assertThat(responseTwo.enhancedStatusCode).isNull()
+ assertThat(responseTwo.texts).containsExactly("Recipient OK")
+ assertInputExhausted(input)
+ }
+
+ @Test
+ fun `multi-line response with reply codes not matching`() {
+ val input = """
+ 200-Line one
+ 500 Line two
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Multi-line response with reply codes not matching: 200 != 500") {
+ parser.readResponse(enhancedStatusCodes = false)
+ }
+
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(
+ throwable = null,
+ message = """
+ SMTP response data on parser error:
+ 200-Line one
+ 500
+ """.trimIndent()
+ )
+ )
+ }
+
+ @Test
+ fun `multi-line response with reply codes not matching and raw protocol logging disabled`() {
+ val input = """
+ 200-Line one
+ 500 Line two
+ """.toPeekableInputStream()
+ val logger = TestSmtpLogger(isRawProtocolLoggingEnabled = false)
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Multi-line response with reply codes not matching: 200 != 500") {
+ parser.readResponse(enhancedStatusCodes = false)
+ }
+
+ assertThat(logger.logEntries).isEmpty()
+ }
+
+ @Test
+ fun `invalid 1st reply code digit`() {
+ val input = "611".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Unsupported 1st reply code digit: 6") {
+ parser.readResponse(enhancedStatusCodes = false)
+ }
+ }
+
+ @Test
+ fun `invalid 2nd reply code digit should only produce a log entry`() {
+ val input = "280 Something".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(280)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("Something")
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(throwable = null, message = "2nd digit of reply code outside of specified range (0..5): 8")
+ )
+ }
+
+ @Test
+ fun `invalid 3rd reply code digit`() {
+ val input = "20x".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Unexpected character: x (120)") {
+ parser.readResponse(enhancedStatusCodes = false)
+ }
+
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(
+ throwable = null,
+ message = """
+ SMTP response data on parser error:
+ 20x
+ """.trimIndent()
+ )
+ )
+ }
+
+ @Test
+ fun `end of stream after reply code`() {
+ val input = PeekableInputStream("200".byteInputStream())
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Unexpected end of stream") {
+ parser.readResponse(enhancedStatusCodes = false)
+ }
+
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(
+ throwable = null,
+ message = """
+ SMTP response data on parser error:
+ 200
+ """.trimIndent()
+ )
+ )
+ }
+
+ @Test
+ fun `response ending with CR only`() {
+ val input = PeekableInputStream("200\r".byteInputStream())
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Unexpected end of stream") {
+ parser.readResponse(enhancedStatusCodes = false)
+ }
+ }
+
+ @Test
+ fun `response ending with LF only`() {
+ val input = PeekableInputStream("200\n".byteInputStream())
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage("Unexpected character: (10)") {
+ parser.readResponse(enhancedStatusCodes = false)
+ }
+
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(
+ throwable = null,
+ message = """
+ SMTP response data on parser error:
+ 200
+ """.trimIndent()
+ )
+ )
+ }
+
+ @Test
+ fun `reply code with space but without text`() {
+ val input = "200 ".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(200)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).isEmpty()
+ assertInputExhausted(input)
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(throwable = null, message = "'textstring' expected, but CR found instead")
+ )
+ }
+
+ @Test
+ fun `text containing non-ASCII character`() {
+ val input = "200 über".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = false)
+
+ assertThat(response.replyCode).isEqualTo(200)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("über")
+ assertInputExhausted(input)
+ assertThat(logger.logEntries).containsExactly(
+ LogEntry(throwable = null, message = "Text contains characters not allowed in 'textstring'")
+ )
+ }
+
+ @Test
+ fun `enhanced status code class does not match reply code`() {
+ val input = "250 5.0.0 text".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(250)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("5.0.0 text")
+ assertInputExhausted(input)
+ assertThat(logger.logEntries).hasSize(1)
+ logger.logEntries.first().let { logEntry ->
+ assertThat(logEntry.message).isEqualTo("Error parsing enhanced status code")
+ assertThat(logEntry.throwable?.message).isEqualTo("Reply code doesn't match status code class: 2 != 5")
+ }
+ }
+
+ @Test
+ fun `response with invalid enhanced status code subject`() {
+ val input = "250 2.1000.0 Text".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(250)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("2.1000.0 Text")
+ assertInputExhausted(input)
+ assertThat(logger.logEntries).hasSize(1)
+ logger.logEntries.first().let { logEntry ->
+ assertThat(logEntry.message).isEqualTo("Error parsing enhanced status code")
+ assertThat(logEntry.throwable?.message).isEqualTo("Unexpected character: 0 (48)")
+ }
+ }
+
+ @Test
+ fun `response with invalid enhanced status code detail`() {
+ val input = "250 2.0.1000 Text".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(250)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("2.0.1000 Text")
+ assertInputExhausted(input)
+ assertThat(logger.logEntries).hasSize(1)
+ logger.logEntries.first().let { logEntry ->
+ assertThat(logEntry.message).isEqualTo("Error parsing enhanced status code")
+ assertThat(logEntry.throwable?.message).isEqualTo("Unexpected character: 0 (48)")
+ }
+ }
+
+ @Test
+ fun `response with missing enhanced status code`() {
+ // Yahoo has been observed to send replies without enhanced status code even though the EHLO keyword is present
+ val input = "550 Request failed; Mailbox unavailable".toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(550)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("Request failed; Mailbox unavailable")
+ assertInputExhausted(input)
+ assertThat(logger.logEntries).hasSize(1)
+ logger.logEntries.first().let { logEntry ->
+ assertThat(logEntry.message).isEqualTo("Error parsing enhanced status code")
+ assertThat(logEntry.throwable?.message).isEqualTo("Unexpected character: R (82)")
+ }
+ }
+
+ @Test
+ fun `multi-line response with enhanced status code missing in last line`() {
+ val input = """
+ 550-5.2.1 Request failed
+ 550 Mailbox unavailable
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ assertFailsWithMessage(
+ "Multi-line response with enhanced status codes not matching: " +
+ "EnhancedStatusCode(statusClass=PERMANENT_FAILURE, subject=2, detail=1) != null"
+ ) {
+ parser.readResponse(enhancedStatusCodes = true)
+ }
+ }
+
+ @Test
+ fun `multi-line response with missing enhanced status code`() {
+ val input = """
+ 550-Request failed
+ 550 Mailbox unavailable
+ """.toPeekableInputStream()
+ val parser = SmtpResponseParser(logger, input)
+
+ val response = parser.readResponse(enhancedStatusCodes = true)
+
+ assertThat(response.replyCode).isEqualTo(550)
+ assertThat(response.enhancedStatusCode).isNull()
+ assertThat(response.texts).containsExactly("Request failed", "Mailbox unavailable")
+ assertInputExhausted(input)
+ }
+
+ private fun assertInputExhausted(input: PeekableInputStream) {
+ assertThat(input.read()).isEqualTo(-1)
+ }
+
+ private fun assertFailsWithMessage(expectedMessage: String, block: () -> Unit) {
+ try {
+ block()
+ fail("Expected SmtpResponseParserException")
+ } catch (e: SmtpResponseParserException) {
+ assertThat(e).hasMessageThat().isEqualTo(expectedMessage)
+ }
+ }
+
+ private fun String.toPeekableInputStream(): PeekableInputStream {
+ return PeekableInputStream((this.trimIndent().crlf() + "\r\n").byteInputStream())
+ }
+
+ private inline fun assertType(actual: Any, block: (T) -> Unit) {
+ assertThat(actual).isInstanceOf(T::class.java)
+ block(actual as T)
+ }
+}
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseTest.kt b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4cfc6ecf9553d9a638c4dd8538a628518144e042
--- /dev/null
+++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpResponseTest.kt
@@ -0,0 +1,172 @@
+package com.fsck.k9.mail.transport.smtp
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class SmtpResponseTest {
+ @Test
+ fun `log reply code only`() {
+ val response = SmtpResponse(
+ replyCode = 200,
+ enhancedStatusCode = null,
+ texts = emptyList()
+ )
+
+ val output = response.toLogString(omitText = false, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 200")
+ }
+
+ @Test
+ fun `log reply code only with omitText = true`() {
+ val response = SmtpResponse(
+ replyCode = 200,
+ enhancedStatusCode = null,
+ texts = emptyList()
+ )
+
+ val output = response.toLogString(omitText = true, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 200")
+ }
+
+ @Test
+ fun `log reply code and text`() {
+ val response = SmtpResponse(
+ replyCode = 200,
+ enhancedStatusCode = null,
+ texts = listOf("OK")
+ )
+
+ val output = response.toLogString(omitText = false, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 200 OK")
+ }
+
+ @Test
+ fun `log reply code and text with omitText = true`() {
+ val response = SmtpResponse(
+ replyCode = 250,
+ enhancedStatusCode = null,
+ texts = listOf("Sender OK")
+ )
+
+ val output = response.toLogString(omitText = true, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 250 [omitted]")
+ }
+
+ @Test
+ fun `log reply code and status code`() {
+ val response = SmtpResponse(
+ replyCode = 200,
+ enhancedStatusCode = EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 0, detail = 0),
+ texts = emptyList()
+ )
+
+ val output = response.toLogString(omitText = false, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 200 2.0.0")
+ }
+
+ @Test
+ fun `log reply code and status code with omitText = true`() {
+ val response = SmtpResponse(
+ replyCode = 200,
+ enhancedStatusCode = EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 0, detail = 0),
+ texts = emptyList()
+ )
+
+ val output = response.toLogString(omitText = true, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 200 2.0.0")
+ }
+
+ @Test
+ fun `log reply code, status code, and text`() {
+ val response = SmtpResponse(
+ replyCode = 200,
+ enhancedStatusCode = EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 0, detail = 0),
+ texts = listOf("OK")
+ )
+
+ val output = response.toLogString(omitText = false, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 200 2.0.0 OK")
+ }
+
+ @Test
+ fun `log reply code, status code, and text with omitText = true`() {
+ val response = SmtpResponse(
+ replyCode = 200,
+ enhancedStatusCode = EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 0, detail = 0),
+ texts = listOf("OK")
+ )
+
+ val output = response.toLogString(omitText = true, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 200 2.0.0 [omitted]")
+ }
+
+ @Test
+ fun `log reply code and multi-line text`() {
+ val response = SmtpResponse(
+ replyCode = 250,
+ enhancedStatusCode = null,
+ texts = listOf("Sender ", "OK")
+ )
+
+ val output = response.toLogString(omitText = false, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo(
+ """
+ SMTP <<< 250-Sender
+ SMTP <<< 250 OK
+ """.trimIndent()
+ )
+ }
+
+ @Test
+ fun `log reply code and multi-line text with omitText = true`() {
+ val response = SmtpResponse(
+ replyCode = 250,
+ enhancedStatusCode = null,
+ texts = listOf("Sender ", "OK")
+ )
+
+ val output = response.toLogString(omitText = true, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 250 [omitted]")
+ }
+
+ @Test
+ fun `log reply code, status code, and multi-line text`() {
+ val response = SmtpResponse(
+ replyCode = 250,
+ enhancedStatusCode = EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 1, detail = 0),
+ texts = listOf("Sender ", "OK")
+ )
+
+ val output = response.toLogString(omitText = false, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo(
+ """
+ SMTP <<< 250-2.1.0 Sender
+ SMTP <<< 250 2.1.0 OK
+ """.trimIndent()
+ )
+ }
+
+ @Test
+ fun `log reply code, status code, and multi-line text with omitText = true`() {
+ val response = SmtpResponse(
+ replyCode = 250,
+ enhancedStatusCode = EnhancedStatusCode(statusClass = StatusCodeClass.SUCCESS, subject = 1, detail = 0),
+ texts = listOf("Sender ", "OK")
+ )
+
+ val output = response.toLogString(omitText = true, linePrefix = "SMTP <<< ")
+
+ assertThat(output).isEqualTo("SMTP <<< 250 2.1.0 [omitted]")
+ }
+}
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.java b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.java
deleted file mode 100644
index d3a84953f87acc3d44ee7e274e035e6f2ca37e3f..0000000000000000000000000000000000000000
--- a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.java
+++ /dev/null
@@ -1,911 +0,0 @@
-package com.fsck.k9.mail.transport.smtp;
-
-
-import com.fsck.k9.mail.AuthType;
-import com.fsck.k9.mail.AuthenticationFailedException;
-import com.fsck.k9.mail.CertificateValidationException;
-import com.fsck.k9.mail.ConnectionSecurity;
-import com.fsck.k9.mail.K9LibRobolectricTestRunner;
-import com.fsck.k9.mail.Message;
-import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.ServerSettings;
-import com.fsck.k9.mail.XOAuth2ChallengeParserTest;
-import com.fsck.k9.mail.filter.Base64;
-import com.fsck.k9.mail.helpers.TestMessageBuilder;
-import com.fsck.k9.mail.helpers.TestTrustedSocketFactory;
-import com.fsck.k9.mail.internet.MimeMessage;
-import com.fsck.k9.mail.oauth.OAuth2TokenProvider;
-import com.fsck.k9.mail.oauth.authorizationserver.codegrantflow.OAuth2NeedUserPromptException;
-import com.fsck.k9.mail.ssl.TrustedSocketFactory;
-import com.fsck.k9.mail.transport.mockServer.MockSmtpServer;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-
-@RunWith(K9LibRobolectricTestRunner.class)
-public class SmtpTransportTest {
- private static final String USERNAME = "user";
- private static final String PASSWORD = "password";
- private static final String CLIENT_CERTIFICATE_ALIAS = null;
-
-
- private TrustedSocketFactory socketFactory;
- private OAuth2TokenProvider oAuth2TokenProvider;
-
-
- @Before
- public void before() throws AuthenticationFailedException, OAuth2NeedUserPromptException {
- socketFactory = TestTrustedSocketFactory.newInstance();
- oAuth2TokenProvider = mock(OAuth2TokenProvider.class);
- when(oAuth2TokenProvider.getToken(eq(USERNAME), anyLong()))
- .thenReturn("oldToken").thenReturn("newToken");
- }
-
- @Test
- public void open__shouldProvideHostname() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 OK");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE,
- null);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withoutAuthLoginExtension_shouldConnectWithoutAuthentication() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 OK");
- SmtpTransport transport = startServerAndCreateSmtpTransportWithoutPassword(server);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withAuthPlainExtension() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH PLAIN LOGIN");
- server.expect("AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withAuthLoginExtension() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH LOGIN");
- server.expect("AUTH LOGIN");
- server.output("250 OK");
- server.expect("dXNlcg==");
- server.output("250 OK");
- server.expect("cGFzc3dvcmQ=");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withoutLoginAndPlainAuthExtensions_shouldThrow() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH");
- server.expect("QUIT");
- server.output("221 BYE");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (MessagingException e) {
- assertEquals("Authentication methods SASL PLAIN and LOGIN are unavailable.", e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withCramMd5AuthExtension() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH CRAM-MD5");
- server.expect("AUTH CRAM-MD5");
- server.output(Base64.encode("<24609.1047914046@localhost>"));
- server.expect("dXNlciA3NmYxNWEzZmYwYTNiOGI1NzcxZmNhODZlNTcyMDk2Zg==");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.CRAM_MD5, ConnectionSecurity.NONE);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withoutCramMd5AuthExtension_shouldThrow() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH PLAIN LOGIN");
- server.expect("QUIT");
- server.output("221 BYE");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.CRAM_MD5, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (MessagingException e) {
- assertEquals("Authentication method CRAM-MD5 is unavailable.", e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withXoauth2Extension() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH XOAUTH2");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withXoauth2Extension_shouldThrowOn401Response() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH XOAUTH2");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("334 "+ XOAuth2ChallengeParserTest.STATUS_401_RESPONSE);
- server.expect("");
- server.output("535-5.7.1 Username and Password not accepted. Learn more at");
- server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68");
- server.expect("QUIT");
- server.output("221 BYE");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (AuthenticationFailedException e) {
- assertEquals(
- "5.7.1 Username and Password not accepted. Learn more at " +
- "5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68",
- e.getMessage());
- }
-
- InOrder inOrder = inOrder(oAuth2TokenProvider);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- inOrder.verify(oAuth2TokenProvider).invalidateToken(USERNAME);
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withXoauth2Extension_shouldInvalidateAndRetryOn400Response() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH XOAUTH2");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("334 "+ XOAuth2ChallengeParserTest.STATUS_400_RESPONSE);
- server.expect("");
- server.output("535-5.7.1 Username and Password not accepted. Learn more at");
- server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- transport.open();
-
- InOrder inOrder = inOrder(oAuth2TokenProvider);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- inOrder.verify(oAuth2TokenProvider).invalidateToken(USERNAME);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withXoauth2Extension_shouldInvalidateAndRetryOnInvalidJsonResponse() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH XOAUTH2");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("334 "+ XOAuth2ChallengeParserTest.INVALID_RESPONSE);
- server.expect("");
- server.output("535-5.7.1 Username and Password not accepted. Learn more at");
- server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- transport.open();
-
- InOrder inOrder = inOrder(oAuth2TokenProvider);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- inOrder.verify(oAuth2TokenProvider).invalidateToken(USERNAME);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withXoauth2Extension_shouldInvalidateAndRetryOnMissingStatusJsonResponse() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH XOAUTH2");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("334 "+ XOAuth2ChallengeParserTest.MISSING_STATUS_RESPONSE);
- server.expect("");
- server.output("535-5.7.1 Username and Password not accepted. Learn more at");
- server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- transport.open();
-
- InOrder inOrder = inOrder(oAuth2TokenProvider);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- inOrder.verify(oAuth2TokenProvider).invalidateToken(USERNAME);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withXoauth2Extension_shouldThrowOnMultipleFailure() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH XOAUTH2");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("334 " + XOAuth2ChallengeParserTest.STATUS_400_RESPONSE);
- server.expect("");
- server.output("535-5.7.1 Username and Password not accepted. Learn more at");
- server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=");
- server.output("334 " + XOAuth2ChallengeParserTest.STATUS_400_RESPONSE);
- server.expect("");
- server.output("535-5.7.1 Username and Password not accepted. Learn more at");
- server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68");
- server.expect("QUIT");
- server.output("221 BYE");
-
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (AuthenticationFailedException e) {
- assertEquals(
- "5.7.1 Username and Password not accepted. Learn more at " +
- "5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68",
- e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withXoauth2Extension_shouldThrowOnFailure_fetchingToken() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH XOAUTH2");
- server.expect("QUIT");
- server.output("221 BYE");
- when(oAuth2TokenProvider.getToken(anyString(), anyLong()))
- .thenThrow(new AuthenticationFailedException("Failed to fetch token"));
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (AuthenticationFailedException e) {
- assertEquals("Failed to fetch token", e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withoutXoauth2Extension_shouldThrow() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH PLAIN LOGIN");
- server.expect("QUIT");
- server.output("221 BYE");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (MessagingException e) {
- assertEquals("Authentication method XOAUTH2 is unavailable.", e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withAuthExternalExtension() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH EXTERNAL");
- server.expect("AUTH EXTERNAL dXNlcg==");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.EXTERNAL, ConnectionSecurity.NONE);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withoutAuthExternalExtension_shouldThrow() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH");
- server.expect("QUIT");
- server.output("221 BYE");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.EXTERNAL, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (CertificateValidationException e) {
- assertEquals(CertificateValidationException.Reason.MissingCapability, e.getReason());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withAutomaticAuthAndNoTransportSecurityAndAuthCramMd5Extension_shouldUseAuthCramMd5()
- throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH CRAM-MD5");
- server.expect("AUTH CRAM-MD5");
- server.output(Base64.encode("<24609.1047914046@localhost>"));
- server.expect("dXNlciA3NmYxNWEzZmYwYTNiOGI1NzcxZmNhODZlNTcyMDk2Zg==");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.AUTOMATIC,
- ConnectionSecurity.NONE);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withAutomaticAuthAndNoTransportSecurityAndAuthPlainExtension_shouldThrow() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250 AUTH PLAIN LOGIN");
- server.expect("QUIT");
- server.output("221 BYE");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.AUTOMATIC,
- ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (MessagingException e) {
- assertEquals("Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable.",
- e.getMessage());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withEhloFailing_shouldTryHelo() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("502 5.5.1, Unrecognized command.");
- server.expect("HELO [127.0.0.1]");
- server.output("250 localhost");
- SmtpTransport transport = startServerAndCreateSmtpTransportWithoutPassword(server);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withSupportWithEnhancedStatusCodesOnAuthFailure_shouldThrowEncodedMessage()
- throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
- server.output("250-ENHANCEDSTATUSCODES");
- server.output("250 AUTH XOAUTH2");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("334 " + XOAuth2ChallengeParserTest.STATUS_401_RESPONSE);
- server.expect("");
- server.output("535-5.7.1 Username and Password not accepted. Learn more at");
- server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68");
- server.expect("QUIT");
- server.output("221 BYE");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- try {
- transport.open();
- fail("Exception expected");
- } catch (AuthenticationFailedException e) {
- assertEquals(
- "Username and Password not accepted. " +
- "Learn more at http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68",
- e.getMessage());
- }
-
- InOrder inOrder = inOrder(oAuth2TokenProvider);
- inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyLong());
- inOrder.verify(oAuth2TokenProvider).invalidateToken(USERNAME);
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void open_withManyExtensions_shouldParseAll() throws Exception {
- MockSmtpServer server = new MockSmtpServer();
- server.output("220 smtp.gmail.com ESMTP x25sm19117693wrx.27 - gsmtp");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-smtp.gmail.com at your service, [86.147.34.216]");
- server.output("250-SIZE 35882577");
- server.output("250-8BITMIME");
- server.output("250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH");
- server.output("250-ENHANCEDSTATUSCODES");
- server.output("250-PIPELINING");
- server.output("250-CHUNKING");
- server.output("250 SMTPUTF8");
- server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=");
- server.output("235 2.7.0 Authentication successful");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE);
-
- transport.open();
-
- server.verifyConnectionStillOpen();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessage_withoutAddressToSendTo_shouldNotOpenConnection() throws Exception {
- MimeMessage message = new MimeMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- transport.sendMessage(message);
-
- server.verifyConnectionNeverCreated();
- }
-
- @Test
- public void sendMessage_withSingleRecipient() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication();
- server.expect("MAIL FROM:");
- server.output("250 OK");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.expect("DATA");
- server.output("354 End data with .");
- server.expect("[message data]");
- server.expect(".");
- server.output("250 OK: queued as 12345");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- transport.sendMessage(message);
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessage_with8BitEncoding() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("8BITMIME");
- server.expect("MAIL FROM: BODY=8BITMIME");
- server.output("250 OK");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.expect("DATA");
- server.output("354 End data with .");
- server.expect("[message data]");
- server.expect(".");
- server.output("250 OK: queued as 12345");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- transport.sendMessage(message);
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessage_with8BitEncodingExtensionNotCaseSensitive() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("8bitmime");
- server.expect("MAIL FROM: BODY=8BITMIME");
- server.output("250 OK");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.expect("DATA");
- server.output("354 End data with .");
- server.expect("[message data]");
- server.expect(".");
- server.output("250 OK: queued as 12345");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- transport.sendMessage(message);
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessage_withMessageTooLarge_shouldThrow() throws Exception {
- Message message = getDefaultMessageBuilder()
- .setHasAttachments(true)
- .messageSize(1234L)
- .build();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("SIZE 1000");
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- try {
- transport.sendMessage(message);
- fail("Expected message too large error");
- } catch (MessagingException e) {
- assertTrue(e.isPermanentFailure());
- assertEquals("Message too large for server", e.getMessage());
- }
-
- //FIXME: Make sure connection was closed
- //server.verifyConnectionClosed();
- }
-
- @Test
- public void sendMessage_withNegativeReply_shouldThrow() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication();
- server.expect("MAIL FROM:");
- server.output("250 OK");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.expect("DATA");
- server.output("354 End data with .");
- server.expect("[message data]");
- server.expect(".");
- server.output("421 4.7.0 Temporary system problem");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- try {
- transport.sendMessage(message);
- fail("Expected exception");
- } catch (NegativeSmtpReplyException e) {
- assertEquals(421, e.getReplyCode());
- assertEquals("4.7.0 Temporary system problem", e.getReplyText());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessage_withPipelining() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("PIPELINING");
- server.expect("MAIL FROM:");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.output("250 OK");
- server.expect("DATA");
- server.output("354 End data with .");
- server.expect("[message data]");
- server.expect(".");
- server.output("250 OK: queued as 12345");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- transport.sendMessage(message);
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessage_withoutPipelining() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication();
- server.expect("MAIL FROM:");
- server.output("250 OK");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.expect("DATA");
- server.output("354 End data with .");
- server.expect("[message data]");
- server.expect(".");
- server.output("250 OK: queued as 12345");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- transport.sendMessage(message);
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessagePipelining_withNegativeReply() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("PIPELINING");
- server.expect("MAIL FROM:");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.output("550 remote mail to not allowed");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- try {
- transport.sendMessage(message);
- fail("Expected exception");
- } catch (NegativeSmtpReplyException e) {
- assertEquals(550, e.getReplyCode());
- assertEquals("remote mail to not allowed", e.getReplyText());
- }
-
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessagePipelining_without354ReplyforData_shouldThrow() throws Exception {
- Message message = getDefaultMessage();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("PIPELINING");
- server.expect("MAIL FROM:");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.output("550 remote mail to not allowed");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- try {
- transport.sendMessage(message);
- fail("Expected exception");
- } catch (NegativeSmtpReplyException e) {
- assertEquals(550, e.getReplyCode());
- assertEquals("remote mail to not allowed", e.getReplyText());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessagePipelining_with250and550ReplyforRecipients_shouldThrowFirst() throws Exception {
- Message message = getMessageWithTwoRecipients();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("PIPELINING");
- server.expect("MAIL FROM:");
- server.expect("RCPT TO:");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.output("550 remote mail to not allowed");
- server.output("550 remote mail to not allowed");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- try {
- transport.sendMessage(message);
- fail("Expected exception");
- } catch (NegativeSmtpReplyException e) {
- assertEquals(550, e.getReplyCode());
- assertEquals("remote mail to not allowed", e.getReplyText());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
- @Test
- public void sendMessagePipelining_with250and550ReplyforRecipientsAnd250ForMessage_shouldThrow() throws Exception {
- Message message = getMessageWithTwoRecipients();
- MockSmtpServer server = createServerAndSetupForPlainAuthentication("PIPELINING");
- server.expect("MAIL FROM:");
- server.expect("RCPT TO:");
- server.expect("RCPT TO:");
- server.output("250 OK");
- server.output("250 OK");
- server.output("550 remote mail to not allowed");
- server.expect("QUIT");
- server.output("221 BYE");
- server.closeConnection();
- SmtpTransport transport = startServerAndCreateSmtpTransport(server);
-
- try {
- transport.sendMessage(message);
- fail("Expected exception");
- } catch (NegativeSmtpReplyException e) {
- assertEquals(550, e.getReplyCode());
- assertEquals("remote mail to not allowed", e.getReplyText());
- }
-
- server.verifyConnectionClosed();
- server.verifyInteractionCompleted();
- }
-
-
- private SmtpTransport startServerAndCreateSmtpTransport(MockSmtpServer server) throws Exception {
- return startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE);
- }
-
- private SmtpTransport startServerAndCreateSmtpTransportWithoutPassword(MockSmtpServer server) throws Exception {
- return startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE, null);
- }
-
- private SmtpTransport startServerAndCreateSmtpTransport(MockSmtpServer server, AuthType authenticationType,
- ConnectionSecurity connectionSecurity) throws Exception {
- return startServerAndCreateSmtpTransport(server, authenticationType, connectionSecurity, PASSWORD);
- }
-
- private SmtpTransport startServerAndCreateSmtpTransport(MockSmtpServer server, AuthType authenticationType,
- ConnectionSecurity connectionSecurity, String password)
- throws Exception {
- server.start();
-
- String host = server.getHost();
- int port = server.getPort();
- ServerSettings serverSettings = new ServerSettings(
- "smtp",
- host,
- port,
- connectionSecurity,
- authenticationType,
- USERNAME,
- password,
- CLIENT_CERTIFICATE_ALIAS);
-
- return new SmtpTransport(serverSettings, socketFactory, oAuth2TokenProvider);
- }
-
- private TestMessageBuilder getDefaultMessageBuilder() {
- return new TestMessageBuilder()
- .from("user@localhost")
- .to("user2@localhost");
- }
-
- private Message getDefaultMessage() {
- return getDefaultMessageBuilder().build();
- }
-
- private Message getMessageWithTwoRecipients() {
- return new TestMessageBuilder()
- .from("user@localhost")
- .to("user2@localhost", "user3@localhost")
- .build();
- }
-
- private MockSmtpServer createServerAndSetupForPlainAuthentication(String... extensions) {
- MockSmtpServer server = new MockSmtpServer();
-
- server.output("220 localhost Simple Mail Transfer Service Ready");
- server.expect("EHLO [127.0.0.1]");
- server.output("250-localhost Hello client.localhost");
-
- for (String extension : extensions) {
- server.output("250-" + extension);
- }
-
- server.output("250 AUTH LOGIN PLAIN CRAM-MD5");
- server.expect("AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=");
- server.output("235 2.7.0 Authentication successful");
-
- return server;
- }
-}
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.kt b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a0c135f6ff2a5b4f43c73457cd944130fa6e6389
--- /dev/null
+++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.kt
@@ -0,0 +1,990 @@
+package com.fsck.k9.mail.transport.smtp
+
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.AuthenticationFailedException
+import com.fsck.k9.mail.CertificateValidationException
+import com.fsck.k9.mail.ConnectionSecurity
+import com.fsck.k9.mail.Message
+import com.fsck.k9.mail.MessagingException
+import com.fsck.k9.mail.ServerSettings
+import com.fsck.k9.mail.XOAuth2ChallengeParserTest
+import com.fsck.k9.mail.filter.Base64
+import com.fsck.k9.mail.helpers.TestMessageBuilder
+import com.fsck.k9.mail.helpers.TestTrustedSocketFactory
+import com.fsck.k9.mail.internet.MimeMessage
+import com.fsck.k9.mail.oauth.OAuth2TokenProvider
+import com.fsck.k9.mail.transport.mockServer.MockSmtpServer
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.fail
+import org.junit.Test
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.inOrder
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stubbing
+
+private const val USERNAME = "user"
+private const val PASSWORD = "password"
+private val CLIENT_CERTIFICATE_ALIAS: String? = null
+
+class SmtpTransportTest {
+ private val socketFactory = TestTrustedSocketFactory.newInstance()
+ private val oAuth2TokenProvider = createMockOAuth2TokenProvider()
+
+ @Test
+ fun `open() should provide hostname`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 OK")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, password = null)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() without AUTH LOGIN extension should connect without authentication`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 OK")
+ }
+ val transport = startServerAndCreateSmtpTransportWithoutPassword(server)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with AUTH PLAIN extension`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH PLAIN LOGIN")
+ expect("AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.PLAIN)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with AUTH LOGIN extension`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH LOGIN")
+ expect("AUTH LOGIN")
+ output("250 OK")
+ expect("dXNlcg==")
+ output("250 OK")
+ expect("cGFzc3dvcmQ=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.PLAIN)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() without LOGIN and PLAIN AUTH extensions should throw`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH")
+ expect("QUIT")
+ output("221 BYE")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.PLAIN)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: MessagingException) {
+ assertThat(e).hasMessageThat().isEqualTo("Authentication methods SASL PLAIN and LOGIN are unavailable.")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with CRAM-MD5 AUTH extension`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH CRAM-MD5")
+ expect("AUTH CRAM-MD5")
+ output("334 " + Base64.encode("<24609.1047914046@localhost>"))
+ expect("dXNlciAyZDBlNTcwYzZlYWI0ZjY3ZDUyZmFkN2Q1NGExZDJhYQ==")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.CRAM_MD5)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() without CRAM-MD5 AUTH extension should throw`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH PLAIN LOGIN")
+ expect("QUIT")
+ output("221 BYE")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.CRAM_MD5)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: MessagingException) {
+ assertThat(e).hasMessageThat().isEqualTo("Authentication method CRAM-MD5 is unavailable.")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with OAUTHBEARER method`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH OAUTHBEARER")
+ expect("AUTH OAUTHBEARER bixhPXVzZXIsAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with OAUTHBEARER method when XOAUTH2 method is also available`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2 OAUTHBEARER")
+ expect("AUTH OAUTHBEARER bixhPXVzZXIsAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with XOAUTH2 extension`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with XOAUTH2 extension should throw on 401 response`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("334 " + XOAuth2ChallengeParserTest.STATUS_401_RESPONSE)
+ expect("")
+ output("535-5.7.1 Username and Password not accepted. Learn more at")
+ output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68")
+ expect("QUIT")
+ output("221 BYE")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: AuthenticationFailedException) {
+ assertThat(e).hasMessageThat().isEqualTo(
+ "5.7.1 Username and Password not accepted. Learn more at " +
+ "5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68"
+ )
+ }
+
+ inOrder(oAuth2TokenProvider) {
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ verify(oAuth2TokenProvider).invalidateToken()
+ }
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with XOAUTH2 extension should invalidate and retry on 400 response`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("334 " + XOAuth2ChallengeParserTest.STATUS_400_RESPONSE)
+ expect("")
+ output("535-5.7.1 Username and Password not accepted. Learn more at")
+ output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ transport.open()
+
+ inOrder(oAuth2TokenProvider) {
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ verify(oAuth2TokenProvider).invalidateToken()
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ }
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with XOAUTH2 extension should invalidate and retry on invalid JSON response`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("334 " + XOAuth2ChallengeParserTest.INVALID_RESPONSE)
+ expect("")
+ output("535-5.7.1 Username and Password not accepted. Learn more at")
+ output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ transport.open()
+
+ inOrder(oAuth2TokenProvider) {
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ verify(oAuth2TokenProvider).invalidateToken()
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ }
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with XOAUTH2 extension should invalidate and retry on missing status JSON response`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("334 " + XOAuth2ChallengeParserTest.MISSING_STATUS_RESPONSE)
+ expect("")
+ output("535-5.7.1 Username and Password not accepted. Learn more at")
+ output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ transport.open()
+
+ inOrder(oAuth2TokenProvider) {
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ verify(oAuth2TokenProvider).invalidateToken()
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ }
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with XOAUTH2 extension should throw on multiple failures`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("334 " + XOAuth2ChallengeParserTest.STATUS_400_RESPONSE)
+ expect("")
+ output("535-5.7.1 Username and Password not accepted. Learn more at")
+ output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE=")
+ output("334 " + XOAuth2ChallengeParserTest.STATUS_400_RESPONSE)
+ expect("")
+ output("535-5.7.1 Username and Password not accepted. Learn more at")
+ output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68")
+ expect("QUIT")
+ output("221 BYE")
+ }
+
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: AuthenticationFailedException) {
+ assertThat(e).hasMessageThat().isEqualTo(
+ "5.7.1 Username and Password not accepted. Learn more at " +
+ "5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68"
+ )
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with XOAUTH2 extension should throw on failure to fetch token`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH XOAUTH2")
+ expect("QUIT")
+ output("221 BYE")
+ }
+ stubbing(oAuth2TokenProvider) {
+ on { getToken(anyLong()) } doThrow AuthenticationFailedException("Failed to fetch token")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: AuthenticationFailedException) {
+ assertThat(e).hasMessageThat().isEqualTo("Failed to fetch token")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() without XOAUTH2 extension should throw`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH PLAIN LOGIN")
+ expect("QUIT")
+ output("221 BYE")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: MessagingException) {
+ assertThat(e).hasMessageThat().isEqualTo("Server doesn't support SASL OAUTHBEARER or XOAUTH2.")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with AUTH EXTERNAL extension`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH EXTERNAL")
+ expect("AUTH EXTERNAL dXNlcg==")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.EXTERNAL)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() without AUTH EXTERNAL extension should throw`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH")
+ expect("QUIT")
+ output("221 BYE")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.EXTERNAL)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: CertificateValidationException) {
+ assertThat(e.reason).isEqualTo(CertificateValidationException.Reason.MissingCapability)
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with automatic auth and no transport security and AUTH CRAM-MD5 extension should use CRAM-MD5`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+ output("250 AUTH CRAM-MD5")
+ expect("AUTH CRAM-MD5")
+ output("334 " + Base64.encode("<24609.1047914046@localhost>"))
+ expect("dXNlciAyZDBlNTcwYzZlYWI0ZjY3ZDUyZmFkN2Q1NGExZDJhYQ==")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(
+ server,
+ authenticationType = AuthType.AUTOMATIC,
+ connectionSecurity = ConnectionSecurity.NONE
+ )
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with automatic auth and no transport security and AUTH PLAIN extension should throw`() {
+ val server = MockSmtpServer()
+ server.output("220 localhost Simple Mail Transfer Service Ready")
+ server.expect("EHLO [127.0.0.1]")
+ server.output("250-localhost Hello client.localhost")
+ server.output("250 AUTH PLAIN LOGIN")
+ server.expect("QUIT")
+ server.output("221 BYE")
+ val transport = startServerAndCreateSmtpTransport(
+ server,
+ authenticationType = AuthType.AUTOMATIC,
+ connectionSecurity = ConnectionSecurity.NONE
+ )
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: MessagingException) {
+ assertThat(e).hasMessageThat().isEqualTo(
+ "Update your outgoing server authentication setting. AUTOMATIC authentication is unavailable."
+ )
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with EHLO failing should try HELO`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("502 5.5.1, Unrecognized command.")
+ expect("HELO [127.0.0.1]")
+ output("250 localhost")
+ }
+ val transport = startServerAndCreateSmtpTransportWithoutPassword(server)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with support for ENHANCEDSTATUSCODES should throw strip enhanced status codes from error message`() {
+ val server = MockSmtpServer()
+ server.output("220 localhost Simple Mail Transfer Service Ready")
+ server.expect("EHLO [127.0.0.1]")
+ server.output("250-localhost Hello client.localhost")
+ server.output("250-ENHANCEDSTATUSCODES")
+ server.output("250 AUTH XOAUTH2")
+ server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ server.output("334 " + XOAuth2ChallengeParserTest.STATUS_401_RESPONSE)
+ server.expect("")
+ server.output("535-5.7.1 Username and Password not accepted. Learn more at")
+ server.output("535 5.7.1 http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68")
+ server.expect("QUIT")
+ server.output("221 BYE")
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ try {
+ transport.open()
+ fail("Exception expected")
+ } catch (e: AuthenticationFailedException) {
+ assertThat(e).hasMessageThat().isEqualTo(
+ "Username and Password not accepted. " +
+ "Learn more at http://support.google.com/mail/bin/answer.py?answer=14257 hx9sm5317360pbc.68"
+ )
+ }
+
+ inOrder(oAuth2TokenProvider) {
+ verify(oAuth2TokenProvider).getToken(anyLong())
+ verify(oAuth2TokenProvider).invalidateToken()
+ }
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with many extensions should parse all`() {
+ val server = MockSmtpServer().apply {
+ output("220 smtp.gmail.com ESMTP x25sm19117693wrx.27 - gsmtp")
+ expect("EHLO [127.0.0.1]")
+ output("250-smtp.gmail.com at your service, [86.147.34.216]")
+ output("250-SIZE 35882577")
+ output("250-8BITMIME")
+ output("250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN XOAUTH")
+ output("250-ENHANCEDSTATUSCODES")
+ output("250-PIPELINING")
+ output("250-CHUNKING")
+ output("250 SMTPUTF8")
+ expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(server, authenticationType = AuthType.XOAUTH2)
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `open() with STARTTLS`() {
+ val server = MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello 127.0.0.1")
+ output("250-STARTTLS")
+ output("250 HELP")
+ expect("STARTTLS")
+ output("220 Ready to start TLS")
+ startTls()
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello 127.0.0.1")
+ output("250 AUTH PLAIN LOGIN")
+ expect("AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=")
+ output("235 2.7.0 Authentication successful")
+ }
+ val transport = startServerAndCreateSmtpTransport(
+ server,
+ authenticationType = AuthType.PLAIN,
+ connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED
+ )
+
+ transport.open()
+
+ server.verifyConnectionStillOpen()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() without address to send to should not open connection`() {
+ val message = MimeMessage()
+ val server = createServerAndSetupForPlainAuthentication()
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ transport.sendMessage(message)
+
+ server.verifyConnectionNeverCreated()
+ }
+
+ @Test
+ fun `sendMessage() with single recipient`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication().apply {
+ expect("MAIL FROM:")
+ output("250 OK")
+ expect("RCPT TO:")
+ output("250 OK")
+ expect("DATA")
+ output("354 End data with .")
+ expect("[message data]")
+ expect(".")
+ output("250 OK: queued as 12345")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ transport.sendMessage(message)
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with 8-bit encoding`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication("8BITMIME").apply {
+ expect("MAIL FROM: BODY=8BITMIME")
+ output("250 OK")
+ expect("RCPT TO:")
+ output("250 OK")
+ expect("DATA")
+ output("354 End data with .")
+ expect("[message data]")
+ expect(".")
+ output("250 OK: queued as 12345")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ transport.sendMessage(message)
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with 8-bit encoding extension not case-sensitive`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication("8bitmime").apply {
+ expect("MAIL FROM: BODY=8BITMIME")
+ output("250 OK")
+ expect("RCPT TO:")
+ output("250 OK")
+ expect("DATA")
+ output("354 End data with .")
+ expect("[message data]")
+ expect(".")
+ output("250 OK: queued as 12345")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ transport.sendMessage(message)
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with message too large should throw`() {
+ val message = createDefaultMessageBuilder()
+ .setHasAttachments(true)
+ .messageSize(1234L)
+ .build()
+ val server = createServerAndSetupForPlainAuthentication("SIZE 1000")
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ try {
+ transport.sendMessage(message)
+ fail("Expected message too large error")
+ } catch (e: MessagingException) {
+ assertThat(e.isPermanentFailure).isTrue()
+ assertThat(e).hasMessageThat().isEqualTo("Message too large for server")
+ }
+
+ // FIXME: Make sure connection was closed
+ // server.verifyConnectionClosed();
+ }
+
+ @Test
+ fun `sendMessage() with negative reply should throw`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication().apply {
+ expect("MAIL FROM:")
+ output("250 OK")
+ expect("RCPT TO:")
+ output("250 OK")
+ expect("DATA")
+ output("354 End data with .")
+ expect("[message data]")
+ expect(".")
+ output("421 4.7.0 Temporary system problem")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ try {
+ transport.sendMessage(message)
+ fail("Expected exception")
+ } catch (e: NegativeSmtpReplyException) {
+ assertThat(e.replyCode).isEqualTo(421)
+ assertThat(e.replyText).isEqualTo("4.7.0 Temporary system problem")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with pipelining`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication("PIPELINING").apply {
+ expect("MAIL FROM:")
+ expect("RCPT TO:")
+ output("250 OK")
+ output("250 OK")
+ expect("DATA")
+ output("354 End data with .")
+ expect("[message data]")
+ expect(".")
+ output("250 OK: queued as 12345")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ transport.sendMessage(message)
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() without pipelining`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication().apply {
+ expect("MAIL FROM:")
+ output("250 OK")
+ expect("RCPT TO:")
+ output("250 OK")
+ expect("DATA")
+ output("354 End data with .")
+ expect("[message data]")
+ expect(".")
+ output("250 OK: queued as 12345")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ transport.sendMessage(message)
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with pipelining and negative reply`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication("PIPELINING").apply {
+ expect("MAIL FROM:")
+ expect("RCPT TO:")
+ output("250 OK")
+ output("550 remote mail to not allowed")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ try {
+ transport.sendMessage(message)
+ fail("Expected exception")
+ } catch (e: NegativeSmtpReplyException) {
+ assertThat(e.replyCode).isEqualTo(550)
+ assertThat(e.replyText).isEqualTo("remote mail to not allowed")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with pipelining and missing 354 reply for DATA should throw`() {
+ val message = createDefaultMessage()
+ val server = createServerAndSetupForPlainAuthentication("PIPELINING")
+ server.expect("MAIL FROM:")
+ server.expect("RCPT TO:")
+ server.output("250 OK")
+ server.output("550 remote mail to not allowed")
+ server.expect("QUIT")
+ server.output("221 BYE")
+ server.closeConnection()
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ try {
+ transport.sendMessage(message)
+ fail("Expected exception")
+ } catch (e: NegativeSmtpReplyException) {
+ assertThat(e.replyCode).isEqualTo(550)
+ assertThat(e.replyText).isEqualTo("remote mail to not allowed")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with pipelining and two 550 replies for recipients should include first error in exception`() {
+ val message = createMessageWithTwoRecipients()
+ val server = createServerAndSetupForPlainAuthentication("PIPELINING").apply {
+ expect("MAIL FROM:")
+ expect("RCPT TO:")
+ expect("RCPT TO:")
+ output("250 OK")
+ output("550 remote mail to not allowed")
+ output("550 remote mail to not allowed")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ try {
+ transport.sendMessage(message)
+ fail("Expected exception")
+ } catch (e: NegativeSmtpReplyException) {
+ assertThat(e.replyCode).isEqualTo(550)
+ assertThat(e.replyText).isEqualTo("remote mail to not allowed")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ @Test
+ fun `sendMessage() with pipelining and both 250 and 550 response for recipients should throw`() {
+ val message = createMessageWithTwoRecipients()
+ val server = createServerAndSetupForPlainAuthentication("PIPELINING").apply {
+ expect("MAIL FROM:")
+ expect("RCPT TO:")
+ expect("RCPT TO:")
+ output("250 OK")
+ output("250 OK")
+ output("550 remote mail to not allowed")
+ expect("QUIT")
+ output("221 BYE")
+ closeConnection()
+ }
+ val transport = startServerAndCreateSmtpTransport(server)
+
+ try {
+ transport.sendMessage(message)
+ fail("Expected exception")
+ } catch (e: NegativeSmtpReplyException) {
+ assertThat(e.replyCode).isEqualTo(550)
+ assertThat(e.replyText).isEqualTo("remote mail to not allowed")
+ }
+
+ server.verifyConnectionClosed()
+ server.verifyInteractionCompleted()
+ }
+
+ private fun startServerAndCreateSmtpTransportWithoutPassword(server: MockSmtpServer): SmtpTransport {
+ return startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE, null)
+ }
+
+ private fun startServerAndCreateSmtpTransport(
+ server: MockSmtpServer,
+ authenticationType: AuthType = AuthType.PLAIN,
+ connectionSecurity: ConnectionSecurity = ConnectionSecurity.NONE,
+ password: String? = PASSWORD
+ ): SmtpTransport {
+ server.start()
+ val host = server.host
+ val port = server.port
+ val serverSettings = ServerSettings(
+ "smtp",
+ host,
+ port,
+ connectionSecurity,
+ authenticationType,
+ USERNAME,
+ password,
+ CLIENT_CERTIFICATE_ALIAS
+ )
+
+ return SmtpTransport(serverSettings, socketFactory, oAuth2TokenProvider)
+ }
+
+ private fun createDefaultMessageBuilder(): TestMessageBuilder {
+ return TestMessageBuilder()
+ .from("user@localhost")
+ .to("user2@localhost")
+ }
+
+ private fun createDefaultMessage(): Message {
+ return createDefaultMessageBuilder().build()
+ }
+
+ private fun createMessageWithTwoRecipients(): Message {
+ return TestMessageBuilder()
+ .from("user@localhost")
+ .to("user2@localhost", "user3@localhost")
+ .build()
+ }
+
+ private fun createServerAndSetupForPlainAuthentication(vararg extensions: String): MockSmtpServer {
+ return MockSmtpServer().apply {
+ output("220 localhost Simple Mail Transfer Service Ready")
+ expect("EHLO [127.0.0.1]")
+ output("250-localhost Hello client.localhost")
+
+ for (extension in extensions) {
+ output("250-$extension")
+ }
+
+ output("250 AUTH LOGIN PLAIN CRAM-MD5")
+ expect("AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=")
+ output("235 2.7.0 Authentication successful")
+ }
+ }
+
+ private fun createMockOAuth2TokenProvider(): OAuth2TokenProvider {
+ return mock {
+ on { getToken(anyLong()) } doReturn "oldToken" doReturn "newToken"
+ }
+ }
+}
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/TestSmtpLogger.kt b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/TestSmtpLogger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ca3e263166551bb6906651ee21f1b74e496808c1
--- /dev/null
+++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/TestSmtpLogger.kt
@@ -0,0 +1,15 @@
+package com.fsck.k9.mail.transport.smtp
+
+class TestSmtpLogger(override val isRawProtocolLoggingEnabled: Boolean = true) : SmtpLogger {
+ val logEntries = mutableListOf()
+
+ override fun log(throwable: Throwable?, message: String, vararg args: Any?) {
+ val formattedMessage = String.format(message, *args)
+ logEntries.add(LogEntry(throwable, formattedMessage))
+ }
+}
+
+data class LogEntry(
+ val throwable: Throwable?,
+ val message: String
+)
diff --git a/mail/protocols/webdav/build.gradle b/mail/protocols/webdav/build.gradle
index 59634c5a6dac807f8b2947ad3f44911331e99450..f7af9dcd2062cb6f82f4c9f63cfc7eb364608827 100644
--- a/mail/protocols/webdav/build.gradle
+++ b/mail/protocols/webdav/build.gradle
@@ -1,51 +1,23 @@
-apply plugin: 'com.android.library'
+apply plugin: 'java-library'
if (rootProject.testCoverage) {
apply plugin: 'jacoco'
}
+java {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+}
+
dependencies {
api project(":mail:common")
implementation "commons-io:commons-io:${versions.commonsIo}"
- implementation "com.jakewharton.timber:timber:${versions.timber}"
- implementation "androidx.annotation:annotation:${versions.androidxAnnotation}"
+ compileOnly "org.apache.httpcomponents:httpclient:4.5.5"
testImplementation project(":mail:testing")
- testImplementation "org.robolectric:robolectric:${versions.robolectric}"
testImplementation "junit:junit:${versions.junit}"
testImplementation "com.google.truth:truth:${versions.truth}"
- testImplementation "org.mockito:mockito-core:${versions.mockito}"
-
- // The Android Gradle plugin doesn't seem to put the Apache HTTP Client on the runtime classpath anymore when
- // running JVM tests.
+ testImplementation "org.mockito:mockito-inline:${versions.mockito}"
testImplementation "org.apache.httpcomponents:httpclient:4.5.5"
}
-
-android {
- compileSdkVersion buildConfig.compileSdk
- buildToolsVersion buildConfig.buildTools
-
- defaultConfig {
- minSdkVersion buildConfig.minSdk
- }
-
- // for using Apache HTTP Client
- useLibrary 'org.apache.http.legacy'
-
- buildTypes {
- debug {
- testCoverageEnabled rootProject.testCoverage
- }
- }
-
- lintOptions {
- abortOnError false
- lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
- }
-
- compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
- }
-}
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/DataSet.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/DataSet.java
index 1ca8179ee32927faa5a1c704e5fff7de0a2ea76b..c1d949fa140c612e95f66f44a8e9ad334300eb03 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/DataSet.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/DataSet.java
@@ -10,7 +10,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
-import timber.log.Timber;
+import com.fsck.k9.logging.Timber;
+
/**
* Maintains WebDAV data
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/HttpGeneric.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/HttpGeneric.java
index 008b2aa4325bd276aa3134938ffd7d14cb57c552..09fa2f1652b72c5de8f85e9b58efb515231f4321 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/HttpGeneric.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/HttpGeneric.java
@@ -1,9 +1,9 @@
package com.fsck.k9.mail.store.webdav;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.K9MailLib;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
-import timber.log.Timber;
import java.net.URI;
@@ -80,4 +80,4 @@ public class HttpGeneric extends HttpEntityEnclosingRequestBase {
METHOD_NAME = method;
}
}
-}
\ No newline at end of file
+}
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/SniHostSetter.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/SniHostSetter.java
new file mode 100644
index 0000000000000000000000000000000000000000..df323946ac2b1352e568341fb4df42d9371a8369
--- /dev/null
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/SniHostSetter.java
@@ -0,0 +1,10 @@
+package com.fsck.k9.mail.store.webdav;
+
+
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+
+public interface SniHostSetter {
+ void setSniHost(SSLSocketFactory factory, SSLSocket socket, String hostname);
+}
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavFolder.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavFolder.java
index ddf5adf28d4b2edeb169c7fa41c6ae623b711b6a..6fde0f2fde9b25051972e01f08bb46fa72fbd88a 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavFolder.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavFolder.java
@@ -1,5 +1,6 @@
package com.fsck.k9.mail.store.webdav;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.FolderType;
@@ -14,7 +15,6 @@ import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.StringEntity;
-import timber.log.Timber;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavHttpClient.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavHttpClient.java
index 05bda8a81e78032072778e3d10e085b0bb4edefe..47b424ea438a3769e0555c16e3b7bb18de0b8544 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavHttpClient.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavHttpClient.java
@@ -1,5 +1,6 @@
package com.fsck.k9.mail.store.webdav;
+import com.fsck.k9.logging.Timber;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
@@ -7,7 +8,6 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HttpContext;
-import timber.log.Timber;
import java.io.IOException;
import java.io.InputStream;
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavMessage.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavMessage.java
index ffbbcbc0fb6574fd12bc3d4daee37b5cd2c9f959..42f4c5bf41ed4dead302407e03cb94626a915f72 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavMessage.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavMessage.java
@@ -1,13 +1,12 @@
package com.fsck.k9.mail.store.webdav;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeMessage;
import java.util.Locale;
import java.util.Map;
-import timber.log.Timber;
-
import static com.fsck.k9.mail.helper.UrlEncodingHelper.decodeUtf8;
import static com.fsck.k9.mail.helper.UrlEncodingHelper.encodeUtf8;
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavSocketFactory.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavSocketFactory.java
index 20ee771fd50320508a05770a78663b6163d840a9..36d7abd8ffad02616938c23bd1276851c2fd7d29 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavSocketFactory.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavSocketFactory.java
@@ -7,7 +7,6 @@ import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
-import com.fsck.k9.mail.ssl.DefaultTrustedSocketFactory;
import com.fsck.k9.mail.ssl.TrustManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
@@ -22,10 +21,13 @@ import org.apache.http.params.HttpParams;
* Using two socket factories looks suspicious.
*/
public class WebDavSocketFactory implements LayeredSocketFactory {
+ private final SniHostSetter sniHostSetter;
private SSLSocketFactory mSocketFactory;
private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory;
- public WebDavSocketFactory(TrustManagerFactory trustManagerFactory, String host, int port) throws NoSuchAlgorithmException, KeyManagementException {
+ public WebDavSocketFactory(TrustManagerFactory trustManagerFactory, SniHostSetter sniHostSetter,
+ String host, int port) throws NoSuchAlgorithmException, KeyManagementException {
+ this.sniHostSetter = sniHostSetter;
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] {
trustManagerFactory.getTrustManagerForDomain(host, port)
@@ -62,7 +64,7 @@ public class WebDavSocketFactory implements LayeredSocketFactory {
port,
autoClose
);
- DefaultTrustedSocketFactory.setSniHost(mSocketFactory, sslSocket, host);
+ sniHostSetter.setSniHost(mSocketFactory, sslSocket, host);
//hostnameVerifier.verify(host, sslSocket);
// verifyHostName() didn't blowup - good!
return sslSocket;
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavStore.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavStore.java
index bad815cca93835d28db33dbf6d18b98da9162fcd..7a6fdada9518400921b805d57ce44698152abc7c 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavStore.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/store/webdav/WebDavStore.java
@@ -16,6 +16,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.FolderType;
@@ -46,7 +47,6 @@ import org.apache.http.protocol.HttpContext;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
-import timber.log.Timber;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_WEBDAV;
import static com.fsck.k9.mail.helper.UrlEncodingHelper.decodeUtf8;
@@ -73,6 +73,7 @@ public class WebDavStore {
private String mailboxPath;
private final TrustManagerFactory trustManagerFactory;
+ private final SniHostSetter sniHostSetter;
private final WebDavHttpClient.WebDavHttpClientFactory httpClientFactory;
private WebDavHttpClient httpClient = null;
private HttpContext httpContext = null;
@@ -84,16 +85,19 @@ public class WebDavStore {
private WebDavFolder sendFolder = null;
private Map folderList = new HashMap<>();
- public WebDavStore(TrustManagerFactory trustManagerFactory, ServerSettings serverSettings,
- DraftsFolderProvider draftsFolderProvider) {
- this(trustManagerFactory, serverSettings, draftsFolderProvider, new WebDavHttpClient.WebDavHttpClientFactory());
+ public WebDavStore(TrustManagerFactory trustManagerFactory, SniHostSetter sniHostSetter,
+ ServerSettings serverSettings, DraftsFolderProvider draftsFolderProvider) {
+ this(trustManagerFactory, sniHostSetter, serverSettings, draftsFolderProvider,
+ new WebDavHttpClient.WebDavHttpClientFactory());
}
- public WebDavStore(TrustManagerFactory trustManagerFactory, ServerSettings serverSettings,
- DraftsFolderProvider draftsFolderProvider, WebDavHttpClientFactory clientFactory) {
+ public WebDavStore(TrustManagerFactory trustManagerFactory, SniHostSetter sniHostSetter,
+ ServerSettings serverSettings, DraftsFolderProvider draftsFolderProvider,
+ WebDavHttpClientFactory clientFactory) {
this.draftsFolderProvider = draftsFolderProvider;
httpClientFactory = clientFactory;
this.trustManagerFactory = trustManagerFactory;
+ this.sniHostSetter = sniHostSetter;
hostname = serverSettings.host;
port = serverSettings.port;
@@ -765,7 +769,9 @@ public class WebDavStore {
SchemeRegistry reg = httpClient.getConnectionManager().getSchemeRegistry();
try {
- Scheme s = new Scheme("https", new WebDavSocketFactory(trustManagerFactory, hostname, 443), 443);
+ WebDavSocketFactory socketFactory =
+ new WebDavSocketFactory(trustManagerFactory, sniHostSetter, hostname, 443);
+ Scheme s = new Scheme("https", socketFactory, 443);
reg.register(s);
} catch (NoSuchAlgorithmException nsa) {
Timber.e(nsa, "NoSuchAlgorithmException in getHttpClient");
diff --git a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/transport/WebDavTransport.java b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/transport/WebDavTransport.java
index a9e8cac0ac88a32c974bb34bb46088a463703557..eddf97680d73e9c31a858bb53618cd53b443330d 100644
--- a/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/transport/WebDavTransport.java
+++ b/mail/protocols/webdav/src/main/java/com/fsck/k9/mail/transport/WebDavTransport.java
@@ -3,6 +3,7 @@ package com.fsck.k9.mail.transport;
import java.util.Collections;
+import com.fsck.k9.logging.Timber;
import com.fsck.k9.mail.K9MailLib;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
@@ -10,15 +11,15 @@ import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.ssl.TrustManagerFactory;
import com.fsck.k9.mail.store.webdav.DraftsFolderProvider;
+import com.fsck.k9.mail.store.webdav.SniHostSetter;
import com.fsck.k9.mail.store.webdav.WebDavStore;
-import timber.log.Timber;
public class WebDavTransport extends Transport {
private WebDavStore store;
- public WebDavTransport(TrustManagerFactory trustManagerFactory, ServerSettings serverSettings,
- DraftsFolderProvider draftsFolderProvider) {
- store = new WebDavStore(trustManagerFactory, serverSettings, draftsFolderProvider);
+ public WebDavTransport(TrustManagerFactory trustManagerFactory, SniHostSetter sniHostSetter,
+ ServerSettings serverSettings, DraftsFolderProvider draftsFolderProvider) {
+ store = new WebDavStore(trustManagerFactory, sniHostSetter, serverSettings, draftsFolderProvider);
if (K9MailLib.isDebug())
Timber.d(">>> New WebDavTransport creation complete");
diff --git a/mail/protocols/webdav/src/test/java/com/fsck/k9/mail/store/webdav/WebDavStoreTest.java b/mail/protocols/webdav/src/test/java/com/fsck/k9/mail/store/webdav/WebDavStoreTest.java
index 958ee1f1599f1258681690e71175db763f9162e4..0c1b464d2c2c2a52667dbf930e23ea9d5105b18f 100644
--- a/mail/protocols/webdav/src/test/java/com/fsck/k9/mail/store/webdav/WebDavStoreTest.java
+++ b/mail/protocols/webdav/src/test/java/com/fsck/k9/mail/store/webdav/WebDavStoreTest.java
@@ -12,12 +12,10 @@ import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.FolderType;
-import com.fsck.k9.mail.K9LibRobolectricTestRunner;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.ssl.TrustManagerFactory;
-
import javax.net.ssl.SSLException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@@ -37,7 +35,6 @@ import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -52,7 +49,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-@RunWith(K9LibRobolectricTestRunner.class)
public class WebDavStoreTest {
private static final HttpResponse OK_200_RESPONSE = createOkResponse();
private static final HttpResponse UNAUTHORIZED_401_RESPONSE = createResponse(401);
@@ -70,6 +66,8 @@ public class WebDavStoreTest {
@Mock
private TrustManagerFactory trustManagerFactory;
@Mock
+ private SniHostSetter sniHostSetter;
+ @Mock
private DraftsFolderProvider draftsFolderProvider;
private ArgumentCaptor requestCaptor;
@@ -352,12 +350,14 @@ public class WebDavStoreTest {
}
private WebDavStore createWebDavStore() {
- return new WebDavStore(trustManagerFactory, serverSettings, draftsFolderProvider, mockHttpClientFactory);
+ return new WebDavStore(trustManagerFactory, sniHostSetter, serverSettings, draftsFolderProvider,
+ mockHttpClientFactory);
}
private WebDavStore createWebDavStore(ConnectionSecurity connectionSecurity) {
ServerSettings serverSettings = createServerSettings(connectionSecurity);
- return new WebDavStore(trustManagerFactory, serverSettings, draftsFolderProvider, mockHttpClientFactory);
+ return new WebDavStore(trustManagerFactory, sniHostSetter, serverSettings, draftsFolderProvider,
+ mockHttpClientFactory);
}
private void configureHttpResponses(HttpResponse... responses) throws IOException {
diff --git a/mail/testing/build.gradle b/mail/testing/build.gradle
index 7f6ed262b72eeb48605155e5906910b30cc983f8..c1d44d29a55c2d773faae4c141c536eefe5a776c 100644
--- a/mail/testing/build.gradle
+++ b/mail/testing/build.gradle
@@ -1,32 +1,18 @@
-apply plugin: 'com.android.library'
-apply plugin: 'org.jetbrains.kotlin.android'
+apply plugin: 'java-library'
+apply plugin: 'kotlin'
+
+if (rootProject.testCoverage) {
+ apply plugin: 'jacoco'
+}
+
+java {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+}
dependencies {
api project(":mail:common")
api "com.squareup.okio:okio:${versions.okio}"
- api "org.robolectric:robolectric:${versions.robolectric}"
api "junit:junit:${versions.junit}"
}
-
-android {
- compileSdkVersion buildConfig.compileSdk
- buildToolsVersion buildConfig.buildTools
-
- defaultConfig {
- minSdkVersion buildConfig.minSdk
- }
-
- lintOptions {
- abortOnError false
- }
-
- compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
- }
-
- kotlinOptions {
- jvmTarget = kotlinJvmVersion
- }
-}
diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/K9LibRobolectricTestRunner.java b/mail/testing/src/main/java/com/fsck/k9/mail/K9LibRobolectricTestRunner.java
deleted file mode 100644
index 561c594e87727cbfe6ffc1b5d08b8d89889bd276..0000000000000000000000000000000000000000
--- a/mail/testing/src/main/java/com/fsck/k9/mail/K9LibRobolectricTestRunner.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.fsck.k9.mail;
-
-import org.junit.runners.model.InitializationError;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-public class K9LibRobolectricTestRunner extends RobolectricTestRunner {
-
- public K9LibRobolectricTestRunner(Class> testClass) throws InitializationError {
- super(testClass);
- }
-
- @Override
- protected Config buildGlobalConfig() {
- return new Config.Builder()
- .setSdk(22)
- .setManifest(Config.NONE)
- .build();
- }
-}
\ No newline at end of file
diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/SystemOutLogger.kt b/mail/testing/src/main/java/com/fsck/k9/mail/SystemOutLogger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d0e0c1562dfefcf6eaa6ffbfa700c93da1438b57
--- /dev/null
+++ b/mail/testing/src/main/java/com/fsck/k9/mail/SystemOutLogger.kt
@@ -0,0 +1,70 @@
+package com.fsck.k9.mail
+
+import com.fsck.k9.logging.Logger
+
+class SystemOutLogger : Logger {
+ override fun v(message: String?, vararg args: Any?) {
+ System.out.printf("V/ ${message.orEmpty()}\n", *args)
+ }
+
+ override fun v(t: Throwable?, message: String?, vararg args: Any?) {
+ t?.printStackTrace(System.out)
+ v(message, *args)
+ }
+
+ override fun v(t: Throwable?) {
+ t?.printStackTrace(System.out)
+ }
+
+ override fun d(message: String?, vararg args: Any?) {
+ System.out.printf("D/ ${message.orEmpty()}\n", *args)
+ }
+
+ override fun d(t: Throwable?, message: String?, vararg args: Any?) {
+ t?.printStackTrace(System.out)
+ d(message, *args)
+ }
+
+ override fun d(t: Throwable?) {
+ t?.printStackTrace(System.out)
+ }
+
+ override fun i(message: String?, vararg args: Any?) {
+ System.out.printf("I/ ${message.orEmpty()}\n", *args)
+ }
+
+ override fun i(t: Throwable?, message: String?, vararg args: Any?) {
+ t?.printStackTrace(System.out)
+ i(message, *args)
+ }
+
+ override fun i(t: Throwable?) {
+ t?.printStackTrace(System.out)
+ }
+
+ override fun w(message: String?, vararg args: Any?) {
+ System.out.printf("W/ ${message.orEmpty()}\n", *args)
+ }
+
+ override fun w(t: Throwable?, message: String?, vararg args: Any?) {
+ t?.printStackTrace(System.out)
+ w(message, *args)
+ }
+
+ override fun w(t: Throwable?) {
+ t?.printStackTrace(System.out)
+ }
+
+ override fun e(message: String?, vararg args: Any?) {
+ System.out.printf("E/ ${message.orEmpty()}\n", *args)
+ }
+
+ override fun e(t: Throwable?, message: String?, vararg args: Any?) {
+ t?.printStackTrace(System.out)
+ e(message, *args)
+ }
+
+ override fun e(t: Throwable?) {
+ t?.printStackTrace(System.out)
+ }
+}
diff --git a/mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java
index a455d55326de3a5b625e7ff1f12a38ce627825be..843358a90dafea26cfa9f306e4633e104bc1a304 100644
--- a/mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java
+++ b/mail/testing/src/main/java/com/fsck/k9/mail/helpers/VeryTrustingTrustManager.java
@@ -1,6 +1,5 @@
package com.fsck.k9.mail.helpers;
-import android.annotation.SuppressLint;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -8,7 +7,6 @@ import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
-@SuppressLint("TrustAllX509TrustManager")
class VeryTrustingTrustManager implements X509TrustManager {
private final X509Certificate serverCertificate;