Skip to content

Commit 8b1fde8

Browse files
Bring 1.29.0 to dev (#1657)
* Bump db migration version * Fix deterministic encryption * Fixing timestamp update in SMS table * Moving the timeout to the config wait only * Fixed avatar processing (#1649) * [SES-4799] - Re-upload avatar when renew fails with expired files (#1653) * Remove profile cipher output stream which contains unsafe cipher usage (#1654) * Fix a couple of avatar processing issues (#1655) --------- Co-authored-by: ThomasSession <[email protected]>
2 parents 12515ad + fcf2f11 commit 8b1fde8

File tree

12 files changed

+275
-207
lines changed

12 files changed

+275
-207
lines changed

app/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,16 @@ class MessageSendJob @AssistedInject constructor(
8989
val isSync = destination is Destination.Contact && destination.publicKey == storage.getUserPublicKey()
9090

9191
try {
92-
withTimeout(20_000L) {
93-
// Shouldn't send message to group when the group has no keys available
94-
if (destination is Destination.ClosedGroup) {
92+
// Shouldn't send message to group when the group has no keys available
93+
if (destination is Destination.ClosedGroup) {
94+
withTimeout(20_000L) {
9595
configFactory
9696
.waitForGroupEncryptionKeys(AccountId(destination.publicKey))
9797
}
98-
99-
MessageSender.sendNonDurably(this@MessageSendJob.message, destination, isSync)
10098
}
10199

100+
MessageSender.sendNonDurably(this@MessageSendJob.message, destination, isSync)
101+
102102
this.handleSuccess(dispatcherName)
103103
statusCallback?.trySend(Result.success(Unit))
104104
} catch (e: HTTP.HTTPRequestFailedException) {

app/src/main/java/org/session/libsignal/streams/ProfileCipherOutputStream.java

Lines changed: 0 additions & 90 deletions
This file was deleted.

app/src/main/java/org/session/libsignal/streams/ProfileCipherOutputStreamFactory.java

Lines changed: 0 additions & 19 deletions
This file was deleted.

app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentProcessor.kt

Lines changed: 147 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import coil3.BitmapImage
88
import coil3.ImageLoader
99
import coil3.decode.DataSource
1010
import coil3.decode.ImageSource
11-
import coil3.fetch.FetchResult
1211
import coil3.fetch.Fetcher
1312
import coil3.fetch.SourceFetchResult
1413
import coil3.request.CachePolicy
@@ -18,24 +17,30 @@ import coil3.request.allowConversionToBitmap
1817
import coil3.request.allowHardware
1918
import coil3.request.allowRgb565
2019
import coil3.size.Precision
20+
import dagger.Lazy
2121
import dagger.hilt.android.qualifiers.ApplicationContext
2222
import network.loki.messenger.libsession_util.encrypt.Attachments
2323
import network.loki.messenger.libsession_util.image.GifUtils
2424
import network.loki.messenger.libsession_util.image.WebPUtils
2525
import okio.BufferedSource
2626
import okio.FileSystem
27+
import okio.buffer
28+
import okio.source
2729
import org.session.libsession.utilities.Util
2830
import org.session.libsignal.streams.AttachmentCipherInputStream
2931
import org.session.libsignal.streams.AttachmentCipherOutputStream
3032
import org.session.libsignal.streams.PaddingInputStream
3133
import org.session.libsignal.utilities.ByteArraySlice
3234
import org.session.libsignal.utilities.ByteArraySlice.Companion.view
3335
import org.session.libsignal.utilities.Log
36+
import org.thoughtcrime.securesms.database.Storage
3437
import org.thoughtcrime.securesms.util.AnimatedImageUtils
3538
import org.thoughtcrime.securesms.util.BitmapUtil
3639
import org.thoughtcrime.securesms.util.ImageUtils
40+
import java.io.ByteArrayInputStream
3741
import java.io.ByteArrayOutputStream
3842
import java.security.MessageDigest
43+
import java.util.concurrent.TimeoutException
3944
import javax.inject.Inject
4045
import javax.inject.Provider
4146
import javax.inject.Singleton
@@ -50,13 +55,124 @@ typealias DigestResult = ByteArray
5055
class AttachmentProcessor @Inject constructor(
5156
@param:ApplicationContext private val context: Context,
5257
private val imageLoader: Provider<ImageLoader>,
58+
private val storage: Lazy<Storage>,
5359
) {
5460
class ProcessResult(
5561
val data: ByteArray,
5662
val mimeType: String,
5763
val imageSize: IntSize
5864
)
5965

66+
suspend fun processAvatar(
67+
data: ByteArray,
68+
): ProcessResult? {
69+
val buffer = ByteArrayInputStream(data).source().buffer()
70+
return when {
71+
AnimatedImageUtils.isAnimatedWebP(buffer) -> {
72+
val convertResult = runCatching {
73+
buffer.peek().use {
74+
processAnimatedWebP(
75+
data = it,
76+
maxImageResolution = MAX_AVATAR_SIZE_PX,
77+
timeoutMills = 5_000L,
78+
)
79+
}
80+
}
81+
82+
val processResult = when {
83+
convertResult.isSuccess -> convertResult.getOrThrow() ?: return null
84+
convertResult.exceptionOrNull() is TimeoutException -> {
85+
Log.w(TAG, "Animated WebP processing timed out, skipping")
86+
return null
87+
}
88+
89+
else -> throw convertResult.exceptionOrNull()!!
90+
}
91+
92+
if (processResult.data.size > data.size) {
93+
Log.d(
94+
TAG,
95+
"Avatar processing increased size from ${data.size} to ${processResult.data.size}, skipped result"
96+
)
97+
return null
98+
} else {
99+
processResult
100+
}
101+
}
102+
103+
AnimatedImageUtils.isAnimatedGif(data) -> {
104+
val origSize = ByteArrayInputStream(data).use(BitmapUtil::getDimensions)
105+
.let { pair -> IntSize(pair.first, pair.second) }
106+
107+
val targetSize = if (origSize.width <= MAX_AVATAR_SIZE_PX.width &&
108+
origSize.height <= MAX_AVATAR_SIZE_PX.height) {
109+
origSize
110+
} else {
111+
scaleToFit(origSize, MAX_AVATAR_SIZE_PX).first
112+
}
113+
114+
// First try to convert to webp in 5 seconds
115+
val convertResult = runCatching {
116+
"image/webp" to WebPUtils.encodeGifToWebP(
117+
input = data,
118+
timeoutMills = 5_000L,
119+
targetWidth = targetSize.width, targetHeight = targetSize.height
120+
)
121+
}.recoverCatching { e ->
122+
if (e is TimeoutException) {
123+
// If we timed out, try re-encoding as GIF in 2 seconds
124+
Log.w(TAG, "WebP conversion timed out, trying GIF re-encoding as fallback")
125+
"image/gif" to GifUtils.reencodeGif(
126+
input = data,
127+
timeoutMills = 2_000L,
128+
targetWidth = targetSize.width,
129+
targetHeight = targetSize.height
130+
)
131+
} else {
132+
throw e
133+
}
134+
}
135+
136+
val processResult = when {
137+
convertResult.isSuccess -> {
138+
val (mimeType, result) = convertResult.getOrThrow()
139+
ProcessResult(
140+
data = result,
141+
mimeType = mimeType,
142+
imageSize = targetSize
143+
)
144+
}
145+
146+
convertResult.exceptionOrNull() is TimeoutException -> {
147+
Log.w(TAG, "All operation times out, skipping avatar processing")
148+
null
149+
}
150+
151+
else -> {
152+
throw convertResult.exceptionOrNull()!!
153+
}
154+
}
155+
156+
if (processResult != null && processResult.data.size > data.size) {
157+
Log.d(TAG, "Avatar processing increased size from ${data.size} to ${processResult.data.size}, skipped result")
158+
return null
159+
}
160+
161+
processResult
162+
}
163+
164+
else -> {
165+
// All static images
166+
val (data, size) = processStaticImage(data, MAX_AVATAR_SIZE_PX, Bitmap.CompressFormat.WEBP, 90)
167+
ProcessResult(
168+
data = data,
169+
mimeType = "image/webp",
170+
imageSize = size
171+
)
172+
}
173+
}
174+
}
175+
60176
/**
61177
* Process a file based on its mime type and the given constraints.
62178
*
@@ -92,7 +208,11 @@ class AttachmentProcessor @Inject constructor(
92208
return null
93209
}
94210

95-
return processAnimatedWebP(data = data, maxImageResolution)
211+
return processAnimatedWebP(
212+
data = data,
213+
maxImageResolution = maxImageResolution,
214+
timeoutMills = 30_000L
215+
)
96216
}
97217

98218
ImageUtils.isWebP(data) -> {
@@ -152,8 +272,16 @@ class AttachmentProcessor @Inject constructor(
152272
*/
153273
fun encryptDeterministically(plaintext: ByteArray, domain: Attachments.Domain): EncryptResult {
154274
val cipherOut = ByteArray(Attachments.encryptedSize(plaintext.size.toLong()).toInt())
275+
val privateKey = requireNotNull(storage.get().getUserED25519KeyPair()?.secretKey) {
276+
"No user identity available"
277+
}
278+
check(privateKey.data.size == 64) {
279+
"Invalid ED25519 private key size: ${privateKey.data.size}"
280+
}
281+
val seed = privateKey.data.sliceArray(0 until 32)
282+
155283
val key = Attachments.encryptBytes(
156-
seed = Util.getSecretBytes(32),
284+
seed = seed,
157285
plaintextIn = plaintext,
158286
cipherOut = cipherOut,
159287
domain = domain,
@@ -250,17 +378,15 @@ class AttachmentProcessor @Inject constructor(
250378

251379
private fun processAnimatedWebP(
252380
data: BufferedSource,
253-
maxImageResolution: IntSize?,
381+
maxImageResolution: IntSize,
382+
timeoutMills: Long,
254383
): ProcessResult? {
255384
val origSize = data.peek().inputStream().use(BitmapUtil::getDimensions)
256385
.let { pair -> IntSize(pair.first, pair.second) }
257386

258387
val targetSize: IntSize
259388

260-
if (maxImageResolution == null || (
261-
origSize.width <= maxImageResolution.width &&
262-
origSize.height <= maxImageResolution.height)
263-
) {
389+
if (origSize.width <= maxImageResolution.width && origSize.height <= maxImageResolution.height) {
264390
// No resizing needed hence no processing
265391
return null
266392
} else {
@@ -276,7 +402,7 @@ class AttachmentProcessor @Inject constructor(
276402
input = data.readByteArray(),
277403
targetWidth = targetSize.width,
278404
targetHeight = targetSize.height,
279-
timeoutMills = 10_000L,
405+
timeoutMills = timeoutMills,
280406
)
281407

282408
Log.d(
@@ -345,14 +471,12 @@ class AttachmentProcessor @Inject constructor(
345471
).first
346472
}
347473

348-
val reencoded = data.peek().inputStream().use { input ->
349-
GifUtils.reencodeGif(
350-
input = input,
351-
targetWidth = targetSize.width,
352-
targetHeight = targetSize.height,
353-
timeoutMills = 10_000L,
354-
)
355-
}
474+
val reencoded = GifUtils.reencodeGif(
475+
input = data.readByteArray(),
476+
targetWidth = targetSize.width,
477+
targetHeight = targetSize.height,
478+
timeoutMills = 10_000L,
479+
)
356480

357481
Log.d(
358482
TAG,
@@ -376,14 +500,12 @@ class AttachmentProcessor @Inject constructor(
376500
options: Options,
377501
imageLoader: ImageLoader
378502
): Fetcher {
379-
return object : Fetcher {
380-
override suspend fun fetch(): FetchResult? {
381-
return SourceFetchResult(
382-
source = ImageSource(data, FileSystem.SYSTEM),
383-
mimeType = null,
384-
dataSource = DataSource.MEMORY
385-
)
386-
}
503+
return Fetcher {
504+
SourceFetchResult(
505+
source = ImageSource(data, FileSystem.SYSTEM),
506+
mimeType = null,
507+
dataSource = DataSource.MEMORY
508+
)
387509
}
388510
}
389511
}

0 commit comments

Comments
 (0)