From 15d27e2cbb616480e6ce6cf5d25de9e5ab81c9c2 Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Tue, 24 Dec 2019 13:52:27 +0100 Subject: Implement download resumption for OMEMO encrypted files --- .../messenger/http/HttpDownloadConnection.java | 65 +++++++++++++++++++--- .../services/AbstractConnectionManager.java | 13 ----- 2 files changed, 56 insertions(+), 22 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java index 6d92529db..ae53f580e 100644 --- a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java +++ b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java @@ -1,10 +1,14 @@ package de.pixart.messenger.http; import android.os.PowerManager; -import androidx.annotation.Nullable; import android.util.Log; +import androidx.annotation.Nullable; + +import com.google.common.io.ByteStreams; + import java.io.BufferedInputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -93,12 +97,17 @@ public class HttpDownloadConnection implements Transferable { } else { ext = extension.main; } - if (message.getStatus() == Message.STATUS_RECEIVED){ + if (message.getStatus() == Message.STATUS_RECEIVED) { message.setRelativeFilePath(fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4) + (ext != null ? ("." + ext) : "")); } else { message.setRelativeFilePath("Sent/" + fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4) + (ext != null ? ("." + ext) : "")); } - this.file = mXmppConnectionService.getFileBackend().getFile(message, false); + if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { + this.file = new DownloadableFile(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + message.getUuid()); + Log.d(Config.LOGTAG, "create temporary OMEMO encrypted file: " + this.file.getAbsolutePath() + "(" + message.getMimeType() + ")"); + } else { + this.file = mXmppConnectionService.getFileBackend().getFile(message, false); + } final String reference = mUrl.getRef(); if (reference != null && AesGcmURLStreamHandler.IV_KEY.matcher(reference).matches()) { this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference)); @@ -141,7 +150,41 @@ public class HttpDownloadConnection implements Transferable { mHttpConnectionManager.updateConversationUi(true); } - private void finish() { + private void decryptOmemoFile() throws Exception { + final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true); + + if (outputFile.getParentFile().mkdirs()) { + Log.d(Config.LOGTAG, "created parent directories for " + outputFile.getAbsolutePath()); + } + + try { + outputFile.createNewFile(); + final InputStream is = new FileInputStream(this.file); + + outputFile.setKey(this.file.getKey()); + outputFile.setIv(this.file.getIv()); + final OutputStream os = AbstractConnectionManager.createOutputStream(outputFile, false, true); + + ByteStreams.copy(is, os); + + FileBackend.close(is); + FileBackend.close(os); + + if (!file.delete()) { + Log.w(Config.LOGTAG, "unable to delete temporary OMEMO encrypted file " + file.getAbsolutePath()); + } + + message.setRelativeFilePath(outputFile.getPath()); + } catch (IOException e) { + message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED); + mXmppConnectionService.updateMessage(message); + } + } + + private void finish() throws Exception { + if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { + decryptOmemoFile(); + } message.setTransferable(null); mHttpConnectionManager.finishConnection(this); boolean notify = acceptedAutomatically && !message.isRead(); @@ -263,6 +306,10 @@ public class HttpDownloadConnection implements Transferable { retrieveFailed(e); return; } + //TODO at this stage we probably also want to persist the file size in the body of the + // message via a similar mechansim as updateFileParams() - essentially body needs to read + // "url|filesize" + // afterwards a file that failed to download mid way will not display 'check file size' anymore file.setExpectedSize(size); message.resetFileParams(); if (mHttpConnectionManager.hasStoragePermission() @@ -349,8 +396,8 @@ public class HttpDownloadConnection implements Transferable { try { changeStatus(STATUS_DOWNLOADING); download(); - updateImageBounds(); finish(); + updateImageBounds(); } catch (SSLHandshakeException e) { changeStatus(STATUS_OFFER); } catch (Exception e) { @@ -383,11 +430,11 @@ public class HttpDownloadConnection implements Transferable { connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getUserAgent()); connection.setRequestProperty("Accept-Encoding", "identity"); final long expected = file.getExpectedSize(); - final boolean tryResume = file.exists() && file.getKey() == null && file.getSize() > 0 && file.getSize() < expected; + final boolean tryResume = file.exists() && file.getSize() > 0 && file.getSize() < expected; long resumeSize = 0; if (tryResume) { resumeSize = file.getSize(); - Log.d(Config.LOGTAG, "http download trying resume after" + resumeSize + " of " + expected); + Log.d(Config.LOGTAG, "http download trying resume after " + resumeSize + " of " + expected); connection.setRequestProperty("Range", "bytes=" + resumeSize + "-"); } connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000); @@ -401,7 +448,7 @@ public class HttpDownloadConnection implements Transferable { Log.d(Config.LOGTAG, "server resumed"); transmitted = file.getSize(); updateProgress(Math.round(((double) transmitted / expected) * 100)); - os = AbstractConnectionManager.createAppendedOutputStream(file); + os = AbstractConnectionManager.createOutputStream(file, true, false); if (os == null) { throw new FileWriterException(); } @@ -421,7 +468,7 @@ public class HttpDownloadConnection implements Transferable { if (!file.exists() && !file.createNewFile()) { throw new FileWriterException(); } - os = AbstractConnectionManager.createOutputStream(file, true); + os = AbstractConnectionManager.createOutputStream(file, false, false); } int count; byte[] buffer = new byte[4096]; diff --git a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java index 19a7341f8..cba382b61 100644 --- a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java +++ b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java @@ -51,19 +51,6 @@ public class AbstractConnectionManager { } } - - public static OutputStream createAppendedOutputStream(DownloadableFile file) { - return createOutputStream(file, false, true); - } - - public static OutputStream createOutputStream(DownloadableFile file) { - return createOutputStream(file, false); - } - - public static OutputStream createOutputStream(DownloadableFile file, boolean gcm) { - return createOutputStream(file, gcm, false); - } - public static OutputStream createOutputStream(DownloadableFile file, boolean append, boolean decrypt) { FileOutputStream os; try { -- cgit v1.2.3