diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModel.kt index 018e797d1..be3ed18e5 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModel.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModel.kt @@ -156,14 +156,14 @@ class DraftViewModel( val isTextTheSame = if (outgoingMessageInfo.signature != null && !draftFingerprint.skipCheckingSignature) { - val textWithoutSignature = outgoingMessageInfo.msg?.replaceFirst( - regex = ("\n\n" + outgoingMessageInfo.signature).toRegex(RegexOption.MULTILINE), - replacement = "" - ) ?: "" - textWithoutSignature == draftFingerprint.msgText - } else { - (outgoingMessageInfo.msg ?: "") == draftFingerprint.msgText - } + val textWithoutSignature = removeSignature( + message = outgoingMessageInfo.msg, + signature = outgoingMessageInfo.signature + ) + textWithoutSignature == draftFingerprint.msgText + } else { + (outgoingMessageInfo.msg ?: "") == draftFingerprint.msgText + } if (!isTextTheSame || outgoingMessageInfo.subject != draftFingerprint.msgSubject @@ -343,5 +343,12 @@ class DraftViewModel( companion object { val DELAY_TIMEOUT = TimeUnit.SECONDS.toMillis(30) + + internal fun removeSignature(message: String?, signature: String): String { + return message?.replaceFirst( + oldValue = "\n\n$signature", + newValue = "" + ) ?: "" + } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt index 034b2edaa..8ff0ea43f 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt @@ -109,6 +109,7 @@ import com.flowcrypt.email.ui.adapter.AutoCompleteResultRecyclerViewAdapter import com.flowcrypt.email.ui.adapter.FromAddressesAdapter import com.flowcrypt.email.ui.adapter.RecipientChipRecyclerViewAdapter import com.flowcrypt.email.ui.adapter.recyclerview.itemdecoration.MarginItemDecoration +import com.flowcrypt.email.util.EmailSignatureUtil import com.flowcrypt.email.util.FileAndDirectoryUtils import com.flowcrypt.email.util.GeneralUtil import com.flowcrypt.email.util.LogsUtil @@ -2164,9 +2165,11 @@ class CreateMessageFragment : BaseFragment(), val oldSignature = composeMsgViewModel.outgoingMessageInfoStateFlow.value.signature if (oldSignature == null && args.messageType == MessageType.DRAFT) { val extractedSignature = aliases.firstOrNull { alias -> - alias.plainTextSignature != null && binding?.editTextEmailMessage?.text?.contains( - ("^${alias.plainTextSignature}$").toRegex(RegexOption.MULTILINE) - ) == true + val aliasSignature = alias.plainTextSignature + aliasSignature != null && EmailSignatureUtil.containsSignature( + message = binding?.editTextEmailMessage?.text, + signature = aliasSignature + ) }?.plainTextSignature if (!extractedSignature.isNullOrEmpty()) { @@ -2180,19 +2183,18 @@ class CreateMessageFragment : BaseFragment(), return } - val messageHasOldSignature = - oldSignature != null && binding?.editTextEmailMessage?.text?.contains( - ("^$oldSignature$").toRegex(RegexOption.MULTILINE) - ) == true + val messageText = binding?.editTextEmailMessage?.text?.toString() ?: "" + val messageWithUpdatedSignature = oldSignature?.let { + EmailSignatureUtil.replaceSignature( + message = messageText, + oldSignature = it, + newSignature = newSignature + ) + } - if (messageHasOldSignature) { + if (messageWithUpdatedSignature != null) { useNewSignature = true - binding?.editTextEmailMessage?.setText( - binding?.editTextEmailMessage?.text?.replaceFirst( - regex = oldSignature.toRegex(RegexOption.MULTILINE), - replacement = newSignature - ) - ) + binding?.editTextEmailMessage?.setText(messageWithUpdatedSignature) } else if (oldSignature == null) { useNewSignature = true if (binding?.editTextEmailMessage?.text?.isEmpty() == true) { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/EmailSignatureUtil.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/EmailSignatureUtil.kt new file mode 100644 index 000000000..4da346b4f --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/EmailSignatureUtil.kt @@ -0,0 +1,26 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.util + +internal object EmailSignatureUtil { + fun containsSignature(message: CharSequence?, signature: String): Boolean { + return message != null && findSignature(message, signature) != null + } + + fun replaceSignature( + message: String, + oldSignature: String, + newSignature: String + ): String? { + val signatureMatch = findSignature(message, oldSignature) ?: return null + return message.replaceRange(signatureMatch.range, newSignature) + } + + private fun findSignature(message: CharSequence, signature: String): MatchResult? { + val signatureRegex = ("^${Regex.escape(signature)}$").toRegex(RegexOption.MULTILINE) + return signatureRegex.find(message) + } +} diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModelTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModelTest.kt new file mode 100644 index 000000000..774fbe1e6 --- /dev/null +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModelTest.kt @@ -0,0 +1,24 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.jetpack.viewmodel + +import org.junit.Assert.assertEquals +import org.junit.Test + +class DraftViewModelTest { + @Test + fun testRemoveSignatureWithRegexMetaCharacters() { + val signature = """ + Kind regards + + (123 456 + [example].*+?\ + """.trimIndent() + val message = "Message body\n\n$signature" + + assertEquals("Message body", DraftViewModel.removeSignature(message, signature)) + } +} diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/util/EmailSignatureUtilTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/util/EmailSignatureUtilTest.kt new file mode 100644 index 000000000..d26772370 --- /dev/null +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/util/EmailSignatureUtilTest.kt @@ -0,0 +1,32 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.util + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class EmailSignatureUtilTest { + @Test + fun testMatchingAndReplacementWithRegexMetaCharacters() { + val oldSignature = """ + Best regards, + (111 222 333).*+?\ + $100 \path + """.trimIndent() + val newSignature = """ + Sincerely, + $500 & \path\to\file + """.trimIndent() + val message = "Hello World\n\n$oldSignature" + + assertTrue(EmailSignatureUtil.containsSignature(message, oldSignature)) + assertEquals( + "Hello World\n\n$newSignature", + EmailSignatureUtil.replaceSignature(message, oldSignature, newSignature) + ) + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b1b8ef56b..eddabd2ee 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a351597e6..1e922f407 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME