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

113 lines
4.6 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
2021-01-05 04:17:42 +01:00
import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.messaging.fileserver.FileServerAPI
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
import org.session.libsession.messaging.utilities.DotNetAPI
import org.session.libsignal.service.api.crypto.AttachmentCipherInputStream
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.logging.Log
2021-01-05 04:17:42 +01:00
import java.io.File
import java.io.FileInputStream
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long): Job {
2021-01-05 04:17:42 +01:00
2020-12-02 06:39:02 +01:00
override var delegate: JobDelegate? = null
override var id: String? = null
override var failureCount: Int = 0
2021-01-05 04:17:42 +01:00
private val MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024
// Error
internal sealed class Error(val description: String) : Exception(description) {
2021-01-05 04:17:42 +01:00
object NoAttachment : Error("No such attachment.")
}
2020-12-02 06:39:02 +01:00
// Settings
2021-01-05 04:17:42 +01:00
override val maxFailureCount: Int = 20
2020-12-02 06:39:02 +01:00
companion object {
2021-01-28 05:24:27 +01:00
val KEY: String = "AttachmentDownloadJob"
//keys used for database storage purpose
private val KEY_ATTACHMENT_ID = "attachment_id"
private val KEY_TS_INCOMING_MESSAGE_ID = "tsIncoming_message_id"
2020-12-02 06:39:02 +01:00
}
override fun execute() {
2021-01-05 04:17:42 +01:00
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)
}
2021-01-05 04:17:42 +01:00
// DECRYPTION
2021-01-06 06:11:00 +01:00
// Assume we're retrieving an attachment for an open group server if the digest is not set
val stream = if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) FileInputStream(tempFile)
else AttachmentCipherInputStream.createForAttachment(tempFile, attachment.size, Base64.decode(attachment.key), attachment.digest)
2021-01-05 04:17:42 +01:00
messageDataProvider.insertAttachment(databaseMessageID, attachment.attachmentId, stream)
2021-01-05 04:17:42 +01:00
tempFile.delete()
handleSuccess()
} catch (e: Exception) {
handleFailure(e)
}
2021-01-05 04:17:42 +01:00
}
private fun handleSuccess() {
Log.w(AttachmentUploadJob.TAG, "Attachment downloaded successfully.")
2021-01-05 04:17:42 +01:00
delegate?.handleJobSucceeded(this)
}
private fun handlePermanentFailure(e: Exception) {
delegate?.handleJobFailedPermanently(this, e)
}
private fun handleFailure(e: Exception) {
delegate?.handleJobFailed(this, e)
}
private fun createTempFile(): File {
val file = File.createTempFile("push-attachment", "tmp", MessagingConfiguration.shared.context.cacheDir)
file.deleteOnExit()
return file
2020-12-02 06:39:02 +01:00
}
//database functions
2021-01-22 05:19:41 +01:00
override fun serialize(): Data {
2021-01-28 05:24:27 +01:00
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
.putLong(KEY_TS_INCOMING_MESSAGE_ID, databaseMessageID)
.build();
}
2021-01-28 05:24:27 +01:00
override fun getFactoryKey(): String {
return KEY
}
class Factory: Job.Factory<AttachmentDownloadJob> {
2021-01-22 05:19:41 +01:00
override fun create(data: Data): AttachmentDownloadJob {
2021-01-28 05:24:27 +01:00
return AttachmentDownloadJob(data.getLong(KEY_ATTACHMENT_ID), data.getLong(KEY_TS_INCOMING_MESSAGE_ID))
}
}
2020-11-25 02:06:41 +01:00
}