diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt
index 42a1bff159..915d5cb155 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt
@@ -133,6 +133,11 @@ object MessageDecryptor {
if (validationResult is EnvelopeContentValidator.Result.Invalid) {
Log.w(TAG, "${logPrefix(envelope, cipherResult)} Invalid content! ${validationResult.reason}", validationResult.throwable)
+
+ if (FeatureFlags.internalUser()) {
+ postInvalidMessageNotification(context, validationResult.reason)
+ }
+
return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList())
}
@@ -182,7 +187,7 @@ object MessageDecryptor {
Log.w(TAG, "${logPrefix(envelope, e)} Decryption error!", e, true)
if (FeatureFlags.internalUser()) {
- postErrorNotification(context)
+ postDecryptionErrorNotification(context)
}
if (FeatureFlags.retryReceipts()) {
@@ -324,11 +329,22 @@ object MessageDecryptor {
}
}
- private fun postErrorNotification(context: Context) {
+ private fun postDecryptionErrorNotification(context: Context) {
val notification: Notification = NotificationCompat.Builder(context, NotificationChannels.getInstance().FAILURES)
.setSmallIcon(R.drawable.ic_notification)
- .setContentTitle(context.getString(R.string.MessageDecryptionUtil_failed_to_decrypt_message))
- .setContentText(context.getString(R.string.MessageDecryptionUtil_tap_to_send_a_debug_log))
+ .setContentTitle("[Internal-only] Failed to decrypt a message!")
+ .setContentText("Tap to send a debug log")
+ .setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, SubmitDebugLogActivity::class.java), PendingIntentFlags.mutable()))
+ .build()
+
+ NotificationManagerCompat.from(context).notify(NotificationIds.INTERNAL_ERROR, notification)
+ }
+
+ private fun postInvalidMessageNotification(context: Context, message: String) {
+ val notification: Notification = NotificationCompat.Builder(context, NotificationChannels.getInstance().FAILURES)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle("[Internal-only] Received an invalid message!")
+ .setContentText("$message Tap to send a debug log.")
.setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, SubmitDebugLogActivity::class.java), PendingIntentFlags.mutable()))
.build()
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 43df93a065..b890bc3e38 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1223,10 +1223,6 @@
All media
Camera
-
- Failed to decrypt message
- Tap to send a debug log
-
Unknown
Received a message encrypted using an old version of Signal that is no longer supported. Please ask the sender to update to the most recent version and resend the message.
diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt
index 49641756e3..de3acb60c0 100644
--- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt
+++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt
@@ -26,6 +26,14 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Typing
object EnvelopeContentValidator {
fun validate(envelope: Envelope, content: Content): Result {
+ if (envelope.type == Envelope.Type.PLAINTEXT_CONTENT) {
+ val result: Result? = createPlaintextResultIfInvalid(content)
+
+ if (result != null) {
+ return result
+ }
+ }
+
return when {
envelope.story && !content.meetsStoryFlagCriteria() -> Result.Invalid("Envelope was flagged as a story, but it did not have any story-related content!")
content.hasDataMessage() -> validateDataMessage(envelope, content.dataMessage)
@@ -288,6 +296,47 @@ object EnvelopeContentValidator {
}
}
+ private fun createPlaintextResultIfInvalid(content: Content): Result? {
+ val errors: MutableList = mutableListOf()
+
+ if (!content.hasDecryptionErrorMessage()) {
+ errors += "Missing DecryptionErrorMessage"
+ }
+ if (content.hasStoryMessage()) {
+ errors += "Unexpected StoryMessage"
+ }
+ if (content.hasSenderKeyDistributionMessage()) {
+ errors += "Unexpected SenderKeyDistributionMessage"
+ }
+ if (content.hasCallMessage()) {
+ errors += "Unexpected CallMessage"
+ }
+ if (content.hasEditMessage()) {
+ errors += "Unexpected EditMessage"
+ }
+ if (content.hasNullMessage()) {
+ errors += "Unexpected NullMessage"
+ }
+ if (content.hasPniSignatureMessage()) {
+ errors += "Unexpected PniSignatureMessage"
+ }
+ if (content.hasReceiptMessage()) {
+ errors += "Unexpected ReceiptMessage"
+ }
+ if (content.hasSyncMessage()) {
+ errors += "Unexpected SyncMessage"
+ }
+ if (content.hasTypingMessage()) {
+ errors += "Unexpected TypingMessage"
+ }
+
+ return if (errors.isNotEmpty()) {
+ Result.Invalid("Invalid PLAINTEXT_CONTENT! Errors: $errors")
+ } else {
+ null
+ }
+ }
+
private fun validateGroupContextV2(groupContext: GroupContextV2, prefix: String): Result.Invalid? {
return if (!groupContext.hasMasterKey()) {
Result.Invalid("$prefix Missing GV2 master key!")