From 705ab4c9c65cf83400eb9c775f35f6d1903a1afa Mon Sep 17 00:00:00 2001 From: Den Date: Wed, 31 Jul 2024 14:07:25 +0300 Subject: [PATCH] wip (#2820) --- .../java/com/flowcrypt/email/GeneralTest.kt | 28 +++++++++++++++++ .../flowcrypt/email/api/email/EmailUtil.kt | 30 +++++++------------ .../email/database/entity/MessageEntity.kt | 9 +++--- .../email/extensions/kotlin/StringExt.kt | 18 +++++++++-- .../workmanager/sync/LoadRecipientsWorker.kt | 3 +- .../email/security/KeysStorageImpl.kt | 4 +-- .../email/security/model/PgpKeyRingDetails.kt | 29 +++--------------- 7 files changed, 68 insertions(+), 53 deletions(-) create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/GeneralTest.kt diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/GeneralTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/GeneralTest.kt new file mode 100644 index 0000000000..304644594b --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/GeneralTest.kt @@ -0,0 +1,28 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.flowcrypt.email.extensions.kotlin.asInternetAddress +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * @author Denys Bondarenko + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class GeneralTest { + @Test + fun testParsingInternetAddressesWithIllegalCharacters() { + val emailWithInvalidCharacters = + "Test “Test Test” test, sometest, hello, test " + + assertEquals("test.test@test.test", emailWithInvalidCharacters.asInternetAddress()?.address) + } +} \ No newline at end of file diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt index 582f4fa3fd..0dc3f6f05d 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt @@ -30,6 +30,7 @@ import com.flowcrypt.email.database.entity.AccountEntity import com.flowcrypt.email.database.entity.AttachmentEntity import com.flowcrypt.email.database.entity.MessageEntity import com.flowcrypt.email.extensions.jakarta.mail.isAttachment +import com.flowcrypt.email.extensions.kotlin.asInternetAddresses import com.flowcrypt.email.model.MessageEncryptionType import com.flowcrypt.email.model.MessageType import com.flowcrypt.email.security.KeysStorageImpl @@ -47,15 +48,6 @@ import com.google.android.gms.common.GooglePlayServicesNotAvailableException import com.google.android.gms.common.GooglePlayServicesRepairableException import com.google.android.gms.security.ProviderInstaller import com.google.api.services.gmail.GmailScopes -import org.eclipse.angus.mail.gimap.GmailRawSearchTerm -import org.eclipse.angus.mail.iap.Argument -import org.eclipse.angus.mail.imap.IMAPBodyPart -import org.eclipse.angus.mail.imap.IMAPFolder -import org.eclipse.angus.mail.imap.protocol.BODY -import org.eclipse.angus.mail.imap.protocol.FetchResponse -import org.eclipse.angus.mail.imap.protocol.UID -import org.eclipse.angus.mail.imap.protocol.UIDSet -import org.eclipse.angus.mail.util.ASCIIUtility import jakarta.activation.DataHandler import jakarta.mail.BodyPart import jakarta.mail.FetchProfile @@ -66,7 +58,6 @@ import jakarta.mail.Multipart import jakarta.mail.Part import jakarta.mail.Session import jakarta.mail.UIDFolder -import jakarta.mail.internet.AddressException import jakarta.mail.internet.InternetAddress import jakarta.mail.internet.MimeBodyPart import jakarta.mail.internet.MimeMessage @@ -86,6 +77,15 @@ import kotlinx.coroutines.withContext import org.apache.commons.io.FilenameUtils import org.apache.commons.io.IOUtils import org.bouncycastle.openpgp.PGPSecretKeyRingCollection +import org.eclipse.angus.mail.gimap.GmailRawSearchTerm +import org.eclipse.angus.mail.iap.Argument +import org.eclipse.angus.mail.imap.IMAPBodyPart +import org.eclipse.angus.mail.imap.IMAPFolder +import org.eclipse.angus.mail.imap.protocol.BODY +import org.eclipse.angus.mail.imap.protocol.FetchResponse +import org.eclipse.angus.mail.imap.protocol.UID +import org.eclipse.angus.mail.imap.protocol.UIDSet +import org.eclipse.angus.mail.util.ASCIIUtility import org.pgpainless.PGPainless import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector import org.pgpainless.key.protection.SecretKeyRingProtector @@ -984,14 +984,6 @@ class EmailUtil { return parameters.joinToString(separator = " ") } - fun parseAddresses(fromAddress: String?): List { - return try { - InternetAddress.parse(fromAddress ?: "").toList() - } catch (e: AddressException) { - emptyList() - } - } - suspend fun prepareNewMsg( session: Session, info: OutgoingMessageInfo, @@ -1265,7 +1257,7 @@ class EmailUtil { val msg = FlowCryptMimeMessage(session) msg.setFrom(InternetAddress(account.email)) - msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(account.email)) + msg.setRecipients(Message.RecipientType.TO, account.email.asInternetAddresses()) msg.subject = context.getString(R.string.your_key_backup, context.getString(R.string.app_name)) return msg diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/MessageEntity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/MessageEntity.kt index 7bb95bf625..d2d6116ce8 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/MessageEntity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/MessageEntity.kt @@ -25,6 +25,7 @@ import com.flowcrypt.email.api.email.model.OutgoingMessageInfo import com.flowcrypt.email.database.MessageState import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.hasPgp import com.flowcrypt.email.extensions.jakarta.mail.hasPgp +import com.flowcrypt.email.extensions.kotlin.asInternetAddresses import com.flowcrypt.email.extensions.kotlin.capitalize import com.flowcrypt.email.extensions.kotlin.toHex import com.flowcrypt.email.extensions.uid @@ -88,19 +89,19 @@ data class MessageEntity( @IgnoredOnParcel @Ignore - val from: List = EmailUtil.parseAddresses(fromAddress) + val from: List = fromAddress.asInternetAddresses().asList() @IgnoredOnParcel @Ignore - val replyToAddress: List = EmailUtil.parseAddresses(replyTo) + val replyToAddress: List = replyTo.asInternetAddresses().asList() @IgnoredOnParcel @Ignore - val to: List = EmailUtil.parseAddresses(toAddress) + val to: List = toAddress.asInternetAddresses().asList() @IgnoredOnParcel @Ignore - val cc: List = EmailUtil.parseAddresses(ccAddress) + val cc: List = ccAddress.asInternetAddresses().asList() @IgnoredOnParcel @Ignore diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt index 782cb7c293..015c0379d4 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt @@ -8,11 +8,13 @@ package com.flowcrypt.email.extensions.kotlin import android.content.Context import android.graphics.Color +import android.util.Patterns import android.util.TypedValue import androidx.core.content.ContextCompat import com.flowcrypt.email.R import com.flowcrypt.email.util.BetterInternetAddress import com.flowcrypt.email.util.UIUtil +import jakarta.mail.internet.AddressException import jakarta.mail.internet.ContentType import jakarta.mail.internet.InternetAddress import org.json.JSONObject @@ -156,11 +158,23 @@ fun String.capitalize(): String { } fun String?.asInternetAddress(): InternetAddress? { + return asInternetAddresses().firstOrNull() +} + +fun String?.asInternetAddresses(): Array { return try { InternetAddress.parse(this) } catch (e: Exception) { - emptyArray() - }.firstOrNull() + if (e is AddressException) { + val pattern = Patterns.EMAIL_ADDRESS + val matcher = this?.let { pattern.matcher(it) } + if (matcher?.find() == true) { + arrayOf(InternetAddress(matcher.group().lowercase())) + } else emptyArray() + } else { + emptyArray() + } + } } fun String?.asContentTypeOrNull(): ContentType? { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/workmanager/sync/LoadRecipientsWorker.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/workmanager/sync/LoadRecipientsWorker.kt index 08681144f3..d60368f3ad 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/workmanager/sync/LoadRecipientsWorker.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/workmanager/sync/LoadRecipientsWorker.kt @@ -17,6 +17,7 @@ import com.flowcrypt.email.api.email.gmail.api.GmaiAPIMimeMessage import com.flowcrypt.email.database.FlowCryptRoomDatabase import com.flowcrypt.email.database.entity.AccountEntity import com.flowcrypt.email.database.entity.RecipientEntity +import com.flowcrypt.email.extensions.kotlin.asInternetAddresses import org.eclipse.angus.mail.imap.IMAPFolder import jakarta.mail.FetchProfile import jakarta.mail.Folder @@ -210,7 +211,7 @@ class LoadRecipientsWorker(context: Context, params: WorkerParameters) : val header = msg.getHeader(recipientType.toString()) ?: return@withContext emptyList() if (header.isNotEmpty()) { if (!TextUtils.isEmpty(header[0])) { - val addresses = InternetAddress.parse(header[0]) + val addresses = header[0].asInternetAddresses() val emailAndNamePairs = mutableListOf>() for (address in addresses) { emailAndNamePairs.add(Pair(address.address.lowercase(), address.personal)) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/KeysStorageImpl.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/KeysStorageImpl.kt index 336b610547..1025ac7717 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/KeysStorageImpl.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/KeysStorageImpl.kt @@ -14,6 +14,7 @@ import androidx.lifecycle.switchMap import com.flowcrypt.email.database.FlowCryptRoomDatabase import com.flowcrypt.email.database.entity.AccountEntity import com.flowcrypt.email.database.entity.KeyEntity +import com.flowcrypt.email.extensions.kotlin.asInternetAddresses import com.flowcrypt.email.extensions.org.bouncycastle.openpgp.toPgpKeyRingDetails import com.flowcrypt.email.extensions.org.pgpainless.key.info.usableForEncryption import com.flowcrypt.email.model.KeysStorage @@ -21,7 +22,6 @@ import com.flowcrypt.email.security.model.PgpKeyRingDetails import com.flowcrypt.email.security.pgp.PgpDecryptAndOrVerify import com.flowcrypt.email.security.pgp.PgpKey import com.flowcrypt.email.util.exception.DecryptionException -import jakarta.mail.internet.InternetAddress import kotlinx.coroutines.flow.Flow import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPSecretKeyRing @@ -135,7 +135,7 @@ class KeysStorageImpl private constructor(context: Context) : KeysStorage { for (secretKey in getPGPSecretKeyRings()) { for (userId in secretKey.publicKey.userIDs) { try { - val internetAddresses = InternetAddress.parse(userId) + val internetAddresses = userId.asInternetAddresses() for (internetAddress in internetAddresses) { if (user.equals(internetAddress.address, true)) { list.add(secretKey) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/model/PgpKeyRingDetails.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/model/PgpKeyRingDetails.kt index beff1d6ee2..431e99b237 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/model/PgpKeyRingDetails.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/model/PgpKeyRingDetails.kt @@ -8,17 +8,17 @@ package com.flowcrypt.email.security.model import android.content.Context import android.content.res.ColorStateList import android.os.Parcelable -import android.util.Patterns import androidx.core.content.ContextCompat import com.flowcrypt.email.R import com.flowcrypt.email.database.entity.AccountEntity import com.flowcrypt.email.database.entity.KeyEntity import com.flowcrypt.email.database.entity.PublicKeyEntity import com.flowcrypt.email.database.entity.RecipientEntity +import com.flowcrypt.email.extensions.kotlin.asInternetAddress +import com.flowcrypt.email.extensions.kotlin.asInternetAddresses import com.flowcrypt.email.model.KeyImportDetails import com.google.gson.annotations.Expose import com.google.gson.annotations.SerializedName -import jakarta.mail.internet.AddressException import jakarta.mail.internet.InternetAddress import kotlinx.parcelize.Parcelize import org.pgpainless.algorithm.KeyFlag @@ -63,13 +63,7 @@ data class PgpKeyRingDetails( get() = parseMimeAddresses() val primaryMimeAddress: InternetAddress? - get() = primaryUserId?.let { - try { - InternetAddress.parse(it).firstOrNull() - } catch (e: Exception) { - null - } - } + get() = primaryUserId?.asInternetAddress() val isPartiallyEncrypted: Boolean get() { @@ -91,22 +85,7 @@ data class PgpKeyRingDetails( } private fun parseMimeAddresses(): List { - val results = mutableListOf() - - for (user in users) { - try { - results.addAll(listOf(*InternetAddress.parse(user))) - } catch (e: AddressException) { - e.printStackTrace() - val pattern = Patterns.EMAIL_ADDRESS - val matcher = pattern.matcher(user) - if (matcher.find()) { - results.add(InternetAddress(matcher.group())) - } - } - } - - return results + return users.flatMap { it.asInternetAddresses().asList() } } fun toKeyEntity(accountEntity: AccountEntity): KeyEntity {