2020-12-02 06:39:02 +01:00
|
|
|
package org.session.libsession.messaging.jobs
|
2020-11-25 02:06:41 +01:00
|
|
|
|
2021-01-22 03:30:00 +01:00
|
|
|
import com.esotericsoftware.kryo.Kryo
|
|
|
|
import com.esotericsoftware.kryo.io.Input
|
|
|
|
import com.esotericsoftware.kryo.io.Output
|
2020-12-17 04:51:08 +01:00
|
|
|
import org.session.libsession.messaging.MessagingConfiguration
|
|
|
|
import org.session.libsession.messaging.fileserver.FileServerAPI
|
|
|
|
import org.session.libsession.messaging.messages.Message
|
|
|
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
|
|
|
import org.session.libsession.messaging.utilities.DotNetAPI
|
2021-03-03 05:14:45 +01:00
|
|
|
import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream
|
|
|
|
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream
|
|
|
|
import org.session.libsignal.service.internal.crypto.PaddingInputStream
|
2020-12-17 04:51:08 +01:00
|
|
|
import org.session.libsignal.service.internal.push.PushAttachmentData
|
|
|
|
import org.session.libsignal.service.internal.push.http.AttachmentCipherOutputStreamFactory
|
|
|
|
import org.session.libsignal.service.internal.util.Util
|
|
|
|
import org.session.libsignal.service.loki.utilities.PlaintextOutputStreamFactory
|
2021-03-03 05:14:45 +01:00
|
|
|
import org.session.libsignal.utilities.logging.Log
|
2020-12-17 04:51:08 +01:00
|
|
|
|
|
|
|
class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val message: Message, val messageSendJobID: String) : Job {
|
|
|
|
|
2020-12-02 06:39:02 +01:00
|
|
|
override var delegate: JobDelegate? = null
|
|
|
|
override var id: String? = null
|
|
|
|
override var failureCount: Int = 0
|
|
|
|
|
2020-12-17 04:51:08 +01:00
|
|
|
// Error
|
2021-03-16 06:31:52 +01:00
|
|
|
internal sealed class Error(val description: String) : Exception(description) {
|
2020-12-17 04:51:08 +01:00
|
|
|
object NoAttachment : Error("No such attachment.")
|
|
|
|
}
|
|
|
|
|
2020-12-02 06:39:02 +01:00
|
|
|
// Settings
|
|
|
|
override val maxFailureCount: Int = 20
|
|
|
|
companion object {
|
2021-03-02 02:24:09 +01:00
|
|
|
val TAG = AttachmentUploadJob::class.simpleName
|
2021-01-28 05:24:27 +01:00
|
|
|
val KEY: String = "AttachmentUploadJob"
|
2020-12-17 04:51:08 +01:00
|
|
|
|
|
|
|
val maxFailureCount: Int = 20
|
2021-01-22 03:30:00 +01:00
|
|
|
|
|
|
|
//keys used for database storage purpose
|
|
|
|
private val KEY_ATTACHMENT_ID = "attachment_id"
|
|
|
|
private val KEY_THREAD_ID = "thread_id"
|
|
|
|
private val KEY_MESSAGE = "message"
|
|
|
|
private val KEY_MESSAGE_SEND_JOB_ID = "message_send_job_id"
|
2020-12-02 06:39:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun execute() {
|
2021-03-12 07:15:33 +01:00
|
|
|
try {
|
|
|
|
val attachment = MessagingConfiguration.shared.messageDataProvider.getScaledSignalAttachmentStream(attachmentID)
|
|
|
|
?: return handleFailure(Error.NoAttachment)
|
|
|
|
|
|
|
|
var server = FileServerAPI.shared.server
|
|
|
|
var shouldEncrypt = true
|
|
|
|
val usePadding = false
|
|
|
|
val openGroup = MessagingConfiguration.shared.storage.getOpenGroup(threadID)
|
|
|
|
openGroup?.let {
|
|
|
|
server = it.server
|
|
|
|
shouldEncrypt = false
|
|
|
|
}
|
|
|
|
|
|
|
|
val attachmentKey = Util.getSecretBytes(64)
|
|
|
|
val paddedLength = if (usePadding) PaddingInputStream.getPaddedSize(attachment.length) else attachment.length
|
|
|
|
val dataStream = if (usePadding) PaddingInputStream(attachment.inputStream, attachment.length) else attachment.inputStream
|
|
|
|
val ciphertextLength = if (shouldEncrypt) AttachmentCipherOutputStream.getCiphertextLength(paddedLength) else attachment.length
|
|
|
|
|
|
|
|
val outputStreamFactory = if (shouldEncrypt) AttachmentCipherOutputStreamFactory(attachmentKey) else PlaintextOutputStreamFactory()
|
|
|
|
val attachmentData = PushAttachmentData(attachment.contentType, dataStream, ciphertextLength, outputStreamFactory, attachment.listener)
|
|
|
|
|
|
|
|
val uploadResult = FileServerAPI.shared.uploadAttachment(server, attachmentData)
|
|
|
|
handleSuccess(attachment, attachmentKey, uploadResult)
|
|
|
|
|
|
|
|
} catch (e: java.lang.Exception) {
|
|
|
|
if (e is Error && e == Error.NoAttachment) {
|
|
|
|
this.handlePermanentFailure(e)
|
|
|
|
} else if (e is DotNetAPI.Error && !e.isRetryable) {
|
|
|
|
this.handlePermanentFailure(e)
|
|
|
|
} else {
|
|
|
|
this.handleFailure(e)
|
2020-12-17 04:51:08 +01:00
|
|
|
}
|
|
|
|
}
|
2021-03-12 07:15:33 +01:00
|
|
|
|
2020-12-17 04:51:08 +01:00
|
|
|
}
|
|
|
|
|
2021-03-03 05:14:45 +01:00
|
|
|
private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
|
2020-12-17 04:51:08 +01:00
|
|
|
Log.w(TAG, "Attachment uploaded successfully.")
|
|
|
|
delegate?.handleJobSucceeded(this)
|
2021-03-03 05:14:45 +01:00
|
|
|
MessagingConfiguration.shared.messageDataProvider.updateAttachmentAfterUploadSucceeded(attachmentID, attachment, attachmentKey, uploadResult)
|
2020-12-17 04:51:08 +01:00
|
|
|
MessagingConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun handlePermanentFailure(e: Exception) {
|
|
|
|
Log.w(TAG, "Attachment upload failed permanently due to error: $this.")
|
|
|
|
delegate?.handleJobFailedPermanently(this, e)
|
2021-03-03 05:14:45 +01:00
|
|
|
MessagingConfiguration.shared.messageDataProvider.updateAttachmentAfterUploadFailed(attachmentID)
|
2020-12-17 04:51:08 +01:00
|
|
|
failAssociatedMessageSendJob(e)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun handleFailure(e: Exception) {
|
|
|
|
Log.w(TAG, "Attachment upload failed due to error: $this.")
|
|
|
|
delegate?.handleJobFailed(this, e)
|
|
|
|
if (failureCount + 1 == AttachmentUploadJob.maxFailureCount) {
|
|
|
|
failAssociatedMessageSendJob(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun failAssociatedMessageSendJob(e: Exception) {
|
|
|
|
val storage = MessagingConfiguration.shared.storage
|
|
|
|
val messageSendJob = storage.getMessageSendJob(messageSendJobID)
|
2021-03-04 04:03:18 +01:00
|
|
|
MessageSender.handleFailedMessageSend(this.message, e)
|
2020-12-17 04:51:08 +01:00
|
|
|
if (messageSendJob != null) {
|
|
|
|
storage.markJobAsFailed(messageSendJob)
|
|
|
|
}
|
2020-12-02 06:39:02 +01:00
|
|
|
}
|
2021-01-22 03:30:00 +01:00
|
|
|
|
|
|
|
//database functions
|
|
|
|
|
2021-01-22 05:19:41 +01:00
|
|
|
override fun serialize(): Data {
|
2021-01-22 03:30:00 +01:00
|
|
|
//serialize Message property
|
|
|
|
val kryo = Kryo()
|
|
|
|
kryo.isRegistrationRequired = false
|
|
|
|
val serializedMessage = ByteArray(4096)
|
|
|
|
val output = Output(serializedMessage)
|
|
|
|
kryo.writeObject(output, message)
|
|
|
|
output.close()
|
2021-01-28 05:24:27 +01:00
|
|
|
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
|
2021-01-22 03:30:00 +01:00
|
|
|
.putString(KEY_THREAD_ID, threadID)
|
|
|
|
.putByteArray(KEY_MESSAGE, serializedMessage)
|
|
|
|
.putString(KEY_MESSAGE_SEND_JOB_ID, messageSendJobID)
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
|
2021-01-28 05:24:27 +01:00
|
|
|
override fun getFactoryKey(): String {
|
2021-03-03 05:14:45 +01:00
|
|
|
return KEY
|
2021-01-28 05:24:27 +01:00
|
|
|
}
|
|
|
|
|
2021-01-22 03:30:00 +01:00
|
|
|
class Factory: Job.Factory<AttachmentUploadJob> {
|
2021-01-22 05:19:41 +01:00
|
|
|
override fun create(data: Data): AttachmentUploadJob {
|
2021-01-22 03:30:00 +01:00
|
|
|
val serializedMessage = data.getByteArray(KEY_MESSAGE)
|
|
|
|
//deserialize Message property
|
|
|
|
val kryo = Kryo()
|
|
|
|
val input = Input(serializedMessage)
|
|
|
|
val message: Message = kryo.readObject(input, Message::class.java)
|
|
|
|
input.close()
|
2021-01-28 05:24:27 +01:00
|
|
|
return AttachmentUploadJob(data.getLong(KEY_ATTACHMENT_ID), data.getString(KEY_THREAD_ID)!!, message, data.getString(KEY_MESSAGE_SEND_JOB_ID)!!)
|
2021-01-22 03:30:00 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-25 02:06:41 +01:00
|
|
|
}
|