Merge remote-tracking branch 'upstream/dev' into feature/unread-mention-indicator

# Conflicts:
#	app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt
#	libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt
This commit is contained in:
Morgan Pretty
2023-01-24 14:02:08 +11:00
28 changed files with 330 additions and 41 deletions

View File

@@ -20,7 +20,9 @@ interface MessageDataProvider {
* @return pair of sms or mms table-specific ID and whether it is in SMS table
*/
fun getMessageID(serverId: Long, threadId: Long): Pair<Long, Boolean>?
fun getMessageIDs(serverIDs: List<Long>, threadID: Long): Pair<List<Long>, List<Long>>
fun deleteMessage(messageID: Long, isSms: Boolean)
fun deleteMessages(messageIDs: List<Long>, threadId: Long, isSms: Boolean)
fun updateMessageAsDeleted(timestamp: Long, author: String): Long?
fun getServerHashForMessage(messageID: Long): String?
fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment?

View File

@@ -81,6 +81,7 @@ interface StorageProtocol {
// Open Group Metadata
fun updateTitle(groupID: String, newValue: String)
fun updateProfilePicture(groupID: String, newValue: ByteArray)
fun hasDownloadedProfilePicture(groupID: String): Boolean
fun setUserCount(room: String, server: String, newValue: Int)
// Last Message Server ID

View File

@@ -77,7 +77,11 @@ object FileServerApi {
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), server, serverPublicKey).map {
it.body ?: throw Error.ParsingFailed
}.fail { e ->
Log.e("Loki", "File server request failed.", e)
when (e) {
// No need for the stack trace for HTTP errors
is HTTP.HTTPRequestFailedException -> Log.e("Loki", "File server request failed due to error: ${e.message}")
else -> Log.e("Loki", "File server request failed", e)
}
}
} else {
Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))

View File

@@ -92,12 +92,23 @@ class BatchMessageReceiveJob(
threadMap[threadID]!! += parsedParams
}
} catch (e: Exception) {
Log.e(TAG, "Couldn't receive message.", e)
if (e is MessageReceiver.Error && !e.isRetryable) {
Log.e(TAG, "Message failed permanently",e)
} else {
Log.e(TAG, "Message failed",e)
failures += messageParameters
when (e) {
is MessageReceiver.Error.DuplicateMessage, MessageReceiver.Error.SelfSend -> {
Log.i(TAG, "Couldn't receive message, failed with error: ${e.message}")
}
is MessageReceiver.Error -> {
if (!e.isRetryable) {
Log.e(TAG, "Couldn't receive message, failed permanently", e)
}
else {
Log.e(TAG, "Couldn't receive message, failed", e)
failures += messageParameters
}
}
else -> {
Log.e(TAG, "Couldn't receive message, failed", e)
failures += messageParameters
}
}
}
}

View File

