This commit is contained in:
Niels Andriesse 2021-05-13 13:19:08 +10:00
parent 0faeb7becf
commit 7c5b4aafec
6 changed files with 53 additions and 29 deletions

View File

@ -108,20 +108,20 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
return null // TODO: Implement return null // TODO: Implement
} }
override fun updateAttachmentAfterUploadSucceeded(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) { override fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
val database = DatabaseFactory.getAttachmentDatabase(context) val database = DatabaseFactory.getAttachmentDatabase(context)
val databaseAttachment = getDatabaseAttachment(attachmentId) ?: return val databaseAttachment = getDatabaseAttachment(attachmentId) ?: return
val attachmentPointer = SignalServiceAttachmentPointer(uploadResult.id, val attachmentPointer = SignalServiceAttachmentPointer(uploadResult.id,
attachmentStream.contentType, attachmentStream.contentType,
attachmentKey, attachmentKey,
Optional.of(Util.toIntExact(attachmentStream.length)), Optional.of(Util.toIntExact(attachmentStream.length)),
attachmentStream.preview, attachmentStream.preview,
attachmentStream.width, attachmentStream.height, attachmentStream.width, attachmentStream.height,
Optional.fromNullable(uploadResult.digest), Optional.fromNullable(uploadResult.digest),
attachmentStream.fileName, attachmentStream.fileName,
attachmentStream.voiceNote, attachmentStream.voiceNote,
attachmentStream.caption, attachmentStream.caption,
uploadResult.url); uploadResult.url);
val attachment = PointerAttachment.forPointer(Optional.of(attachmentPointer), databaseAttachment.fastPreflightId).get() val attachment = PointerAttachment.forPointer(Optional.of(attachmentPointer), databaseAttachment.fastPreflightId).get()
database.updateAttachmentAfterUploadSucceeded(databaseAttachment.attachmentId, attachment) database.updateAttachmentAfterUploadSucceeded(databaseAttachment.attachmentId, attachment)
} }

View File

