diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt index bfd176d6fd..2a6ae1aca5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt @@ -9,6 +9,7 @@ import org.signal.libsignal.protocol.message.DecryptionErrorMessage import org.signal.libsignal.zkgroup.groups.GroupMasterKey import org.signal.storageservice.protos.groups.local.DecryptedGroupChange import org.thoughtcrime.securesms.attachments.Attachment +import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.PointerAttachment import org.thoughtcrime.securesms.database.model.StoryType import org.thoughtcrime.securesms.groups.GroupId @@ -175,7 +176,12 @@ object SignalServiceProtoUtil { fun AttachmentPointer.toPointer(stickerLocator: StickerLocator? = null): Attachment? { return try { - PointerAttachment.forPointer(Optional.of(toSignalServiceAttachmentPointer()), stickerLocator).orNull() + val pointer = PointerAttachment.forPointer(Optional.of(toSignalServiceAttachmentPointer()), stickerLocator).orNull() + if (pointer?.cdn != Cdn.S3) { + pointer + } else { + null + } } catch (e: InvalidMessageStructureException) { null } diff --git a/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt b/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt index 3f5c2c7e46..c8c56e8aa1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.s3 import android.content.Context +import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread import okhttp3.Request import okhttp3.Response @@ -20,6 +21,9 @@ import java.io.File import java.io.FileOutputStream import java.io.IOException import java.io.OutputStream +import java.net.URI +import java.net.URISyntaxException +import java.net.URL import java.nio.charset.Charset import java.security.MessageDigest import java.util.regex.Matcher @@ -31,9 +35,8 @@ import java.util.regex.Pattern object S3 { private val TAG = Log.tag(S3::class.java) - private val okHttpClient = AppDependencies.signalOkHttpClient + private val okHttpClient by lazy { AppDependencies.signalOkHttpClient } - private const val S3_BASE = "https://updates2.signal.org" const val DYNAMIC_PATH = "/dynamic" const val STATIC_PATH = "/static" @@ -78,6 +81,8 @@ object S3 { /** * Retrieves an S3 object from the given endpoint. + * + * @param endpoint Must be an absolute path to the resource */ @WorkerThread @JvmStatic @@ -85,7 +90,7 @@ object S3 { fun getObject(endpoint: String): Response { val request = Request.Builder() .get() - .url("$S3_BASE$endpoint") + .url(s3Url(endpoint)) .build() return okHttpClient.newCall(request).execute() @@ -191,12 +196,14 @@ object S3 { /** * Downloads and parses the ETAG from an S3 object, utilizing a HEAD request. + * + * @param endpoint Must be an absolute path to the resource */ @WorkerThread fun getObjectMD5(endpoint: String): ByteArray? { val request = Request.Builder() .head() - .url("$S3_BASE$endpoint") + .url(s3Url(endpoint)) .build() try { @@ -229,5 +236,15 @@ object S3 { } } + @Throws(IOException::class) + @VisibleForTesting + fun s3Url(path: String): URL { + try { + return URI("https", "updates2.signal.org", path, null).toURL() + } catch (e: URISyntaxException) { + throw IOException(e) + } + } + class Md5FailureException : IOException("Failed to getting or comparing MD5") } diff --git a/app/src/test/java/org/thoughtcrime/securesms/s3/S3Test_getS3Url.kt b/app/src/test/java/org/thoughtcrime/securesms/s3/S3Test_getS3Url.kt new file mode 100644 index 0000000000..dd74fc5daf --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/s3/S3Test_getS3Url.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.s3 + +import okio.IOException +import org.junit.Test +import org.thoughtcrime.securesms.assertIs + +@Suppress("ClassName") +class S3Test_getS3Url { + + @Test + fun validS3Urls() { + S3.s3Url("/static/heart.png").toString() assertIs "https://updates2.signal.org/static/heart.png" + S3.s3Url("/static/heart.png?weee=1").toString() assertIs "https://updates2.signal.org/static/heart.png%3Fweee=1" + S3.s3Url("/@signal.org").toString() assertIs "https://updates2.signal.org/@signal.org" + } + + @Test(expected = IOException::class) + fun invalid() { + S3.s3Url("@signal.org") + } + + @Test(expected = IOException::class) + fun invalidRelative() { + S3.s3Url("static/heart.png") + } +}