Merge branch 'dev' of https://github.com/loki-project/session-android into data-extraction-2

This commit is contained in:
Brice-W 2021-03-23 10:57:13 +11:00
commit 5f297835fa
14 changed files with 57 additions and 51 deletions

View File

@ -41,6 +41,11 @@ import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
import org.thoughtcrime.securesms.loki.utilities.get import org.thoughtcrime.securesms.loki.utilities.get
import org.thoughtcrime.securesms.loki.utilities.getString import org.thoughtcrime.securesms.loki.utilities.getString
import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.mms.PartAuthority
import org.session.libsession.messaging.messages.signal.IncomingGroupMessage
import org.session.libsession.messaging.messages.signal.IncomingTextMessage
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
import org.session.libsession.utilities.preferences.ProfileKeyUtil
import org.session.libsignal.service.loki.utilities.prettifiedDescription
class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol { class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol {
override fun getUserPublicKey(): String? { override fun getUserPublicKey(): String? {
@ -370,6 +375,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val smsDatabase = DatabaseFactory.getSmsDatabase(context) val smsDatabase = DatabaseFactory.getSmsDatabase(context)
smsDatabase.markAsSentFailed(messageRecord.getId()) smsDatabase.markAsSentFailed(messageRecord.getId())
} }
if (error.localizedMessage != null) {
DatabaseFactory.getLokiMessageDatabase(context).setErrorMessage(messageRecord.getId(), error.localizedMessage!!)
} else {
DatabaseFactory.getLokiMessageDatabase(context).setErrorMessage(messageRecord.getId(), error.javaClass.simpleName)
}
} }
override fun getGroup(groupID: String): GroupRecord? { override fun getGroup(groupID: String): GroupRecord? {

View File

@ -30,6 +30,7 @@ import org.session.libsession.messaging.sending_receiving.MessageSender
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.utilities.* import org.session.libsession.utilities.*
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
import org.session.libsignal.service.loki.utilities.toHexString import org.session.libsignal.service.loki.utilities.toHexString
@ -139,6 +140,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
if (userPublicKey != null) { if (userPublicKey != null) {
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
application.publicChatManager.startPollersIfNeeded() application.publicChatManager.startPollersIfNeeded()
JobQueue.shared.resumePendingJobs()
} }
IP2Country.configureIfNeeded(this) IP2Country.configureIfNeeded(this)
application.registerForFCMIfNeeded(false) application.registerForFCMIfNeeded(false)

View File

@ -12,11 +12,11 @@ import org.thoughtcrime.securesms.loki.utilities.*
class SessionJobDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) { class SessionJobDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
companion object { companion object {
private val sessionJobTable = "session_job_database" private const val sessionJobTable = "session_job_database"
val jobID = "job_id" const val jobID = "job_id"
val jobType = "job_type" const val jobType = "job_type"
val failureCount = "failure_count" const val failureCount = "failure_count"
val serializedData = "serialized_data" const val serializedData = "serialized_data"
@JvmStatic val createSessionJobTableCommand = "CREATE TABLE $sessionJobTable ($jobID INTEGER PRIMARY KEY, $jobType STRING, $failureCount INTEGER DEFAULT 0, $serializedData TEXT);" @JvmStatic val createSessionJobTableCommand = "CREATE TABLE $sessionJobTable ($jobID INTEGER PRIMARY KEY, $jobType STRING, $failureCount INTEGER DEFAULT 0, $serializedData TEXT);"
} }
@ -85,10 +85,7 @@ class SessionJobDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
} }
} }
class SessionJobHelper() { object SessionJobHelper {
val dataSerializer: Data.Serializer = JsonDataSerializer()
companion object { val sessionJobInstantiator: SessionJobInstantiator = SessionJobInstantiator(SessionJobManagerFactories.getSessionJobFactories())
val dataSerializer: Data.Serializer = JsonDataSerializer()
val sessionJobInstantiator: SessionJobInstantiator = SessionJobInstantiator(SessionJobManagerFactories.getSessionJobFactories())
}
} }

View File

@ -17,7 +17,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
private val MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024 private val MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024
// Error // Error
internal sealed class Error(val description: String) : Exception() { internal sealed class Error(val description: String) : Exception(description) {
object NoAttachment : Error("No such attachment.") object NoAttachment : Error("No such attachment.")
} }