@ -29,7 +29,7 @@ interface MessageDataProvider {
fun isOutgoingMessage(timestamp: Long): Boolean fun isOutgoingMessage(timestamp: Long): Boolean
fun updateAttachmentAfterUploadSucceeded(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult)
fun updateAttachmentAfterUploadFailed(attachmentId: Long) fun updateAttachmentAfterUploadFailed(attachmentId: Long)
fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>? fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>?

View File

@ -15,8 +15,8 @@ import org.session.libsignal.utilities.logging.Log
object FileServerAPIV2 { object FileServerAPIV2 {
private const val DEFAULT_SERVER_PUBLIC_KEY = "7cb31905b55cd5580c686911debf672577b3fb0bff81df4ce2d5c4cb3a7aaa69" private const val SERVER_PUBLIC_KEY = "7cb31905b55cd5580c686911debf672577b3fb0bff81df4ce2d5c4cb3a7aaa69"
const val DEFAULT_SERVER = "http://88.99.175.227" const val SERVER = "http://88.99.175.227"
sealed class Error(message: String) : Exception(message) { sealed class Error(message: String) : Exception(message) {
object ParsingFailed : Error("Invalid response.") object ParsingFailed : Error("Invalid response.")
@ -43,7 +43,7 @@ object FileServerAPIV2 {
} }
private fun send(request: Request): Promise<Map<*, *>, Exception> { private fun send(request: Request): Promise<Map<*, *>, Exception> {
val url = HttpUrl.parse(DEFAULT_SERVER) ?: return Promise.ofFail(OpenGroupAPIV2.Error.InvalidURL) val url = HttpUrl.parse(SERVER) ?: return Promise.ofFail(OpenGroupAPIV2.Error.InvalidURL)
val urlBuilder = HttpUrl.Builder() val urlBuilder = HttpUrl.Builder()
.scheme(url.scheme()) .scheme(url.scheme())
.host(url.host()) .host(url.host())
@ -64,7 +64,7 @@ object FileServerAPIV2 {
HTTP.Verb.DELETE -> requestBuilder.delete(createBody(request.parameters)) HTTP.Verb.DELETE -> requestBuilder.delete(createBody(request.parameters))
} }
if (request.useOnionRouting) { if (request.useOnionRouting) {
return OnionRequestAPI.sendOnionRequest(requestBuilder.build(), DEFAULT_SERVER, DEFAULT_SERVER_PUBLIC_KEY).fail { e -> return OnionRequestAPI.sendOnionRequest(requestBuilder.build(), SERVER, SERVER_PUBLIC_KEY).fail { e ->
Log.e("Loki", "File server request failed.", e) Log.e("Loki", "File server request failed.", e)
} }
} else { } else {

View File

@ -4,6 +4,8 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import nl.komponents.kovenant.Promise import nl.komponents.kovenant.Promise
import okhttp3.MultipartBody
import okio.Buffer
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.file_server.FileServerAPI import org.session.libsession.messaging.file_server.FileServerAPI
import org.session.libsession.messaging.file_server.FileServerAPIV2 import org.session.libsession.messaging.file_server.FileServerAPIV2
@ -21,6 +23,7 @@ import org.session.libsignal.service.internal.push.http.DigestingRequestBody
import org.session.libsignal.service.internal.util.Util import org.session.libsignal.service.internal.util.Util
import org.session.libsignal.service.loki.PlaintextOutputStreamFactory import org.session.libsignal.service.loki.PlaintextOutputStreamFactory
import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.logging.Log
import java.util.*
class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val message: Message, val messageSendJobID: String) : Job { class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val message: Message, val messageSendJobID: String) : Job {
override var delegate: JobDelegate? = null override var delegate: JobDelegate? = null
@ -60,7 +63,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
} }
handleSuccess(attachment, keyAndResult.first, keyAndResult.second) handleSuccess(attachment, keyAndResult.first, keyAndResult.second)
} else if (v1OpenGroup == null) { } else if (v1OpenGroup == null) {
val keyAndResult = upload(attachment, FileServerAPIV2.DEFAULT_SERVER, true) { val keyAndResult = upload(attachment, FileServerAPIV2.SERVER, true) {
FileServerAPIV2.upload(it) FileServerAPIV2.upload(it)
} }
handleSuccess(attachment, keyAndResult.first, keyAndResult.second) handleSuccess(attachment, keyAndResult.first, keyAndResult.second)
@ -83,22 +86,46 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
} }
private fun upload(attachment: SignalServiceAttachmentStream, server: String, encrypt: Boolean, upload: (ByteArray) -> Promise<Long, Exception>): Pair<ByteArray, DotNetAPI.UploadResult> { private fun upload(attachment: SignalServiceAttachmentStream, server: String, encrypt: Boolean, upload: (ByteArray) -> Promise<Long, Exception>): Pair<ByteArray, DotNetAPI.UploadResult> {
// Key
val key = if (encrypt) Util.getSecretBytes(64) else ByteArray(0) val key = if (encrypt) Util.getSecretBytes(64) else ByteArray(0)
// Length
val rawLength = attachment.length val rawLength = attachment.length
val length = if (encrypt) PaddingInputStream.getPaddedSize(rawLength) else rawLength val length = if (encrypt) {
val stream = if (encrypt) PaddingInputStream(attachment.inputStream, rawLength) else attachment.inputStream val paddedLength = PaddingInputStream.getPaddedSize(rawLength)
val ciphertextLength = if (encrypt) AttachmentCipherOutputStream.getCiphertextLength(length) else rawLength AttachmentCipherOutputStream.getCiphertextLength(paddedLength)
} else {
attachment.length
}
// In & out streams
// PaddingInputStream adds padding as data is read out from it. AttachmentCipherOutputStream
// encrypts as it writes data.
val inputStream = if (encrypt) PaddingInputStream(attachment.inputStream, rawLength) else attachment.inputStream
val outputStreamFactory = if (encrypt) AttachmentCipherOutputStreamFactory(key) else PlaintextOutputStreamFactory() val outputStreamFactory = if (encrypt) AttachmentCipherOutputStreamFactory(key) else PlaintextOutputStreamFactory()
val pushData = PushAttachmentData(attachment.contentType, stream, ciphertextLength, outputStreamFactory, attachment.listener) // Create a multipart request body but immediately read it out to a buffer. Doing this makes
val file = DigestingRequestBody(pushData.data, outputStreamFactory, attachment.contentType, pushData.dataSize, attachment.listener) // it easier to deal with inputStream and outputStreamFactory.
val id = upload(pushData.data.readBytes()).get() val pad = PushAttachmentData(attachment.contentType, inputStream, length, outputStreamFactory, attachment.listener)
return Pair(key, DotNetAPI.UploadResult(id, "${server}/files/$id", file.transmittedDigest)) val contentType = "application/octet-stream"
val drb = DigestingRequestBody(pad.data, pad.outputStreamFactory, contentType, pad.dataSize, attachment.listener)
Log.d("Loki", "File size: ${length.toDouble() / 1000} kb.")
val mpb = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("Content-Type", contentType)
.addFormDataPart("content", UUID.randomUUID().toString(), drb)
.build()
val b = Buffer()
mpb.writeTo(b)
val data = b.readByteArray()
// Upload the data
val id = upload(data).get()
val digest = drb.transmittedDigest
// Return
return Pair(key, DotNetAPI.UploadResult(id, "${server}/files/$id", digest))
} }
private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) { private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
Log.d(TAG, "Attachment uploaded successfully.") Log.d(TAG, "Attachment uploaded successfully.")
delegate?.handleJobSucceeded(this) delegate?.handleJobSucceeded(this)
MessagingModuleConfiguration.shared.messageDataProvider.updateAttachmentAfterUploadSucceeded(attachmentID, attachment, attachmentKey, uploadResult) MessagingModuleConfiguration.shared.messageDataProvider.handleSuccessfulAttachmentUpload(attachmentID, attachment, attachmentKey, uploadResult)
MessagingModuleConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID) MessagingModuleConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID)
} }