@@ -26,7 +26,7 @@ class JobQueue : JobDelegate {
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
private val rxDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val rxMediaDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
private val openGroupDispatcher = Executors.newCachedThreadPool().asCoroutineDispatcher()
private val openGroupDispatcher = Executors.newFixedThreadPool(8).asCoroutineDispatcher()
private val txDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val scope = CoroutineScope(Dispatchers.Default) + SupervisorJob()
private val queue = Channel<Job>(UNLIMITED)

View File

@@ -11,6 +11,7 @@ import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.utilities.Data
import org.session.libsession.snode.OnionRequestAPI
import org.session.libsignal.utilities.HTTP
import org.session.libsignal.utilities.Log
class MessageSendJob(val message: Message, val destination: Destination) : Job {
@@ -67,14 +68,25 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
val promise = MessageSender.send(this.message, this.destination).success {
this.handleSuccess()
}.fail { exception ->
Log.e(TAG, "Couldn't send message due to error: $exception.")
if (exception is MessageSender.Error) {
if (!exception.isRetryable) { this.handlePermanentFailure(exception) }
var logStacktrace = true
when (exception) {
// No need for the stack trace for HTTP errors
is HTTP.HTTPRequestFailedException -> {
logStacktrace = false
if (exception.statusCode == 429) { this.handlePermanentFailure(exception) }
else { this.handleFailure(exception) }
}
is MessageSender.Error -> {
if (!exception.isRetryable) { this.handlePermanentFailure(exception) }
else { this.handleFailure(exception) }
}
else -> this.handleFailure(exception)
}
if (exception is OnionRequestAPI.HTTPRequestFailedAtDestinationException && exception.statusCode == 429) {
this.handlePermanentFailure(exception)
}
this.handleFailure(exception)
if (logStacktrace) { Log.e(TAG, "Couldn't send message due to error", exception) }
else { Log.e(TAG, "Couldn't send message due to error: ${exception.message}") }
}
try {
promise.get()

View File

@@ -23,14 +23,27 @@ class OpenGroupDeleteJob(private val messageServerIds: LongArray, private val th
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
val numberToDelete = messageServerIds.size
Log.d(TAG, "Deleting $numberToDelete messages")
var numberDeleted = 0
messageServerIds.forEach { serverId ->
val (messageId, isSms) = dataProvider.getMessageID(serverId, threadId) ?: return@forEach
dataProvider.deleteMessage(messageId, isSms)
numberDeleted++
// FIXME: This entire process should probably run in a transaction (with the attachment deletion happening only if it succeeded)
try {
val messageIds = dataProvider.getMessageIDs(messageServerIds.toList(), threadId)
// Delete the SMS messages
if (messageIds.first.isNotEmpty()) {
dataProvider.deleteMessages(messageIds.first, threadId, true)
}
// Delete the MMS messages
if (messageIds.second.isNotEmpty()) {
dataProvider.deleteMessages(messageIds.second, threadId, false)
}
Log.d(TAG, "Deleted ${messageIds.first.size + messageIds.second.size} messages successfully")
delegate?.handleJobSucceeded(this)
}
catch (e: Exception) {
delegate?.handleJobFailed(this, e)
}
Log.d(TAG, "Deleted $numberDeleted messages successfully")
delegate?.handleJobSucceeded(this)
}
override fun serialize(): Data = Data.Builder()

View File

@@ -383,7 +383,11 @@ object OpenGroupApi {
}
return if (request.useOnionRouting) {
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey).fail { e ->
Log.e("SOGS", "Failed onion request", e)
when (e) {
// No need for the stack trace for HTTP errors
is HTTP.HTTPRequestFailedException -> Log.e("SOGS", "Failed onion request: ${e.message}")
else -> Log.e("SOGS", "Failed onion request", e)
}
}
} else {
Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))

View File

@@ -117,6 +117,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S
) {
val storage = MessagingModuleConfiguration.shared.storage
val groupId = "$server.$roomToken"
val dbGroupId = GroupUtil.getEncodedOpenGroupID(groupId.toByteArray())
val existingOpenGroup = storage.getOpenGroup(roomToken, server)
val publicKey = existingOpenGroup?.publicKey ?: return
@@ -157,8 +158,19 @@ class OpenGroupPoller(private val server: String, private val executorService: S
})
}
// Start downloading the room image (if we don't have one or it's been updated)
if (pollInfo.details?.imageId != null && pollInfo.details.imageId != existingOpenGroup.imageId) {
if (
(
pollInfo.details != null &&
pollInfo.details.imageId != null && (
pollInfo.details.imageId != existingOpenGroup.imageId ||
!storage.hasDownloadedProfilePicture(dbGroupId)
)
) || (
pollInfo.details == null &&
existingOpenGroup.imageId != null &&
!storage.hasDownloadedProfilePicture(dbGroupId)
)
) {
JobQueue.shared.add(GroupAvatarDownloadJob(roomToken, server))
}
}

View File

@@ -78,8 +78,8 @@ object OnionRequestAPI {
// endregion
class HTTPRequestFailedBlindingRequiredException(statusCode: Int, json: Map<*, *>, destination: String): HTTPRequestFailedAtDestinationException(statusCode, json, destination)
open class HTTPRequestFailedAtDestinationException(val statusCode: Int, val json: Map<*, *>, val destination: String)
: Exception("HTTP request failed at destination ($destination) with status code $statusCode.")
open class HTTPRequestFailedAtDestinationException(statusCode: Int, json: Map<*, *>, val destination: String)
: HTTP.HTTPRequestFailedException(statusCode, json, "HTTP request failed at destination ($destination) with status code $statusCode.")
class InsufficientSnodesException : Exception("Couldn't find enough snodes to build a path.")
private data class OnionBuildingResult(

View File

@@ -56,6 +56,10 @@ object SnodeAPI {
* user's clock is incorrect.
*/
internal var clockOffset = 0L
val nowWithOffset
get() = System.currentTimeMillis() + clockOffset
internal var forkInfo by observable(database.getForkInfo()) { _, oldValue, newValue ->
if (newValue > oldValue) {
Log.d("Loki", "Setting new fork info new: $newValue, old: $oldValue")

View File

@@ -2,6 +2,7 @@ package org.session.libsession.utilities
import okhttp3.HttpUrl
import org.session.libsession.messaging.file_server.FileServerApi
import org.session.libsignal.utilities.HTTP
import org.session.libsignal.utilities.Log
import java.io.*
@@ -40,7 +41,11 @@ object DownloadUtilities {
outputStream.write(it)
}
} catch (e: Exception) {
Log.e("Loki", "Couldn't download attachment.", e)
when (e) {
// No need for the stack trace for HTTP errors
is HTTP.HTTPRequestFailedException -> Log.e("Loki", "Couldn't download attachment due to error: ${e.message}")
else -> Log.e("Loki", "Couldn't download attachment", e)
}
throw e
}
}