From 303e205276abfc08cd7a6f50a21326fb3295be30 Mon Sep 17 00:00:00 2001
From: Daniel Gultsch <daniel@gultsch.de>
Date: Sun, 6 Dec 2020 19:22:36 +0100
Subject: [PATCH] =?UTF-8?q?if=20file=20extension=20doesn=E2=80=99t=20exist?=
 =?UTF-8?q?.=20try=20to=20guess=20from=20content=20type.=20fixes=20#3939?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../http/HttpDownloadConnection.java          | 50 ++++++++++++-------
 .../services/AbstractConnectionManager.java   | 10 ++++
 2 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
index e01eb2578..476ea2d20 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
@@ -4,6 +4,7 @@ import android.os.PowerManager;
 import android.support.annotation.Nullable;
 import android.util.Log;
 
+import com.google.common.base.Strings;
 import com.google.common.io.ByteStreams;
 
 import java.io.BufferedInputStream;
@@ -30,16 +31,16 @@ import eu.siacs.conversations.services.AbstractConnectionManager;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.FileWriterException;
+import eu.siacs.conversations.utils.MimeUtils;
 import eu.siacs.conversations.utils.WakeLockHelper;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
-import eu.siacs.conversations.xmpp.Jid;
 
 public class HttpDownloadConnection implements Transferable {
 
     private final Message message;
     private final boolean mUseTor;
-    private HttpConnectionManager mHttpConnectionManager;
-    private XmppConnectionService mXmppConnectionService;
+    private final HttpConnectionManager mHttpConnectionManager;
+    private final XmppConnectionService mXmppConnectionService;
     private URL mUrl;
     private DownloadableFile file;
     private int mStatus = Transferable.STATUS_UNKNOWN;
@@ -97,23 +98,13 @@ public class HttpDownloadConnection implements Transferable {
                     && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
                 this.message.setEncryption(Message.ENCRYPTION_NONE);
             }
-            final String ext;
-            if (VALID_CRYPTO_EXTENSIONS.contains(extension.main)) {
-                ext = extension.secondary;
-            } else {
-                ext = extension.main;
+            final String ext = extension.getExtension();
+            if (ext != null) {
+                message.setRelativeFilePath(String.format("%s.%s", message.getUuid(), ext));
+            } else if (Strings.isNullOrEmpty(message.getRelativeFilePath())) {
+                message.setRelativeFilePath(message.getUuid());
             }
-            message.setRelativeFilePath(message.getUuid() + (ext != null ? ("." + ext) : ""));
-            final String reference = mUrl.getRef();
-            if (reference != null && AesGcmURLStreamHandler.IV_KEY.matcher(reference).matches()) {
-                this.file = new DownloadableFile(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + message.getUuid());
-                this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
-                Log.d(Config.LOGTAG, "create temporary OMEMO encrypted file: " + this.file.getAbsolutePath() + "(" + message.getMimeType() + ")");
-            } else {
-                this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
-            }
-
-
+            setupFile();
             if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() == null) {
                 this.message.setEncryption(Message.ENCRYPTION_NONE);
             }
@@ -130,6 +121,17 @@ public class HttpDownloadConnection implements Transferable {
         }
     }
 
+    private void setupFile() {
+        final String reference = mUrl.getRef();
+        if (reference != null && AesGcmURLStreamHandler.IV_KEY.matcher(reference).matches()) {
+            this.file = new DownloadableFile(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + message.getUuid());
+            this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
+            Log.d(Config.LOGTAG, "create temporary OMEMO encrypted file: " + this.file.getAbsolutePath() + "(" + message.getMimeType() + ")");
+        } else {
+            this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
+        }
+    }
+
     private void download(boolean interactive) {
         new Thread(new FileDownloader(interactive)).start();
     }
@@ -363,6 +365,16 @@ public class HttpDownloadConnection implements Transferable {
                 } else {
                     contentLength = connection.getHeaderField("Content-Length");
                 }
+                final String contentType = connection.getContentType();
+                final AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(mUrl.getPath());
+                if (Strings.isNullOrEmpty(extension.getExtension()) && contentType != null) {
+                    final String fileExtension = MimeUtils.guessExtensionFromMimeType(contentType);
+                    if (fileExtension != null) {
+                        message.setRelativeFilePath(String.format("%s.%s", message.getUuid(), fileExtension));
+                        Log.d(Config.LOGTAG, "rewriting name after not finding extension in url but in content type");
+                        setupFile();
+                    }
+                }
                 connection.disconnect();
                 if (contentLength == null) {
                     throw new IOException("no content-length found in HEAD response");
diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
index 98721aaf4..87dffab29 100644
--- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
@@ -30,6 +30,8 @@ import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.DownloadableFile;
 import eu.siacs.conversations.utils.Compatibility;
 
+import static eu.siacs.conversations.entities.Transferable.VALID_CRYPTO_EXTENSIONS;
+
 public class AbstractConnectionManager {
 
     private static final int UI_REFRESH_THRESHOLD = 250;
@@ -106,6 +108,14 @@ public class AbstractConnectionManager {
             this.secondary = secondary;
         }
 
+        public String getExtension() {
+            if (VALID_CRYPTO_EXTENSIONS.contains(main)) {
+                return secondary;
+            } else {
+                return main;
+            }
+        }
+
         public static Extension of(String path) {
             final int pos = path.lastIndexOf('/');
             final String filename = path.substring(pos + 1).toLowerCase();