session-android/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt

136 lines
5.5 KiB
Kotlin
Raw Normal View History

2020-12-02 06:39:02 +01:00
package org.session.libsession.messaging.jobs
2020-11-25 02:06:41 +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-02-03 02:22:40 +01:00
import org.session.libsignal.utilities.logging.Log
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
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
internal sealed class Error(val description: String) : Exception() {
object NoAttachment : Error("No such attachment.")
}
2020-12-02 06:39:02 +01:00
// Settings
override val maxFailureCount: Int = 20
companion object {
2020-12-17 04:51:08 +01:00
val TAG = AttachmentUploadJob::class.qualifiedName
2021-01-28 05:24:27 +01:00
val KEY: String = "AttachmentUploadJob"
2020-12-17 04:51:08 +01:00
val maxFailureCount: Int = 20
//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() {
2020-12-17 04:51:08 +01:00
try {
val attachmentStream = MessagingConfiguration.shared.messageDataProvider.getAttachmentStream(attachmentID)
?: return handleFailure(Error.NoAttachment)
val openGroup = MessagingConfiguration.shared.storage.getOpenGroup(threadID)
val server = openGroup?.server ?: FileServerAPI.server
//TODO add some encryption stuff here
val isEncryptionRequired = false
//val isEncryptionRequired = (server == FileServerAPI.server)
val attachmentKey = Util.getSecretBytes(64)
val outputStreamFactory = if (isEncryptionRequired) AttachmentCipherOutputStreamFactory(attachmentKey) else PlaintextOutputStreamFactory()
val ciphertextLength = attachmentStream.length
val attachmentData = PushAttachmentData(attachmentStream.contentType, attachmentStream.inputStream, ciphertextLength, outputStreamFactory, attachmentStream.listener)
FileServerAPI.shared.uploadAttachment(server, attachmentData)
} 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)
}
}
}
private fun handleSuccess() {
Log.w(TAG, "Attachment uploaded successfully.")
delegate?.handleJobSucceeded(this)
MessagingConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID)
//TODO interaction stuff, not sure how to deal with that
}
private fun handlePermanentFailure(e: Exception) {
Log.w(TAG, "Attachment upload failed permanently due to error: $this.")
delegate?.handleJobFailedPermanently(this, e)
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)
MessageSender.handleFailedMessageSend(this.message!!, e)
if (messageSendJob != null) {
storage.markJobAsFailed(messageSendJob)
}
2020-12-02 06:39:02 +01:00
}
//database functions
2021-01-22 05:19:41 +01:00
override fun serialize(): Data {
//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)
.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 {
return AttachmentDownloadJob.KEY
}
class Factory: Job.Factory<AttachmentUploadJob> {
2021-01-22 05:19:41 +01:00
override fun create(data: Data): AttachmentUploadJob {
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)!!)
}
}
2020-11-25 02:06:41 +01:00
}