fix: various fixes wrt open groups, config messages, job queueing

pull/486/head
jubb 3 years ago
parent c3f7425ccd
commit 3654d1731c

@ -230,14 +230,14 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
if (closedGroupPoller != null) {
closedGroupPoller.stopIfNeeded();
}
if (publicChatManager != null) {
publicChatManager.stopPollers();
}
}
@Override
public void onTerminate() {
stopKovenant(); // Loki
if (publicChatManager != null) {
publicChatManager.stopPollers();
}
super.onTerminate();
}

@ -8,6 +8,7 @@ import org.session.libsession.messaging.jobs.AttachmentUploadJob
import org.session.libsession.messaging.jobs.Job
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.jobs.MessageSendJob
import org.session.libsession.messaging.messages.control.ConfigurationMessage
import org.session.libsession.messaging.messages.signal.*
import org.session.libsession.messaging.messages.signal.IncomingTextMessage
import org.session.libsession.messaging.messages.visible.Attachment
@ -30,7 +31,9 @@ import org.session.libsignal.libsignal.util.guava.Optional
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
import org.session.libsignal.service.api.messages.SignalServiceGroup
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
@ -65,12 +68,27 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return TextSecurePreferences.getProfilePictureURL(context)
}
override fun setUserProfilePictureUrl(newProfilePicture: String) {
val ourRecipient = Address.fromSerialized(getUserPublicKey()!!).let {
Recipient.from(context, it, false)
}
TextSecurePreferences.setProfilePictureURL(context, newProfilePicture)
RetrieveProfileAvatarJob(ourRecipient, newProfilePicture)
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, newProfilePicture))
}
override fun getProfileKeyForRecipient(recipientPublicKey: String): ByteArray? {
val address = Address.fromSerialized(recipientPublicKey)
val recipient = Recipient.from(context, address, false)
return recipient.profileKey
}
override fun setProfileKeyForRecipient(recipientPublicKey: String, profileKey: ByteArray) {
val address = Address.fromSerialized(recipientPublicKey)
val recipient = Recipient.from(context, address, false)
DatabaseFactory.getRecipientDatabase(context).setProfileKey(recipient, profileKey)
}
override fun getOrGenerateRegistrationID(): Int {
var registrationID = TextSecurePreferences.getLocalRegistrationId(context)
if (registrationID == 0) {
@ -511,6 +529,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey)
}
override fun setDisplayName(publicKey: String, newName: String) {
DatabaseFactory.getLokiUserDatabase(context).setDisplayName(publicKey, newName)
}
override fun getServerDisplayName(serverID: String, publicKey: String): String? {
return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverID, publicKey)
}
@ -524,6 +546,31 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return if (recipientSettings.isPresent) { recipientSettings.get() } else null
}
override fun addContacts(contacts: List<ConfigurationMessage.Contact>) {
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
val threadDatabase = DatabaseFactory.getThreadDatabase(context)
for (contact in contacts) {
val address = Address.fromSerialized(contact.publicKey)
val recipient = Recipient.from(context, address, true)
if (!contact.profilePicture.isNullOrEmpty()) {
recipientDatabase.setProfileAvatar(recipient, contact.profilePicture)
}
if (contact.profileKey?.isNotEmpty() == true) {
recipientDatabase.setProfileKey(recipient, contact.profileKey)
}
if (contact.name.isNotEmpty()) {
recipientDatabase.setProfileName(recipient, contact.name)
}
recipientDatabase.setProfileSharing(recipient, true)
recipientDatabase.setRegistered(recipient, Recipient.RegisteredState.REGISTERED)
// create Thread if needed
threadDatabase.getOrCreateThreadIdFor(recipient)
}
if (contacts.isNotEmpty()) {
threadDatabase.notifyUpdatedFromConfig()
}
}
override fun getAttachmentDataUri(attachmentId: AttachmentId): Uri {
return PartAuthority.getAttachmentDataUri(attachmentId)
}