View File

@ -25,7 +25,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
override var failureCount: Int = 0 override var failureCount: Int = 0
// Error // Error
internal sealed class Error(val description: String) : Exception() { internal sealed class Error(val description: String) : Exception(description) {
object NoAttachment : Error("No such attachment.") object NoAttachment : Error("No such attachment.")
} }

View File

@ -55,7 +55,7 @@ class ClosedGroupControlMessage() : ControlMessage() {
class MemberLeft() : Kind() class MemberLeft() : Kind()
class EncryptionKeyPairRequest(): Kind() class EncryptionKeyPairRequest(): Kind()
val description: String = run { val description: String =
when(this) { when(this) {
is New -> "new" is New -> "new"
is Update -> "update" is Update -> "update"
@ -66,7 +66,6 @@ class ClosedGroupControlMessage() : ControlMessage() {
is MemberLeft -> "memberLeft" is MemberLeft -> "memberLeft"
is EncryptionKeyPairRequest -> "encryptionKeyPairRequest" is EncryptionKeyPairRequest -> "encryptionKeyPairRequest"
} }
}
} }
companion object { companion object {

View File

@ -14,12 +14,11 @@ class DataExtractionNotification(): ControlMessage() {
class Screenshot() : Kind() class Screenshot() : Kind()
class MediaSaved(val timestamp: Long) : Kind() class MediaSaved(val timestamp: Long) : Kind()
val description: String = run { val description: String =
when(this) { when(this) {
is Screenshot -> "screenshot" is Screenshot -> "screenshot"
is MediaSaved -> "mediaSaved" is MediaSaved -> "mediaSaved"
} }
}
} }
companion object { companion object {
@ -27,12 +26,11 @@ class DataExtractionNotification(): ControlMessage() {
fun fromProto(proto: SignalServiceProtos.Content): DataExtractionNotification? { fun fromProto(proto: SignalServiceProtos.Content): DataExtractionNotification? {
val dataExtractionNotification = proto.dataExtractionNotification ?: return null val dataExtractionNotification = proto.dataExtractionNotification ?: return null
val kind: Kind val kind: Kind = when(dataExtractionNotification.type) {
when(dataExtractionNotification.type) { SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT -> Kind.Screenshot()
SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT -> kind = Kind.Screenshot()
SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED -> { SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED -> {
val timestamp = if (dataExtractionNotification.hasTimestamp()) dataExtractionNotification.timestamp else 0 val timestamp = if (dataExtractionNotification.hasTimestamp()) dataExtractionNotification.timestamp else return null
kind = Kind.MediaSaved(timestamp) Kind.MediaSaved(timestamp)
} }
} }
return DataExtractionNotification(kind) return DataExtractionNotification(kind)

View File

@ -35,7 +35,7 @@ class ReadReceipt() : ControlMessage() {
override fun toProto(): SignalServiceProtos.Content? { override fun toProto(): SignalServiceProtos.Content? {
val timestamps = timestamps val timestamps = timestamps
if (timestamps == null) { if (timestamps == null) {
Log.w(ExpirationTimerUpdate.TAG, "Couldn't construct read receipt proto from: $this") Log.w(TAG, "Couldn't construct read receipt proto from: $this")
return null return null
} }
val receiptProto = SignalServiceProtos.ReceiptMessage.newBuilder() val receiptProto = SignalServiceProtos.ReceiptMessage.newBuilder()
@ -46,7 +46,7 @@ class ReadReceipt() : ControlMessage() {
contentProto.receiptMessage = receiptProto.build() contentProto.receiptMessage = receiptProto.build()
return contentProto.build() return contentProto.build()
} catch (e: Exception) { } catch (e: Exception) {
Log.w(ExpirationTimerUpdate.TAG, "Couldn't construct read receipt proto from: $this") Log.w(TAG, "Couldn't construct read receipt proto from: $this")
return null return null
} }
} }

View File

@ -11,7 +11,7 @@ object MessageReceiver {
private val lastEncryptionKeyPairRequest = mutableMapOf<String, Long>() private val lastEncryptionKeyPairRequest = mutableMapOf<String, Long>()
internal sealed class Error(val description: String) : Exception() { internal sealed class Error(val description: String) : Exception(description) {
object DuplicateMessage: Error("Duplicate message.") object DuplicateMessage: Error("Duplicate message.")
object InvalidMessage: Error("Invalid message.") object InvalidMessage: Error("Invalid message.")
object UnknownMessage: Error("Unknown message type.") object UnknownMessage: Error("Unknown message type.")

View File

@ -35,7 +35,7 @@ import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as S
object MessageSender { object MessageSender {
// Error // Error
sealed class Error(val description: String) : Exception() { sealed class Error(val description: String) : Exception(description) {
object InvalidMessage : Error("Invalid message.") object InvalidMessage : Error("Invalid message.")
object ProtoConversionFailed : Error("Couldn't convert message to proto.") object ProtoConversionFailed : Error("Couldn't convert message to proto.")
object ProofOfWorkCalculationFailed : Error("Proof of work calculation failed.") object ProofOfWorkCalculationFailed : Error("Proof of work calculation failed.")
@ -50,6 +50,9 @@ object MessageSender {
object NoPrivateKey : Error("Couldn't find a private key associated with the given group public key.") object NoPrivateKey : Error("Couldn't find a private key associated with the given group public key.")
object InvalidClosedGroupUpdate : Error("Invalid group update.") object InvalidClosedGroupUpdate : Error("Invalid group update.")
// Precondition
class PreconditionFailure(val reason: String): Error(reason)
internal val isRetryable: Boolean = when (this) { internal val isRetryable: Boolean = when (this) {
is InvalidMessage -> false is InvalidMessage -> false
is ProtoConversionFailed -> false is ProtoConversionFailed -> false
@ -73,7 +76,6 @@ object MessageSender {
val promise = deferred.promise val promise = deferred.promise
val storage = MessagingConfiguration.shared.storage val storage = MessagingConfiguration.shared.storage
val userPublicKey = storage.getUserPublicKey() val userPublicKey = storage.getUserPublicKey()
val preconditionFailure = Exception("Destination should not be open groups!")
// Set the timestamp, sender and recipient // Set the timestamp, sender and recipient
message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() } /* Visible messages will already have their sent timestamp set */ message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() } /* Visible messages will already have their sent timestamp set */
message.sender = userPublicKey message.sender = userPublicKey
@ -90,7 +92,7 @@ object MessageSender {
when (destination) { when (destination) {
is Destination.Contact -> message.recipient = destination.publicKey is Destination.Contact -> message.recipient = destination.publicKey
is Destination.ClosedGroup -> message.recipient = destination.groupPublicKey is Destination.ClosedGroup -> message.recipient = destination.groupPublicKey
is Destination.OpenGroup -> throw preconditionFailure is Destination.OpenGroup -> throw Error.PreconditionFailure("Destination should not be open groups!")
} }
// Validate the message // Validate the message
if (!message.isValid()) { throw Error.InvalidMessage } if (!message.isValid()) { throw Error.InvalidMessage }
@ -128,7 +130,7 @@ object MessageSender {
val encryptionKeyPair = MessagingConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair(destination.groupPublicKey)!! val encryptionKeyPair = MessagingConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair(destination.groupPublicKey)!!
ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, encryptionKeyPair.hexEncodedPublicKey) ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, encryptionKeyPair.hexEncodedPublicKey)
} }
is Destination.OpenGroup -> throw preconditionFailure is Destination.OpenGroup -> throw Error.PreconditionFailure("Destination should not be open groups!")
} }
// Wrap the result // Wrap the result
val kind: SignalServiceProtos.Envelope.Type val kind: SignalServiceProtos.Envelope.Type
@ -142,7 +144,7 @@ object MessageSender {
kind = SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT kind = SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT
senderPublicKey = destination.groupPublicKey senderPublicKey = destination.groupPublicKey
} }
is Destination.OpenGroup -> throw preconditionFailure is Destination.OpenGroup -> throw Error.PreconditionFailure("Destination should not be open groups!")
} }
val wrappedMessage = MessageWrapper.wrap(kind, message.sentTimestamp!!, senderPublicKey, ciphertext) val wrappedMessage = MessageWrapper.wrap(kind, message.sentTimestamp!!, senderPublicKey, ciphertext)
// Calculate proof of work // Calculate proof of work
@ -200,34 +202,31 @@ object MessageSender {
private fun sendToOpenGroupDestination(destination: Destination, message: Message): Promise<Unit, Exception> { private fun sendToOpenGroupDestination(destination: Destination, message: Message): Promise<Unit, Exception> {
val deferred = deferred<Unit, Exception>() val deferred = deferred<Unit, Exception>()
val storage = MessagingConfiguration.shared.storage val storage = MessagingConfiguration.shared.storage
val preconditionFailure = Exception("Destination should not be contacts or closed groups!")
message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() } message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() }
message.sender = storage.getUserPublicKey() message.sender = storage.getUserPublicKey()
// Set the failure handler (need it here already for precondition failure handling)
fun handleFailure(error: Exception) {
handleFailedMessageSend(message, error)
deferred.reject(error)
}
try { try {
val server: String val server: String
val channel: Long val channel: Long
when (destination) { when (destination) {
is Destination.Contact -> throw preconditionFailure is Destination.Contact -> throw Error.PreconditionFailure("Destination should not be contacts!")
is Destination.ClosedGroup -> throw preconditionFailure is Destination.ClosedGroup -> throw Error.PreconditionFailure("Destination should not be closed groups!")
is Destination.OpenGroup -> { is Destination.OpenGroup -> {
message.recipient = "${destination.server}.${destination.channel}" message.recipient = "${destination.server}.${destination.channel}"
server = destination.server server = destination.server
channel = destination.channel channel = destination.channel
} }
} }
// Set the failure handler (need it here already for precondition failure handling)
fun handleFailure(error: Exception) {
handleFailedMessageSend(message, error)
deferred.reject(error)
}
// Validate the message // Validate the message
if (message !is VisibleMessage || !message.isValid()) { if (message !is VisibleMessage || !message.isValid()) {
handleFailure(Error.InvalidMessage)
throw Error.InvalidMessage throw Error.InvalidMessage
} }
// Convert the message to an open group message // Convert the message to an open group message
val openGroupMessage = OpenGroupMessage.from(message, server) ?: kotlin.run { val openGroupMessage = OpenGroupMessage.from(message, server) ?: kotlin.run {
handleFailure(Error.InvalidMessage)
throw Error.InvalidMessage throw Error.InvalidMessage
} }
// Send the result // Send the result
@ -239,7 +238,7 @@ object MessageSender {
handleFailure(it) handleFailure(it)
} }
} catch (exception: Exception) { } catch (exception: Exception) {
deferred.reject(exception) handleFailure(exception)
} }
return deferred.promise return deferred.promise
} }
@ -247,7 +246,8 @@ object MessageSender {
// Result Handling // Result Handling
fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false) { fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false) {
val storage = MessagingConfiguration.shared.storage val storage = MessagingConfiguration.shared.storage
val messageId = storage.getMessageIdInDatabase(message.sentTimestamp!!, message.sender!!) ?: return val userPublicKey = storage.getUserPublicKey()!!
val messageId = storage.getMessageIdInDatabase(message.sentTimestamp!!, message.sender?:userPublicKey) ?: return
// Ignore future self-sends // Ignore future self-sends
storage.addReceivedMessageTimestamp(message.sentTimestamp!!) storage.addReceivedMessageTimestamp(message.sentTimestamp!!)
// Track the open group server message ID // Track the open group server message ID
@ -255,17 +255,16 @@ object MessageSender {
storage.setOpenGroupServerMessageID(messageId, message.openGroupServerMessageID!!) storage.setOpenGroupServerMessageID(messageId, message.openGroupServerMessageID!!)
} }
// Mark the message as sent // Mark the message as sent
storage.markAsSent(message.sentTimestamp!!, message.sender!!) storage.markAsSent(message.sentTimestamp!!, message.sender?:userPublicKey)
storage.markUnidentified(message.sentTimestamp!!, message.sender!!) storage.markUnidentified(message.sentTimestamp!!, message.sender?:userPublicKey)
// Start the disappearing messages timer if needed // Start the disappearing messages timer if needed
if (message is VisibleMessage && !isSyncMessage) { if (message is VisibleMessage && !isSyncMessage) {
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message.sentTimestamp!!, message.sender!!) SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message.sentTimestamp!!, message.sender?:userPublicKey)
} }
// Sync the message if: // Sync the message if:
// • it's a visible message // • it's a visible message
// • the destination was a contact // • the destination was a contact
// • we didn't sync it already // • we didn't sync it already
val userPublicKey = storage.getUserPublicKey()!!
if (destination is Destination.Contact && !isSyncMessage) { if (destination is Destination.Contact && !isSyncMessage) {
if (message is VisibleMessage) { message.syncTarget = destination.publicKey } if (message is VisibleMessage) { message.syncTarget = destination.publicKey }
if (message is ExpirationTimerUpdate) { message.syncTarget = destination.publicKey } if (message is ExpirationTimerUpdate) { message.syncTarget = destination.publicKey }
@ -275,7 +274,8 @@ object MessageSender {
fun handleFailedMessageSend(message: Message, error: Exception) { fun handleFailedMessageSend(message: Message, error: Exception) {
val storage = MessagingConfiguration.shared.storage val storage = MessagingConfiguration.shared.storage
storage.setErrorMessage(message.sentTimestamp!!, message.sender!!, error) val userPublicKey = storage.getUserPublicKey()!!
storage.setErrorMessage(message.sentTimestamp!!, message.sender?:userPublicKey, error)
} }
// Convenience // Convenience

View File

@ -42,7 +42,7 @@ open class DotNetAPI {
internal enum class HTTPVerb { GET, PUT, POST, DELETE, PATCH } internal enum class HTTPVerb { GET, PUT, POST, DELETE, PATCH }
// Error // Error
internal sealed class Error(val description: String) : Exception() { internal sealed class Error(val description: String) : Exception(description) {
object Generic : Error("An error occurred.") object Generic : Error("An error occurred.")
object InvalidURL : Error("Invalid URL.") object InvalidURL : Error("Invalid URL.")
object ParsingFailed : Error("Invalid file server response.") object ParsingFailed : Error("Invalid file server response.")

View File

@ -10,7 +10,7 @@ import java.security.SecureRandom
object MessageWrapper { object MessageWrapper {
// region Types // region Types
sealed class Error(val description: String) : Exception() { sealed class Error(val description: String) : Exception(description) {
object FailedToWrapData : Error("Failed to wrap data.") object FailedToWrapData : Error("Failed to wrap data.")
object FailedToWrapMessageInEnvelope : Error("Failed to wrap message in envelope.") object FailedToWrapMessageInEnvelope : Error("Failed to wrap message in envelope.")
object FailedToWrapEnvelopeInWebSocketMessage : Error("Failed to wrap envelope in web socket message.") object FailedToWrapEnvelopeInWebSocketMessage : Error("Failed to wrap envelope in web socket message.")

View File

@ -45,7 +45,7 @@ object SnodeAPI {
internal var powDifficulty = 1 internal var powDifficulty = 1
// Error // Error
internal sealed class Error(val description: String) : Exception() { internal sealed class Error(val description: String) : Exception(description) {
object Generic : Error("An error occurred.") object Generic : Error("An error occurred.")
object ClockOutOfSync : Error("The user's clock is out of sync with the service node network.") object ClockOutOfSync : Error("The user's clock is out of sync with the service node network.")
object RandomSnodePoolUpdatingFailed : Error("Failed to update random service node pool.") object RandomSnodePoolUpdatingFailed : Error("Failed to update random service node pool.")

View File

@ -50,7 +50,7 @@ class MnemonicCodec(private val loadFileContents: (String) -> String) {
} }
} }
sealed class DecodingError(val description: String) : Exception() { sealed class DecodingError(val description: String) : Exception(description) {
object Generic : DecodingError("Something went wrong. Please check your mnemonic and try again.") object Generic : DecodingError("Something went wrong. Please check your mnemonic and try again.")
object InputTooShort : DecodingError("Looks like you didn't enter enough words. Please check your mnemonic and try again.") object InputTooShort : DecodingError("Looks like you didn't enter enough words. Please check your mnemonic and try again.")
object MissingLastWord : DecodingError("You seem to be missing the last word of your mnemonic. Please check what you entered and try again.") object MissingLastWord : DecodingError("You seem to be missing the last word of your mnemonic. Please check what you entered and try again.")