From 625e9f172ac9656a7998137698cb88eb9d6f0e60 Mon Sep 17 00:00:00 2001 From: Brice Date: Tue, 15 Dec 2020 15:45:44 +1100 Subject: [PATCH 1/3] attachmentId type changed to Long --- .../attachments/DatabaseAttachmentProvider.kt | 28 +++++++++++++++---- .../database/MessageDataProvider.kt | 11 +++++++- .../database/dto/DatabaseAttachmentDTO.kt | 2 ++ .../libsession/messaging/StorageProtocol.kt | 2 +- .../messaging/messages/visible/LinkPreview.kt | 4 +-- .../messaging/messages/visible/Quote.kt | 4 +-- .../messages/visible/VisibleMessage.kt | 2 +- 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt index 671f0a508b..468aae89ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -4,29 +4,45 @@ import android.content.Context import com.google.protobuf.ByteString import org.session.libsession.database.dto.DatabaseAttachmentDTO import org.session.libsession.database.MessageDataProvider +import org.session.libsession.database.dto.AttachmentState import org.session.libsignal.service.internal.push.SignalServiceProtos import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.SmsDatabase import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.jobs.AttachmentUploadJob import org.thoughtcrime.securesms.util.MediaUtil class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), MessageDataProvider { - override fun getAttachment(uniqueID: String): DatabaseAttachmentDTO? { - + override fun getAttachment(attachmentId: Long): DatabaseAttachmentDTO? { val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context) - val uniqueID = uniqueID.toLongOrNull() ?: return null - val attachmentID = AttachmentId(0, uniqueID) - val databaseAttachment = attachmentDatabase.getAttachment(attachmentID) ?: return null - + val databaseAttachment = attachmentDatabase.getAttachment(AttachmentId(attachmentId, 0)) ?: return null return databaseAttachment.toDTO() } + override fun setAttachmentState(attachmentState: AttachmentState, attachment: DatabaseAttachmentDTO, messageID: Long) { + val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context) + attachmentDatabase.setTransferState(messageID, AttachmentId(attachment.attachmentId, 0), attachmentState.value) + } + + @Throws(Exception::class) + override fun uploadAttachment(attachmentId: Long) { + val attachmentUploadJob = AttachmentUploadJob(AttachmentId(attachmentId, 0), null) + attachmentUploadJob.onRun() + } + + override fun isOutgoingMessage(timestamp: Long): Boolean { + val smsDatabase = DatabaseFactory.getSmsDatabase(context) + return smsDatabase.isOutgoingMessage(timestamp) + } + } // Extension to DatabaseAttachment class fun DatabaseAttachment.toDTO(): DatabaseAttachmentDTO { var databaseAttachmentDTO = DatabaseAttachmentDTO() + databaseAttachmentDTO.attachmentId = this.attachmentId.rowId databaseAttachmentDTO.contentType = this.contentType databaseAttachmentDTO.fileName = this.fileName databaseAttachmentDTO.caption = this.caption diff --git a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt index 2f089ff4ff..fc232cfda0 100644 --- a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt +++ b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt @@ -1,9 +1,18 @@ package org.session.libsession.database +import org.session.libsession.database.dto.AttachmentState import org.session.libsession.database.dto.DatabaseAttachmentDTO +import org.session.libsession.messaging.messages.visible.Attachment interface MessageDataProvider { - fun getAttachment(uniqueID: String): DatabaseAttachmentDTO? + fun getAttachment(attachmentId: Long): DatabaseAttachmentDTO? + + fun setAttachmentState(attachmentState: AttachmentState, attachment: DatabaseAttachmentDTO, messageID: Long) + + fun isOutgoingMessage(timestamp: Long): Boolean + + @Throws(Exception::class) + fun uploadAttachment(attachmentId: Long) } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt b/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt index 1a7690fdb6..bf5f31f822 100644 --- a/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt +++ b/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt @@ -6,6 +6,8 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos import kotlin.math.round class DatabaseAttachmentDTO { + var attachmentId: Long = 0 + var contentType: String? = null var fileName: String? = null diff --git a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt index e0d880d94c..25c4437ee8 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -35,7 +35,7 @@ interface StorageProtocol { fun markJobAsSucceeded(job: Job) fun markJobAsFailed(job: Job) fun getAllPendingJobs(type: String): List - fun getAttachmentUploadJob(attachmentID: String): AttachmentUploadJob? + fun getAttachmentUploadJob(attachmentID: Long): AttachmentUploadJob? fun getMessageSendJob(messageSendJobID: String): MessageSendJob? fun resumeMessageSendJobIfNeeded(messageSendJobID: String) fun isJobCanceled(job: Job): Boolean diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt index 246474b9a3..3999148dff 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt @@ -8,7 +8,7 @@ class LinkPreview() { var title: String? = null var url: String? = null - var attachmentID: String? = null + var attachmentID: Long? = 0 companion object { const val TAG = "LinkPreview" @@ -21,7 +21,7 @@ class LinkPreview() { } //constructor - internal constructor(title: String?, url: String, attachmentID: String?) : this() { + internal constructor(title: String?, url: String, attachmentID: Long?) : this() { this.title = title this.url = url this.attachmentID = attachmentID diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt index 3914072c36..ed6dbb29ea 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt @@ -11,7 +11,7 @@ class Quote() { var timestamp: Long? = 0 var publicKey: String? = null var text: String? = null - var attachmentID: String? = null + var attachmentID: Long? = null companion object { const val TAG = "Quote" @@ -25,7 +25,7 @@ class Quote() { } //constructor - internal constructor(timestamp: Long, publicKey: String, text: String?, attachmentID: String?) : this() { + internal constructor(timestamp: Long, publicKey: String, text: String?, attachmentID: Long?) : this() { this.timestamp = timestamp this.publicKey = publicKey this.text = text diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt index a1344754e0..b761eb2846 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt @@ -11,7 +11,7 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos class VisibleMessage : Message() { var text: String? = null - var attachmentIDs = ArrayList() + var attachmentIDs = ArrayList() var quote: Quote? = null var linkPreview: LinkPreview? = null var contact: Contact? = null From e9c5eb5257ed71aea45025d2dc0e23be40500fce Mon Sep 17 00:00:00 2001 From: Brice Date: Tue, 15 Dec 2020 15:47:47 +1100 Subject: [PATCH 2/3] super class updated --- .../session/libsession/messaging/fileserver/FileServerAPI.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/fileserver/FileServerAPI.kt b/libsession/src/main/java/org/session/libsession/messaging/fileserver/FileServerAPI.kt index 4cf728a106..750a10824f 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/fileserver/FileServerAPI.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/fileserver/FileServerAPI.kt @@ -4,12 +4,12 @@ import nl.komponents.kovenant.Promise import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.functional.map import okhttp3.Request +import org.session.libsession.messaging.utilities.DotNetAPI import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.libsignal.util.Hex import org.session.libsignal.service.internal.util.Base64 import org.session.libsignal.service.internal.util.JsonUtil import org.session.libsignal.service.loki.api.SnodeAPI -import org.session.libsignal.service.loki.api.LokiDotNetAPI import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink @@ -18,7 +18,7 @@ import java.net.URL import java.util.concurrent.ConcurrentHashMap import kotlin.collections.set -class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : LokiDotNetAPI(userPublicKey, userPrivateKey, database) { +class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : DotNetAPI() { companion object { // region Settings @@ -49,6 +49,7 @@ class FileServerAPI(public val server: String, userPublicKey: String, userPrivat * possible after proof of work has been calculated and the onion request encryption has happened, which takes several seconds. */ public val fileSizeORMultiplier = 2 // TODO: It should be possible to set this to 1.5? + val server = "https://file.getsession.org" public val fileStorageBucketURL = "https://file-static.lokinet.org" // endregion From 3a10d8c1b40650185e8c62cf750311185226943a Mon Sep 17 00:00:00 2001 From: Brice Date: Tue, 15 Dec 2020 15:50:15 +1100 Subject: [PATCH 3/3] MessageReceive & Send Jobs implementations --- .../messaging/jobs/MessageReceiveJob.kt | 42 ++++++++++++++- .../messaging/jobs/MessageSendJob.kt | 52 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt index 5f9ee29c6b..126a2a91ad 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt @@ -1,6 +1,13 @@ package org.session.libsession.messaging.jobs +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import org.session.libsession.messaging.sending_receiving.MessageReceiver +import org.session.libsession.messaging.sending_receiving.handle +import org.session.libsignal.libsignal.logging.Log + class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val openGroupMessageServerID: Long? = null, val openGroupID: String? = null) : Job { + override var delegate: JobDelegate? = null override var id: String? = null override var failureCount: Int = 0 @@ -8,10 +15,43 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val // Settings override val maxFailureCount: Int = 10 companion object { + val TAG = MessageReceiveJob::class.qualifiedName + val collection: String = "MessageReceiveJobCollection" } override fun execute() { - TODO("Not yet implemented") + exec() + } + + fun exec(): Promise { + val deferred = deferred() + try { + val (message, proto) = MessageReceiver.parse(this.data, this.openGroupMessageServerID) + MessageReceiver.handle(message, proto, this.openGroupID) + this.handleSuccess() + deferred.resolve(Unit) + } catch (e: Exception) { + Log.d(TAG, "Couldn't receive message due to error: $e.") + val error = e as? MessageReceiver.Error + error?.let { + if (!error.isRetryable) this.handlePermanentFailure(error) + } + this.handleFailure(e) + deferred.resolve(Unit) // The promise is just used to keep track of when we're done + } + return deferred.promise + } + + private fun handleSuccess() { + delegate?.handleJobSucceeded(this) + } + + private fun handlePermanentFailure(e: Exception) { + delegate?.handleJobFailedPermanently(this, e) + } + + private fun handleFailure(e: Exception) { + delegate?.handleJobFailed(this, e) } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt index 71cab56c30..5d707d5e93 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt @@ -1,9 +1,15 @@ package org.session.libsession.messaging.jobs +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.Message +import org.session.libsession.messaging.messages.visible.VisibleMessage +import org.session.libsession.messaging.sending_receiving.MessageSender +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.push.SignalServiceProtos class MessageSendJob(val message: Message, val destination: Destination) : Job { + override var delegate: JobDelegate? = null override var id: String? = null override var failureCount: Int = 0 @@ -11,10 +17,54 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job { // Settings override val maxFailureCount: Int = 10 companion object { + val TAG = MessageSendJob::class.qualifiedName + val collection: String = "MessageSendJobCollection" } override fun execute() { - TODO("Not yet implemented") + val messageDataProvider = MessagingConfiguration.shared.messageDataProvider + val message = message as? VisibleMessage + message?.let { + if(!messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) return // The message has been deleted + val attachments = message.attachmentIDs.map { messageDataProvider.getAttachment(it) }.filterNotNull() + val attachmentsToUpload = attachments.filter { !it.isUploaded } + attachmentsToUpload.forEach { + if(MessagingConfiguration.shared.storage.getAttachmentUploadJob(it.attachmentId) != null) { + // Wait for it to finish + } else { + val job = AttachmentUploadJob(it.attachmentId, message.threadID!!, message, id!!) + JobQueue.shared.add(job) + } + } + if (attachmentsToUpload.isNotEmpty()) return // Wait for all attachments to upload before continuing + } + MessageSender.send(this.message, this.destination).success { + this.handleSuccess() + }.fail { exception -> + Log.e(TAG, "Couldn't send message due to error: $exception.") + val e = exception as? MessageSender.Error + e?.let { + if (!e.isRetryable) this.handlePermanentFailure(e) + } + this.handleFailure(exception) + } + } + + private fun handleSuccess() { + delegate?.handleJobSucceeded(this) + } + + private fun handlePermanentFailure(error: Exception) { + delegate?.handleJobFailedPermanently(this, error) + } + + private fun handleFailure(error: Exception) { + Log.w(TAG, "Failed to send $message::class.simpleName.") + val message = message as? VisibleMessage + message?.let { + if(!MessagingConfiguration.shared.messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) return // The message has been deleted + } + delegate?.handleJobFailed(this, error) } } \ No newline at end of file