@ -3,11 +3,11 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import org.session.libsession.messaging.threads.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsignal.service.api.messages.SignalServiceEnvelope;
import org.session.libsignal.utilities.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.jobmanager.Job;
public abstract class PushReceivedJob extends BaseJob {

@ -4,13 +4,13 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.jobs.MessageReceiveJob
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.service.api.messages.SignalServiceEnvelope
import org.session.libsignal.utilities.Base64
import org.session.libsignal.service.loki.api.MessageWrapper
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.logging.Log
import org.thoughtcrime.securesms.notifications.NotificationChannels
class PushNotificationService : FirebaseMessagingService() {
@ -27,8 +27,7 @@ class PushNotificationService : FirebaseMessagingService() {
val data = base64EncodedData?.let { Base64.decode(it) }
if (data != null) {
try {
val envelope = MessageWrapper.unwrap(data)
PushContentReceiveJob(this).processEnvelope(SignalServiceEnvelope(envelope), true)
JobQueue.shared.add(MessageReceiveJob(MessageWrapper.unwrap(data).toByteArray(),true))
} catch (e: Exception) {
Log.d("Loki", "Failed to unwrap data for message due to error: $e.")
}

@ -6,6 +6,7 @@ import android.net.Uri
import org.session.libsession.messaging.jobs.AttachmentUploadJob
import org.session.libsession.messaging.jobs.Job
import org.session.libsession.messaging.jobs.MessageSendJob
import org.session.libsession.messaging.messages.control.ConfigurationMessage
import org.session.libsession.messaging.messages.visible.Attachment
import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.opengroups.OpenGroup
@ -30,8 +31,10 @@ interface StorageProtocol {
fun getUserDisplayName(): String?
fun getUserProfileKey(): ByteArray?
fun getUserProfilePictureURL(): String?
fun setUserProfilePictureUrl(newProfilePicture: String)
fun getProfileKeyForRecipient(recipientPublicKey: String): ByteArray?
fun setProfileKeyForRecipient(recipientPublicKey: String, profileKey: ByteArray)
// Signal Protocol
@ -140,11 +143,13 @@ interface StorageProtocol {
// Loki User
fun getDisplayName(publicKey: String): String?
fun setDisplayName(publicKey: String, newName: String)
fun getServerDisplayName(serverID: String, publicKey: String): String?
fun getProfilePictureURL(publicKey: String): String?
// Recipient
fun getRecipientSettings(address: Address): RecipientSettings?
fun addContacts(contacts: List<ConfigurationMessage.Contact>)
// PartAuthority
fun getAttachmentDataUri(attachmentId: AttachmentId): Uri

@ -34,30 +34,26 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
}
override fun execute() {
val handleFailure: (java.lang.Exception) -> Unit = { exception ->
if(exception is Error && exception == Error.NoAttachment) {
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
this.handlePermanentFailure(exception)
} else if (exception is DotNetAPI.Error && exception == DotNetAPI.Error.ParsingFailed) {
// No need to retry if the response is invalid. Most likely this means we (incorrectly)
// got a "Cannot GET ..." error from the file server.
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
this.handlePermanentFailure(exception)
} else {
this.handleFailure(exception)
}
}
try {
val messageDataProvider = MessagingConfiguration.shared.messageDataProvider
val attachment = messageDataProvider.getDatabaseAttachment(attachmentID) ?: return handleFailure(Error.NoAttachment)
messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachmentID, this.databaseMessageID)
val tempFile = createTempFile()
val handleFailure: (java.lang.Exception) -> Unit = { exception ->
tempFile.delete()
if(exception is Error && exception == Error.NoAttachment) {
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
this.handlePermanentFailure(exception)
} else if (exception is DotNetAPI.Error && exception == DotNetAPI.Error.ParsingFailed) {
// No need to retry if the response is invalid. Most likely this means we (incorrectly)
// got a "Cannot GET ..." error from the file server.
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
this.handlePermanentFailure(exception)
} else {
this.handleFailure(exception)
}
}
try {
FileServerAPI.shared.downloadFile(tempFile, attachment.url, MAX_ATTACHMENT_SIZE, null)
} catch (e: Exception) {
return handleFailure(e)
}
FileServerAPI.shared.downloadFile(tempFile, attachment.url, MAX_ATTACHMENT_SIZE, null)
// DECRYPTION
@ -70,7 +66,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
tempFile.delete()
handleSuccess()
} catch (e: Exception) {
handleFailure(e)
return handleFailure(e)
}
}

@ -16,19 +16,17 @@ import kotlin.math.roundToLong
class JobQueue : JobDelegate {
private var hasResumedPendingJobs = false // Just for debugging
private val dispatcher = Executors.newCachedThreadPool().asCoroutineDispatcher()
private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val scope = GlobalScope + SupervisorJob()
private val queue = Channel<Job>(UNLIMITED)
init {
// process jobs
scope.launch {
scope.launch(dispatcher) {
while (isActive) {
queue.receive().let { job ->
launch(dispatcher) {
job.delegate = this@JobQueue
job.execute()
}
job.delegate = this@JobQueue
job.execute()
}
}
}
@ -44,7 +42,7 @@ class JobQueue : JobDelegate {
queue.offer(job) // offer always called on unlimited capacity
}
fun addWithoutExecuting(job: Job) {
private fun addWithoutExecuting(job: Job) {
job.id = System.currentTimeMillis().toString()
MessagingConfiguration.shared.storage.persistJob(job)
}

@ -18,6 +18,7 @@ import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.SSKEnvironment
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.preferences.ProfileKeyUtil
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
import org.session.libsignal.libsignal.ecc.ECKeyPair
@ -26,6 +27,7 @@ import org.session.libsignal.service.api.messages.SignalServiceGroup
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import org.session.libsignal.service.loki.utilities.toHexString
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.logging.Log
import java.security.MessageDigest
import java.util.*
@ -91,8 +93,11 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer
private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMessage) {
val context = MessagingConfiguration.shared.context
val storage = MessagingConfiguration.shared.storage
if (TextSecurePreferences.getConfigurationMessageSynced(context)) return
if (message.sender != storage.getUserPublicKey()) return
if (TextSecurePreferences.getConfigurationMessageSynced(context) && !TextSecurePreferences.shouldUpdateProfile(context, message.sentTimestamp!!)) return
val userPublicKey = storage.getUserPublicKey()
if (userPublicKey == null || message.sender != storage.getUserPublicKey()) return
TextSecurePreferences.setConfigurationMessageSynced(context, true)
TextSecurePreferences.setLastProfileUpdateTime(context, message.sentTimestamp!!)
val allClosedGroupPublicKeys = storage.getAllClosedGroupPublicKeys()
for (closeGroup in message.closedGroups) {
if (allClosedGroupPublicKeys.contains(closeGroup.publicKey)) continue
@ -103,8 +108,20 @@ private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMes
if (allOpenGroups.contains(openGroup)) continue
storage.addOpenGroup(openGroup, 1)
}
// TODO: in future handle the latest in config messages
TextSecurePreferences.setConfigurationMessageSynced(context, true)
if (message.displayName.isNotEmpty()) {
TextSecurePreferences.setProfileName(context, message.displayName)
storage.setDisplayName(userPublicKey, message.displayName)
}
if (message.profileKey.isNotEmpty()) {
val profileKey = Base64.encodeBytes(message.profileKey)
ProfileKeyUtil.setEncodedProfileKey(context, profileKey)
storage.setProfileKeyForRecipient(userPublicKey, message.profileKey)
// handle profile photo
if (!message.profilePicture.isNullOrEmpty() && TextSecurePreferences.getProfilePictureURL(context) != message.profilePicture) {
storage.setUserProfilePictureUrl(message.profilePicture!!)
}
}
storage.addContacts(message.contacts)
}
fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalServiceProtos.Content, openGroupID: String?) {
@ -112,7 +129,7 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS
val context = MessagingConfiguration.shared.context
// Update profile if needed
val newProfile = message.profile
if (newProfile != null) {
if (newProfile != null && openGroupID.isNullOrEmpty()) {
val profileManager = SSKEnvironment.shared.profileManager
val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false)
val displayName = newProfile.displayName!!

@ -83,10 +83,7 @@ class OpenGroupPoller(private val openGroup: OpenGroup, private val executorServ
messages.forEach { message ->
try {
val senderPublicKey = message.senderPublicKey
fun generateDisplayName(rawDisplayName: String): String {
return "${rawDisplayName} (${senderPublicKey.takeLast(8)})"
}
val senderDisplayName = MessagingConfiguration.shared.storage.getOpenGroupDisplayName(senderPublicKey, openGroup.channel, openGroup.server) ?: generateDisplayName("Anonymous")
val senderDisplayName = message.displayName
val id = openGroup.id.toByteArray()
// Main message
val dataMessageProto = DataMessage.newBuilder()
@ -145,7 +142,7 @@ class OpenGroupPoller(private val openGroup: OpenGroup, private val executorServ
val messageServerID = message.serverID
// Profile
val profileProto = DataMessage.LokiProfile.newBuilder()
profileProto.setDisplayName(message.displayName)
profileProto.setDisplayName(senderDisplayName)
val profilePicture = message.profilePicture
if (profilePicture != null) {
profileProto.setProfilePicture(profilePicture.url)

Loading…
Cancel
Save