View File

@ -5,7 +5,6 @@ import okhttp3.Request
import org.session.libsession.messaging.file_server.FileServerAPI import org.session.libsession.messaging.file_server.FileServerAPI
import org.session.libsession.messaging.file_server.FileServerAPIV2 import org.session.libsession.messaging.file_server.FileServerAPIV2
import org.session.libsession.snode.OnionRequestAPI import org.session.libsession.snode.OnionRequestAPI
import org.session.libsignal.service.api.crypto.AttachmentCipherInputStream
import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.service.api.messages.SignalServiceAttachment import org.session.libsignal.service.api.messages.SignalServiceAttachment
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException
@ -42,7 +41,7 @@ object DownloadUtilities {
@JvmStatic @JvmStatic
fun downloadFile(outputStream: OutputStream, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) { fun downloadFile(outputStream: OutputStream, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) {
if (url.contains(FileServerAPIV2.DEFAULT_SERVER)) { if (url.contains(FileServerAPIV2.SERVER)) {
val httpUrl = HttpUrl.parse(url)!! val httpUrl = HttpUrl.parse(url)!!
val fileId = httpUrl.pathSegments().last() val fileId = httpUrl.pathSegments().last()
try { try {

View File

@ -1,6 +1,5 @@
package org.session.libsignal.service.internal.push.http; package org.session.libsignal.service.internal.push.http;
import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream; import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream;
import org.session.libsignal.service.api.crypto.DigestingOutputStream; import org.session.libsignal.service.api.crypto.DigestingOutputStream;
@ -19,5 +18,4 @@ public class AttachmentCipherOutputStreamFactory implements OutputStreamFactory
public DigestingOutputStream createFor(OutputStream wrap) throws IOException { public DigestingOutputStream createFor(OutputStream wrap) throws IOException {
return new AttachmentCipherOutputStream(key, wrap); return new AttachmentCipherOutputStream(key, wrap);
} }
} }